From 48265c446fff36404ba22798ac4446e22ec63ede Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 7 Feb 2014 23:30:31 +0400 Subject: [PATCH] Adds possibility to implement JSObject ant pass it as a callback to a method of another JSObject --- .../dependency/DependencyGraphBuilder.java | 2 +- .../java/org/teavm/javascript/Decompiler.java | 7 ++- .../javascript/JavascriptNativeProcessor.java | 57 ++++++++++--------- .../JavascriptProcessedClassSource.java | 11 +++- .../NativeJavascriptClassRepository.java | 44 ++++++++++++++ .../java/org/teavm/javascript/Renderer.java | 25 +++----- .../org/teavm/javascript/ast/MethodNode.java | 9 +++ .../main/java/org/teavm/javascript/ni/JS.java | 13 +++++ .../javascript/ni/JSNativeGenerator.java | 29 +++++++++- .../javascript/ni/PreserveOriginalName.java | 15 +++++ .../main/java/org/teavm/parsing/Parser.java | 3 + .../org/teavm/parsing/SSATransformer.java | 3 + .../org/teavm/dom/events/EventListener.java | 3 +- .../java/org/teavm/samples/HelloWorld.java | 20 +++++++ 14 files changed, 193 insertions(+), 48 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/javascript/NativeJavascriptClassRepository.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/ni/PreserveOriginalName.java diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index 6c4a192b6..cf0cd4938 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -144,7 +144,7 @@ class DependencyGraphBuilder { if (insn.getInstance() != null) { nodes[insn.getInstance().getIndex()].connect(targetParams[0]); } - if (targetGraph.getResultNode() != null) { + if (targetGraph.getResultNode() != null && insn.getReceiver() != null) { targetGraph.getResultNode().connect(nodes[insn.getReceiver().getIndex()]); } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index aa3120576..63cec5cb2 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -21,6 +21,7 @@ import org.teavm.javascript.ast.*; import org.teavm.javascript.ni.GeneratedBy; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.InjectedBy; +import org.teavm.javascript.ni.PreserveOriginalName; import org.teavm.model.*; import org.teavm.model.util.ProgramUtils; @@ -121,7 +122,11 @@ public class Decompiler { if (method.getAnnotations().get(InjectedBy.class.getName()) != null) { continue; } - clsNode.getMethods().add(decompile(method)); + MethodNode methodNode = decompile(method); + clsNode.getMethods().add(methodNode); + if (method.getAnnotations().get(PreserveOriginalName.class.getName()) != null) { + methodNode.setOriginalNamePreserved(true); + } } clsNode.getInterfaces().addAll(cls.getInterfaces()); clsNode.getModifiers().addAll(mapModifiers(cls.getModifiers())); diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptNativeProcessor.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptNativeProcessor.java index 0d5cb4b0d..b40fedb49 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/JavascriptNativeProcessor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptNativeProcessor.java @@ -26,13 +26,38 @@ import org.teavm.model.instructions.*; */ class JavascriptNativeProcessor { private ClassHolderSource classSource; - private Map knownJavaScriptClasses = new HashMap<>(); private Program program; private List replacement = new ArrayList<>(); + private NativeJavascriptClassRepository nativeRepos; public JavascriptNativeProcessor(ClassHolderSource classSource) { this.classSource = classSource; - knownJavaScriptClasses.put(JSObject.class.getName(), true); + nativeRepos = new NativeJavascriptClassRepository(classSource); + } + + public void processClass(ClassHolder cls) { + Set preservedMethods = new HashSet<>(); + for (String iface : cls.getInterfaces()) { + if (nativeRepos.isJavaScriptClass(iface)) { + addPreservedMethods(iface, preservedMethods); + } + } + for (MethodHolder method : cls.getMethods()) { + if (preservedMethods.contains(method.getDescriptor()) && + method.getAnnotations().get(PreserveOriginalName.class.getName()) == null) { + method.getAnnotations().add(new AnnotationHolder(PreserveOriginalName.class.getName())); + } + } + } + + private void addPreservedMethods(String ifaceName, Set methods) { + ClassHolder iface = classSource.getClassHolder(ifaceName); + for (MethodHolder method : iface.getMethods()) { + methods.add(method.getDescriptor()); + } + for (String superIfaceName : iface.getInterfaces()) { + addPreservedMethods(superIfaceName, methods); + } } public void processProgram(Program program) { @@ -46,7 +71,7 @@ class JavascriptNativeProcessor { continue; } InvokeInstruction invoke = (InvokeInstruction)insn; - if (!isJavaScriptClass(invoke.getMethod().getClassName())) { + if (!nativeRepos.isJavaScriptClass(invoke.getMethod().getClassName())) { continue; } replacement.clear(); @@ -86,7 +111,7 @@ class JavascriptNativeProcessor { "a proper native JavaScript indexer declaration"); } } else { - if (!isSupportedType(method.getResultType())) { + if (method.getResultType() != ValueType.VOID && !isSupportedType(method.getResultType())) { throw new RuntimeException("Method " + invoke.getMethod() + " is not " + "a proper native JavaScript method declaration"); } @@ -258,28 +283,6 @@ class JavascriptNativeProcessor { return result; } - private boolean isJavaScriptClass(String className) { - Boolean known = knownJavaScriptClasses.get(className); - if (known == null) { - known = exploreIfJavaScriptClass(className); - knownJavaScriptClasses.put(className, known); - } - return known; - } - - private boolean exploreIfJavaScriptClass(String className) { - ClassHolder cls = classSource.getClassHolder(className); - if (cls == null || !cls.getModifiers().contains(ElementModifier.INTERFACE)) { - return false; - } - for (String iface : cls.getInterfaces()) { - if (isJavaScriptClass(iface)) { - return true; - } - } - return false; - } - private MethodHolder getMethod(MethodReference ref) { ClassHolder cls = classSource.getClassHolder(ref.getClassName()); MethodHolder method = cls.getMethod(ref.getDescriptor()); @@ -359,7 +362,7 @@ class JavascriptNativeProcessor { return isSupportedType(((ValueType.Array)type).getItemType()); } else if (type instanceof ValueType.Object) { String typeName = ((ValueType.Object)type).getClassName(); - return typeName.equals("java.lang.String") || isJavaScriptClass(typeName); + return typeName.equals("java.lang.String") || nativeRepos.isJavaScriptClass(typeName); } else { return false; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptProcessedClassSource.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptProcessedClassSource.java index 6321d9ec1..60749f5c4 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/JavascriptProcessedClassSource.java +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptProcessedClassSource.java @@ -24,6 +24,7 @@ import org.teavm.model.MethodHolder; * @author Alexey Andreev */ public class JavascriptProcessedClassSource implements ClassHolderSource { + private ThreadLocal processor = new ThreadLocal<>(); private ClassHolderSource innerSource; public JavascriptProcessedClassSource(ClassHolderSource innerSource) { @@ -40,11 +41,19 @@ public class JavascriptProcessedClassSource implements ClassHolderSource { } private void transformClass(ClassHolder cls) { - JavascriptNativeProcessor processor = new JavascriptNativeProcessor(innerSource); + JavascriptNativeProcessor processor = getProcessor(); + processor.processClass(cls); for (MethodHolder method : cls.getMethods()) { if (method.getProgram() != null) { processor.processProgram(method.getProgram()); } } } + + private JavascriptNativeProcessor getProcessor() { + if (processor.get() == null) { + processor.set(new JavascriptNativeProcessor(innerSource)); + } + return processor.get(); + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/NativeJavascriptClassRepository.java b/teavm-core/src/main/java/org/teavm/javascript/NativeJavascriptClassRepository.java new file mode 100644 index 000000000..1b69c98b1 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/NativeJavascriptClassRepository.java @@ -0,0 +1,44 @@ +package org.teavm.javascript; + +import java.util.HashMap; +import java.util.Map; +import org.teavm.javascript.ni.JSObject; +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderSource; +import org.teavm.model.ElementModifier; + +/** + * + * @author Alexey Andreev + */ +class NativeJavascriptClassRepository { + private ClassHolderSource classSource; + private Map knownJavaScriptClasses = new HashMap<>(); + + public NativeJavascriptClassRepository(ClassHolderSource classSource) { + this.classSource = classSource; + knownJavaScriptClasses.put(JSObject.class.getName(), true); + } + + public boolean isJavaScriptClass(String className) { + Boolean known = knownJavaScriptClasses.get(className); + if (known == null) { + known = figureOutIfJavaScriptClass(className); + knownJavaScriptClasses.put(className, known); + } + return known; + } + + private boolean figureOutIfJavaScriptClass(String className) { + ClassHolder cls = classSource.getClassHolder(className); + if (cls == null || !cls.getModifiers().contains(ElementModifier.INTERFACE)) { + return false; + } + for (String iface : cls.getInterfaces()) { + if (isJavaScriptClass(iface)) { + return true; + } + } + return false; + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index 6766736f7..57e579f9c 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -290,15 +290,8 @@ public class Renderer implements ExprVisitor, StatementVisitor { if (ref.getDescriptor().getName().equals("")) { renderInitializer(method); } - int startParam = 0; - if (method.getModifiers().contains(NodeModifier.STATIC)) { - startParam = 1; - } - writer.appendClass(ref.getClassName()).append('.'); - if (startParam == 0) { - writer.append("prototype."); - } - writer.appendMethod(ref).ws().append("=").ws().append("function("); + writer.appendClass(ref.getClassName()).append(".prototype.").appendMethod(ref) + .ws().append("=").ws().append("function("); for (int i = 1; i <= ref.parameterCount(); ++i) { if (i > 1) { writer.append(", "); @@ -307,17 +300,17 @@ public class Renderer implements ExprVisitor, StatementVisitor { } writer.append(")").ws().append("{").softNewLine().indent(); writer.append("return ").appendMethodBody(ref).append("("); - if (startParam == 0) { - writer.append("this"); - } + writer.append("this"); for (int i = 1; i <= ref.parameterCount(); ++i) { - if (i > 1 || startParam == 0) { - writer.append(",").ws(); - } - writer.append(variableName(i)); + writer.append(",").ws().append(variableName(i)); } writer.append(");").softNewLine(); writer.outdent().append("}").newLine(); + if (method.isOriginalNamePreserved()) { + writer.appendClass(ref.getClassName()).append(".prototype.").append(ref.getName()).ws().append("=") + .ws().appendClass(ref.getClassName()).append(".prototype.").appendMethod(ref) + .append(';').newLine(); + } } catch (NamingException e) { throw new RenderingException("Error rendering method " + method.getReference() + ". " + "See cause for details", e); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/MethodNode.java b/teavm-core/src/main/java/org/teavm/javascript/ast/MethodNode.java index aeb47d5c6..ba1e8653b 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/MethodNode.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/MethodNode.java @@ -26,6 +26,7 @@ import org.teavm.model.MethodReference; public abstract class MethodNode { private MethodReference reference; private Set modifiers = EnumSet.noneOf(NodeModifier.class); + private boolean originalNamePreserved; public MethodNode(MethodReference reference) { this.reference = reference; @@ -40,5 +41,13 @@ public abstract class MethodNode { return modifiers; } + public boolean isOriginalNamePreserved() { + return originalNamePreserved; + } + + public void setOriginalNamePreserved(boolean originalNamePreserved) { + this.originalNamePreserved = originalNamePreserved; + } + public abstract void acceptVisitor(MethodNodeVisitor visitor); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/JS.java b/teavm-core/src/main/java/org/teavm/javascript/ni/JS.java index 84a43546f..c08fcf3b6 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/JS.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/JS.java @@ -16,6 +16,7 @@ package org.teavm.javascript.ni; import java.util.Iterator; +import org.teavm.dependency.PluggableDependency; /** * @@ -66,6 +67,9 @@ public final class JS { @InjectedBy(JSNativeGenerator.class) public static native JSObject wrap(double num); + @InjectedBy(JSNativeGenerator.class) + public static native JSObject wrap(boolean num); + public static JSArray wrap(T[] array) { JSArray result = createArray(array.length); for (int i = 0; i < array.length; ++i) { @@ -120,34 +124,43 @@ public final class JS { public static native boolean isUndefined(JSObject obj); @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) public static native JSObject invoke(JSObject instance, JSObject method); @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) public static native JSObject invoke(JSObject instance, JSObject method, JSObject a); @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b); @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c); @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, JSObject d); @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e); @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f); @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f, JSObject g); @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f, JSObject g, JSObject h); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/JSNativeGenerator.java b/teavm-core/src/main/java/org/teavm/javascript/ni/JSNativeGenerator.java index 5fb8584ad..698064309 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/JSNativeGenerator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/JSNativeGenerator.java @@ -17,17 +17,23 @@ package org.teavm.javascript.ni; 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.ast.ConstantExpr; import org.teavm.javascript.ast.Expr; import org.teavm.javascript.ast.InvocationExpr; +import org.teavm.model.ClassHolder; import org.teavm.model.FieldReference; +import org.teavm.model.MethodHolder; import org.teavm.model.MethodReference; /** * * @author Alexey Andreev */ -public class JSNativeGenerator implements Generator, Injector { +public class JSNativeGenerator implements Generator, Injector, DependencyPlugin { @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { @@ -87,6 +93,27 @@ public class JSNativeGenerator implements Generator, Injector { } } + @Override + public void methodAchieved(final DependencyChecker checker, MethodReference method) { + MethodGraph graph = checker.attachMethodGraph(method); + for (int i = 0; i < method.parameterCount(); ++i) { + graph.getVariableNode(i).addConsumer(new DependencyConsumer() { + @Override public void consume(String type) { + achieveFunctorMethods(checker, type); + } + }); + } + } + + private void achieveFunctorMethods(DependencyChecker checker, String type) { + ClassHolder cls = checker.getClassSource().getClassHolder(type); + if (cls != null) { + for (MethodHolder method : cls.getMethods()) { + checker.attachMethodGraph(method.getReference()); + } + } + } + private void generateWrapString(GeneratorContext context, SourceWriter writer) throws IOException { FieldReference charsField = new FieldReference("java.lang.String", "characters"); writer.append("var result = \"\";").softNewLine(); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/PreserveOriginalName.java b/teavm-core/src/main/java/org/teavm/javascript/ni/PreserveOriginalName.java new file mode 100644 index 000000000..5a787aadf --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/PreserveOriginalName.java @@ -0,0 +1,15 @@ +package org.teavm.javascript.ni; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Alexey Andreev + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface PreserveOriginalName { +} diff --git a/teavm-core/src/main/java/org/teavm/parsing/Parser.java b/teavm-core/src/main/java/org/teavm/parsing/Parser.java index 8a43fe93a..67abb12d8 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/Parser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/Parser.java @@ -39,6 +39,9 @@ public class Parser { ssaProducer.transformToSSA(program, method.getParameterTypes()); method.setProgram(program); parseAnnotations(method.getAnnotations(), node); + while (program.variableCount() <= method.parameterCount()) { + program.createVariable(); + } return method; } diff --git a/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java b/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java index 0a933bab5..7b92b234a 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java +++ b/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java @@ -62,6 +62,9 @@ public class SSATransformer { } private void applySignature() { + if (program.variableCount() == 0) { + return; + } int index = 0; variableMap[index] = program.variableAt(index); ++index; diff --git a/teavm-dom/src/main/java/org/teavm/dom/events/EventListener.java b/teavm-dom/src/main/java/org/teavm/dom/events/EventListener.java index 1d633f515..64c89f206 100644 --- a/teavm-dom/src/main/java/org/teavm/dom/events/EventListener.java +++ b/teavm-dom/src/main/java/org/teavm/dom/events/EventListener.java @@ -16,12 +16,13 @@ package org.teavm.dom.events; import org.teavm.javascript.ni.JSFunctor; +import org.teavm.javascript.ni.JSObject; /** * * @author Alexey Andreev */ @JSFunctor -public interface EventListener { +public interface EventListener extends JSObject { void handleEvent(Event evt); } diff --git a/teavm-samples/src/main/java/org/teavm/samples/HelloWorld.java b/teavm-samples/src/main/java/org/teavm/samples/HelloWorld.java index 97eeefcbc..077886832 100644 --- a/teavm-samples/src/main/java/org/teavm/samples/HelloWorld.java +++ b/teavm-samples/src/main/java/org/teavm/samples/HelloWorld.java @@ -18,6 +18,9 @@ package org.teavm.samples; import org.teavm.dom.core.Document; import org.teavm.dom.core.Element; import org.teavm.dom.core.Window; +import org.teavm.dom.events.Event; +import org.teavm.dom.events.EventListener; +import org.teavm.dom.events.EventTarget; import org.teavm.javascript.ni.JS; @@ -34,7 +37,24 @@ public class HelloWorld { window = (Window)JS.getGlobal(); document = window.getDocument(); body = document.getDocumentElement().getElementsByTagName("body").item(0); + createButton(); + } + private static void createButton() { + Element elem = document.createElement("div"); + body.appendChild(elem); + final Element button = document.createElement("button"); + button.appendChild(document.createTextNode("Click me!")); + elem.appendChild(button); + ((EventTarget)button).addEventListener("click", new EventListener() { + @Override public void handleEvent(Event evt) { + button.getParentNode().removeChild(button); + printHelloWorld(); + } + }, false); + } + + private static void printHelloWorld() { println("Hello, world!"); println("Here is the Fibonacci sequence:"); long a = 0;