mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Trying to improve functors for better performance and for functor
persistence (see https://github.com/konsoletyper/teavm/issues/103)
This commit is contained in:
parent
11d69f15c9
commit
25310a5082
|
@ -18,6 +18,7 @@ package org.teavm.jso;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import org.teavm.dependency.PluggableDependency;
|
import org.teavm.dependency.PluggableDependency;
|
||||||
|
import org.teavm.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.javascript.spi.InjectedBy;
|
import org.teavm.javascript.spi.InjectedBy;
|
||||||
import org.teavm.jso.plugin.JSNativeGenerator;
|
import org.teavm.jso.plugin.JSNativeGenerator;
|
||||||
|
|
||||||
|
@ -557,7 +558,7 @@ public final class JS {
|
||||||
@InjectedBy(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);
|
||||||
|
|
||||||
@InjectedBy(JSNativeGenerator.class)
|
@GeneratedBy(JSNativeGenerator.class)
|
||||||
@PluggableDependency(JSNativeGenerator.class)
|
@PluggableDependency(JSNativeGenerator.class)
|
||||||
public static native JSObject function(JSObject instance, JSObject property);
|
public static native JSObject function(JSObject instance, JSObject property);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 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.jso.plugin;
|
||||||
|
|
||||||
|
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.TYPE)
|
||||||
|
@interface FunctorImpl {
|
||||||
|
String value();
|
||||||
|
}
|
|
@ -21,6 +21,8 @@ import org.teavm.dependency.*;
|
||||||
import org.teavm.javascript.ast.ConstantExpr;
|
import org.teavm.javascript.ast.ConstantExpr;
|
||||||
import org.teavm.javascript.ast.Expr;
|
import org.teavm.javascript.ast.Expr;
|
||||||
import org.teavm.javascript.ast.InvocationExpr;
|
import org.teavm.javascript.ast.InvocationExpr;
|
||||||
|
import org.teavm.javascript.spi.Generator;
|
||||||
|
import org.teavm.javascript.spi.GeneratorContext;
|
||||||
import org.teavm.javascript.spi.Injector;
|
import org.teavm.javascript.spi.Injector;
|
||||||
import org.teavm.javascript.spi.InjectorContext;
|
import org.teavm.javascript.spi.InjectorContext;
|
||||||
import org.teavm.jso.JS;
|
import org.teavm.jso.JS;
|
||||||
|
@ -33,7 +35,39 @@ import org.teavm.model.MethodReference;
|
||||||
*
|
*
|
||||||
* @author Alexey Andreev
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
public class JSNativeGenerator implements Injector, DependencyPlugin {
|
public class JSNativeGenerator implements Injector, DependencyPlugin, Generator {
|
||||||
|
@Override
|
||||||
|
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef)
|
||||||
|
throws IOException {
|
||||||
|
switch (methodRef.getName()) {
|
||||||
|
case "function":
|
||||||
|
writeFunction(context, writer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeFunction(GeneratorContext context, SourceWriter writer) throws IOException {
|
||||||
|
String thisName = context.getParameterName(1);
|
||||||
|
String methodName = context.getParameterName(2);
|
||||||
|
writer.append("var name").ws().append('=').ws().append("'jso$functor$'").ws().append('+').ws()
|
||||||
|
.append(methodName).append(';').softNewLine();
|
||||||
|
writer.append("if").ws().append("(!").append(thisName).append("[name])").ws().append('{')
|
||||||
|
.indent().softNewLine();
|
||||||
|
|
||||||
|
writer.append("var fn").ws().append('=').ws().append("function()").ws().append('{')
|
||||||
|
.indent().softNewLine();
|
||||||
|
writer.append("return ").append(thisName).append('[').append(methodName).append(']')
|
||||||
|
.append(".apply(").append(thisName).append(',').ws().append("arguments);").softNewLine();
|
||||||
|
writer.outdent().append("};").softNewLine();
|
||||||
|
writer.append(thisName).append("[name]").ws().append('=').ws().append("function()").ws().append('{')
|
||||||
|
.indent().softNewLine();
|
||||||
|
writer.append("return fn;").softNewLine();
|
||||||
|
writer.outdent().append("};").softNewLine();
|
||||||
|
|
||||||
|
writer.outdent().append('}').softNewLine();
|
||||||
|
writer.append("return ").append(thisName).append("[name]();").softNewLine();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
|
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
|
||||||
SourceWriter writer = context.getWriter();
|
SourceWriter writer = context.getWriter();
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.teavm.vm.spi.RendererListener;
|
||||||
* @author Alexey Andreev
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
class JSOAliasRenderer implements RendererListener {
|
class JSOAliasRenderer implements RendererListener {
|
||||||
|
private static String variableChars = "abcdefghijklmnopqrstuvwxyz";
|
||||||
private JSODependencyListener dependencyListener;
|
private JSODependencyListener dependencyListener;
|
||||||
private SourceWriter writer;
|
private SourceWriter writer;
|
||||||
|
|
||||||
|
@ -50,15 +51,64 @@ class JSOAliasRenderer implements RendererListener {
|
||||||
writer.append("(function()").ws().append("{").softNewLine().indent();
|
writer.append("(function()").ws().append("{").softNewLine().indent();
|
||||||
writer.append("var c;").softNewLine();
|
writer.append("var c;").softNewLine();
|
||||||
for (Map.Entry<String, ExposedClass> entry : dependencyListener.getExposedClasses().entrySet()) {
|
for (Map.Entry<String, ExposedClass> entry : dependencyListener.getExposedClasses().entrySet()) {
|
||||||
if (entry.getValue().methods.isEmpty()) {
|
ExposedClass cls = entry.getValue();
|
||||||
|
if (cls.methods.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
writer.append("c").ws().append("=").ws().appendClass(entry.getKey()).append(".prototype;").softNewLine();
|
writer.append("c").ws().append("=").ws().appendClass(entry.getKey()).append(".prototype;").softNewLine();
|
||||||
for (Map.Entry<MethodDescriptor, String> aliasEntry : entry.getValue().methods.entrySet()) {
|
for (Map.Entry<MethodDescriptor, String> aliasEntry : cls.methods.entrySet()) {
|
||||||
writer.append("c.").append(aliasEntry.getValue()).ws().append("=").ws().append("c.")
|
writer.append("c.").append(aliasEntry.getValue()).ws().append("=").ws().append("c.")
|
||||||
.appendMethod(aliasEntry.getKey()).append(";").softNewLine();
|
.appendMethod(aliasEntry.getKey()).append(";").softNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cls.functorField != null) {
|
||||||
|
writeFunctor(cls);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
writer.outdent().append("})();").newLine();
|
writer.outdent().append("})();").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void writeFunctor(ExposedClass cls) throws IOException {
|
||||||
|
String alias = cls.methods.get(cls.functorMethod);
|
||||||
|
if (alias == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.append("c.jso$functor$").append(alias).ws().append("=").ws().append("function()").ws().append("{")
|
||||||
|
.indent().softNewLine();
|
||||||
|
writer.append("if").ws().append("(!this.").appendField(cls.functorField).append(")").ws().append("{")
|
||||||
|
.indent().softNewLine();
|
||||||
|
writer.append("var self").ws().append('=').ws().append("this;").softNewLine();
|
||||||
|
|
||||||
|
writer.append("this.").appendField(cls.functorField).ws().append('=').ws().append("function(");
|
||||||
|
appendArguments(cls.functorMethod.parameterCount());
|
||||||
|
writer.append(")").ws().append('{').indent().softNewLine();
|
||||||
|
writer.append("return self.").appendMethod(cls.functorMethod).append('(');
|
||||||
|
appendArguments(cls.functorMethod.parameterCount());
|
||||||
|
writer.append(");").softNewLine();
|
||||||
|
writer.outdent().append("};").softNewLine();
|
||||||
|
|
||||||
|
writer.outdent().append("}").softNewLine();
|
||||||
|
writer.append("return this.").appendField(cls.functorField).append(';').softNewLine();
|
||||||
|
writer.outdent().append("};").softNewLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendArguments(int count) throws IOException {
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
if (i > 0) {
|
||||||
|
writer.append(',').ws();
|
||||||
|
}
|
||||||
|
writer.append(variableName(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String variableName(int index) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(variableChars.charAt(index % variableChars.length()));
|
||||||
|
index /= variableChars.length();
|
||||||
|
if (index > 0) {
|
||||||
|
sb.append(index);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,8 @@ class JSODependencyListener implements DependencyListener {
|
||||||
Map<MethodDescriptor, String> inheritedMethods = new HashMap<>();
|
Map<MethodDescriptor, String> inheritedMethods = new HashMap<>();
|
||||||
Map<MethodDescriptor, String> methods = new HashMap<>();
|
Map<MethodDescriptor, String> methods = new HashMap<>();
|
||||||
Set<String> implementedInterfaces = new HashSet<>();
|
Set<String> implementedInterfaces = new HashSet<>();
|
||||||
|
FieldReference functorField;
|
||||||
|
MethodDescriptor functorMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExposedClass getExposedClass(String name) {
|
private ExposedClass getExposedClass(String name) {
|
||||||
|
@ -92,6 +94,14 @@ class JSODependencyListener implements DependencyListener {
|
||||||
methodDep.use();
|
methodDep.use();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (exposedCls.functorField == null) {
|
||||||
|
FieldReader functorField = cls.getField("$$jso_functor$$");
|
||||||
|
if (functorField != null) {
|
||||||
|
exposedCls.functorField = functorField.getReference();
|
||||||
|
AnnotationReader annot = cls.getAnnotations().get(FunctorImpl.class.getName());
|
||||||
|
exposedCls.functorMethod = MethodDescriptor.parse(annot.getValue("value").getString());
|
||||||
|
}
|
||||||
|
}
|
||||||
return exposedCls;
|
return exposedCls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,12 @@ public class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||||
if (processor.isNativeImplementation(cls.getName())) {
|
if (processor.isNativeImplementation(cls.getName())) {
|
||||||
processor.makeSync(cls);
|
processor.makeSync(cls);
|
||||||
}
|
}
|
||||||
|
MethodReference functorMethod = processor.isFunctor(cls.getName());
|
||||||
|
if (functorMethod != null) {
|
||||||
|
if (processor.isFunctor(cls.getParent()) == null) {
|
||||||
|
processor.addFunctorField(cls, functorMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
||||||
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
||||||
processor.processJSBody(cls, method);
|
processor.processJSBody(cls, method);
|
||||||
|
|
|
@ -59,6 +59,48 @@ class JavascriptNativeProcessor {
|
||||||
this.diagnostics = diagnostics;
|
this.diagnostics = diagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MethodReference isFunctor(String className) {
|
||||||
|
if (!nativeRepos.isJavaScriptImplementation(className)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ClassReader cls = classSource.get(className);
|
||||||
|
if (cls == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Map<MethodDescriptor, MethodReference> methods = new HashMap<>();
|
||||||
|
getFunctorMethods(className, new HashSet<String>(), methods);
|
||||||
|
if (methods.size() == 1) {
|
||||||
|
return methods.values().iterator().next();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getFunctorMethods(String className, Set<String> visited,
|
||||||
|
Map<MethodDescriptor, MethodReference> methods) {
|
||||||
|
if (!visited.add(className)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassReader cls = classSource.get(className);
|
||||||
|
if (cls == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cls.getAnnotations().get(JSFunctor.class.getName()) != null && isProperFunctor(cls)) {
|
||||||
|
MethodReference method = cls.getMethods().iterator().next().getReference();
|
||||||
|
if (!methods.containsKey(method.getDescriptor())) {
|
||||||
|
methods.put(method.getDescriptor(), method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) {
|
||||||
|
getFunctorMethods(cls.getParent(), visited, methods);
|
||||||
|
}
|
||||||
|
for (String iface : cls.getInterfaces()) {
|
||||||
|
getFunctorMethods(iface, visited, methods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void processClass(ClassHolder cls) {
|
public void processClass(ClassHolder cls) {
|
||||||
Set<MethodDescriptor> preservedMethods = new HashSet<>();
|
Set<MethodDescriptor> preservedMethods = new HashSet<>();
|
||||||
for (String iface : cls.getInterfaces()) {
|
for (String iface : cls.getInterfaces()) {
|
||||||
|
@ -126,6 +168,17 @@ class JavascriptNativeProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addFunctorField(ClassHolder cls, MethodReference method) {
|
||||||
|
FieldHolder field = new FieldHolder("$$jso_functor$$");
|
||||||
|
field.setLevel(AccessLevel.PUBLIC);
|
||||||
|
field.setType(ValueType.parse(JSObject.class));
|
||||||
|
cls.addField(field);
|
||||||
|
|
||||||
|
AnnotationHolder annot = new AnnotationHolder(FunctorImpl.class.getName());
|
||||||
|
annot.getValues().put("value", new AnnotationValue(method.getDescriptor().toString()));
|
||||||
|
cls.getAnnotations().add(annot);
|
||||||
|
}
|
||||||
|
|
||||||
public void makeSync(ClassHolder cls) {
|
public void makeSync(ClassHolder cls) {
|
||||||
Set<MethodDescriptor> methods = new HashSet<>();
|
Set<MethodDescriptor> methods = new HashSet<>();
|
||||||
findInheritedMethods(cls, methods, new HashSet<String>());
|
findInheritedMethods(cls, methods, new HashSet<String>());
|
||||||
|
@ -664,8 +717,12 @@ class JavascriptNativeProcessor {
|
||||||
return wrap(var, type, location.getSourceLocation());
|
return wrap(var, type, location.getSourceLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isProperFunctor(ClassReader type) {
|
||||||
|
return type.hasModifier(ElementModifier.INTERFACE) && type.getMethods().size() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
private Variable wrapFunctor(CallLocation location, Variable var, ClassReader type) {
|
private Variable wrapFunctor(CallLocation location, Variable var, ClassReader type) {
|
||||||
if (!type.hasModifier(ElementModifier.INTERFACE) || type.getMethods().size() != 1) {
|
if (!isProperFunctor(type)) {
|
||||||
diagnostics.error(location, "Wrong functor: {{c0}}", type.getName());
|
diagnostics.error(location, "Wrong functor: {{c0}}", type.getName());
|
||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@
|
||||||
<targetDirectory>${project.build.directory}/generated/js/teavm</targetDirectory>
|
<targetDirectory>${project.build.directory}/generated/js/teavm</targetDirectory>
|
||||||
<mainClass>org.teavm.samples.benchmark.teavm.BenchmarkStarter</mainClass>
|
<mainClass>org.teavm.samples.benchmark.teavm.BenchmarkStarter</mainClass>
|
||||||
<runtime>SEPARATE</runtime>
|
<runtime>SEPARATE</runtime>
|
||||||
<minifying>true</minifying>
|
<minifying>false</minifying>
|
||||||
<debugInformationGenerated>true</debugInformationGenerated>
|
<debugInformationGenerated>true</debugInformationGenerated>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user