js: add limit for top-level declarations

Rationale: turns out that V8 utilizes stack even to represent module-level functions.
This can cause SOE when there's too many classes and methods in source JVM
This commit is contained in:
Alexey Andreev 2024-01-30 17:01:28 +01:00
parent ca273390ef
commit 6ac598b927
37 changed files with 697 additions and 352 deletions

View File

@ -44,7 +44,7 @@ public class ServiceLoaderJSSupport implements Generator {
}
first = false;
writer.append("[").appendClass(implName).append(",").ws()
.appendMethodBody(new MethodReference(implName, INIT_METHOD))
.appendMethod(new MethodReference(implName, INIT_METHOD))
.append("]");
}
}

View File

@ -272,7 +272,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
if (method.getResultType() != ValueType.VOID) {
writer.append("return ");
}
writer.appendMethodBody(method.getReference());
writer.appendMethod(method.getReference());
writer.append('(');
boolean first = true;
@ -315,30 +315,30 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
writer.appendMethodBody(new MethodReference(Boolean.class, "valueOf", boolean.class,
writer.appendMethod(new MethodReference(Boolean.class, "valueOf", boolean.class,
Boolean.class));
break;
case BYTE:
writer.appendMethodBody(new MethodReference(Byte.class, "valueOf", byte.class, Byte.class));
writer.appendMethod(new MethodReference(Byte.class, "valueOf", byte.class, Byte.class));
break;
case SHORT:
writer.appendMethodBody(new MethodReference(Short.class, "valueOf", short.class, Short.class));
writer.appendMethod(new MethodReference(Short.class, "valueOf", short.class, Short.class));
break;
case CHARACTER:
writer.appendMethodBody(new MethodReference(Character.class, "valueOf", char.class,
writer.appendMethod(new MethodReference(Character.class, "valueOf", char.class,
Character.class));
break;
case INTEGER:
writer.appendMethodBody(new MethodReference(Integer.class, "valueOf", int.class, Integer.class));
writer.appendMethod(new MethodReference(Integer.class, "valueOf", int.class, Integer.class));
break;
case LONG:
writer.appendMethodBody(new MethodReference(Long.class, "valueOf", long.class, Long.class));
writer.appendMethod(new MethodReference(Long.class, "valueOf", long.class, Long.class));
break;
case FLOAT:
writer.appendMethodBody(new MethodReference(Float.class, "valueOf", float.class, Float.class));
writer.appendMethod(new MethodReference(Float.class, "valueOf", float.class, Float.class));
break;
case DOUBLE:
writer.appendMethodBody(new MethodReference(Double.class, "valueOf", double.class, Double.class));
writer.appendMethod(new MethodReference(Double.class, "valueOf", double.class, Double.class));
break;
}
writer.append('(');
@ -355,28 +355,28 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
writer.appendMethodBody(new MethodReference(Boolean.class, "booleanValue", boolean.class));
writer.appendMethod(new MethodReference(Boolean.class, "booleanValue", boolean.class));
break;
case BYTE:
writer.appendMethodBody(new MethodReference(Byte.class, "byteValue", byte.class));
writer.appendMethod(new MethodReference(Byte.class, "byteValue", byte.class));
break;
case SHORT:
writer.appendMethodBody(new MethodReference(Short.class, "shortValue", short.class));
writer.appendMethod(new MethodReference(Short.class, "shortValue", short.class));
break;
case CHARACTER:
writer.appendMethodBody(new MethodReference(Character.class, "charValue", char.class));
writer.appendMethod(new MethodReference(Character.class, "charValue", char.class));
break;
case INTEGER:
writer.appendMethodBody(new MethodReference(Integer.class, "intValue", int.class));
writer.appendMethod(new MethodReference(Integer.class, "intValue", int.class));
break;
case LONG:
writer.appendMethodBody(new MethodReference(Long.class, "longValue", long.class));
writer.appendMethod(new MethodReference(Long.class, "longValue", long.class));
break;
case FLOAT:
writer.appendMethodBody(new MethodReference(Float.class, "floatValue", float.class));
writer.appendMethod(new MethodReference(Float.class, "floatValue", float.class));
break;
case DOUBLE:
writer.appendMethodBody(new MethodReference(Double.class, "doubleValue", double.class));
writer.appendMethod(new MethodReference(Double.class, "doubleValue", double.class));
break;
}
writer.append('(');

View File

@ -129,6 +129,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
private final Map<String, String> importedModules = new LinkedHashMap<>();
private JavaScriptTemplateFactory templateFactory;
private JSModuleType moduleType = JSModuleType.UMD;
private int maxTopLevelNames = 80_000;
@Override
public List<ClassHolderTransformer> getTransformers() {
@ -228,6 +229,10 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
this.stackTraceIncluded = stackTraceIncluded;
}
public void setMaxTopLevelNames(int maxTopLevelNames) {
this.maxTopLevelNames = maxTopLevelNames;
}
@Override
public List<TeaVMHostExtension> getHostExtensions() {
return Collections.singletonList(this);
@ -351,7 +356,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
}
private void emit(ListableClassHolderSource classes, Writer writer, BuildTarget target) {
var aliasProvider = obfuscated ? new MinifyingAliasProvider() : new DefaultAliasProvider();
var aliasProvider = obfuscated
? new MinifyingAliasProvider(maxTopLevelNames)
: new DefaultAliasProvider(maxTopLevelNames);
DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, controller.getUnprocessedClassSource());
DebugInformationEmitter debugEmitterToUse = debugEmitter;
if (debugEmitterToUse == null) {
@ -410,9 +417,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
for (var entry : controller.getEntryPoints().entrySet()) {
var alias = "$rt_export_" + entry.getKey();
var ref = entry.getValue().getMethod();
rememberingWriter.append("let ").appendFunction(alias).ws().append("=").ws()
.appendFunction("$rt_mainStarter").append("(").appendMethodBody(ref);
rememberingWriter.append(");").newLine();
rememberingWriter.startVariableDeclaration().appendFunction(alias)
.appendFunction("$rt_mainStarter").append("(").appendMethod(ref);
rememberingWriter.append(")").endDeclaration();
rememberingWriter.appendFunction(alias).append(".")
.append("javaException").ws().append("=").ws().appendFunction("$rt_javaException")
.append(";").newLine();
@ -424,31 +431,49 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
var epilogue = rememberingWriter.save();
rememberingWriter.clear();
if (renderingContext.isMinifying()) {
var frequencyEstimator = new NameFrequencyEstimator();
declarations.replay(frequencyEstimator, RememberedSource.FILTER_REF);
epilogue.replay(frequencyEstimator, RememberedSource.FILTER_REF);
frequencyEstimator.apply(naming);
}
var sourceWriter = builder.build(writer);
sourceWriter.setDebugInformationEmitter(debugEmitterToUse);
printWrapperStart(sourceWriter);
int start = sourceWriter.getOffset();
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter,
controller.getClassInitializerInfo());
var runtimeRenderer = new RuntimeRenderer(classes, rememberingWriter, controller.getClassInitializerInfo());
runtimeRenderer.prepareAstParts(renderer.isThreadLibraryUsed());
declarations.replay(runtimeRenderer.sink, RememberedSource.FILTER_REF);
epilogue.replay(runtimeRenderer.sink, RememberedSource.FILTER_REF);
runtimeRenderer.removeUnusedParts();
runtimeRenderer.renderRuntime();
declarations.write(sourceWriter, 0);
var runtime = rememberingWriter.save();
rememberingWriter.clear();
runtimeRenderer.renderEpilogue();
var runtimeEpilogue = rememberingWriter.save();
rememberingWriter.clear();
naming.additionalScopeName();
naming.functionName("$rt_exports");
for (var module : importedModules.values()) {
naming.functionName(module);
}
for (var exportedName : controller.getEntryPoints().keySet()) {
naming.functionName("$rt_export_" + exportedName);
}
var frequencyEstimator = new NameFrequencyEstimator();
runtime.replay(frequencyEstimator, RememberedSource.FILTER_REF);
runtimeEpilogue.replay(frequencyEstimator, RememberedSource.FILTER_REF);
declarations.replay(frequencyEstimator, RememberedSource.FILTER_REF);
epilogue.replay(frequencyEstimator, RememberedSource.FILTER_REF);
frequencyEstimator.apply(naming);
var sourceWriter = builder.build(writer);
sourceWriter.setDebugInformationEmitter(debugEmitterToUse);
printWrapperStart(sourceWriter);
if (frequencyEstimator.hasAdditionalScope()) {
sourceWriter.append("let ").append(naming.additionalScopeName()).ws().append('=').ws()
.append("{};").softNewLine();
}
int start = sourceWriter.getOffset();
runtime.write(sourceWriter, 0);
declarations.write(sourceWriter, 0);
runtimeEpilogue.write(sourceWriter, 0);
epilogue.write(sourceWriter, 0);
printWrapperEnd(sourceWriter);
printModuleEnd(sourceWriter);
sourceWriter.finish();
int totalSize = sourceWriter.getOffset() - start;
printStats(sourceWriter, totalSize);
@ -560,7 +585,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
}
}
private void printWrapperEnd(SourceWriter writer) {
private void printModuleEnd(SourceWriter writer) {
switch (moduleType) {
case UMD:
printUmdEnd(writer);
@ -580,7 +605,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
private void printUmdEnd(SourceWriter writer) {
for (var export : controller.getEntryPoints().keySet()) {
writer.appendFunction("$rt_exports").append(".").append(export).ws().append("=").ws()
.appendFunction("$rt_export_" + export);
.appendFunction("$rt_export_" + export).append(";").softNewLine();
}
writer.outdent().append("}));").newLine();
}

View File

@ -22,17 +22,19 @@ 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);
ScopedName getFunctionAlias(String name);
String getClassInitAlias(String className);
ScopedName getClassInitAlias(String className);
String getAdditionalScopeName();
void reserveName(String name);
}

View File

@ -17,24 +17,27 @@ package org.teavm.backend.javascript.codegen;
import com.carrotsearch.hppc.ObjectIntHashMap;
import com.carrotsearch.hppc.ObjectIntMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
public class DefaultAliasProvider implements AliasProvider {
private final Map<String, String> classAliases = new HashMap<>();
private final int maxTopLevelNames;
private final Set<String> knownAliases = new HashSet<>(200, 0.5f);
private final ObjectIntMap<String> knowAliasesCounter = new ObjectIntHashMap<>();
private final Set<String> knownVirtualAliases = new HashSet<>(200, 0.5f);
private final ObjectIntMap<String> knowVirtualAliasesCounter = new ObjectIntHashMap<>();
private final Set<String> knownInstanceAliases = new HashSet<>(200, 0.5f);
private final ObjectIntMap<String> knowInstanceAliasesCounter = new ObjectIntHashMap<>();
private boolean additionalScopeStarted;
public DefaultAliasProvider(int maxTopLevelNames) {
this.maxTopLevelNames = maxTopLevelNames;
}
@Override
public String getClassAlias(String cls) {
return classAliases.computeIfAbsent(cls, key -> makeUniqueTopLevel(suggestAliasForClass(key)));
public ScopedName getClassAlias(String cls) {
return makeUnique(suggestAliasForClass(cls));
}
private static String suggestAliasForClass(String cls) {
@ -84,11 +87,11 @@ public class DefaultAliasProvider implements AliasProvider {
alias = "$" + alias;
break;
}
return makeUnique(knownVirtualAliases, knowVirtualAliasesCounter, alias);
return makeUniqueInstance(alias);
}
@Override
public String getStaticMethodAlias(MethodReference method) {
public ScopedName getStaticMethodAlias(MethodReference method) {
String suggested = method.getDescriptor().getName();
switch (suggested) {
case "<init>":
@ -99,35 +102,55 @@ public class DefaultAliasProvider implements AliasProvider {
break;
}
return makeUniqueTopLevel(getClassAlias(method.getClassName()) + "_" + suggested);
return makeUnique(suggestAliasForClass(method.getClassName()) + "_" + suggested);
}
@Override
public String getFieldAlias(FieldReference field) {
return makeUnique(knownVirtualAliases, knowVirtualAliasesCounter, "$" + field.getFieldName());
return makeUniqueInstance("$" + field.getFieldName());
}
@Override
public String getStaticFieldAlias(FieldReference field) {
return makeUniqueTopLevel(getClassAlias(field.getClassName()) + "_" + field.getFieldName());
public ScopedName getStaticFieldAlias(FieldReference field) {
return makeUnique(suggestAliasForClass(field.getClassName()) + "_" + field.getFieldName());
}
@Override
public String getFunctionAlias(String name) {
return name;
public ScopedName getFunctionAlias(String name) {
return makeUnique(name);
}
@Override
public String getClassInitAlias(String className) {
return makeUniqueTopLevel(suggestAliasForClass(className) + "_$callClinit");
public ScopedName getClassInitAlias(String className) {
return makeUnique(suggestAliasForClass(className) + "_$callClinit");
}
@Override
public String getAdditionalScopeName() {
return makeUnique("$rt_java").name;
}
@Override
public void reserveName(String name) {
}
private String makeUniqueTopLevel(String suggested) {
return makeUnique(knownAliases, knowAliasesCounter, suggested);
private ScopedName makeUnique(String suggested) {
suggested = sanitize(suggested);
if (!additionalScopeStarted && knownAliases.size() >= maxTopLevelNames) {
additionalScopeStarted = true;
knownAliases.clear();
knowAliasesCounter.clear();
}
var alias = suggested;
int index = knowAliasesCounter.get(alias);
if (index > 0) {
alias = suggested + index++;
}
while (!knownAliases.add(alias)) {
alias = suggested + index++;
}
knowAliasesCounter.put(alias, index);
return new ScopedName(alias, additionalScopeStarted);
}
private String sanitize(String s) {
@ -163,17 +186,17 @@ public class DefaultAliasProvider implements AliasProvider {
return isIdentifierStart(c) || c >= '0' && c <= '9';
}
private String makeUnique(Set<String> knowAliases, ObjectIntMap<String> indexMap, String alias) {
private String makeUniqueInstance(String alias) {
alias = sanitize(alias);
String uniqueAlias = alias;
int index = indexMap.get(alias);
int index = knowInstanceAliasesCounter.get(alias);
if (index > 0) {
uniqueAlias = alias + index++;
}
while (!knowAliases.add(uniqueAlias)) {
while (!knownInstanceAliases.add(uniqueAlias)) {
uniqueAlias = alias + index++;
}
indexMap.put(alias, index);
knowInstanceAliasesCounter.put(alias, index);
return uniqueAlias;
}
}

View File

@ -33,12 +33,13 @@ public class DefaultNamingStrategy implements NamingStrategy {
private final AliasProvider aliasProvider;
private final ClassReaderSource classSource;
private final Map<MethodDescriptor, String> aliases = new HashMap<>();
private final Map<Key, String> privateAliases = new HashMap<>();
private final Map<String, String> classAliases = new HashMap<>();
private final Map<Key, 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<String, String> functionAliases = new HashMap<>();
private final Map<String, String> classInitAliases = new HashMap<>();
private final Map<FieldReference, ScopedName> staticFieldAliases = new HashMap<>();
private final Map<String, ScopedName> functionAliases = new HashMap<>();
private final Map<String, ScopedName> classInitAliases = new HashMap<>();
private String additionalScopeName;
public DefaultNamingStrategy(AliasProvider aliasProvider, ClassReaderSource classSource) {
this.aliasProvider = aliasProvider;
@ -46,12 +47,12 @@ public class DefaultNamingStrategy implements NamingStrategy {
}
@Override
public String getNameFor(String cls) {
public ScopedName className(String cls) {
return classAliases.computeIfAbsent(cls, key -> aliasProvider.getClassAlias(cls));
}
@Override
public String getNameFor(MethodDescriptor method) {
public String instanceMethodName(MethodDescriptor method) {
String alias = aliases.get(method);
if (alias == null) {
alias = aliasProvider.getMethodAlias(method);
@ -61,16 +62,16 @@ public class DefaultNamingStrategy implements NamingStrategy {
}
@Override
public String getFullNameFor(MethodReference method) {
public ScopedName methodName(MethodReference method) {
return getFullNameFor(method, NO_CLASSIFIER);
}
@Override
public String getNameForInit(MethodReference method) {
public ScopedName initializerName(MethodReference method) {
return getFullNameFor(method, INIT_CLASSIFIER);
}
private String getFullNameFor(MethodReference method, byte classifier) {
private ScopedName getFullNameFor(MethodReference method, byte classifier) {
MethodReference originalMethod = method;
method = getRealMethod(method);
if (method == null) {
@ -82,14 +83,14 @@ public class DefaultNamingStrategy implements NamingStrategy {
}
@Override
public String getNameFor(FieldReference field) {
public String instanceFieldName(FieldReference field) {
String alias = fieldAliases.get(field);
if (alias == null) {
FieldReference realField = getRealField(field);
if (realField.equals(field)) {
alias = aliasProvider.getFieldAlias(realField);
} else {
alias = getNameFor(realField);
alias = instanceFieldName(realField);
}
fieldAliases.put(field, alias);
}
@ -97,14 +98,14 @@ public class DefaultNamingStrategy implements NamingStrategy {
}
@Override
public String getFullNameFor(FieldReference field) {
public ScopedName fieldName(FieldReference field) {
var alias = staticFieldAliases.get(field);
if (alias == null) {
FieldReference realField = getRealField(field);
if (realField.equals(field)) {
alias = aliasProvider.getStaticFieldAlias(realField);
} else {
alias = getFullNameFor(realField);
alias = fieldName(realField);
}
staticFieldAliases.put(field, alias);
}
@ -112,13 +113,21 @@ public class DefaultNamingStrategy implements NamingStrategy {
}
@Override
public String getNameForFunction(String name) {
return functionAliases.computeIfAbsent(name, key -> aliasProvider.getFunctionAlias(key));
public ScopedName functionName(String name) {
return functionAliases.computeIfAbsent(name, aliasProvider::getFunctionAlias);
}
@Override
public String getNameForClassInit(String className) {
return classInitAliases.computeIfAbsent(className, key -> aliasProvider.getClassInitAlias(key));
public ScopedName classInitializerName(String className) {
return classInitAliases.computeIfAbsent(className, aliasProvider::getClassInitAlias);
}
@Override
public String additionalScopeName() {
if (additionalScopeName == null) {
additionalScopeName = aliasProvider.getAdditionalScopeName();
}
return additionalScopeName;
}
@Override

View File

@ -25,64 +25,80 @@ import org.teavm.model.MethodReference;
public class MinifyingAliasProvider implements AliasProvider {
private static final String startLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String startVirtualLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private final int maxTopLevelNames;
private int lastSuffix;
private int lastVirtual;
private int lastInstanceSuffix;
private int topLevelNames;
private boolean additionalScopeStarted;
private final Set<String> usedAliases = new HashSet<>();
private final Set<String> usedVirtualAliases = new HashSet<>();
@Override
public String getFieldAlias(FieldReference field) {
String result;
do {
result = RenderingUtil.indexToId(lastVirtual++, startVirtualLetters);
} while (!usedVirtualAliases.add(result) || RenderingUtil.KEYWORDS.contains(result));
return result;
public MinifyingAliasProvider(int maxTopLevelNames) {
this.maxTopLevelNames = maxTopLevelNames;
}
@Override
public String getStaticFieldAlias(FieldReference field) {
public String getFieldAlias(FieldReference field) {
return createInstanceName();
}
@Override
public ScopedName getStaticFieldAlias(FieldReference field) {
return createTopLevelName();
}
@Override
public String getStaticMethodAlias(MethodReference method) {
public ScopedName getStaticMethodAlias(MethodReference method) {
return createTopLevelName();
}
@Override
public String getMethodAlias(MethodDescriptor method) {
String result;
do {
result = RenderingUtil.indexToId(lastVirtual++, startVirtualLetters);
} while (!usedVirtualAliases.add(result) || RenderingUtil.KEYWORDS.contains(result));
return result;
return createInstanceName();
}
@Override
public String getClassAlias(String className) {
public ScopedName getClassAlias(String className) {
return createTopLevelName();
}
@Override
public String getFunctionAlias(String className) {
return RenderingUtil.indexToId(lastSuffix++, startLetters);
public ScopedName getFunctionAlias(String className) {
return createTopLevelName();
}
@Override
public String getClassInitAlias(String className) {
public ScopedName getClassInitAlias(String className) {
return createTopLevelName();
}
@Override
public String getAdditionalScopeName() {
return createTopLevelName().name;
}
@Override
public void reserveName(String name) {
usedAliases.add(name);
}
private String createTopLevelName() {
private ScopedName createTopLevelName() {
if (!additionalScopeStarted && topLevelNames >= maxTopLevelNames) {
additionalScopeStarted = true;
lastSuffix = 0;
}
String result;
do {
result = RenderingUtil.indexToId(lastSuffix++, startLetters);
} while (!usedAliases.add(result) || RenderingUtil.KEYWORDS.contains(result));
} while ((!additionalScopeStarted && usedAliases.contains(result)) || RenderingUtil.KEYWORDS.contains(result));
++topLevelNames;
return new ScopedName(result, additionalScopeStarted);
}
private String createInstanceName() {
String result;
do {
result = RenderingUtil.indexToId(lastInstanceSuffix++, startVirtualLetters);
} while (RenderingUtil.KEYWORDS.contains(result));
return result;
}
}

View File

@ -20,21 +20,23 @@ import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
public interface NamingStrategy {
String getNameFor(String cls);
ScopedName className(String cls);
String getNameFor(MethodDescriptor method);
String instanceMethodName(MethodDescriptor method);
String getNameForInit(MethodReference method);
ScopedName initializerName(MethodReference method);
String getFullNameFor(MethodReference method);
ScopedName methodName(MethodReference method);
String getNameFor(FieldReference field);
String instanceFieldName(FieldReference field);
String getFullNameFor(FieldReference method);
ScopedName fieldName(FieldReference method);
String getNameForFunction(String name);
ScopedName functionName(String name);
String getNameForClassInit(String className);
ScopedName classInitializerName(String className);
String additionalScopeName();
void reserveName(String name);
}

View File

@ -29,6 +29,7 @@ import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
public class OutputSourceWriter extends SourceWriter implements LocationProvider {
private static final int LET_SEQUENCE_LIMIT = 50;
private final Appendable innerWriter;
private int indentSize;
private final NamingStrategy naming;
@ -45,6 +46,9 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
private int sectionMarkSection = -1;
private int sectionMarkPos;
private IntIntMap sectionSizes = new IntIntHashMap();
private DeclarationType currentDeclarationType;
private boolean expectingDeclarationName;
private int letSequenceSize;
OutputSourceWriter(NamingStrategy naming, Appendable innerWriter, int lineWidth) {
this.naming = naming;
@ -61,9 +65,31 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
this.minified = minified;
}
public void finish() {
finishLet();
}
private void finishLetImplicitly() {
if (currentDeclarationType == null) {
finishLet();
}
}
private void finishLet() {
if (letSequenceSize > 0) {
letSequenceSize = 0;
try {
innerWriter.append(";\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Override
public SourceWriter append(char value) {
appendIndent();
finishLetImplicitly();
try {
innerWriter.append(value);
} catch (IOException e) {
@ -96,6 +122,7 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
if (start == end) {
return;
}
finishLetImplicitly();
appendIndent();
column += end - start;
offset += end - start;
@ -108,32 +135,32 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
@Override
public SourceWriter appendClass(String cls) {
return appendName(naming.getNameFor(cls));
return appendDeclaration(naming.className(cls));
}
@Override
public SourceWriter appendField(FieldReference field) {
return append(naming.getNameFor(field));
return append(naming.instanceFieldName(field));
}
@Override
public SourceWriter appendStaticField(FieldReference field) {
return appendName(naming.getFullNameFor(field));
return appendDeclaration(naming.fieldName(field));
}
@Override
public SourceWriter appendMethod(MethodDescriptor method) {
return append(naming.getNameFor(method));
public SourceWriter appendVirtualMethod(MethodDescriptor method) {
return append(naming.instanceMethodName(method));
}
@Override
public SourceWriter appendMethodBody(MethodReference method) {
return appendName(naming.getFullNameFor(method));
public SourceWriter appendMethod(MethodReference method) {
return appendDeclaration(naming.methodName(method));
}
@Override
public SourceWriter appendFunction(String name) {
return append(naming.getNameForFunction(name));
return appendDeclaration(naming.functionName(name));
}
@Override
@ -143,16 +170,117 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
@Override
public SourceWriter appendInit(MethodReference method) {
return appendName(naming.getNameForInit(method));
return appendDeclaration(naming.initializerName(method));
}
@Override
public SourceWriter appendClassInit(String className) {
return appendName(naming.getNameForClassInit(className));
return appendDeclaration(naming.classInitializerName(className));
}
private SourceWriter appendName(String name) {
append(name);
@Override
public SourceWriter startVariableDeclaration() {
checkStartDeclaration();
currentDeclarationType = DeclarationType.VARIABLE;
expectingDeclarationName = true;
return this;
}
@Override
public SourceWriter startFunctionDeclaration() {
checkStartDeclaration();
currentDeclarationType = DeclarationType.FUNCTION;
expectingDeclarationName = true;
return this;
}
@Override
public SourceWriter declareVariable() {
checkStartDeclaration();
currentDeclarationType = DeclarationType.VARIABLE_WITHOUT_VALUE;
expectingDeclarationName = true;
return this;
}
private void checkStartDeclaration() {
if (currentDeclarationType != null) {
throw new IllegalStateException();
}
}
@Override
public SourceWriter endDeclaration() {
if (currentDeclarationType == null || expectingDeclarationName) {
throw new IllegalStateException();
}
switch (currentDeclarationType) {
case FUNCTION:
newLine();
break;
case VARIABLE:
if (letSequenceSize == 0) {
append(';').softNewLine();
} else if (letSequenceSize >= LET_SEQUENCE_LIMIT) {
finishLet();
}
break;
case VARIABLE_WITHOUT_VALUE:
throw new IllegalStateException();
}
currentDeclarationType = null;
return this;
}
private SourceWriter appendDeclaration(ScopedName name) {
if (!expectingDeclarationName) {
return appendName(name);
}
expectingDeclarationName = false;
switch (currentDeclarationType) {
case FUNCTION:
finishLet();
if (name.scoped) {
append(naming.additionalScopeName()).append('.').append(name.name).ws()
.append('=').ws().append("function");
} else {
append("function ").append(name.name);
}
break;
case VARIABLE:
if (name.scoped) {
finishLet();
append(naming.additionalScopeName()).append('.');
} else {
if (letSequenceSize++ == 0) {
append("let ");
} else {
append(',').softNewLine();
}
}
append(name.name).ws().append('=').ws();
break;
case VARIABLE_WITHOUT_VALUE:
if (!name.scoped) {
if (letSequenceSize++ == 0) {
append("let ");
} else {
append(',').softNewLine();
}
append(name.name);
}
expectingDeclarationName = false;
currentDeclarationType = null;
break;
}
return this;
}
private SourceWriter appendName(ScopedName name) {
if (name.scoped) {
append(naming.additionalScopeName());
append('.');
}
append(name.name);
return this;
}
@ -160,6 +288,7 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
if (minified) {
return;
}
finishLetImplicitly();
if (lineStart) {
try {
for (int i = 0; i < indentSize; ++i) {
@ -176,6 +305,7 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
@Override
public SourceWriter newLine() {
finishLetImplicitly();
try {
innerWriter.append('\n');
} catch (IOException e) {
@ -194,6 +324,7 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
newLine();
} else {
if (!minified) {
finishLetImplicitly();
try {
innerWriter.append(' ');
} catch (IOException e) {
@ -209,6 +340,7 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
@Override
public SourceWriter sameLineWs() {
if (!minified) {
finishLetImplicitly();
try {
innerWriter.append(' ');
} catch (IOException e) {
@ -231,6 +363,7 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
@Override
public SourceWriter softNewLine() {
if (!minified) {
finishLetImplicitly();
try {
innerWriter.append('\n');
} catch (IOException e) {
@ -362,4 +495,10 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
public int getSectionSize(int sectionId) {
return sectionSizes.get(sectionId);
}
private enum DeclarationType {
FUNCTION,
VARIABLE,
VARIABLE_WITHOUT_VALUE
}
}

View File

@ -88,14 +88,14 @@ public class RememberedSource implements SourceFragment {
case RememberingSourceWriter.METHOD:
if ((filter & FILTER_REF) != 0) {
sink.appendMethod(methodDescriptors[intArgs[intArgIndex]]);
sink.appendVirtualMethod(methodDescriptors[intArgs[intArgIndex]]);
}
intArgIndex++;
break;
case RememberingSourceWriter.METHOD_BODY:
if ((filter & FILTER_REF) != 0) {
sink.appendMethodBody(methods[intArgs[intArgIndex]]);
sink.appendMethod(methods[intArgs[intArgIndex]]);
}
intArgIndex++;
break;
@ -170,6 +170,27 @@ public class RememberedSource implements SourceFragment {
}
break;
case RememberingSourceWriter.START_FUNCTION:
if ((filter & FILTER_TEXT) != 0) {
sink.startFunctionDeclaration();
}
break;
case RememberingSourceWriter.START_VARIABLE:
if ((filter & FILTER_TEXT) != 0) {
sink.startVariableDeclaration();
}
break;
case RememberingSourceWriter.END_DECLARATION:
if ((filter & FILTER_TEXT) != 0) {
sink.endDeclaration();
}
break;
case RememberingSourceWriter.DECLARE_VARIABLE:
if ((filter & FILTER_TEXT) != 0) {
sink.declareVariable();
}
break;
case RememberingSourceWriter.EMIT_LOCATION:
if ((filter & FILTER_DEBUG) != 0) {
var fileIndex = intArgs[intArgIndex];

View File

@ -42,6 +42,10 @@ public class RememberingSourceWriter extends SourceWriter {
static final byte SOFT_NEW_LINE = 12;
static final byte INDENT = 13;
static final byte OUTDENT = 14;
static final byte START_FUNCTION = 27;
static final byte START_VARIABLE = 28;
static final byte END_DECLARATION = 29;
static final byte DECLARE_VARIABLE = 30;
static final byte EMIT_LOCATION = 15;
static final byte ENTER_LOCATION = 16;
static final byte EXIT_LOCATION = 17;
@ -73,6 +77,8 @@ public class RememberingSourceWriter extends SourceWriter {
private List<MethodReference> methods = new ArrayList<>();
private ObjectIntMap<MethodReference> methodIndexes = new ObjectIntHashMap<>();
private boolean isInDeclaration;
public RememberingSourceWriter(boolean debug) {
this.debug = debug;
}
@ -129,7 +135,7 @@ public class RememberingSourceWriter extends SourceWriter {
}
@Override
public SourceWriter appendMethod(MethodDescriptor method) {
public SourceWriter appendVirtualMethod(MethodDescriptor method) {
flush();
commands.add(METHOD);
appendMethodDescriptorArg(method);
@ -137,7 +143,7 @@ public class RememberingSourceWriter extends SourceWriter {
}
@Override
public SourceWriter appendMethodBody(MethodReference method) {
public SourceWriter appendMethod(MethodReference method) {
flush();
commands.add(METHOD_BODY);
appendMethodArg(method);
@ -225,6 +231,49 @@ public class RememberingSourceWriter extends SourceWriter {
return this;
}
@Override
public SourceWriter startFunctionDeclaration() {
checkNotInDeclaration();
isInDeclaration = true;
flush();
commands.add(START_FUNCTION);
return this;
}
@Override
public SourceWriter startVariableDeclaration() {
checkNotInDeclaration();
isInDeclaration = true;
flush();
commands.add(START_VARIABLE);
return this;
}
private void checkNotInDeclaration() {
if (isInDeclaration) {
throw new IllegalStateException();
}
}
@Override
public SourceWriter endDeclaration() {
if (!isInDeclaration) {
throw new IllegalStateException();
}
isInDeclaration = false;
flush();
commands.add(END_DECLARATION);
return this;
}
@Override
public SourceWriter declareVariable() {
checkNotInDeclaration();
flush();
commands.add(DECLARE_VARIABLE);
return this;
}
@Override
public SourceWriter emitLocation(String fileName, int line) {
if (debug) {

View File

@ -0,0 +1,26 @@
/*
* Copyright 2024 konsoletyper.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.javascript.codegen;
public class ScopedName {
public final String name;
public final boolean scoped;
public ScopedName(String name, boolean scoped) {
this.name = name;
this.scoped = scoped;
}
}

View File

@ -76,26 +76,38 @@ public abstract class SourceWriter implements Appendable, SourceWriterSink {
public abstract SourceWriter appendStaticField(FieldReference field);
@Override
public abstract SourceWriter appendMethod(MethodDescriptor method);
public abstract SourceWriter appendVirtualMethod(MethodDescriptor method);
public SourceWriter appendMethod(String name, Class<?>... params) {
return appendMethod(new MethodDescriptor(name, params));
public SourceWriter appendVirtualMethod(String name, Class<?>... params) {
return appendVirtualMethod(new MethodDescriptor(name, params));
}
@Override
public abstract SourceWriter appendMethodBody(MethodReference method);
public abstract SourceWriter appendMethod(MethodReference method);
public SourceWriter appendMethodBody(String className, String name, ValueType... params) {
return appendMethodBody(new MethodReference(className, new MethodDescriptor(name, params)));
public SourceWriter appendMethod(String className, String name, ValueType... params) {
return appendMethod(new MethodReference(className, new MethodDescriptor(name, params)));
}
public SourceWriter appendMethodBody(Class<?> cls, String name, Class<?>... params) {
return appendMethodBody(new MethodReference(cls, name, params));
public SourceWriter appendMethod(Class<?> cls, String name, Class<?>... params) {
return appendMethod(new MethodReference(cls, name, params));
}
@Override
public abstract SourceWriter appendFunction(String name);
@Override
public abstract SourceWriter startFunctionDeclaration();
@Override
public abstract SourceWriter startVariableDeclaration();
@Override
public abstract SourceWriter endDeclaration();
@Override
public abstract SourceWriter declareVariable();
@Override
public abstract SourceWriter appendGlobal(String name);

View File

@ -36,11 +36,27 @@ public interface SourceWriterSink {
return this;
}
default SourceWriterSink appendMethod(MethodDescriptor method) {
default SourceWriterSink appendVirtualMethod(MethodDescriptor method) {
return this;
}
default SourceWriterSink appendMethodBody(MethodReference method) {
default SourceWriterSink appendMethod(MethodReference method) {
return this;
}
default SourceWriterSink startVariableDeclaration() {
return this;
}
default SourceWriterSink startFunctionDeclaration() {
return this;
}
default SourceWriterSink endDeclaration() {
return this;
}
default SourceWriterSink declareVariable() {
return this;
}

View File

@ -98,12 +98,18 @@ public class AstWriter {
private Set<String> aliases = new HashSet<>();
private Function<String, NameEmitter> globalNameWriter;
public final Map<String, Scope> currentScopes = new HashMap<>();
protected final Set<Scope> topLevelScopes = new HashSet<>();
private boolean inFunction;
public AstWriter(SourceWriter writer, Function<String, NameEmitter> globalNameWriter) {
this.writer = writer;
this.globalNameWriter = globalNameWriter;
}
protected final boolean inFunction() {
return inFunction;
}
public void declareName(String name) {
if (nameMap.containsKey(name)) {
return;
@ -125,10 +131,6 @@ public class AstWriter {
nameMap.put(name, emitter);
}
public void hoist(Object node) {
hoist((AstNode) node);
}
public void hoist(AstNode node) {
declareName("arguments");
node.visit(n -> {
@ -162,11 +164,11 @@ public class AstWriter {
print((AstNode) node, precedence);
}
public void print(AstNode node) {
print(node, PRECEDENCE_COMMA);
public boolean print(AstNode node) {
return print(node, PRECEDENCE_COMMA);
}
public void print(AstNode node, int precedence) {
public boolean print(AstNode node, int precedence) {
switch (node.getType()) {
case Token.SCRIPT:
print((AstRoot) node);
@ -176,8 +178,7 @@ public class AstWriter {
print((FunctionCall) node, precedence);
break;
case Token.FUNCTION:
print((FunctionNode) node);
break;
return print((FunctionNode) node);
case Token.ARRAYCOMP:
print((ArrayComprehension) node);
break;
@ -287,8 +288,7 @@ public class AstWriter {
case Token.CONST:
case Token.VAR:
case Token.LET:
print((VariableDeclaration) node);
break;
return print((VariableDeclaration) node);
case Token.WHILE:
print((WhileLoop) node);
break;
@ -302,20 +302,23 @@ public class AstWriter {
}
break;
}
return false;
}
private void print(AstRoot node) {
for (Node child : node) {
print((AstNode) child);
writer.softNewLine();
if (!print((AstNode) child)) {
writer.softNewLine();
}
}
}
private void print(Block node) {
writer.append('{').softNewLine().indent();
for (Node child = node.getFirstChild(); child != null; child = child.getNext()) {
print((AstNode) child);
writer.softNewLine();
if (!print((AstNode) child)) {
writer.softNewLine();
}
}
writer.outdent().append('}');
}
@ -324,8 +327,9 @@ public class AstWriter {
var scope = enterScope(node);
writer.append('{').softNewLine().indent();
for (Node child = node.getFirstChild(); child != null; child = child.getNext()) {
print((AstNode) child);
writer.softNewLine();
if (!print((AstNode) child)) {
writer.softNewLine();
}
}
writer.outdent().append('}');
leaveScope(scope, node);
@ -456,7 +460,7 @@ public class AstWriter {
print(node.getTryBlock());
for (CatchClause cc : node.getCatchClauses()) {
writer.ws().append("catch").ws().append('(');
print(cc.getVarName());
writer.append(cc.getVarName().getIdentifier());
if (cc.getCatchCondition() != null) {
writer.append(" if ");
print(cc.getCatchCondition());
@ -470,7 +474,15 @@ public class AstWriter {
}
}
private void print(VariableDeclaration node) {
private boolean print(VariableDeclaration node) {
if (isTopLevel() && node.getVariables().get(0).getTarget() instanceof Name) {
var name = (Name) node.getVariables().get(0).getTarget();
var definingScope = scopeOfId(name.getIdentifier());
if (definingScope == null || topLevelScopes.contains(definingScope)) {
printTopLevel(node);
return true;
}
}
switch (node.getType()) {
case Token.VAR:
writer.append("var ");
@ -492,6 +504,23 @@ public class AstWriter {
if (node.isStatement()) {
writer.append(';');
}
return false;
}
private void printTopLevel(VariableDeclaration node) {
for (var variable : node.getVariables()) {
var target = variable.getTarget();
if (target instanceof Name) {
var id = ((Name) target).getIdentifier();
if (variable.getInitializer() != null) {
writer.startVariableDeclaration().appendFunction(id);
print(variable.getInitializer());
writer.endDeclaration();
} else {
writer.declareVariable().appendFunction(id);
}
}
}
}
private void print(VariableInitializer node) {
@ -575,7 +604,7 @@ public class AstWriter {
return false;
}
writer.appendMethodBody(method).append('(');
writer.appendMethod(method).append('(');
printList(node.getArguments());
writer.append(')');
return true;
@ -722,9 +751,18 @@ public class AstWriter {
print(node.getRight());
}
protected void print(FunctionNode node) {
var scope = enterScope(node);
protected boolean print(FunctionNode node) {
var isArrow = node.getFunctionType() == FunctionNode.ARROW_FUNCTION;
if (isTopLevel() && !isArrow && node.getFunctionName() != null) {
var definingScope = scopeOfId(node.getFunctionName().getIdentifier());
if (definingScope == null || topLevelScopes.contains(definingScope)) {
printTopLevel(node);
return true;
}
}
var wasInFunction = inFunction;
inFunction = true;
var scope = enterScope(node);
if (!isArrow) {
currentScopes.put("arguments", node);
}
@ -760,6 +798,23 @@ public class AstWriter {
}
leaveScope(scope, node);
inFunction = wasInFunction;
return false;
}
private void printTopLevel(FunctionNode node) {
var wasInFunction = inFunction;
inFunction = true;
var scope = enterScope(node);
currentScopes.put("arguments", node);
writer.startFunctionDeclaration().appendFunction(node.getFunctionName().getIdentifier());
writer.append('(');
printList(node.getParams());
writer.append(')').ws();
print(node.getBody());
writer.endDeclaration();
leaveScope(scope, node);
inFunction = wasInFunction;
}
private void print(LetNode node) {
@ -980,6 +1035,9 @@ public class AstWriter {
}
protected void onEnterScope(Scope scope) {
if (isTopLevel() && !inFunction()) {
topLevelScopes.add(scope);
}
}
private void leaveScope(Map<String, Scope> backup, Scope scope) {
@ -994,9 +1052,16 @@ public class AstWriter {
}
protected void onLeaveScope(Scope scope) {
if (isTopLevel() && !inFunction()) {
topLevelScopes.remove(scope);
}
}
protected Scope scopeOfId(String id) {
return currentScopes.get(id);
}
protected boolean isTopLevel() {
return false;
}
}

View File

@ -155,7 +155,7 @@ public class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
statementRenderer.setCurrentPart(0);
if (method.getModifiers().contains(ElementModifier.SYNCHRONIZED)) {
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_SYNC_METHOD);
writer.appendMethod(NameFrequencyEstimator.MONITOR_ENTER_SYNC_METHOD);
writer.append("(");
appendMonitor(statementRenderer, method);
writer.append(");").softNewLine();
@ -168,7 +168,7 @@ public class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
if (method.getModifiers().contains(ElementModifier.SYNCHRONIZED)) {
writer.outdent().append("}").ws().append("finally").ws().append("{").indent().softNewLine();
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_SYNC_METHOD);
writer.appendMethod(NameFrequencyEstimator.MONITOR_EXIT_SYNC_METHOD);
writer.append("(");
appendMonitor(statementRenderer, method);
writer.append(");").softNewLine();
@ -242,7 +242,7 @@ public class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
for (int i = 0; i < methodNode.getBody().size(); ++i) {
writer.append("case ").append(i).append(":").indent().softNewLine();
if (i == 0 && methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) {
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_METHOD);
writer.appendMethod(NameFrequencyEstimator.MONITOR_ENTER_METHOD);
writer.append("(");
appendMonitor(statementRenderer, methodNode);
writer.append(");").softNewLine();
@ -260,7 +260,7 @@ public class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
writer.outdent().append("}").ws().append("finally").ws().append('{').indent().softNewLine();
writer.append("if").ws().append("(!").appendFunction("$rt_suspending").append("())")
.ws().append("{").indent().softNewLine();
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_METHOD).append("(");
writer.appendMethod(NameFrequencyEstimator.MONITOR_EXIT_METHOD).append("(");
appendMonitor(statementRenderer, methodNode);
writer.append(");").softNewLine();
writer.outdent().append('}').softNewLine();

View File

@ -38,6 +38,11 @@ public class NameFrequencyEstimator implements SourceWriterSink {
private Map<String, Entry> entries = new HashMap<>();
private Set<String> reservedNames = new HashSet<>();
private boolean hasAdditionalScope;
public boolean hasAdditionalScope() {
return hasAdditionalScope;
}
@Override
public SourceWriterSink appendClass(String cls) {
@ -45,7 +50,7 @@ public class NameFrequencyEstimator implements SourceWriterSink {
var entry = entries.get(key);
if (entry == null) {
entry = new Entry();
entry.operation = naming -> naming.getNameFor(cls);
entry.operation = naming -> naming.className(cls).scoped;
entries.put(key, entry);
}
entry.frequency++;
@ -58,7 +63,10 @@ public class NameFrequencyEstimator implements SourceWriterSink {
var entry = entries.get(key);
if (entry == null) {
entry = new Entry();
entry.operation = naming -> naming.getNameFor(field);
entry.operation = naming -> {
naming.instanceFieldName(field);
return false;
};
entries.put(key, entry);
}
entry.frequency++;
@ -71,7 +79,7 @@ public class NameFrequencyEstimator implements SourceWriterSink {
var entry = entries.get(key);
if (entry == null) {
entry = new Entry();
entry.operation = naming -> naming.getFullNameFor(field);
entry.operation = naming -> naming.fieldName(field).scoped;
entries.put(key, entry);
}
entry.frequency++;
@ -79,12 +87,15 @@ public class NameFrequencyEstimator implements SourceWriterSink {
}
@Override
public SourceWriterSink appendMethod(MethodDescriptor method) {
public SourceWriterSink appendVirtualMethod(MethodDescriptor method) {
var key = "r:" + method;
var entry = entries.get(key);
if (entry == null) {
entry = new Entry();
entry.operation = naming -> naming.getNameFor(method);
entry.operation = naming -> {
naming.instanceMethodName(method);
return false;
};
entries.put(key, entry);
}
entry.frequency++;
@ -92,12 +103,12 @@ public class NameFrequencyEstimator implements SourceWriterSink {
}
@Override
public SourceWriterSink appendMethodBody(MethodReference method) {
public SourceWriterSink appendMethod(MethodReference method) {
var key = "R:" + method;
var entry = entries.get(key);
if (entry == null) {
entry = new Entry();
entry.operation = naming -> naming.getFullNameFor(method);
entry.operation = naming -> naming.methodName(method).scoped;
entries.put(key, entry);
}
entry.frequency++;
@ -110,7 +121,7 @@ public class NameFrequencyEstimator implements SourceWriterSink {
var entry = entries.get(key);
if (entry == null) {
entry = new Entry();
entry.operation = naming -> naming.getNameForFunction(name);
entry.operation = naming -> naming.functionName(name).scoped;
entries.put(key, entry);
}
entry.frequency++;
@ -129,7 +140,7 @@ public class NameFrequencyEstimator implements SourceWriterSink {
var entry = entries.get(key);
if (entry == null) {
entry = new Entry();
entry.operation = naming -> naming.getNameForInit(method);
entry.operation = naming -> naming.initializerName(method).scoped;
entries.put(key, entry);
}
entry.frequency++;
@ -142,7 +153,7 @@ public class NameFrequencyEstimator implements SourceWriterSink {
var entry = entries.get(key);
if (entry == null) {
entry = new Entry();
entry.operation = naming -> naming.getNameForClassInit(className);
entry.operation = naming -> naming.classInitializerName(className).scoped;
entries.put(key, entry);
}
entry.frequency++;
@ -156,7 +167,7 @@ public class NameFrequencyEstimator implements SourceWriterSink {
var entryList = new ArrayList<>(entries.values());
entryList.sort((o1, o2) -> Integer.compare(o2.frequency, o1.frequency));
for (var entry : entryList) {
entry.operation.perform(naming);
hasAdditionalScope |= entry.operation.perform(naming);
}
}
@ -166,6 +177,6 @@ public class NameFrequencyEstimator implements SourceWriterSink {
}
private interface NamingOperation {
void perform(NamingStrategy naming);
boolean perform(NamingStrategy naming);
}
}

View File

@ -188,7 +188,7 @@ public class Renderer implements RenderingManager {
writer.appendClass("java.lang.Object").append(".prototype.toString").ws().append("=").ws()
.append("function()").ws().append("{").indent().softNewLine();
writer.append("return ").appendFunction("$rt_ustr").append("(")
.appendMethodBody(Object.class, "toString", String.class).append("(this));")
.appendMethod(Object.class, "toString", String.class).append("(this));")
.softNewLine();
writer.outdent().append("};").newLine();
}
@ -265,7 +265,6 @@ public class Renderer implements RenderingManager {
renderFullClassFunctionDeclaration(cls, nonStaticFields);
}
var hasLet = false;
for (FieldHolder field : staticFields) {
Object value = field.getInitialValue();
if (value == null) {
@ -278,23 +277,16 @@ public class Renderer implements RenderingManager {
value = null;
}
if (!hasLet) {
writer.append("let ");
hasLet = true;
} else {
writer.append(",").ws();
}
writer.appendStaticField(fieldRef).ws().append("=").ws();
writer.startVariableDeclaration().appendStaticField(fieldRef);
context.constantToString(writer, value);
}
if (hasLet) {
writer.append(";").newLine();
writer.endDeclaration();
}
}
private void renderFullClassFunctionDeclaration(ClassReader cls, List<FieldHolder> nonStaticFields) {
boolean thisAliased = false;
writer.append("function ").appendClass(cls.getName()).append("()").ws().append("{").indent().softNewLine();
writer.startFunctionDeclaration().appendClass(cls.getName()).append("()").ws().append("{")
.indent().softNewLine();
if (nonStaticFields.size() > 1) {
thisAliased = true;
writer.append("let a").ws().append("=").ws().append("this;").ws();
@ -321,18 +313,18 @@ public class Renderer implements RenderingManager {
}
writer.outdent().append("}");
writer.newLine();
writer.endDeclaration();
}
private void renderShortClassFunctionDeclaration(ClassReader cls) {
writer.append("let ").appendClass(cls.getName()).ws().append("=").ws()
writer.startVariableDeclaration().appendClass(cls.getName())
.appendFunction("$rt_classWithoutFields").append("(");
if (cls.hasModifier(ElementModifier.INTERFACE)) {
writer.append("0");
} else if (!cls.getParent().equals("java.lang.Object")) {
writer.appendClass(cls.getParent());
}
writer.append(");").newLine();
writer.append(")").endDeclaration();
}
private void renderMethodBodies(ClassHolder cls, Decompiler decompiler) {
@ -346,27 +338,19 @@ public class Renderer implements RenderingManager {
var needsInitializers = !cls.hasModifier(ElementModifier.INTERFACE)
&& !cls.hasModifier(ElementModifier.ABSTRACT);
var hasLet = false;
for (var method : cls.getMethods()) {
if (!filterMethod(method)) {
continue;
}
if (!hasLet) {
writer.append("let ");
hasLet = true;
} else {
writer.append(",").newLine();
}
writer.startVariableDeclaration();
renderBody(method, decompiler);
writer.endDeclaration();
if (needsInitializers && !method.hasModifier(ElementModifier.STATIC)
&& method.getName().equals("<init>")) {
writer.append(",").newLine();
renderInitializer(method);
}
}
if (hasLet) {
writer.append(";").newLine();
}
writer.emitClass(null);
}
@ -390,11 +374,10 @@ public class Renderer implements RenderingManager {
var clinitCalledField = new FieldReference(cls.getName(), "$_teavm_clinitCalled_$");
if (isAsync) {
writer.append("let ").appendStaticField(clinitCalledField).ws().append("=").ws().append("false;")
.softNewLine();
writer.startVariableDeclaration().appendStaticField(clinitCalledField).append("false").endDeclaration();
}
writer.append("let ").appendClassInit(cls.getName()).ws().append("=").ws();
writer.startVariableDeclaration().appendClassInit(cls.getName());
writer.append("()").sameLineWs().append("=>").ws().append("{").softNewLine().indent();
if (isAsync) {
@ -423,7 +406,7 @@ public class Renderer implements RenderingManager {
writer.outdent().append("case 1:").indent().softNewLine();
}
writer.appendMethodBody(new MethodReference(cls.getName(), clinit.getDescriptor()))
writer.appendMethod(new MethodReference(cls.getName(), clinit.getDescriptor()))
.append("();").softNewLine();
if (isAsync) {
@ -438,8 +421,7 @@ public class Renderer implements RenderingManager {
writer.appendFunction("$rt_nativeThread").append("().push(" + context.pointerName() + ");").softNewLine();
}
writer.outdent().append("};");
writer.newLine();
writer.outdent().append("}").endDeclaration();
}
private void renderEraseClinit(ClassReader cls) {
@ -696,7 +678,7 @@ public class Renderer implements RenderingManager {
private void renderInitializer(MethodReader method) {
MethodReference ref = method.getReference();
writer.emitMethod(ref.getDescriptor());
writer.appendInit(ref).ws().append("=").ws();
writer.startVariableDeclaration().appendInit(ref);
if (ref.parameterCount() != 1) {
writer.append("(");
}
@ -714,14 +696,14 @@ public class Renderer implements RenderingManager {
String instanceName = variableNameForInitializer(ref.parameterCount());
writer.append("let " + instanceName).ws().append("=").ws().append("new ").appendClass(
ref.getClassName()).append("();").softNewLine();
writer.appendMethodBody(ref).append("(" + instanceName);
writer.appendMethod(ref).append("(" + instanceName);
for (int i = 0; i < ref.parameterCount(); ++i) {
writer.append(",").ws();
writer.append(variableNameForInitializer(i));
}
writer.append(");").softNewLine();
writer.append("return " + instanceName + ";").softNewLine();
writer.outdent().append("}");
writer.outdent().append("}").endDeclaration();
writer.emitMethod(null);
}
@ -753,7 +735,7 @@ public class Renderer implements RenderingManager {
}
private void emitVirtualDeclaration(MethodReference ref) {
String methodName = context.getNaming().getNameFor(ref.getDescriptor());
String methodName = context.getNaming().instanceMethodName(ref.getDescriptor());
writer.append("\"").append(methodName).append("\"");
writer.append(",").ws();
emitVirtualFunctionWrapper(ref);
@ -762,7 +744,7 @@ public class Renderer implements RenderingManager {
private void emitVirtualFunctionWrapper(MethodReference method) {
if (method.parameterCount() <= 4) {
writer.appendFunction("$rt_wrapFunction" + method.parameterCount());
writer.append("(").appendMethodBody(method).append(")");
writer.append("(").appendMethod(method).append(")");
return;
}
@ -781,7 +763,7 @@ public class Renderer implements RenderingManager {
if (method.getDescriptor().getResultType() != ValueType.VOID) {
writer.append("return ");
}
writer.appendMethodBody(method).append("(");
writer.appendMethod(method).append("(");
writer.append("this");
for (String arg : args) {
writer.append(",").ws().append(arg);
@ -793,7 +775,7 @@ public class Renderer implements RenderingManager {
MethodReference ref = method.getReference();
writer.emitMethod(ref.getDescriptor());
writer.appendMethodBody(ref).ws().append("=").ws();
writer.appendMethod(ref);
if (method.hasModifier(ElementModifier.NATIVE)) {
renderNativeBody(method, classSource);
} else {

View File

@ -28,7 +28,6 @@ import org.mozilla.javascript.ast.AstRoot;
import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.backend.javascript.codegen.SourceWriterSink;
import org.teavm.backend.javascript.templating.AstRemoval;
import org.teavm.backend.javascript.templating.LetJoiner;
import org.teavm.backend.javascript.templating.RemovablePartsFinder;
import org.teavm.backend.javascript.templating.TemplatingAstTransformer;
import org.teavm.backend.javascript.templating.TemplatingAstWriter;
@ -69,13 +68,13 @@ public class RuntimeRenderer {
public void renderRuntime() {
for (var ast : runtimeAstParts) {
renderHandWrittenRuntime(ast);
renderRuntimePart(ast);
}
}
public void renderEpilogue() {
for (var ast : epilogueAstParts) {
renderHandWrittenRuntime(ast);
renderRuntimePart(ast);
}
}
@ -87,7 +86,7 @@ public class RuntimeRenderer {
return ast;
}
private void renderHandWrittenRuntime(AstRoot ast) {
private void renderRuntimePart(AstRoot ast) {
var astWriter = new TemplatingAstWriter(writer, null, null, classInitializerInfo);
astWriter.hoist(ast);
astWriter.print(ast);
@ -118,14 +117,11 @@ public class RuntimeRenderer {
public void removeUnusedParts() {
var removal = new AstRemoval(removablePartsFinder.getAllRemovableParts());
var letJoiner = new LetJoiner();
for (var part : runtimeAstParts) {
removal.visit(part);
letJoiner.visit(part);
}
for (var part : epilogueAstParts) {
removal.visit(part);
letJoiner.visit(part);
}
}
}

View File

@ -1100,10 +1100,10 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
expr.getArguments().get(0).acceptVisitor(this);
}
MethodReference method = expr.getMethod();
String name = naming.getNameFor(method.getDescriptor());
String name = naming.instanceMethodName(method.getDescriptor());
switch (expr.getType()) {
case STATIC:
writer.appendMethodBody(method).append("(");
writer.appendMethod(method).append("(");
for (int i = 0; i < expr.getArguments().size(); ++i) {
if (i > 0) {
writer.append(",").ws();
@ -1113,7 +1113,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
}
break;
case SPECIAL:
writer.appendMethodBody(method).append("(");
writer.appendMethod(method).append("(");
precedence = Precedence.min();
expr.getArguments().get(0).acceptVisitor(this);
for (int i = 1; i < expr.getArguments().size(); ++i) {
@ -1507,13 +1507,13 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
@Override
public void visit(MonitorEnterStatement statement) {
if (async) {
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_METHOD).append("(");
writer.appendMethod(NameFrequencyEstimator.MONITOR_ENTER_METHOD).append("(");
precedence = Precedence.min();
statement.getObjectRef().acceptVisitor(this);
writer.append(");").softNewLine();
emitSuspendChecker();
} else {
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_SYNC_METHOD).append('(');
writer.appendMethod(NameFrequencyEstimator.MONITOR_ENTER_SYNC_METHOD).append('(');
precedence = Precedence.min();
statement.getObjectRef().acceptVisitor(this);
writer.append(");").softNewLine();
@ -1530,12 +1530,12 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
@Override
public void visit(MonitorExitStatement statement) {
if (async) {
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_METHOD).append("(");
writer.appendMethod(NameFrequencyEstimator.MONITOR_EXIT_METHOD).append("(");
precedence = Precedence.min();
statement.getObjectRef().acceptVisitor(this);
writer.append(");").softNewLine();
} else {
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_SYNC_METHOD).append('(');
writer.appendMethod(NameFrequencyEstimator.MONITOR_EXIT_SYNC_METHOD).append('(');
precedence = Precedence.min();
statement.getObjectRef().acceptVisitor(this);
writer.append(");").softNewLine();

View File

@ -1,70 +0,0 @@
/*
* Copyright 2023 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.javascript.templating;
import org.mozilla.javascript.ast.AstNode;
import org.mozilla.javascript.ast.AstRoot;
import org.mozilla.javascript.ast.Block;
import org.mozilla.javascript.ast.FunctionNode;
import org.mozilla.javascript.ast.Scope;
import org.mozilla.javascript.ast.VariableDeclaration;
import org.teavm.backend.javascript.ast.AstVisitor;
public class LetJoiner extends AstVisitor {
@Override
public void visit(Block node) {
visitMany(node);
super.visit(node);
}
@Override
public void visit(Scope node) {
visitMany(node);
super.visit(node);
}
@Override
public void visit(AstRoot node) {
visitMany(node);
super.visit(node);
}
@Override
public void visit(FunctionNode node) {
visitMany(node.getBody());
super.visit(node);
}
private void visitMany(AstNode node) {
VariableDeclaration previous = null;
for (var childNode = node.getFirstChild(); childNode != null;) {
var nextNode = childNode.getNext();
var child = (AstNode) childNode;
if (child instanceof VariableDeclaration) {
var decl = (VariableDeclaration) childNode;
if (previous != null && previous.getType() == decl.getType()) {
previous.getVariables().addAll(decl.getVariables());
node.removeChild(decl);
} else {
previous = decl;
}
} else {
previous = null;
}
childNode = nextNode;
}
}
}

View File

@ -16,9 +16,7 @@
package org.teavm.backend.javascript.templating;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.mozilla.javascript.ast.ElementGet;
import org.mozilla.javascript.ast.FunctionCall;
import org.mozilla.javascript.ast.FunctionNode;
@ -39,8 +37,6 @@ public class TemplatingAstWriter extends AstWriter {
private Scope scope;
private Map<String, SourceFragment> fragments = new HashMap<>();
private ClassInitializerInfo classInitializerInfo;
private Set<Scope> topLevelScopes = new HashSet<>();
private boolean inFunction;
public TemplatingAstWriter(SourceWriter writer, Map<String, SourceFragment> names, Scope scope,
ClassInitializerInfo classInitializerInfo) {
@ -113,7 +109,7 @@ public class TemplatingAstWriter extends AstWriter {
}
var method = new MethodReference(((StringLiteral) classArg).getValue(),
MethodDescriptor.parse(((StringLiteral) methodArg).getValue()));
writer.appendMethodBody(method);
writer.appendMethod(method);
return true;
}
@ -210,7 +206,7 @@ public class TemplatingAstWriter extends AstWriter {
}
var method = MethodDescriptor.parse(((StringLiteral) arg).getValue());
print(get.getTarget());
writer.append('.').appendMethod(method);
writer.append('.').appendVirtualMethod(method);
return true;
}
@ -250,27 +246,7 @@ public class TemplatingAstWriter extends AstWriter {
}
@Override
protected void print(FunctionNode node) {
if (inFunction) {
super.print(node);
} else {
inFunction = true;
super.print(node);
inFunction = false;
}
}
@Override
protected void onEnterScope(Scope scope) {
if (names == null && !inFunction) {
topLevelScopes.add(scope);
}
}
@Override
protected void onLeaveScope(Scope scope) {
if (names == null && !inFunction) {
topLevelScopes.remove(scope);
}
protected boolean isTopLevel() {
return names == null;
}
}

View File

@ -50,8 +50,8 @@ class JSAliasRenderer implements RendererListener, VirtualMethodContributor {
return;
}
writer.append("let ").appendFunction("$rt_jso_marker").ws().append("=").ws()
.appendGlobal("Symbol").append("('jsoClass');").newLine();
writer.startVariableDeclaration().appendFunction("$rt_jso_marker")
.appendGlobal("Symbol").append("('jsoClass')").endDeclaration();
writer.append("(function()").ws().append("{").softNewLine().indent();
writer.append("var c;").softNewLine();
for (String className : classSource.getClassNames()) {
@ -100,7 +100,7 @@ class JSAliasRenderer implements RendererListener, VirtualMethodContributor {
} else {
writer.append("c.").append(aliasEntry.getKey());
}
writer.ws().append("=").ws().append("c.").appendMethod(aliasEntry.getValue())
writer.ws().append("=").ws().append("c.").appendVirtualMethod(aliasEntry.getValue())
.append(";").softNewLine();
}
for (var aliasEntry : properties.entrySet()) {
@ -111,10 +111,10 @@ class JSAliasRenderer implements RendererListener, VirtualMethodContributor {
writer.append("Object.defineProperty(c,")
.ws().append("\"").append(aliasEntry.getKey()).append("\",")
.ws().append("{").indent().softNewLine();
writer.append("get:").ws().append("c.").appendMethod(propInfo.getter);
writer.append("get:").ws().append("c.").appendVirtualMethod(propInfo.getter);
if (propInfo.setter != null && classReader.getMethod(propInfo.setter) != null) {
writer.append(",").softNewLine();
writer.append("set:").ws().append("c.").appendMethod(propInfo.setter);
writer.append("set:").ws().append("c.").appendVirtualMethod(propInfo.setter);
}
writer.softNewLine().outdent().append("});").softNewLine();
}
@ -198,7 +198,7 @@ class JSAliasRenderer implements RendererListener, VirtualMethodContributor {
writer.append("this.").appendField(functorField).ws().append('=').ws().append("function(");
appendArguments(functorMethod.parameterCount());
writer.append(")").ws().append('{').indent().softNewLine();
writer.append("return self.").appendMethod(functorMethod).append('(');
writer.append("return self.").appendVirtualMethod(functorMethod).append('(');
appendArguments(functorMethod.parameterCount());
writer.append(");").softNewLine();
writer.outdent().append("};").softNewLine();

View File

@ -69,8 +69,8 @@ class JSBodyBloatedEmitter implements JSBodyEmitter {
private void emit(SourceWriter writer, EmissionStrategy strategy) {
int bodyParamCount = isStatic ? method.parameterCount() : method.parameterCount() - 1;
writer.append("if (!").appendMethodBody(method).append(".$native)").ws().append('{').indent().newLine();
writer.appendMethodBody(method).append(".$native").ws().append('=').ws().append("function(");
writer.append("if (!").appendMethod(method).append(".$native)").ws().append('{').indent().newLine();
writer.appendMethod(method).append(".$native").ws().append('=').ws().append("function(");
int count = method.parameterCount();
var first = true;
@ -135,11 +135,11 @@ class JSBodyBloatedEmitter implements JSBodyEmitter {
writer.append(");").softNewLine();
writer.outdent().append("};").softNewLine();
writer.appendMethodBody(method).ws().append('=').ws().appendMethodBody(method).append(".$native;")
writer.appendMethod(method).ws().append('=').ws().appendMethod(method).append(".$native;")
.softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return ").appendMethodBody(method).append('(');
writer.append("return ").appendMethod(method).append('(');
for (int i = 0; i < count; ++i) {
if (i > 0) {
writer.append(',').ws();

View File

@ -49,7 +49,7 @@ public class AsyncMethodGenerator implements Generator, DependencyPlugin, Virtua
template.builder("asyncMethod")
.withContext(context)
.withFragment("callMethod", (w, p) -> {
w.appendMethodBody(asyncRef).append('(');
w.appendMethod(asyncRef).append('(');
ClassReader cls = context.getClassSource().get(methodRef.getClassName());
MethodReader method = cls.getMethod(methodRef.getDescriptor());
int start = method.hasModifier(ElementModifier.STATIC) ? 1 : 0;

View File

@ -132,7 +132,7 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
MethodReader method = cls.getMethod(new MethodDescriptor("<init>", void.class));
if (method != null) {
writer.appendClass(clsName).append("[c]").ws().append("=").ws()
.appendMethodBody(method.getReference()).append(";").softNewLine();
.appendMethod(method.getReference()).append(";").softNewLine();
}
}
}
@ -159,14 +159,14 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
ValueType.arrayOf(ValueType.object(clsName))));
if (method != null) {
writer.appendClass(clsName).append("[c]").ws().append("=").ws();
writer.appendMethodBody(method.getReference());
writer.appendMethod(method.getReference());
writer.append(";").softNewLine();
}
}
MethodReference selfRef = new MethodReference(Platform.class, "getEnumConstants",
PlatformClass.class, Enum[].class);
writer.appendMethodBody(selfRef).ws().append("=").ws().append("cls").sameLineWs().append("=>").ws()
writer.appendMethod(selfRef).ws().append("=").ws().append("cls").sameLineWs().append("=>").ws()
.append("{").softNewLine().indent();
writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine();
writer.append("return null;").softNewLine();
@ -178,7 +178,7 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
writer.append("return cls[c];").softNewLine();
writer.outdent().append("};").softNewLine();
writer.append("return ").appendMethodBody(selfRef).append("(").append(context.getParameterName(1))
writer.append("return ").appendMethod(selfRef).append("(").append(context.getParameterName(1))
.append(");").softNewLine();
}
@ -196,15 +196,16 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
MethodReference selfRef = new MethodReference(Platform.class, "getAnnotations", PlatformClass.class,
Annotation[].class);
writer.appendMethodBody(selfRef).ws().append("=").ws().append("cls").sameLineWs().append("=>").ws()
writer.appendMethod(selfRef).ws().append("=").ws().append("cls").sameLineWs().append("=>").ws()
.append("{").softNewLine().indent();
writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine();
writer.append("return null;").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return cls[c].").appendMethod("getAnnotations", Annotation[].class).append("();").softNewLine();
writer.append("return cls[c].").appendVirtualMethod("getAnnotations", Annotation[].class).append("();")
.softNewLine();
writer.outdent().append("};").softNewLine();
writer.append("return ").appendMethodBody(selfRef).append("(").append(context.getParameterName(1))
writer.append("return ").appendMethod(selfRef).append("(").append(context.getParameterName(1))
.append(");").softNewLine();
}
}

View File

@ -241,6 +241,15 @@ public final class TeaVMRunner {
tool.setObfuscated(commandLine.hasOption("m"));
tool.setStrict(commandLine.hasOption("strict"));
parseJsModuleOption();
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();
}
}
}
private void parseJsModuleOption() {

View File

@ -78,6 +78,7 @@ public class TeaVMTool {
private boolean obfuscated = true;
private JSModuleType jsModuleType = JSModuleType.UMD;
private boolean strict;
private int maxTopLevelNames = 80_000;
private String mainClass;
private String entryPointName = "main";
private Properties properties = new Properties();
@ -139,6 +140,10 @@ public class TeaVMTool {
this.strict = strict;
}
public void setMaxTopLevelNames(int maxTopLevelNames) {
this.maxTopLevelNames = maxTopLevelNames;
}
public boolean isIncremental() {
return incremental;
}
@ -336,6 +341,7 @@ public class TeaVMTool {
javaScriptTarget = new JavaScriptTarget();
javaScriptTarget.setObfuscated(obfuscated);
javaScriptTarget.setStrict(strict);
javaScriptTarget.setMaxTopLevelNames(maxTopLevelNames);
debugEmitter = debugInformationGenerated || sourceMapsFileGenerated
? new DebugInformationBuilder(referenceCache) : null;

View File

@ -63,6 +63,8 @@ public interface BuildStrategy {
void setJsModuleType(JSModuleType jsModuleType);
void setMaxTopLevelNames(int maxTopLevelNames);
void setProperties(Properties properties);
void setTransformers(String[] transformers);

View File

@ -56,6 +56,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
private boolean obfuscated;
private JSModuleType jsModuleType;
private boolean strict;
private int maxTopLevelNames = 80_000;
private boolean sourceMapsFileGenerated;
private boolean debugInformationGenerated;
private TeaVMSourceFilePolicy sourceMapsSourcePolicy;
@ -174,6 +175,11 @@ public class InProcessBuildStrategy implements BuildStrategy {
this.jsModuleType = jsModuleType;
}
@Override
public void setMaxTopLevelNames(int maxTopLevelNames) {
this.maxTopLevelNames = maxTopLevelNames;
}
@Override
public void setTransformers(String[] transformers) {
this.transformers = transformers.clone();
@ -256,6 +262,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
tool.setObfuscated(obfuscated);
tool.setJsModuleType(jsModuleType);
tool.setStrict(strict);
tool.setMaxTopLevelNames(maxTopLevelNames);
tool.setIncremental(incremental);
tool.getTransformers().addAll(Arrays.asList(transformers));
tool.getClassesToPreserve().addAll(Arrays.asList(classesToPreserve));

View File

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

View File

@ -44,6 +44,7 @@ public class RemoteBuildRequest implements Serializable {
public boolean obfuscated;
public boolean strict;
public JSModuleType jsModuleType;
public int maxTopLevelNames = 80_000;
public Properties properties;
public TeaVMOptimizationLevel optimizationLevel;
public boolean fastDependencyAnalysis;

View File

@ -123,6 +123,7 @@ public class TeaVMPlugin implements Plugin<Project> {
task.getStrict().convention(js.getStrict());
task.getEntryPointName().convention(js.getEntryPointName());
task.getSourceFilePolicy().convention(js.getSourceFilePolicy());
task.getMaxTopLevelNames().convention(js.getMaxTopLevelNames());
task.getSourceFiles().from(project.provider(() -> {
var result = new ArrayList<File>();

View File

@ -31,4 +31,6 @@ public interface TeaVMJSConfiguration extends TeaVMWebConfiguration {
Property<String> getTargetFileName();
Property<SourceFilePolicy> getSourceFilePolicy();
Property<Integer> getMaxTopLevelNames();
}

View File

@ -63,11 +63,18 @@ public abstract class GenerateJavaScriptTask extends TeaVMTask {
@Optional
public abstract Property<SourceFilePolicy> getSourceFilePolicy();
@Input
@Optional
public abstract Property<Integer> getMaxTopLevelNames();
@Override
protected void setupBuilder(BuildStrategy builder) {
builder.setTargetType(TeaVMTargetType.JAVASCRIPT);
builder.setObfuscated(getObfuscated().get());
builder.setStrict(getStrict().get());
if (getMaxTopLevelNames().isPresent()) {
builder.setMaxTopLevelNames(getMaxTopLevelNames().get());
}
switch (getModuleType().get()) {
case UMD:
builder.setJsModuleType(org.teavm.backend.javascript.JSModuleType.UMD);

View File

@ -65,7 +65,7 @@ class TestExceptionPlugin implements TeaVMPlugin {
writer.appendClass("java.lang.Throwable").append(".prototype.getMessage").ws().append("=").ws()
.append("function()").ws().append("{").indent().softNewLine();
writer.append("return ").appendFunction("$rt_ustr").append("(this.")
.appendMethod("getMessage", String.class).append("());").softNewLine();
.appendVirtualMethod("getMessage", String.class).append("());").softNewLine();
writer.outdent().append("};").newLine();
}
}

View File

@ -87,6 +87,9 @@ public class TeaVMCompileMojo extends AbstractMojo {
@Parameter(property = "teavm.jsModuleType", defaultValue = "UMD")
private JSModuleType jsModuleType;
@Parameter(property = "teavm.maxTopLevelNames", defaultValue = "80000")
private int maxTopLevelNames = 80_000;
@Parameter
private Properties properties;
@ -169,6 +172,7 @@ public class TeaVMCompileMojo extends AbstractMojo {
builder.setObfuscated(minifying);
builder.setStrict(strict);
builder.setJsModuleType(jsModuleType);
builder.setMaxTopLevelNames(maxTopLevelNames);
builder.setTargetDirectory(targetDirectory.getAbsolutePath());
if (transformers != null) {
builder.setTransformers(transformers);