From 6738d6f1f810821275487a2f37feaeb0fcdf7cc2 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 1 Nov 2023 19:43:52 +0100 Subject: [PATCH] JS: add remembering source writer, refactor PreparedMethod and Renderer to use it, get rid of $rt_globals --- .../java/lang/LongNativeGenerator.java | 1 - .../java/lang/MathNativeGenerator.java | 2 +- .../backend/javascript/JavaScriptTarget.java | 200 ++++----- .../javascript/TeaVMJavaScriptHost.java | 3 - .../javascript/codegen/AliasProvider.java | 2 + .../codegen/DefaultAliasProvider.java | 4 + .../codegen/DefaultNamingStrategy.java | 5 + .../codegen/MinifyingAliasProvider.java | 5 + .../codegen/NameFrequencyConsumer.java | 4 + .../javascript/codegen/NamingOrderer.java | 21 + .../javascript/codegen/NamingStrategy.java | 2 + .../codegen/OutputSourceWriter.java | 5 + .../javascript/codegen/RememberedSource.java | 216 +++++++++ .../codegen/RememberingSourceWriter.java | 345 +++++++++++++++ .../javascript/codegen/SourceWriter.java | 25 +- .../javascript/codegen/SourceWriterSink.java | 108 +++++ .../javascript/decompile/PreparedMethod.java | 30 +- .../PreparedVariable.java} | 20 +- .../rendering/DefaultGlobalNameWriter.java | 2 +- .../rendering/MethodBodyRenderer.java | 361 +++++++++++++++ .../rendering/NameFrequencyEstimator.java | 411 ++---------------- .../javascript/rendering/Renderer.java | 351 +-------------- .../rendering/StatementRenderer.java | 16 +- .../javascript/spi/GeneratorContext.java | 4 - .../javascript/spi/ModuleImporterContext.java | 36 -- .../templating/TemplatingAstWriter.java | 3 +- .../java/org/teavm/jso/impl/JSOPlugin.java | 5 - .../jso/impl/JsBodyImportsContributor.java | 34 -- 28 files changed, 1277 insertions(+), 944 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/javascript/codegen/RememberedSource.java create mode 100644 core/src/main/java/org/teavm/backend/javascript/codegen/RememberingSourceWriter.java create mode 100644 core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriterSink.java rename core/src/main/java/org/teavm/backend/javascript/{spi/ModuleImporter.java => decompile/PreparedVariable.java} (54%) create mode 100644 core/src/main/java/org/teavm/backend/javascript/rendering/MethodBodyRenderer.java delete mode 100644 core/src/main/java/org/teavm/backend/javascript/spi/ModuleImporterContext.java delete mode 100644 jso/impl/src/main/java/org/teavm/jso/impl/JsBodyImportsContributor.java diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java index 5e6ed5b71..a26c6e1d0 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java @@ -43,6 +43,5 @@ public class LongNativeGenerator implements Generator { writer.append("return ").appendFunction(name).append("(").append(context.getParameterName(1)) .append(",").ws() .append(context.getParameterName(2)).append(");").softNewLine(); - context.useLongLibrary(); } } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/MathNativeGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/MathNativeGenerator.java index cd9b1c137..27ae52775 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/MathNativeGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/MathNativeGenerator.java @@ -34,7 +34,7 @@ public class MathNativeGenerator implements Generator { } private void function(GeneratorContext context, SourceWriter writer, String name, int paramCount) { - writer.append("return ").append("$rt_globals.Math").append(".").append(name).append("("); + writer.append("return ").appendGlobal("Math").append(".").append(name).append("("); for (int i = 0; i < paramCount; ++i) { if (i > 0) { writer.append(",").ws(); 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 118924665..eae64fe82 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -42,6 +42,7 @@ import java.util.Set; import java.util.function.Function; import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.ControlFlowEntry; +import org.teavm.ast.MethodNode; import org.teavm.ast.RegularMethodNode; import org.teavm.ast.analysis.LocationGraphBuilder; import org.teavm.ast.decompilation.DecompilationException; @@ -59,6 +60,7 @@ import org.teavm.backend.javascript.intrinsics.ref.ReferenceQueueTransformer; import org.teavm.backend.javascript.intrinsics.ref.WeakReferenceDependencyListener; import org.teavm.backend.javascript.intrinsics.ref.WeakReferenceGenerator; import org.teavm.backend.javascript.intrinsics.ref.WeakReferenceTransformer; +import org.teavm.backend.javascript.rendering.MethodBodyRenderer; import org.teavm.backend.javascript.rendering.Renderer; import org.teavm.backend.javascript.rendering.RenderingContext; import org.teavm.backend.javascript.rendering.RenderingUtil; @@ -67,8 +69,6 @@ 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.ModuleImporter; -import org.teavm.backend.javascript.spi.ModuleImporterContext; import org.teavm.backend.javascript.spi.VirtualMethodContributor; import org.teavm.backend.javascript.spi.VirtualMethodContributorContext; import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory; @@ -100,7 +100,6 @@ import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; import org.teavm.model.ListableClassHolderSource; -import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodHolder; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; @@ -138,7 +137,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { private final Map methodInjectors = new HashMap<>(); private final List> generatorProviders = new ArrayList<>(); private final List> injectorProviders = new ArrayList<>(); - private final List> moduleImporterProviders = new ArrayList<>(); private final List rendererListeners = new ArrayList<>(); private DebugInformationEmitter debugEmitter; private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE; @@ -153,6 +151,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { private final Map importedModules = new LinkedHashMap<>(); private Map generatorCache = new HashMap<>(); private JavaScriptTemplateFactory templateFactory; + private boolean threadLibraryUsed; + private MethodBodyRenderer bodyRenderer; @Override public List getTransformers() { @@ -210,11 +210,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { injectorProviders.add(provider); } - @Override - public void addModuleImporterProvider(Function provider) { - moduleImporterProviders.add(provider); - } - /** * Specifies whether this TeaVM instance uses obfuscation when generating the JavaScript code. * @@ -404,25 +399,14 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { } private void emit(ListableClassHolderSource classes, Writer writer, BuildTarget target) { - List clsNodes = modelToAst(classes); - prepareModules(classes); - if (controller.wasCancelled()) { - return; - } - AliasProvider aliasProvider = obfuscated ? new MinifyingAliasProvider(topLevelNameLimit) : new DefaultAliasProvider(topLevelNameLimit); DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, controller.getUnprocessedClassSource()); - OutputSourceWriterBuilder builder = new OutputSourceWriterBuilder(naming); - builder.setMinified(obfuscated); - var sourceWriter = builder.build(writer); - DebugInformationEmitter debugEmitterToUse = debugEmitter; if (debugEmitterToUse == null) { debugEmitterToUse = new DummyDebugInformationEmitter(); } - sourceWriter.setDebugInformationEmitter(debugEmitterToUse); var virtualMethodContributorContext = new VirtualMethodContributorContextImpl(classes); RenderingContext renderingContext = new RenderingContext(debugEmitterToUse, @@ -437,8 +421,22 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { } }; renderingContext.setMinifying(obfuscated); - Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods, - controller.getDiagnostics(), renderingContext); + + bodyRenderer = new MethodBodyRenderer(renderingContext, controller.getDiagnostics(), obfuscated, + debugEmitter != null, asyncMethods); + + var clsNodes = modelToAst(classes, renderingContext); + if (controller.wasCancelled()) { + return; + } + + var builder = new OutputSourceWriterBuilder(naming); + builder.setMinified(obfuscated); + var sourceWriter = builder.build(writer); + + sourceWriter.setDebugInformationEmitter(debugEmitterToUse); + + var renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods, renderingContext); RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter); renderer.setProperties(controller.getProperties()); renderer.setMinifying(obfuscated); @@ -456,9 +454,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { } renderer.setDebugEmitter(debugEmitter); } - for (var entry : methodInjectors.entrySet()) { - renderingContext.addInjector(entry.getKey(), entry.getValue()); - } + renderer.prepare(clsNodes); printWrapperStart(sourceWriter); @@ -467,7 +463,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { } int start = sourceWriter.getOffset(); - renderer.prepare(clsNodes); runtimeRenderer.renderRuntime(); sourceWriter.append("var ").append(renderer.getNaming().getScopeName()).ws().append("=").ws() .append("Object.create(null);").newLine(); @@ -479,22 +474,21 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { renderer.renderStringConstants(); renderer.renderCompatibilityStubs(); - if (renderer.isLongLibraryUsed()) { - runtimeRenderer.renderHandWrittenRuntime("long.js"); - } - if (renderer.isThreadLibraryUsed()) { + runtimeRenderer.renderHandWrittenRuntime("long.js"); + if (threadLibraryUsed) { runtimeRenderer.renderHandWrittenRuntime("thread.js"); } else { runtimeRenderer.renderHandWrittenRuntime("simpleThread.js"); } for (var entry : controller.getEntryPoints().entrySet()) { - sourceWriter.append("$rt_exports.").append(entry.getKey()).ws().append("=").ws(); + sourceWriter.appendFunction("$rt_exports").append(".").append(entry.getKey()).ws().append("=").ws(); var ref = entry.getValue().getMethod(); sourceWriter.appendFunction("$rt_mainStarter").append("(").appendMethodBody(ref); sourceWriter.append(");").newLine(); - sourceWriter.append("$rt_exports.").append(entry.getKey()).append(".").append("javaException") - .ws().append("=").ws().appendFunction("$rt_javaException").append(";").newLine(); + sourceWriter.appendFunction("$rt_exports").append(".").append(entry.getKey()).append(".") + .append("javaException").ws().append("=").ws().appendFunction("$rt_javaException") + .append(";").newLine(); } for (var listener : rendererListeners) { @@ -510,7 +504,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { private void printWrapperStart(SourceWriter writer) { writer.append("\"use strict\";").newLine(); printUmdStart(writer); - writer.append("function($rt_globals,").ws().append("$rt_exports"); + writer.append("function(").appendFunction("$rt_exports"); for (var moduleName : importedModules.values()) { writer.append(",").ws().appendFunction(moduleName); } @@ -518,11 +512,11 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { } private String importModule(String name) { - return importedModules.get(name); + return importedModules.computeIfAbsent(name, n -> "$rt_imported_" + importedModules.size()); } private void printUmdStart(SourceWriter writer) { - writer.append("(function(root,").ws().append("module)").appendBlockStart(); + writer.append("(function(module)").appendBlockStart(); writer.appendIf().append("typeof define").ws().append("===").ws().append("'function'") .ws().append("&&").ws().append("define.amd)").appendBlockStart(); writer.append("define(['exports'"); @@ -534,7 +528,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { writer.append(',').ws().appendFunction(moduleAlias); } writer.append(")").ws().appendBlockStart(); - writer.append("module(root,").ws().append("exports"); + writer.append("module(exports"); for (var moduleAlias : importedModules.values()) { writer.append(',').ws().appendFunction(moduleAlias); } @@ -545,19 +539,21 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { .append("===").ws().append("'object'").ws().append("&&").ws() .append("exports").ws().append("!==").ws().append("null").ws().append("&&").ws() .append("typeof exports.nodeName").ws().append("!==").ws().append("'string')").appendBlockStart(); - writer.append("module(global,").ws().append("exports"); + writer.append("module(exports"); for (var moduleName : importedModules.keySet()) { writer.append(',').ws().append("require(\"").append(RenderingUtil.escapeString(moduleName)).append("\")"); } writer.append(");").softNewLine(); writer.appendElse(); - writer.append("module(root,").ws().append("root);").softNewLine(); - writer.appendBlockEnd(); - writer.outdent().append("}(typeof self").ws().append("!==").ws().append("'undefined'") + writer.append("module("); + writer.outdent().append("typeof self").ws().append("!==").ws().append("'undefined'") .ws().append("?").ws().append("self") - .ws().append(":").ws().append("this,") - .ws(); + .ws().append(":").ws().append("this"); + + writer.append(");").softNewLine(); + writer.appendBlockEnd(); + writer.outdent().append("}("); } private void printWrapperEnd(SourceWriter writer) { @@ -592,29 +588,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { return STATS_NUM_FORMAT.format(size) + " (" + STATS_PERCENT_FORMAT.format((double) size / totalSize) + ")"; } - private void prepareModules(ListableClassHolderSource classes) { - var context = new ImporterContext(classes); - for (var className : classes.getClassNames()) { - var cls = classes.get(className); - for (var method : cls.getMethods()) { - if (method.getModifiers().contains(ElementModifier.ABSTRACT)) { - continue; - } - - var providerContext = new ProviderContextImpl(method.getReference()); - for (var provider : moduleImporterProviders) { - var importer = provider.apply(providerContext); - if (importer != null) { - context.method = method; - importer.importModules(context); - context.method = null; - } - } - } - } - } - - private List modelToAst(ListableClassHolderSource classes) { + private List modelToAst(ListableClassHolderSource classes, RenderingContext context) { AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(), controller.getDependencyInfo()); asyncFinder.find(classes); @@ -634,7 +608,16 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { break; } } - classNodes.add(decompile(decompiler, cls, classes)); + } + for (var entry : methodInjectors.entrySet()) { + context.addInjector(entry.getKey(), entry.getValue()); + } + for (String className : getClassOrdering(classes)) { + ClassHolder cls = classes.get(className); + if (controller.wasCancelled()) { + break; + } + classNodes.add(prepare(decompiler, cls, classes)); } return classNodes; } @@ -665,7 +648,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { order.add(className); } - private PreparedClass decompile(Decompiler decompiler, ClassHolder cls, ClassReaderSource classes) { + private PreparedClass prepare(Decompiler decompiler, ClassHolder cls, ClassReaderSource classes) { PreparedClass clsNode = new PreparedClass(cls); for (MethodHolder method : cls.getMethods()) { if (method.getModifiers().contains(ElementModifier.ABSTRACT)) { @@ -680,14 +663,14 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { } PreparedMethod preparedMethod = method.hasModifier(ElementModifier.NATIVE) - ? decompileNative(method, classes) - : decompile(decompiler, method); + ? prepareNative(method, classes) + : prepare(decompiler, method); clsNode.getMethods().add(preparedMethod); } return clsNode; } - private PreparedMethod decompileNative(MethodHolder method, ClassReaderSource classes) { + private PreparedMethod prepareNative(MethodHolder method, ClassReaderSource classes) { MethodReference reference = method.getReference(); Generator generator = methodGenerators.get(reference); if (generator == null && !isBootstrap()) { @@ -702,7 +685,13 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { name -> createGenerator(name, method, classes)); } - return new PreparedMethod(method, null, generator, asyncMethods.contains(reference), null); + var async = asyncMethods.contains(reference); + bodyRenderer.renderNative(generator, async, reference, method.getModifiers()); + threadLibraryUsed |= bodyRenderer.isThreadLibraryUsed(); + var result = new PreparedMethod(method.getLevel(), method.getModifiers(), method.getReference(), + bodyRenderer.getBody(), bodyRenderer.getParameters(), async, null, null); + bodyRenderer.clear(); + return result; } private Generator createGenerator(String name, MethodHolder method, ClassReaderSource classes) { @@ -754,16 +743,25 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { } } - private PreparedMethod decompile(Decompiler decompiler, MethodHolder method) { + private PreparedMethod prepare(Decompiler decompiler, MethodHolder method) { MethodReference reference = method.getReference(); - if (asyncMethods.contains(reference)) { - AsyncMethodNode node = decompileAsync(decompiler, method); - ControlFlowEntry[] cfg = ProgramUtils.getLocationCFG(method.getProgram()); - return new PreparedMethod(method, node, null, false, cfg); + ControlFlowEntry[] cfg; + MethodNode node; + var async = asyncMethods.contains(reference); + if (async) { + node = decompileAsync(decompiler, method); + cfg = ProgramUtils.getLocationCFG(method.getProgram()); } else { - AstCacheEntry entry = decompileRegular(decompiler, method); - return new PreparedMethod(method, entry.method, null, false, entry.cfg); + var entry = decompileRegular(decompiler, method); + node = entry.method; + cfg = entry.cfg; } + bodyRenderer.render(node, async); + var result = new PreparedMethod(method.getLevel(), method.getModifiers(), method.getReference(), + bodyRenderer.getBody(), bodyRenderer.getParameters(), async, cfg, bodyRenderer.getVariables()); + threadLibraryUsed |= bodyRenderer.isThreadLibraryUsed(); + bodyRenderer.clear(); + return result; } private AstCacheEntry decompileRegular(Decompiler decompiler, MethodHolder method) { @@ -964,48 +962,4 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { return classSource; } } - - class ImporterContext implements ModuleImporterContext { - private ListableClassReaderSource classSource; - MethodReader method; - - ImporterContext(ListableClassReaderSource classSource) { - this.classSource = classSource; - } - - @Override - public MethodReader getMethod() { - return method; - } - - @Override - public void importModule(String name) { - importedModules.computeIfAbsent(name, n -> "$rt_import_" + importedModules.size()); - } - - @Override - public ListableClassReaderSource getClassSource() { - return classSource; - } - - @Override - public ClassLoader getClassLoader() { - return controller.getClassLoader(); - } - - @Override - public Properties getProperties() { - return JavaScriptTarget.this.controller.getProperties(); - } - - @Override - public DependencyInfo getDependency() { - return controller.getDependencyInfo(); - } - - @Override - public T getService(Class type) { - return controller.getServices().getService(type); - } - } } 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 381f1e686..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,7 +18,6 @@ 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.ModuleImporter; import org.teavm.backend.javascript.spi.VirtualMethodContributor; import org.teavm.model.MethodReference; import org.teavm.vm.spi.RendererListener; @@ -33,8 +32,6 @@ public interface TeaVMJavaScriptHost extends TeaVMHostExtension { void addInjectorProvider(Function provider); - void addModuleImporterProvider(Function provider); - void add(RendererListener listener); void addVirtualMethods(VirtualMethodContributor virtualMethods); diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/AliasProvider.java b/core/src/main/java/org/teavm/backend/javascript/codegen/AliasProvider.java index 9ef8e95be..3e3a37c43 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/AliasProvider.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/AliasProvider.java @@ -35,4 +35,6 @@ public interface AliasProvider { ScopedName getClassInitAlias(String className); String getScopeAlias(); + + void reserveName(String name); } diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultAliasProvider.java b/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultAliasProvider.java index 233123f88..0cd4fd424 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultAliasProvider.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultAliasProvider.java @@ -134,6 +134,10 @@ public class DefaultAliasProvider implements AliasProvider { return makeUnique(knownAliases, knowAliasesCounter, "$java"); } + @Override + public void reserveName(String name) { + } + private ScopedName makeUniqueTopLevel(String suggested) { if (knownAliases.size() < topLevelAliasLimit) { return new ScopedName(false, makeUnique(knownAliases, knowAliasesCounter, suggested)); diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultNamingStrategy.java b/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultNamingStrategy.java index fa71227a7..e3d09d7e0 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultNamingStrategy.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultNamingStrategy.java @@ -130,6 +130,11 @@ public class DefaultNamingStrategy implements NamingStrategy { return scopeName; } + @Override + public void reserveName(String name) { + aliasProvider.reserveName(name); + } + private MethodReference getRealMethod(MethodReference methodRef) { String className = methodRef.getClassName(); while (className != null) { diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/MinifyingAliasProvider.java b/core/src/main/java/org/teavm/backend/javascript/codegen/MinifyingAliasProvider.java index ac11ad640..0cf621332 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/MinifyingAliasProvider.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/MinifyingAliasProvider.java @@ -89,6 +89,11 @@ public class MinifyingAliasProvider implements AliasProvider { return result; } + @Override + public void reserveName(String name) { + usedAliases.add(name); + } + private ScopedName createTopLevelName() { if (usedAliases.size() < topLevelAliasLimit) { String result; diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/NameFrequencyConsumer.java b/core/src/main/java/org/teavm/backend/javascript/codegen/NameFrequencyConsumer.java index 182b8fd23..2c224cf4d 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/NameFrequencyConsumer.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/NameFrequencyConsumer.java @@ -32,5 +32,9 @@ public interface NameFrequencyConsumer { void consume(FieldReference field); + void consumeStatic(FieldReference field); + void consumeFunction(String name); + + void consumeGlobal(String name); } diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/NamingOrderer.java b/core/src/main/java/org/teavm/backend/javascript/codegen/NamingOrderer.java index 676d5248d..2166e60e2 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/NamingOrderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/NamingOrderer.java @@ -22,6 +22,7 @@ import org.teavm.model.MethodReference; public class NamingOrderer implements NameFrequencyConsumer { private Map entries = new HashMap<>(); + private Set reservedNames = new HashSet<>(); @Override public void consume(MethodReference method) { @@ -96,6 +97,18 @@ public class NamingOrderer implements NameFrequencyConsumer { entry.frequency++; } + @Override + public void consumeStatic(FieldReference field) { + var key = "sf:" + field; + var entry = entries.get(key); + if (entry == null) { + entry = new Entry(); + entry.operation = naming -> naming.getFullNameFor(field); + entries.put(key, entry); + } + entry.frequency++; + } + @Override public void consumeFunction(String name) { String key = "n:" + name; @@ -108,7 +121,15 @@ public class NamingOrderer implements NameFrequencyConsumer { entry.frequency++; } + @Override + public void consumeGlobal(String name) { + reservedNames.add(name); + } + public void apply(NamingStrategy naming) { + for (var name : reservedNames) { + naming.reserveName(name); + } List entryList = new ArrayList<>(entries.values()); Collections.sort(entryList, (o1, o2) -> Integer.compare(o2.frequency, o1.frequency)); for (Entry entry : entryList) { diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/NamingStrategy.java b/core/src/main/java/org/teavm/backend/javascript/codegen/NamingStrategy.java index 7c486bd40..bfac4bfad 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/NamingStrategy.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/NamingStrategy.java @@ -37,4 +37,6 @@ public interface NamingStrategy { ScopedName getNameForClassInit(String className); String getScopeName(); + + void reserveName(String name); } diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/OutputSourceWriter.java b/core/src/main/java/org/teavm/backend/javascript/codegen/OutputSourceWriter.java index 88a00e66b..3c7000128 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/OutputSourceWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/OutputSourceWriter.java @@ -124,6 +124,11 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider return append(naming.getNameForFunction(name)); } + @Override + public SourceWriter appendGlobal(String name) { + return append(name); + } + @Override public SourceWriter appendInit(MethodReference method) { return appendName(naming.getNameForInit(method)); diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/RememberedSource.java b/core/src/main/java/org/teavm/backend/javascript/codegen/RememberedSource.java new file mode 100644 index 000000000..288306885 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/RememberedSource.java @@ -0,0 +1,216 @@ +/* + * Copyright 2023 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.codegen; + +import org.teavm.backend.javascript.templating.SourceFragment; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReference; + +public class RememberedSource implements SourceFragment { + public static final int FILTER_TEXT = 1; + public static final int FILTER_REF = 2; + public static final int FILTER_DEBUG = 4; + public static final int FILTER_ALL = FILTER_TEXT | FILTER_REF | FILTER_DEBUG; + + private byte[] commands; + private String chars; + private int[] intArgs; + private String[] strings; + private FieldReference[] fields; + private MethodDescriptor[] methodDescriptors; + private MethodReference[] methods; + + RememberedSource(byte[] commands, String chars, int[] intArgs, String[] strings, FieldReference[] fields, + MethodDescriptor[] methodDescriptors, MethodReference[] methods) { + this.commands = commands; + this.chars = chars; + this.intArgs = intArgs; + this.strings = strings; + this.fields = fields; + this.methodDescriptors = methodDescriptors; + this.methods = methods; + } + + public void replay(SourceWriterSink sink, int filter) { + var commandIndex = 0; + var charIndex = 0; + var intArgIndex = 0; + + var commands = this.commands; + var intArgs = this.intArgs; + var chars = this.chars; + while (commandIndex < commands.length) { + var command = commands[commandIndex++]; + if ((command & 128) != 0) { + var count = 1 + (command & 127); + if ((filter & FILTER_TEXT) != 0) { + sink.append(chars, charIndex, charIndex + count); + } + charIndex += count; + continue; + } + switch (command) { + case RememberingSourceWriter.CLASS: + if ((filter & FILTER_REF) != 0) { + sink.appendClass(strings[intArgs[intArgIndex]]); + } + intArgIndex++; + break; + + case RememberingSourceWriter.FIELD: + if ((filter & FILTER_REF) != 0) { + sink.appendField(fields[intArgs[intArgIndex]]); + } + intArgIndex++; + break; + + case RememberingSourceWriter.STATIC_FIELD: + if ((filter & FILTER_REF) != 0) { + sink.appendStaticField(fields[intArgs[intArgIndex]]); + } + intArgIndex++; + break; + + case RememberingSourceWriter.METHOD: + if ((filter & FILTER_REF) != 0) { + sink.appendMethod(methodDescriptors[intArgs[intArgIndex]]); + } + intArgIndex++; + break; + + case RememberingSourceWriter.METHOD_BODY: + if ((filter & FILTER_REF) != 0) { + sink.appendMethodBody(methods[intArgs[intArgIndex]]); + } + intArgIndex++; + break; + + case RememberingSourceWriter.FUNCTION: + if ((filter & FILTER_REF) != 0) { + sink.appendFunction(strings[intArgs[intArgIndex]]); + } + intArgIndex++; + break; + + case RememberingSourceWriter.GLOBAL: + if ((filter & FILTER_REF) != 0) { + sink.appendGlobal(strings[intArgs[intArgIndex]]); + } + intArgIndex++; + break; + + case RememberingSourceWriter.INIT: + if ((filter & FILTER_REF) != 0) { + sink.appendInit(methods[intArgs[intArgIndex]]); + } + intArgIndex++; + break; + + case RememberingSourceWriter.CLASS_INIT: + if ((filter & FILTER_REF) != 0) { + sink.appendClassInit(strings[intArgs[intArgIndex]]); + } + intArgIndex++; + break; + + case RememberingSourceWriter.NEW_LINE: + if ((filter & FILTER_TEXT) != 0) { + sink.newLine(); + } + break; + + case RememberingSourceWriter.WS: + if ((filter & FILTER_TEXT) != 0) { + sink.ws(); + } + break; + + case RememberingSourceWriter.TOKEN_BOUNDARY: + if ((filter & FILTER_TEXT) != 0) { + sink.tokenBoundary(); + } + break; + + case RememberingSourceWriter.SOFT_NEW_LINE: + if ((filter & FILTER_TEXT) != 0) { + sink.softNewLine(); + } + break; + + case RememberingSourceWriter.INDENT: + if ((filter & FILTER_TEXT) != 0) { + sink.indent(); + } + break; + + case RememberingSourceWriter.OUTDENT: + if ((filter & FILTER_TEXT) != 0) { + sink.outdent(); + } + break; + + case RememberingSourceWriter.EMIT_LOCATION: + if ((filter & FILTER_DEBUG) != 0) { + var fileIndex = intArgs[intArgIndex]; + var file = fileIndex >= 0 ? strings[fileIndex] : null; + sink.emitLocation(file, intArgs[intArgIndex + 1]); + } + intArgIndex += 2; + break; + + case RememberingSourceWriter.ENTER_LOCATION: + if ((filter & FILTER_DEBUG) != 0) { + sink.enterLocation(); + } + break; + + case RememberingSourceWriter.EXIT_LOCATION: + if ((filter & FILTER_DEBUG) != 0) { + sink.exitLocation(); + } + break; + + case RememberingSourceWriter.EMIT_STATEMENT_START: + if ((filter & FILTER_DEBUG) != 0) { + sink.emitStatementStart(); + } + break; + + case RememberingSourceWriter.EMIT_CLASS: + if ((filter & FILTER_DEBUG) != 0) { + var classIndex = intArgs[intArgIndex]; + sink.emitClass(classIndex >= 0 ? strings[classIndex] : null); + } + intArgIndex++; + break; + + case RememberingSourceWriter.EMIT_METHOD: + if ((filter & FILTER_DEBUG) != 0) { + var methodIndex = intArgs[intArgIndex]; + sink.emitMethod(methodIndex >= 0 ? methodDescriptors[methodIndex] : null); + } + intArgIndex++; + break; + } + } + } + + @Override + public void write(SourceWriter writer, int precedence) { + replay(writer, FILTER_ALL); + } +} diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/RememberingSourceWriter.java b/core/src/main/java/org/teavm/backend/javascript/codegen/RememberingSourceWriter.java new file mode 100644 index 000000000..ee97fa2cd --- /dev/null +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/RememberingSourceWriter.java @@ -0,0 +1,345 @@ +/* + * Copyright 2023 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.codegen; + +import com.carrotsearch.hppc.ByteArrayList; +import com.carrotsearch.hppc.IntArrayList; +import com.carrotsearch.hppc.ObjectIntHashMap; +import com.carrotsearch.hppc.ObjectIntMap; +import java.util.ArrayList; +import java.util.List; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReference; + +public class RememberingSourceWriter extends SourceWriter { + static final byte CLASS = 0; + static final byte FIELD = 1; + static final byte STATIC_FIELD = 2; + static final byte METHOD = 3; + static final byte METHOD_BODY = 4; + static final byte FUNCTION = 5; + static final byte GLOBAL = 6; + static final byte INIT = 7; + static final byte CLASS_INIT = 8; + static final byte NEW_LINE = 9; + static final byte WS = 10; + static final byte TOKEN_BOUNDARY = 11; + static final byte SOFT_NEW_LINE = 12; + static final byte INDENT = 13; + static final byte OUTDENT = 14; + static final byte EMIT_LOCATION = 15; + static final byte ENTER_LOCATION = 16; + static final byte EXIT_LOCATION = 17; + static final byte EMIT_STATEMENT_START = 18; + static final byte EMIT_METHOD = 19; + static final byte EMIT_CLASS = 20; + + private boolean debug; + + private StringBuilder sb = new StringBuilder(); + private int lastWrittenChar; + private IntArrayList intArgs = new IntArrayList(); + private ByteArrayList commands = new ByteArrayList(); + + private List strings = new ArrayList<>(); + private ObjectIntMap stringIndexes = new ObjectIntHashMap<>(); + + private List fields = new ArrayList<>(); + private ObjectIntMap fieldIndexes = new ObjectIntHashMap<>(); + + private List methodDescriptors = new ArrayList<>(); + private ObjectIntMap methodDescriptorIndexes = new ObjectIntHashMap<>(); + + private List methods = new ArrayList<>(); + private ObjectIntMap methodIndexes = new ObjectIntHashMap<>(); + + public RememberingSourceWriter(boolean debug) { + this.debug = debug; + } + + public void clear() { + sb.setLength(0); + lastWrittenChar = 0; + intArgs.clear(); + commands.clear(); + strings.clear(); + stringIndexes.clear(); + fields.clear(); + fieldIndexes.clear(); + methodDescriptors.clear(); + methodDescriptorIndexes.clear(); + methods.clear(); + methodIndexes.clear(); + } + + @Override + public SourceWriter append(char value) { + sb.append(value); + return this; + } + + @Override + public SourceWriter append(CharSequence csq, int start, int end) { + sb.append(csq, start, end); + return this; + } + + @Override + public SourceWriter appendClass(String cls) { + flush(); + commands.add(CLASS); + appendStringArg(cls); + return this; + } + + @Override + public SourceWriter appendField(FieldReference field) { + flush(); + commands.add(FIELD); + appendFieldArg(field); + return this; + } + + @Override + public SourceWriter appendStaticField(FieldReference field) { + flush(); + commands.add(STATIC_FIELD); + appendFieldArg(field); + return this; + } + + @Override + public SourceWriter appendMethod(MethodDescriptor method) { + flush(); + commands.add(METHOD); + appendMethodDescriptorArg(method); + return this; + } + + @Override + public SourceWriter appendMethodBody(MethodReference method) { + flush(); + commands.add(METHOD_BODY); + appendMethodArg(method); + return this; + } + + @Override + public SourceWriter appendFunction(String name) { + flush(); + commands.add(FUNCTION); + appendStringArg(name); + return this; + } + + @Override + public SourceWriter appendGlobal(String name) { + flush(); + commands.add(GLOBAL); + appendStringArg(name); + return this; + } + + @Override + public SourceWriter appendInit(MethodReference method) { + flush(); + commands.add(INIT); + appendMethodArg(method); + return this; + } + + @Override + public SourceWriter appendClassInit(String className) { + flush(); + commands.add(CLASS_INIT); + appendStringArg(className); + return this; + } + + @Override + public SourceWriter newLine() { + flush(); + commands.add(NEW_LINE); + return this; + } + + @Override + public SourceWriter ws() { + flush(); + commands.add(WS); + return this; + } + + @Override + public SourceWriter tokenBoundary() { + flush(); + commands.add(TOKEN_BOUNDARY); + return this; + } + + @Override + public SourceWriter softNewLine() { + flush(); + commands.add(SOFT_NEW_LINE); + return this; + } + + @Override + public SourceWriter indent() { + flush(); + commands.add(INDENT); + return this; + } + + @Override + public SourceWriter outdent() { + flush(); + commands.add(OUTDENT); + return this; + } + + @Override + public SourceWriter emitLocation(String fileName, int line) { + if (debug) { + flush(); + commands.add(EMIT_LOCATION); + if (fileName == null) { + intArgs.add(-1); + } else { + appendStringArg(fileName); + } + intArgs.add(line); + } + return this; + } + + @Override + public SourceWriter enterLocation() { + if (debug) { + flush(); + commands.add(ENTER_LOCATION); + } + return this; + } + + @Override + public SourceWriter exitLocation() { + if (debug) { + flush(); + commands.add(EXIT_LOCATION); + } + return this; + } + + @Override + public SourceWriter emitStatementStart() { + if (debug) { + flush(); + commands.add(EMIT_STATEMENT_START); + } + return this; + } + + @Override + public void emitMethod(MethodDescriptor method) { + if (!debug) { + return; + } + flush(); + commands.add(EMIT_METHOD); + if (method == null) { + intArgs.add(-1); + } else { + appendMethodDescriptorArg(method); + } + } + + @Override + public void emitClass(String className) { + if (!debug) { + return; + } + flush(); + commands.add(EMIT_CLASS); + if (className == null) { + intArgs.add(-1); + } else { + appendStringArg(className); + } + } + + public void flush() { + if (lastWrittenChar == sb.length()) { + return; + } + for (var i = lastWrittenChar; i < sb.length(); i += 128) { + var j = Math.min(sb.length(), i + 128); + var n = (j - i) - 1; + commands.add((byte) (128 | n)); + } + lastWrittenChar = sb.length(); + } + + public RememberedSource save() { + flush(); + return new RememberedSource(commands.toArray(), sb.toString(), intArgs.toArray(), + !strings.isEmpty() ? strings.toArray(new String[0]) : null, + !fields.isEmpty() ? fields.toArray(new FieldReference[0]) : null, + !methodDescriptors.isEmpty() ? methodDescriptors.toArray(new MethodDescriptor[0]) : null, + !methods.isEmpty() ? methods.toArray(new MethodReference[0]) : null); + } + + private void appendStringArg(String arg) { + var index = stringIndexes.getOrDefault(arg, -1); + if (index < 0) { + index = strings.size(); + stringIndexes.put(arg, index); + strings.add(arg); + } + intArgs.add(index); + } + + private void appendFieldArg(FieldReference arg) { + var index = fieldIndexes.getOrDefault(arg, -1); + if (index < 0) { + index = fields.size(); + fieldIndexes.put(arg, index); + fields.add(arg); + } + intArgs.add(index); + } + + private void appendMethodDescriptorArg(MethodDescriptor arg) { + var index = methodDescriptorIndexes.getOrDefault(arg, -1); + if (index < 0) { + index = methodDescriptors.size(); + methodDescriptorIndexes.put(arg, index); + methodDescriptors.add(arg); + } + intArgs.add(index); + } + + private void appendMethodArg(MethodReference arg) { + var index = methodIndexes.getOrDefault(arg, -1); + if (index < 0) { + index = methods.size(); + methodIndexes.put(arg, index); + methods.add(arg); + } + intArgs.add(index); + } +} 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 7e1d856f7..d5429adb6 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 @@ -20,7 +20,7 @@ import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; -public abstract class SourceWriter implements Appendable { +public abstract class SourceWriter implements Appendable, SourceWriterSink { public SourceWriter append(String value) { append((CharSequence) value); return this; @@ -62,22 +62,27 @@ public abstract class SourceWriter implements Appendable { @Override public abstract SourceWriter append(CharSequence csq, int start, int end); + @Override public abstract SourceWriter appendClass(String cls); public SourceWriter appendClass(Class cls) { return appendClass(cls.getName()); } + @Override public abstract SourceWriter appendField(FieldReference field); + @Override public abstract SourceWriter appendStaticField(FieldReference field); + @Override public abstract SourceWriter appendMethod(MethodDescriptor method); public SourceWriter appendMethod(String name, Class... params) { return appendMethod(new MethodDescriptor(name, params)); } + @Override public abstract SourceWriter appendMethodBody(MethodReference method); public SourceWriter appendMethodBody(String className, String name, ValueType... params) { @@ -88,33 +93,51 @@ public abstract class SourceWriter implements Appendable { return appendMethodBody(new MethodReference(cls, name, params)); } + @Override public abstract SourceWriter appendFunction(String name); + @Override + public abstract SourceWriter appendGlobal(String name); + + @Override public abstract SourceWriter appendInit(MethodReference method); + @Override public abstract SourceWriter appendClassInit(String className); + @Override public abstract SourceWriter newLine(); + @Override public abstract SourceWriter ws(); + @Override public abstract SourceWriter tokenBoundary(); + @Override public abstract SourceWriter softNewLine(); + @Override public abstract SourceWriter indent(); + @Override public abstract SourceWriter outdent(); + @Override public abstract SourceWriter emitLocation(String fileName, int line); + @Override public abstract SourceWriter enterLocation(); + @Override public abstract SourceWriter exitLocation(); + @Override public abstract SourceWriter emitStatementStart(); + @Override public abstract void emitMethod(MethodDescriptor method); + @Override public abstract void emitClass(String className); } diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriterSink.java b/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriterSink.java new file mode 100644 index 000000000..30031bff3 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriterSink.java @@ -0,0 +1,108 @@ +/* + * Copyright 2023 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.codegen; + +import org.teavm.model.FieldReference; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReference; + +public interface SourceWriterSink { + default SourceWriterSink append(CharSequence csq, int start, int end) { + return this; + } + + default SourceWriterSink appendClass(String cls) { + return this; + } + + default SourceWriterSink appendField(FieldReference field) { + return this; + } + + default SourceWriterSink appendStaticField(FieldReference field) { + return this; + } + + default SourceWriterSink appendMethod(MethodDescriptor method) { + return this; + } + + default SourceWriterSink appendMethodBody(MethodReference method) { + return this; + } + + default SourceWriterSink appendFunction(String name) { + return this; + } + + default SourceWriterSink appendGlobal(String name) { + return this; + } + + default SourceWriterSink appendInit(MethodReference method) { + return this; + } + + default SourceWriterSink appendClassInit(String className) { + return this; + } + + default SourceWriterSink newLine() { + return this; + } + + default SourceWriterSink ws() { + return this; + } + + default SourceWriterSink tokenBoundary() { + return this; + } + + default SourceWriterSink softNewLine() { + return this; + } + + default SourceWriterSink indent() { + return this; + } + + default SourceWriterSink outdent() { + return this; + } + + default SourceWriterSink emitLocation(String fileName, int line) { + return this; + } + + default SourceWriterSink enterLocation() { + return this; + } + + default SourceWriterSink exitLocation() { + return this; + } + + default SourceWriterSink emitStatementStart() { + return this; + } + + default void emitMethod(MethodDescriptor method) { + } + + default void emitClass(String className) { + } +} diff --git a/core/src/main/java/org/teavm/backend/javascript/decompile/PreparedMethod.java b/core/src/main/java/org/teavm/backend/javascript/decompile/PreparedMethod.java index b41ba427f..8c4176c83 100644 --- a/core/src/main/java/org/teavm/backend/javascript/decompile/PreparedMethod.java +++ b/core/src/main/java/org/teavm/backend/javascript/decompile/PreparedMethod.java @@ -15,27 +15,33 @@ */ package org.teavm.backend.javascript.decompile; +import java.util.Set; import org.teavm.ast.ControlFlowEntry; -import org.teavm.ast.MethodNode; -import org.teavm.backend.javascript.spi.Generator; -import org.teavm.model.MethodHolder; +import org.teavm.backend.javascript.codegen.RememberedSource; +import org.teavm.model.AccessLevel; +import org.teavm.model.ElementModifier; import org.teavm.model.MethodReference; public class PreparedMethod { - public final MethodHolder methodHolder; + public final AccessLevel accessLevel; + public final Set modifiers; public final MethodReference reference; - public final MethodNode node; - public final Generator generator; + public final RememberedSource body; + public final RememberedSource parameters; public final boolean async; public final ControlFlowEntry[] cfg; + public final PreparedVariable[] variables; - public PreparedMethod(MethodHolder method, MethodNode node, Generator generator, boolean async, - ControlFlowEntry[] cfg) { - this.reference = method.getReference(); - this.methodHolder = method; - this.node = node; - this.generator = generator; + public PreparedMethod(AccessLevel accessLevel, Set modifiers, MethodReference reference, + RememberedSource body, RememberedSource parameters, boolean async, ControlFlowEntry[] cfg, + PreparedVariable[] variables) { + this.accessLevel = accessLevel; + this.modifiers = modifiers; + this.reference = reference; + this.body = body; + this.parameters = parameters; this.async = async; this.cfg = cfg; + this.variables = variables; } } diff --git a/core/src/main/java/org/teavm/backend/javascript/spi/ModuleImporter.java b/core/src/main/java/org/teavm/backend/javascript/decompile/PreparedVariable.java similarity index 54% rename from core/src/main/java/org/teavm/backend/javascript/spi/ModuleImporter.java rename to core/src/main/java/org/teavm/backend/javascript/decompile/PreparedVariable.java index a478e6447..cad1d38e0 100644 --- a/core/src/main/java/org/teavm/backend/javascript/spi/ModuleImporter.java +++ b/core/src/main/java/org/teavm/backend/javascript/decompile/PreparedVariable.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 konsoletyper. + * Copyright 2023 Alexey Andreev. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.backend.javascript.spi; +package org.teavm.backend.javascript.decompile; -public interface ModuleImporter { - void importModules(ModuleImporterContext context); +import org.teavm.debugging.information.DebugInformationEmitter; + +public class PreparedVariable { + public final String[] names; + public final String jsName; + + public PreparedVariable(String[] names, String jsName) { + this.names = names; + this.jsName = jsName; + } + + public void emit(DebugInformationEmitter emitter) { + emitter.emitVariable(names, jsName); + } } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/DefaultGlobalNameWriter.java b/core/src/main/java/org/teavm/backend/javascript/rendering/DefaultGlobalNameWriter.java index 9f2522891..fd8478f3a 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/DefaultGlobalNameWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/DefaultGlobalNameWriter.java @@ -30,6 +30,6 @@ public class DefaultGlobalNameWriter implements Function { if (s.startsWith("$rt_") || s.startsWith("Long_") || s.equals("Long")) { return prec -> writer.appendFunction(s); } - return prec -> writer.append("$rt_globals").append('.').append(s); + return prec -> writer.appendGlobal(s); } } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/MethodBodyRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/MethodBodyRenderer.java new file mode 100644 index 000000000..35b48231d --- /dev/null +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/MethodBodyRenderer.java @@ -0,0 +1,361 @@ +/* + * Copyright 2023 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.rendering; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import org.teavm.ast.AsyncMethodNode; +import org.teavm.ast.AsyncMethodPart; +import org.teavm.ast.MethodNode; +import org.teavm.ast.MethodNodeVisitor; +import org.teavm.ast.RegularMethodNode; +import org.teavm.ast.VariableNode; +import org.teavm.backend.javascript.codegen.RememberedSource; +import org.teavm.backend.javascript.codegen.RememberingSourceWriter; +import org.teavm.backend.javascript.codegen.SourceWriter; +import org.teavm.backend.javascript.decompile.PreparedVariable; +import org.teavm.backend.javascript.spi.Generator; +import org.teavm.backend.javascript.spi.GeneratorContext; +import org.teavm.dependency.DependencyInfo; +import org.teavm.diagnostics.Diagnostics; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.ElementModifier; +import org.teavm.model.ListableClassReaderSource; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; + +public class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext { + private RenderingContext context; + private Diagnostics diagnostics; + private boolean minifying; + private boolean async; + private Set asyncMethods; + private RememberingSourceWriter writer; + private StatementRenderer statementRenderer; + private boolean threadLibraryUsed; + private RememberedSource body; + private RememberedSource parameters; + private PreparedVariable[] variables; + + public MethodBodyRenderer(RenderingContext context, Diagnostics diagnostics, boolean minifying, boolean debug, + Set asyncMethods) { + this.context = context; + this.diagnostics = diagnostics; + this.minifying = minifying; + this.asyncMethods = asyncMethods; + writer = new RememberingSourceWriter(debug); + statementRenderer = new StatementRenderer(context, writer); + } + + public boolean isThreadLibraryUsed() { + return threadLibraryUsed; + } + + @Override + public DependencyInfo getDependency() { + return context.getDependencyInfo(); + } + + public void renderNative(Generator generator, boolean async, MethodReference reference, + Set modifiers) { + threadLibraryUsed = false; + this.async = async; + statementRenderer.setAsync(async); + renderParameters(reference, modifiers); + generator.generate(this, writer, reference); + body = writer.save(); + writer.clear(); + } + + public void render(MethodNode node, boolean async) { + threadLibraryUsed = false; + this.async = async; + statementRenderer.setAsync(async); + statementRenderer.setCurrentMethod(node); + renderParameters(node.getReference(), node.getModifiers()); + node.acceptVisitor(this); + body = writer.save(); + prepareVariables(node); + writer.clear(); + } + + public PreparedVariable[] getVariables() { + return variables; + } + + private void prepareVariables(MethodNode method) { + var variables = new ArrayList(); + for (int i = 0; i < method.getVariables().size(); ++i) { + variables.add(new PreparedVariable(new String[] { method.getVariables().get(i).getName() }, + statementRenderer.variableName(i))); + } + this.variables = variables.toArray(new PreparedVariable[0]); + } + + public RememberedSource getBody() { + return body; + } + + public RememberedSource getParameters() { + return parameters; + } + + public void clear() { + body = null; + parameters = null; + variables = null; + } + + private void renderParameters(MethodReference reference, Set modifiers) { + int startParam = 0; + if (modifiers.contains(ElementModifier.STATIC)) { + startParam = 1; + } + for (int i = startParam; i <= reference.parameterCount(); ++i) { + if (i > startParam) { + writer.append(",").ws(); + } + writer.append(statementRenderer.variableName(i)); + } + parameters = writer.save(); + writer.clear(); + } + + private void appendMonitor(StatementRenderer statementRenderer, MethodNode methodNode) { + if (methodNode.getModifiers().contains(ElementModifier.STATIC)) { + writer.appendFunction("$rt_cls").append("(") + .appendClass(methodNode.getReference().getClassName()).append(")"); + } else { + writer.append(statementRenderer.variableName(0)); + } + } + + @Override + public void visit(RegularMethodNode method) { + statementRenderer.setAsync(false); + this.async = false; + + int variableCount = 0; + for (VariableNode var : method.getVariables()) { + variableCount = Math.max(variableCount, var.getIndex() + 1); + } + TryCatchFinder tryCatchFinder = new TryCatchFinder(); + method.getBody().acceptVisitor(tryCatchFinder); + boolean hasTryCatch = tryCatchFinder.tryCatchFound; + List variableNames = new ArrayList<>(); + for (int i = method.getReference().parameterCount() + 1; i < variableCount; ++i) { + variableNames.add(statementRenderer.variableName(i)); + } + if (hasTryCatch) { + variableNames.add("$$je"); + } + if (!variableNames.isEmpty()) { + writer.append("var "); + for (int i = 0; i < variableNames.size(); ++i) { + if (i > 0) { + writer.append(",").ws(); + } + writer.append(variableNames.get(i)); + } + writer.append(";").softNewLine(); + } + + statementRenderer.setEnd(true); + statementRenderer.setCurrentPart(0); + + if (method.getModifiers().contains(ElementModifier.SYNCHRONIZED)) { + writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_SYNC_METHOD); + writer.append("("); + appendMonitor(statementRenderer, method); + writer.append(");").softNewLine(); + + writer.append("try").ws().append("{").softNewLine().indent(); + } + + method.getBody().acceptVisitor(statementRenderer); + + if (method.getModifiers().contains(ElementModifier.SYNCHRONIZED)) { + writer.outdent().append("}").ws().append("finally").ws().append("{").indent().softNewLine(); + + writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_SYNC_METHOD); + writer.append("("); + appendMonitor(statementRenderer, method); + writer.append(");").softNewLine(); + + writer.outdent().append("}").softNewLine(); + } + } + + @Override + public void visit(AsyncMethodNode methodNode) { + threadLibraryUsed = true; + statementRenderer.setAsync(true); + this.async = true; + MethodReference ref = methodNode.getReference(); + int variableCount = 0; + for (VariableNode var : methodNode.getVariables()) { + variableCount = Math.max(variableCount, var.getIndex() + 1); + } + List variableNames = new ArrayList<>(); + for (int i = ref.parameterCount() + 1; i < variableCount; ++i) { + variableNames.add(statementRenderer.variableName(i)); + } + TryCatchFinder tryCatchFinder = new TryCatchFinder(); + for (AsyncMethodPart part : methodNode.getBody()) { + if (!tryCatchFinder.tryCatchFound) { + part.getStatement().acceptVisitor(tryCatchFinder); + } + } + boolean hasTryCatch = tryCatchFinder.tryCatchFound; + if (hasTryCatch) { + variableNames.add("$$je"); + } + variableNames.add(context.pointerName()); + variableNames.add(context.tempVarName()); + writer.append("var "); + for (int i = 0; i < variableNames.size(); ++i) { + if (i > 0) { + writer.append(",").ws(); + } + writer.append(variableNames.get(i)); + } + writer.append(";").softNewLine(); + + int firstToSave = 0; + if (methodNode.getModifiers().contains(ElementModifier.STATIC)) { + firstToSave = 1; + } + + String popName = minifying ? "l" : "pop"; + String pushName = minifying ? "s" : "push"; + writer.append(context.pointerName()).ws().append('=').ws().append("0;").softNewLine(); + writer.append("if").ws().append("(").appendFunction("$rt_resuming").append("())").ws() + .append("{").indent().softNewLine(); + writer.append("var ").append(context.threadName()).ws().append('=').ws() + .appendFunction("$rt_nativeThread").append("();").softNewLine(); + writer.append(context.pointerName()).ws().append('=').ws().append(context.threadName()).append(".") + .append(popName).append("();"); + for (int i = variableCount - 1; i >= firstToSave; --i) { + writer.append(statementRenderer.variableName(i)).ws().append('=').ws() + .append(context.threadName()) + .append(".").append(popName).append("();"); + } + writer.softNewLine(); + writer.outdent().append("}").softNewLine(); + + if (methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) { + writer.append("try").ws().append('{').indent().softNewLine(); + } + + Renderer.renderAsyncPrologue(writer, context); + for (int i = 0; i < methodNode.getBody().size(); ++i) { + writer.append("case ").append(i).append(":").indent().softNewLine(); + if (i == 0 && methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) { + writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_METHOD); + writer.append("("); + appendMonitor(statementRenderer, methodNode); + writer.append(");").softNewLine(); + statementRenderer.emitSuspendChecker(); + } + AsyncMethodPart part = methodNode.getBody().get(i); + statementRenderer.setEnd(true); + statementRenderer.setCurrentPart(i); + part.getStatement().acceptVisitor(statementRenderer); + writer.outdent(); + } + Renderer.renderAsyncEpilogue(writer); + + if (methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) { + writer.outdent().append("}").ws().append("finally").ws().append('{').indent().softNewLine(); + writer.append("if").ws().append("(!").appendFunction("$rt_suspending").append("())") + .ws().append("{").indent().softNewLine(); + writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_METHOD).append("("); + appendMonitor(statementRenderer, methodNode); + writer.append(");").softNewLine(); + writer.outdent().append('}').softNewLine(); + writer.outdent().append('}').softNewLine(); + } + + writer.appendFunction("$rt_nativeThread").append("().").append(pushName).append("("); + for (int i = firstToSave; i < variableCount; ++i) { + writer.append(statementRenderer.variableName(i)).append(',').ws(); + } + writer.append(context.pointerName()).append(");"); + writer.softNewLine(); + } + + @Override + public String getParameterName(int index) { + return statementRenderer.variableName(index); + } + + @Override + public ListableClassReaderSource getClassSource() { + return context.getClassSource(); + } + + @Override + public ClassReaderSource getInitialClassSource() { + return context.getInitialClassSource(); + } + + @Override + public ClassLoader getClassLoader() { + return context.getClassLoader(); + } + + @Override + public Properties getProperties() { + return new Properties(context.getProperties()); + } + + @Override + public T getService(Class type) { + return context.getServices().getService(type); + } + + @Override + public boolean isAsync() { + return async; + } + + @Override + public boolean isAsync(MethodReference method) { + return asyncMethods.contains(method); + } + + @Override + public Diagnostics getDiagnostics() { + return diagnostics; + } + + @Override + public void typeToClassString(SourceWriter writer, ValueType type) { + context.typeToClsString(writer, type); + } + + @Override + public boolean isDynamicInitializer(String className) { + return context.isDynamicInitializer(className); + } + + @Override + public String importModule(String name) { + return context.importModule(name); + } +} diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java b/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java index 4b02a3ee0..dec8a8015 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java @@ -16,35 +16,11 @@ package org.teavm.backend.javascript.rendering; import java.util.Set; -import org.teavm.ast.ArrayFromDataExpr; -import org.teavm.ast.AssignmentStatement; -import org.teavm.ast.AsyncMethodNode; -import org.teavm.ast.AsyncMethodPart; -import org.teavm.ast.BinaryExpr; -import org.teavm.ast.BoundCheckExpr; -import org.teavm.ast.CastExpr; -import org.teavm.ast.ConstantExpr; -import org.teavm.ast.InitClassStatement; -import org.teavm.ast.InstanceOfExpr; -import org.teavm.ast.InvocationExpr; -import org.teavm.ast.MethodNodeVisitor; -import org.teavm.ast.MonitorEnterStatement; -import org.teavm.ast.MonitorExitStatement; -import org.teavm.ast.NewArrayExpr; -import org.teavm.ast.NewExpr; -import org.teavm.ast.NewMultiArrayExpr; -import org.teavm.ast.OperationType; -import org.teavm.ast.PrimitiveCastExpr; -import org.teavm.ast.QualificationExpr; -import org.teavm.ast.RecursiveVisitor; -import org.teavm.ast.RegularMethodNode; -import org.teavm.ast.ThrowStatement; -import org.teavm.ast.TryCatchStatement; -import org.teavm.ast.UnaryExpr; import org.teavm.backend.javascript.codegen.NameFrequencyConsumer; +import org.teavm.backend.javascript.codegen.RememberedSource; +import org.teavm.backend.javascript.codegen.SourceWriterSink; import org.teavm.backend.javascript.decompile.PreparedClass; import org.teavm.backend.javascript.decompile.PreparedMethod; -import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.FieldHolder; @@ -54,7 +30,7 @@ import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; -class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisitor { +class NameFrequencyEstimator implements SourceWriterSink { static final MethodReference MONITOR_ENTER_METHOD = new MethodReference(Object.class, "monitorEnter", Object.class, void.class); static final MethodReference MONITOR_ENTER_SYNC_METHOD = new MethodReference(Object.class, @@ -67,19 +43,13 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit private final NameFrequencyConsumer consumer; private final ClassReaderSource classSource; - private boolean async; - private final Set injectedMethods; private final Set asyncFamilyMethods; - private final boolean strict; NameFrequencyEstimator(NameFrequencyConsumer consumer, ClassReaderSource classSource, - Set injectedMethods, Set asyncFamilyMethods, - boolean strict) { + Set asyncFamilyMethods) { this.consumer = consumer; this.classSource = classSource; - this.injectedMethods = injectedMethods; this.asyncFamilyMethods = asyncFamilyMethods; - this.strict = strict; } public void estimate(PreparedClass cls) { @@ -102,11 +72,11 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit if (asyncFamilyMethods.contains(method.reference)) { consumer.consume(method.reference); } - if (clinit != null && (method.methodHolder.getModifiers().contains(ElementModifier.STATIC) + if (clinit != null && (method.modifiers.contains(ElementModifier.STATIC) || method.reference.getName().equals(""))) { consumer.consume(method.reference); } - if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) { + if (!method.modifiers.contains(ElementModifier.STATIC)) { consumer.consume(method.reference.getDescriptor()); consumer.consume(method.reference); } @@ -117,9 +87,7 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit consumer.consumeFunction("$rt_invalidPointer"); } - if (method.node != null) { - method.node.acceptVisitor(this); - } + method.body.replay(this, RememberedSource.FILTER_REF); } if (clinit != null) { @@ -149,367 +117,56 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit } @Override - public void visit(RegularMethodNode methodNode) { - async = false; - methodNode.getBody().acceptVisitor(this); + public SourceWriterSink appendClass(String cls) { + consumer.consume(cls); + return this; } @Override - public void visit(AsyncMethodNode methodNode) { - async = true; - for (AsyncMethodPart part : methodNode.getBody()) { - part.getStatement().acceptVisitor(this); - } + public SourceWriterSink appendField(FieldReference field) { + consumer.consume(field); + return this; } @Override - public void visit(AssignmentStatement statement) { - super.visit(statement); - if (statement.isAsync()) { - consumer.consumeFunction("$rt_suspending"); - } + public SourceWriterSink appendStaticField(FieldReference field) { + consumer.consumeStatic(field); + return this; } @Override - public void visit(ThrowStatement statement) { - statement.getException().acceptVisitor(this); - consumer.consumeFunction("$rt_throw"); + public SourceWriterSink appendMethod(MethodDescriptor method) { + consumer.consume(method); + return this; } @Override - public void visit(InitClassStatement statement) { - consumer.consumeClassInit(statement.getClassName()); + public SourceWriterSink appendMethodBody(MethodReference method) { + consumer.consume(method); + return this; } @Override - public void visit(TryCatchStatement statement) { - super.visit(statement); - if (statement.getExceptionType() != null) { - consumer.consume(statement.getExceptionType()); - } - consumer.consumeFunction("$rt_wrapException"); + public SourceWriterSink appendFunction(String name) { + consumer.consumeFunction(name); + return this; } @Override - public void visit(MonitorEnterStatement statement) { - super.visit(statement); - if (async) { - consumer.consume(MONITOR_ENTER_METHOD); - consumer.consumeFunction("$rt_suspending"); - } else { - consumer.consume(MONITOR_ENTER_SYNC_METHOD); - } + public SourceWriterSink appendGlobal(String name) { + consumer.consumeGlobal(name); + return this; } @Override - public void visit(MonitorExitStatement statement) { - super.visit(statement); - if (async) { - consumer.consume(MONITOR_EXIT_METHOD); - } else { - consumer.consume(MONITOR_EXIT_SYNC_METHOD); - } + public SourceWriterSink appendInit(MethodReference method) { + consumer.consumeInit(method); + return this; } @Override - public void visit(BinaryExpr expr) { - super.visit(expr); - if (expr.getType() == OperationType.LONG) { - switch (expr.getOperation()) { - case ADD: - consumer.consumeFunction("Long_add"); - break; - case SUBTRACT: - consumer.consumeFunction("Long_sub"); - break; - case MULTIPLY: - consumer.consumeFunction("Long_mul"); - break; - case DIVIDE: - consumer.consumeFunction("Long_div"); - break; - case MODULO: - consumer.consumeFunction("Long_rem"); - break; - case BITWISE_OR: - consumer.consumeFunction("Long_or"); - break; - case BITWISE_AND: - consumer.consumeFunction("Long_and"); - break; - case BITWISE_XOR: - consumer.consumeFunction("Long_xor"); - break; - case LEFT_SHIFT: - consumer.consumeFunction("Long_shl"); - break; - case RIGHT_SHIFT: - consumer.consumeFunction("Long_shr"); - break; - case UNSIGNED_RIGHT_SHIFT: - consumer.consumeFunction("Long_shru"); - break; - case COMPARE: - consumer.consumeFunction("Long_compare"); - break; - case EQUALS: - consumer.consumeFunction("Long_eq"); - break; - case NOT_EQUALS: - consumer.consumeFunction("Long_ne"); - break; - case LESS: - consumer.consumeFunction("Long_lt"); - break; - case LESS_OR_EQUALS: - consumer.consumeFunction("Long_le"); - break; - case GREATER: - consumer.consumeFunction("Long_gt"); - break; - case GREATER_OR_EQUALS: - consumer.consumeFunction("Long_ge"); - break; - } - return; - } - switch (expr.getOperation()) { - case COMPARE: - consumer.consumeFunction("$rt_compare"); - break; - case MULTIPLY: - if (expr.getType() == OperationType.INT && !RenderingUtil.isSmallInteger(expr.getFirstOperand()) - && !RenderingUtil.isSmallInteger(expr.getSecondOperand())) { - consumer.consumeFunction("$rt_imul"); - } - break; - default: - break; - } - } - - @Override - public void visit(UnaryExpr expr) { - super.visit(expr); - switch (expr.getOperation()) { - case NULL_CHECK: - consumer.consumeFunction("$rt_nullCheck"); - break; - case NEGATE: - if (expr.getType() == OperationType.LONG) { - consumer.consumeFunction("Long_neg"); - } - break; - case NOT: - if (expr.getType() == OperationType.LONG) { - consumer.consumeFunction("Long_not"); - } - break; - default: - break; - } - } - - @Override - public void visit(PrimitiveCastExpr expr) { - super.visit(expr); - if (expr.getSource() == OperationType.LONG) { - if (expr.getTarget() == OperationType.DOUBLE || expr.getTarget() == OperationType.FLOAT) { - consumer.consumeFunction("Long_toNumber"); - } else if (expr.getTarget() == OperationType.INT) { - consumer.consumeFunction("Long_lo"); - } - } else if (expr.getTarget() == OperationType.LONG) { - switch (expr.getSource()) { - case INT: - consumer.consumeFunction("Long_fromInt"); - break; - case FLOAT: - case DOUBLE: - consumer.consumeFunction("Long_fromNUmber"); - break; - } - } - } - - @Override - public void visit(ConstantExpr expr) { - if (expr.getValue() instanceof ValueType) { - visitType((ValueType) expr.getValue()); - } else if (expr.getValue() instanceof String) { - consumer.consumeFunction("$rt_s"); - } else if (expr.getValue() instanceof Long) { - long value = (Long) expr.getValue(); - if (value == 0) { - consumer.consumeFunction("Long_ZERO"); - } else if ((int) value == value) { - consumer.consumeFunction("Long_fromInt"); - } else { - consumer.consumeFunction("Long_create"); - } - } - } - - private void visitType(ValueType type) { - while (type instanceof ValueType.Array) { - type = ((ValueType.Array) type).getItemType(); - } - if (type instanceof ValueType.Object) { - String clsName = ((ValueType.Object) type).getClassName(); - consumer.consume(clsName); - consumer.consumeFunction("$rt_cls"); - } - } - @Override - public void visit(InvocationExpr expr) { - super.visit(expr); - if (injectedMethods.contains(expr.getMethod())) { - return; - } - switch (expr.getType()) { - case SPECIAL: - case STATIC: - consumer.consume(expr.getMethod()); - break; - case CONSTRUCTOR: - consumer.consumeInit(expr.getMethod()); - break; - case DYNAMIC: - consumer.consume(expr.getMethod().getDescriptor()); - break; - } - } - - @Override - public void visit(QualificationExpr expr) { - super.visit(expr); - consumer.consume(expr.getField()); - } - - @Override - public void visit(NewExpr expr) { - super.visit(expr); - consumer.consume(expr.getConstructedClass()); - } - - @Override - public void visit(NewArrayExpr expr) { - super.visit(expr); - visitType(expr.getType()); - if (expr.getType() instanceof ValueType.Primitive) { - switch (((ValueType.Primitive) expr.getType()).getKind()) { - case BOOLEAN: - consumer.consumeFunction("$rt_createBooleanArray"); - break; - case BYTE: - consumer.consumeFunction("$rt_createByteArray"); - break; - case SHORT: - consumer.consumeFunction("$rt_createShortArray"); - break; - case CHARACTER: - consumer.consumeFunction("$rt_createCharArray"); - break; - case INTEGER: - consumer.consumeFunction("$rt_createIntArray"); - break; - case LONG: - consumer.consumeFunction("$rt_createLongArray"); - break; - case FLOAT: - consumer.consumeFunction("$rt_createFloatArray"); - break; - case DOUBLE: - consumer.consumeFunction("$rt_createDoubleArray"); - break; - } - } else { - consumer.consumeFunction("$rt_createArray"); - } - } - - @Override - public void visit(ArrayFromDataExpr expr) { - super.visit(expr); - visitType(expr.getType()); - if (expr.getType() instanceof ValueType.Primitive) { - switch (((ValueType.Primitive) expr.getType()).getKind()) { - case BOOLEAN: - consumer.consumeFunction("$rt_createBooleanArrayFromData"); - break; - case BYTE: - consumer.consumeFunction("$rt_createByteArrayFromData"); - break; - case SHORT: - consumer.consumeFunction("$rt_createShortArrayFromData"); - break; - case CHARACTER: - consumer.consumeFunction("$rt_createCharArrayFromData"); - break; - case INTEGER: - consumer.consumeFunction("$rt_createIntArrayFromData"); - break; - case LONG: - consumer.consumeFunction("$rt_createLongArrayFromData"); - break; - case FLOAT: - consumer.consumeFunction("$rt_createFloatArrayFromData"); - break; - case DOUBLE: - consumer.consumeFunction("$rt_createDoubleArrayFromData"); - break; - } - } else { - consumer.consumeFunction("$rt_createArrayFromData"); - } - } - - @Override - public void visit(NewMultiArrayExpr expr) { - super.visit(expr); - visitType(expr.getType()); - } - - @Override - public void visit(InstanceOfExpr expr) { - super.visit(expr); - visitType(expr.getType()); - if (!isClass(expr.getType())) { - consumer.consumeFunction("$rt_isInstance"); - } - } - - @Override - public void visit(CastExpr expr) { - super.visit(expr); - if (strict) { - visitType(expr.getTarget()); - if (isClass(expr.getTarget())) { - consumer.consumeFunction("$rt_castToClass"); - } else { - consumer.consumeFunction("$rt_castToInterface"); - } - } - } - - private boolean isClass(ValueType type) { - if (!(type instanceof ValueType.Object)) { - return false; - } - String className = ((ValueType.Object) type).getClassName(); - ClassReader cls = classSource.get(className); - return cls != null && !cls.hasModifier(ElementModifier.INTERFACE); - } - - @Override - public void visit(BoundCheckExpr expr) { - super.visit(expr); - if (expr.getArray() != null && expr.getIndex() != null) { - consumer.consumeFunction("$rt_checkBounds"); - } else if (expr.getArray() != null) { - consumer.consumeFunction("$rt_checkUpperBound"); - } else if (expr.isLower()) { - consumer.consumeFunction("$rt_checkLowerBound"); - } + public SourceWriterSink appendClassInit(String className) { + consumer.consumeClassInit(className); + return this; } } 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 6cebd29b4..710b163e1 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 @@ -27,30 +27,19 @@ import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.function.IntFunction; -import org.teavm.ast.AsyncMethodNode; -import org.teavm.ast.AsyncMethodPart; -import org.teavm.ast.MethodNode; -import org.teavm.ast.MethodNodeVisitor; -import org.teavm.ast.RegularMethodNode; -import org.teavm.ast.ReturnStatement; -import org.teavm.ast.Statement; -import org.teavm.ast.VariableNode; import org.teavm.backend.javascript.codegen.NamingOrderer; import org.teavm.backend.javascript.codegen.NamingStrategy; import org.teavm.backend.javascript.codegen.OutputSourceWriter; +import org.teavm.backend.javascript.codegen.RememberedSource; import org.teavm.backend.javascript.codegen.ScopedName; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.decompile.PreparedClass; import org.teavm.backend.javascript.decompile.PreparedMethod; -import org.teavm.backend.javascript.spi.GeneratorContext; import org.teavm.common.ServiceRepository; import org.teavm.debugging.information.DebugInformationEmitter; import org.teavm.debugging.information.DummyDebugInformationEmitter; -import org.teavm.dependency.DependencyInfo; -import org.teavm.diagnostics.Diagnostics; import org.teavm.model.AccessLevel; import org.teavm.model.ClassReader; -import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.FieldHolder; import org.teavm.model.FieldReference; @@ -74,7 +63,6 @@ public class Renderer implements RenderingManager { private DebugInformationEmitter debugEmitter = new DummyDebugInformationEmitter(); private final Set asyncMethods; private final Set asyncFamilyMethods; - private final Diagnostics diagnostics; private RenderingContext context; private List postponedFieldInitializers = new ArrayList<>(); private IntFunction progressConsumer = p -> TeaVMProgressFeedback.CONTINUE; @@ -84,11 +72,8 @@ public class Renderer implements RenderingManager { private int stringPoolSize; private int metadataSize; - private boolean longLibraryUsed; - private boolean threadLibraryUsed; - public Renderer(OutputSourceWriter writer, Set asyncMethods, - Set asyncFamilyMethods, Diagnostics diagnostics, RenderingContext context) { + Set asyncFamilyMethods, RenderingContext context) { this.naming = context.getNaming(); this.writer = writer; this.classSource = context.getClassSource(); @@ -96,18 +81,9 @@ public class Renderer implements RenderingManager { this.services = context.getServices(); this.asyncMethods = new HashSet<>(asyncMethods); this.asyncFamilyMethods = new HashSet<>(asyncFamilyMethods); - this.diagnostics = diagnostics; this.context = context; } - public boolean isLongLibraryUsed() { - return longLibraryUsed; - } - - public boolean isThreadLibraryUsed() { - return threadLibraryUsed; - } - public int getStringPoolSize() { return stringPoolSize; } @@ -160,10 +136,6 @@ public class Renderer implements RenderingManager { return properties; } - public DebugInformationEmitter getDebugEmitter() { - return debugEmitter; - } - public void setDebugEmitter(DebugInformationEmitter debugEmitter) { this.debugEmitter = debugEmitter; } @@ -242,8 +214,8 @@ public class Renderer implements RenderingManager { public void prepare(List classes) { if (minifying) { NamingOrderer orderer = new NamingOrderer(); - NameFrequencyEstimator estimator = new NameFrequencyEstimator(orderer, classSource, asyncMethods, - asyncFamilyMethods, context.isStrict()); + NameFrequencyEstimator estimator = new NameFrequencyEstimator(orderer, classSource, + asyncFamilyMethods); for (PreparedClass cls : classes) { estimator.estimate(cls); } @@ -374,7 +346,7 @@ public class Renderer implements RenderingManager { if (!cls.getClassHolder().hasModifier(ElementModifier.INTERFACE) && !cls.getClassHolder().hasModifier(ElementModifier.ABSTRACT)) { for (PreparedMethod method : cls.getMethods()) { - if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) { + if (!method.modifiers.contains(ElementModifier.STATIC)) { if (method.reference.getName().equals("")) { renderInitializer(method); } @@ -416,7 +388,7 @@ public class Renderer implements RenderingManager { writer.append("return;").softNewLine(); writer.outdent().append("}").softNewLine(); - renderAsyncPrologue(); + renderAsyncPrologue(writer, context); writer.append("case 0:").indent().softNewLine(); writer.append(clinitCalled).ws().append('=').ws().append("true;").softNewLine(); @@ -440,7 +412,7 @@ public class Renderer implements RenderingManager { renderEraseClinit(cls); writer.append("return;").softNewLine().outdent(); - renderAsyncEpilogue(); + renderAsyncEpilogue(writer); writer.appendFunction("$rt_nativeThread").append("().push(" + context.pointerName() + ");").softNewLine(); } @@ -561,8 +533,8 @@ public class Renderer implements RenderingManager { Map virtualMethods = new LinkedHashMap<>(); collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods); for (PreparedMethod method : cls.getMethods()) { - if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC) - && method.methodHolder.getLevel() != AccessLevel.PRIVATE) { + if (!method.modifiers.contains(ElementModifier.STATIC) + && method.accessLevel != AccessLevel.PRIVATE) { virtualMethods.put(method.reference.getDescriptor(), method.reference); } } @@ -811,37 +783,20 @@ public class Renderer implements RenderingManager { } private void renderBody(PreparedMethod method) { - StatementRenderer statementRenderer = new StatementRenderer(context, writer); - statementRenderer.setCurrentMethod(method.node); - MethodReference ref = method.reference; debugEmitter.emitMethod(ref.getDescriptor()); ScopedName name = naming.getFullNameFor(ref); renderFunctionDeclaration(name); writer.append("("); - int startParam = 0; - if (method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) { - startParam = 1; - } - for (int i = startParam; i <= ref.parameterCount(); ++i) { - if (i > startParam) { - writer.append(",").ws(); + method.parameters.replay(writer, RememberedSource.FILTER_ALL); + if (method.variables != null) { + for (var variable : method.variables) { + variable.emit(debugEmitter); } - writer.append(statementRenderer.variableName(i)); - } - writer.append(")").ws().append("{").indent(); - - MethodBodyRenderer renderer = new MethodBodyRenderer(statementRenderer); - if (method.node != null) { - if (!isTrivialBody(method.node)) { - writer.softNewLine(); - method.node.acceptVisitor(renderer); - } - } else { - writer.softNewLine(); - renderer.renderNative(method); } + writer.append(")").ws().append("{").indent().softNewLine(); + method.body.replay(writer, RememberedSource.FILTER_ALL); writer.outdent().append("}"); if (name.scoped) { @@ -850,16 +805,6 @@ public class Renderer implements RenderingManager { writer.newLine(); debugEmitter.emitMethod(null); - - longLibraryUsed |= statementRenderer.isLongLibraryUsed(); - } - - private static boolean isTrivialBody(MethodNode node) { - if (!(node instanceof RegularMethodNode)) { - return false; - } - Statement body = ((RegularMethodNode) node).getBody(); - return body instanceof ReturnStatement && ((ReturnStatement) body).getResult() == null; } private void renderFunctionDeclaration(ScopedName name) { @@ -872,280 +817,18 @@ public class Renderer implements RenderingManager { } } - private void renderAsyncPrologue() { + static void renderAsyncPrologue(SourceWriter writer, RenderingContext context) { writer.append(context.mainLoopName()).append(":").ws().append("while").ws().append("(true)") .ws().append("{").ws(); writer.append("switch").ws().append("(").append(context.pointerName()).append(")").ws() .append('{').softNewLine(); } - private void renderAsyncEpilogue() { + static void renderAsyncEpilogue(SourceWriter writer) { writer.append("default:").ws().appendFunction("$rt_invalidPointer").append("();").softNewLine(); writer.append("}}").softNewLine(); } - private class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext { - private boolean async; - private StatementRenderer statementRenderer; - - MethodBodyRenderer(StatementRenderer statementRenderer) { - this.statementRenderer = statementRenderer; - } - - @Override - public DependencyInfo getDependency() { - return context.getDependencyInfo(); - } - - public void renderNative(PreparedMethod method) { - this.async = method.async; - statementRenderer.setAsync(method.async); - method.generator.generate(this, writer, method.reference); - } - - @Override - public void visit(RegularMethodNode method) { - statementRenderer.setAsync(false); - this.async = false; - MethodReference ref = method.getReference(); - for (int i = 0; i < method.getVariables().size(); ++i) { - debugEmitter.emitVariable(new String[] { method.getVariables().get(i).getName() }, - statementRenderer.variableName(i)); - } - - int variableCount = 0; - for (VariableNode var : method.getVariables()) { - variableCount = Math.max(variableCount, var.getIndex() + 1); - } - TryCatchFinder tryCatchFinder = new TryCatchFinder(); - method.getBody().acceptVisitor(tryCatchFinder); - boolean hasTryCatch = tryCatchFinder.tryCatchFound; - List variableNames = new ArrayList<>(); - for (int i = ref.parameterCount() + 1; i < variableCount; ++i) { - variableNames.add(statementRenderer.variableName(i)); - } - if (hasTryCatch) { - variableNames.add("$$je"); - } - if (!variableNames.isEmpty()) { - writer.append("var "); - for (int i = 0; i < variableNames.size(); ++i) { - if (i > 0) { - writer.append(",").ws(); - } - writer.append(variableNames.get(i)); - } - writer.append(";").softNewLine(); - } - - statementRenderer.setEnd(true); - statementRenderer.setCurrentPart(0); - - if (method.getModifiers().contains(ElementModifier.SYNCHRONIZED)) { - writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_SYNC_METHOD); - writer.append("("); - appendMonitor(statementRenderer, method); - writer.append(");").softNewLine(); - - writer.append("try").ws().append("{").softNewLine().indent(); - } - - method.getBody().acceptVisitor(statementRenderer); - - if (method.getModifiers().contains(ElementModifier.SYNCHRONIZED)) { - writer.outdent().append("}").ws().append("finally").ws().append("{").indent().softNewLine(); - - writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_SYNC_METHOD); - writer.append("("); - appendMonitor(statementRenderer, method); - writer.append(");").softNewLine(); - - writer.outdent().append("}").softNewLine(); - } - } - - @Override - public void visit(AsyncMethodNode methodNode) { - threadLibraryUsed = true; - statementRenderer.setAsync(true); - this.async = true; - MethodReference ref = methodNode.getReference(); - for (int i = 0; i < methodNode.getVariables().size(); ++i) { - debugEmitter.emitVariable(new String[] { methodNode.getVariables().get(i).getName() }, - statementRenderer.variableName(i)); - } - int variableCount = 0; - for (VariableNode var : methodNode.getVariables()) { - variableCount = Math.max(variableCount, var.getIndex() + 1); - } - List variableNames = new ArrayList<>(); - for (int i = ref.parameterCount() + 1; i < variableCount; ++i) { - variableNames.add(statementRenderer.variableName(i)); - } - TryCatchFinder tryCatchFinder = new TryCatchFinder(); - for (AsyncMethodPart part : methodNode.getBody()) { - if (!tryCatchFinder.tryCatchFound) { - part.getStatement().acceptVisitor(tryCatchFinder); - } - } - boolean hasTryCatch = tryCatchFinder.tryCatchFound; - if (hasTryCatch) { - variableNames.add("$$je"); - } - variableNames.add(context.pointerName()); - variableNames.add(context.tempVarName()); - writer.append("var "); - for (int i = 0; i < variableNames.size(); ++i) { - if (i > 0) { - writer.append(",").ws(); - } - writer.append(variableNames.get(i)); - } - writer.append(";").softNewLine(); - - int firstToSave = 0; - if (methodNode.getModifiers().contains(ElementModifier.STATIC)) { - firstToSave = 1; - } - - String popName = minifying ? "l" : "pop"; - String pushName = minifying ? "s" : "push"; - writer.append(context.pointerName()).ws().append('=').ws().append("0;").softNewLine(); - writer.append("if").ws().append("(").appendFunction("$rt_resuming").append("())").ws() - .append("{").indent().softNewLine(); - writer.append("var ").append(context.threadName()).ws().append('=').ws() - .appendFunction("$rt_nativeThread").append("();").softNewLine(); - writer.append(context.pointerName()).ws().append('=').ws().append(context.threadName()).append(".") - .append(popName).append("();"); - for (int i = variableCount - 1; i >= firstToSave; --i) { - writer.append(statementRenderer.variableName(i)).ws().append('=').ws().append(context.threadName()) - .append(".").append(popName).append("();"); - } - writer.softNewLine(); - writer.outdent().append("}").softNewLine(); - - if (methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) { - writer.append("try").ws().append('{').indent().softNewLine(); - } - - renderAsyncPrologue(); - for (int i = 0; i < methodNode.getBody().size(); ++i) { - writer.append("case ").append(i).append(":").indent().softNewLine(); - if (i == 0 && methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) { - writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_METHOD); - writer.append("("); - appendMonitor(statementRenderer, methodNode); - writer.append(");").softNewLine(); - statementRenderer.emitSuspendChecker(); - } - AsyncMethodPart part = methodNode.getBody().get(i); - statementRenderer.setEnd(true); - statementRenderer.setCurrentPart(i); - part.getStatement().acceptVisitor(statementRenderer); - writer.outdent(); - } - renderAsyncEpilogue(); - - if (methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) { - writer.outdent().append("}").ws().append("finally").ws().append('{').indent().softNewLine(); - writer.append("if").ws().append("(!").appendFunction("$rt_suspending").append("())") - .ws().append("{").indent().softNewLine(); - writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_METHOD); - writer.append("("); - appendMonitor(statementRenderer, methodNode); - writer.append(");").softNewLine(); - writer.outdent().append('}').softNewLine(); - writer.outdent().append('}').softNewLine(); - } - - writer.appendFunction("$rt_nativeThread").append("().").append(pushName).append("("); - for (int i = firstToSave; i < variableCount; ++i) { - writer.append(statementRenderer.variableName(i)).append(',').ws(); - } - writer.append(context.pointerName()).append(");"); - writer.softNewLine(); - } - - @Override - public String getParameterName(int index) { - return statementRenderer.variableName(index); - } - - @Override - public ListableClassReaderSource getClassSource() { - return classSource; - } - - @Override - public ClassReaderSource getInitialClassSource() { - return context.getInitialClassSource(); - } - - @Override - public ClassLoader getClassLoader() { - return classLoader; - } - - @Override - public Properties getProperties() { - return new Properties(properties); - } - - @Override - public T getService(Class type) { - return services.getService(type); - } - - @Override - public boolean isAsync() { - return async; - } - - @Override - public boolean isAsync(MethodReference method) { - return asyncMethods.contains(method); - } - - @Override - public boolean isAsyncFamily(MethodReference method) { - return asyncFamilyMethods.contains(method); - } - - @Override - public Diagnostics getDiagnostics() { - return diagnostics; - } - - @Override - public void typeToClassString(SourceWriter writer, ValueType type) { - context.typeToClsString(writer, type); - } - - @Override - public void useLongLibrary() { - longLibraryUsed = true; - } - - @Override - public boolean isDynamicInitializer(String className) { - return context.isDynamicInitializer(className); - } - - @Override - public String importModule(String name) { - return context.importModule(name); - } - } - - private void appendMonitor(StatementRenderer statementRenderer, MethodNode methodNode) { - if (methodNode.getModifiers().contains(ElementModifier.STATIC)) { - writer.appendFunction("$rt_cls").append("(") - .appendClass(methodNode.getReference().getClassName()).append(")"); - } else { - writer.append(statementRenderer.variableName(0)); - } - } - @Override public T getService(Class type) { return services.getService(type); diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java index ae14a4dec..d0bc72970 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java @@ -96,7 +96,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { private int currentPart; private List blockIds = new ArrayList<>(); private IntIndexedContainer blockIndexMap = new IntArrayList(); - private boolean longLibraryUsed; private static final MethodDescriptor CLINIT_METHOD = new MethodDescriptor("", ValueType.VOID); private VariableNameGenerator variableNameGenerator; private final Deque locationStack = new ArrayDeque<>(); @@ -111,8 +110,16 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { variableNameGenerator = new VariableNameGenerator(minifying); } - public boolean isLongLibraryUsed() { - return longLibraryUsed; + public void clear() { + blockIdMap.clear(); + blockIds.clear(); + blockIndexMap.clear(); + currentPart = 0; + end = false; + precedence = null; + variableNameGenerator.setCurrentMethod(null); + locationStack.clear(); + lastEmittedLocation = TextLocation.EMPTY; } public boolean isAsync() { @@ -624,7 +631,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { @Override public void visit(BinaryExpr expr) { if (expr.getType() == OperationType.LONG) { - longLibraryUsed = true; switch (expr.getOperation()) { case ADD: visitBinaryFunction(expr, "Long_add"); @@ -772,7 +778,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { switch (expr.getOperation()) { case NOT: { if (expr.getType() == OperationType.LONG) { - longLibraryUsed = true; writer.appendFunction("Long_not").append("("); precedence = Precedence.min(); expr.getOperand().acceptVisitor(this); @@ -792,7 +797,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { } case NEGATE: if (expr.getType() == OperationType.LONG) { - longLibraryUsed = true; writer.appendFunction("Long_neg").append("("); precedence = Precedence.min(); expr.getOperand().acceptVisitor(this); diff --git a/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java b/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java index 256a1b394..a03f8b502 100644 --- a/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java +++ b/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java @@ -42,15 +42,11 @@ public interface GeneratorContext extends ServiceRepository { boolean isAsync(MethodReference method); - boolean isAsyncFamily(MethodReference method); - Diagnostics getDiagnostics(); DependencyInfo getDependency(); void typeToClassString(SourceWriter writer, ValueType type); - void useLongLibrary(); - boolean isDynamicInitializer(String className); } diff --git a/core/src/main/java/org/teavm/backend/javascript/spi/ModuleImporterContext.java b/core/src/main/java/org/teavm/backend/javascript/spi/ModuleImporterContext.java deleted file mode 100644 index bab706e59..000000000 --- a/core/src/main/java/org/teavm/backend/javascript/spi/ModuleImporterContext.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2023 konsoletyper. - * - * 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 java.util.Properties; -import org.teavm.common.ServiceRepository; -import org.teavm.dependency.DependencyInfo; -import org.teavm.model.ListableClassReaderSource; -import org.teavm.model.MethodReader; - -public interface ModuleImporterContext extends ServiceRepository { - MethodReader getMethod(); - - void importModule(String name); - - ListableClassReaderSource getClassSource(); - - ClassLoader getClassLoader(); - - Properties getProperties(); - - DependencyInfo getDependency(); -} diff --git a/core/src/main/java/org/teavm/backend/javascript/templating/TemplatingAstWriter.java b/core/src/main/java/org/teavm/backend/javascript/templating/TemplatingAstWriter.java index 7e8504bb8..0a5225ccd 100644 --- a/core/src/main/java/org/teavm/backend/javascript/templating/TemplatingAstWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/templating/TemplatingAstWriter.java @@ -182,8 +182,7 @@ public class TemplatingAstWriter extends AstWriter { if (scope == null && name.getIdentifier().equals("teavm_globals")) { var oldRootScope = rootScope; rootScope = false; - writer.append("$rt_globals").append("."); - print(node.getProperty()); + writer.appendGlobal(node.getProperty().getIdentifier()); rootScope = oldRootScope; return; } 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 0290b1d10..4d408da7a 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 @@ -81,10 +81,5 @@ public class JSOPlugin implements TeaVMPlugin { wrapperGenerator); TeaVMPluginUtil.handleNatives(host, JS.class); - - jsHost.addModuleImporterProvider(providerContext -> { - var imports = repository.imports.get(providerContext.getMethod()); - return imports != null ? new JsBodyImportsContributor(imports) : null; - }); } } diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JsBodyImportsContributor.java b/jso/impl/src/main/java/org/teavm/jso/impl/JsBodyImportsContributor.java deleted file mode 100644 index 2f730b2eb..000000000 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JsBodyImportsContributor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2023 konsoletyper. - * - * 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 org.teavm.backend.javascript.spi.ModuleImporter; -import org.teavm.backend.javascript.spi.ModuleImporterContext; - -class JsBodyImportsContributor implements ModuleImporter { - private JsBodyImportInfo[] imports; - - JsBodyImportsContributor(JsBodyImportInfo[] imports) { - this.imports = imports; - } - - @Override - public void importModules(ModuleImporterContext context) { - for (var importInfo : imports) { - context.importModule(importInfo.fromModule); - } - } -}