mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Adds possibility to implement JSObject ant pass it as a callback to a
method of another JSObject
This commit is contained in:
parent
c9f78c5cdf
commit
48265c446f
|
@ -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()]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,9 @@ public class SSATransformer {
|
|||
}
|
||||
|
||||
private void applySignature() {
|
||||
if (program.variableCount() == 0) {
|
||||
return;
|
||||
}
|
||||
int index = 0;
|
||||
variableMap[index] = program.variableAt(index);
|
||||
++index;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user