Wrap function by an object if returning value of native method is

a JSFunctor interface.

See #280
This commit is contained in:
Alexey Andreev 2017-06-11 00:15:41 +03:00
parent 45ba247265
commit 2992c6e406
6 changed files with 180 additions and 99 deletions

View File

@ -405,4 +405,8 @@ final class JS {
@GeneratedBy(JSNativeGenerator.class) @GeneratedBy(JSNativeGenerator.class)
@PluggableDependency(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class)
public static native JSObject function(JSObject instance, JSObject property); public static native JSObject function(JSObject instance, JSObject property);
@GeneratedBy(JSNativeGenerator.class)
@PluggableDependency(JSNativeGenerator.class)
public static native JSObject functionAsObject(JSObject instance, JSObject property);
} }

View File

@ -66,7 +66,6 @@ import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.ExitInstruction; import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.StringConstantInstruction;
import org.teavm.model.util.InstructionVariableMapper; import org.teavm.model.util.InstructionVariableMapper;
import org.teavm.model.util.ModelUtils; import org.teavm.model.util.ModelUtils;
import org.teavm.model.util.ProgramUtils; import org.teavm.model.util.ProgramUtils;
@ -114,7 +113,7 @@ class JSClassProcessor {
private void getFunctorMethods(String className, Map<MethodDescriptor, MethodReference> methods) { private void getFunctorMethods(String className, Map<MethodDescriptor, MethodReference> methods) {
classSource.getAncestors(className).forEach(cls -> { classSource.getAncestors(className).forEach(cls -> {
if (cls.getAnnotations().get(JSFunctor.class.getName()) != null && isProperFunctor(cls)) { if (cls.getAnnotations().get(JSFunctor.class.getName()) != null && marshaller.isProperFunctor(cls)) {
MethodReference method = cls.getMethods().iterator().next().getReference(); MethodReference method = cls.getMethods().iterator().next().getReference();
if (!methods.containsKey(method.getDescriptor())) { if (!methods.containsKey(method.getDescriptor())) {
methods.put(method.getDescriptor(), method); methods.put(method.getDescriptor(), method);
@ -241,7 +240,7 @@ class JSClassProcessor {
private void setCurrentProgram(Program program) { private void setCurrentProgram(Program program) {
this.program = program; this.program = program;
marshaller = new JSValueMarshaller(diagnostics, typeHelper, program, replacement); marshaller = new JSValueMarshaller(diagnostics, typeHelper, classSource, program, replacement);
} }
void processProgram(MethodHolder methodToProcess) { void processProgram(MethodHolder methodToProcess) {
@ -333,18 +332,18 @@ class JSClassProcessor {
newInvoke.setReceiver(result); newInvoke.setReceiver(result);
newInvoke.setLocation(invoke.getLocation()); newInvoke.setLocation(invoke.getLocation());
if (invoke.getInstance() != null) { if (invoke.getInstance() != null) {
Variable arg = wrapArgument(callLocation, invoke.getInstance(), Variable arg = marshaller.wrapArgument(callLocation, invoke.getInstance(),
ValueType.object(method.getOwnerName()), false); ValueType.object(method.getOwnerName()), false);
newInvoke.getArguments().add(arg); newInvoke.getArguments().add(arg);
} }
for (int i = 0; i < invoke.getArguments().size(); ++i) { for (int i = 0; i < invoke.getArguments().size(); ++i) {
Variable arg = wrapArgument(callLocation, invoke.getArguments().get(i), Variable arg = marshaller.wrapArgument(callLocation, invoke.getArguments().get(i),
method.parameterType(i), byRefParams[i]); method.parameterType(i), byRefParams[i]);
newInvoke.getArguments().add(arg); newInvoke.getArguments().add(arg);
} }
replacement.add(newInvoke); replacement.add(newInvoke);
if (result != null) { if (result != null) {
result = marshaller.unwrap(callLocation, result, method.getResultType()); result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType());
copyVar(result, invoke.getReceiver(), invoke.getLocation()); copyVar(result, invoke.getReceiver(), invoke.getLocation());
} }
@ -365,7 +364,7 @@ class JSClassProcessor {
Variable result = invoke.getReceiver() != null ? program.createVariable() : null; Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
addPropertyGet(propertyName, invoke.getInstance(), result, invoke.getLocation()); addPropertyGet(propertyName, invoke.getInstance(), result, invoke.getLocation());
if (result != null) { if (result != null) {
result = marshaller.unwrap(callLocation, result, method.getResultType()); result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType());
copyVar(result, invoke.getReceiver(), invoke.getLocation()); copyVar(result, invoke.getReceiver(), invoke.getLocation());
} }
return true; return true;
@ -375,7 +374,7 @@ class JSClassProcessor {
if (propertyName == null) { if (propertyName == null) {
propertyName = cutPrefix(method.getName(), 3); propertyName = cutPrefix(method.getName(), 3);
} }
Variable wrapped = wrapArgument(callLocation, invoke.getArguments().get(0), Variable wrapped = marshaller.wrapArgument(callLocation, invoke.getArguments().get(0),
method.parameterType(0), false); method.parameterType(0), false);
addPropertySet(propertyName, invoke.getInstance(), wrapped, invoke.getLocation()); addPropertySet(propertyName, invoke.getInstance(), wrapped, invoke.getLocation());
return true; return true;
@ -394,19 +393,19 @@ class JSClassProcessor {
private boolean processIndexer(MethodReader method, CallLocation callLocation, InvokeInstruction invoke) { private boolean processIndexer(MethodReader method, CallLocation callLocation, InvokeInstruction invoke) {
if (isProperGetIndexer(method.getDescriptor())) { if (isProperGetIndexer(method.getDescriptor())) {
Variable result = invoke.getReceiver() != null ? program.createVariable() : null; Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
addIndexerGet(invoke.getInstance(), marshaller.wrap(invoke.getArguments().get(0), addIndexerGet(invoke.getInstance(), marshaller.wrapArgument(callLocation, invoke.getArguments().get(0),
method.parameterType(0), invoke.getLocation(), false), result, invoke.getLocation()); method.parameterType(0), false), result, invoke.getLocation());
if (result != null) { if (result != null) {
result = marshaller.unwrap(callLocation, result, method.getResultType()); result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType());
copyVar(result, invoke.getReceiver(), invoke.getLocation()); copyVar(result, invoke.getReceiver(), invoke.getLocation());
} }
return true; return true;
} }
if (isProperSetIndexer(method.getDescriptor())) { if (isProperSetIndexer(method.getDescriptor())) {
Variable index = marshaller.wrap(invoke.getArguments().get(0), method.parameterType(0), Variable index = marshaller.wrapArgument(callLocation, invoke.getArguments().get(0),
invoke.getLocation(), false); method.parameterType(0), false);
Variable value = marshaller.wrap(invoke.getArguments().get(1), method.parameterType(1), Variable value = marshaller.wrapArgument(callLocation, invoke.getArguments().get(1),
invoke.getLocation(), false); method.parameterType(1), false);
addIndexerSet(invoke.getInstance(), index, value, invoke.getLocation()); addIndexerSet(invoke.getInstance(), index, value, invoke.getLocation());
return true; return true;
} }
@ -469,17 +468,17 @@ class JSClassProcessor {
newInvoke.setType(InvocationType.SPECIAL); newInvoke.setType(InvocationType.SPECIAL);
newInvoke.setReceiver(result); newInvoke.setReceiver(result);
newInvoke.getArguments().add(invoke.getInstance()); newInvoke.getArguments().add(invoke.getInstance());
newInvoke.getArguments().add(addStringWrap(addString(name, invoke.getLocation()), newInvoke.getArguments().add(marshaller.addStringWrap(marshaller.addString(name, invoke.getLocation()),
invoke.getLocation())); invoke.getLocation()));
newInvoke.setLocation(invoke.getLocation()); newInvoke.setLocation(invoke.getLocation());
for (int i = 0; i < invoke.getArguments().size(); ++i) { for (int i = 0; i < invoke.getArguments().size(); ++i) {
Variable arg = wrapArgument(callLocation, invoke.getArguments().get(i), Variable arg = marshaller.wrapArgument(callLocation, invoke.getArguments().get(i),
method.parameterType(i), byRefParams[i]); method.parameterType(i), byRefParams[i]);
newInvoke.getArguments().add(arg); newInvoke.getArguments().add(arg);
} }
replacement.add(newInvoke); replacement.add(newInvoke);
if (result != null) { if (result != null) {
result = marshaller.unwrap(callLocation, result, method.getResultType()); result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType());
copyVar(result, invoke.getReceiver(), invoke.getLocation()); copyVar(result, invoke.getReceiver(), invoke.getLocation());
} }
@ -627,11 +626,11 @@ class JSClassProcessor {
insn.setMethod(calleeRef); insn.setMethod(calleeRef);
replacement.clear(); replacement.clear();
if (!callee.hasModifier(ElementModifier.STATIC)) { if (!callee.hasModifier(ElementModifier.STATIC)) {
insn.setInstance(marshaller.unwrap(location, program.variableAt(paramIndex++), insn.setInstance(marshaller.unwrapReturnValue(location, program.variableAt(paramIndex++),
ValueType.object(calleeRef.getClassName()))); ValueType.object(calleeRef.getClassName())));
} }
for (int i = 0; i < callee.parameterCount(); ++i) { for (int i = 0; i < callee.parameterCount(); ++i) {
insn.getArguments().add(marshaller.unwrap(location, program.variableAt(paramIndex++), insn.getArguments().add(marshaller.unwrapReturnValue(location, program.variableAt(paramIndex++),
callee.parameterType(i))); callee.parameterType(i)));
} }
if (callee.getResultType() != ValueType.VOID) { if (callee.getResultType() != ValueType.VOID) {
@ -655,7 +654,7 @@ class JSClassProcessor {
private void addPropertyGet(String propertyName, Variable instance, Variable receiver, private void addPropertyGet(String propertyName, Variable instance, Variable receiver,
TextLocation location) { TextLocation location) {
Variable nameVar = addStringWrap(addString(propertyName, location), location); Variable nameVar = marshaller.addStringWrap(marshaller.addString(propertyName, location), location);
InvokeInstruction insn = new InvokeInstruction(); InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL); insn.setType(InvocationType.SPECIAL);
insn.setMethod(new MethodReference(JS.class, "get", JSObject.class, JSObject.class, JSObject.class)); insn.setMethod(new MethodReference(JS.class, "get", JSObject.class, JSObject.class, JSObject.class));
@ -667,7 +666,7 @@ class JSClassProcessor {
} }
private void addPropertySet(String propertyName, Variable instance, Variable value, TextLocation location) { private void addPropertySet(String propertyName, Variable instance, Variable value, TextLocation location) {
Variable nameVar = addStringWrap(addString(propertyName, location), location); Variable nameVar = marshaller.addStringWrap(marshaller.addString(propertyName, location), location);
InvokeInstruction insn = new InvokeInstruction(); InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL); insn.setType(InvocationType.SPECIAL);
insn.setMethod(new MethodReference(JS.class, "set", JSObject.class, JSObject.class, insn.setMethod(new MethodReference(JS.class, "set", JSObject.class, JSObject.class,
@ -710,61 +709,6 @@ class JSClassProcessor {
replacement.add(insn); replacement.add(insn);
} }
private Variable addStringWrap(Variable var, TextLocation location) {
return marshaller.wrap(var, ValueType.object("java.lang.String"), location, false);
}
private Variable addString(String str, TextLocation location) {
Variable var = program.createVariable();
StringConstantInstruction nameInsn = new StringConstantInstruction();
nameInsn.setReceiver(var);
nameInsn.setConstant(str);
nameInsn.setLocation(location);
replacement.add(nameInsn);
return var;
}
private Variable wrapArgument(CallLocation location, Variable var, ValueType type, boolean byRef) {
if (type instanceof ValueType.Object) {
String className = ((ValueType.Object) type).getClassName();
ClassReader cls = classSource.get(className);
if (cls.getAnnotations().get(JSFunctor.class.getName()) != null) {
return wrapFunctor(location, var, cls);
}
}
return marshaller.wrap(var, type, location.getSourceLocation(), byRef);
}
private boolean isProperFunctor(ClassReader type) {
if (!type.hasModifier(ElementModifier.INTERFACE)) {
return false;
}
return type.getMethods().stream()
.filter(method -> method.hasModifier(ElementModifier.ABSTRACT))
.count() == 1;
}
private Variable wrapFunctor(CallLocation location, Variable var, ClassReader type) {
if (!isProperFunctor(type)) {
diagnostics.error(location, "Wrong functor: {{c0}}", type.getName());
return var;
}
String name = type.getMethods().stream()
.filter(method -> method.hasModifier(ElementModifier.ABSTRACT))
.findFirst().get().getName();
Variable functor = program.createVariable();
Variable nameVar = addStringWrap(addString(name, location.getSourceLocation()), location.getSourceLocation());
InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL);
insn.setMethod(new MethodReference(JS.class, "function", JSObject.class, JSObject.class, JSObject.class));
insn.setReceiver(functor);
insn.getArguments().add(var);
insn.getArguments().add(nameVar);
insn.setLocation(location.getSourceLocation());
replacement.add(insn);
return functor;
}
private MethodReader getMethod(MethodReference ref) { private MethodReader getMethod(MethodReference ref) {
ClassReader cls = classSource.get(ref.getClassName()); ClassReader cls = classSource.get(ref.getClassName());
if (cls == null) { if (cls == null) {

View File

@ -44,6 +44,9 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator
case "function": case "function":
writeFunction(context, writer); writeFunction(context, writer);
break; break;
case "functionAsObject":
writeFunctionAsObject(context, writer);
break;
} }
} }
@ -69,6 +72,16 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator
writer.append("return ").append(thisName).append("[name]();").softNewLine(); writer.append("return ").append(thisName).append("[name]();").softNewLine();
} }
private void writeFunctionAsObject(GeneratorContext context, SourceWriter writer) throws IOException {
String thisName = context.getParameterName(1);
String methodName = context.getParameterName(2);
writer.append("var result").ws().append("=").ws().append("{};").softNewLine();
writer.append("result[").append(methodName).append("]").ws().append("=").ws().append(thisName)
.append(";").softNewLine();
writer.append("return result;").softNewLine();
}
@Override @Override
public void generate(InjectorContext context, MethodReference methodRef) throws IOException { public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
SourceWriter writer = context.getWriter(); SourceWriter writer = context.getWriter();
@ -143,9 +156,6 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator
context.writeExpr(context.getArgument(0), context.getPrecedence()); context.writeExpr(context.getArgument(0), context.getPrecedence());
} }
break; break;
case "function":
generateFunction(context);
break;
case "unwrapString": case "unwrapString":
writer.append("$rt_str("); writer.append("$rt_str(");
context.writeExpr(context.getArgument(0), Precedence.min()); context.writeExpr(context.getArgument(0), Precedence.min());
@ -200,17 +210,6 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator
} }
} }
private void generateFunction(InjectorContext context) throws IOException {
SourceWriter writer = context.getWriter();
writer.append("(function($instance,").ws().append("$property)").ws().append("{").ws()
.append("return function()").ws().append("{").indent().softNewLine();
writer.append("return $instance[$property].apply($instance,").ws().append("arguments);").softNewLine();
writer.outdent().append("};})(");
context.writeExpr(context.getArgument(0));
writer.append(",").ws();
context.writeExpr(context.getArgument(1));
writer.append(")");
}
private void renderProperty(Expr property, InjectorContext context) throws IOException { private void renderProperty(Expr property, InjectorContext context) throws IOException {
SourceWriter writer = context.getWriter(); SourceWriter writer = context.getWriter();

View File

@ -120,7 +120,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
BasicBlock basicBlock = program.createBasicBlock(); BasicBlock basicBlock = program.createBasicBlock();
List<Instruction> marshallInstructions = new ArrayList<>(); List<Instruction> marshallInstructions = new ArrayList<>();
JSValueMarshaller marshaller = new JSValueMarshaller(diagnostics, typeHelper, program, JSValueMarshaller marshaller = new JSValueMarshaller(diagnostics, typeHelper, innerSource, program,
marshallInstructions); marshallInstructions);
List<Variable> variablesToPass = new ArrayList<>(); List<Variable> variablesToPass = new ArrayList<>();
@ -129,7 +129,8 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
} }
for (int i = 0; i < method.parameterCount(); ++i) { for (int i = 0; i < method.parameterCount(); ++i) {
Variable var = marshaller.unwrap(callLocation, variablesToPass.get(i), method.parameterType(i)); Variable var = marshaller.unwrapReturnValue(callLocation, variablesToPass.get(i),
method.parameterType(i));
variablesToPass.set(i, var); variablesToPass.set(i, var);
} }
@ -146,8 +147,8 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
ExitInstruction exit = new ExitInstruction(); ExitInstruction exit = new ExitInstruction();
if (method.getResultType() != ValueType.VOID) { if (method.getResultType() != ValueType.VOID) {
invocation.setReceiver(program.createVariable()); invocation.setReceiver(program.createVariable());
exit.setValueToReturn(marshaller.wrap(invocation.getReceiver(), method.getResultType(), exit.setValueToReturn(marshaller.wrapArgument(callLocation, invocation.getReceiver(),
null, false)); method.getResultType(), false));
basicBlock.addAll(marshallInstructions); basicBlock.addAll(marshallInstructions);
marshallInstructions.clear(); marshallInstructions.clear();
} }

View File

@ -18,10 +18,14 @@ package org.teavm.jso.impl;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject; import org.teavm.jso.JSObject;
import org.teavm.jso.core.JSArray; import org.teavm.jso.core.JSArray;
import org.teavm.jso.core.JSArrayReader; import org.teavm.jso.core.JSArrayReader;
import org.teavm.model.CallLocation; import org.teavm.model.CallLocation;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Program; import org.teavm.model.Program;
@ -32,22 +36,66 @@ import org.teavm.model.instructions.CastInstruction;
import org.teavm.model.instructions.ClassConstantInstruction; import org.teavm.model.instructions.ClassConstantInstruction;
import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.StringConstantInstruction;
class JSValueMarshaller { class JSValueMarshaller {
private Diagnostics diagnostics; private Diagnostics diagnostics;
private JSTypeHelper typeHelper; private JSTypeHelper typeHelper;
private ClassReaderSource classSource;
private Program program; private Program program;
private List<Instruction> replacement; private List<Instruction> replacement;
JSValueMarshaller(Diagnostics diagnostics, JSTypeHelper typeHelper, Program program, JSValueMarshaller(Diagnostics diagnostics, JSTypeHelper typeHelper, ClassReaderSource classSource,
List<Instruction> replacement) { Program program, List<Instruction> replacement) {
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
this.typeHelper = typeHelper; this.typeHelper = typeHelper;
this.classSource = classSource;
this.program = program; this.program = program;
this.replacement = replacement; this.replacement = replacement;
} }
public Variable wrap(Variable var, ValueType type, TextLocation location, boolean byRef) { Variable wrapArgument(CallLocation location, Variable var, ValueType type, boolean byRef) {
if (type instanceof ValueType.Object) {
String className = ((ValueType.Object) type).getClassName();
ClassReader cls = classSource.get(className);
if (cls.getAnnotations().get(JSFunctor.class.getName()) != null) {
return wrapFunctor(location, var, cls);
}
}
return wrap(var, type, location.getSourceLocation(), byRef);
}
boolean isProperFunctor(ClassReader type) {
if (!type.hasModifier(ElementModifier.INTERFACE)) {
return false;
}
return type.getMethods().stream()
.filter(method -> method.hasModifier(ElementModifier.ABSTRACT))
.count() == 1;
}
private Variable wrapFunctor(CallLocation location, Variable var, ClassReader type) {
if (!isProperFunctor(type)) {
diagnostics.error(location, "Wrong functor: {{c0}}", type.getName());
return var;
}
String name = type.getMethods().stream()
.filter(method -> method.hasModifier(ElementModifier.ABSTRACT))
.findFirst().get().getName();
Variable functor = program.createVariable();
Variable nameVar = addStringWrap(addString(name, location.getSourceLocation()), location.getSourceLocation());
InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL);
insn.setMethod(new MethodReference(JS.class, "function", JSObject.class, JSObject.class, JSObject.class));
insn.setReceiver(functor);
insn.getArguments().add(var);
insn.getArguments().add(nameVar);
insn.setLocation(location.getSourceLocation());
replacement.add(insn);
return functor;
}
Variable wrap(Variable var, ValueType type, TextLocation location, boolean byRef) {
if (byRef) { if (byRef) {
InvokeInstruction insn = new InvokeInstruction(); InvokeInstruction insn = new InvokeInstruction();
insn.setMethod(new MethodReference(JS.class, "arrayData", Object.class, JSObject.class)); insn.setMethod(new MethodReference(JS.class, "arrayData", Object.class, JSObject.class));
@ -169,6 +217,17 @@ class JSValueMarshaller {
return new MethodReference(JS.class, "arrayWrapper", Function.class); return new MethodReference(JS.class, "arrayWrapper", Function.class);
} }
Variable unwrapReturnValue(CallLocation location, Variable var, ValueType type) {
if (type instanceof ValueType.Object) {
String className = ((ValueType.Object) type).getClassName();
ClassReader cls = classSource.get(className);
if (cls.getAnnotations().get(JSFunctor.class.getName()) != null) {
return unwrapFunctor(location, var, cls);
}
}
return unwrap(location, var, type);
}
Variable unwrap(CallLocation location, Variable var, ValueType type) { Variable unwrap(CallLocation location, Variable var, ValueType type) {
if (type instanceof ValueType.Primitive) { if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) { switch (((ValueType.Primitive) type).getKind()) {
@ -402,4 +461,40 @@ class JSValueMarshaller {
replacement.add(insn); replacement.add(insn);
return result; return result;
} }
private Variable unwrapFunctor(CallLocation location, Variable var, ClassReader type) {
if (!isProperFunctor(type)) {
diagnostics.error(location, "Wrong functor: {{c0}}", type.getName());
return var;
}
String name = type.getMethods().stream()
.filter(method -> method.hasModifier(ElementModifier.ABSTRACT))
.findFirst().get().getName();
Variable functor = program.createVariable();
Variable nameVar = addStringWrap(addString(name, location.getSourceLocation()), location.getSourceLocation());
InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL);
insn.setMethod(new MethodReference(JS.class, "functionAsObject", JSObject.class, JSObject.class,
JSObject.class));
insn.setReceiver(functor);
insn.getArguments().add(var);
insn.getArguments().add(nameVar);
insn.setLocation(location.getSourceLocation());
replacement.add(insn);
return functor;
}
Variable addStringWrap(Variable var, TextLocation location) {
return wrap(var, ValueType.object("java.lang.String"), location, false);
}
Variable addString(String str, TextLocation location) {
Variable var = program.createVariable();
StringConstantInstruction nameInsn = new StringConstantInstruction();
nameInsn.setReceiver(var);
nameInsn.setConstant(str);
nameInsn.setLocation(location);
replacement.add(nameInsn);
return var;
}
} }

View File

@ -15,7 +15,8 @@
*/ */
package org.teavm.jso.test; package org.teavm.jso.test;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
@ -33,6 +34,11 @@ public class FunctorTest {
assertEquals("(5)", testMethod((a, b) -> a + b, 2, 3)); assertEquals("(5)", testMethod((a, b) -> a + b, 2, 3));
} }
@Test
public void functorParamsMarshaled() {
assertEquals("(q,w)", testMethod((a, b) -> a + "," + b, "q", "w"));
}
@Test @Test
public void functorIdentityPreserved() { public void functorIdentityPreserved() {
JSBiFunction javaFunction = (a, b) -> a + b; JSBiFunction javaFunction = (a, b) -> a + b;
@ -65,9 +71,36 @@ public class FunctorTest {
assertEquals("baz_ok", wp.propbaz()); assertEquals("baz_ok", wp.propbaz());
} }
@Test
public void functorPassedBack() {
JSBiFunction function = getBiFunction();
assertEquals(23042, function.foo(23, 42));
}
@Test
public void functorParamsMarshaledBack() {
JSStringBiFunction function = getStringBiFunction();
assertEquals("q,w", function.foo("q", "w"));
}
@JSBody(params = { "f", "a", "b" }, script = "return '(' + f(a, b) + ')';") @JSBody(params = { "f", "a", "b" }, script = "return '(' + f(a, b) + ')';")
private static native String testMethod(JSBiFunction f, int a, int b); private static native String testMethod(JSBiFunction f, int a, int b);
@JSBody(params = { "f", "a", "b" }, script = "return '(' + f(a, b) + ')';")
private static native String testMethod(JSStringBiFunction f, String a, String b);
@JSBody(script = ""
+ "return function(a, b) {"
+ "return a * 1000 + b;"
+ "};")
private static native JSBiFunction getBiFunction();
@JSBody(script = ""
+ "return function(a, b) {"
+ "return a + ',' + b;"
+ "};")
private static native JSStringBiFunction getStringBiFunction();
@JSBody(params = "f", script = "return f;") @JSBody(params = "f", script = "return f;")
private static native JSObject getFunction(JSBiFunction f); private static native JSObject getFunction(JSBiFunction f);
@ -79,6 +112,11 @@ public class FunctorTest {
int foo(int a, int b); int foo(int a, int b);
} }
@JSFunctor
interface JSStringBiFunction extends JSObject {
String foo(String a, String b);
}
@JSFunctor @JSFunctor
interface JSFunctionWithDefaultMethod extends JSObject { interface JSFunctionWithDefaultMethod extends JSObject {
int foo(int a); int foo(int a);