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.
This commit is contained in:
Alexey Andreev 2019-02-13 17:03:13 +03:00
parent 1214534671
commit ce13c05342
25 changed files with 241 additions and 134 deletions

View File

@ -179,6 +179,8 @@ public class ClassGenerator {
tryUsingGenerator(method);
}
continue;
} else if (method.getProgram() == null) {
continue;
}
generateMethodForwardDeclaration(method);

View File

@ -119,7 +119,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
private final Set<MethodReference> asyncFamilyMethods = new HashSet<>();
private ClassInitializerInsertionTransformer clinitInsertionTransformer;
private List<VirtualMethodContributor> customVirtualMethods = new ArrayList<>();
private boolean classScoped;
private int topLevelNameLimit = 10000;
@Override
public List<ClassHolderTransformer> 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);
}
}

View File

@ -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();
}

View File

@ -26,16 +26,22 @@ import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
public class DefaultAliasProvider implements AliasProvider {
private final Map<String, String> classAliases = new HashMap<>();
int topLevelAliasLimit;
private final Map<String, ScopedName> classAliases = new HashMap<>();
private final Set<String> knownAliases = new HashSet<>(200, 0.5f);
private final ObjectIntMap<String> knowAliasesCounter = new ObjectIntHashMap<>();
private final Set<String> knownScopedAliases = new HashSet<>(200, 0.5f);
private final ObjectIntMap<String> knowScopedAliasesCounter = new ObjectIntHashMap<>();
private final Set<String> knownVirtualAliases = new HashSet<>(200, 0.5f);
private final ObjectIntMap<String> 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 "<init>":
alias = "_init_";
suggested = "_init_";
break;
case "<clinit>":
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<String> knowAliases, ObjectIntMap<String> indexMap, String alias) {

View File

@ -23,12 +23,13 @@ public class DefaultNamingStrategy implements NamingStrategy {
private final AliasProvider aliasProvider;
private final ClassReaderSource classSource;
private final Map<String, String> aliases = new HashMap<>();
private final Map<String, String> privateAliases = new HashMap<>();
private final Map<String, String> classAliases = new HashMap<>();
private final Map<String, ScopedName> privateAliases = new HashMap<>();
private final Map<String, ScopedName> classAliases = new HashMap<>();
private final Map<FieldReference, String> fieldAliases = new HashMap<>();
private final Map<FieldReference, String> staticFieldAliases = new HashMap<>();
private final Map<FieldReference, ScopedName> staticFieldAliases = new HashMap<>();
private final Map<String, String> functionAliases = new HashMap<>();
private final Map<String, String> classInitAliases = new HashMap<>();
private final Map<String, ScopedName> 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);

View File

@ -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<String> usedAliases = new HashSet<>();
private final Set<String> usedVirtualAliases = new HashSet<>();
private final Set<String> 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);
}
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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;
}

View File

@ -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 {

View File

@ -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<MethodReference> asyncMethods, Set<MethodReference> 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);
}
}

View File

@ -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;
}

View File

@ -68,13 +68,16 @@ public class Inlining {
private InliningStrategy strategy;
private MethodUsageCounter usageCounter;
private Set<MethodReference> methodsUsedOnce = new HashSet<>();
private boolean devirtualization;
public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo, InliningStrategy strategy,
ListableClassReaderSource classes, Predicate<MethodReference> externalMethods) {
ListableClassReaderSource classes, Predicate<MethodReference> 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;

View File

@ -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<MethodReference> methodReferences = inlining.getOrder();
int classCount = classes.getClassNames().size();
int initialValue = compileProgressValue;

View File

@ -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) {

View File

@ -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();
}
}
}

View File

@ -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;

View File

@ -54,6 +54,8 @@ public interface BuildStrategy {
void setMinifying(boolean minifying);
void setMaxTopLevelNames(int maxTopLevelNames);
void setProperties(Properties properties);
void setTransformers(String[] transformers);

View File

@ -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));

View File

@ -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();

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);