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