diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java index 48d45c0da..e9b4b7c00 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java @@ -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,25 +253,55 @@ class JSClassProcessor { for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlock block = program.basicBlockAt(i); for (Instruction insn : block) { - if (!(insn instanceof InvokeInstruction)) { - continue; - } - InvokeInstruction invoke = (InvokeInstruction) insn; + 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()); - if (method == null) { - continue; - } - CallLocation callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation()); - replacement.clear(); - if (processInvocation(method, callLocation, invoke, methodToProcess)) { - insn.insertNextAll(replacement); - insn.delete(); + MethodReader method = getMethod(invoke.getMethod()); + if (method == null) { + continue; + } + CallLocation callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation()); + replacement.clear(); + if (processInvocation(method, callLocation, invoke, methodToProcess)) { + insn.insertNextAll(replacement); + insn.delete(); + } } } } } + 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) { if (method.getAnnotations().get(JSBody.class.getName()) != null) { diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSNativeGenerator.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSNativeGenerator.java index 6204f7b26..63fe9de6d 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSNativeGenerator.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSNativeGenerator.java @@ -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(); diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSValueMarshaller.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSValueMarshaller.java index eba3eb5f5..6368b51d1 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSValueMarshaller.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSValueMarshaller.java @@ -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; diff --git a/tests/src/test/java/org/teavm/jso/test/FunctorTest.java b/tests/src/test/java/org/teavm/jso/test/FunctorTest.java index 95d5b6e80..09b78b905 100644 --- a/tests/src/test/java/org/teavm/jso/test/FunctorTest.java +++ b/tests/src/test/java/org/teavm/jso/test/FunctorTest.java @@ -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;"