From ce13c053422004987ac269cd04f7e794f43014c4 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 13 Feb 2019 17:03:13 +0300 Subject: [PATCH] JS: add ability to set limit for top-level names. The purpose of this option is JS engines are too sensible for number of methods in closure, while they don't care about methods in an object. --- .../backend/c/generate/ClassGenerator.java | 2 + .../backend/javascript/JavaScriptTarget.java | 24 ++++---- .../javascript/codegen/AliasProvider.java | 10 ++-- .../codegen/DefaultAliasProvider.java | 48 ++++++++++----- .../codegen/DefaultNamingStrategy.java | 34 +++++++---- .../codegen/MinifyingAliasProvider.java | 59 ++++++++++++------- .../javascript/codegen/NamingStrategy.java | 12 ++-- .../javascript/codegen/ScopedName.java | 26 ++++++++ .../javascript/codegen/SourceWriter.java | 32 ++++------ .../codegen/SourceWriterBuilder.java | 7 +-- .../javascript/rendering/AstWriter.java | 2 +- .../javascript/rendering/Renderer.java | 49 ++++++++------- .../javascript/rendering/RuntimeRenderer.java | 5 +- .../teavm/model/optimization/Inlining.java | 13 +++- core/src/main/java/org/teavm/vm/TeaVM.java | 2 +- .../platform/plugin/ResourceWriterHelper.java | 2 +- .../main/java/org/teavm/cli/TeaVMRunner.java | 20 +++++-- .../java/org/teavm/tooling/TeaVMTool.java | 6 ++ .../teavm/tooling/builder/BuildStrategy.java | 2 + .../builder/InProcessBuildStrategy.java | 7 +++ .../tooling/builder/RemoteBuildStrategy.java | 5 ++ .../org/teavm/tooling/daemon/BuildDaemon.java | 1 + .../tooling/daemon/RemoteBuildRequest.java | 1 + .../java/org/teavm/devserver/CodeServlet.java | 2 +- .../org/teavm/maven/TeaVMCompileMojo.java | 4 ++ 25 files changed, 241 insertions(+), 134 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/javascript/codegen/ScopedName.java diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index 2744d2f55..7c3a63246 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -179,6 +179,8 @@ public class ClassGenerator { tryUsingGenerator(method); } continue; + } else if (method.getProgram() == null) { + continue; } generateMethodForwardDeclaration(method); diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index 834664a8e..f902c4a2e 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -119,7 +119,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { private final Set asyncFamilyMethods = new HashSet<>(); private ClassInitializerInsertionTransformer clinitInsertionTransformer; private List customVirtualMethods = new ArrayList<>(); - private boolean classScoped; + private int topLevelNameLimit = 10000; + @Override public List getTransformers() { return Collections.emptyList(); @@ -197,8 +198,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { this.debugEmitter = debugEmitter; } - public void setClassScoped(boolean classScoped) { - this.classScoped = classScoped; + public void setTopLevelNameLimit(int topLevelNameLimit) { + this.topLevelNameLimit = topLevelNameLimit; } @Override @@ -324,11 +325,12 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { return; } - AliasProvider aliasProvider = minifying ? new MinifyingAliasProvider() : new DefaultAliasProvider(); + AliasProvider aliasProvider = minifying + ? new MinifyingAliasProvider(topLevelNameLimit) + : new DefaultAliasProvider(topLevelNameLimit); DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, controller.getUnprocessedClassSource()); SourceWriterBuilder builder = new SourceWriterBuilder(naming); builder.setMinified(minifying); - builder.setClassScoped(classScoped); SourceWriter sourceWriter = builder.build(writer); DebugInformationEmitter debugEmitterToUse = debugEmitter; @@ -343,8 +345,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m)); renderingContext.setMinifying(minifying); Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods, - controller.getDiagnostics(), renderingContext, classScoped); - RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, naming, sourceWriter); + controller.getDiagnostics(), renderingContext); + RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter); renderer.setProperties(controller.getProperties()); renderer.setMinifying(minifying); renderer.setProgressConsumer(controller::reportProgress); @@ -381,10 +383,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { renderer.prepare(clsNodes); runtimeRenderer.renderRuntime(); - if (classScoped) { - sourceWriter.append("var ").append(Renderer.CONTAINER_OBJECT).ws().append("=").ws() - .append("Object.create(null);").newLine(); - } + sourceWriter.append("var ").append(renderer.getNaming().getScopeName()).ws().append("=").ws() + .append("Object.create(null);").newLine(); if (!renderer.render(clsNodes)) { return; } @@ -418,7 +418,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { int totalSize = sourceWriter.getOffset() - start; printStats(renderer, totalSize); } catch (IOException e) { - throw new RenderingException("IO Error occured", e); + throw new RenderingException("IO Error occurred", e); } } 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 2b4887c0f..9ef8e95be 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 @@ -22,15 +22,17 @@ import org.teavm.model.MethodReference; public interface AliasProvider { String getFieldAlias(FieldReference field); - String getStaticFieldAlias(FieldReference field); + ScopedName getStaticFieldAlias(FieldReference field); - String getStaticMethodAlias(MethodReference method); + ScopedName getStaticMethodAlias(MethodReference method); String getMethodAlias(MethodDescriptor method); - String getClassAlias(String className); + ScopedName getClassAlias(String className); String getFunctionAlias(String name); - String getClassInitAlias(String className); + ScopedName getClassInitAlias(String className); + + String getScopeAlias(); } 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 cb68d218c..bc5569545 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 @@ -26,16 +26,22 @@ import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; public class DefaultAliasProvider implements AliasProvider { - private final Map classAliases = new HashMap<>(); + int topLevelAliasLimit; + private final Map classAliases = new HashMap<>(); private final Set knownAliases = new HashSet<>(200, 0.5f); private final ObjectIntMap knowAliasesCounter = new ObjectIntHashMap<>(); + private final Set knownScopedAliases = new HashSet<>(200, 0.5f); + private final ObjectIntMap knowScopedAliasesCounter = new ObjectIntHashMap<>(); private final Set knownVirtualAliases = new HashSet<>(200, 0.5f); private final ObjectIntMap knowVirtualAliasesCounter = new ObjectIntHashMap<>(); + public DefaultAliasProvider(int topLevelAliasLimit) { + this.topLevelAliasLimit = topLevelAliasLimit; + } + @Override - public String getClassAlias(String cls) { - return classAliases.computeIfAbsent(cls, key -> makeUnique(knownAliases, knowAliasesCounter, - suggestAliasForClass(key))); + public ScopedName getClassAlias(String cls) { + return classAliases.computeIfAbsent(cls, key -> makeUniqueTopLevel(suggestAliasForClass(key))); } private static String suggestAliasForClass(String cls) { @@ -89,18 +95,18 @@ public class DefaultAliasProvider implements AliasProvider { } @Override - public String getStaticMethodAlias(MethodReference method) { - String alias = method.getDescriptor().getName(); - switch (alias) { + public ScopedName getStaticMethodAlias(MethodReference method) { + String suggested = method.getDescriptor().getName(); + switch (suggested) { case "": - alias = "_init_"; + suggested = "_init_"; break; case "": - alias = "_clinit_"; + suggested = "_clinit_"; break; } - return makeUnique(knownAliases, knowAliasesCounter, getClassAlias(method.getClassName()) + "_" + alias); + return makeUniqueTopLevel(getClassAlias(method.getClassName()).value + "_" + suggested); } @Override @@ -109,9 +115,8 @@ public class DefaultAliasProvider implements AliasProvider { } @Override - public String getStaticFieldAlias(FieldReference field) { - return makeUnique(knownAliases, knowAliasesCounter, - getClassAlias(field.getClassName()) + "_" + field.getFieldName()); + public ScopedName getStaticFieldAlias(FieldReference field) { + return makeUniqueTopLevel(getClassAlias(field.getClassName()).value + "_" + field.getFieldName()); } @Override @@ -120,8 +125,21 @@ public class DefaultAliasProvider implements AliasProvider { } @Override - public String getClassInitAlias(String className) { - return makeUnique(knownAliases, knowAliasesCounter, suggestAliasForClass(className) + "_$callClinit"); + public ScopedName getClassInitAlias(String className) { + return makeUniqueTopLevel(suggestAliasForClass(className) + "_$callClinit"); + } + + @Override + public String getScopeAlias() { + return makeUnique(knownAliases, knowAliasesCounter, "$java"); + } + + private ScopedName makeUniqueTopLevel(String suggested) { + if (knownAliases.size() < topLevelAliasLimit) { + return new ScopedName(false, makeUnique(knownAliases, knowAliasesCounter, suggested)); + } else { + return new ScopedName(true, makeUnique(knownScopedAliases, knowScopedAliasesCounter, suggested)); + } } private String makeUnique(Set knowAliases, ObjectIntMap indexMap, String alias) { 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 ca5e01ef0..a8d1dd225 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 @@ -23,12 +23,13 @@ public class DefaultNamingStrategy implements NamingStrategy { private final AliasProvider aliasProvider; private final ClassReaderSource classSource; private final Map aliases = new HashMap<>(); - private final Map privateAliases = new HashMap<>(); - private final Map classAliases = new HashMap<>(); + private final Map privateAliases = new HashMap<>(); + private final Map classAliases = new HashMap<>(); private final Map fieldAliases = new HashMap<>(); - private final Map staticFieldAliases = new HashMap<>(); + private final Map staticFieldAliases = new HashMap<>(); private final Map functionAliases = new HashMap<>(); - private final Map classInitAliases = new HashMap<>(); + private final Map classInitAliases = new HashMap<>(); + private String scopeName; public DefaultNamingStrategy(AliasProvider aliasProvider, ClassReaderSource classSource) { this.aliasProvider = aliasProvider; @@ -36,7 +37,7 @@ public class DefaultNamingStrategy implements NamingStrategy { } @Override - public String getNameFor(String cls) { + public ScopedName getNameFor(String cls) { return classAliases.computeIfAbsent(cls, key -> aliasProvider.getClassAlias(cls)); } @@ -52,16 +53,16 @@ public class DefaultNamingStrategy implements NamingStrategy { } @Override - public String getFullNameFor(MethodReference method) { + public ScopedName getFullNameFor(MethodReference method) { return getFullNameFor(method, 'M'); } @Override - public String getNameForInit(MethodReference method) { + public ScopedName getNameForInit(MethodReference method) { return getFullNameFor(method, 'I'); } - private String getFullNameFor(MethodReference method, char classifier) { + private ScopedName getFullNameFor(MethodReference method, char classifier) { MethodReference originalMethod = method; method = getRealMethod(method); if (method == null) { @@ -89,14 +90,14 @@ public class DefaultNamingStrategy implements NamingStrategy { } @Override - public String getFullNameFor(FieldReference field) { - String alias = staticFieldAliases.get(field); + public ScopedName getFullNameFor(FieldReference field) { + ScopedName alias = staticFieldAliases.get(field); if (alias == null) { FieldReference realField = getRealField(field); if (realField.equals(field)) { alias = aliasProvider.getStaticFieldAlias(realField); } else { - alias = getNameFor(realField); + alias = getFullNameFor(realField); } staticFieldAliases.put(field, alias); } @@ -109,10 +110,18 @@ public class DefaultNamingStrategy implements NamingStrategy { } @Override - public String getNameForClassInit(String className) { + public ScopedName getNameForClassInit(String className) { return classInitAliases.computeIfAbsent(className, key -> aliasProvider.getClassInitAlias(key)); } + @Override + public String getScopeName() { + if (scopeName == null) { + scopeName = aliasProvider.getScopeAlias(); + } + return scopeName; + } + private MethodReference getRealMethod(MethodReference methodRef) { String className = methodRef.getClassName(); while (className != null) { @@ -133,7 +142,6 @@ public class DefaultNamingStrategy implements NamingStrategy { } private FieldReference getRealField(FieldReference fieldRef) { - String initialCls = fieldRef.getClassName(); String cls = fieldRef.getClassName(); while (cls != null) { ClassReader clsReader = classSource.get(cls); 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 18fa3fb6c..ac11ad640 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 @@ -23,37 +23,37 @@ import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; public class MinifyingAliasProvider implements AliasProvider { + private int topLevelAliasLimit; private static final String startLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static final String startVirtualLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private int lastSuffix; + private int lastScopedSuffix; private int lastVirtual; private final Set usedAliases = new HashSet<>(); + private final Set usedVirtualAliases = new HashSet<>(); + private final Set usedScopedAliases = new HashSet<>(); + + public MinifyingAliasProvider(int topLevelAliasLimit) { + this.topLevelAliasLimit = topLevelAliasLimit; + } @Override public String getFieldAlias(FieldReference field) { String result; do { result = RenderingUtil.indexToId(lastVirtual++, startVirtualLetters); - } while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); + } while (!usedVirtualAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); return result; } @Override - public String getStaticFieldAlias(FieldReference field) { - String result; - do { - result = RenderingUtil.indexToId(lastSuffix++, startLetters); - } while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); - return result; + public ScopedName getStaticFieldAlias(FieldReference field) { + return createTopLevelName(); } @Override - public String getStaticMethodAlias(MethodReference method) { - String result; - do { - result = RenderingUtil.indexToId(lastSuffix++, startLetters); - } while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); - return result; + public ScopedName getStaticMethodAlias(MethodReference method) { + return createTopLevelName(); } @Override @@ -61,17 +61,13 @@ public class MinifyingAliasProvider implements AliasProvider { String result; do { result = RenderingUtil.indexToId(lastVirtual++, startVirtualLetters); - } while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); + } while (!usedVirtualAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); return result; } @Override - public String getClassAlias(String className) { - String result; - do { - result = RenderingUtil.indexToId(lastSuffix++, startLetters); - } while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); - return result; + public ScopedName getClassAlias(String className) { + return createTopLevelName(); } @Override @@ -80,11 +76,32 @@ public class MinifyingAliasProvider implements AliasProvider { } @Override - public String getClassInitAlias(String className) { + public ScopedName getClassInitAlias(String className) { + return createTopLevelName(); + } + + @Override + public String getScopeAlias() { String result; do { result = RenderingUtil.indexToId(lastSuffix++, startLetters); } while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); return result; } + + private ScopedName createTopLevelName() { + if (usedAliases.size() < topLevelAliasLimit) { + String result; + do { + result = RenderingUtil.indexToId(lastSuffix++, startLetters); + } while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); + return new ScopedName(false, result); + } else { + String result; + do { + result = RenderingUtil.indexToId(lastScopedSuffix++, startLetters); + } while (!usedScopedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); + return new ScopedName(true, result); + } + } } 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 e7f2ee79d..7c486bd40 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 @@ -20,19 +20,21 @@ import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; public interface NamingStrategy { - String getNameFor(String cls); + ScopedName getNameFor(String cls); String getNameFor(MethodDescriptor method); - String getNameForInit(MethodReference method); + ScopedName getNameForInit(MethodReference method); - String getFullNameFor(MethodReference method); + ScopedName getFullNameFor(MethodReference method); String getNameFor(FieldReference field); - String getFullNameFor(FieldReference method); + ScopedName getFullNameFor(FieldReference method); String getNameForFunction(String name); - String getNameForClassInit(String className); + ScopedName getNameForClassInit(String className); + + String getScopeName(); } diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/ScopedName.java b/core/src/main/java/org/teavm/backend/javascript/codegen/ScopedName.java new file mode 100644 index 000000000..584811212 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/ScopedName.java @@ -0,0 +1,26 @@ +/* + * Copyright 2019 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; + +public class ScopedName { + public final boolean scoped; + public final String value; + + public ScopedName(boolean scoped, String value) { + this.scoped = scoped; + this.value = value; + } +} 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 a720a79df..3e6a5d774 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 @@ -16,7 +16,6 @@ package org.teavm.backend.javascript.codegen; import java.io.IOException; -import org.teavm.backend.javascript.rendering.Renderer; import org.teavm.model.FieldReference; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; @@ -32,13 +31,11 @@ public class SourceWriter implements Appendable, LocationProvider { private int column; private int line; private int offset; - private boolean classScoped; - SourceWriter(NamingStrategy naming, Appendable innerWriter, int lineWidth, boolean classScoped) { + SourceWriter(NamingStrategy naming, Appendable innerWriter, int lineWidth) { this.naming = naming; this.innerWriter = innerWriter; this.lineWidth = lineWidth; - this.classScoped = classScoped; } void setMinified(boolean minified) { @@ -50,10 +47,6 @@ public class SourceWriter implements Appendable, LocationProvider { return this; } - public SourceWriter append(Object value) throws IOException { - return append(String.valueOf(value)); - } - public SourceWriter append(int value) throws IOException { return append(String.valueOf(value)); } @@ -102,8 +95,7 @@ public class SourceWriter implements Appendable, LocationProvider { } public SourceWriter appendClass(String cls) throws IOException { - appendScopeIfNecessary(); - return append(naming.getNameFor(cls)); + return appendName(naming.getNameFor(cls)); } public SourceWriter appendClass(Class cls) throws IOException { @@ -115,8 +107,7 @@ public class SourceWriter implements Appendable, LocationProvider { } public SourceWriter appendStaticField(FieldReference field) throws IOException { - appendScopeIfNecessary(); - return append(naming.getFullNameFor(field)); + return appendName(naming.getFullNameFor(field)); } public SourceWriter appendMethod(MethodDescriptor method) throws IOException { @@ -128,8 +119,7 @@ public class SourceWriter implements Appendable, LocationProvider { } public SourceWriter appendMethodBody(MethodReference method) throws IOException { - appendScopeIfNecessary(); - return append(naming.getFullNameFor(method)); + return appendName(naming.getFullNameFor(method)); } public SourceWriter appendMethodBody(String className, String name, ValueType... params) throws IOException { @@ -145,19 +135,19 @@ public class SourceWriter implements Appendable, LocationProvider { } public SourceWriter appendInit(MethodReference method) throws IOException { - appendScopeIfNecessary(); - return append(naming.getNameForInit(method)); + return appendName(naming.getNameForInit(method)); } public SourceWriter appendClassInit(String className) throws IOException { - appendScopeIfNecessary(); - return append(naming.getNameForClassInit(className)); + return appendName(naming.getNameForClassInit(className)); } - private void appendScopeIfNecessary() throws IOException { - if (classScoped) { - append(Renderer.CONTAINER_OBJECT).append("."); + private SourceWriter appendName(ScopedName name) throws IOException { + if (name.scoped) { + append(naming.getScopeName()).append("."); } + append(name.value); + return this; } private void appendIndent() throws IOException { diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriterBuilder.java b/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriterBuilder.java index 56785b3bf..8ecdd12ee 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriterBuilder.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriterBuilder.java @@ -18,7 +18,6 @@ package org.teavm.backend.javascript.codegen; public class SourceWriterBuilder { private NamingStrategy naming; private boolean minified; - private boolean classScoped; private int lineWidth = 512; public SourceWriterBuilder(NamingStrategy naming) { @@ -37,12 +36,8 @@ public class SourceWriterBuilder { this.lineWidth = lineWidth; } - public void setClassScoped(boolean classScoped) { - this.classScoped = classScoped; - } - public SourceWriter build(Appendable innerWriter) { - SourceWriter writer = new SourceWriter(naming, innerWriter, lineWidth, classScoped); + SourceWriter writer = new SourceWriter(naming, innerWriter, lineWidth); writer.setMinified(minified); return writer; } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/AstWriter.java b/core/src/main/java/org/teavm/backend/javascript/rendering/AstWriter.java index c8a9264ec..0cb41f7d8 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/AstWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/AstWriter.java @@ -700,7 +700,7 @@ public class AstWriter { writer.append("let").ws().append('('); printList(node.getVariables().getVariables()); writer.append(')'); - writer.append(node.getBody()); + print(node.getBody()); } private void print(ParenthesizedExpression node, int precedence) throws IOException { 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 f15700597..83b545083 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 @@ -40,6 +40,7 @@ import org.teavm.ast.RegularMethodNode; 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.ScopedName; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.spi.GeneratorContext; import org.teavm.common.ServiceRepository; @@ -61,13 +62,11 @@ import org.teavm.vm.RenderingException; import org.teavm.vm.TeaVMProgressFeedback; public class Renderer implements RenderingManager { - public static final String CONTAINER_OBJECT = "$java"; private final NamingStrategy naming; private final SourceWriter writer; private final ListableClassReaderSource classSource; private final ClassLoader classLoader; private boolean minifying; - private boolean classScoped; private final Properties properties = new Properties(); private final ServiceRepository services; private DebugInformationEmitter debugEmitter = new DummyDebugInformationEmitter(); @@ -86,7 +85,7 @@ public class Renderer implements RenderingManager { private boolean threadLibraryUsed; public Renderer(SourceWriter writer, Set asyncMethods, Set asyncFamilyMethods, - Diagnostics diagnostics, RenderingContext context, boolean classScoped) { + Diagnostics diagnostics, RenderingContext context) { this.naming = context.getNaming(); this.writer = writer; this.classSource = context.getClassSource(); @@ -96,7 +95,6 @@ public class Renderer implements RenderingManager { this.asyncFamilyMethods = new HashSet<>(asyncFamilyMethods); this.diagnostics = diagnostics; this.context = context; - this.classScoped = classScoped; } public boolean isLongLibraryUsed() { @@ -272,6 +270,7 @@ public class Renderer implements RenderingManager { for (ClassNode cls : classes) { estimator.estimate(cls); } + naming.getScopeName(); orderer.apply(naming); } } @@ -299,8 +298,8 @@ public class Renderer implements RenderingManager { } private void renderDeclaration(ClassNode cls) throws RenderingException { - String jsName = naming.getNameFor(cls.getName()); - debugEmitter.addClass(jsName, cls.getName(), cls.getParentName()); + ScopedName jsName = naming.getNameFor(cls.getName()); + debugEmitter.addClass(jsName.value, cls.getName(), cls.getParentName()); try { renderFunctionDeclaration(jsName); writer.append("()").ws().append("{") @@ -341,7 +340,7 @@ public class Renderer implements RenderingManager { } writer.outdent().append("}"); - if (classScoped) { + if (jsName.scoped) { writer.append(";"); } writer.newLine(); @@ -357,12 +356,14 @@ public class Renderer implements RenderingManager { postponedFieldInitializers.add(new PostponedFieldInitializer(fieldRef, (String) value)); value = null; } - if (classScoped) { - writer.append(CONTAINER_OBJECT).append("."); + + ScopedName fieldName = naming.getFullNameFor(fieldRef); + if (fieldName.scoped) { + writer.append(naming.getScopeName()).append("."); } else { writer.append("var "); } - writer.append(naming.getFullNameFor(fieldRef)).ws().append("=").ws(); + writer.append(fieldName.value).ws().append("=").ws(); context.constantToString(writer, value); writer.append(";").softNewLine(); } @@ -403,12 +404,15 @@ public class Renderer implements RenderingManager { throws IOException { boolean isAsync = asyncMethods.contains(clinit.getReference()); - String clinitCalled = naming.getNameFor(cls.getName()) + "_$clinitCalled"; + ScopedName className = naming.getNameFor(cls.getName()); + String clinitCalled = (className.scoped ? naming.getScopeName() + "_" : "") + className.value + + "_$clinitCalled"; if (isAsync) { writer.append("var ").append(clinitCalled).ws().append("=").ws().append("false;").softNewLine(); } - renderFunctionDeclaration(naming.getNameForClassInit(cls.getName())); + ScopedName name = naming.getNameForClassInit(cls.getName()); + renderFunctionDeclaration(name); writer.append("()").ws() .append("{").softNewLine().indent(); @@ -454,7 +458,7 @@ public class Renderer implements RenderingManager { } writer.outdent().append("}"); - if (classScoped) { + if (name.scoped) { writer.append(";"); } writer.newLine(); @@ -703,7 +707,8 @@ public class Renderer implements RenderingManager { private void renderInitializer(MethodNode method) throws IOException { MethodReference ref = method.getReference(); debugEmitter.emitMethod(ref.getDescriptor()); - renderFunctionDeclaration(naming.getNameForInit(ref)); + ScopedName name = naming.getNameForInit(ref); + renderFunctionDeclaration(name); writer.append("("); for (int i = 0; i < ref.parameterCount(); ++i) { if (i > 0) { @@ -724,7 +729,7 @@ public class Renderer implements RenderingManager { writer.append(");").softNewLine(); writer.append("return " + instanceName + ";").softNewLine(); writer.outdent().append("}"); - if (classScoped) { + if (name.scoped) { writer.append(";"); } writer.newLine(); @@ -790,7 +795,7 @@ public class Renderer implements RenderingManager { MethodReference ref = method.getReference(); debugEmitter.emitMethod(ref.getDescriptor()); - String name = naming.getFullNameFor(ref); + ScopedName name = naming.getFullNameFor(ref); renderFunctionDeclaration(name); writer.append("("); @@ -808,7 +813,7 @@ public class Renderer implements RenderingManager { method.acceptVisitor(new MethodBodyRenderer(statementRenderer)); writer.outdent().append("}"); - if (classScoped) { + if (name.scoped) { writer.append(";"); } @@ -818,13 +823,13 @@ public class Renderer implements RenderingManager { longLibraryUsed |= statementRenderer.isLongLibraryUsed(); } - private void renderFunctionDeclaration(String name) throws IOException { - if (classScoped) { - writer.append(CONTAINER_OBJECT).append(".").append(name).ws().append("=").ws(); + private void renderFunctionDeclaration(ScopedName name) throws IOException { + if (name.scoped) { + writer.append(naming.getScopeName()).append(".").append(name.value).ws().append("=").ws(); } writer.append("function"); - if (!classScoped) { - writer.append(" ").append(name); + if (!name.scoped) { + writer.append(" ").append(name.value); } } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java index 100421440..6c3f832cb 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java @@ -23,7 +23,6 @@ import java.nio.charset.StandardCharsets; import org.mozilla.javascript.CompilerEnvirons; import org.mozilla.javascript.Context; import org.mozilla.javascript.ast.AstRoot; -import org.teavm.backend.javascript.codegen.NamingStrategy; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; @@ -48,12 +47,10 @@ public class RuntimeRenderer { "setStackTrace", StackTraceElement[].class, void.class); private final ClassReaderSource classSource; - private final NamingStrategy naming; private final SourceWriter writer; - public RuntimeRenderer(ClassReaderSource classSource, NamingStrategy naming, SourceWriter writer) { + public RuntimeRenderer(ClassReaderSource classSource, SourceWriter writer) { this.classSource = classSource; - this.naming = naming; this.writer = writer; } diff --git a/core/src/main/java/org/teavm/model/optimization/Inlining.java b/core/src/main/java/org/teavm/model/optimization/Inlining.java index 146d5958e..0d6339504 100644 --- a/core/src/main/java/org/teavm/model/optimization/Inlining.java +++ b/core/src/main/java/org/teavm/model/optimization/Inlining.java @@ -68,13 +68,16 @@ public class Inlining { private InliningStrategy strategy; private MethodUsageCounter usageCounter; private Set methodsUsedOnce = new HashSet<>(); + private boolean devirtualization; public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo, InliningStrategy strategy, - ListableClassReaderSource classes, Predicate externalMethods) { + ListableClassReaderSource classes, Predicate externalMethods, + boolean devirtualization) { this.hierarchy = hierarchy; this.classes = classes; this.dependencyInfo = dependencyInfo; this.strategy = strategy; + this.devirtualization = devirtualization; usageCounter = new MethodUsageCounter(externalMethods); for (String className : classes.getClassNames()) { @@ -156,8 +159,12 @@ public class Inlining { } instructionsToSkip = new HashSet<>(); - while (applyOnce(program, method)) { - devirtualize(program, method, dependencyInfo); + if (devirtualization) { + while (applyOnce(program, method)) { + devirtualize(program, method, dependencyInfo); + } + } else { + applyOnce(program, method); } depthsByBlock = null; instructionsToSkip = null; diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index dd82b7dff..0cba30c56 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -550,7 +550,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyAnalyzer, inliningStrategy, - classes, this::isExternal); + classes, this::isExternal, optimizationLevel == TeaVMOptimizationLevel.FULL); List methodReferences = inlining.getOrder(); int classCount = classes.getClassNames().size(); int initialValue = compileProgressValue; diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java index 220407a89..09da2309a 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java @@ -30,7 +30,7 @@ final class ResourceWriterHelper { if (resource instanceof ResourceWriter) { ((ResourceWriter) resource).write(writer); } else if (resource instanceof Number) { - writer.append(resource); + writer.append(resource.toString()); } else if (resource instanceof Boolean) { writer.append(resource == Boolean.TRUE ? "true" : "false"); } else if (resource instanceof String) { diff --git a/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java b/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java index ac56034e6..369739061 100644 --- a/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java +++ b/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java @@ -134,6 +134,13 @@ public final class TeaVMRunner { .hasArg() .withDescription("Minimum heap size in bytes (for C and WebAssembly)") .create()); + options.addOption(OptionBuilder + .withLongOpt("max-toplevel-names") + .withArgName("number") + .hasArg() + .withDescription("Maximum number of names kept in top-level scope (" + + "other will be put in a separate object. 10000 by default.") + .create()); } private TeaVMRunner(CommandLine commandLine) { @@ -215,10 +222,15 @@ public final class TeaVMRunner { } private void parseJavaScriptOptions() { - if (commandLine.hasOption("m")) { - tool.setMinifying(true); - } else { - tool.setMinifying(false); + tool.setMinifying(commandLine.hasOption("m")); + + if (commandLine.hasOption("max-toplevel-names")) { + try { + tool.setMaxTopLevelNames(Integer.parseInt(commandLine.getOptionValue("max-toplevel-names"))); + } catch (NumberFormatException e) { + System.err.println("'--max-toplevel-names' must be integer number"); + printUsage(); + } } } diff --git a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java index b43b9ae77..ba32a9eb9 100644 --- a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java +++ b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java @@ -69,6 +69,7 @@ public class TeaVMTool { private TeaVMTargetType targetType = TeaVMTargetType.JAVASCRIPT; private String targetFileName = ""; private boolean minifying = true; + private int maxTopLevelNames = 10000; private String mainClass; private String entryPointName = "main"; private Properties properties = new Properties(); @@ -124,6 +125,10 @@ public class TeaVMTool { this.minifying = minifying; } + public void setMaxTopLevelNames(int maxTopLevelNames) { + this.maxTopLevelNames = maxTopLevelNames; + } + public boolean isIncremental() { return incremental; } @@ -291,6 +296,7 @@ public class TeaVMTool { private TeaVMTarget prepareJavaScriptTarget() { javaScriptTarget = new JavaScriptTarget(); javaScriptTarget.setMinifying(minifying); + javaScriptTarget.setTopLevelNameLimit(maxTopLevelNames); debugEmitter = debugInformationGenerated || sourceMapsFileGenerated ? new DebugInformationBuilder() : null; diff --git a/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java b/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java index a8e890baa..2461f279d 100644 --- a/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java @@ -54,6 +54,8 @@ public interface BuildStrategy { void setMinifying(boolean minifying); + void setMaxTopLevelNames(int maxTopLevelNames); + void setProperties(Properties properties); void setTransformers(String[] transformers); diff --git a/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java b/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java index a05b6fa98..f1a38243e 100644 --- a/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java @@ -52,6 +52,7 @@ public class InProcessBuildStrategy implements BuildStrategy { private TeaVMOptimizationLevel optimizationLevel = TeaVMOptimizationLevel.ADVANCED; private boolean fastDependencyAnalysis; private boolean minifying; + private int maxTopLevelNames; private boolean sourceMapsFileGenerated; private boolean debugInformationGenerated; private boolean sourceFilesCopied; @@ -150,6 +151,11 @@ public class InProcessBuildStrategy implements BuildStrategy { this.minifying = minifying; } + @Override + public void setMaxTopLevelNames(int maxTopLevelNames) { + this.maxTopLevelNames = maxTopLevelNames; + } + @Override public void setTransformers(String[] transformers) { this.transformers = transformers.clone(); @@ -209,6 +215,7 @@ public class InProcessBuildStrategy implements BuildStrategy { tool.setSourceFilesCopied(sourceFilesCopied); tool.setMinifying(minifying); + tool.setMaxTopLevelNames(maxTopLevelNames); tool.setIncremental(incremental); tool.getTransformers().addAll(Arrays.asList(transformers)); tool.getClassesToPreserve().addAll(Arrays.asList(classesToPreserve)); diff --git a/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java b/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java index 3da229aba..f95bc7d17 100644 --- a/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java @@ -129,6 +129,11 @@ public class RemoteBuildStrategy implements BuildStrategy { request.minifying = minifying; } + @Override + public void setMaxTopLevelNames(int maxTopLevelNames) { + request.maxTopLevelNames = maxTopLevelNames; + } + @Override public void setTransformers(String[] transformers) { request.transformers = transformers.clone(); diff --git a/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java b/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java index a12a529da..7e2cdda4e 100644 --- a/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java @@ -156,6 +156,7 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi tool.setOptimizationLevel(request.optimizationLevel); tool.setFastDependencyAnalysis(request.fastDependencyAnalysis); tool.setMinifying(request.minifying); + tool.setMaxTopLevelNames(request.maxTopLevelNames); tool.setWasmVersion(request.wasmVersion); tool.setMinHeapSize(request.heapSize); diff --git a/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java index e5dcf1f69..4cf32b9b4 100644 --- a/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java @@ -40,6 +40,7 @@ public class RemoteBuildRequest implements Serializable { public boolean incremental; public String cacheDirectory; public boolean minifying; + public int maxTopLevelNames; public Properties properties; public TeaVMOptimizationLevel optimizationLevel; public boolean fastDependencyAnalysis; diff --git a/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java b/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java index e7f0547ae..ee2db3d87 100644 --- a/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java +++ b/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java @@ -745,7 +745,7 @@ public class CodeServlet extends HttpServlet { jsTarget.setMinifying(false); jsTarget.setAstCache(astCache); jsTarget.setDebugEmitter(debugInformationBuilder); - jsTarget.setClassScoped(true); + jsTarget.setTopLevelNameLimit(500); vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE); vm.setCacheStatus(classSource); vm.addVirtualMethods(m -> true); diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TeaVMCompileMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TeaVMCompileMojo.java index e25558212..69b2de494 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/TeaVMCompileMojo.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/TeaVMCompileMojo.java @@ -80,6 +80,9 @@ public class TeaVMCompileMojo extends AbstractMojo { @Parameter(property = "teavm.minifying", defaultValue = "true") private boolean minifying = true; + @Parameter(property = "teavm.maxTopLevelNames", defaultValue = "10000") + private int maxTopLevelNames = 10000; + @Parameter private Properties properties; @@ -148,6 +151,7 @@ public class TeaVMCompileMojo extends AbstractMojo { try { builder.setClassPathEntries(prepareClassPath()); builder.setMinifying(minifying); + builder.setMaxTopLevelNames(maxTopLevelNames); builder.setTargetDirectory(targetDirectory.getAbsolutePath()); if (transformers != null) { builder.setTransformers(transformers);