Make JS function out of JS object when casting to JSFunctor interface

This commit is contained in:
Alexey Andreev 2018-10-24 14:30:26 +03:00
parent 5035c58533
commit 6551f3eb68
4 changed files with 59 additions and 16 deletions

View File

@ -61,6 +61,7 @@ import org.teavm.model.TextLocation;
import org.teavm.model.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.CastInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
@ -252,9 +253,14 @@ class JSClassProcessor {
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlock block = program.basicBlockAt(i);
for (Instruction insn : block) {
if (!(insn instanceof InvokeInstruction)) {
continue;
if (insn instanceof CastInstruction) {
replacement.clear();
CallLocation callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation());
if (processCast((CastInstruction) insn, callLocation)) {
insn.insertNextAll(replacement);
insn.delete();
}
} else if (insn instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction) insn;
MethodReader method = getMethod(invoke.getMethod());
@ -270,6 +276,31 @@ class JSClassProcessor {
}
}
}
}
private boolean processCast(CastInstruction cast, CallLocation location) {
if (!(cast.getTargetType() instanceof ValueType.Object)) {
return false;
}
String targetClassName = ((ValueType.Object) cast.getTargetType()).getClassName();
if (!typeHelper.isJavaScriptClass(targetClassName)) {
return false;
}
ClassReader targetClass = classSource.get(targetClassName);
if (targetClass.getAnnotations().get(JSFunctor.class.getName()) == null) {
return false;
}
Variable result = marshaller.unwrapFunctor(location, cast.getValue(), targetClass);
AssignInstruction assign = new AssignInstruction();
assign.setLocation(location.getSourceLocation());
assign.setAssignee(result);
assign.setReceiver(cast.getReceiver());
replacement.add(assign);
return true;
}
private boolean processInvocation(MethodReader method, CallLocation callLocation, InvokeInstruction invoke,
MethodHolder methodToProcess) {

View File

@ -76,8 +76,8 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator
String thisName = context.getParameterName(1);
String methodName = context.getParameterName(2);
writer.append("if").ws().append("(").append(thisName).ws().append("===").ws().append("null)").ws()
.append("return null;").softNewLine();
writer.append("if").ws().append("(typeof ").append(thisName).ws().append("!==").ws().append("\"function\")")
.ws().append("return ").append(thisName).append(";").softNewLine();
writer.append("var result").ws().append("=").ws().append("{};").softNewLine();
writer.append("result[").append(methodName).append("]").ws().append("=").ws().append(thisName)
.append(";").softNewLine();

View File

@ -462,7 +462,7 @@ class JSValueMarshaller {
return result;
}
private Variable unwrapFunctor(CallLocation location, Variable var, ClassReader type) {
Variable unwrapFunctor(CallLocation location, Variable var, ClassReader type) {
if (!isProperFunctor(type)) {
diagnostics.error(location, "Wrong functor: {{c0}}", type.getName());
return var;

View File

@ -83,6 +83,12 @@ public class FunctorTest {
assertEquals("q,w", function.foo("q", "w"));
}
@Test
public void castToFunctor() {
JSBiFunction f = getBiFunctionAsObject().cast();
assertEquals(23042, f.foo(23, 42));
}
@JSBody(params = { "f", "a", "b" }, script = "return '(' + f(a, b) + ')';")
private static native String testMethod(JSBiFunction f, int a, int b);
@ -95,6 +101,12 @@ public class FunctorTest {
+ "};")
private static native JSBiFunction getBiFunction();
@JSBody(script = ""
+ "return function(a, b) {"
+ "return a * 1000 + b;"
+ "};")
private static native JSObject getBiFunctionAsObject();
@JSBody(script = ""
+ "return function(a, b) {"
+ "return a + ',' + b;"