mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Introduces concept of an *injector*. Uses injectors to make generated
JavaScript code neat.
This commit is contained in:
parent
ae2e669ec3
commit
c9f78c5cdf
|
@ -20,6 +20,7 @@ import org.teavm.common.*;
|
||||||
import org.teavm.javascript.ast.*;
|
import org.teavm.javascript.ast.*;
|
||||||
import org.teavm.javascript.ni.GeneratedBy;
|
import org.teavm.javascript.ni.GeneratedBy;
|
||||||
import org.teavm.javascript.ni.Generator;
|
import org.teavm.javascript.ni.Generator;
|
||||||
|
import org.teavm.javascript.ni.InjectedBy;
|
||||||
import org.teavm.model.*;
|
import org.teavm.model.*;
|
||||||
import org.teavm.model.util.ProgramUtils;
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
|
||||||
|
@ -117,6 +118,9 @@ public class Decompiler {
|
||||||
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
|
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (method.getAnnotations().get(InjectedBy.class.getName()) != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
clsNode.getMethods().add(decompile(method));
|
clsNode.getMethods().add(decompile(method));
|
||||||
}
|
}
|
||||||
clsNode.getInterfaces().addAll(cls.getInterfaces());
|
clsNode.getInterfaces().addAll(cls.getInterfaces());
|
||||||
|
|
|
@ -96,7 +96,6 @@ public class JavascriptBuilder {
|
||||||
SourceWriterBuilder builder = new SourceWriterBuilder(naming);
|
SourceWriterBuilder builder = new SourceWriterBuilder(naming);
|
||||||
builder.setMinified(minifying);
|
builder.setMinified(minifying);
|
||||||
SourceWriter sourceWriter = builder.build(writer);
|
SourceWriter sourceWriter = builder.build(writer);
|
||||||
Renderer renderer = new Renderer(sourceWriter, classSource);
|
|
||||||
dependencyChecker.attachMethodGraph(new MethodReference("java.lang.Class", new MethodDescriptor("createNew",
|
dependencyChecker.attachMethodGraph(new MethodReference("java.lang.Class", new MethodDescriptor("createNew",
|
||||||
ValueType.object("java.lang.Class"))));
|
ValueType.object("java.lang.Class"))));
|
||||||
dependencyChecker.attachMethodGraph(new MethodReference("java.lang.String", new MethodDescriptor("<init>",
|
dependencyChecker.attachMethodGraph(new MethodReference("java.lang.String", new MethodDescriptor("<init>",
|
||||||
|
@ -104,6 +103,7 @@ public class JavascriptBuilder {
|
||||||
executor.complete();
|
executor.complete();
|
||||||
ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses();
|
ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses();
|
||||||
Decompiler decompiler = new Decompiler(classSet, classLoader, executor);
|
Decompiler decompiler = new Decompiler(classSet, classLoader, executor);
|
||||||
|
Renderer renderer = new Renderer(sourceWriter, classSet, classLoader);
|
||||||
ClassSetOptimizer optimizer = new ClassSetOptimizer(executor);
|
ClassSetOptimizer optimizer = new ClassSetOptimizer(executor);
|
||||||
optimizer.optimizeAll(classSet);
|
optimizer.optimizeAll(classSet);
|
||||||
executor.complete();
|
executor.complete();
|
||||||
|
|
|
@ -16,13 +16,20 @@
|
||||||
package org.teavm.javascript;
|
package org.teavm.javascript;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import org.teavm.codegen.NamingException;
|
import org.teavm.codegen.NamingException;
|
||||||
import org.teavm.codegen.NamingStrategy;
|
import org.teavm.codegen.NamingStrategy;
|
||||||
import org.teavm.codegen.SourceWriter;
|
import org.teavm.codegen.SourceWriter;
|
||||||
import org.teavm.javascript.ast.*;
|
import org.teavm.javascript.ast.*;
|
||||||
import org.teavm.javascript.ni.GeneratorContext;
|
import org.teavm.javascript.ni.GeneratorContext;
|
||||||
|
import org.teavm.javascript.ni.InjectedBy;
|
||||||
|
import org.teavm.javascript.ni.Injector;
|
||||||
|
import org.teavm.javascript.ni.InjectorContext;
|
||||||
import org.teavm.model.*;
|
import org.teavm.model.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,12 +41,23 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
||||||
private NamingStrategy naming;
|
private NamingStrategy naming;
|
||||||
private SourceWriter writer;
|
private SourceWriter writer;
|
||||||
private ClassHolderSource classSource;
|
private ClassHolderSource classSource;
|
||||||
|
private ClassLoader classLoader;
|
||||||
private boolean minifying;
|
private boolean minifying;
|
||||||
|
private Map<MethodReference, InjectorHolder> injectorMap = new HashMap<>();
|
||||||
|
|
||||||
public Renderer(SourceWriter writer, ClassHolderSource classSource) {
|
private static class InjectorHolder {
|
||||||
|
public final Injector injector;
|
||||||
|
|
||||||
|
public InjectorHolder(Injector injector) {
|
||||||
|
this.injector = injector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Renderer(SourceWriter writer, ClassHolderSource classSource, ClassLoader classLoader) {
|
||||||
this.naming = writer.getNaming();
|
this.naming = writer.getNaming();
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
|
this.classLoader = classLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SourceWriter getWriter() {
|
public SourceWriter getWriter() {
|
||||||
|
@ -193,6 +211,7 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
||||||
}
|
}
|
||||||
writer.outdent().append("}").newLine();
|
writer.outdent().append("}").newLine();
|
||||||
for (MethodNode method : cls.getMethods()) {
|
for (MethodNode method : cls.getMethods()) {
|
||||||
|
cls.getMethods();
|
||||||
if (!method.getModifiers().contains(NodeModifier.STATIC)) {
|
if (!method.getModifiers().contains(NodeModifier.STATIC)) {
|
||||||
renderDeclaration(method);
|
renderDeclaration(method);
|
||||||
}
|
}
|
||||||
|
@ -947,6 +966,11 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
||||||
@Override
|
@Override
|
||||||
public void visit(InvocationExpr expr) {
|
public void visit(InvocationExpr expr) {
|
||||||
try {
|
try {
|
||||||
|
Injector injector = getInjector(expr.getMethod());
|
||||||
|
if (injector != null) {
|
||||||
|
injector.generate(new InjectorContextImpl(expr.getArguments()), expr.getMethod());
|
||||||
|
return;
|
||||||
|
}
|
||||||
String className = naming.getNameFor(expr.getMethod().getClassName());
|
String className = naming.getNameFor(expr.getMethod().getClassName());
|
||||||
String name = naming.getNameFor(expr.getMethod());
|
String name = naming.getNameFor(expr.getMethod());
|
||||||
String fullName = naming.getFullNameFor(expr.getMethod());
|
String fullName = naming.getFullNameFor(expr.getMethod());
|
||||||
|
@ -1124,4 +1148,77 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
||||||
throw new RenderingException("IO error occured", e);
|
throw new RenderingException("IO error occured", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Injector getInjector(MethodReference ref) {
|
||||||
|
InjectorHolder holder = injectorMap.get(ref);
|
||||||
|
if (holder == null) {
|
||||||
|
MethodHolder method = classSource.getClassHolder(ref.getClassName()).getMethod(ref.getDescriptor());
|
||||||
|
AnnotationHolder injectedByAnnot = method.getAnnotations().get(InjectedBy.class.getName());
|
||||||
|
if (injectedByAnnot != null) {
|
||||||
|
ValueType type = injectedByAnnot.getValues().get("value").getJavaClass();
|
||||||
|
holder = new InjectorHolder(instantiateInjector(((ValueType.Object)type).getClassName()));
|
||||||
|
} else {
|
||||||
|
holder = new InjectorHolder(null);
|
||||||
|
}
|
||||||
|
injectorMap.put(ref, holder);
|
||||||
|
}
|
||||||
|
return holder.injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Injector instantiateInjector(String type) {
|
||||||
|
try {
|
||||||
|
Class<? extends Injector> cls = Class.forName(type, true, classLoader).asSubclass(Injector.class);
|
||||||
|
Constructor<? extends Injector> cons = cls.getConstructor();
|
||||||
|
return cons.newInstance();
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException("Illegal injector: " + type, e);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new RuntimeException("Default constructor was not found in the " + type + " injector", e);
|
||||||
|
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
|
||||||
|
throw new RuntimeException("Error instantiating injector " + type, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InjectorContextImpl implements InjectorContext {
|
||||||
|
private List<Expr> arguments;
|
||||||
|
|
||||||
|
public InjectorContextImpl(List<Expr> arguments) {
|
||||||
|
this.arguments = arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expr getArgument(int index) {
|
||||||
|
return arguments.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMinifying() {
|
||||||
|
return minifying;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SourceWriter getWriter() {
|
||||||
|
return writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeEscaped(String str) throws IOException {
|
||||||
|
writer.append(escapeString(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeType(ValueType type) throws IOException {
|
||||||
|
writer.append(typeToClsString(naming, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeExpr(Expr expr) throws IOException {
|
||||||
|
expr.acceptVisitor(Renderer.this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int argumentCount() {
|
||||||
|
return arguments.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 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.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 <konsoletyper@gmail.com>
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
public @interface InjectedBy {
|
||||||
|
Class<? extends Injector> value();
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 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.javascript.ni;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||||
|
*/
|
||||||
|
public interface Injector {
|
||||||
|
void generate(InjectorContext context, MethodReference methodRef) throws IOException;
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 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.javascript.ni;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.teavm.codegen.SourceWriter;
|
||||||
|
import org.teavm.javascript.ast.Expr;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||||
|
*/
|
||||||
|
public interface InjectorContext {
|
||||||
|
Expr getArgument(int index);
|
||||||
|
|
||||||
|
int argumentCount();
|
||||||
|
|
||||||
|
boolean isMinifying();
|
||||||
|
|
||||||
|
SourceWriter getWriter();
|
||||||
|
|
||||||
|
void writeEscaped(String str) throws IOException;
|
||||||
|
|
||||||
|
void writeType(ValueType type) throws IOException;
|
||||||
|
|
||||||
|
void writeExpr(Expr expr) throws IOException;
|
||||||
|
}
|
|
@ -45,25 +45,25 @@ public final class JS {
|
||||||
|
|
||||||
public static native <T extends JSObject> JSArray<T> createArray(int size);
|
public static native <T extends JSObject> JSArray<T> createArray(int size);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject getTypeName(JSObject obj);
|
public static native JSObject getTypeName(JSObject obj);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject getGlobal();
|
public static native JSObject getGlobal();
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@GeneratedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject wrap(String str);
|
public static native JSObject wrap(String str);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject wrap(char c);
|
public static native JSObject wrap(char c);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject wrap(int num);
|
public static native JSObject wrap(int num);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject wrap(float num);
|
public static native JSObject wrap(float num);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject wrap(double num);
|
public static native JSObject wrap(double num);
|
||||||
|
|
||||||
public static <T extends JSObject> JSArray<T> wrap(T[] array) {
|
public static <T extends JSObject> JSArray<T> wrap(T[] array) {
|
||||||
|
@ -90,7 +90,7 @@ public final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native boolean unwrapBoolean(JSObject obj);
|
public static native boolean unwrapBoolean(JSObject obj);
|
||||||
|
|
||||||
public static byte unwrapByte(JSObject obj) {
|
public static byte unwrapByte(JSObject obj) {
|
||||||
|
@ -101,53 +101,53 @@ public final class JS {
|
||||||
return (short)unwrapInt(obj);
|
return (short)unwrapInt(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native int unwrapInt(JSObject obj);
|
public static native int unwrapInt(JSObject obj);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native float unwrapFloat(JSObject obj);
|
public static native float unwrapFloat(JSObject obj);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native double unwrapDouble(JSObject obj);
|
public static native double unwrapDouble(JSObject obj);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@GeneratedBy(JSNativeGenerator.class)
|
||||||
public static native String unwrapString(JSObject obj);
|
public static native String unwrapString(JSObject obj);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native char unwrapCharacter(JSObject obj);
|
public static native char unwrapCharacter(JSObject obj);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native boolean isUndefined(JSObject obj);
|
public static native boolean isUndefined(JSObject obj);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject invoke(JSObject instance, JSObject method);
|
public static native JSObject invoke(JSObject instance, JSObject method);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a);
|
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b);
|
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c);
|
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||||
JSObject d);
|
JSObject d);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||||
JSObject d, JSObject e);
|
JSObject d, JSObject e);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||||
JSObject d, JSObject e, JSObject f);
|
JSObject d, JSObject e, JSObject f);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||||
JSObject d, JSObject e, JSObject f, JSObject g);
|
JSObject d, JSObject e, JSObject f, JSObject g);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
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);
|
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h);
|
||||||
|
|
||||||
|
@ -170,9 +170,9 @@ public final class JS {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject get(JSObject instance, JSObject index);
|
public static native JSObject get(JSObject instance, JSObject index);
|
||||||
|
|
||||||
@GeneratedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native void set(JSObject instance, JSObject index, JSObject obj);
|
public static native void set(JSObject instance, JSObject index, JSObject obj);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@ package org.teavm.javascript.ni;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.teavm.codegen.SourceWriter;
|
import org.teavm.codegen.SourceWriter;
|
||||||
|
import org.teavm.javascript.ast.ConstantExpr;
|
||||||
|
import org.teavm.javascript.ast.Expr;
|
||||||
|
import org.teavm.javascript.ast.InvocationExpr;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
@ -24,47 +27,62 @@ import org.teavm.model.MethodReference;
|
||||||
*
|
*
|
||||||
* @author Alexey Andreev
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
public class JSNativeGenerator implements Generator {
|
public class JSNativeGenerator implements Generator, Injector {
|
||||||
@Override
|
@Override
|
||||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef)
|
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
switch (methodRef.getName()) {
|
if (methodRef.getName().equals("wrap")) {
|
||||||
case "getTypeName":
|
|
||||||
writer.append("return typeof ").append(context.getParameterName(1)).append(";").softNewLine();
|
|
||||||
break;
|
|
||||||
case "getGlobal":
|
|
||||||
writer.append("return window;").softNewLine();
|
|
||||||
break;
|
|
||||||
case "wrap":
|
|
||||||
if (methodRef.getParameterTypes()[0].isObject("java.lang.String")) {
|
|
||||||
generateWrapString(context, writer);
|
generateWrapString(context, writer);
|
||||||
} else {
|
} else if (methodRef.getName().equals("unwrapString")) {
|
||||||
writer.append("return ").append(context.getParameterName(1)).append(";").softNewLine();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "get":
|
|
||||||
writer.append("return ").append(context.getParameterName(1)).append("[")
|
|
||||||
.append(context.getParameterName(2)).append("];").softNewLine();
|
|
||||||
break;
|
|
||||||
case "set":
|
|
||||||
writer.append(context.getParameterName(1)).append("[").append(context.getParameterName(2))
|
|
||||||
.append("] = ").append(context.getParameterName(3)).softNewLine();
|
|
||||||
break;
|
|
||||||
case "invoke":
|
|
||||||
generateInvoke(context, writer, methodRef.parameterCount() - 2);
|
|
||||||
break;
|
|
||||||
case "isUndefined":
|
|
||||||
writer.append("return ").append(context.getParameterName(1)).append(" === undefined;");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (methodRef.getName().startsWith("unwrap")) {
|
|
||||||
if (methodRef.getDescriptor().getResultType().isObject("java.lang.String")) {
|
|
||||||
writer.append("return $rt_str(").append(context.getParameterName(1)).append(");")
|
writer.append("return $rt_str(").append(context.getParameterName(1)).append(");")
|
||||||
.softNewLine();
|
.softNewLine();
|
||||||
} else {
|
|
||||||
writer.append("return ").append(context.getParameterName(1)).append(";").softNewLine();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
|
||||||
|
SourceWriter writer = context.getWriter();
|
||||||
|
switch (methodRef.getName()) {
|
||||||
|
case "getGlobal":
|
||||||
|
writer.append("window");
|
||||||
|
break;
|
||||||
|
case "isUndefined":
|
||||||
|
writer.append("(");
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
writer.ws().append("===").ws().append("undefined)");
|
||||||
|
break;
|
||||||
|
case "getTypeName":
|
||||||
|
writer.append("(typeof ");
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
writer.append(")");
|
||||||
|
break;
|
||||||
|
case "get":
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
renderProperty(context.getArgument(1), context);
|
||||||
|
break;
|
||||||
|
case "set":
|
||||||
|
writer.append('(');
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
renderProperty(context.getArgument(1), context);
|
||||||
|
writer.ws().append('=').ws();
|
||||||
|
context.writeExpr(context.getArgument(2));
|
||||||
|
writer.append(')');
|
||||||
|
break;
|
||||||
|
case "invoke":
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
renderProperty(context.getArgument(1), context);
|
||||||
|
writer.append('(');
|
||||||
|
for (int i = 2; i < context.argumentCount(); ++i) {
|
||||||
|
if (i > 2) {
|
||||||
|
writer.append(',').ws();
|
||||||
|
}
|
||||||
|
context.writeExpr(context.getArgument(i));
|
||||||
|
}
|
||||||
|
writer.append(')');
|
||||||
|
break;
|
||||||
|
case "wrap":
|
||||||
|
case "unwrap":
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,15 +98,51 @@ public class JSNativeGenerator implements Generator {
|
||||||
writer.append("return result;").softNewLine();
|
writer.append("return result;").softNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateInvoke(GeneratorContext context, SourceWriter writer, int argNum) throws IOException {
|
private void renderProperty(Expr property, InjectorContext context) throws IOException {
|
||||||
writer.append("return ").append(context.getParameterName(1)).append("[")
|
SourceWriter writer = context.getWriter();
|
||||||
.append(context.getParameterName(2)).append("](");
|
String name = extractPropertyName(property);
|
||||||
for (int i = 0; i < argNum; ++i) {
|
if (name == null) {
|
||||||
if (i > 0) {
|
writer.append('[');
|
||||||
writer.append(",").ws();
|
context.writeExpr(property);
|
||||||
}
|
writer.append(']');
|
||||||
writer.append(context.getParameterName(i + 3));
|
} else if (!isIdentifier(name)) {
|
||||||
}
|
writer.append("[\"");
|
||||||
writer.append(");").softNewLine();
|
context.writeEscaped(name);
|
||||||
|
writer.append("\"]");
|
||||||
|
} else {
|
||||||
|
writer.append(".").append(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractPropertyName(Expr propertyName) {
|
||||||
|
if (!(propertyName instanceof InvocationExpr)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
InvocationExpr invoke = (InvocationExpr)propertyName;
|
||||||
|
if (!invoke.getMethod().getClassName().equals(JS.class.getName())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!invoke.getMethod().getName().equals("wrap") ||
|
||||||
|
!invoke.getMethod().getDescriptor().parameterType(0).isObject("java.lang.String")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Expr arg = invoke.getArguments().get(0);
|
||||||
|
if (!(arg instanceof ConstantExpr)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ConstantExpr constant = (ConstantExpr)arg;
|
||||||
|
return constant.getValue() instanceof String ? (String)constant.getValue() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isIdentifier(String name) {
|
||||||
|
if (name.isEmpty() || !Character.isJavaIdentifierStart(name.charAt(0))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < name.length(); ++i) {
|
||||||
|
if (!Character.isJavaIdentifierPart(name.charAt(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user