From 19930608249d40e5e1372acd9b057948d53b0e0d Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 15 Apr 2017 13:17:20 +0300 Subject: [PATCH] html4j: add toString method to enums passed to JavaScript via html4j API --- .../java/org/teavm/html4j/HTML4JPlugin.java | 5 +- .../html4j/JavaScriptBodyDependency.java | 44 +++++++++------ .../html4j/JavaScriptObjectEnhancer.java | 55 +++++++++++++++++++ .../teavm/html4j/test/JavaScriptTCKTest.java | 2 +- 4 files changed, 87 insertions(+), 19 deletions(-) create mode 100644 html4j/src/main/java/org/teavm/html4j/JavaScriptObjectEnhancer.java diff --git a/html4j/src/main/java/org/teavm/html4j/HTML4JPlugin.java b/html4j/src/main/java/org/teavm/html4j/HTML4JPlugin.java index 5b676ba9e..f9fe62c5f 100644 --- a/html4j/src/main/java/org/teavm/html4j/HTML4JPlugin.java +++ b/html4j/src/main/java/org/teavm/html4j/HTML4JPlugin.java @@ -25,10 +25,13 @@ public class HTML4JPlugin implements TeaVMPlugin { if (host.getExtension(TeaVMJavaScriptHost.class) == null) { return; } - host.add(new JavaScriptBodyDependency()); + + JavaScriptBodyDependency bodyDependency = new JavaScriptBodyDependency(); + host.add(bodyDependency); host.add(new JavaScriptBodyTransformer()); host.add(new JCLHacks()); host.getExtension(TeaVMJavaScriptHost.class).add(new JavaScriptResourceInterceptor()); + host.getExtension(TeaVMJavaScriptHost.class).add(new JavaScriptObjectEnhancer(bodyDependency)); } } diff --git a/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java b/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java index 4ac433212..e58bb6fe5 100644 --- a/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java +++ b/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java @@ -35,14 +35,11 @@ import org.teavm.model.ElementModifier; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; -/** - * - * @author Alexey Andreev - */ public class JavaScriptBodyDependency extends AbstractDependencyListener { private DependencyNode allClassesNode; - private Map> achievedMethods = new HashMap<>(); + private Map> reachedMethods = new HashMap<>(); @Override public void started(DependencyAgent agent) { @@ -50,9 +47,13 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener { allClassesNode.setTag("JavaScriptBody:global"); } - private static class OneDirectionalConnection implements DependencyConsumer { + public String[] getClassesPassedToJavaScript() { + return allClassesNode.getTypes(); + } + + static class OneDirectionalConnection implements DependencyConsumer { private DependencyNode target; - public OneDirectionalConnection(DependencyNode target) { + OneDirectionalConnection(DependencyNode target) { this.target = target; } @Override public void consume(DependencyType type) { @@ -71,14 +72,14 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener { @Override public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) { - Set methodsToAchieve = achievedMethods.get(method.getReference()); - if (methodsToAchieve != null) { - for (MethodReference methodToAchieve : methodsToAchieve) { - agent.linkMethod(methodToAchieve, location); + Set methodsToReach = reachedMethods.get(method.getReference()); + if (methodsToReach != null) { + for (MethodReference methodToReach : methodsToReach) { + agent.linkMethod(methodToReach, location); } return; } - achievedMethods.put(method.getReference(), new HashSet()); + reachedMethods.put(method.getReference(), new HashSet<>()); if (method.isMissing()) { return; } @@ -94,6 +95,15 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener { } for (int i = 0; i < method.getParameterCount(); ++i) { method.getVariable(i).connect(allClassesNode); + method.getVariable(i).addConsumer(type -> { + if (agent.getClassSource().isSuperType("java.lang.Enum", type.getName()).orElse(false)) { + MethodReference toStringMethod = new MethodReference(type.getName(), "toString", + ValueType.parse(String.class)); + MethodDependency dependency = agent.linkMethod(toStringMethod, location); + dependency.getVariable(0).propagate(type); + dependency.use(); + } + }); allClassesNode.addConsumer(new OneDirectionalConnection(method.getVariable(i).getArrayItem())); allClassesNode.addConsumer(new OneDirectionalConnection(method.getVariable(i).getArrayItem() .getArrayItem())); @@ -157,11 +167,11 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener { return true; } - private class GeneratorJsCallback extends JsCallback { + class GeneratorJsCallback extends JsCallback { private DependencyAgent agent; private MethodDependency caller; private CallLocation location; - public GeneratorJsCallback(DependencyAgent agent, MethodDependency caller, CallLocation location) { + GeneratorJsCallback(DependencyAgent agent, MethodDependency caller, CallLocation location) { this.agent = agent; this.caller = caller; this.location = location; @@ -171,7 +181,7 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener { MethodReader reader = findMethod(agent.getClassSource(), fqn, desc); MethodReference ref = reader != null ? reader.getReference() : new MethodReference(fqn, desc); MethodDependency methodDep = agent.linkMethod(ref, location); - achievedMethods.get(caller.getReference()).add(ref); + reachedMethods.get(caller.getReference()).add(ref); if (!methodDep.isMissing()) { if (reader.hasModifier(ElementModifier.STATIC) || reader.hasModifier(ElementModifier.FINAL)) { methodDep.use(); @@ -186,12 +196,12 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener { } } - private class VirtualCallbackConsumer implements DependencyConsumer { + class VirtualCallbackConsumer implements DependencyConsumer { private DependencyAgent agent; private MethodReader superMethod; private ClassReader superClass; private MethodDependency caller; - public VirtualCallbackConsumer(DependencyAgent agent, MethodReader superMethod, MethodDependency caller) { + VirtualCallbackConsumer(DependencyAgent agent, MethodReader superMethod, MethodDependency caller) { this.agent = agent; this.superMethod = superMethod; this.caller = caller; diff --git a/html4j/src/main/java/org/teavm/html4j/JavaScriptObjectEnhancer.java b/html4j/src/main/java/org/teavm/html4j/JavaScriptObjectEnhancer.java new file mode 100644 index 000000000..534d66269 --- /dev/null +++ b/html4j/src/main/java/org/teavm/html4j/JavaScriptObjectEnhancer.java @@ -0,0 +1,55 @@ +/* + * Copyright 2017 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.backend.javascript.codegen.SourceWriter; +import org.teavm.backend.javascript.rendering.RenderingManager; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; +import org.teavm.vm.BuildTarget; +import org.teavm.vm.spi.RendererListener; + +public class JavaScriptObjectEnhancer implements RendererListener { + private ClassReaderSource classSource; + private SourceWriter writer; + private JavaScriptBodyDependency dependencyListener; + + public JavaScriptObjectEnhancer(JavaScriptBodyDependency dependencyListener) { + this.dependencyListener = dependencyListener; + } + + @Override + public void begin(RenderingManager context, BuildTarget buildTarget) throws IOException { + classSource = context.getClassSource(); + writer = context.getWriter(); + } + + @Override + public void complete() throws IOException { + for (String className : dependencyListener.getClassesPassedToJavaScript()) { + if (classSource.isSuperType("java.lang.Enum", className).orElse(false)) { + MethodReference toStringMethod = new MethodReference(className, "toString", + ValueType.parse(String.class)); + writer.appendClass(className).append(".prototype.toString").ws().append("=").ws() + .append("function()").ws().append("{").indent().newLine(); + writer.append("return $rt_ustr(").appendMethodBody(toStringMethod).append("(this));").softNewLine(); + writer.outdent().append("};").softNewLine(); + } + } + } +} diff --git a/html4j/src/test/java/org/teavm/html4j/test/JavaScriptTCKTest.java b/html4j/src/test/java/org/teavm/html4j/test/JavaScriptTCKTest.java index 48022a357..9cd965d88 100644 --- a/html4j/src/test/java/org/teavm/html4j/test/JavaScriptTCKTest.java +++ b/html4j/src/test/java/org/teavm/html4j/test/JavaScriptTCKTest.java @@ -236,7 +236,7 @@ public class JavaScriptTCKTest extends JavaScriptBodyTest { @Test @Override public void toStringOfAnEnum() { - //super.toStringOfAnEnum(); + super.toStringOfAnEnum(); } @Test @Override