diff --git a/classlib/src/main/java/org/teavm/classlib/impl/AnnotationVirtualMethods.java b/classlib/src/main/java/org/teavm/classlib/impl/AnnotationVirtualMethods.java new file mode 100644 index 000000000..a9a88548a --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/AnnotationVirtualMethods.java @@ -0,0 +1,33 @@ +/* + * Copyright 2018 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.classlib.impl; + +import org.teavm.backend.javascript.spi.VirtualMethodContributor; +import org.teavm.backend.javascript.spi.VirtualMethodContributorContext; +import org.teavm.model.ClassReader; +import org.teavm.model.MethodReference; +import org.teavm.platform.PlatformAnnotationProvider; + +public class AnnotationVirtualMethods implements VirtualMethodContributor { + @Override + public boolean isVirtual(VirtualMethodContributorContext context, MethodReference methodRef) { + ClassReader cls = context.getClassSource().get(methodRef.getClassName()); + if (cls == null || !cls.getInterfaces().contains(PlatformAnnotationProvider.class.getName())) { + return false; + } + return methodRef.getName().equals("getAnnotations"); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java index bf5d1b176..575c3be1b 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java @@ -46,6 +46,7 @@ public class JCLPlugin implements TeaVMPlugin { TeaVMJavaScriptHost jsExtension = host.getExtension(TeaVMJavaScriptHost.class); if (jsExtension != null) { jsExtension.add(loadServicesMethod, serviceLoaderSupp); + jsExtension.addVirtualMethods(new AnnotationVirtualMethods()); } JavacSupport javacSupport = new JavacSupport(); diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java index 4cb07244a..3a53f861a 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java @@ -267,17 +267,19 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin { if (method.getResultType() != ValueType.VOID) { writer.append("return "); } - if (method.hasModifier(ElementModifier.STATIC)) { - writer.appendMethodBody(method.getReference()); - } else { - writer.append("obj.").appendMethod(method.getDescriptor()); - } + writer.appendMethodBody(method.getReference()); writer.append('('); + boolean first = true; + if (!method.hasModifier(ElementModifier.STATIC)) { + writer.append("obj").ws(); + first = false; + } for (int i = 0; i < method.parameterCount(); ++i) { - if (i > 0) { + if (!first) { writer.append(',').ws(); } + first = false; int index = i; unboxIfNecessary(writer, method.parameterType(i), () -> writer.append("args[" + index + "]")); } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java index 09e3deeb5..74a09ef00 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java @@ -24,7 +24,6 @@ import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; import org.teavm.model.CallLocation; import org.teavm.model.ClassReader; -import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -40,13 +39,18 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) { switch (method.getReference().getName()) { case "getLength": - achieveGetLength(agent, method); + reachGetLength(agent, method); break; case "newInstance": method.getVariable(1).getClassValueNode().addConsumer(t -> { - String arrayTypeName = t.getName().startsWith("[") - ? t.getName() - : ValueType.object(t.getName()).toString(); + String arrayTypeName; + if (t.getName().startsWith("[")) { + arrayTypeName = t.getName(); + } else if (t.getName().startsWith("~")) { + arrayTypeName = t.getName().substring(1); + } else { + arrayTypeName = ValueType.object(t.getName()).toString(); + } method.getResult().propagate(agent.getType("[" + arrayTypeName)); }); break; @@ -81,14 +85,13 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { String array = context.getParameterName(1); writer.append("if (" + array + " === null || " + array + ".constructor.$meta.item === undefined) {") .softNewLine().indent(); - String clsName = "java.lang.IllegalArgumentException"; - MethodDescriptor cons = new MethodDescriptor("", ValueType.VOID); - writer.append("$rt_throw(").appendClass(clsName).append(".").appendMethod(cons).append("());").softNewLine(); + MethodReference cons = new MethodReference("java.lang.IllegalArgumentException", "", ValueType.VOID); + writer.append("$rt_throw(").append(writer.getNaming().getNameForInit(cons)).append("());").softNewLine(); writer.outdent().append("}").softNewLine(); writer.append("return " + array + ".data.length;").softNewLine(); } - private void achieveGetLength(final DependencyAgent agent, final MethodDependency method) { + private void reachGetLength(final DependencyAgent agent, final MethodDependency method) { method.getVariable(1).addConsumer(type -> { if (!type.getName().startsWith("[")) { MethodReference cons = new MethodReference(IllegalArgumentException.class, "", void.class); diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index 06b002e19..f858ac9ef 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -49,6 +49,8 @@ import org.teavm.backend.javascript.spi.GeneratedBy; import org.teavm.backend.javascript.spi.Generator; import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.backend.javascript.spi.Injector; +import org.teavm.backend.javascript.spi.VirtualMethodContributor; +import org.teavm.backend.javascript.spi.VirtualMethodContributorContext; import org.teavm.debugging.information.DebugInformationEmitter; import org.teavm.debugging.information.DummyDebugInformationEmitter; import org.teavm.debugging.information.SourceLocation; @@ -104,6 +106,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { private final Set asyncMethods = new HashSet<>(); private final Set asyncFamilyMethods = new HashSet<>(); private ClassInitializerInsertionTransformer clinitInsertionTransformer; + private List customVirtualMethods = new ArrayList<>(); @Override public List getTransformers() { @@ -194,19 +197,35 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { @Override public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) { - dependencyAnalyzer.linkMethod(new MethodReference(Class.class.getName(), "getClass", - ValueType.object("org.teavm.platform.PlatformClass"), ValueType.parse(Class.class)), null).use(); - dependencyAnalyzer.linkMethod(new MethodReference(String.class, "", char[].class, void.class), - null).use(); - dependencyAnalyzer.linkMethod(new MethodReference(String.class, "getChars", int.class, int.class, char[].class, - int.class, void.class), null).use(); + MethodDependency dep; + + dep = dependencyAnalyzer.linkMethod(new MethodReference(Class.class.getName(), "getClass", + ValueType.object("org.teavm.platform.PlatformClass"), ValueType.parse(Class.class)), null); + dep.getVariable(0).propagate(dependencyAnalyzer.getType("org.teavm.platform.PlatformClass")); + dep.getResult().propagate(dependencyAnalyzer.getType("java.lang.Class")); + dep.use(); + + dep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "", char[].class, void.class), + null); + dep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.String")); + dep.getVariable(1).propagate(dependencyAnalyzer.getType("[C")); + dep.use(); + + dep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "getChars", int.class, int.class, + char[].class, int.class, void.class), null); + dep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.String")); + dep.getVariable(3).propagate(dependencyAnalyzer.getType("[C")); + dep.use(); MethodDependency internDep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "intern", String.class), null); internDep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.String")); internDep.use(); - dependencyAnalyzer.linkMethod(new MethodReference(String.class, "length", int.class), null).use(); + dep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "length", int.class), null); + dep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.String")); + dep.use(); + dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "clone", Object.class), null).use(); dependencyAnalyzer.linkMethod(new MethodReference(Thread.class, "currentThread", Thread.class), null).use(); dependencyAnalyzer.linkMethod(new MethodReference(Thread.class, "getMainThread", Thread.class), null).use(); @@ -264,10 +283,12 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { if (debugEmitterToUse == null) { debugEmitterToUse = new DummyDebugInformationEmitter(); } + VirtualMethodContributorContext virtualMethodContributorContext = new VirtualMethodContributorContextImpl( + classes); RenderingContext renderingContext = new RenderingContext(debugEmitterToUse, controller.getUnprocessedClassSource(), classes, controller.getClassLoader(), controller.getServices(), controller.getProperties(), naming, - controller.getDependencyInfo()); + controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m)); renderingContext.setMinifying(minifying); Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods, controller.getDiagnostics(), renderingContext); @@ -505,8 +526,38 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { return new String[] { PlatformMarkers.JAVASCRIPT }; } + @Override + public void addVirtualMethods(VirtualMethodContributor virtualMethods) { + customVirtualMethods.add(virtualMethods); + } + @Override public boolean isAsyncSupported() { return true; } + + private boolean isVirtual(VirtualMethodContributorContext context, MethodReference method) { + if (controller.isVirtual(method)) { + return true; + } + for (VirtualMethodContributor predicate : customVirtualMethods) { + if (predicate.isVirtual(context, method)) { + return true; + } + } + return false; + } + + static class VirtualMethodContributorContextImpl implements VirtualMethodContributorContext { + private ClassReaderSource classSource; + + VirtualMethodContributorContextImpl(ClassReaderSource classSource) { + this.classSource = classSource; + } + + @Override + public ClassReaderSource getClassSource() { + return classSource; + } + }; } diff --git a/core/src/main/java/org/teavm/backend/javascript/TeaVMJavaScriptHost.java b/core/src/main/java/org/teavm/backend/javascript/TeaVMJavaScriptHost.java index 051a52b3c..d7684fee5 100644 --- a/core/src/main/java/org/teavm/backend/javascript/TeaVMJavaScriptHost.java +++ b/core/src/main/java/org/teavm/backend/javascript/TeaVMJavaScriptHost.java @@ -18,6 +18,7 @@ package org.teavm.backend.javascript; import java.util.function.Function; import org.teavm.backend.javascript.spi.Generator; import org.teavm.backend.javascript.spi.Injector; +import org.teavm.backend.javascript.spi.VirtualMethodContributor; import org.teavm.model.MethodReference; import org.teavm.vm.spi.RendererListener; import org.teavm.vm.spi.TeaVMHostExtension; @@ -32,4 +33,6 @@ public interface TeaVMJavaScriptHost extends TeaVMHostExtension { void addInjectorProvider(Function provider); void add(RendererListener listener); + + void addVirtualMethods(VirtualMethodContributor virtualMethods); } diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java b/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java index f5a12443f..068622a42 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java @@ -118,10 +118,6 @@ public class SourceWriter implements Appendable, LocationProvider { return append(naming.getNameFor(method)); } - public SourceWriter appendMethod(String name, ValueType... params) throws NamingException, IOException { - return append(naming.getNameFor(new MethodDescriptor(name, params))); - } - public SourceWriter appendMethod(String name, Class... params) throws NamingException, IOException { return append(naming.getNameFor(new MethodDescriptor(name, params))); } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index 801291e4e..f3adeafb4 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -741,6 +741,9 @@ public class Renderer implements RenderingManager { writer.append("["); boolean first = true; for (MethodReference method : methods) { + if (!isVirtual(method)) { + continue; + } debugEmitter.emitMethod(method.getDescriptor()); if (!first) { writer.append(",").ws(); @@ -1069,4 +1072,8 @@ public class Renderer implements RenderingManager { this.value = value; } } + + private boolean isVirtual(MethodReference method) { + return context.isVirtual(method); + } } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java index 28badd6bd..440d1090f 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.function.Predicate; import org.teavm.backend.javascript.codegen.NamingStrategy; import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.backend.javascript.spi.Injector; @@ -50,6 +51,7 @@ public class RenderingContext { private Properties properties; private NamingStrategy naming; private DependencyInfo dependencyInfo; + private Predicate virtualPredicate; private final Deque locationStack = new ArrayDeque<>(); private final Map stringPoolMap = new HashMap<>(); private final List stringPool = new ArrayList<>(); @@ -60,7 +62,8 @@ public class RenderingContext { public RenderingContext(DebugInformationEmitter debugEmitter, ClassReaderSource initialClassSource, ListableClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, Properties properties, - NamingStrategy naming, DependencyInfo dependencyInfo) { + NamingStrategy naming, DependencyInfo dependencyInfo, + Predicate virtualPredicate) { this.debugEmitter = debugEmitter; this.initialClassSource = initialClassSource; this.classSource = classSource; @@ -69,6 +72,7 @@ public class RenderingContext { this.properties = properties; this.naming = naming; this.dependencyInfo = dependencyInfo; + this.virtualPredicate = virtualPredicate; } public ClassReaderSource getInitialClassSource() { @@ -107,6 +111,10 @@ public class RenderingContext { return debugEmitter; } + public boolean isVirtual(MethodReference method) { + return virtualPredicate.test(method); + } + public void pushLocation(TextLocation location) { LocationStackEntry prevEntry = locationStack.peek(); if (location != null) { diff --git a/core/src/main/java/org/teavm/backend/javascript/spi/VirtualMethodContributor.java b/core/src/main/java/org/teavm/backend/javascript/spi/VirtualMethodContributor.java new file mode 100644 index 000000000..1b3cabe68 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/javascript/spi/VirtualMethodContributor.java @@ -0,0 +1,22 @@ +/* + * Copyright 2018 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.backend.javascript.spi; + +import org.teavm.model.MethodReference; + +public interface VirtualMethodContributor { + boolean isVirtual(VirtualMethodContributorContext context, MethodReference methodRef); +} diff --git a/core/src/main/java/org/teavm/backend/javascript/spi/VirtualMethodContributorContext.java b/core/src/main/java/org/teavm/backend/javascript/spi/VirtualMethodContributorContext.java new file mode 100644 index 000000000..8e042fc63 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/javascript/spi/VirtualMethodContributorContext.java @@ -0,0 +1,22 @@ +/* + * Copyright 2018 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.backend.javascript.spi; + +import org.teavm.model.ClassReaderSource; + +public interface VirtualMethodContributorContext { + ClassReaderSource getClassSource(); +} diff --git a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java index defac52d0..96c3e98b6 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java @@ -112,7 +112,11 @@ public class DependencyAnalyzer implements DependencyInfo { if (field != null && !field.getReference().equals(preimage)) { return fieldCache.map(field.getReference()); } - return createFieldNode(preimage, field); + FieldDependency node = createFieldNode(preimage, field); + if (field != null && field.getInitialValue() instanceof String) { + node.getValue().propagate(getType("java.lang.String")); + } + return node; }); classCache = new CachedMapper<>(this::createClassDependency); diff --git a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index b8d49cfe9..73f0845c5 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -403,6 +403,8 @@ class DependencyGraphBuilder { sb.append(cst.toString()); } node.getClassValueNode().propagate(dependencyAnalyzer.getType(sb.toString())); + } else { + node.getClassValueNode().propagate(dependencyAnalyzer.getType("~" + cst.toString())); } } while (cst instanceof ValueType.Array) { diff --git a/core/src/main/java/org/teavm/model/optimization/Devirtualization.java b/core/src/main/java/org/teavm/model/optimization/Devirtualization.java index 975ca6ab4..a7ab58488 100644 --- a/core/src/main/java/org/teavm/model/optimization/Devirtualization.java +++ b/core/src/main/java/org/teavm/model/optimization/Devirtualization.java @@ -15,6 +15,7 @@ */ package org.teavm.model.optimization; +import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.teavm.dependency.DependencyInfo; @@ -33,6 +34,8 @@ import org.teavm.model.instructions.InvokeInstruction; public class Devirtualization { private DependencyInfo dependency; private ClassReaderSource classSource; + private Set virtualMethods = new HashSet<>(); + private Set readonlyVirtualMethods = Collections.unmodifiableSet(virtualMethods); public Devirtualization(DependencyInfo dependency, ClassReaderSource classSource) { this.dependency = dependency; @@ -61,6 +64,8 @@ public class Devirtualization { if (implementations.size() == 1) { invoke.setType(InvocationType.SPECIAL); invoke.setMethod(implementations.iterator().next()); + } else { + virtualMethods.addAll(implementations); } } } @@ -84,4 +89,8 @@ public class Devirtualization { } return methods; } + + public Set getVirtualMethods() { + return readonlyVirtualMethods; + } } diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index ca06d2c7f..711589cec 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -124,6 +124,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { private ListableClassHolderSource writtenClasses; private TeaVMTarget target; private Map, TeaVMHostExtension> extensions = new HashMap<>(); + private Set virtualMethods; TeaVM(TeaVMBuilder builder) { target = builder.target; @@ -457,6 +458,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { return; } } + virtualMethods = devirtualization.getVirtualMethods(); } private void inline(ListableClassHolderSource classes, DependencyInfo dependencyInfo) { @@ -692,5 +694,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository { public boolean isFriendlyToDebugger() { return optimizationLevel == TeaVMOptimizationLevel.SIMPLE; } + + @Override + public boolean isVirtual(MethodReference method) { + return incremental || virtualMethods == null || virtualMethods.contains(method); + } }; } diff --git a/core/src/main/java/org/teavm/vm/TeaVMTargetController.java b/core/src/main/java/org/teavm/vm/TeaVMTargetController.java index 44f3d5825..88d1186ed 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMTargetController.java +++ b/core/src/main/java/org/teavm/vm/TeaVMTargetController.java @@ -22,6 +22,7 @@ import org.teavm.common.ServiceRepository; import org.teavm.dependency.DependencyInfo; import org.teavm.diagnostics.Diagnostics; import org.teavm.model.ClassReaderSource; +import org.teavm.model.MethodReference; public interface TeaVMTargetController { boolean wasCancelled(); @@ -45,4 +46,6 @@ public interface TeaVMTargetController { Map getEntryPoints(); Set getPreservedClasses(); + + boolean isVirtual(MethodReference method); } diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSAliasRenderer.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSAliasRenderer.java index 4342c4697..591505801 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSAliasRenderer.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSAliasRenderer.java @@ -20,6 +20,8 @@ import java.util.HashMap; import java.util.Map; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.rendering.RenderingManager; +import org.teavm.backend.javascript.spi.VirtualMethodContributor; +import org.teavm.backend.javascript.spi.VirtualMethodContributorContext; import org.teavm.model.AnnotationReader; import org.teavm.model.ClassReader; import org.teavm.model.FieldReader; @@ -27,16 +29,17 @@ import org.teavm.model.FieldReference; import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; import org.teavm.vm.BuildTarget; import org.teavm.vm.spi.RendererListener; -class JSAliasRenderer implements RendererListener { +class JSAliasRenderer implements RendererListener, VirtualMethodContributor { private static String variableChars = "abcdefghijklmnopqrstuvwxyz"; private SourceWriter writer; private ListableClassReaderSource classSource; @Override - public void begin(RenderingManager context, BuildTarget buildTarget) throws IOException { + public void begin(RenderingManager context, BuildTarget buildTarget) { writer = context.getWriter(); classSource = context.getClassSource(); } @@ -172,4 +175,19 @@ class JSAliasRenderer implements RendererListener { } return sb.toString(); } + + @Override + public boolean isVirtual(VirtualMethodContributorContext context, MethodReference methodRef) { + ClassReader classReader = context.getClassSource().get(methodRef.getClassName()); + if (classReader == null) { + return false; + } + + if (getFunctorField(classReader) != null) { + return true; + } + + MethodReader methodReader = classReader.getMethod(methodRef.getDescriptor()); + return methodReader != null && getPublicAlias(methodReader) != null; + } } diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSOPlugin.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSOPlugin.java index 0eaef274f..08f62c680 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSOPlugin.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSOPlugin.java @@ -35,12 +35,12 @@ public class JSOPlugin implements TeaVMPlugin { JSAliasRenderer aliasRenderer = new JSAliasRenderer(); host.add(dependencyListener); - jsHost.add(aliasRenderer); jsHost.addGeneratorProvider(new GeneratorAnnotationInstaller<>(new JSBodyGenerator(), DynamicGenerator.class.getName())); jsHost.addInjectorProvider(new GeneratorAnnotationInstaller<>(new JSBodyGenerator(), DynamicInjector.class.getName())); + jsHost.addVirtualMethods(aliasRenderer); TeaVMPluginUtil.handleNatives(host, JS.class); } diff --git a/platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java b/platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java index 001b1185e..1e1ad40a9 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java +++ b/platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java @@ -19,6 +19,8 @@ import java.io.IOException; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.spi.Generator; import org.teavm.backend.javascript.spi.GeneratorContext; +import org.teavm.backend.javascript.spi.VirtualMethodContributor; +import org.teavm.backend.javascript.spi.VirtualMethodContributorContext; import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; @@ -30,7 +32,7 @@ import org.teavm.model.MethodReference; import org.teavm.model.ValueType; import org.teavm.platform.async.AsyncCallback; -public class AsyncMethodGenerator implements Generator, DependencyPlugin { +public class AsyncMethodGenerator implements Generator, DependencyPlugin, VirtualMethodContributor { private static final MethodReference completeMethod = new MethodReference(AsyncCallback.class, "complete", Object.class, void.class); private static final MethodReference errorMethod = new MethodReference(AsyncCallback.class, "error", @@ -122,4 +124,9 @@ public class AsyncMethodGenerator implements Generator, DependencyPlugin { asyncMethod.use(); } + + @Override + public boolean isVirtual(VirtualMethodContributorContext context, MethodReference methodRef) { + return methodRef.equals(completeMethod) || methodRef.equals(errorMethod); + } } diff --git a/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java b/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java index 498ab45a9..f1f78f0a5 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java +++ b/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java @@ -25,7 +25,12 @@ import org.teavm.backend.javascript.spi.InjectorContext; import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; -import org.teavm.model.*; +import org.teavm.model.CallLocation; +import org.teavm.model.ClassReader; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; import org.teavm.platform.Platform; import org.teavm.platform.PlatformClass; import org.teavm.platform.PlatformRunnable; diff --git a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java index e2d5df5ca..022f1bdca 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java +++ b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java @@ -43,7 +43,8 @@ public class PlatformPlugin implements TeaVMPlugin { host.add(new ResourceTransformer()); host.add(new ResourceAccessorTransformer(host)); host.add(new ResourceAccessorDependencyListener()); - host.getExtension(TeaVMJavaScriptHost.class).addGeneratorProvider(context -> { + TeaVMJavaScriptHost jsHost = host.getExtension(TeaVMJavaScriptHost.class); + jsHost.addGeneratorProvider(context -> { ClassReader cls = context.getClassSource().get(context.getMethod().getClassName()); if (cls == null) { return null; @@ -55,6 +56,7 @@ public class PlatformPlugin implements TeaVMPlugin { return method.getAnnotations().get(Async.class.getName()) != null ? new AsyncMethodGenerator() : null; }); + jsHost.addVirtualMethods(new AsyncMethodGenerator()); } else if (!isBootstrap()) { host.add(new StringAmplifierTransformer()); }