From ae2ca37ee0b3a03fea09c1ab7ab66c8111c452fa Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Mon, 17 Feb 2014 08:15:06 +0400 Subject: [PATCH] Adds type conversion from Java array and integer to JS array and number --- .../java/lang/StringNativeGenerator.java | 1 - .../teavm/classlib/java/lang/TInteger.java | 2 +- .../teavm/codegen/DefaultNamingStrategy.java | 3 +- .../teavm/html4j/JavaScriptBodyConverter.java | 32 ++++++ .../JavaScriptBodyConverterGenerator.java | 103 ++++++++++++++++++ .../html4j/JavaScriptBodyDependency.java | 10 ++ .../teavm/html4j/JavaScriptBodyGenerator.java | 13 ++- .../test/JavaScriptBodyConversionTests.java | 34 ++++++ .../html4j/test/JavaScriptBodyTests.java | 14 +++ 9 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverter.java create mode 100644 teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverterGenerator.java create mode 100644 teavm-html4j/src/test/java/org/teavm/html4j/test/JavaScriptBodyConversionTests.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java index d54ff12ab..69bf0e7a7 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java @@ -25,7 +25,6 @@ import org.teavm.model.MethodReference; * @author Alexey Andreev */ public class StringNativeGenerator implements Injector { - @Override public void generate(InjectorContext context, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TInteger.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TInteger.java index d1ea4eea3..3e2caaa36 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TInteger.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TInteger.java @@ -123,7 +123,7 @@ public class TInteger extends TNumber implements TComparable { if (integerCache == null) { integerCache = new TInteger[256]; for (int j = 0; j < integerCache.length; ++j) { - integerCache[j - 128] = new TInteger(j); + integerCache[j] = new TInteger(j - 128); } } } diff --git a/teavm-core/src/main/java/org/teavm/codegen/DefaultNamingStrategy.java b/teavm-core/src/main/java/org/teavm/codegen/DefaultNamingStrategy.java index 225eeea3d..2a9684fc5 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/DefaultNamingStrategy.java +++ b/teavm-core/src/main/java/org/teavm/codegen/DefaultNamingStrategy.java @@ -57,9 +57,10 @@ public class DefaultNamingStrategy implements NamingStrategy { @Override public String getNameFor(MethodReference method) { + MethodReference origMethod = method; method = getRealMethod(method); if (method == null) { - throw new NamingException("Can't provide name for method as it was not found: " + method); + throw new NamingException("Can't provide name for method as it was not found: " + origMethod); } ClassHolder clsHolder = classSource.get(method.getClassName()); MethodHolder methodHolder = clsHolder.getMethod(method.getDescriptor()); diff --git a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverter.java b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverter.java new file mode 100644 index 000000000..9b2dbef12 --- /dev/null +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverter.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.html4j; + +import org.teavm.dependency.PluggableDependency; +import org.teavm.javascript.ni.GeneratedBy; + +/** + * + * @author Alexey Andreev + */ +public final class JavaScriptBodyConverter { + private JavaScriptBodyConverter() { + } + + @GeneratedBy(JavaScriptBodyConverterGenerator.class) + @PluggableDependency(JavaScriptBodyConverterGenerator.class) + public static native Object toJavaScript(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 new file mode 100644 index 000000000..3e010b73f --- /dev/null +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyConverterGenerator.java @@ -0,0 +1,103 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +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.*; + +/** + * + * @author Alexey Andreev + */ +public class JavaScriptBodyConverterGenerator implements Generator, DependencyPlugin { + private 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); + } + } + }); + } + + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { + switch (methodRef.getName()) { + case "toJavaScript": + generateToJavaScript(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("{") + .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("}"); + 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(); + } + + private void generateStringToJavaScript(GeneratorContext context, SourceWriter writer) throws IOException { + FieldReference charsField = new FieldReference("java.lang.String", "characters"); + writer.append("var result = \"\";").softNewLine(); + writer.append("var data = ").append(context.getParameterName(1)).append('.') + .appendField(charsField).append(".data;").softNewLine(); + writer.append("for (var i = 0; i < data.length; i = (i + 1) | 0) {").indent().softNewLine(); + writer.append("result += String.fromCharCode(data[i]);").softNewLine(); + writer.outdent().append("}").softNewLine(); + writer.append("return result;").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 ae6eb87ef..136a2a376 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java @@ -73,6 +73,16 @@ 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)); + } + } } } 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 d56bd9f2d..9a7c6d512 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java @@ -53,11 +53,22 @@ public class JavaScriptBodyGenerator implements Generator { writer.outdent().append("}).call(").append(context.getParameterName(0)); for (int i = 0; i < args.size(); ++i) { writer.append(",").ws(); - writer.append(context.getParameterName(i + 1)); + wrapParameter(writer, methodRef.getDescriptor().parameterType(i), context.getParameterName(i + 1)); } 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.append("(").append(param).append(")"); + } else { + writer.append(param); + } + } + private static class GeneratorJsCallback extends JsCallback { private ClassReaderSource classSource; private NamingStrategy naming; diff --git a/teavm-html4j/src/test/java/org/teavm/html4j/test/JavaScriptBodyConversionTests.java b/teavm-html4j/src/test/java/org/teavm/html4j/test/JavaScriptBodyConversionTests.java new file mode 100644 index 000000000..b64d34f37 --- /dev/null +++ b/teavm-html4j/src/test/java/org/teavm/html4j/test/JavaScriptBodyConversionTests.java @@ -0,0 +1,34 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.html4j.test; + +import static org.junit.Assert.*; +import net.java.html.js.JavaScriptBody; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +public class JavaScriptBodyConversionTests { + @Test + public void convertsInteger() { + assertEquals(23, returnAsInt(23)); + } + + @JavaScriptBody(args = { "value" }, body = "return value;") + private native int returnAsInt(Object value); +} 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 b0a4b44d0..30e96d3cb 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 @@ -64,6 +64,11 @@ public class JavaScriptBodyTests { assertEquals(23, invokeCallback(a)); } + @Test + public void staticCallbackInvoked() { + assertEquals(23, invokeStaticCallback(new AImpl())); + } + private static class AImpl implements A { @Override public int foo() { return 23; @@ -90,4 +95,13 @@ public class JavaScriptBodyTests { "@org.teavm.html4j.test.B::bar(" + "Lorg/teavm/html4j/test/A;)(_global_)", javacall = true) private native int invokeCallback(B callback); + + public static int staticCallback(A a) { + return a.foo(); + } + + @JavaScriptBody(args = { "a" }, body = "return " + + "@org.teavm.html4j.test.JavaScriptBodyTests::staticCallback(" + + "Lorg/teavm/html4j/test/A;)(a)", javacall = true) + private native int invokeStaticCallback(A a); }