html4j: add toString method to enums passed to JavaScript via html4j API

This commit is contained in:
Alexey Andreev 2017-04-15 13:17:20 +03:00
parent c4ada881d2
commit 1993060824
4 changed files with 87 additions and 19 deletions

View File

@ -25,10 +25,13 @@ public class HTML4JPlugin implements TeaVMPlugin {
if (host.getExtension(TeaVMJavaScriptHost.class) == null) { if (host.getExtension(TeaVMJavaScriptHost.class) == null) {
return; return;
} }
host.add(new JavaScriptBodyDependency());
JavaScriptBodyDependency bodyDependency = new JavaScriptBodyDependency();
host.add(bodyDependency);
host.add(new JavaScriptBodyTransformer()); host.add(new JavaScriptBodyTransformer());
host.add(new JCLHacks()); host.add(new JCLHacks());
host.getExtension(TeaVMJavaScriptHost.class).add(new JavaScriptResourceInterceptor()); host.getExtension(TeaVMJavaScriptHost.class).add(new JavaScriptResourceInterceptor());
host.getExtension(TeaVMJavaScriptHost.class).add(new JavaScriptObjectEnhancer(bodyDependency));
} }
} }

View File

@ -35,14 +35,11 @@ import org.teavm.model.ElementModifier;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
/**
*
* @author Alexey Andreev
*/
public class JavaScriptBodyDependency extends AbstractDependencyListener { public class JavaScriptBodyDependency extends AbstractDependencyListener {
private DependencyNode allClassesNode; private DependencyNode allClassesNode;
private Map<MethodReference, Set<MethodReference>> achievedMethods = new HashMap<>(); private Map<MethodReference, Set<MethodReference>> reachedMethods = new HashMap<>();
@Override @Override
public void started(DependencyAgent agent) { public void started(DependencyAgent agent) {
@ -50,9 +47,13 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener {
allClassesNode.setTag("JavaScriptBody:global"); allClassesNode.setTag("JavaScriptBody:global");
} }
private static class OneDirectionalConnection implements DependencyConsumer { public String[] getClassesPassedToJavaScript() {
return allClassesNode.getTypes();
}
static class OneDirectionalConnection implements DependencyConsumer {
private DependencyNode target; private DependencyNode target;
public OneDirectionalConnection(DependencyNode target) { OneDirectionalConnection(DependencyNode target) {
this.target = target; this.target = target;
} }
@Override public void consume(DependencyType type) { @Override public void consume(DependencyType type) {
@ -71,14 +72,14 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener {
@Override @Override
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) { public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
Set<MethodReference> methodsToAchieve = achievedMethods.get(method.getReference()); Set<MethodReference> methodsToReach = reachedMethods.get(method.getReference());
if (methodsToAchieve != null) { if (methodsToReach != null) {
for (MethodReference methodToAchieve : methodsToAchieve) { for (MethodReference methodToReach : methodsToReach) {
agent.linkMethod(methodToAchieve, location); agent.linkMethod(methodToReach, location);
} }
return; return;
} }
achievedMethods.put(method.getReference(), new HashSet<MethodReference>()); reachedMethods.put(method.getReference(), new HashSet<>());
if (method.isMissing()) { if (method.isMissing()) {
return; return;
} }
@ -94,6 +95,15 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener {
} }
for (int i = 0; i < method.getParameterCount(); ++i) { for (int i = 0; i < method.getParameterCount(); ++i) {
method.getVariable(i).connect(allClassesNode); 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()));
allClassesNode.addConsumer(new OneDirectionalConnection(method.getVariable(i).getArrayItem() allClassesNode.addConsumer(new OneDirectionalConnection(method.getVariable(i).getArrayItem()
.getArrayItem())); .getArrayItem()));
@ -157,11 +167,11 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener {
return true; return true;
} }
private class GeneratorJsCallback extends JsCallback { class GeneratorJsCallback extends JsCallback {
private DependencyAgent agent; private DependencyAgent agent;
private MethodDependency caller; private MethodDependency caller;
private CallLocation location; private CallLocation location;
public GeneratorJsCallback(DependencyAgent agent, MethodDependency caller, CallLocation location) { GeneratorJsCallback(DependencyAgent agent, MethodDependency caller, CallLocation location) {
this.agent = agent; this.agent = agent;
this.caller = caller; this.caller = caller;
this.location = location; this.location = location;
@ -171,7 +181,7 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener {
MethodReader reader = findMethod(agent.getClassSource(), fqn, desc); MethodReader reader = findMethod(agent.getClassSource(), fqn, desc);
MethodReference ref = reader != null ? reader.getReference() : new MethodReference(fqn, desc); MethodReference ref = reader != null ? reader.getReference() : new MethodReference(fqn, desc);
MethodDependency methodDep = agent.linkMethod(ref, location); MethodDependency methodDep = agent.linkMethod(ref, location);
achievedMethods.get(caller.getReference()).add(ref); reachedMethods.get(caller.getReference()).add(ref);
if (!methodDep.isMissing()) { if (!methodDep.isMissing()) {
if (reader.hasModifier(ElementModifier.STATIC) || reader.hasModifier(ElementModifier.FINAL)) { if (reader.hasModifier(ElementModifier.STATIC) || reader.hasModifier(ElementModifier.FINAL)) {
methodDep.use(); methodDep.use();
@ -186,12 +196,12 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener {
} }
} }
private class VirtualCallbackConsumer implements DependencyConsumer { class VirtualCallbackConsumer implements DependencyConsumer {
private DependencyAgent agent; private DependencyAgent agent;
private MethodReader superMethod; private MethodReader superMethod;
private ClassReader superClass; private ClassReader superClass;
private MethodDependency caller; private MethodDependency caller;
public VirtualCallbackConsumer(DependencyAgent agent, MethodReader superMethod, MethodDependency caller) { VirtualCallbackConsumer(DependencyAgent agent, MethodReader superMethod, MethodDependency caller) {
this.agent = agent; this.agent = agent;
this.superMethod = superMethod; this.superMethod = superMethod;
this.caller = caller; this.caller = caller;

View File

@ -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();
}
}
}
}

View File

@ -236,7 +236,7 @@ public class JavaScriptTCKTest extends JavaScriptBodyTest {
@Test @Override @Test @Override
public void toStringOfAnEnum() { public void toStringOfAnEnum() {
//super.toStringOfAnEnum(); super.toStringOfAnEnum();
} }
@Test @Override @Test @Override