Adds possibility to implement JSObject ant pass it as a callback to a

method of another JSObject
This commit is contained in:
Alexey Andreev 2014-02-07 23:30:31 +04:00
parent c9f78c5cdf
commit 48265c446f
14 changed files with 193 additions and 48 deletions

View File

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

View File

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

View File

@ -26,13 +26,38 @@ import org.teavm.model.instructions.*;
*/
class JavascriptNativeProcessor {
private ClassHolderSource classSource;
private Map<String, Boolean> knownJavaScriptClasses = new HashMap<>();
private Program program;
private List<Instruction> 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<MethodDescriptor> 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<MethodDescriptor> 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;
}

View File

@ -24,6 +24,7 @@ import org.teavm.model.MethodHolder;
* @author Alexey Andreev
*/
public class JavascriptProcessedClassSource implements ClassHolderSource {
private ThreadLocal<JavascriptNativeProcessor> 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();
}
}

View File

@ -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<String, Boolean> 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;
}
}

View File

@ -290,15 +290,8 @@ public class Renderer implements ExprVisitor, StatementVisitor {
if (ref.getDescriptor().getName().equals("<init>")) {
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);

View File

@ -26,6 +26,7 @@ import org.teavm.model.MethodReference;
public abstract class MethodNode {
private MethodReference reference;
private Set<NodeModifier> 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);
}

View File

@ -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 <T extends JSObject> JSArray<T> wrap(T[] array) {
JSArray<T> 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);

View File

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

View File

@ -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 {
}

View File

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

View File

@ -62,6 +62,9 @@ public class SSATransformer {
}
private void applySignature() {
if (program.variableCount() == 0) {
return;
}
int index = 0;
variableMap[index] = program.variableAt(index);
++index;

View File

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

View File

@ -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;