mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -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.util.Iterator;
|
||||
import org.teavm.dependency.PluggableDependency;
|
||||
import org.teavm.javascript.spi.GeneratedBy;
|
||||
import org.teavm.javascript.spi.InjectedBy;
|
||||
import org.teavm.jso.plugin.JSNativeGenerator;
|
||||
|
||||
|
@ -557,7 +558,7 @@ public final class JS {
|
|||
@InjectedBy(JSNativeGenerator.class)
|
||||
public static native void set(JSObject instance, JSObject index, JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@GeneratedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
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.Expr;
|
||||
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.InjectorContext;
|
||||
import org.teavm.jso.JS;
|
||||
|
@ -33,7 +35,39 @@ import org.teavm.model.MethodReference;
|
|||
*
|
||||
* @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
|
||||
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
|
||||
SourceWriter writer = context.getWriter();
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.teavm.vm.spi.RendererListener;
|
|||
* @author Alexey Andreev
|
||||
*/
|
||||
class JSOAliasRenderer implements RendererListener {
|
||||
private static String variableChars = "abcdefghijklmnopqrstuvwxyz";
|
||||
private JSODependencyListener dependencyListener;
|
||||
private SourceWriter writer;
|
||||
|
||||
|
@ -50,15 +51,64 @@ class JSOAliasRenderer implements RendererListener {
|
|||
writer.append("(function()").ws().append("{").softNewLine().indent();
|
||||
writer.append("var c;").softNewLine();
|
||||
for (Map.Entry<String, ExposedClass> entry : dependencyListener.getExposedClasses().entrySet()) {
|
||||
if (entry.getValue().methods.isEmpty()) {
|
||||
ExposedClass cls = entry.getValue();
|
||||
if (cls.methods.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
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.")
|
||||
.appendMethod(aliasEntry.getKey()).append(";").softNewLine();
|
||||
}
|
||||
|
||||
if (cls.functorField != null) {
|
||||
writeFunctor(cls);
|
||||
}
|
||||
}
|
||||
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> methods = new HashMap<>();
|
||||
Set<String> implementedInterfaces = new HashSet<>();
|
||||
FieldReference functorField;
|
||||
MethodDescriptor functorMethod;
|
||||
}
|
||||
|
||||
private ExposedClass getExposedClass(String name) {
|
||||
|
@ -92,6 +94,14 @@ class JSODependencyListener implements DependencyListener {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,12 @@ public class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
if (processor.isNativeImplementation(cls.getName())) {
|
||||
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])) {
|
||||
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
||||
processor.processJSBody(cls, method);
|
||||
|
|
|
@ -59,6 +59,48 @@ class JavascriptNativeProcessor {
|
|||
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) {
|
||||
Set<MethodDescriptor> preservedMethods = new HashSet<>();
|
||||
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) {
|
||||
Set<MethodDescriptor> methods = new HashSet<>();
|
||||
findInheritedMethods(cls, methods, new HashSet<String>());
|
||||
|
@ -664,8 +717,12 @@ class JavascriptNativeProcessor {
|
|||
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) {
|
||||
if (!type.hasModifier(ElementModifier.INTERFACE) || type.getMethods().size() != 1) {
|
||||
if (!isProperFunctor(type)) {
|
||||
diagnostics.error(location, "Wrong functor: {{c0}}", type.getName());
|
||||
return var;
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
<targetDirectory>${project.build.directory}/generated/js/teavm</targetDirectory>
|
||||
<mainClass>org.teavm.samples.benchmark.teavm.BenchmarkStarter</mainClass>
|
||||
<runtime>SEPARATE</runtime>
|
||||
<minifying>true</minifying>
|
||||
<minifying>false</minifying>
|
||||
<debugInformationGenerated>true</debugInformationGenerated>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
|
Loading…
Reference in New Issue
Block a user