diff --git a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js index 3e3800468..02f6e9043 100644 --- a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js +++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js @@ -43,6 +43,10 @@ $rt_createArray = function(cls, sz) { } return arr; } +$rt_wrapArray = function(cls, data) { + var arr = new ($rt_arraycls(cls))(data); + return arr; +} $rt_createUnfilledArray = function(cls, sz) { return new ($rt_arraycls(cls))(new Array(sz)); } diff --git a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverter.java b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverter.java index 9b2dbef12..1dc0d0fdd 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverter.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverter.java @@ -15,7 +15,6 @@ */ package org.teavm.html4j; -import org.teavm.dependency.PluggableDependency; import org.teavm.javascript.ni.GeneratedBy; /** @@ -27,6 +26,8 @@ public final class JavaScriptBodyConverter { } @GeneratedBy(JavaScriptBodyConverterGenerator.class) - @PluggableDependency(JavaScriptBodyConverterGenerator.class) public static native Object toJavaScript(Object obj); + + @GeneratedBy(JavaScriptBodyConverterGenerator.class) + public static native Object fromJavaScript(Object obj); } diff --git a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverterGenerator.java b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverterGenerator.java index 3e010b73f..930695608 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverterGenerator.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverterGenerator.java @@ -17,10 +17,6 @@ package org.teavm.html4j; import java.io.IOException; import org.teavm.codegen.SourceWriter; -import org.teavm.dependency.DependencyChecker; -import org.teavm.dependency.DependencyConsumer; -import org.teavm.dependency.DependencyPlugin; -import org.teavm.dependency.MethodGraph; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; import org.teavm.model.*; @@ -29,29 +25,15 @@ import org.teavm.model.*; * * @author Alexey Andreev */ -public class JavaScriptBodyConverterGenerator implements Generator, DependencyPlugin { - private static final MethodReference intValueMethod = new MethodReference("java.lang.Integer", +public class JavaScriptBodyConverterGenerator implements Generator { + private static final String convCls = JavaScriptBodyConverter.class.getName(); + static final MethodReference intValueMethod = new MethodReference("java.lang.Integer", new MethodDescriptor("intValue", ValueType.INTEGER)); - - @Override - public void methodAchieved(DependencyChecker checker, MethodReference method) { - switch (method.getName()) { - case "toJavaScript": - achieveToJavaScript(checker, method); - break; - } - } - - private void achieveToJavaScript(final DependencyChecker checker, MethodReference method) { - MethodGraph graph = checker.attachMethodGraph(method); - graph.getVariable(1).addConsumer(new DependencyConsumer() { - @Override public void consume(String type) { - if (type.equals("java.lang.Integer")) { - checker.attachMethodGraph(intValueMethod); - } - } - }); - } + private static final ValueType objType = ValueType.object("java.lang.Object"); + static final MethodReference toJsMethod = new MethodReference(convCls, new MethodDescriptor( + "toJavaScript", objType, objType)); + static final MethodReference fromJsMethod = new MethodReference(convCls, new MethodDescriptor( + "fromJavaScript", objType, objType)); @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { @@ -59,32 +41,52 @@ public class JavaScriptBodyConverterGenerator implements Generator, DependencyPl case "toJavaScript": generateToJavaScript(context, writer); break; + case "fromJavaScript": + generateFromJavaScript(context, writer); + break; } } private void generateToJavaScript(GeneratorContext context, SourceWriter writer) throws IOException { - ClassReaderSource classSource = context.getClassSource(); String obj = context.getParameterName(1); - writer.append("if").ws().append("(").append(obj).ws().append("===").ws().append("null)").ws().append("{") + writer.append("if (" + obj + " === null) {").softNewLine().indent(); + writer.append("return null;").softNewLine(); + writer.outdent().append("} else if (" + obj + ".constructor.$meta.item) {").indent().softNewLine(); + writer.append("var arr = new Array(" + obj + ".data.length);").softNewLine(); + writer.append("for (var i = 0; i < arr.length; ++i) {").indent().softNewLine(); + writer.append("arr[i] = ").appendMethodBody(toJsMethod).append("(" + obj + ".data[i]);").softNewLine(); + writer.outdent().append("}").softNewLine(); + writer.append("return arr;").softNewLine(); + writer.outdent().append("}"); + writer.append(" else if (" + obj + ".constructor === ").appendClass("java.lang.String") + .append(") {").indent().softNewLine(); + generateStringToJavaScript(context, writer); + writer.outdent().append("} else if (" + obj + ".constructor === ").appendClass("java.lang.Integer") + .append(") {").indent().softNewLine(); + writer.append("return ").appendMethodBody(intValueMethod).append("(" + obj + ");").softNewLine(); + writer.outdent().append("}"); + writer.append(" else {").indent().softNewLine(); + writer.append("return " + obj + ";").softNewLine(); + writer.outdent().append("}").softNewLine(); + } + + private void generateFromJavaScript(GeneratorContext context, SourceWriter writer) throws IOException { + String obj = context.getParameterName(1); + writer.append("if (" + obj +" === null || " + obj + " === undefined)").ws().append("{") .softNewLine().indent(); writer.append("return null;").softNewLine(); - writer.outdent().append("}").ws().append("else if").ws().append('(').append(obj) - .append(".constructor.$meta.item)").ws().append("{").indent().softNewLine(); - writer.append("return ").append(obj).append(".data;").softNewLine(); + writer.outdent().append("} else if (" + obj + " instanceof Array) {").indent().softNewLine(); + writer.append("var arr = $rt_createArray($rt_objcls(), " + obj + ".length);").softNewLine(); + writer.append("for (var i = 0; i < arr.data.length; ++i) {").indent().softNewLine(); + writer.append("arr.data[i] = ").appendMethodBody(fromJsMethod).append("(" + obj + "[i]);") + .softNewLine(); + writer.outdent().append("}").softNewLine(); + writer.append("return arr;").softNewLine(); + writer.outdent().append("}"); + writer.append(" else if (" + obj + ".constructor === ").appendClass("java.lang.String") + .append(") {").indent().softNewLine(); + writer.append("return $rt_str(" + obj + ");").softNewLine(); writer.outdent().append("}"); - if (classSource.get("java.lang.String") != null) { - writer.ws().append("else if").ws().append("(").append(obj).append(".constructor").ws().append("===").ws() - .appendClass("java.lang.String").append(")").ws().append("{").indent().softNewLine(); - generateStringToJavaScript(context, writer); - writer.outdent().append("}"); - } - if (classSource.get("java.lang.Integer") != null) { - writer.ws().append("else if").ws().append("(").append(obj).append(".constructor").ws().append("===").ws() - .appendClass("java.lang.Integer").append(")").ws().append("{").indent().softNewLine(); - writer.append("return ").appendMethodBody(intValueMethod).append("(").append(obj) - .append(");").softNewLine(); - writer.outdent().append("}"); - } writer.ws().append("else").ws().append("{").indent().softNewLine(); writer.append("return ").append(obj).append(";").softNewLine(); writer.outdent().append("}").softNewLine(); diff --git a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java index 136a2a376..ded276f45 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java @@ -30,8 +30,6 @@ public class JavaScriptBodyDependency implements DependencyListener { public void started(DependencyChecker dependencyChecker) { allClassesNode = dependencyChecker.createNode(); allClassesNode.setTag("JavaScriptBody:global"); - allClassesNode.getArrayItem().addConsumer(new OneDirectionalConnection(allClassesNode)); - allClassesNode.getArrayItem().getArrayItem().addConsumer(new OneDirectionalConnection(allClassesNode)); } private static class OneDirectionalConnection implements DependencyConsumer { @@ -55,6 +53,7 @@ public class JavaScriptBodyDependency implements DependencyListener { MethodHolder method = cls.getMethod(methodRef.getDescriptor()); AnnotationReader annot = method.getAnnotations().get(JavaScriptBody.class.getName()); if (annot != null) { + includeDefaultDependencies(dependencyChecker); AnnotationValue javacall = annot.getValue("javacall"); MethodGraph graph = dependencyChecker.attachMethodGraph(methodRef); if (graph.getResult() != null) { @@ -73,19 +72,15 @@ public class JavaScriptBodyDependency implements DependencyListener { String body = annot.getValue("body").getString(); new GeneratorJsCallback(dependencyChecker.getClassSource(), dependencyChecker).parse(body); } - for (int i = 0; i < methodRef.parameterCount(); ++i) { - ValueType type = methodRef.getDescriptor().parameterType(i); - if (type.isObject("java.lang.Object")) { - MethodGraph convGraph = dependencyChecker.attachMethodGraph(new MethodReference( - JavaScriptBodyConverter.class.getName(), - new MethodDescriptor("toJavaScript", ValueType.object("java.lang.Object"), - ValueType.object("java.lang.Object")))); - graph.getVariable(i + 1).connect(convGraph.getVariable(i + 1)); - } - } } } + private void includeDefaultDependencies(DependencyChecker dependencyChecker) { + dependencyChecker.attachMethodGraph(JavaScriptBodyConverterGenerator.fromJsMethod); + dependencyChecker.attachMethodGraph(JavaScriptBodyConverterGenerator.toJsMethod); + dependencyChecker.attachMethodGraph(JavaScriptBodyConverterGenerator.intValueMethod); + } + @Override public void fieldAchieved(DependencyChecker dependencyChecker, FieldReference field) { } diff --git a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java index 9a7c6d512..88c7677b2 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java @@ -41,7 +41,7 @@ public class JavaScriptBodyGenerator implements Generator { GeneratorJsCallback callbackGen = new GeneratorJsCallback(context.getClassSource(), writer.getNaming()); body = callbackGen.parse(body); } - writer.append("return (function("); + writer.append("var result = (function("); for (int i = 0; i < args.size(); ++i) { if (i > 0) { writer.append(",").ws(); @@ -55,14 +55,24 @@ public class JavaScriptBodyGenerator implements Generator { writer.append(",").ws(); wrapParameter(writer, methodRef.getDescriptor().parameterType(i), context.getParameterName(i + 1)); } - writer.append(");").softNewLine(); + writer.append(")").softNewLine(); + writer.append("return "); + unwrapValue(writer, method.getResultType(), "result"); + writer.append(";").softNewLine(); } private void wrapParameter(SourceWriter writer, ValueType type, String param) throws IOException { if (type.isObject("java.lang.Object")) { - writer.appendMethodBody(new MethodReference(JavaScriptBodyConverter.class.getName(), - new MethodDescriptor("toJavaScript", ValueType.object("java.lang.Object"), - ValueType.object("java.lang.Object")))); + writer.appendMethodBody(JavaScriptBodyConverterGenerator.toJsMethod); + writer.append("(").append(param).append(")"); + } else { + writer.append(param); + } + } + + private void unwrapValue(SourceWriter writer, ValueType type, String param) throws IOException { + if (type.isObject("java.lang.Object")) { + writer.appendMethodBody(JavaScriptBodyConverterGenerator.fromJsMethod); writer.append("(").append(param).append(")"); } else { writer.append(param); diff --git a/teavm-html4j/src/test/java/org/teavm/html4j/test/JavaScriptBodyTests.java b/teavm-html4j/src/test/java/org/teavm/html4j/test/JavaScriptBodyTests.java index 30e96d3cb..a49ab769a 100644 --- a/teavm-html4j/src/test/java/org/teavm/html4j/test/JavaScriptBodyTests.java +++ b/teavm-html4j/src/test/java/org/teavm/html4j/test/JavaScriptBodyTests.java @@ -49,15 +49,6 @@ public class JavaScriptBodyTests { assertEquals(23, array[0].foo()); } - @Test - public void dependencyPropagatedBackThroughArray() { - A[] first = new A[1]; - storeObject(first); - Object[] second = (Object[])retrieveObject(); - second[0] = new AImpl(); - assertEquals(23, first[0].foo()); - } - @Test public void valuePropagatedToCallback() { A a = new AImpl();