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); tryUsingGenerator(method);
} }
continue; continue;
} else if (method.getProgram() == null) {
continue;
} }
generateMethodForwardDeclaration(method); generateMethodForwardDeclaration(method);

View File

@ -119,7 +119,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
private final Set<MethodReference> asyncFamilyMethods = new HashSet<>(); private final Set<MethodReference> asyncFamilyMethods = new HashSet<>();
private ClassInitializerInsertionTransformer clinitInsertionTransformer; private ClassInitializerInsertionTransformer clinitInsertionTransformer;
private List<VirtualMethodContributor> customVirtualMethods = new ArrayList<>(); private List<VirtualMethodContributor> customVirtualMethods = new ArrayList<>();
private boolean classScoped; private int topLevelNameLimit = 10000;
@Override @Override
public List<ClassHolderTransformer> getTransformers() { public List<ClassHolderTransformer> getTransformers() {
return Collections.emptyList(); return Collections.emptyList();
@ -197,8 +198,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
this.debugEmitter = debugEmitter; this.debugEmitter = debugEmitter;
} }
public void setClassScoped(boolean classScoped) { public void setTopLevelNameLimit(int topLevelNameLimit) {
this.classScoped = classScoped; this.topLevelNameLimit = topLevelNameLimit;
} }
@Override @Override
@ -324,11 +325,12 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
return; return;
} }
AliasProvider aliasProvider = minifying ? new MinifyingAliasProvider() : new DefaultAliasProvider(); AliasProvider aliasProvider = minifying
? new MinifyingAliasProvider(topLevelNameLimit)
: new DefaultAliasProvider(topLevelNameLimit);
DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, controller.getUnprocessedClassSource()); DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, controller.getUnprocessedClassSource());
SourceWriterBuilder builder = new SourceWriterBuilder(naming); SourceWriterBuilder builder = new SourceWriterBuilder(naming);
builder.setMinified(minifying); builder.setMinified(minifying);
builder.setClassScoped(classScoped);
SourceWriter sourceWriter = builder.build(writer); SourceWriter sourceWriter = builder.build(writer);
DebugInformationEmitter debugEmitterToUse = debugEmitter; DebugInformationEmitter debugEmitterToUse = debugEmitter;
@ -343,8 +345,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m)); controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m));
renderingContext.setMinifying(minifying); renderingContext.setMinifying(minifying);
Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods, Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods,
controller.getDiagnostics(), renderingContext, classScoped); controller.getDiagnostics(), renderingContext);
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, naming, sourceWriter); RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter);
renderer.setProperties(controller.getProperties()); renderer.setProperties(controller.getProperties());
renderer.setMinifying(minifying); renderer.setMinifying(minifying);
renderer.setProgressConsumer(controller::reportProgress); renderer.setProgressConsumer(controller::reportProgress);
@ -381,10 +383,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
renderer.prepare(clsNodes); renderer.prepare(clsNodes);
runtimeRenderer.renderRuntime(); runtimeRenderer.renderRuntime();
if (classScoped) { sourceWriter.append("var ").append(renderer.getNaming().getScopeName()).ws().append("=").ws()
sourceWriter.append("var ").append(Renderer.CONTAINER_OBJECT).ws().append("=").ws() .append("Object.create(null);").newLine();
.append("Object.create(null);").newLine();
}
if (!renderer.render(clsNodes)) { if (!renderer.render(clsNodes)) {
return; return;
} }
@ -418,7 +418,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
int totalSize = sourceWriter.getOffset() - start; int totalSize = sourceWriter.getOffset() - start;
printStats(renderer, totalSize); printStats(renderer, totalSize);
} catch (IOException e) { } 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 { public interface AliasProvider {
String getFieldAlias(FieldReference field); 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 getMethodAlias(MethodDescriptor method);
String getClassAlias(String className); ScopedName getClassAlias(String className);
String getFunctionAlias(String name); 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; import org.teavm.model.MethodReference;
public class DefaultAliasProvider implements AliasProvider { 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 Set<String> knownAliases = new HashSet<>(200, 0.5f);
private final ObjectIntMap<String> knowAliasesCounter = new ObjectIntHashMap<>(); 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 Set<String> knownVirtualAliases = new HashSet<>(200, 0.5f);
private final ObjectIntMap<String> knowVirtualAliasesCounter = new ObjectIntHashMap<>(); private final ObjectIntMap<String> knowVirtualAliasesCounter = new ObjectIntHashMap<>();
public DefaultAliasProvider(int topLevelAliasLimit) {
this.topLevelAliasLimit = topLevelAliasLimit;
}
@Override @Override
public String getClassAlias(String cls) { public ScopedName getClassAlias(String cls) {
return classAliases.computeIfAbsent(cls, key -> makeUnique(knownAliases, knowAliasesCounter, return classAliases.computeIfAbsent(cls, key -> makeUniqueTopLevel(suggestAliasForClass(key)));
suggestAliasForClass(key)));
} }
private static String suggestAliasForClass(String cls) { private static String suggestAliasForClass(String cls) {
@ -89,18 +95,18 @@ public class DefaultAliasProvider implements AliasProvider {
} }
@Override @Override
public String getStaticMethodAlias(MethodReference method) { public ScopedName getStaticMethodAlias(MethodReference method) {
String alias = method.getDescriptor().getName(); String suggested = method.getDescriptor().getName();
switch (alias) { switch (suggested) {
case "<init>": case "<init>":
alias = "_init_"; suggested = "_init_";
break; break;
case "<clinit>": case "<clinit>":
alias = "_clinit_"; suggested = "_clinit_";
break; break;
} }
return makeUnique(knownAliases, knowAliasesCounter, getClassAlias(method.getClassName()) + "_" + alias); return makeUniqueTopLevel(getClassAlias(method.getClassName()).value + "_" + suggested);
} }
@Override @Override
@ -109,9 +115,8 @@ public class DefaultAliasProvider implements AliasProvider {
} }
@Override @Override
public String getStaticFieldAlias(FieldReference field) { public ScopedName getStaticFieldAlias(FieldReference field) {
return makeUnique(knownAliases, knowAliasesCounter, return makeUniqueTopLevel(getClassAlias(field.getClassName()).value + "_" + field.getFieldName());
getClassAlias(field.getClassName()) + "_" + field.getFieldName());
} }
@Override @Override
@ -120,8 +125,21 @@ public class DefaultAliasProvider implements AliasProvider {
} }
@Override @Override
public String getClassInitAlias(String className) { public ScopedName getClassInitAlias(String className) {
return makeUnique(knownAliases, knowAliasesCounter, suggestAliasForClass(className) + "_$callClinit"); 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) { 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 AliasProvider aliasProvider;
private final ClassReaderSource classSource; private final ClassReaderSource classSource;
private final Map<String, String> aliases = new HashMap<>(); private final Map<String, String> aliases = new HashMap<>();
private final Map<String, String> privateAliases = new HashMap<>(); private final Map<String, ScopedName> privateAliases = new HashMap<>();
private final Map<String, String> classAliases = new HashMap<>(); private final Map<String, ScopedName> classAliases = new HashMap<>();
private final Map<FieldReference, String> fieldAliases = 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> 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) { public DefaultNamingStrategy(AliasProvider aliasProvider, ClassReaderSource classSource) {
this.aliasProvider = aliasProvider; this.aliasProvider = aliasProvider;
@ -36,7 +37,7 @@ public class DefaultNamingStrategy implements NamingStrategy {
} }
@Override @Override
public String getNameFor(String cls) { public ScopedName getNameFor(String cls) {
return classAliases.computeIfAbsent(cls, key -> aliasProvider.getClassAlias(cls)); return classAliases.computeIfAbsent(cls, key -> aliasProvider.getClassAlias(cls));
} }
@ -52,16 +53,16 @@ public class DefaultNamingStrategy implements NamingStrategy {
} }
@Override @Override
public String getFullNameFor(MethodReference method) { public ScopedName getFullNameFor(MethodReference method) {
return getFullNameFor(method, 'M'); return getFullNameFor(method, 'M');
} }
@Override @Override
public String getNameForInit(MethodReference method) { public ScopedName getNameForInit(MethodReference method) {
return getFullNameFor(method, 'I'); return getFullNameFor(method, 'I');
} }
private String getFullNameFor(MethodReference method, char classifier) { private ScopedName getFullNameFor(MethodReference method, char classifier) {
MethodReference originalMethod = method; MethodReference originalMethod = method;
method = getRealMethod(method); method = getRealMethod(method);
if (method == null) { if (method == null) {
@ -89,14 +90,14 @@ public class DefaultNamingStrategy implements NamingStrategy {
} }
@Override @Override
public String getFullNameFor(FieldReference field) { public ScopedName getFullNameFor(FieldReference field) {
String alias = staticFieldAliases.get(field); ScopedName alias = staticFieldAliases.get(field);
if (alias == null) { if (alias == null) {
FieldReference realField = getRealField(field); FieldReference realField = getRealField(field);
if (realField.equals(field)) { if (realField.equals(field)) {
alias = aliasProvider.getStaticFieldAlias(realField); alias = aliasProvider.getStaticFieldAlias(realField);
} else { } else {
alias = getNameFor(realField); alias = getFullNameFor(realField);
} }
staticFieldAliases.put(field, alias); staticFieldAliases.put(field, alias);
} }
@ -109,10 +110,18 @@ public class DefaultNamingStrategy implements NamingStrategy {
} }
@Override @Override
public String getNameForClassInit(String className) { public ScopedName getNameForClassInit(String className) {
return classInitAliases.computeIfAbsent(className, key -> aliasProvider.getClassInitAlias(key)); 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) { private MethodReference getRealMethod(MethodReference methodRef) {
String className = methodRef.getClassName(); String className = methodRef.getClassName();
while (className != null) { while (className != null) {
@ -133,7 +142,6 @@ public class DefaultNamingStrategy implements NamingStrategy {
} }
private FieldReference getRealField(FieldReference fieldRef) { private FieldReference getRealField(FieldReference fieldRef) {
String initialCls = fieldRef.getClassName();
String cls = fieldRef.getClassName(); String cls = fieldRef.getClassName();
while (cls != null) { while (cls != null) {
ClassReader clsReader = classSource.get(cls); ClassReader clsReader = classSource.get(cls);

View File

@ -23,37 +23,37 @@ import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
public class MinifyingAliasProvider implements AliasProvider { public class MinifyingAliasProvider implements AliasProvider {
private int topLevelAliasLimit;
private static final String startLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static final String startLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String startVirtualLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static final String startVirtualLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private int lastSuffix; private int lastSuffix;
private int lastScopedSuffix;
private int lastVirtual; private int lastVirtual;
private final Set<String> usedAliases = new HashSet<>(); 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 @Override
public String getFieldAlias(FieldReference field) { public String getFieldAlias(FieldReference field) {
String result; String result;
do { do {
result = RenderingUtil.indexToId(lastVirtual++, startVirtualLetters); result = RenderingUtil.indexToId(lastVirtual++, startVirtualLetters);
} while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); } while (!usedVirtualAliases.add(result) || RenderingUtil.KEYWORDS.contains(result));
return result; return result;
} }
@Override @Override
public String getStaticFieldAlias(FieldReference field) { public ScopedName getStaticFieldAlias(FieldReference field) {
String result; return createTopLevelName();
do {
result = RenderingUtil.indexToId(lastSuffix++, startLetters);
} while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result));
return result;
} }
@Override @Override
public String getStaticMethodAlias(MethodReference method) { public ScopedName getStaticMethodAlias(MethodReference method) {
String result; return createTopLevelName();
do {
result = RenderingUtil.indexToId(lastSuffix++, startLetters);
} while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result));
return result;
} }
@Override @Override
@ -61,17 +61,13 @@ public class MinifyingAliasProvider implements AliasProvider {
String result; String result;
do { do {
result = RenderingUtil.indexToId(lastVirtual++, startVirtualLetters); result = RenderingUtil.indexToId(lastVirtual++, startVirtualLetters);
} while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); } while (!usedVirtualAliases.add(result) || RenderingUtil.KEYWORDS.contains(result));
return result; return result;
} }
@Override @Override
public String getClassAlias(String className) { public ScopedName getClassAlias(String className) {
String result; return createTopLevelName();
do {
result = RenderingUtil.indexToId(lastSuffix++, startLetters);
} while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result));
return result;
} }
@Override @Override
@ -80,11 +76,32 @@ public class MinifyingAliasProvider implements AliasProvider {
} }
@Override @Override
public String getClassInitAlias(String className) { public ScopedName getClassInitAlias(String className) {
return createTopLevelName();
}
@Override
public String getScopeAlias() {
String result; String result;
do { do {
result = RenderingUtil.indexToId(lastSuffix++, startLetters); result = RenderingUtil.indexToId(lastSuffix++, startLetters);
} while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result)); } while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result));
return 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; import org.teavm.model.MethodReference;
public interface NamingStrategy { public interface NamingStrategy {
String getNameFor(String cls); ScopedName getNameFor(String cls);
String getNameFor(MethodDescriptor method); 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 getNameFor(FieldReference field);
String getFullNameFor(FieldReference method); ScopedName getFullNameFor(FieldReference method);
String getNameForFunction(String name); 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; package org.teavm.backend.javascript.codegen;
import java.io.IOException; import java.io.IOException;
import org.teavm.backend.javascript.rendering.Renderer;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -32,13 +31,11 @@ public class SourceWriter implements Appendable, LocationProvider {
private int column; private int column;
private int line; private int line;
private int offset; 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.naming = naming;
this.innerWriter = innerWriter; this.innerWriter = innerWriter;
this.lineWidth = lineWidth; this.lineWidth = lineWidth;
this.classScoped = classScoped;
} }
void setMinified(boolean minified) { void setMinified(boolean minified) {
@ -50,10 +47,6 @@ public class SourceWriter implements Appendable, LocationProvider {
return this; return this;
} }
public SourceWriter append(Object value) throws IOException {
return append(String.valueOf(value));
}
public SourceWriter append(int value) throws IOException { public SourceWriter append(int value) throws IOException {
return append(String.valueOf(value)); return append(String.valueOf(value));
} }
@ -102,8 +95,7 @@ public class SourceWriter implements Appendable, LocationProvider {
} }
public SourceWriter appendClass(String cls) throws IOException { public SourceWriter appendClass(String cls) throws IOException {
appendScopeIfNecessary(); return appendName(naming.getNameFor(cls));
return append(naming.getNameFor(cls));
} }
public SourceWriter appendClass(Class<?> cls) throws IOException { public SourceWriter appendClass(Class<?> cls) throws IOException {
@ -115,8 +107,7 @@ public class SourceWriter implements Appendable, LocationProvider {
} }
public SourceWriter appendStaticField(FieldReference field) throws IOException { public SourceWriter appendStaticField(FieldReference field) throws IOException {
appendScopeIfNecessary(); return appendName(naming.getFullNameFor(field));
return append(naming.getFullNameFor(field));
} }
public SourceWriter appendMethod(MethodDescriptor method) throws IOException { public SourceWriter appendMethod(MethodDescriptor method) throws IOException {
@ -128,8 +119,7 @@ public class SourceWriter implements Appendable, LocationProvider {
} }
public SourceWriter appendMethodBody(MethodReference method) throws IOException { public SourceWriter appendMethodBody(MethodReference method) throws IOException {
appendScopeIfNecessary(); return appendName(naming.getFullNameFor(method));
return append(naming.getFullNameFor(method));
} }
public SourceWriter appendMethodBody(String className, String name, ValueType... params) throws IOException { 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 { public SourceWriter appendInit(MethodReference method) throws IOException {
appendScopeIfNecessary(); return appendName(naming.getNameForInit(method));
return append(naming.getNameForInit(method));
} }
public SourceWriter appendClassInit(String className) throws IOException { public SourceWriter appendClassInit(String className) throws IOException {
appendScopeIfNecessary(); return appendName(naming.getNameForClassInit(className));
return append(naming.getNameForClassInit(className));
} }
private void appendScopeIfNecessary() throws IOException { private SourceWriter appendName(ScopedName name) throws IOException {
if (classScoped) { if (name.scoped) {
append(Renderer.CONTAINER_OBJECT).append("."); append(naming.getScopeName()).append(".");
} }
append(name.value);
return this;
} }
private void appendIndent() throws IOException { private void appendIndent() throws IOException {

View File

@ -18,7 +18,6 @@ package org.teavm.backend.javascript.codegen;
public class SourceWriterBuilder { public class SourceWriterBuilder {
private NamingStrategy naming; private NamingStrategy naming;
private boolean minified; private boolean minified;
private boolean classScoped;
private int lineWidth = 512; private int lineWidth = 512;
public SourceWriterBuilder(NamingStrategy naming) { public SourceWriterBuilder(NamingStrategy naming) {
@ -37,12 +36,8 @@ public class SourceWriterBuilder {
this.lineWidth = lineWidth; this.lineWidth = lineWidth;
} }
public void setClassScoped(boolean classScoped) {
this.classScoped = classScoped;
}
public SourceWriter build(Appendable innerWriter) { public SourceWriter build(Appendable innerWriter) {
SourceWriter writer = new SourceWriter(naming, innerWriter, lineWidth, classScoped); SourceWriter writer = new SourceWriter(naming, innerWriter, lineWidth);
writer.setMinified(minified); writer.setMinified(minified);
return writer; return writer;
} }

View File

@ -700,7 +700,7 @@ public class AstWriter {
writer.append("let").ws().append('('); writer.append("let").ws().append('(');
printList(node.getVariables().getVariables()); printList(node.getVariables().getVariables());
writer.append(')'); writer.append(')');
writer.append(node.getBody()); print(node.getBody());
} }
private void print(ParenthesizedExpression node, int precedence) throws IOException { 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.ast.VariableNode;
import org.teavm.backend.javascript.codegen.NamingOrderer; import org.teavm.backend.javascript.codegen.NamingOrderer;
import org.teavm.backend.javascript.codegen.NamingStrategy; 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.codegen.SourceWriter;
import org.teavm.backend.javascript.spi.GeneratorContext; import org.teavm.backend.javascript.spi.GeneratorContext;
import org.teavm.common.ServiceRepository; import org.teavm.common.ServiceRepository;
@ -61,13 +62,11 @@ import org.teavm.vm.RenderingException;
import org.teavm.vm.TeaVMProgressFeedback; import org.teavm.vm.TeaVMProgressFeedback;
public class Renderer implements RenderingManager { public class Renderer implements RenderingManager {
public static final String CONTAINER_OBJECT = "$java";
private final NamingStrategy naming; private final NamingStrategy naming;
private final SourceWriter writer; private final SourceWriter writer;
private final ListableClassReaderSource classSource; private final ListableClassReaderSource classSource;
private final ClassLoader classLoader; private final ClassLoader classLoader;
private boolean minifying; private boolean minifying;
private boolean classScoped;
private final Properties properties = new Properties(); private final Properties properties = new Properties();
private final ServiceRepository services; private final ServiceRepository services;
private DebugInformationEmitter debugEmitter = new DummyDebugInformationEmitter(); private DebugInformationEmitter debugEmitter = new DummyDebugInformationEmitter();
@ -86,7 +85,7 @@ public class Renderer implements RenderingManager {
private boolean threadLibraryUsed; private boolean threadLibraryUsed;
public Renderer(SourceWriter writer, Set<MethodReference> asyncMethods, Set<MethodReference> asyncFamilyMethods, 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.naming = context.getNaming();
this.writer = writer; this.writer = writer;
this.classSource = context.getClassSource(); this.classSource = context.getClassSource();
@ -96,7 +95,6 @@ public class Renderer implements RenderingManager {
this.asyncFamilyMethods = new HashSet<>(asyncFamilyMethods); this.asyncFamilyMethods = new HashSet<>(asyncFamilyMethods);
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
this.context = context; this.context = context;
this.classScoped = classScoped;
} }
public boolean isLongLibraryUsed() { public boolean isLongLibraryUsed() {
@ -272,6 +270,7 @@ public class Renderer implements RenderingManager {
for (ClassNode cls : classes) { for (ClassNode cls : classes) {
estimator.estimate(cls); estimator.estimate(cls);
} }
naming.getScopeName();
orderer.apply(naming); orderer.apply(naming);
} }
} }
@ -299,8 +298,8 @@ public class Renderer implements RenderingManager {
} }
private void renderDeclaration(ClassNode cls) throws RenderingException { private void renderDeclaration(ClassNode cls) throws RenderingException {
String jsName = naming.getNameFor(cls.getName()); ScopedName jsName = naming.getNameFor(cls.getName());
debugEmitter.addClass(jsName, cls.getName(), cls.getParentName()); debugEmitter.addClass(jsName.value, cls.getName(), cls.getParentName());
try { try {
renderFunctionDeclaration(jsName); renderFunctionDeclaration(jsName);
writer.append("()").ws().append("{") writer.append("()").ws().append("{")
@ -341,7 +340,7 @@ public class Renderer implements RenderingManager {
} }
writer.outdent().append("}"); writer.outdent().append("}");
if (classScoped) { if (jsName.scoped) {
writer.append(";"); writer.append(";");
} }
writer.newLine(); writer.newLine();
@ -357,12 +356,14 @@ public class Renderer implements RenderingManager {
postponedFieldInitializers.add(new PostponedFieldInitializer(fieldRef, (String) value)); postponedFieldInitializers.add(new PostponedFieldInitializer(fieldRef, (String) value));
value = null; value = null;
} }
if (classScoped) {
writer.append(CONTAINER_OBJECT).append("."); ScopedName fieldName = naming.getFullNameFor(fieldRef);
if (fieldName.scoped) {
writer.append(naming.getScopeName()).append(".");
} else { } else {
writer.append("var "); writer.append("var ");
} }
writer.append(naming.getFullNameFor(fieldRef)).ws().append("=").ws(); writer.append(fieldName.value).ws().append("=").ws();
context.constantToString(writer, value); context.constantToString(writer, value);
writer.append(";").softNewLine(); writer.append(";").softNewLine();
} }
@ -403,12 +404,15 @@ public class Renderer implements RenderingManager {
throws IOException { throws IOException {
boolean isAsync = asyncMethods.contains(clinit.getReference()); 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) { if (isAsync) {
writer.append("var ").append(clinitCalled).ws().append("=").ws().append("false;").softNewLine(); 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() writer.append("()").ws()
.append("{").softNewLine().indent(); .append("{").softNewLine().indent();
@ -454,7 +458,7 @@ public class Renderer implements RenderingManager {
} }
writer.outdent().append("}"); writer.outdent().append("}");
if (classScoped) { if (name.scoped) {
writer.append(";"); writer.append(";");
} }
writer.newLine(); writer.newLine();
@ -703,7 +707,8 @@ public class Renderer implements RenderingManager {
private void renderInitializer(MethodNode method) throws IOException { private void renderInitializer(MethodNode method) throws IOException {
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
debugEmitter.emitMethod(ref.getDescriptor()); debugEmitter.emitMethod(ref.getDescriptor());
renderFunctionDeclaration(naming.getNameForInit(ref)); ScopedName name = naming.getNameForInit(ref);
renderFunctionDeclaration(name);
writer.append("("); writer.append("(");
for (int i = 0; i < ref.parameterCount(); ++i) { for (int i = 0; i < ref.parameterCount(); ++i) {
if (i > 0) { if (i > 0) {
@ -724,7 +729,7 @@ public class Renderer implements RenderingManager {
writer.append(");").softNewLine(); writer.append(");").softNewLine();
writer.append("return " + instanceName + ";").softNewLine(); writer.append("return " + instanceName + ";").softNewLine();
writer.outdent().append("}"); writer.outdent().append("}");
if (classScoped) { if (name.scoped) {
writer.append(";"); writer.append(";");
} }
writer.newLine(); writer.newLine();
@ -790,7 +795,7 @@ public class Renderer implements RenderingManager {
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
debugEmitter.emitMethod(ref.getDescriptor()); debugEmitter.emitMethod(ref.getDescriptor());
String name = naming.getFullNameFor(ref); ScopedName name = naming.getFullNameFor(ref);
renderFunctionDeclaration(name); renderFunctionDeclaration(name);
writer.append("("); writer.append("(");
@ -808,7 +813,7 @@ public class Renderer implements RenderingManager {
method.acceptVisitor(new MethodBodyRenderer(statementRenderer)); method.acceptVisitor(new MethodBodyRenderer(statementRenderer));
writer.outdent().append("}"); writer.outdent().append("}");
if (classScoped) { if (name.scoped) {
writer.append(";"); writer.append(";");
} }
@ -818,13 +823,13 @@ public class Renderer implements RenderingManager {
longLibraryUsed |= statementRenderer.isLongLibraryUsed(); longLibraryUsed |= statementRenderer.isLongLibraryUsed();
} }
private void renderFunctionDeclaration(String name) throws IOException { private void renderFunctionDeclaration(ScopedName name) throws IOException {
if (classScoped) { if (name.scoped) {
writer.append(CONTAINER_OBJECT).append(".").append(name).ws().append("=").ws(); writer.append(naming.getScopeName()).append(".").append(name.value).ws().append("=").ws();
} }
writer.append("function"); writer.append("function");
if (!classScoped) { if (!name.scoped) {
writer.append(" ").append(name); 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.CompilerEnvirons;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
import org.mozilla.javascript.ast.AstRoot; import org.mozilla.javascript.ast.AstRoot;
import org.teavm.backend.javascript.codegen.NamingStrategy;
import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
@ -48,12 +47,10 @@ public class RuntimeRenderer {
"setStackTrace", StackTraceElement[].class, void.class); "setStackTrace", StackTraceElement[].class, void.class);
private final ClassReaderSource classSource; private final ClassReaderSource classSource;
private final NamingStrategy naming;
private final SourceWriter writer; private final SourceWriter writer;
public RuntimeRenderer(ClassReaderSource classSource, NamingStrategy naming, SourceWriter writer) { public RuntimeRenderer(ClassReaderSource classSource, SourceWriter writer) {
this.classSource = classSource; this.classSource = classSource;
this.naming = naming;
this.writer = writer; this.writer = writer;
} }

View File

@ -68,13 +68,16 @@ public class Inlining {
private InliningStrategy strategy; private InliningStrategy strategy;
private MethodUsageCounter usageCounter; private MethodUsageCounter usageCounter;
private Set<MethodReference> methodsUsedOnce = new HashSet<>(); private Set<MethodReference> methodsUsedOnce = new HashSet<>();
private boolean devirtualization;
public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo, InliningStrategy strategy, public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo, InliningStrategy strategy,
ListableClassReaderSource classes, Predicate<MethodReference> externalMethods) { ListableClassReaderSource classes, Predicate<MethodReference> externalMethods,
boolean devirtualization) {
this.hierarchy = hierarchy; this.hierarchy = hierarchy;
this.classes = classes; this.classes = classes;
this.dependencyInfo = dependencyInfo; this.dependencyInfo = dependencyInfo;
this.strategy = strategy; this.strategy = strategy;
this.devirtualization = devirtualization;
usageCounter = new MethodUsageCounter(externalMethods); usageCounter = new MethodUsageCounter(externalMethods);
for (String className : classes.getClassNames()) { for (String className : classes.getClassNames()) {
@ -156,8 +159,12 @@ public class Inlining {
} }
instructionsToSkip = new HashSet<>(); instructionsToSkip = new HashSet<>();
while (applyOnce(program, method)) { if (devirtualization) {
devirtualize(program, method, dependencyInfo); while (applyOnce(program, method)) {
devirtualize(program, method, dependencyInfo);
}
} else {
applyOnce(program, method);
} }
depthsByBlock = null; depthsByBlock = null;
instructionsToSkip = null; instructionsToSkip = null;

View File

@ -550,7 +550,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} }
Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyAnalyzer, inliningStrategy, Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyAnalyzer, inliningStrategy,
classes, this::isExternal); classes, this::isExternal, optimizationLevel == TeaVMOptimizationLevel.FULL);
List<MethodReference> methodReferences = inlining.getOrder(); List<MethodReference> methodReferences = inlining.getOrder();
int classCount = classes.getClassNames().size(); int classCount = classes.getClassNames().size();
int initialValue = compileProgressValue; int initialValue = compileProgressValue;

View File

@ -30,7 +30,7 @@ final class ResourceWriterHelper {
if (resource instanceof ResourceWriter) { if (resource instanceof ResourceWriter) {
((ResourceWriter) resource).write(writer); ((ResourceWriter) resource).write(writer);
} else if (resource instanceof Number) { } else if (resource instanceof Number) {
writer.append(resource); writer.append(resource.toString());
} else if (resource instanceof Boolean) { } else if (resource instanceof Boolean) {
writer.append(resource == Boolean.TRUE ? "true" : "false"); writer.append(resource == Boolean.TRUE ? "true" : "false");
} else if (resource instanceof String) { } else if (resource instanceof String) {

View File

@ -134,6 +134,13 @@ public final class TeaVMRunner {
.hasArg() .hasArg()
.withDescription("Minimum heap size in bytes (for C and WebAssembly)") .withDescription("Minimum heap size in bytes (for C and WebAssembly)")
.create()); .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) { private TeaVMRunner(CommandLine commandLine) {
@ -215,10 +222,15 @@ public final class TeaVMRunner {
} }
private void parseJavaScriptOptions() { private void parseJavaScriptOptions() {
if (commandLine.hasOption("m")) { tool.setMinifying(commandLine.hasOption("m"));
tool.setMinifying(true);
} else { if (commandLine.hasOption("max-toplevel-names")) {
tool.setMinifying(false); 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 TeaVMTargetType targetType = TeaVMTargetType.JAVASCRIPT;
private String targetFileName = ""; private String targetFileName = "";
private boolean minifying = true; private boolean minifying = true;
private int maxTopLevelNames = 10000;
private String mainClass; private String mainClass;
private String entryPointName = "main"; private String entryPointName = "main";
private Properties properties = new Properties(); private Properties properties = new Properties();
@ -124,6 +125,10 @@ public class TeaVMTool {
this.minifying = minifying; this.minifying = minifying;
} }
public void setMaxTopLevelNames(int maxTopLevelNames) {
this.maxTopLevelNames = maxTopLevelNames;
}
public boolean isIncremental() { public boolean isIncremental() {
return incremental; return incremental;
} }
@ -291,6 +296,7 @@ public class TeaVMTool {
private TeaVMTarget prepareJavaScriptTarget() { private TeaVMTarget prepareJavaScriptTarget() {
javaScriptTarget = new JavaScriptTarget(); javaScriptTarget = new JavaScriptTarget();
javaScriptTarget.setMinifying(minifying); javaScriptTarget.setMinifying(minifying);
javaScriptTarget.setTopLevelNameLimit(maxTopLevelNames);
debugEmitter = debugInformationGenerated || sourceMapsFileGenerated debugEmitter = debugInformationGenerated || sourceMapsFileGenerated
? new DebugInformationBuilder() : null; ? new DebugInformationBuilder() : null;

View File

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

View File

@ -52,6 +52,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
private TeaVMOptimizationLevel optimizationLevel = TeaVMOptimizationLevel.ADVANCED; private TeaVMOptimizationLevel optimizationLevel = TeaVMOptimizationLevel.ADVANCED;
private boolean fastDependencyAnalysis; private boolean fastDependencyAnalysis;
private boolean minifying; private boolean minifying;
private int maxTopLevelNames;
private boolean sourceMapsFileGenerated; private boolean sourceMapsFileGenerated;
private boolean debugInformationGenerated; private boolean debugInformationGenerated;
private boolean sourceFilesCopied; private boolean sourceFilesCopied;
@ -150,6 +151,11 @@ public class InProcessBuildStrategy implements BuildStrategy {
this.minifying = minifying; this.minifying = minifying;
} }
@Override
public void setMaxTopLevelNames(int maxTopLevelNames) {
this.maxTopLevelNames = maxTopLevelNames;
}
@Override @Override
public void setTransformers(String[] transformers) { public void setTransformers(String[] transformers) {
this.transformers = transformers.clone(); this.transformers = transformers.clone();
@ -209,6 +215,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
tool.setSourceFilesCopied(sourceFilesCopied); tool.setSourceFilesCopied(sourceFilesCopied);
tool.setMinifying(minifying); tool.setMinifying(minifying);
tool.setMaxTopLevelNames(maxTopLevelNames);
tool.setIncremental(incremental); tool.setIncremental(incremental);
tool.getTransformers().addAll(Arrays.asList(transformers)); tool.getTransformers().addAll(Arrays.asList(transformers));
tool.getClassesToPreserve().addAll(Arrays.asList(classesToPreserve)); tool.getClassesToPreserve().addAll(Arrays.asList(classesToPreserve));

View File

@ -129,6 +129,11 @@ public class RemoteBuildStrategy implements BuildStrategy {
request.minifying = minifying; request.minifying = minifying;
} }
@Override
public void setMaxTopLevelNames(int maxTopLevelNames) {
request.maxTopLevelNames = maxTopLevelNames;
}
@Override @Override
public void setTransformers(String[] transformers) { public void setTransformers(String[] transformers) {
request.transformers = transformers.clone(); request.transformers = transformers.clone();

View File

@ -156,6 +156,7 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi
tool.setOptimizationLevel(request.optimizationLevel); tool.setOptimizationLevel(request.optimizationLevel);
tool.setFastDependencyAnalysis(request.fastDependencyAnalysis); tool.setFastDependencyAnalysis(request.fastDependencyAnalysis);
tool.setMinifying(request.minifying); tool.setMinifying(request.minifying);
tool.setMaxTopLevelNames(request.maxTopLevelNames);
tool.setWasmVersion(request.wasmVersion); tool.setWasmVersion(request.wasmVersion);
tool.setMinHeapSize(request.heapSize); tool.setMinHeapSize(request.heapSize);

View File

@ -40,6 +40,7 @@ public class RemoteBuildRequest implements Serializable {
public boolean incremental; public boolean incremental;
public String cacheDirectory; public String cacheDirectory;
public boolean minifying; public boolean minifying;
public int maxTopLevelNames;
public Properties properties; public Properties properties;
public TeaVMOptimizationLevel optimizationLevel; public TeaVMOptimizationLevel optimizationLevel;
public boolean fastDependencyAnalysis; public boolean fastDependencyAnalysis;

View File

@ -745,7 +745,7 @@ public class CodeServlet extends HttpServlet {
jsTarget.setMinifying(false); jsTarget.setMinifying(false);
jsTarget.setAstCache(astCache); jsTarget.setAstCache(astCache);
jsTarget.setDebugEmitter(debugInformationBuilder); jsTarget.setDebugEmitter(debugInformationBuilder);
jsTarget.setClassScoped(true); jsTarget.setTopLevelNameLimit(500);
vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE); vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
vm.setCacheStatus(classSource); vm.setCacheStatus(classSource);
vm.addVirtualMethods(m -> true); vm.addVirtualMethods(m -> true);

View File

@ -80,6 +80,9 @@ public class TeaVMCompileMojo extends AbstractMojo {
@Parameter(property = "teavm.minifying", defaultValue = "true") @Parameter(property = "teavm.minifying", defaultValue = "true")
private boolean minifying = true; private boolean minifying = true;
@Parameter(property = "teavm.maxTopLevelNames", defaultValue = "10000")
private int maxTopLevelNames = 10000;
@Parameter @Parameter
private Properties properties; private Properties properties;
@ -148,6 +151,7 @@ public class TeaVMCompileMojo extends AbstractMojo {
try { try {
builder.setClassPathEntries(prepareClassPath()); builder.setClassPathEntries(prepareClassPath());
builder.setMinifying(minifying); builder.setMinifying(minifying);
builder.setMaxTopLevelNames(maxTopLevelNames);
builder.setTargetDirectory(targetDirectory.getAbsolutePath()); builder.setTargetDirectory(targetDirectory.getAbsolutePath());
if (transformers != null) { if (transformers != null) {
builder.setTransformers(transformers); builder.setTransformers(transformers);