mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -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))
|
writer.append("return ").appendFunction(name).append("(").append(context.getParameterName(1))
|
||||||
.append(",").ws()
|
.append(",").ws()
|
||||||
.append(context.getParameterName(2)).append(");").softNewLine();
|
.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) {
|
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) {
|
for (int i = 0; i < paramCount; ++i) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
writer.append(",").ws();
|
writer.append(",").ws();
|
||||||
|
|
|
@ -42,6 +42,7 @@ import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import org.teavm.ast.AsyncMethodNode;
|
import org.teavm.ast.AsyncMethodNode;
|
||||||
import org.teavm.ast.ControlFlowEntry;
|
import org.teavm.ast.ControlFlowEntry;
|
||||||
|
import org.teavm.ast.MethodNode;
|
||||||
import org.teavm.ast.RegularMethodNode;
|
import org.teavm.ast.RegularMethodNode;
|
||||||
import org.teavm.ast.analysis.LocationGraphBuilder;
|
import org.teavm.ast.analysis.LocationGraphBuilder;
|
||||||
import org.teavm.ast.decompilation.DecompilationException;
|
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.WeakReferenceDependencyListener;
|
||||||
import org.teavm.backend.javascript.intrinsics.ref.WeakReferenceGenerator;
|
import org.teavm.backend.javascript.intrinsics.ref.WeakReferenceGenerator;
|
||||||
import org.teavm.backend.javascript.intrinsics.ref.WeakReferenceTransformer;
|
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.Renderer;
|
||||||
import org.teavm.backend.javascript.rendering.RenderingContext;
|
import org.teavm.backend.javascript.rendering.RenderingContext;
|
||||||
import org.teavm.backend.javascript.rendering.RenderingUtil;
|
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.Generator;
|
||||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
import org.teavm.backend.javascript.spi.Injector;
|
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.VirtualMethodContributor;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
||||||
import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory;
|
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.ElementModifier;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.ListableClassHolderSource;
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
import org.teavm.model.ListableClassReaderSource;
|
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
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 Map<MethodReference, Injector> methodInjectors = new HashMap<>();
|
||||||
private final List<Function<ProviderContext, Generator>> generatorProviders = new ArrayList<>();
|
private final List<Function<ProviderContext, Generator>> generatorProviders = new ArrayList<>();
|
||||||
private final List<Function<ProviderContext, Injector>> injectorProviders = 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 final List<RendererListener> rendererListeners = new ArrayList<>();
|
||||||
private DebugInformationEmitter debugEmitter;
|
private DebugInformationEmitter debugEmitter;
|
||||||
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
|
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
|
||||||
|
@ -153,6 +151,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
private final Map<String, String> importedModules = new LinkedHashMap<>();
|
private final Map<String, String> importedModules = new LinkedHashMap<>();
|
||||||
private Map<String, Generator> generatorCache = new HashMap<>();
|
private Map<String, Generator> generatorCache = new HashMap<>();
|
||||||
private JavaScriptTemplateFactory templateFactory;
|
private JavaScriptTemplateFactory templateFactory;
|
||||||
|
private boolean threadLibraryUsed;
|
||||||
|
private MethodBodyRenderer bodyRenderer;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ClassHolderTransformer> getTransformers() {
|
public List<ClassHolderTransformer> getTransformers() {
|
||||||
|
@ -210,11 +210,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
injectorProviders.add(provider);
|
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.
|
* 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) {
|
private void emit(ListableClassHolderSource classes, Writer writer, BuildTarget target) {
|
||||||
List<PreparedClass> clsNodes = modelToAst(classes);
|
|
||||||
prepareModules(classes);
|
|
||||||
if (controller.wasCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AliasProvider aliasProvider = obfuscated
|
AliasProvider aliasProvider = obfuscated
|
||||||
? new MinifyingAliasProvider(topLevelNameLimit)
|
? new MinifyingAliasProvider(topLevelNameLimit)
|
||||||
: new DefaultAliasProvider(topLevelNameLimit);
|
: new DefaultAliasProvider(topLevelNameLimit);
|
||||||
DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, controller.getUnprocessedClassSource());
|
DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, controller.getUnprocessedClassSource());
|
||||||
OutputSourceWriterBuilder builder = new OutputSourceWriterBuilder(naming);
|
|
||||||
builder.setMinified(obfuscated);
|
|
||||||
var sourceWriter = builder.build(writer);
|
|
||||||
|
|
||||||
DebugInformationEmitter debugEmitterToUse = debugEmitter;
|
DebugInformationEmitter debugEmitterToUse = debugEmitter;
|
||||||
if (debugEmitterToUse == null) {
|
if (debugEmitterToUse == null) {
|
||||||
debugEmitterToUse = new DummyDebugInformationEmitter();
|
debugEmitterToUse = new DummyDebugInformationEmitter();
|
||||||
}
|
}
|
||||||
sourceWriter.setDebugInformationEmitter(debugEmitterToUse);
|
|
||||||
|
|
||||||
var virtualMethodContributorContext = new VirtualMethodContributorContextImpl(classes);
|
var virtualMethodContributorContext = new VirtualMethodContributorContextImpl(classes);
|
||||||
RenderingContext renderingContext = new RenderingContext(debugEmitterToUse,
|
RenderingContext renderingContext = new RenderingContext(debugEmitterToUse,
|
||||||
|
@ -437,8 +421,22 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
renderingContext.setMinifying(obfuscated);
|
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);
|
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter);
|
||||||
renderer.setProperties(controller.getProperties());
|
renderer.setProperties(controller.getProperties());
|
||||||
renderer.setMinifying(obfuscated);
|
renderer.setMinifying(obfuscated);
|
||||||
|
@ -456,9 +454,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
}
|
}
|
||||||
renderer.setDebugEmitter(debugEmitter);
|
renderer.setDebugEmitter(debugEmitter);
|
||||||
}
|
}
|
||||||
for (var entry : methodInjectors.entrySet()) {
|
renderer.prepare(clsNodes);
|
||||||
renderingContext.addInjector(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
printWrapperStart(sourceWriter);
|
printWrapperStart(sourceWriter);
|
||||||
|
|
||||||
|
@ -467,7 +463,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
}
|
}
|
||||||
int start = sourceWriter.getOffset();
|
int start = sourceWriter.getOffset();
|
||||||
|
|
||||||
renderer.prepare(clsNodes);
|
|
||||||
runtimeRenderer.renderRuntime();
|
runtimeRenderer.renderRuntime();
|
||||||
sourceWriter.append("var ").append(renderer.getNaming().getScopeName()).ws().append("=").ws()
|
sourceWriter.append("var ").append(renderer.getNaming().getScopeName()).ws().append("=").ws()
|
||||||
.append("Object.create(null);").newLine();
|
.append("Object.create(null);").newLine();
|
||||||
|
@ -479,22 +474,21 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
renderer.renderStringConstants();
|
renderer.renderStringConstants();
|
||||||
renderer.renderCompatibilityStubs();
|
renderer.renderCompatibilityStubs();
|
||||||
|
|
||||||
if (renderer.isLongLibraryUsed()) {
|
runtimeRenderer.renderHandWrittenRuntime("long.js");
|
||||||
runtimeRenderer.renderHandWrittenRuntime("long.js");
|
if (threadLibraryUsed) {
|
||||||
}
|
|
||||||
if (renderer.isThreadLibraryUsed()) {
|
|
||||||
runtimeRenderer.renderHandWrittenRuntime("thread.js");
|
runtimeRenderer.renderHandWrittenRuntime("thread.js");
|
||||||
} else {
|
} else {
|
||||||
runtimeRenderer.renderHandWrittenRuntime("simpleThread.js");
|
runtimeRenderer.renderHandWrittenRuntime("simpleThread.js");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var entry : controller.getEntryPoints().entrySet()) {
|
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();
|
var ref = entry.getValue().getMethod();
|
||||||
sourceWriter.appendFunction("$rt_mainStarter").append("(").appendMethodBody(ref);
|
sourceWriter.appendFunction("$rt_mainStarter").append("(").appendMethodBody(ref);
|
||||||
sourceWriter.append(");").newLine();
|
sourceWriter.append(");").newLine();
|
||||||
sourceWriter.append("$rt_exports.").append(entry.getKey()).append(".").append("javaException")
|
sourceWriter.appendFunction("$rt_exports").append(".").append(entry.getKey()).append(".")
|
||||||
.ws().append("=").ws().appendFunction("$rt_javaException").append(";").newLine();
|
.append("javaException").ws().append("=").ws().appendFunction("$rt_javaException")
|
||||||
|
.append(";").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var listener : rendererListeners) {
|
for (var listener : rendererListeners) {
|
||||||
|
@ -510,7 +504,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
private void printWrapperStart(SourceWriter writer) {
|
private void printWrapperStart(SourceWriter writer) {
|
||||||
writer.append("\"use strict\";").newLine();
|
writer.append("\"use strict\";").newLine();
|
||||||
printUmdStart(writer);
|
printUmdStart(writer);
|
||||||
writer.append("function($rt_globals,").ws().append("$rt_exports");
|
writer.append("function(").appendFunction("$rt_exports");
|
||||||
for (var moduleName : importedModules.values()) {
|
for (var moduleName : importedModules.values()) {
|
||||||
writer.append(",").ws().appendFunction(moduleName);
|
writer.append(",").ws().appendFunction(moduleName);
|
||||||
}
|
}
|
||||||
|
@ -518,11 +512,11 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String importModule(String name) {
|
private String importModule(String name) {
|
||||||
return importedModules.get(name);
|
return importedModules.computeIfAbsent(name, n -> "$rt_imported_" + importedModules.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printUmdStart(SourceWriter writer) {
|
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'")
|
writer.appendIf().append("typeof define").ws().append("===").ws().append("'function'")
|
||||||
.ws().append("&&").ws().append("define.amd)").appendBlockStart();
|
.ws().append("&&").ws().append("define.amd)").appendBlockStart();
|
||||||
writer.append("define(['exports'");
|
writer.append("define(['exports'");
|
||||||
|
@ -534,7 +528,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
writer.append(',').ws().appendFunction(moduleAlias);
|
writer.append(',').ws().appendFunction(moduleAlias);
|
||||||
}
|
}
|
||||||
writer.append(")").ws().appendBlockStart();
|
writer.append(")").ws().appendBlockStart();
|
||||||
writer.append("module(root,").ws().append("exports");
|
writer.append("module(exports");
|
||||||
for (var moduleAlias : importedModules.values()) {
|
for (var moduleAlias : importedModules.values()) {
|
||||||
writer.append(',').ws().appendFunction(moduleAlias);
|
writer.append(',').ws().appendFunction(moduleAlias);
|
||||||
}
|
}
|
||||||
|
@ -545,19 +539,21 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
.append("===").ws().append("'object'").ws().append("&&").ws()
|
.append("===").ws().append("'object'").ws().append("&&").ws()
|
||||||
.append("exports").ws().append("!==").ws().append("null").ws().append("&&").ws()
|
.append("exports").ws().append("!==").ws().append("null").ws().append("&&").ws()
|
||||||
.append("typeof exports.nodeName").ws().append("!==").ws().append("'string')").appendBlockStart();
|
.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()) {
|
for (var moduleName : importedModules.keySet()) {
|
||||||
writer.append(',').ws().append("require(\"").append(RenderingUtil.escapeString(moduleName)).append("\")");
|
writer.append(',').ws().append("require(\"").append(RenderingUtil.escapeString(moduleName)).append("\")");
|
||||||
}
|
}
|
||||||
writer.append(");").softNewLine();
|
writer.append(");").softNewLine();
|
||||||
|
|
||||||
writer.appendElse();
|
writer.appendElse();
|
||||||
writer.append("module(root,").ws().append("root);").softNewLine();
|
writer.append("module(");
|
||||||
writer.appendBlockEnd();
|
writer.outdent().append("typeof self").ws().append("!==").ws().append("'undefined'")
|
||||||
writer.outdent().append("}(typeof self").ws().append("!==").ws().append("'undefined'")
|
|
||||||
.ws().append("?").ws().append("self")
|
.ws().append("?").ws().append("self")
|
||||||
.ws().append(":").ws().append("this,")
|
.ws().append(":").ws().append("this");
|
||||||
.ws();
|
|
||||||
|
writer.append(");").softNewLine();
|
||||||
|
writer.appendBlockEnd();
|
||||||
|
writer.outdent().append("}(");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printWrapperEnd(SourceWriter writer) {
|
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) + ")";
|
return STATS_NUM_FORMAT.format(size) + " (" + STATS_PERCENT_FORMAT.format((double) size / totalSize) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareModules(ListableClassHolderSource classes) {
|
private List<PreparedClass> modelToAst(ListableClassHolderSource classes, RenderingContext context) {
|
||||||
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) {
|
|
||||||
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(),
|
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(),
|
||||||
controller.getDependencyInfo());
|
controller.getDependencyInfo());
|
||||||
asyncFinder.find(classes);
|
asyncFinder.find(classes);
|
||||||
|
@ -634,7 +608,16 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
break;
|
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;
|
return classNodes;
|
||||||
}
|
}
|
||||||
|
@ -665,7 +648,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
order.add(className);
|
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);
|
PreparedClass clsNode = new PreparedClass(cls);
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
|
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
|
||||||
|
@ -680,14 +663,14 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
PreparedMethod preparedMethod = method.hasModifier(ElementModifier.NATIVE)
|
PreparedMethod preparedMethod = method.hasModifier(ElementModifier.NATIVE)
|
||||||
? decompileNative(method, classes)
|
? prepareNative(method, classes)
|
||||||
: decompile(decompiler, method);
|
: prepare(decompiler, method);
|
||||||
clsNode.getMethods().add(preparedMethod);
|
clsNode.getMethods().add(preparedMethod);
|
||||||
}
|
}
|
||||||
return clsNode;
|
return clsNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PreparedMethod decompileNative(MethodHolder method, ClassReaderSource classes) {
|
private PreparedMethod prepareNative(MethodHolder method, ClassReaderSource classes) {
|
||||||
MethodReference reference = method.getReference();
|
MethodReference reference = method.getReference();
|
||||||
Generator generator = methodGenerators.get(reference);
|
Generator generator = methodGenerators.get(reference);
|
||||||
if (generator == null && !isBootstrap()) {
|
if (generator == null && !isBootstrap()) {
|
||||||
|
@ -702,7 +685,13 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
name -> createGenerator(name, method, classes));
|
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) {
|
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();
|
MethodReference reference = method.getReference();
|
||||||
if (asyncMethods.contains(reference)) {
|
ControlFlowEntry[] cfg;
|
||||||
AsyncMethodNode node = decompileAsync(decompiler, method);
|
MethodNode node;
|
||||||
ControlFlowEntry[] cfg = ProgramUtils.getLocationCFG(method.getProgram());
|
var async = asyncMethods.contains(reference);
|
||||||
return new PreparedMethod(method, node, null, false, cfg);
|
if (async) {
|
||||||
|
node = decompileAsync(decompiler, method);
|
||||||
|
cfg = ProgramUtils.getLocationCFG(method.getProgram());
|
||||||
} else {
|
} else {
|
||||||
AstCacheEntry entry = decompileRegular(decompiler, method);
|
var entry = decompileRegular(decompiler, method);
|
||||||
return new PreparedMethod(method, entry.method, null, false, entry.cfg);
|
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) {
|
private AstCacheEntry decompileRegular(Decompiler decompiler, MethodHolder method) {
|
||||||
|
@ -964,48 +962,4 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
return classSource;
|
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 java.util.function.Function;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.Injector;
|
import org.teavm.backend.javascript.spi.Injector;
|
||||||
import org.teavm.backend.javascript.spi.ModuleImporter;
|
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.vm.spi.RendererListener;
|
import org.teavm.vm.spi.RendererListener;
|
||||||
|
@ -33,8 +32,6 @@ public interface TeaVMJavaScriptHost extends TeaVMHostExtension {
|
||||||
|
|
||||||
void addInjectorProvider(Function<ProviderContext, Injector> provider);
|
void addInjectorProvider(Function<ProviderContext, Injector> provider);
|
||||||
|
|
||||||
void addModuleImporterProvider(Function<ProviderContext, ModuleImporter> provider);
|
|
||||||
|
|
||||||
void add(RendererListener listener);
|
void add(RendererListener listener);
|
||||||
|
|
||||||
void addVirtualMethods(VirtualMethodContributor virtualMethods);
|
void addVirtualMethods(VirtualMethodContributor virtualMethods);
|
||||||
|
|
|
@ -35,4 +35,6 @@ public interface AliasProvider {
|
||||||
ScopedName getClassInitAlias(String className);
|
ScopedName getClassInitAlias(String className);
|
||||||
|
|
||||||
String getScopeAlias();
|
String getScopeAlias();
|
||||||
|
|
||||||
|
void reserveName(String name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,6 +134,10 @@ public class DefaultAliasProvider implements AliasProvider {
|
||||||
return makeUnique(knownAliases, knowAliasesCounter, "$java");
|
return makeUnique(knownAliases, knowAliasesCounter, "$java");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reserveName(String name) {
|
||||||
|
}
|
||||||
|
|
||||||
private ScopedName makeUniqueTopLevel(String suggested) {
|
private ScopedName makeUniqueTopLevel(String suggested) {
|
||||||
if (knownAliases.size() < topLevelAliasLimit) {
|
if (knownAliases.size() < topLevelAliasLimit) {
|
||||||
return new ScopedName(false, makeUnique(knownAliases, knowAliasesCounter, suggested));
|
return new ScopedName(false, makeUnique(knownAliases, knowAliasesCounter, suggested));
|
||||||
|
|
|
@ -130,6 +130,11 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
||||||
return scopeName;
|
return scopeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reserveName(String name) {
|
||||||
|
aliasProvider.reserveName(name);
|
||||||
|
}
|
||||||
|
|
||||||
private MethodReference getRealMethod(MethodReference methodRef) {
|
private MethodReference getRealMethod(MethodReference methodRef) {
|
||||||
String className = methodRef.getClassName();
|
String className = methodRef.getClassName();
|
||||||
while (className != null) {
|
while (className != null) {
|
||||||
|
|
|
@ -89,6 +89,11 @@ public class MinifyingAliasProvider implements AliasProvider {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reserveName(String name) {
|
||||||
|
usedAliases.add(name);
|
||||||
|
}
|
||||||
|
|
||||||
private ScopedName createTopLevelName() {
|
private ScopedName createTopLevelName() {
|
||||||
if (usedAliases.size() < topLevelAliasLimit) {
|
if (usedAliases.size() < topLevelAliasLimit) {
|
||||||
String result;
|
String result;
|
||||||
|
|
|
@ -32,5 +32,9 @@ public interface NameFrequencyConsumer {
|
||||||
|
|
||||||
void consume(FieldReference field);
|
void consume(FieldReference field);
|
||||||
|
|
||||||
|
void consumeStatic(FieldReference field);
|
||||||
|
|
||||||
void consumeFunction(String name);
|
void consumeFunction(String name);
|
||||||
|
|
||||||
|
void consumeGlobal(String name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class NamingOrderer implements NameFrequencyConsumer {
|
public class NamingOrderer implements NameFrequencyConsumer {
|
||||||
private Map<String, Entry> entries = new HashMap<>();
|
private Map<String, Entry> entries = new HashMap<>();
|
||||||
|
private Set<String> reservedNames = new HashSet<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void consume(MethodReference method) {
|
public void consume(MethodReference method) {
|
||||||
|
@ -96,6 +97,18 @@ public class NamingOrderer implements NameFrequencyConsumer {
|
||||||
entry.frequency++;
|
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
|
@Override
|
||||||
public void consumeFunction(String name) {
|
public void consumeFunction(String name) {
|
||||||
String key = "n:" + name;
|
String key = "n:" + name;
|
||||||
|
@ -108,7 +121,15 @@ public class NamingOrderer implements NameFrequencyConsumer {
|
||||||
entry.frequency++;
|
entry.frequency++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void consumeGlobal(String name) {
|
||||||
|
reservedNames.add(name);
|
||||||
|
}
|
||||||
|
|
||||||
public void apply(NamingStrategy naming) {
|
public void apply(NamingStrategy naming) {
|
||||||
|
for (var name : reservedNames) {
|
||||||
|
naming.reserveName(name);
|
||||||
|
}
|
||||||
List<Entry> entryList = new ArrayList<>(entries.values());
|
List<Entry> entryList = new ArrayList<>(entries.values());
|
||||||
Collections.sort(entryList, (o1, o2) -> Integer.compare(o2.frequency, o1.frequency));
|
Collections.sort(entryList, (o1, o2) -> Integer.compare(o2.frequency, o1.frequency));
|
||||||
for (Entry entry : entryList) {
|
for (Entry entry : entryList) {
|
||||||
|
|
|
@ -37,4 +37,6 @@ public interface NamingStrategy {
|
||||||
ScopedName getNameForClassInit(String className);
|
ScopedName getNameForClassInit(String className);
|
||||||
|
|
||||||
String getScopeName();
|
String getScopeName();
|
||||||
|
|
||||||
|
void reserveName(String name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,11 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
|
||||||
return append(naming.getNameForFunction(name));
|
return append(naming.getNameForFunction(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SourceWriter appendGlobal(String name) {
|
||||||
|
return append(name);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceWriter appendInit(MethodReference method) {
|
public SourceWriter appendInit(MethodReference method) {
|
||||||
return appendName(naming.getNameForInit(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.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
public abstract class SourceWriter implements Appendable {
|
public abstract class SourceWriter implements Appendable, SourceWriterSink {
|
||||||
public SourceWriter append(String value) {
|
public SourceWriter append(String value) {
|
||||||
append((CharSequence) value);
|
append((CharSequence) value);
|
||||||
return this;
|
return this;
|
||||||
|
@ -62,22 +62,27 @@ public abstract class SourceWriter implements Appendable {
|
||||||
@Override
|
@Override
|
||||||
public abstract SourceWriter append(CharSequence csq, int start, int end);
|
public abstract SourceWriter append(CharSequence csq, int start, int end);
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter appendClass(String cls);
|
public abstract SourceWriter appendClass(String cls);
|
||||||
|
|
||||||
public SourceWriter appendClass(Class<?> cls) {
|
public SourceWriter appendClass(Class<?> cls) {
|
||||||
return appendClass(cls.getName());
|
return appendClass(cls.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter appendField(FieldReference field);
|
public abstract SourceWriter appendField(FieldReference field);
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter appendStaticField(FieldReference field);
|
public abstract SourceWriter appendStaticField(FieldReference field);
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter appendMethod(MethodDescriptor method);
|
public abstract SourceWriter appendMethod(MethodDescriptor method);
|
||||||
|
|
||||||
public SourceWriter appendMethod(String name, Class<?>... params) {
|
public SourceWriter appendMethod(String name, Class<?>... params) {
|
||||||
return appendMethod(new MethodDescriptor(name, params));
|
return appendMethod(new MethodDescriptor(name, params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter appendMethodBody(MethodReference method);
|
public abstract SourceWriter appendMethodBody(MethodReference method);
|
||||||
|
|
||||||
public SourceWriter appendMethodBody(String className, String name, ValueType... params) {
|
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));
|
return appendMethodBody(new MethodReference(cls, name, params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter appendFunction(String name);
|
public abstract SourceWriter appendFunction(String name);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract SourceWriter appendGlobal(String name);
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter appendInit(MethodReference method);
|
public abstract SourceWriter appendInit(MethodReference method);
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter appendClassInit(String className);
|
public abstract SourceWriter appendClassInit(String className);
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter newLine();
|
public abstract SourceWriter newLine();
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter ws();
|
public abstract SourceWriter ws();
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter tokenBoundary();
|
public abstract SourceWriter tokenBoundary();
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter softNewLine();
|
public abstract SourceWriter softNewLine();
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter indent();
|
public abstract SourceWriter indent();
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter outdent();
|
public abstract SourceWriter outdent();
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter emitLocation(String fileName, int line);
|
public abstract SourceWriter emitLocation(String fileName, int line);
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter enterLocation();
|
public abstract SourceWriter enterLocation();
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter exitLocation();
|
public abstract SourceWriter exitLocation();
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract SourceWriter emitStatementStart();
|
public abstract SourceWriter emitStatementStart();
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract void emitMethod(MethodDescriptor method);
|
public abstract void emitMethod(MethodDescriptor method);
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract void emitClass(String className);
|
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;
|
package org.teavm.backend.javascript.decompile;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
import org.teavm.ast.ControlFlowEntry;
|
import org.teavm.ast.ControlFlowEntry;
|
||||||
import org.teavm.ast.MethodNode;
|
import org.teavm.backend.javascript.codegen.RememberedSource;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.model.AccessLevel;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class PreparedMethod {
|
public class PreparedMethod {
|
||||||
public final MethodHolder methodHolder;
|
public final AccessLevel accessLevel;
|
||||||
|
public final Set<ElementModifier> modifiers;
|
||||||
public final MethodReference reference;
|
public final MethodReference reference;
|
||||||
public final MethodNode node;
|
public final RememberedSource body;
|
||||||
public final Generator generator;
|
public final RememberedSource parameters;
|
||||||
public final boolean async;
|
public final boolean async;
|
||||||
public final ControlFlowEntry[] cfg;
|
public final ControlFlowEntry[] cfg;
|
||||||
|
public final PreparedVariable[] variables;
|
||||||
|
|
||||||
public PreparedMethod(MethodHolder method, MethodNode node, Generator generator, boolean async,
|
public PreparedMethod(AccessLevel accessLevel, Set<ElementModifier> modifiers, MethodReference reference,
|
||||||
ControlFlowEntry[] cfg) {
|
RememberedSource body, RememberedSource parameters, boolean async, ControlFlowEntry[] cfg,
|
||||||
this.reference = method.getReference();
|
PreparedVariable[] variables) {
|
||||||
this.methodHolder = method;
|
this.accessLevel = accessLevel;
|
||||||
this.node = node;
|
this.modifiers = modifiers;
|
||||||
this.generator = generator;
|
this.reference = reference;
|
||||||
|
this.body = body;
|
||||||
|
this.parameters = parameters;
|
||||||
this.async = async;
|
this.async = async;
|
||||||
this.cfg = cfg;
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.javascript.spi;
|
package org.teavm.backend.javascript.decompile;
|
||||||
|
|
||||||
public interface ModuleImporter {
|
import org.teavm.debugging.information.DebugInformationEmitter;
|
||||||
void importModules(ModuleImporterContext context);
|
|
||||||
|
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")) {
|
if (s.startsWith("$rt_") || s.startsWith("Long_") || s.equals("Long")) {
|
||||||
return prec -> writer.appendFunction(s);
|
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;
|
package org.teavm.backend.javascript.rendering;
|
||||||
|
|
||||||
import java.util.Set;
|
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.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.PreparedClass;
|
||||||
import org.teavm.backend.javascript.decompile.PreparedMethod;
|
import org.teavm.backend.javascript.decompile.PreparedMethod;
|
||||||
import org.teavm.model.ClassReader;
|
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldHolder;
|
import org.teavm.model.FieldHolder;
|
||||||
|
@ -54,7 +30,7 @@ import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
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,
|
static final MethodReference MONITOR_ENTER_METHOD = new MethodReference(Object.class,
|
||||||
"monitorEnter", Object.class, void.class);
|
"monitorEnter", Object.class, void.class);
|
||||||
static final MethodReference MONITOR_ENTER_SYNC_METHOD = new MethodReference(Object.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 NameFrequencyConsumer consumer;
|
||||||
private final ClassReaderSource classSource;
|
private final ClassReaderSource classSource;
|
||||||
private boolean async;
|
|
||||||
private final Set<MethodReference> injectedMethods;
|
|
||||||
private final Set<MethodReference> asyncFamilyMethods;
|
private final Set<MethodReference> asyncFamilyMethods;
|
||||||
private final boolean strict;
|
|
||||||
|
|
||||||
NameFrequencyEstimator(NameFrequencyConsumer consumer, ClassReaderSource classSource,
|
NameFrequencyEstimator(NameFrequencyConsumer consumer, ClassReaderSource classSource,
|
||||||
Set<MethodReference> injectedMethods, Set<MethodReference> asyncFamilyMethods,
|
Set<MethodReference> asyncFamilyMethods) {
|
||||||
boolean strict) {
|
|
||||||
this.consumer = consumer;
|
this.consumer = consumer;
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
this.injectedMethods = injectedMethods;
|
|
||||||
this.asyncFamilyMethods = asyncFamilyMethods;
|
this.asyncFamilyMethods = asyncFamilyMethods;
|
||||||
this.strict = strict;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void estimate(PreparedClass cls) {
|
public void estimate(PreparedClass cls) {
|
||||||
|
@ -102,11 +72,11 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
|
||||||
if (asyncFamilyMethods.contains(method.reference)) {
|
if (asyncFamilyMethods.contains(method.reference)) {
|
||||||
consumer.consume(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>"))) {
|
|| method.reference.getName().equals("<init>"))) {
|
||||||
consumer.consume(method.reference);
|
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.getDescriptor());
|
||||||
consumer.consume(method.reference);
|
consumer.consume(method.reference);
|
||||||
}
|
}
|
||||||
|
@ -117,9 +87,7 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
|
||||||
consumer.consumeFunction("$rt_invalidPointer");
|
consumer.consumeFunction("$rt_invalidPointer");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method.node != null) {
|
method.body.replay(this, RememberedSource.FILTER_REF);
|
||||||
method.node.acceptVisitor(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clinit != null) {
|
if (clinit != null) {
|
||||||
|
@ -149,367 +117,56 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(RegularMethodNode methodNode) {
|
public SourceWriterSink appendClass(String cls) {
|
||||||
async = false;
|
consumer.consume(cls);
|
||||||
methodNode.getBody().acceptVisitor(this);
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(AsyncMethodNode methodNode) {
|
public SourceWriterSink appendField(FieldReference field) {
|
||||||
async = true;
|
consumer.consume(field);
|
||||||
for (AsyncMethodPart part : methodNode.getBody()) {
|
return this;
|
||||||
part.getStatement().acceptVisitor(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(AssignmentStatement statement) {
|
public SourceWriterSink appendStaticField(FieldReference field) {
|
||||||
super.visit(statement);
|
consumer.consumeStatic(field);
|
||||||
if (statement.isAsync()) {
|
return this;
|
||||||
consumer.consumeFunction("$rt_suspending");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ThrowStatement statement) {
|
public SourceWriterSink appendMethod(MethodDescriptor method) {
|
||||||
statement.getException().acceptVisitor(this);
|
consumer.consume(method);
|
||||||
consumer.consumeFunction("$rt_throw");
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(InitClassStatement statement) {
|
public SourceWriterSink appendMethodBody(MethodReference method) {
|
||||||
consumer.consumeClassInit(statement.getClassName());
|
consumer.consume(method);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(TryCatchStatement statement) {
|
public SourceWriterSink appendFunction(String name) {
|
||||||
super.visit(statement);
|
consumer.consumeFunction(name);
|
||||||
if (statement.getExceptionType() != null) {
|
return this;
|
||||||
consumer.consume(statement.getExceptionType());
|
|
||||||
}
|
|
||||||
consumer.consumeFunction("$rt_wrapException");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(MonitorEnterStatement statement) {
|
public SourceWriterSink appendGlobal(String name) {
|
||||||
super.visit(statement);
|
consumer.consumeGlobal(name);
|
||||||
if (async) {
|
return this;
|
||||||
consumer.consume(MONITOR_ENTER_METHOD);
|
|
||||||
consumer.consumeFunction("$rt_suspending");
|
|
||||||
} else {
|
|
||||||
consumer.consume(MONITOR_ENTER_SYNC_METHOD);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(MonitorExitStatement statement) {
|
public SourceWriterSink appendInit(MethodReference method) {
|
||||||
super.visit(statement);
|
consumer.consumeInit(method);
|
||||||
if (async) {
|
return this;
|
||||||
consumer.consume(MONITOR_EXIT_METHOD);
|
|
||||||
} else {
|
|
||||||
consumer.consume(MONITOR_EXIT_SYNC_METHOD);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(BinaryExpr expr) {
|
public SourceWriterSink appendClassInit(String className) {
|
||||||
super.visit(expr);
|
consumer.consumeClassInit(className);
|
||||||
if (expr.getType() == OperationType.LONG) {
|
return this;
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,30 +27,19 @@ import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.IntFunction;
|
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.NamingOrderer;
|
||||||
import org.teavm.backend.javascript.codegen.NamingStrategy;
|
import org.teavm.backend.javascript.codegen.NamingStrategy;
|
||||||
import org.teavm.backend.javascript.codegen.OutputSourceWriter;
|
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.ScopedName;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.decompile.PreparedClass;
|
import org.teavm.backend.javascript.decompile.PreparedClass;
|
||||||
import org.teavm.backend.javascript.decompile.PreparedMethod;
|
import org.teavm.backend.javascript.decompile.PreparedMethod;
|
||||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
|
||||||
import org.teavm.common.ServiceRepository;
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.debugging.information.DebugInformationEmitter;
|
import org.teavm.debugging.information.DebugInformationEmitter;
|
||||||
import org.teavm.debugging.information.DummyDebugInformationEmitter;
|
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.AccessLevel;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldHolder;
|
import org.teavm.model.FieldHolder;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
|
@ -74,7 +63,6 @@ public class Renderer implements RenderingManager {
|
||||||
private DebugInformationEmitter debugEmitter = new DummyDebugInformationEmitter();
|
private DebugInformationEmitter debugEmitter = new DummyDebugInformationEmitter();
|
||||||
private final Set<MethodReference> asyncMethods;
|
private final Set<MethodReference> asyncMethods;
|
||||||
private final Set<MethodReference> asyncFamilyMethods;
|
private final Set<MethodReference> asyncFamilyMethods;
|
||||||
private final Diagnostics diagnostics;
|
|
||||||
private RenderingContext context;
|
private RenderingContext context;
|
||||||
private List<PostponedFieldInitializer> postponedFieldInitializers = new ArrayList<>();
|
private List<PostponedFieldInitializer> postponedFieldInitializers = new ArrayList<>();
|
||||||
private IntFunction<TeaVMProgressFeedback> progressConsumer = p -> TeaVMProgressFeedback.CONTINUE;
|
private IntFunction<TeaVMProgressFeedback> progressConsumer = p -> TeaVMProgressFeedback.CONTINUE;
|
||||||
|
@ -84,11 +72,8 @@ public class Renderer implements RenderingManager {
|
||||||
private int stringPoolSize;
|
private int stringPoolSize;
|
||||||
private int metadataSize;
|
private int metadataSize;
|
||||||
|
|
||||||
private boolean longLibraryUsed;
|
|
||||||
private boolean threadLibraryUsed;
|
|
||||||
|
|
||||||
public Renderer(OutputSourceWriter writer, Set<MethodReference> asyncMethods,
|
public Renderer(OutputSourceWriter writer, Set<MethodReference> asyncMethods,
|
||||||
Set<MethodReference> asyncFamilyMethods, Diagnostics diagnostics, RenderingContext context) {
|
Set<MethodReference> asyncFamilyMethods, RenderingContext context) {
|
||||||
this.naming = context.getNaming();
|
this.naming = context.getNaming();
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
this.classSource = context.getClassSource();
|
this.classSource = context.getClassSource();
|
||||||
|
@ -96,18 +81,9 @@ public class Renderer implements RenderingManager {
|
||||||
this.services = context.getServices();
|
this.services = context.getServices();
|
||||||
this.asyncMethods = new HashSet<>(asyncMethods);
|
this.asyncMethods = new HashSet<>(asyncMethods);
|
||||||
this.asyncFamilyMethods = new HashSet<>(asyncFamilyMethods);
|
this.asyncFamilyMethods = new HashSet<>(asyncFamilyMethods);
|
||||||
this.diagnostics = diagnostics;
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLongLibraryUsed() {
|
|
||||||
return longLibraryUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isThreadLibraryUsed() {
|
|
||||||
return threadLibraryUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getStringPoolSize() {
|
public int getStringPoolSize() {
|
||||||
return stringPoolSize;
|
return stringPoolSize;
|
||||||
}
|
}
|
||||||
|
@ -160,10 +136,6 @@ public class Renderer implements RenderingManager {
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DebugInformationEmitter getDebugEmitter() {
|
|
||||||
return debugEmitter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDebugEmitter(DebugInformationEmitter debugEmitter) {
|
public void setDebugEmitter(DebugInformationEmitter debugEmitter) {
|
||||||
this.debugEmitter = debugEmitter;
|
this.debugEmitter = debugEmitter;
|
||||||
}
|
}
|
||||||
|
@ -242,8 +214,8 @@ public class Renderer implements RenderingManager {
|
||||||
public void prepare(List<PreparedClass> classes) {
|
public void prepare(List<PreparedClass> classes) {
|
||||||
if (minifying) {
|
if (minifying) {
|
||||||
NamingOrderer orderer = new NamingOrderer();
|
NamingOrderer orderer = new NamingOrderer();
|
||||||
NameFrequencyEstimator estimator = new NameFrequencyEstimator(orderer, classSource, asyncMethods,
|
NameFrequencyEstimator estimator = new NameFrequencyEstimator(orderer, classSource,
|
||||||
asyncFamilyMethods, context.isStrict());
|
asyncFamilyMethods);
|
||||||
for (PreparedClass cls : classes) {
|
for (PreparedClass cls : classes) {
|
||||||
estimator.estimate(cls);
|
estimator.estimate(cls);
|
||||||
}
|
}
|
||||||
|
@ -374,7 +346,7 @@ public class Renderer implements RenderingManager {
|
||||||
if (!cls.getClassHolder().hasModifier(ElementModifier.INTERFACE)
|
if (!cls.getClassHolder().hasModifier(ElementModifier.INTERFACE)
|
||||||
&& !cls.getClassHolder().hasModifier(ElementModifier.ABSTRACT)) {
|
&& !cls.getClassHolder().hasModifier(ElementModifier.ABSTRACT)) {
|
||||||
for (PreparedMethod method : cls.getMethods()) {
|
for (PreparedMethod method : cls.getMethods()) {
|
||||||
if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) {
|
if (!method.modifiers.contains(ElementModifier.STATIC)) {
|
||||||
if (method.reference.getName().equals("<init>")) {
|
if (method.reference.getName().equals("<init>")) {
|
||||||
renderInitializer(method);
|
renderInitializer(method);
|
||||||
}
|
}
|
||||||
|
@ -416,7 +388,7 @@ public class Renderer implements RenderingManager {
|
||||||
writer.append("return;").softNewLine();
|
writer.append("return;").softNewLine();
|
||||||
writer.outdent().append("}").softNewLine();
|
writer.outdent().append("}").softNewLine();
|
||||||
|
|
||||||
renderAsyncPrologue();
|
renderAsyncPrologue(writer, context);
|
||||||
|
|
||||||
writer.append("case 0:").indent().softNewLine();
|
writer.append("case 0:").indent().softNewLine();
|
||||||
writer.append(clinitCalled).ws().append('=').ws().append("true;").softNewLine();
|
writer.append(clinitCalled).ws().append('=').ws().append("true;").softNewLine();
|
||||||
|
@ -440,7 +412,7 @@ public class Renderer implements RenderingManager {
|
||||||
renderEraseClinit(cls);
|
renderEraseClinit(cls);
|
||||||
writer.append("return;").softNewLine().outdent();
|
writer.append("return;").softNewLine().outdent();
|
||||||
|
|
||||||
renderAsyncEpilogue();
|
renderAsyncEpilogue(writer);
|
||||||
writer.appendFunction("$rt_nativeThread").append("().push(" + context.pointerName() + ");").softNewLine();
|
writer.appendFunction("$rt_nativeThread").append("().push(" + context.pointerName() + ");").softNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,8 +533,8 @@ public class Renderer implements RenderingManager {
|
||||||
Map<MethodDescriptor, MethodReference> virtualMethods = new LinkedHashMap<>();
|
Map<MethodDescriptor, MethodReference> virtualMethods = new LinkedHashMap<>();
|
||||||
collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods);
|
collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods);
|
||||||
for (PreparedMethod method : cls.getMethods()) {
|
for (PreparedMethod method : cls.getMethods()) {
|
||||||
if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)
|
if (!method.modifiers.contains(ElementModifier.STATIC)
|
||||||
&& method.methodHolder.getLevel() != AccessLevel.PRIVATE) {
|
&& method.accessLevel != AccessLevel.PRIVATE) {
|
||||||
virtualMethods.put(method.reference.getDescriptor(), method.reference);
|
virtualMethods.put(method.reference.getDescriptor(), method.reference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -811,37 +783,20 @@ public class Renderer implements RenderingManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderBody(PreparedMethod method) {
|
private void renderBody(PreparedMethod method) {
|
||||||
StatementRenderer statementRenderer = new StatementRenderer(context, writer);
|
|
||||||
statementRenderer.setCurrentMethod(method.node);
|
|
||||||
|
|
||||||
MethodReference ref = method.reference;
|
MethodReference ref = method.reference;
|
||||||
debugEmitter.emitMethod(ref.getDescriptor());
|
debugEmitter.emitMethod(ref.getDescriptor());
|
||||||
ScopedName name = naming.getFullNameFor(ref);
|
ScopedName name = naming.getFullNameFor(ref);
|
||||||
|
|
||||||
renderFunctionDeclaration(name);
|
renderFunctionDeclaration(name);
|
||||||
writer.append("(");
|
writer.append("(");
|
||||||
int startParam = 0;
|
method.parameters.replay(writer, RememberedSource.FILTER_ALL);
|
||||||
if (method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) {
|
if (method.variables != null) {
|
||||||
startParam = 1;
|
for (var variable : method.variables) {
|
||||||
}
|
variable.emit(debugEmitter);
|
||||||
for (int i = startParam; i <= ref.parameterCount(); ++i) {
|
|
||||||
if (i > startParam) {
|
|
||||||
writer.append(",").ws();
|
|
||||||
}
|
}
|
||||||
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("}");
|
writer.outdent().append("}");
|
||||||
if (name.scoped) {
|
if (name.scoped) {
|
||||||
|
@ -850,16 +805,6 @@ public class Renderer implements RenderingManager {
|
||||||
|
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
debugEmitter.emitMethod(null);
|
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) {
|
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)")
|
writer.append(context.mainLoopName()).append(":").ws().append("while").ws().append("(true)")
|
||||||
.ws().append("{").ws();
|
.ws().append("{").ws();
|
||||||
writer.append("switch").ws().append("(").append(context.pointerName()).append(")").ws()
|
writer.append("switch").ws().append("(").append(context.pointerName()).append(")").ws()
|
||||||
.append('{').softNewLine();
|
.append('{').softNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderAsyncEpilogue() {
|
static void renderAsyncEpilogue(SourceWriter writer) {
|
||||||
writer.append("default:").ws().appendFunction("$rt_invalidPointer").append("();").softNewLine();
|
writer.append("default:").ws().appendFunction("$rt_invalidPointer").append("();").softNewLine();
|
||||||
writer.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
|
@Override
|
||||||
public <T> T getService(Class<T> type) {
|
public <T> T getService(Class<T> type) {
|
||||||
return services.getService(type);
|
return services.getService(type);
|
||||||
|
|
|
@ -96,7 +96,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
||||||
private int currentPart;
|
private int currentPart;
|
||||||
private List<String> blockIds = new ArrayList<>();
|
private List<String> blockIds = new ArrayList<>();
|
||||||
private IntIndexedContainer blockIndexMap = new IntArrayList();
|
private IntIndexedContainer blockIndexMap = new IntArrayList();
|
||||||
private boolean longLibraryUsed;
|
|
||||||
private static final MethodDescriptor CLINIT_METHOD = new MethodDescriptor("<clinit>", ValueType.VOID);
|
private static final MethodDescriptor CLINIT_METHOD = new MethodDescriptor("<clinit>", ValueType.VOID);
|
||||||
private VariableNameGenerator variableNameGenerator;
|
private VariableNameGenerator variableNameGenerator;
|
||||||
private final Deque<LocationStackEntry> locationStack = new ArrayDeque<>();
|
private final Deque<LocationStackEntry> locationStack = new ArrayDeque<>();
|
||||||
|
@ -111,8 +110,16 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
||||||
variableNameGenerator = new VariableNameGenerator(minifying);
|
variableNameGenerator = new VariableNameGenerator(minifying);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLongLibraryUsed() {
|
public void clear() {
|
||||||
return longLibraryUsed;
|
blockIdMap.clear();
|
||||||
|
blockIds.clear();
|
||||||
|
blockIndexMap.clear();
|
||||||
|
currentPart = 0;
|
||||||
|
end = false;
|
||||||
|
precedence = null;
|
||||||
|
variableNameGenerator.setCurrentMethod(null);
|
||||||
|
locationStack.clear();
|
||||||
|
lastEmittedLocation = TextLocation.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAsync() {
|
public boolean isAsync() {
|
||||||
|
@ -624,7 +631,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
||||||
@Override
|
@Override
|
||||||
public void visit(BinaryExpr expr) {
|
public void visit(BinaryExpr expr) {
|
||||||
if (expr.getType() == OperationType.LONG) {
|
if (expr.getType() == OperationType.LONG) {
|
||||||
longLibraryUsed = true;
|
|
||||||
switch (expr.getOperation()) {
|
switch (expr.getOperation()) {
|
||||||
case ADD:
|
case ADD:
|
||||||
visitBinaryFunction(expr, "Long_add");
|
visitBinaryFunction(expr, "Long_add");
|
||||||
|
@ -772,7 +778,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
||||||
switch (expr.getOperation()) {
|
switch (expr.getOperation()) {
|
||||||
case NOT: {
|
case NOT: {
|
||||||
if (expr.getType() == OperationType.LONG) {
|
if (expr.getType() == OperationType.LONG) {
|
||||||
longLibraryUsed = true;
|
|
||||||
writer.appendFunction("Long_not").append("(");
|
writer.appendFunction("Long_not").append("(");
|
||||||
precedence = Precedence.min();
|
precedence = Precedence.min();
|
||||||
expr.getOperand().acceptVisitor(this);
|
expr.getOperand().acceptVisitor(this);
|
||||||
|
@ -792,7 +797,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
||||||
}
|
}
|
||||||
case NEGATE:
|
case NEGATE:
|
||||||
if (expr.getType() == OperationType.LONG) {
|
if (expr.getType() == OperationType.LONG) {
|
||||||
longLibraryUsed = true;
|
|
||||||
writer.appendFunction("Long_neg").append("(");
|
writer.appendFunction("Long_neg").append("(");
|
||||||
precedence = Precedence.min();
|
precedence = Precedence.min();
|
||||||
expr.getOperand().acceptVisitor(this);
|
expr.getOperand().acceptVisitor(this);
|
||||||
|
|
|
@ -42,15 +42,11 @@ public interface GeneratorContext extends ServiceRepository {
|
||||||
|
|
||||||
boolean isAsync(MethodReference method);
|
boolean isAsync(MethodReference method);
|
||||||
|
|
||||||
boolean isAsyncFamily(MethodReference method);
|
|
||||||
|
|
||||||
Diagnostics getDiagnostics();
|
Diagnostics getDiagnostics();
|
||||||
|
|
||||||
DependencyInfo getDependency();
|
DependencyInfo getDependency();
|
||||||
|
|
||||||
void typeToClassString(SourceWriter writer, ValueType type);
|
void typeToClassString(SourceWriter writer, ValueType type);
|
||||||
|
|
||||||
void useLongLibrary();
|
|
||||||
|
|
||||||
boolean isDynamicInitializer(String className);
|
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")) {
|
if (scope == null && name.getIdentifier().equals("teavm_globals")) {
|
||||||
var oldRootScope = rootScope;
|
var oldRootScope = rootScope;
|
||||||
rootScope = false;
|
rootScope = false;
|
||||||
writer.append("$rt_globals").append(".");
|
writer.appendGlobal(node.getProperty().getIdentifier());
|
||||||
print(node.getProperty());
|
|
||||||
rootScope = oldRootScope;
|
rootScope = oldRootScope;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,10 +81,5 @@ public class JSOPlugin implements TeaVMPlugin {
|
||||||
wrapperGenerator);
|
wrapperGenerator);
|
||||||
|
|
||||||
TeaVMPluginUtil.handleNatives(host, JS.class);
|
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