From ff19dc15c98cb58c5b0cd262e7a9c03638145548 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 20 Nov 2017 23:05:01 +0300 Subject: [PATCH] Further bootstrapping --- .../org/teavm/classlib/impl/JCLPlugin.java | 11 ++- .../org/teavm/classlib/java/lang/TSystem.java | 1 + .../teavm/ast/decompilation/Decompiler.java | 10 ++- .../backend/javascript/JavaScriptTarget.java | 78 +++++++++++++++++-- .../backend/javascript/ProviderContext.java | 25 ++++++ .../javascript/TeaVMJavaScriptHost.java | 5 ++ .../javascript/rendering/Renderer.java | 2 +- .../rendering/RenderingContext.java | 24 ++++-- .../teavm/dependency/DependencyAnalyzer.java | 21 +++++ .../teavm/model/util/AsyncMethodFinder.java | 2 +- core/src/main/java/org/teavm/vm/TeaVM.java | 18 ++++- .../java/org/teavm/vm/TeaVMPluginUtil.java | 7 +- .../main/java/org/teavm/vm/spi/TeaVMHost.java | 3 + .../org/teavm/jso/impl/DynamicGenerator.java | 26 +++++++ .../org/teavm/jso/impl/DynamicInjector.java | 26 +++++++ .../impl/GeneratorAnnotationInstaller.java | 50 ++++++++++++ .../org/teavm/jso/impl/JSClassProcessor.java | 6 +- .../java/org/teavm/jso/impl/JSOPlugin.java | 14 +++- .../impl/reflect/ReflectClassImpl.java | 2 +- .../teavm/platform/plugin/PlatformPlugin.java | 4 +- 20 files changed, 298 insertions(+), 37 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/javascript/ProviderContext.java create mode 100644 jso/impl/src/main/java/org/teavm/jso/impl/DynamicGenerator.java create mode 100644 jso/impl/src/main/java/org/teavm/jso/impl/DynamicInjector.java create mode 100644 jso/impl/src/main/java/org/teavm/jso/impl/GeneratorAnnotationInstaller.java 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 bd22aceb0..026fbe670 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java @@ -15,6 +15,7 @@ */ package org.teavm.classlib.impl; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; import java.util.ServiceLoader; @@ -22,12 +23,12 @@ import org.teavm.backend.javascript.TeaVMJavaScriptHost; import org.teavm.classlib.ReflectionSupplier; import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor; import org.teavm.classlib.impl.unicode.CLDRReader; -import org.teavm.classlib.java.lang.SystemNativeGenerator; import org.teavm.classlib.java.lang.reflect.AnnotationDependencyListener; import org.teavm.interop.PlatformMarker; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; import org.teavm.platform.PlatformClass; +import org.teavm.vm.TeaVMPluginUtil; import org.teavm.vm.spi.TeaVMHost; import org.teavm.vm.spi.TeaVMPlugin; @@ -86,9 +87,11 @@ public class JCLPlugin implements TeaVMPlugin { host.add(new PlatformMarkerSupport()); } - TeaVMJavaScriptHost jsHost = host.getExtension(TeaVMJavaScriptHost.class); - jsHost.add(new MethodReference("java.lang.System", "currentTimeMillis", ValueType.LONG), - new SystemNativeGenerator()); + TeaVMPluginUtil.handleNatives(host, Class.class); + TeaVMPluginUtil.handleNatives(host, ClassLoader.class); + TeaVMPluginUtil.handleNatives(host, System.class); + TeaVMPluginUtil.handleNatives(host, Array.class); + TeaVMPluginUtil.handleNatives(host, Math.class); } @PlatformMarker diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java index 39d0644ff..0135f4986 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java @@ -102,6 +102,7 @@ public final class TSystem extends TObject { } @DelegateTo("currentTimeMillisLowLevel") + @GeneratedBy(SystemNativeGenerator.class) public static native long currentTimeMillis(); private static long currentTimeMillisLowLevel() { diff --git a/core/src/main/java/org/teavm/ast/decompilation/Decompiler.java b/core/src/main/java/org/teavm/ast/decompilation/Decompiler.java index 268364a09..5248ac808 100644 --- a/core/src/main/java/org/teavm/ast/decompilation/Decompiler.java +++ b/core/src/main/java/org/teavm/ast/decompilation/Decompiler.java @@ -53,6 +53,7 @@ import org.teavm.common.GraphIndexer; import org.teavm.common.Loop; import org.teavm.common.LoopGraph; import org.teavm.common.RangeTree; +import org.teavm.interop.PlatformMarker; import org.teavm.model.AnnotationHolder; import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderSource; @@ -200,7 +201,7 @@ public class Decompiler { if (method.getModifiers().contains(ElementModifier.ABSTRACT)) { continue; } - if (method.getAnnotations().get(InjectedBy.class.getName()) != null + if ((!isBootstrap() && method.getAnnotations().get(InjectedBy.class.getName()) != null) || methodsToSkip.contains(method.getReference())) { continue; } @@ -220,7 +221,7 @@ public class Decompiler { public NativeMethodNode decompileNative(MethodHolder method) { Generator generator = generators.get(method.getReference()); - if (generator == null) { + if (generator == null && !isBootstrap()) { AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName()); if (annotHolder == null) { throw new DecompilationException("Method " + method.getOwnerName() + "." + method.getDescriptor() @@ -244,6 +245,11 @@ public class Decompiler { return methodNode; } + @PlatformMarker + private static boolean isBootstrap() { + return false; + } + public RegularMethodNode decompileRegular(MethodHolder method) { if (regularMethodCache == null || method.getAnnotations().get(NoCache.class.getName()) != null) { return decompileRegularCacheMiss(method); 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 c88590ad8..2f2421cf3 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -26,6 +26,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import org.teavm.ast.ClassNode; import org.teavm.ast.cache.EmptyRegularMethodNodeCache; import org.teavm.ast.cache.MethodNodeCache; @@ -48,10 +49,12 @@ import org.teavm.debugging.information.SourceLocation; import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyListener; import org.teavm.dependency.MethodDependency; +import org.teavm.interop.PlatformMarker; import org.teavm.model.BasicBlock; import org.teavm.model.CallLocation; import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassReaderSource; @@ -83,6 +86,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { private boolean minifying = true; private final Map methodGenerators = new HashMap<>(); private final Map methodInjectors = new HashMap<>(); + private final List> generatorProviders = new ArrayList<>(); + private final List> injectorProviders = new ArrayList<>(); private final List rendererListeners = new ArrayList<>(); private DebugInformationEmitter debugEmitter; private MethodNodeCache astCache = new EmptyRegularMethodNodeCache(); @@ -121,6 +126,16 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { methodInjectors.put(methodRef, injector); } + @Override + public void addGeneratorProvider(Function provider) { + generatorProviders.add(provider); + } + + @Override + public void addInjectorProvider(Function provider) { + injectorProviders.add(provider); + } + /** * Reports whether this TeaVM instance uses obfuscation when generating the JavaScript code. * @@ -309,7 +324,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { for (String className : classOrder) { ClassHolder cls = classes.get(className); for (MethodHolder method : cls.getMethods()) { - preprocessNativeMethod(method); + preprocessNativeMethod(method, decompiler); if (controller.wasCancelled()) { break; } @@ -319,14 +334,44 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { return classNodes; } - private void preprocessNativeMethod(MethodHolder method) { + private void preprocessNativeMethod(MethodHolder method, Decompiler decompiler) { if (!method.getModifiers().contains(ElementModifier.NATIVE) || methodGenerators.get(method.getReference()) != null - || methodInjectors.get(method.getReference()) != null - || method.getAnnotations().get(GeneratedBy.class.getName()) != null - || method.getAnnotations().get(InjectedBy.class.getName()) != null) { + || methodInjectors.get(method.getReference()) != null) { return; } + + boolean found = false; + ProviderContext context = new ProviderContextImpl(method.getReference()); + for (Function provider : generatorProviders) { + Generator generator = provider.apply(context); + if (generator != null) { + methodGenerators.put(method.getReference(), generator); + decompiler.addGenerator(method.getReference(), generator); + found = true; + break; + } + } + for (Function provider : injectorProviders) { + Injector injector = provider.apply(context); + if (injector != null) { + methodInjectors.put(method.getReference(), injector); + decompiler.addMethodToSkip(method.getReference()); + found = true; + break; + } + } + + if (found) { + return; + } + + if (!isBootstrap()) { + if (method.getAnnotations().get(GeneratedBy.class.getName()) != null + || method.getAnnotations().get(InjectedBy.class.getName()) != null) { + return; + } + } method.getModifiers().remove(ElementModifier.NATIVE); Program program = new Program(); @@ -364,6 +409,29 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { "Native method {{m0}} has no implementation", method.getReference()); } + class ProviderContextImpl implements ProviderContext { + private MethodReference method; + + ProviderContextImpl(MethodReference method) { + this.method = method; + } + + @Override + public MethodReference getMethod() { + return method; + } + + @Override + public ClassReaderSource getClassSource() { + return controller.getUnprocessedClassSource(); + } + } + + @PlatformMarker + private static boolean isBootstrap() { + return false; + } + private void emitCFG(DebugInformationEmitter emitter, Program program) { Map cfg = ProgramUtils.getLocationCFG(program); for (Map.Entry entry : cfg.entrySet()) { diff --git a/core/src/main/java/org/teavm/backend/javascript/ProviderContext.java b/core/src/main/java/org/teavm/backend/javascript/ProviderContext.java new file mode 100644 index 000000000..4712fa8ae --- /dev/null +++ b/core/src/main/java/org/teavm/backend/javascript/ProviderContext.java @@ -0,0 +1,25 @@ +/* + * Copyright 2017 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; + +import org.teavm.model.ClassReaderSource; +import org.teavm.model.MethodReference; + +public interface ProviderContext { + MethodReference getMethod(); + + ClassReaderSource getClassSource(); +} 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 39f1b1b8c..051a52b3c 100644 --- a/core/src/main/java/org/teavm/backend/javascript/TeaVMJavaScriptHost.java +++ b/core/src/main/java/org/teavm/backend/javascript/TeaVMJavaScriptHost.java @@ -15,6 +15,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.model.MethodReference; @@ -26,5 +27,9 @@ public interface TeaVMJavaScriptHost extends TeaVMHostExtension { void add(MethodReference methodRef, Injector injector); + void addGeneratorProvider(Function provider); + + void addInjectorProvider(Function provider); + void add(RendererListener listener); } 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 c60bf6223..fc9287e5f 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 @@ -176,7 +176,7 @@ public class Renderer implements RenderingManager { private void renderSetCloneMethod() throws IOException { writer.append("function $rt_setCloneMethod(target, f)").ws().append("{").softNewLine().indent(); writer.append("target.").appendMethod("clone", Object.class).ws().append('=').ws().append("f;"). - softNewLine().indent(); + softNewLine(); writer.outdent().append("}").newLine(); } 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 d44a47936..4857219b4 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 @@ -30,6 +30,7 @@ import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.backend.javascript.spi.Injector; import org.teavm.common.ServiceRepository; import org.teavm.debugging.information.DebugInformationEmitter; +import org.teavm.interop.PlatformMarker; import org.teavm.model.AnnotationReader; import org.teavm.model.ClassReader; import org.teavm.model.ListableClassReaderSource; @@ -243,14 +244,16 @@ public class RenderingContext { InjectorHolder holder = injectorMap.get(ref); if (holder == null) { holder = new InjectorHolder(null); - ClassReader cls = classSource.get(ref.getClassName()); - if (cls != null) { - MethodReader method = cls.getMethod(ref.getDescriptor()); - if (method != null) { - AnnotationReader injectedByAnnot = method.getAnnotations().get(InjectedBy.class.getName()); - if (injectedByAnnot != null) { - ValueType type = injectedByAnnot.getValue("value").getJavaClass(); - holder = new InjectorHolder(instantiateInjector(((ValueType.Object) type).getClassName())); + if (!isBootstrap()) { + ClassReader cls = classSource.get(ref.getClassName()); + if (cls != null) { + MethodReader method = cls.getMethod(ref.getDescriptor()); + if (method != null) { + AnnotationReader injectedByAnnot = method.getAnnotations().get(InjectedBy.class.getName()); + if (injectedByAnnot != null) { + ValueType type = injectedByAnnot.getValue("value").getJavaClass(); + holder = new InjectorHolder(instantiateInjector(((ValueType.Object) type).getClassName())); + } } } } @@ -259,6 +262,11 @@ public class RenderingContext { return holder.injector; } + @PlatformMarker + private static boolean isBootstrap() { + return false; + } + private Injector instantiateInjector(String type) { try { Class cls = Class.forName(type, true, classLoader).asSubclass(Injector.class); diff --git a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java index 07504e634..2a1a0a5f0 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java @@ -35,6 +35,7 @@ import org.teavm.common.CachedMapper; import org.teavm.common.Mapper; import org.teavm.common.ServiceRepository; import org.teavm.diagnostics.Diagnostics; +import org.teavm.interop.PlatformMarker; import org.teavm.model.AnnotationReader; import org.teavm.model.CallLocation; import org.teavm.model.ClassHolder; @@ -83,6 +84,7 @@ public class DependencyAnalyzer implements DependencyInfo { DefaultCallGraph callGraph = new DefaultCallGraph(); private DependencyAgent agent; Map bootstrapMethodSubstitutors = new HashMap<>(); + Map dependencyPlugins = new HashMap<>(); private boolean completing; private Map superClassFilters = new HashMap<>(); @@ -320,6 +322,10 @@ public class DependencyAnalyzer implements DependencyInfo { private Set classesAddedByRoot = new HashSet<>(); + public void defer(Runnable task) { + deferredTasks.add(task); + } + public ClassDependency linkClass(String className, CallLocation callLocation) { if (completing && getClass(className) == null) { throw new IllegalStateException("Can't link class during completion phase"); @@ -526,6 +532,12 @@ public class DependencyAnalyzer implements DependencyInfo { return; } methodDep.dependencyPluginAttached = true; + + methodDep.dependencyPlugin = dependencyPlugins.get(methodDep.getReference()); + if (methodDep.dependencyPlugin != null || isBootstrap()) { + return; + } + AnnotationReader depAnnot = methodDep.getMethod().getAnnotations().get(PluggableDependency.class.getName()); if (depAnnot == null) { return; @@ -545,6 +557,11 @@ public class DependencyAnalyzer implements DependencyInfo { } } + @PlatformMarker + private static boolean isBootstrap() { + return false; + } + @Override public MethodDependency getMethod(MethodReference methodRef) { return methodCache.getKnown(methodRef); @@ -660,6 +677,10 @@ public class DependencyAnalyzer implements DependencyInfo { bootstrapMethodSubstitutors.put(method, substitutor); } + public void addDependencyPlugin(MethodReference method, DependencyPlugin dependencyPlugin) { + dependencyPlugins.put(method, dependencyPlugin); + } + SuperClassFilter getSuperClassFilter(String superClass) { return superClassFilters.computeIfAbsent(superClass, s -> new SuperClassFilter(classSource, s)); } diff --git a/core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java b/core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java index 94d16306c..f6668b9e5 100644 --- a/core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java +++ b/core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java @@ -164,7 +164,7 @@ public class AsyncMethodFinder { return; } else { diagnostics.warning(new CallLocation(methodRef), "Error as Warning because " - + " Method {{m0}} has @SuppressSyncErrors annoation. Method {{m0}} " + + " Method {{m0}} has @SuppressSyncErrors annotation. Method {{m0}} " + "is claimed to be synchronous, but it is has invocations of " + "asynchronous methods:" + stack.toString(), methodRef); } diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 53d706e5d..531246598 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -33,6 +33,7 @@ import org.teavm.dependency.BootstrapMethodSubstitutor; import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyInfo; import org.teavm.dependency.DependencyListener; +import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.Linker; import org.teavm.diagnostics.AccumulationDiagnostics; import org.teavm.diagnostics.Diagnostics; @@ -164,6 +165,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository { dependencyAnalyzer.addBootstrapMethodSubstitutor(methodRef, substitutor); } + @Override + public void add(MethodReference methodRef, DependencyPlugin dependencyPlugin) { + dependencyAnalyzer.addDependencyPlugin(methodRef, dependencyPlugin); + } + @Override public ClassLoader getClassLoader() { return classLoader; @@ -249,7 +255,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } } TeaVMEntryPoint entryPoint = new TeaVMEntryPoint(name, ref, dependencyAnalyzer.linkMethod(ref, null)); - dependencyAnalyzer.linkClass(ref.getClassName(), null).initClass(null); + dependencyAnalyzer.defer(() -> { + dependencyAnalyzer.linkClass(ref.getClassName(), null).initClass(null); + }); if (name != null) { entryPoints.put(name, entryPoint); } @@ -274,7 +282,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository { public TeaVMEntryPoint linkMethod(MethodReference ref) { TeaVMEntryPoint entryPoint = new TeaVMEntryPoint("", ref, dependencyAnalyzer.linkMethod(ref, null)); - dependencyAnalyzer.linkClass(ref.getClassName(), null).initClass(null); + dependencyAnalyzer.defer(() -> { + dependencyAnalyzer.linkClass(ref.getClassName(), null).initClass(null); + }); return entryPoint; } @@ -283,7 +293,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository { throw new IllegalArgumentException("Class with public name `" + name + "' already defined for class " + className); } - dependencyAnalyzer.linkClass(className, null).initClass(null); + dependencyAnalyzer.defer(() -> { + dependencyAnalyzer.linkClass(className, null).initClass(null); + }); exportedClasses.put(name, className); } diff --git a/core/src/main/java/org/teavm/vm/TeaVMPluginUtil.java b/core/src/main/java/org/teavm/vm/TeaVMPluginUtil.java index 14159566d..d917d3b8c 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMPluginUtil.java +++ b/core/src/main/java/org/teavm/vm/TeaVMPluginUtil.java @@ -23,6 +23,7 @@ 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.dependency.DependencyPlugin; import org.teavm.dependency.PluggableDependency; import org.teavm.interop.PlatformMarker; import org.teavm.metaprogramming.CompileTime; @@ -58,8 +59,6 @@ public final class TeaVMPluginUtil { private static void handleNativesImpl(Value host, Value jsHost, ReflectClass cls) { - - for (ReflectMethod method : cls.getDeclaredMethods()) { if (!Modifier.isNative(method.getModifiers())) { continue; @@ -89,7 +88,7 @@ public final class TeaVMPluginUtil { ReflectMethod generatorConstructor = generatorClass.getMethod(""); Value methodRef = methodToReference(method); - //emit(() -> host.get().add(methodRef.get(), (DependencyPlugin) generatorConstructor.construct())); + emit(() -> host.get().add(methodRef.get(), (DependencyPlugin) generatorConstructor.construct())); } } } @@ -102,6 +101,8 @@ public final class TeaVMPluginUtil { int index = i; emit(() -> signature.get()[index] = paramType.get()); } + Value returnType = classToValueType(method.getReturnType()); + emit(() -> signature.get()[signatureSize - 1] = returnType.get()); String className = method.getDeclaringClass().getName(); String name = method.getName(); diff --git a/core/src/main/java/org/teavm/vm/spi/TeaVMHost.java b/core/src/main/java/org/teavm/vm/spi/TeaVMHost.java index b247b296f..52cfb86a7 100644 --- a/core/src/main/java/org/teavm/vm/spi/TeaVMHost.java +++ b/core/src/main/java/org/teavm/vm/spi/TeaVMHost.java @@ -18,6 +18,7 @@ package org.teavm.vm.spi; import java.util.Properties; import org.teavm.dependency.BootstrapMethodSubstitutor; import org.teavm.dependency.DependencyListener; +import org.teavm.dependency.DependencyPlugin; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.MethodReference; import org.teavm.vm.TeaVM; @@ -36,6 +37,8 @@ public interface TeaVMHost { void add(MethodReference methodRef, BootstrapMethodSubstitutor substitutor); + void add(MethodReference methodRef, DependencyPlugin dependencyPlugin); + T getExtension(Class extensionType); void registerService(Class type, T instance); diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/DynamicGenerator.java b/jso/impl/src/main/java/org/teavm/jso/impl/DynamicGenerator.java new file mode 100644 index 000000000..ae9940ebe --- /dev/null +++ b/jso/impl/src/main/java/org/teavm/jso/impl/DynamicGenerator.java @@ -0,0 +1,26 @@ +/* + * Copyright 2017 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.impl; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.METHOD) +@interface DynamicGenerator { +} diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/DynamicInjector.java b/jso/impl/src/main/java/org/teavm/jso/impl/DynamicInjector.java new file mode 100644 index 000000000..152cdf0e9 --- /dev/null +++ b/jso/impl/src/main/java/org/teavm/jso/impl/DynamicInjector.java @@ -0,0 +1,26 @@ +/* + * Copyright 2017 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.impl; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.METHOD) +@interface DynamicInjector { +} diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/GeneratorAnnotationInstaller.java b/jso/impl/src/main/java/org/teavm/jso/impl/GeneratorAnnotationInstaller.java new file mode 100644 index 000000000..b68797223 --- /dev/null +++ b/jso/impl/src/main/java/org/teavm/jso/impl/GeneratorAnnotationInstaller.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017 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.impl; + +import java.util.function.Function; +import org.teavm.backend.javascript.ProviderContext; +import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; + +class GeneratorAnnotationInstaller implements Function { + private T generator; + private String annotationName; + + GeneratorAnnotationInstaller(T generator, String annotationName) { + this.generator = generator; + this.annotationName = annotationName; + } + + @Override + public T apply(ProviderContext providerContext) { + ClassReaderSource classSource = providerContext.getClassSource(); + MethodReference methodRef = providerContext.getMethod(); + ClassReader cls = classSource.get(methodRef.getClassName()); + if (cls == null) { + return null; + } + + MethodReader method = cls.getMethod(methodRef.getDescriptor()); + if (method == null) { + return null; + } + + return method.getAnnotations().get(annotationName) != null ? generator : null; + } +} diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java index 71e38ae19..aaf290d3f 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java @@ -30,8 +30,6 @@ import org.mozilla.javascript.Context; import org.mozilla.javascript.ast.AstNode; import org.mozilla.javascript.ast.AstRoot; import org.mozilla.javascript.ast.FunctionNode; -import org.teavm.backend.javascript.spi.GeneratedBy; -import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.cache.NoCache; import org.teavm.diagnostics.Diagnostics; import org.teavm.interop.Sync; @@ -592,9 +590,7 @@ class JSClassProcessor { proxyMethod.getModifiers().add(ElementModifier.STATIC); boolean inline = repository.inlineMethods.contains(methodRef); AnnotationHolder generatorAnnot = new AnnotationHolder(inline - ? InjectedBy.class.getName() : GeneratedBy.class.getName()); - generatorAnnot.getValues().put("value", - new AnnotationValue(ValueType.parse(JSBodyGenerator.class))); + ? DynamicInjector.class.getName() : DynamicGenerator.class.getName()); proxyMethod.getAnnotations().add(generatorAnnot); cls.addMethod(proxyMethod); 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 ec848d1d3..0eaef274f 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 @@ -16,13 +16,15 @@ package org.teavm.jso.impl; import org.teavm.backend.javascript.TeaVMJavaScriptHost; +import org.teavm.vm.TeaVMPluginUtil; import org.teavm.vm.spi.TeaVMHost; import org.teavm.vm.spi.TeaVMPlugin; public class JSOPlugin implements TeaVMPlugin { @Override public void install(TeaVMHost host) { - if (host.getExtension(TeaVMJavaScriptHost.class) == null) { + TeaVMJavaScriptHost jsHost = host.getExtension(TeaVMJavaScriptHost.class); + if (jsHost == null) { return; } @@ -32,6 +34,14 @@ public class JSOPlugin implements TeaVMPlugin { JSDependencyListener dependencyListener = new JSDependencyListener(repository); JSAliasRenderer aliasRenderer = new JSAliasRenderer(); host.add(dependencyListener); - host.getExtension(TeaVMJavaScriptHost.class).add(aliasRenderer); + + + jsHost.add(aliasRenderer); + jsHost.addGeneratorProvider(new GeneratorAnnotationInstaller<>(new JSBodyGenerator(), + DynamicGenerator.class.getName())); + jsHost.addInjectorProvider(new GeneratorAnnotationInstaller<>(new JSBodyGenerator(), + DynamicInjector.class.getName())); + + TeaVMPluginUtil.handleNatives(host, JS.class); } } diff --git a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/reflect/ReflectClassImpl.java b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/reflect/ReflectClassImpl.java index e570baca0..1e904bf99 100644 --- a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/reflect/ReflectClassImpl.java +++ b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/reflect/ReflectClassImpl.java @@ -60,7 +60,7 @@ public class ReflectClassImpl implements ReflectClass { @Override public boolean isPrimitive() { - return type instanceof ValueType.Primitive; + return type instanceof ValueType.Primitive || type == ValueType.VOID; } @Override 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 14f6bda5b..23dd3ccf5 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java +++ b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java @@ -22,10 +22,9 @@ import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.interop.PlatformMarker; -import org.teavm.metaprogramming.Meta; -import org.teavm.metaprogramming.Value; import org.teavm.model.MethodReference; import org.teavm.platform.Platform; +import org.teavm.platform.PlatformQueue; import org.teavm.vm.TeaVMPluginUtil; import org.teavm.vm.spi.TeaVMHost; import org.teavm.vm.spi.TeaVMPlugin; @@ -71,6 +70,7 @@ public class PlatformPlugin implements TeaVMPlugin { host.add(new PlatformDependencyListener()); TeaVMPluginUtil.handleNatives(host, Platform.class); + TeaVMPluginUtil.handleNatives(host, PlatformQueue.class); } @PlatformMarker