mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-23 00:24:11 -08:00
JS: render all declarations into one remembered fragment, then output this fragment to real output
This commit is contained in:
parent
ceffde38f3
commit
5ec4450bf8
|
@ -16,7 +16,6 @@
|
||||||
package org.teavm.backend.javascript;
|
package org.teavm.backend.javascript;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||||
import com.carrotsearch.hppc.ObjectIntMap;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
|
@ -24,7 +23,6 @@ import java.io.Writer;
|
||||||
import java.lang.ref.Reference;
|
import java.lang.ref.Reference;
|
||||||
import java.lang.ref.ReferenceQueue;
|
import java.lang.ref.ReferenceQueue;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
|
@ -37,30 +35,24 @@ import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
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.analysis.LocationGraphBuilder;
|
|
||||||
import org.teavm.ast.decompilation.DecompilationException;
|
|
||||||
import org.teavm.ast.decompilation.Decompiler;
|
|
||||||
import org.teavm.backend.javascript.codegen.AliasProvider;
|
import org.teavm.backend.javascript.codegen.AliasProvider;
|
||||||
import org.teavm.backend.javascript.codegen.DefaultAliasProvider;
|
import org.teavm.backend.javascript.codegen.DefaultAliasProvider;
|
||||||
import org.teavm.backend.javascript.codegen.DefaultNamingStrategy;
|
import org.teavm.backend.javascript.codegen.DefaultNamingStrategy;
|
||||||
import org.teavm.backend.javascript.codegen.MinifyingAliasProvider;
|
import org.teavm.backend.javascript.codegen.MinifyingAliasProvider;
|
||||||
|
import org.teavm.backend.javascript.codegen.OutputSourceWriter;
|
||||||
import org.teavm.backend.javascript.codegen.OutputSourceWriterBuilder;
|
import org.teavm.backend.javascript.codegen.OutputSourceWriterBuilder;
|
||||||
|
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.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.decompile.PreparedClass;
|
|
||||||
import org.teavm.backend.javascript.decompile.PreparedMethod;
|
|
||||||
import org.teavm.backend.javascript.intrinsics.ref.ReferenceQueueGenerator;
|
import org.teavm.backend.javascript.intrinsics.ref.ReferenceQueueGenerator;
|
||||||
import org.teavm.backend.javascript.intrinsics.ref.ReferenceQueueTransformer;
|
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.NameFrequencyEstimator;
|
||||||
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;
|
||||||
|
@ -72,29 +64,21 @@ import org.teavm.backend.javascript.spi.Injector;
|
||||||
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;
|
||||||
import org.teavm.cache.AstCacheEntry;
|
|
||||||
import org.teavm.cache.AstDependencyExtractor;
|
|
||||||
import org.teavm.cache.CacheStatus;
|
|
||||||
import org.teavm.cache.EmptyMethodNodeCache;
|
import org.teavm.cache.EmptyMethodNodeCache;
|
||||||
import org.teavm.cache.MethodNodeCache;
|
import org.teavm.cache.MethodNodeCache;
|
||||||
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.debugging.information.SourceLocation;
|
import org.teavm.debugging.information.SourceLocation;
|
||||||
import org.teavm.dependency.AbstractDependencyListener;
|
import org.teavm.dependency.AbstractDependencyListener;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.DependencyAnalyzer;
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
import org.teavm.dependency.DependencyInfo;
|
|
||||||
import org.teavm.dependency.DependencyListener;
|
import org.teavm.dependency.DependencyListener;
|
||||||
import org.teavm.dependency.DependencyType;
|
import org.teavm.dependency.DependencyType;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
import org.teavm.interop.PlatformMarker;
|
import org.teavm.interop.PlatformMarker;
|
||||||
import org.teavm.interop.Platforms;
|
import org.teavm.interop.Platforms;
|
||||||
import org.teavm.model.AnnotationHolder;
|
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
import org.teavm.model.ClassHolder;
|
|
||||||
import org.teavm.model.ClassHolderSource;
|
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
|
@ -115,8 +99,6 @@ import org.teavm.model.instructions.StringConstantInstruction;
|
||||||
import org.teavm.model.transformation.BoundCheckInsertion;
|
import org.teavm.model.transformation.BoundCheckInsertion;
|
||||||
import org.teavm.model.transformation.NullCheckFilter;
|
import org.teavm.model.transformation.NullCheckFilter;
|
||||||
import org.teavm.model.transformation.NullCheckInsertion;
|
import org.teavm.model.transformation.NullCheckInsertion;
|
||||||
import org.teavm.model.util.AsyncMethodFinder;
|
|
||||||
import org.teavm.model.util.ProgramUtils;
|
|
||||||
import org.teavm.vm.BuildTarget;
|
import org.teavm.vm.BuildTarget;
|
||||||
import org.teavm.vm.RenderingException;
|
import org.teavm.vm.RenderingException;
|
||||||
import org.teavm.vm.TeaVMTarget;
|
import org.teavm.vm.TeaVMTarget;
|
||||||
|
@ -141,18 +123,13 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
private DebugInformationEmitter debugEmitter;
|
private DebugInformationEmitter debugEmitter;
|
||||||
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
|
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
|
||||||
private final Set<MethodReference> asyncMethods = new HashSet<>();
|
private final Set<MethodReference> asyncMethods = new HashSet<>();
|
||||||
private final Set<MethodReference> asyncFamilyMethods = new HashSet<>();
|
|
||||||
private List<VirtualMethodContributor> customVirtualMethods = new ArrayList<>();
|
private List<VirtualMethodContributor> customVirtualMethods = new ArrayList<>();
|
||||||
private int topLevelNameLimit = 500000;
|
private int topLevelNameLimit = 500000;
|
||||||
private AstDependencyExtractor dependencyExtractor = new AstDependencyExtractor();
|
|
||||||
private boolean strict;
|
private boolean strict;
|
||||||
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
||||||
private NullCheckInsertion nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
|
private NullCheckInsertion nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
|
||||||
private final Map<String, String> importedModules = new LinkedHashMap<>();
|
private final Map<String, String> importedModules = new LinkedHashMap<>();
|
||||||
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() {
|
||||||
|
@ -422,71 +399,47 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
};
|
};
|
||||||
renderingContext.setMinifying(obfuscated);
|
renderingContext.setMinifying(obfuscated);
|
||||||
|
|
||||||
bodyRenderer = new MethodBodyRenderer(renderingContext, controller.getDiagnostics(), obfuscated,
|
|
||||||
debugEmitter != null, asyncMethods);
|
|
||||||
|
|
||||||
var clsNodes = modelToAst(classes, renderingContext);
|
|
||||||
if (controller.wasCancelled()) {
|
if (controller.wasCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builder = new OutputSourceWriterBuilder(naming);
|
var builder = new OutputSourceWriterBuilder(naming);
|
||||||
builder.setMinified(obfuscated);
|
builder.setMinified(obfuscated);
|
||||||
var sourceWriter = builder.build(writer);
|
|
||||||
|
|
||||||
sourceWriter.setDebugInformationEmitter(debugEmitterToUse);
|
for (var className : classes.getClassNames()) {
|
||||||
|
var cls = classes.get(className);
|
||||||
var renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods, renderingContext);
|
for (var method : cls.getMethods()) {
|
||||||
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter);
|
preprocessNativeMethod(method);
|
||||||
renderer.setProperties(controller.getProperties());
|
|
||||||
renderer.setMinifying(obfuscated);
|
|
||||||
renderer.setProgressConsumer(controller::reportProgress);
|
|
||||||
if (debugEmitter != null) {
|
|
||||||
for (PreparedClass preparedClass : clsNodes) {
|
|
||||||
for (PreparedMethod preparedMethod : preparedClass.getMethods()) {
|
|
||||||
if (preparedMethod.cfg != null) {
|
|
||||||
emitCFG(debugEmitter, preparedMethod.cfg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (controller.wasCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
renderer.setDebugEmitter(debugEmitter);
|
|
||||||
}
|
}
|
||||||
renderer.prepare(clsNodes);
|
for (var entry : methodInjectors.entrySet()) {
|
||||||
|
renderingContext.addInjector(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
printWrapperStart(sourceWriter);
|
var rememberingWriter = new RememberingSourceWriter(debugEmitter != null);
|
||||||
|
var renderer = new Renderer(rememberingWriter, asyncMethods, renderingContext, controller.getDiagnostics(),
|
||||||
|
methodGenerators, astCache, controller.getCacheStatus(), templateFactory);
|
||||||
|
renderer.setProperties(controller.getProperties());
|
||||||
|
renderer.setProgressConsumer(controller::reportProgress);
|
||||||
|
|
||||||
for (RendererListener listener : rendererListeners) {
|
for (var listener : rendererListeners) {
|
||||||
listener.begin(renderer, target);
|
listener.begin(renderer, target);
|
||||||
}
|
}
|
||||||
int start = sourceWriter.getOffset();
|
if (!renderer.render(classes, controller.isFriendlyToDebugger())) {
|
||||||
|
|
||||||
runtimeRenderer.renderRuntime();
|
|
||||||
sourceWriter.append("var ").append(renderer.getNaming().getScopeName()).ws().append("=").ws()
|
|
||||||
.append("Object.create(null);").newLine();
|
|
||||||
if (!renderer.render(clsNodes)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
runtimeRenderer.renderHandWrittenRuntime("array.js");
|
var declarations = rememberingWriter.save();
|
||||||
|
rememberingWriter.clear();
|
||||||
|
|
||||||
renderer.renderStringPool();
|
renderer.renderStringPool();
|
||||||
renderer.renderStringConstants();
|
renderer.renderStringConstants();
|
||||||
renderer.renderCompatibilityStubs();
|
renderer.renderCompatibilityStubs();
|
||||||
|
|
||||||
runtimeRenderer.renderHandWrittenRuntime("long.js");
|
|
||||||
if (threadLibraryUsed) {
|
|
||||||
runtimeRenderer.renderHandWrittenRuntime("thread.js");
|
|
||||||
} else {
|
|
||||||
runtimeRenderer.renderHandWrittenRuntime("simpleThread.js");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var entry : controller.getEntryPoints().entrySet()) {
|
for (var entry : controller.getEntryPoints().entrySet()) {
|
||||||
sourceWriter.appendFunction("$rt_exports").append(".").append(entry.getKey()).ws().append("=").ws();
|
rememberingWriter.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);
|
rememberingWriter.appendFunction("$rt_mainStarter").append("(").appendMethodBody(ref);
|
||||||
sourceWriter.append(");").newLine();
|
rememberingWriter.append(");").newLine();
|
||||||
sourceWriter.appendFunction("$rt_exports").append(".").append(entry.getKey()).append(".")
|
rememberingWriter.appendFunction("$rt_exports").append(".").append(entry.getKey()).append(".")
|
||||||
.append("javaException").ws().append("=").ws().appendFunction("$rt_javaException")
|
.append("javaException").ws().append("=").ws().appendFunction("$rt_javaException")
|
||||||
.append(";").newLine();
|
.append(";").newLine();
|
||||||
}
|
}
|
||||||
|
@ -494,11 +447,36 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
for (var listener : rendererListeners) {
|
for (var listener : rendererListeners) {
|
||||||
listener.complete();
|
listener.complete();
|
||||||
}
|
}
|
||||||
|
var epilogue = rememberingWriter.save();
|
||||||
|
rememberingWriter.clear();
|
||||||
|
|
||||||
|
var frequencyEstimator = new NameFrequencyEstimator();
|
||||||
|
declarations.replay(frequencyEstimator, RememberedSource.FILTER_REF);
|
||||||
|
epilogue.replay(frequencyEstimator, RememberedSource.FILTER_REF);
|
||||||
|
frequencyEstimator.apply(naming);
|
||||||
|
|
||||||
|
var sourceWriter = builder.build(writer);
|
||||||
|
sourceWriter.setDebugInformationEmitter(debugEmitterToUse);
|
||||||
|
printWrapperStart(sourceWriter);
|
||||||
|
|
||||||
|
int start = sourceWriter.getOffset();
|
||||||
|
|
||||||
|
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter);
|
||||||
|
runtimeRenderer.renderRuntime();
|
||||||
|
runtimeRenderer.renderHandWrittenRuntime("long.js");
|
||||||
|
if (renderer.isThreadLibraryUsed()) {
|
||||||
|
runtimeRenderer.renderHandWrittenRuntime("thread.js");
|
||||||
|
} else {
|
||||||
|
runtimeRenderer.renderHandWrittenRuntime("simpleThread.js");
|
||||||
|
}
|
||||||
|
declarations.write(sourceWriter, 0);
|
||||||
|
runtimeRenderer.renderHandWrittenRuntime("array.js");
|
||||||
|
epilogue.write(sourceWriter, 0);
|
||||||
|
|
||||||
printWrapperEnd(sourceWriter);
|
printWrapperEnd(sourceWriter);
|
||||||
|
|
||||||
int totalSize = sourceWriter.getOffset() - start;
|
int totalSize = sourceWriter.getOffset() - start;
|
||||||
printStats(renderer, totalSize);
|
printStats(sourceWriter, totalSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printWrapperStart(SourceWriter writer) {
|
private void printWrapperStart(SourceWriter writer) {
|
||||||
|
@ -560,19 +538,21 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
writer.outdent().append("}));").newLine();
|
writer.outdent().append("}));").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printStats(Renderer renderer, int totalSize) {
|
private void printStats(OutputSourceWriter writer, int totalSize) {
|
||||||
if (!Boolean.parseBoolean(System.getProperty("teavm.js.stats", "false"))) {
|
if (!Boolean.parseBoolean(System.getProperty("teavm.js.stats", "false"))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Total output size: " + STATS_NUM_FORMAT.format(totalSize));
|
System.out.println("Total output size: " + STATS_NUM_FORMAT.format(totalSize));
|
||||||
System.out.println("Metadata size: " + getSizeWithPercentage(renderer.getMetadataSize(), totalSize));
|
System.out.println("Metadata size: " + getSizeWithPercentage(
|
||||||
System.out.println("String pool size: " + getSizeWithPercentage(renderer.getStringPoolSize(), totalSize));
|
writer.getSectionSize(Renderer.SECTION_METADATA), totalSize));
|
||||||
|
System.out.println("String pool size: " + getSizeWithPercentage(
|
||||||
|
writer.getSectionSize(Renderer.SECTION_STRING_POOL), totalSize));
|
||||||
|
|
||||||
ObjectIntMap<String> packageSizeMap = new ObjectIntHashMap<>();
|
var packageSizeMap = new ObjectIntHashMap<String>();
|
||||||
for (String className : renderer.getClassesInStats()) {
|
for (String className : writer.getClassesInStats()) {
|
||||||
String packageName = className.substring(0, className.lastIndexOf('.') + 1);
|
String packageName = className.substring(0, className.lastIndexOf('.') + 1);
|
||||||
int classSize = renderer.getClassSize(className);
|
int classSize = writer.getClassSize(className);
|
||||||
packageSizeMap.put(packageName, packageSizeMap.getOrDefault(packageName, 0) + classSize);
|
packageSizeMap.put(packageName, packageSizeMap.getOrDefault(packageName, 0) + classSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,222 +568,6 @@ 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 List<PreparedClass> modelToAst(ListableClassHolderSource classes, RenderingContext context) {
|
|
||||||
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(),
|
|
||||||
controller.getDependencyInfo());
|
|
||||||
asyncFinder.find(classes);
|
|
||||||
asyncMethods.addAll(asyncFinder.getAsyncMethods());
|
|
||||||
asyncFamilyMethods.addAll(asyncFinder.getAsyncFamilyMethods());
|
|
||||||
Set<MethodReference> splitMethods = new HashSet<>(asyncMethods);
|
|
||||||
splitMethods.addAll(asyncFamilyMethods);
|
|
||||||
|
|
||||||
Decompiler decompiler = new Decompiler(classes, splitMethods, controller.isFriendlyToDebugger());
|
|
||||||
|
|
||||||
List<PreparedClass> classNodes = new ArrayList<>();
|
|
||||||
for (String className : getClassOrdering(classes)) {
|
|
||||||
ClassHolder cls = classes.get(className);
|
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
|
||||||
preprocessNativeMethod(method);
|
|
||||||
if (controller.wasCancelled()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getClassOrdering(ListableClassHolderSource classes) {
|
|
||||||
List<String> sequence = new ArrayList<>();
|
|
||||||
Set<String> visited = new HashSet<>();
|
|
||||||
for (String className : classes.getClassNames()) {
|
|
||||||
orderClasses(classes, className, visited, sequence);
|
|
||||||
}
|
|
||||||
return sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void orderClasses(ClassHolderSource classes, String className, Set<String> visited, List<String> order) {
|
|
||||||
if (!visited.add(className)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ClassHolder cls = classes.get(className);
|
|
||||||
if (cls == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (cls.getParent() != null) {
|
|
||||||
orderClasses(classes, cls.getParent(), visited, order);
|
|
||||||
}
|
|
||||||
for (String iface : cls.getInterfaces()) {
|
|
||||||
orderClasses(classes, iface, visited, order);
|
|
||||||
}
|
|
||||||
order.add(className);
|
|
||||||
}
|
|
||||||
|
|
||||||
private PreparedClass prepare(Decompiler decompiler, ClassHolder cls, ClassReaderSource classes) {
|
|
||||||
PreparedClass clsNode = new PreparedClass(cls);
|
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
|
||||||
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ((!isBootstrap() && method.getAnnotations().get(InjectedBy.class.getName()) != null)
|
|
||||||
|| methodInjectors.containsKey(method.getReference())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!method.hasModifier(ElementModifier.NATIVE) && !method.hasProgram()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
PreparedMethod preparedMethod = method.hasModifier(ElementModifier.NATIVE)
|
|
||||||
? prepareNative(method, classes)
|
|
||||||
: prepare(decompiler, method);
|
|
||||||
clsNode.getMethods().add(preparedMethod);
|
|
||||||
}
|
|
||||||
return clsNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PreparedMethod prepareNative(MethodHolder method, ClassReaderSource classes) {
|
|
||||||
MethodReference reference = method.getReference();
|
|
||||||
Generator generator = methodGenerators.get(reference);
|
|
||||||
if (generator == null && !isBootstrap()) {
|
|
||||||
AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName());
|
|
||||||
if (annotHolder == null) {
|
|
||||||
throw new DecompilationException("Method " + method.getOwnerName() + "." + method.getDescriptor()
|
|
||||||
+ " is native, but no " + GeneratedBy.class.getName() + " annotation found");
|
|
||||||
}
|
|
||||||
ValueType annotValue = annotHolder.getValues().get("value").getJavaClass();
|
|
||||||
String generatorClassName = ((ValueType.Object) annotValue).getClassName();
|
|
||||||
generator = generatorCache.computeIfAbsent(generatorClassName,
|
|
||||||
name -> createGenerator(name, method, classes));
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
Class<?> generatorClass;
|
|
||||||
try {
|
|
||||||
generatorClass = Class.forName(name, true, controller.getClassLoader());
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
throw new DecompilationException("Error instantiating generator " + name
|
|
||||||
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
|
||||||
}
|
|
||||||
|
|
||||||
var constructors = generatorClass.getConstructors();
|
|
||||||
if (constructors.length != 1) {
|
|
||||||
throw new DecompilationException("Error instantiating generator " + name
|
|
||||||
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
|
||||||
}
|
|
||||||
|
|
||||||
var constructor = constructors[0];
|
|
||||||
var parameterTypes = constructor.getParameterTypes();
|
|
||||||
var arguments = new Object[parameterTypes.length];
|
|
||||||
for (var i = 0; i < arguments.length; ++i) {
|
|
||||||
var parameterType = parameterTypes[i];
|
|
||||||
if (parameterType.equals(ClassReaderSource.class)) {
|
|
||||||
arguments[i] = classes;
|
|
||||||
} else if (parameterType.equals(Properties.class)) {
|
|
||||||
arguments[i] = controller.getProperties();
|
|
||||||
} else if (parameterType.equals(DependencyInfo.class)) {
|
|
||||||
arguments[i] = controller.getDependencyInfo();
|
|
||||||
} else if (parameterType.equals(ServiceRepository.class)) {
|
|
||||||
arguments[i] = controller.getServices();
|
|
||||||
} else if (parameterType.equals(JavaScriptTemplateFactory.class)) {
|
|
||||||
arguments[i] = templateFactory;
|
|
||||||
} else {
|
|
||||||
var service = controller.getServices().getService(parameterType);
|
|
||||||
if (service == null) {
|
|
||||||
throw new DecompilationException("Error instantiating generator " + name
|
|
||||||
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor() + ". "
|
|
||||||
+ "Its constructor requires " + parameterType + " as its parameter #" + (i + 1)
|
|
||||||
+ " which is not available.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return (Generator) constructor.newInstance(arguments);
|
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new DecompilationException("Error instantiating generator " + name
|
|
||||||
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private PreparedMethod prepare(Decompiler decompiler, MethodHolder method) {
|
|
||||||
MethodReference reference = method.getReference();
|
|
||||||
ControlFlowEntry[] cfg;
|
|
||||||
MethodNode node;
|
|
||||||
var async = asyncMethods.contains(reference);
|
|
||||||
if (async) {
|
|
||||||
node = decompileAsync(decompiler, method);
|
|
||||||
cfg = ProgramUtils.getLocationCFG(method.getProgram());
|
|
||||||
} else {
|
|
||||||
var entry = decompileRegular(decompiler, method);
|
|
||||||
node = entry.method;
|
|
||||||
cfg = entry.cfg;
|
|
||||||
}
|
|
||||||
bodyRenderer.render(node, async);
|
|
||||||
var result = new PreparedMethod(method.getLevel(), method.getModifiers(), method.getReference(),
|
|
||||||
bodyRenderer.getBody(), bodyRenderer.getParameters(), async, cfg, bodyRenderer.getVariables());
|
|
||||||
threadLibraryUsed |= bodyRenderer.isThreadLibraryUsed();
|
|
||||||
bodyRenderer.clear();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AstCacheEntry decompileRegular(Decompiler decompiler, MethodHolder method) {
|
|
||||||
if (astCache == null) {
|
|
||||||
return decompileRegularCacheMiss(decompiler, method);
|
|
||||||
}
|
|
||||||
|
|
||||||
CacheStatus cacheStatus = controller.getCacheStatus();
|
|
||||||
AstCacheEntry entry = !cacheStatus.isStaleMethod(method.getReference())
|
|
||||||
? astCache.get(method.getReference(), cacheStatus)
|
|
||||||
: null;
|
|
||||||
if (entry == null) {
|
|
||||||
entry = decompileRegularCacheMiss(decompiler, method);
|
|
||||||
RegularMethodNode finalNode = entry.method;
|
|
||||||
astCache.store(method.getReference(), entry, () -> dependencyExtractor.extract(finalNode));
|
|
||||||
}
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AstCacheEntry decompileRegularCacheMiss(Decompiler decompiler, MethodHolder method) {
|
|
||||||
RegularMethodNode node = decompiler.decompileRegular(method);
|
|
||||||
ControlFlowEntry[] cfg = LocationGraphBuilder.build(node.getBody());
|
|
||||||
return new AstCacheEntry(node, cfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AsyncMethodNode decompileAsync(Decompiler decompiler, MethodHolder method) {
|
|
||||||
if (astCache == null) {
|
|
||||||
return decompiler.decompileAsync(method);
|
|
||||||
}
|
|
||||||
|
|
||||||
CacheStatus cacheStatus = controller.getCacheStatus();
|
|
||||||
AsyncMethodNode node = !cacheStatus.isStaleMethod(method.getReference())
|
|
||||||
? astCache.getAsync(method.getReference(), cacheStatus)
|
|
||||||
: null;
|
|
||||||
if (node == null) {
|
|
||||||
node = decompiler.decompileAsync(method);
|
|
||||||
AsyncMethodNode finalNode = node;
|
|
||||||
astCache.storeAsync(method.getReference(), node, () -> dependencyExtractor.extract(finalNode));
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void preprocessNativeMethod(MethodHolder method) {
|
private void preprocessNativeMethod(MethodHolder method) {
|
||||||
if (!method.getModifiers().contains(ElementModifier.NATIVE)
|
if (!method.getModifiers().contains(ElementModifier.NATIVE)
|
||||||
|| methodGenerators.get(method.getReference()) != null
|
|| methodGenerators.get(method.getReference()) != null
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 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 NameFrequencyConsumer {
|
|
||||||
void consume(MethodReference method);
|
|
||||||
|
|
||||||
void consumeInit(MethodReference method);
|
|
||||||
|
|
||||||
void consume(MethodDescriptor method);
|
|
||||||
|
|
||||||
void consume(String className);
|
|
||||||
|
|
||||||
void consumeClassInit(String className);
|
|
||||||
|
|
||||||
void consume(FieldReference field);
|
|
||||||
|
|
||||||
void consumeStatic(FieldReference field);
|
|
||||||
|
|
||||||
void consumeFunction(String name);
|
|
||||||
|
|
||||||
void consumeGlobal(String name);
|
|
||||||
}
|
|
|
@ -1,148 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 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 java.util.*;
|
|
||||||
import org.teavm.model.FieldReference;
|
|
||||||
import org.teavm.model.MethodDescriptor;
|
|
||||||
import org.teavm.model.MethodReference;
|
|
||||||
|
|
||||||
public class NamingOrderer implements NameFrequencyConsumer {
|
|
||||||
private Map<String, Entry> entries = new HashMap<>();
|
|
||||||
private Set<String> reservedNames = new HashSet<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void consume(MethodReference method) {
|
|
||||||
String key = "R:" + method;
|
|
||||||
Entry entry = entries.get(key);
|
|
||||||
if (entry == null) {
|
|
||||||
entry = new Entry();
|
|
||||||
entry.operation = naming -> naming.getFullNameFor(method);
|
|
||||||
entries.put(key, entry);
|
|
||||||
}
|
|
||||||
entry.frequency++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void consumeInit(MethodReference method) {
|
|
||||||
String key = "I:" + method;
|
|
||||||
Entry entry = entries.get(key);
|
|
||||||
if (entry == null) {
|
|
||||||
entry = new Entry();
|
|
||||||
entry.operation = naming -> naming.getNameForInit(method);
|
|
||||||
entries.put(key, entry);
|
|
||||||
}
|
|
||||||
entry.frequency++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void consume(MethodDescriptor method) {
|
|
||||||
String key = "r:" + method;
|
|
||||||
Entry entry = entries.get(key);
|
|
||||||
if (entry == null) {
|
|
||||||
entry = new Entry();
|
|
||||||
entry.operation = naming -> naming.getNameFor(method);
|
|
||||||
entries.put(key, entry);
|
|
||||||
}
|
|
||||||
entry.frequency++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void consume(String className) {
|
|
||||||
String key = "c:" + className;
|
|
||||||
Entry entry = entries.get(key);
|
|
||||||
if (entry == null) {
|
|
||||||
entry = new Entry();
|
|
||||||
entry.operation = naming -> naming.getNameFor(className);
|
|
||||||
entries.put(key, entry);
|
|
||||||
}
|
|
||||||
entry.frequency++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void consumeClassInit(String className) {
|
|
||||||
String key = "C:" + className;
|
|
||||||
Entry entry = entries.get(key);
|
|
||||||
if (entry == null) {
|
|
||||||
entry = new Entry();
|
|
||||||
entry.operation = naming -> naming.getNameForClassInit(className);
|
|
||||||
entries.put(key, entry);
|
|
||||||
}
|
|
||||||
entry.frequency++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void consume(FieldReference field) {
|
|
||||||
String key = "f:" + field;
|
|
||||||
Entry entry = entries.get(key);
|
|
||||||
if (entry == null) {
|
|
||||||
entry = new Entry();
|
|
||||||
entry.operation = naming -> naming.getNameFor(field);
|
|
||||||
entries.put(key, entry);
|
|
||||||
}
|
|
||||||
entry.frequency++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void consumeStatic(FieldReference field) {
|
|
||||||
var key = "sf:" + field;
|
|
||||||
var entry = entries.get(key);
|
|
||||||
if (entry == null) {
|
|
||||||
entry = new Entry();
|
|
||||||
entry.operation = naming -> naming.getFullNameFor(field);
|
|
||||||
entries.put(key, entry);
|
|
||||||
}
|
|
||||||
entry.frequency++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void consumeFunction(String name) {
|
|
||||||
String key = "n:" + name;
|
|
||||||
Entry entry = entries.get(key);
|
|
||||||
if (entry == null) {
|
|
||||||
entry = new Entry();
|
|
||||||
entry.operation = naming -> naming.getNameForFunction(name);
|
|
||||||
entries.put(key, entry);
|
|
||||||
}
|
|
||||||
entry.frequency++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void consumeGlobal(String name) {
|
|
||||||
reservedNames.add(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void apply(NamingStrategy naming) {
|
|
||||||
for (var name : reservedNames) {
|
|
||||||
naming.reserveName(name);
|
|
||||||
}
|
|
||||||
List<Entry> entryList = new ArrayList<>(entries.values());
|
|
||||||
Collections.sort(entryList, (o1, o2) -> Integer.compare(o2.frequency, o1.frequency));
|
|
||||||
for (Entry entry : entryList) {
|
|
||||||
entry.operation.perform(naming);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Entry {
|
|
||||||
NamingOperation operation;
|
|
||||||
int frequency;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NamingOperation {
|
|
||||||
void perform(NamingStrategy naming);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,7 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.javascript.codegen;
|
package org.teavm.backend.javascript.codegen;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntIntHashMap;
|
||||||
|
import com.carrotsearch.hppc.IntIntMap;
|
||||||
|
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||||
|
import com.carrotsearch.hppc.ObjectIntMap;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
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.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
|
@ -33,6 +39,12 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
|
||||||
private int line;
|
private int line;
|
||||||
private int offset;
|
private int offset;
|
||||||
private DebugInformationEmitter debugInformationEmitter = new DummyDebugInformationEmitter();
|
private DebugInformationEmitter debugInformationEmitter = new DummyDebugInformationEmitter();
|
||||||
|
private String classMarkClass;
|
||||||
|
private int classMarkPos;
|
||||||
|
private ObjectIntMap<String> classSizes = new ObjectIntHashMap<>();
|
||||||
|
private int sectionMarkSection = -1;
|
||||||
|
private int sectionMarkPos;
|
||||||
|
private IntIntMap sectionSizes = new IntIntHashMap();
|
||||||
|
|
||||||
OutputSourceWriter(NamingStrategy naming, Appendable innerWriter, int lineWidth) {
|
OutputSourceWriter(NamingStrategy naming, Appendable innerWriter, int lineWidth) {
|
||||||
this.naming = naming;
|
this.naming = naming;
|
||||||
|
@ -140,9 +152,6 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
private SourceWriter appendName(ScopedName name) {
|
private SourceWriter appendName(ScopedName name) {
|
||||||
if (name.scoped) {
|
|
||||||
append(naming.getScopeName()).append(".");
|
|
||||||
}
|
|
||||||
append(name.value);
|
append(name.value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -271,6 +280,12 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SourceWriter emitVariables(String[] names, String jsName) {
|
||||||
|
debugInformationEmitter.emitVariable(names, jsName);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emitMethod(MethodDescriptor method) {
|
public void emitMethod(MethodDescriptor method) {
|
||||||
debugInformationEmitter.emitMethod(method);
|
debugInformationEmitter.emitMethod(method);
|
||||||
|
@ -295,4 +310,56 @@ public class OutputSourceWriter extends SourceWriter implements LocationProvider
|
||||||
public int getOffset() {
|
public int getOffset() {
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markClassStart(String className) {
|
||||||
|
classMarkClass = className;
|
||||||
|
classMarkPos = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markClassEnd() {
|
||||||
|
if (classMarkClass != null) {
|
||||||
|
var size = offset - classMarkPos;
|
||||||
|
if (size > 0) {
|
||||||
|
var currentSize = classSizes.get(classMarkClass);
|
||||||
|
classSizes.put(classMarkClass, currentSize + size);
|
||||||
|
}
|
||||||
|
classMarkClass = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markSectionStart(int id) {
|
||||||
|
sectionMarkSection = id;
|
||||||
|
sectionMarkPos = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markSectionEnd() {
|
||||||
|
if (sectionMarkSection >= 0) {
|
||||||
|
int size = offset - sectionMarkPos;
|
||||||
|
if (size > 0) {
|
||||||
|
var currentSize = sectionSizes.get(sectionMarkSection);
|
||||||
|
sectionSizes.put(sectionMarkSection, currentSize + size);
|
||||||
|
}
|
||||||
|
sectionMarkSection = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<String> getClassesInStats() {
|
||||||
|
var result = new ArrayList<String>();
|
||||||
|
for (var cursor : classSizes.keys()) {
|
||||||
|
result.add(cursor.value);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getClassSize(String className) {
|
||||||
|
return classSizes.get(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSectionSize(int sectionId) {
|
||||||
|
return sectionSizes.get(sectionId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,8 @@ public class RememberedSource implements SourceFragment {
|
||||||
public static final int FILTER_TEXT = 1;
|
public static final int FILTER_TEXT = 1;
|
||||||
public static final int FILTER_REF = 2;
|
public static final int FILTER_REF = 2;
|
||||||
public static final int FILTER_DEBUG = 4;
|
public static final int FILTER_DEBUG = 4;
|
||||||
public static final int FILTER_ALL = FILTER_TEXT | FILTER_REF | FILTER_DEBUG;
|
public static final int FILTER_STATS = 8;
|
||||||
|
public static final int FILTER_ALL = FILTER_TEXT | FILTER_REF | FILTER_DEBUG | FILTER_STATS;
|
||||||
|
|
||||||
private byte[] commands;
|
private byte[] commands;
|
||||||
private String chars;
|
private String chars;
|
||||||
|
@ -196,6 +197,20 @@ public class RememberedSource implements SourceFragment {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RememberingSourceWriter.EMIT_VARIABLES:
|
||||||
|
var count = intArgs[intArgIndex++];
|
||||||
|
if ((filter & FILTER_DEBUG) != 0) {
|
||||||
|
var names = new String[count];
|
||||||
|
for (var i = 0; i < count; ++i) {
|
||||||
|
names[i] = strings[intArgs[intArgIndex++]];
|
||||||
|
}
|
||||||
|
var jsName = strings[intArgs[intArgIndex++]];
|
||||||
|
sink.emitVariables(names, jsName);
|
||||||
|
} else {
|
||||||
|
intArgIndex += count + 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case RememberingSourceWriter.EMIT_CLASS:
|
case RememberingSourceWriter.EMIT_CLASS:
|
||||||
if ((filter & FILTER_DEBUG) != 0) {
|
if ((filter & FILTER_DEBUG) != 0) {
|
||||||
var classIndex = intArgs[intArgIndex];
|
var classIndex = intArgs[intArgIndex];
|
||||||
|
@ -211,6 +226,32 @@ public class RememberedSource implements SourceFragment {
|
||||||
}
|
}
|
||||||
intArgIndex++;
|
intArgIndex++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RememberingSourceWriter.MARK_CLASS_START:
|
||||||
|
if ((filter & FILTER_STATS) != 0) {
|
||||||
|
sink.markClassStart(strings[intArgs[intArgIndex]]);
|
||||||
|
}
|
||||||
|
intArgIndex++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RememberingSourceWriter.MARK_CLASS_END:
|
||||||
|
if ((filter & FILTER_STATS) != 0) {
|
||||||
|
sink.markClassEnd();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RememberingSourceWriter.MARK_SECTION_START:
|
||||||
|
if ((filter & FILTER_STATS) != 0) {
|
||||||
|
sink.markSectionStart(intArgs[intArgIndex]);
|
||||||
|
}
|
||||||
|
intArgIndex++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RememberingSourceWriter.MARK_SECTION_END:
|
||||||
|
if ((filter & FILTER_STATS) != 0) {
|
||||||
|
sink.markSectionEnd();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,13 @@ public class RememberingSourceWriter extends SourceWriter {
|
||||||
static final byte ENTER_LOCATION = 16;
|
static final byte ENTER_LOCATION = 16;
|
||||||
static final byte EXIT_LOCATION = 17;
|
static final byte EXIT_LOCATION = 17;
|
||||||
static final byte EMIT_STATEMENT_START = 18;
|
static final byte EMIT_STATEMENT_START = 18;
|
||||||
|
static final byte EMIT_VARIABLES = 26;
|
||||||
static final byte EMIT_METHOD = 19;
|
static final byte EMIT_METHOD = 19;
|
||||||
static final byte EMIT_CLASS = 20;
|
static final byte EMIT_CLASS = 20;
|
||||||
|
static final byte MARK_CLASS_START = 22;
|
||||||
|
static final byte MARK_CLASS_END = 23;
|
||||||
|
static final byte MARK_SECTION_START = 24;
|
||||||
|
static final byte MARK_SECTION_END = 25;
|
||||||
|
|
||||||
private boolean debug;
|
private boolean debug;
|
||||||
|
|
||||||
|
@ -262,6 +267,20 @@ public class RememberingSourceWriter extends SourceWriter {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SourceWriter emitVariables(String[] names, String jsName) {
|
||||||
|
if (debug) {
|
||||||
|
flush();
|
||||||
|
commands.add(EMIT_VARIABLES);
|
||||||
|
intArgs.add(names.length);
|
||||||
|
for (var name : names) {
|
||||||
|
appendStringArg(name);
|
||||||
|
}
|
||||||
|
appendStringArg(jsName);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emitMethod(MethodDescriptor method) {
|
public void emitMethod(MethodDescriptor method) {
|
||||||
if (!debug) {
|
if (!debug) {
|
||||||
|
@ -290,6 +309,32 @@ public class RememberingSourceWriter extends SourceWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markClassStart(String className) {
|
||||||
|
flush();
|
||||||
|
commands.add(MARK_CLASS_START);
|
||||||
|
appendStringArg(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markClassEnd() {
|
||||||
|
flush();
|
||||||
|
commands.add(MARK_CLASS_END);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markSectionStart(int id) {
|
||||||
|
flush();
|
||||||
|
commands.add(MARK_SECTION_START);
|
||||||
|
intArgs.add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markSectionEnd() {
|
||||||
|
flush();
|
||||||
|
commands.add(MARK_SECTION_END);
|
||||||
|
}
|
||||||
|
|
||||||
public void flush() {
|
public void flush() {
|
||||||
if (lastWrittenChar == sb.length()) {
|
if (lastWrittenChar == sb.length()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -138,9 +138,24 @@ public abstract class SourceWriter implements Appendable, SourceWriterSink {
|
||||||
@Override
|
@Override
|
||||||
public abstract SourceWriter emitStatementStart();
|
public abstract SourceWriter emitStatementStart();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract SourceWriter emitVariables(String[] names, String jsName);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract void emitMethod(MethodDescriptor method);
|
public abstract void emitMethod(MethodDescriptor method);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract void emitClass(String className);
|
public abstract void emitClass(String className);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract void markClassStart(String className);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract void markClassEnd();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract void markSectionStart(int id);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract void markSectionEnd();
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,9 +104,25 @@ public interface SourceWriterSink {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default SourceWriterSink emitVariables(String[] names, String jsName) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
default void emitMethod(MethodDescriptor method) {
|
default void emitMethod(MethodDescriptor method) {
|
||||||
}
|
}
|
||||||
|
|
||||||
default void emitClass(String className) {
|
default void emitClass(String className) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void markClassStart(String className) {
|
||||||
|
}
|
||||||
|
|
||||||
|
default void markClassEnd() {
|
||||||
|
}
|
||||||
|
|
||||||
|
default void markSectionStart(int id) {
|
||||||
|
}
|
||||||
|
|
||||||
|
default void markSectionEnd() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019 Alexey Andreev.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.teavm.backend.javascript.decompile;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import org.teavm.model.ClassHolder;
|
|
||||||
|
|
||||||
public class PreparedClass {
|
|
||||||
private ClassHolder classHolder;
|
|
||||||
private List<PreparedMethod> methods = new ArrayList<>();
|
|
||||||
|
|
||||||
public PreparedClass(ClassHolder classHolder) {
|
|
||||||
this.classHolder = classHolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return classHolder.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getParentName() {
|
|
||||||
return classHolder.getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<PreparedMethod> getMethods() {
|
|
||||||
return methods;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClassHolder getClassHolder() {
|
|
||||||
return classHolder;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019 Alexey Andreev.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.teavm.backend.javascript.decompile;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
import org.teavm.ast.ControlFlowEntry;
|
|
||||||
import org.teavm.backend.javascript.codegen.RememberedSource;
|
|
||||||
import org.teavm.model.AccessLevel;
|
|
||||||
import org.teavm.model.ElementModifier;
|
|
||||||
import org.teavm.model.MethodReference;
|
|
||||||
|
|
||||||
public class PreparedMethod {
|
|
||||||
public final AccessLevel accessLevel;
|
|
||||||
public final Set<ElementModifier> modifiers;
|
|
||||||
public final MethodReference reference;
|
|
||||||
public final RememberedSource body;
|
|
||||||
public final RememberedSource parameters;
|
|
||||||
public final boolean async;
|
|
||||||
public final ControlFlowEntry[] cfg;
|
|
||||||
public final PreparedVariable[] variables;
|
|
||||||
|
|
||||||
public PreparedMethod(AccessLevel accessLevel, Set<ElementModifier> modifiers, MethodReference reference,
|
|
||||||
RememberedSource body, RememberedSource parameters, boolean async, ControlFlowEntry[] cfg,
|
|
||||||
PreparedVariable[] variables) {
|
|
||||||
this.accessLevel = accessLevel;
|
|
||||||
this.modifiers = modifiers;
|
|
||||||
this.reference = reference;
|
|
||||||
this.body = body;
|
|
||||||
this.parameters = parameters;
|
|
||||||
this.async = async;
|
|
||||||
this.cfg = cfg;
|
|
||||||
this.variables = variables;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023 Alexey Andreev.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.teavm.backend.javascript.decompile;
|
|
||||||
|
|
||||||
import org.teavm.debugging.information.DebugInformationEmitter;
|
|
||||||
|
|
||||||
public class PreparedVariable {
|
|
||||||
public final String[] names;
|
|
||||||
public final String jsName;
|
|
||||||
|
|
||||||
public PreparedVariable(String[] names, String jsName) {
|
|
||||||
this.names = names;
|
|
||||||
this.jsName = jsName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void emit(DebugInformationEmitter emitter) {
|
|
||||||
emitter.emitVariable(names, jsName);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,10 +25,7 @@ import org.teavm.ast.MethodNode;
|
||||||
import org.teavm.ast.MethodNodeVisitor;
|
import org.teavm.ast.MethodNodeVisitor;
|
||||||
import org.teavm.ast.RegularMethodNode;
|
import org.teavm.ast.RegularMethodNode;
|
||||||
import org.teavm.ast.VariableNode;
|
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.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.decompile.PreparedVariable;
|
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||||
import org.teavm.dependency.DependencyInfo;
|
import org.teavm.dependency.DependencyInfo;
|
||||||
|
@ -45,20 +42,17 @@ public class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
|
||||||
private boolean minifying;
|
private boolean minifying;
|
||||||
private boolean async;
|
private boolean async;
|
||||||
private Set<MethodReference> asyncMethods;
|
private Set<MethodReference> asyncMethods;
|
||||||
private RememberingSourceWriter writer;
|
private SourceWriter writer;
|
||||||
private StatementRenderer statementRenderer;
|
private StatementRenderer statementRenderer;
|
||||||
private boolean threadLibraryUsed;
|
private boolean threadLibraryUsed;
|
||||||
private RememberedSource body;
|
|
||||||
private RememberedSource parameters;
|
|
||||||
private PreparedVariable[] variables;
|
|
||||||
|
|
||||||
public MethodBodyRenderer(RenderingContext context, Diagnostics diagnostics, boolean minifying, boolean debug,
|
public MethodBodyRenderer(RenderingContext context, Diagnostics diagnostics, boolean minifying,
|
||||||
Set<MethodReference> asyncMethods) {
|
Set<MethodReference> asyncMethods, SourceWriter writer) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.diagnostics = diagnostics;
|
this.diagnostics = diagnostics;
|
||||||
this.minifying = minifying;
|
this.minifying = minifying;
|
||||||
this.asyncMethods = asyncMethods;
|
this.asyncMethods = asyncMethods;
|
||||||
writer = new RememberingSourceWriter(debug);
|
this.writer = writer;
|
||||||
statementRenderer = new StatementRenderer(context, writer);
|
statementRenderer = new StatementRenderer(context, writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,15 +65,11 @@ public class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
|
||||||
return context.getDependencyInfo();
|
return context.getDependencyInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderNative(Generator generator, boolean async, MethodReference reference,
|
public void renderNative(Generator generator, boolean async, MethodReference reference) {
|
||||||
Set<ElementModifier> modifiers) {
|
|
||||||
threadLibraryUsed = false;
|
threadLibraryUsed = false;
|
||||||
this.async = async;
|
this.async = async;
|
||||||
statementRenderer.setAsync(async);
|
statementRenderer.setAsync(async);
|
||||||
renderParameters(reference, modifiers);
|
|
||||||
generator.generate(this, writer, reference);
|
generator.generate(this, writer, reference);
|
||||||
body = writer.save();
|
|
||||||
writer.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(MethodNode node, boolean async) {
|
public void render(MethodNode node, boolean async) {
|
||||||
|
@ -87,41 +77,18 @@ public class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
|
||||||
this.async = async;
|
this.async = async;
|
||||||
statementRenderer.setAsync(async);
|
statementRenderer.setAsync(async);
|
||||||
statementRenderer.setCurrentMethod(node);
|
statementRenderer.setCurrentMethod(node);
|
||||||
renderParameters(node.getReference(), node.getModifiers());
|
|
||||||
node.acceptVisitor(this);
|
|
||||||
body = writer.save();
|
|
||||||
prepareVariables(node);
|
prepareVariables(node);
|
||||||
writer.clear();
|
node.acceptVisitor(this);
|
||||||
}
|
|
||||||
|
|
||||||
public PreparedVariable[] getVariables() {
|
|
||||||
return variables;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareVariables(MethodNode method) {
|
private void prepareVariables(MethodNode method) {
|
||||||
var variables = new ArrayList<PreparedVariable>();
|
|
||||||
for (int i = 0; i < method.getVariables().size(); ++i) {
|
for (int i = 0; i < method.getVariables().size(); ++i) {
|
||||||
variables.add(new PreparedVariable(new String[] { method.getVariables().get(i).getName() },
|
writer.emitVariables(new String[] { method.getVariables().get(i).getName() },
|
||||||
statementRenderer.variableName(i)));
|
statementRenderer.variableName(i));
|
||||||
}
|
}
|
||||||
this.variables = variables.toArray(new PreparedVariable[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RememberedSource getBody() {
|
public void renderParameters(MethodReference reference, Set<ElementModifier> modifiers) {
|
||||||
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;
|
int startParam = 0;
|
||||||
if (modifiers.contains(ElementModifier.STATIC)) {
|
if (modifiers.contains(ElementModifier.STATIC)) {
|
||||||
startParam = 1;
|
startParam = 1;
|
||||||
|
@ -139,8 +106,6 @@ public class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
|
||||||
if (count != 1) {
|
if (count != 1) {
|
||||||
writer.append(")");
|
writer.append(")");
|
||||||
}
|
}
|
||||||
parameters = writer.save();
|
|
||||||
writer.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendMonitor(StatementRenderer statementRenderer, MethodNode methodNode) {
|
private void appendMonitor(StatementRenderer statementRenderer, MethodNode methodNode) {
|
||||||
|
|
|
@ -15,22 +15,18 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.javascript.rendering;
|
package org.teavm.backend.javascript.rendering;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.backend.javascript.codegen.NameFrequencyConsumer;
|
import org.teavm.backend.javascript.codegen.NamingStrategy;
|
||||||
import org.teavm.backend.javascript.codegen.RememberedSource;
|
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriterSink;
|
import org.teavm.backend.javascript.codegen.SourceWriterSink;
|
||||||
import org.teavm.backend.javascript.decompile.PreparedClass;
|
|
||||||
import org.teavm.backend.javascript.decompile.PreparedMethod;
|
|
||||||
import org.teavm.model.ClassReaderSource;
|
|
||||||
import org.teavm.model.ElementModifier;
|
|
||||||
import org.teavm.model.FieldHolder;
|
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodReader;
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
|
||||||
|
|
||||||
class NameFrequencyEstimator implements SourceWriterSink {
|
public 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,
|
||||||
|
@ -39,134 +35,137 @@ class NameFrequencyEstimator implements SourceWriterSink {
|
||||||
"monitorExit", Object.class, void.class);
|
"monitorExit", Object.class, void.class);
|
||||||
static final MethodReference MONITOR_EXIT_SYNC_METHOD = new MethodReference(Object.class,
|
static final MethodReference MONITOR_EXIT_SYNC_METHOD = new MethodReference(Object.class,
|
||||||
"monitorExitSync", Object.class, void.class);
|
"monitorExitSync", Object.class, void.class);
|
||||||
private static final MethodDescriptor CLINIT_METHOD = new MethodDescriptor("<clinit>", ValueType.VOID);
|
|
||||||
|
|
||||||
private final NameFrequencyConsumer consumer;
|
private Map<String, Entry> entries = new HashMap<>();
|
||||||
private final ClassReaderSource classSource;
|
private Set<String> reservedNames = new HashSet<>();
|
||||||
private final Set<MethodReference> asyncFamilyMethods;
|
|
||||||
|
|
||||||
NameFrequencyEstimator(NameFrequencyConsumer consumer, ClassReaderSource classSource,
|
|
||||||
Set<MethodReference> asyncFamilyMethods) {
|
|
||||||
this.consumer = consumer;
|
|
||||||
this.classSource = classSource;
|
|
||||||
this.asyncFamilyMethods = asyncFamilyMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void estimate(PreparedClass cls) {
|
|
||||||
// Declaration
|
|
||||||
consumer.consume(cls.getName());
|
|
||||||
if (cls.getParentName() != null) {
|
|
||||||
consumer.consume(cls.getParentName());
|
|
||||||
}
|
|
||||||
for (FieldHolder field : cls.getClassHolder().getFields()) {
|
|
||||||
consumer.consume(new FieldReference(cls.getName(), field.getName()));
|
|
||||||
if (field.getModifiers().contains(ElementModifier.STATIC)) {
|
|
||||||
consumer.consume(cls.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods
|
|
||||||
MethodReader clinit = classSource.get(cls.getName()).getMethod(CLINIT_METHOD);
|
|
||||||
for (PreparedMethod method : cls.getMethods()) {
|
|
||||||
consumer.consume(method.reference);
|
|
||||||
if (asyncFamilyMethods.contains(method.reference)) {
|
|
||||||
consumer.consume(method.reference);
|
|
||||||
}
|
|
||||||
if (clinit != null && (method.modifiers.contains(ElementModifier.STATIC)
|
|
||||||
|| method.reference.getName().equals("<init>"))) {
|
|
||||||
consumer.consume(method.reference);
|
|
||||||
}
|
|
||||||
if (!method.modifiers.contains(ElementModifier.STATIC)) {
|
|
||||||
consumer.consume(method.reference.getDescriptor());
|
|
||||||
consumer.consume(method.reference);
|
|
||||||
}
|
|
||||||
if (method.async) {
|
|
||||||
consumer.consumeFunction("$rt_nativeThread");
|
|
||||||
consumer.consumeFunction("$rt_nativeThread");
|
|
||||||
consumer.consumeFunction("$rt_resuming");
|
|
||||||
consumer.consumeFunction("$rt_invalidPointer");
|
|
||||||
}
|
|
||||||
|
|
||||||
method.body.replay(this, RememberedSource.FILTER_REF);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clinit != null) {
|
|
||||||
consumer.consumeFunction("$rt_eraseClinit");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metadata
|
|
||||||
consumer.consume(cls.getName());
|
|
||||||
consumer.consume(cls.getName());
|
|
||||||
if (cls.getParentName() != null) {
|
|
||||||
consumer.consume(cls.getParentName());
|
|
||||||
}
|
|
||||||
for (String iface : cls.getClassHolder().getInterfaces()) {
|
|
||||||
consumer.consume(iface);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasFields = false;
|
|
||||||
for (FieldHolder field : cls.getClassHolder().getFields()) {
|
|
||||||
if (!field.hasModifier(ElementModifier.STATIC)) {
|
|
||||||
hasFields = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasFields) {
|
|
||||||
consumer.consumeFunction("$rt_classWithoutFields");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceWriterSink appendClass(String cls) {
|
public SourceWriterSink appendClass(String cls) {
|
||||||
consumer.consume(cls);
|
var key = "c:" + cls;
|
||||||
|
var entry = entries.get(key);
|
||||||
|
if (entry == null) {
|
||||||
|
entry = new Entry();
|
||||||
|
entry.operation = naming -> naming.getNameFor(cls);
|
||||||
|
entries.put(key, entry);
|
||||||
|
}
|
||||||
|
entry.frequency++;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceWriterSink appendField(FieldReference field) {
|
public SourceWriterSink appendField(FieldReference field) {
|
||||||
consumer.consume(field);
|
var key = "f:" + field;
|
||||||
|
var entry = entries.get(key);
|
||||||
|
if (entry == null) {
|
||||||
|
entry = new Entry();
|
||||||
|
entry.operation = naming -> naming.getNameFor(field);
|
||||||
|
entries.put(key, entry);
|
||||||
|
}
|
||||||
|
entry.frequency++;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceWriterSink appendStaticField(FieldReference field) {
|
public SourceWriterSink appendStaticField(FieldReference field) {
|
||||||
consumer.consumeStatic(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++;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceWriterSink appendMethod(MethodDescriptor method) {
|
public SourceWriterSink appendMethod(MethodDescriptor method) {
|
||||||
consumer.consume(method);
|
var key = "r:" + method;
|
||||||
|
var entry = entries.get(key);
|
||||||
|
if (entry == null) {
|
||||||
|
entry = new Entry();
|
||||||
|
entry.operation = naming -> naming.getNameFor(method);
|
||||||
|
entries.put(key, entry);
|
||||||
|
}
|
||||||
|
entry.frequency++;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceWriterSink appendMethodBody(MethodReference method) {
|
public SourceWriterSink appendMethodBody(MethodReference method) {
|
||||||
consumer.consume(method);
|
var key = "R:" + method;
|
||||||
|
var entry = entries.get(key);
|
||||||
|
if (entry == null) {
|
||||||
|
entry = new Entry();
|
||||||
|
entry.operation = naming -> naming.getFullNameFor(method);
|
||||||
|
entries.put(key, entry);
|
||||||
|
}
|
||||||
|
entry.frequency++;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceWriterSink appendFunction(String name) {
|
public SourceWriterSink appendFunction(String name) {
|
||||||
consumer.consumeFunction(name);
|
var key = "n:" + name;
|
||||||
|
var entry = entries.get(key);
|
||||||
|
if (entry == null) {
|
||||||
|
entry = new Entry();
|
||||||
|
entry.operation = naming -> naming.getNameForFunction(name);
|
||||||
|
entries.put(key, entry);
|
||||||
|
}
|
||||||
|
entry.frequency++;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceWriterSink appendGlobal(String name) {
|
public SourceWriterSink appendGlobal(String name) {
|
||||||
consumer.consumeGlobal(name);
|
reservedNames.add(name);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceWriterSink appendInit(MethodReference method) {
|
public SourceWriterSink appendInit(MethodReference method) {
|
||||||
consumer.consumeInit(method);
|
var key = "I:" + method;
|
||||||
|
var entry = entries.get(key);
|
||||||
|
if (entry == null) {
|
||||||
|
entry = new Entry();
|
||||||
|
entry.operation = naming -> naming.getNameForInit(method);
|
||||||
|
entries.put(key, entry);
|
||||||
|
}
|
||||||
|
entry.frequency++;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceWriterSink appendClassInit(String className) {
|
public SourceWriterSink appendClassInit(String className) {
|
||||||
consumer.consumeClassInit(className);
|
var key = "C:" + className;
|
||||||
|
var entry = entries.get(key);
|
||||||
|
if (entry == null) {
|
||||||
|
entry = new Entry();
|
||||||
|
entry.operation = naming -> naming.getNameForClassInit(className);
|
||||||
|
entries.put(key, entry);
|
||||||
|
}
|
||||||
|
entry.frequency++;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void apply(NamingStrategy naming) {
|
||||||
|
for (var name : reservedNames) {
|
||||||
|
naming.reserveName(name);
|
||||||
|
}
|
||||||
|
var entryList = new ArrayList<>(entries.values());
|
||||||
|
entryList.sort((o1, o2) -> Integer.compare(o2.frequency, o1.frequency));
|
||||||
|
for (var entry : entryList) {
|
||||||
|
entry.operation.perform(naming);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Entry {
|
||||||
|
NamingOperation operation;
|
||||||
|
int frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface NamingOperation {
|
||||||
|
void perform(NamingStrategy naming);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.backend.javascript.rendering;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||||
import com.carrotsearch.hppc.ObjectIntMap;
|
import com.carrotsearch.hppc.ObjectIntMap;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -27,77 +28,84 @@ 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.backend.javascript.codegen.NamingOrderer;
|
import org.teavm.ast.AsyncMethodNode;
|
||||||
import org.teavm.backend.javascript.codegen.NamingStrategy;
|
import org.teavm.ast.ControlFlowEntry;
|
||||||
import org.teavm.backend.javascript.codegen.OutputSourceWriter;
|
import org.teavm.ast.MethodNode;
|
||||||
import org.teavm.backend.javascript.codegen.RememberedSource;
|
import org.teavm.ast.RegularMethodNode;
|
||||||
import org.teavm.backend.javascript.codegen.ScopedName;
|
import org.teavm.ast.analysis.LocationGraphBuilder;
|
||||||
|
import org.teavm.ast.decompilation.DecompilationException;
|
||||||
|
import org.teavm.ast.decompilation.Decompiler;
|
||||||
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.spi.GeneratedBy;
|
||||||
import org.teavm.backend.javascript.decompile.PreparedMethod;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
|
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
|
import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory;
|
||||||
|
import org.teavm.cache.AstCacheEntry;
|
||||||
|
import org.teavm.cache.AstDependencyExtractor;
|
||||||
|
import org.teavm.cache.CacheStatus;
|
||||||
|
import org.teavm.cache.MethodNodeCache;
|
||||||
import org.teavm.common.ServiceRepository;
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.debugging.information.DebugInformationEmitter;
|
import org.teavm.dependency.DependencyInfo;
|
||||||
import org.teavm.debugging.information.DummyDebugInformationEmitter;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.model.AccessLevel;
|
import org.teavm.model.AccessLevel;
|
||||||
|
import org.teavm.model.AnnotationHolder;
|
||||||
|
import org.teavm.model.ClassHolder;
|
||||||
|
import org.teavm.model.ClassHolderSource;
|
||||||
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;
|
||||||
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
import org.teavm.model.ListableClassReaderSource;
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
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;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.analysis.ClassMetadataRequirements;
|
import org.teavm.model.analysis.ClassMetadataRequirements;
|
||||||
|
import org.teavm.model.util.AsyncMethodFinder;
|
||||||
import org.teavm.vm.RenderingException;
|
import org.teavm.vm.RenderingException;
|
||||||
import org.teavm.vm.TeaVMProgressFeedback;
|
import org.teavm.vm.TeaVMProgressFeedback;
|
||||||
|
|
||||||
public class Renderer implements RenderingManager {
|
public class Renderer implements RenderingManager {
|
||||||
private final NamingStrategy naming;
|
public static final int SECTION_STRING_POOL = 0;
|
||||||
private final OutputSourceWriter writer;
|
public static final int SECTION_METADATA = 1;
|
||||||
|
|
||||||
|
private final SourceWriter writer;
|
||||||
private final ListableClassReaderSource classSource;
|
private final ListableClassReaderSource classSource;
|
||||||
private final ClassLoader classLoader;
|
private final ClassLoader classLoader;
|
||||||
private boolean minifying;
|
|
||||||
private final Properties properties = new Properties();
|
private final Properties properties = new Properties();
|
||||||
private final ServiceRepository services;
|
private final ServiceRepository services;
|
||||||
private DebugInformationEmitter debugEmitter = new DummyDebugInformationEmitter();
|
|
||||||
private final Set<MethodReference> asyncMethods;
|
private final Set<MethodReference> asyncMethods;
|
||||||
private final Set<MethodReference> asyncFamilyMethods;
|
|
||||||
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;
|
||||||
|
private MethodBodyRenderer methodBodyRenderer;
|
||||||
|
private Map<String, Generator> generatorCache = new HashMap<>();
|
||||||
|
private Map<MethodReference, Generator> generators;
|
||||||
|
private MethodNodeCache astCache;
|
||||||
|
private CacheStatus cacheStatus;
|
||||||
|
private JavaScriptTemplateFactory templateFactory;
|
||||||
|
private boolean threadLibraryUsed;
|
||||||
|
private AstDependencyExtractor dependencyExtractor = new AstDependencyExtractor();
|
||||||
public static final MethodDescriptor CLINIT_METHOD = new MethodDescriptor("<clinit>", ValueType.VOID);
|
public static final MethodDescriptor CLINIT_METHOD = new MethodDescriptor("<clinit>", ValueType.VOID);
|
||||||
|
|
||||||
private ObjectIntMap<String> sizeByClass = new ObjectIntHashMap<>();
|
public Renderer(SourceWriter writer, Set<MethodReference> asyncMethods, RenderingContext context,
|
||||||
private int stringPoolSize;
|
Diagnostics diagnostics, Map<MethodReference, Generator> generators,
|
||||||
private int metadataSize;
|
MethodNodeCache astCache, CacheStatus cacheStatus, JavaScriptTemplateFactory templateFactory) {
|
||||||
|
|
||||||
public Renderer(OutputSourceWriter writer, Set<MethodReference> asyncMethods,
|
|
||||||
Set<MethodReference> asyncFamilyMethods, RenderingContext context) {
|
|
||||||
this.naming = context.getNaming();
|
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
this.classSource = context.getClassSource();
|
this.classSource = context.getClassSource();
|
||||||
this.classLoader = context.getClassLoader();
|
this.classLoader = context.getClassLoader();
|
||||||
this.services = context.getServices();
|
this.services = context.getServices();
|
||||||
this.asyncMethods = new HashSet<>(asyncMethods);
|
this.asyncMethods = new HashSet<>(asyncMethods);
|
||||||
this.asyncFamilyMethods = new HashSet<>(asyncFamilyMethods);
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
methodBodyRenderer = new MethodBodyRenderer(context, diagnostics, context.isMinifying(), asyncMethods,
|
||||||
|
writer);
|
||||||
public int getStringPoolSize() {
|
this.generators = generators;
|
||||||
return stringPoolSize;
|
this.astCache = astCache;
|
||||||
}
|
this.cacheStatus = cacheStatus;
|
||||||
|
this.templateFactory = templateFactory;
|
||||||
public int getMetadataSize() {
|
|
||||||
return metadataSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getClassesInStats() {
|
|
||||||
return sizeByClass.keys().toArray(String.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getClassSize(String className) {
|
|
||||||
return sizeByClass.getOrDefault(className, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -105,18 +113,8 @@ public class Renderer implements RenderingManager {
|
||||||
return writer;
|
return writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isThreadLibraryUsed() {
|
||||||
public NamingStrategy getNaming() {
|
return threadLibraryUsed;
|
||||||
return naming;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMinifying() {
|
|
||||||
return minifying;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMinifying(boolean minifying) {
|
|
||||||
this.minifying = minifying;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -136,10 +134,6 @@ public class Renderer implements RenderingManager {
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDebugEmitter(DebugInformationEmitter debugEmitter) {
|
|
||||||
this.debugEmitter = debugEmitter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setProgressConsumer(IntFunction<TeaVMProgressFeedback> progressConsumer) {
|
public void setProgressConsumer(IntFunction<TeaVMProgressFeedback> progressConsumer) {
|
||||||
this.progressConsumer = progressConsumer;
|
this.progressConsumer = progressConsumer;
|
||||||
}
|
}
|
||||||
|
@ -153,7 +147,7 @@ public class Renderer implements RenderingManager {
|
||||||
if (context.getStringPool().isEmpty()) {
|
if (context.getStringPool().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int start = writer.getOffset();
|
writer.markSectionStart(SECTION_STRING_POOL);
|
||||||
writer.appendFunction("$rt_stringPool").append("([");
|
writer.appendFunction("$rt_stringPool").append("([");
|
||||||
for (int i = 0; i < context.getStringPool().size(); ++i) {
|
for (int i = 0; i < context.getStringPool().size(); ++i) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
|
@ -162,17 +156,16 @@ public class Renderer implements RenderingManager {
|
||||||
RenderingUtil.writeString(writer, context.getStringPool().get(i));
|
RenderingUtil.writeString(writer, context.getStringPool().get(i));
|
||||||
}
|
}
|
||||||
writer.append("]);").newLine();
|
writer.append("]);").newLine();
|
||||||
stringPoolSize = writer.getOffset() - start;
|
writer.markSectionEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderStringConstants() throws RenderingException {
|
public void renderStringConstants() throws RenderingException {
|
||||||
for (PostponedFieldInitializer initializer : postponedFieldInitializers) {
|
for (PostponedFieldInitializer initializer : postponedFieldInitializers) {
|
||||||
int start = writer.getOffset();
|
writer.markSectionStart(SECTION_STRING_POOL);
|
||||||
writer.appendStaticField(initializer.field).ws().append("=").ws();
|
writer.appendStaticField(initializer.field).ws().append("=").ws();
|
||||||
context.constantToString(writer, initializer.value);
|
context.constantToString(writer, initializer.value);
|
||||||
writer.append(";").softNewLine();
|
writer.append(";").softNewLine();
|
||||||
int sz = writer.getOffset() - start;
|
writer.markSectionEnd();
|
||||||
appendClassSize(initializer.field.getClassName(), sz);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,44 +200,58 @@ public class Renderer implements RenderingManager {
|
||||||
writer.outdent().append("};").newLine();
|
writer.outdent().append("};").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendClassSize(String className, int sz) {
|
public boolean render(ListableClassHolderSource classes, boolean isFriendlyToDebugger) {
|
||||||
sizeByClass.put(className, sizeByClass.getOrDefault(className, 0) + sz);
|
var sequence = new ArrayList<ClassHolder>();
|
||||||
}
|
var visited = new HashSet<String>();
|
||||||
|
for (String className : classes.getClassNames()) {
|
||||||
public void prepare(List<PreparedClass> classes) {
|
orderClasses(classes, className, visited, sequence);
|
||||||
if (minifying) {
|
|
||||||
NamingOrderer orderer = new NamingOrderer();
|
|
||||||
NameFrequencyEstimator estimator = new NameFrequencyEstimator(orderer, classSource,
|
|
||||||
asyncFamilyMethods);
|
|
||||||
for (PreparedClass cls : classes) {
|
|
||||||
estimator.estimate(cls);
|
|
||||||
}
|
|
||||||
naming.getScopeName();
|
|
||||||
orderer.apply(naming);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public boolean render(List<PreparedClass> classes) throws RenderingException {
|
var asyncFinder = new AsyncMethodFinder(context.getDependencyInfo().getCallGraph(),
|
||||||
|
context.getDependencyInfo());
|
||||||
|
asyncFinder.find(classes);
|
||||||
|
asyncMethods.addAll(asyncFinder.getAsyncMethods());
|
||||||
|
var splitMethods = new HashSet<>(asyncMethods);
|
||||||
|
splitMethods.addAll(asyncFinder.getAsyncFamilyMethods());
|
||||||
|
|
||||||
|
var decompiler = new Decompiler(classes, splitMethods, isFriendlyToDebugger);
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (PreparedClass cls : classes) {
|
for (var cls : sequence) {
|
||||||
int start = writer.getOffset();
|
writer.markClassStart(cls.getName());
|
||||||
renderDeclaration(cls);
|
renderDeclaration(cls);
|
||||||
renderMethodBodies(cls);
|
renderMethodBodies(cls, decompiler);
|
||||||
appendClassSize(cls.getName(), writer.getOffset() - start);
|
writer.markClassEnd();
|
||||||
if (progressConsumer.apply(1000 * ++index / classes.size()) == TeaVMProgressFeedback.CANCEL) {
|
if (progressConsumer.apply(1000 * ++index / sequence.size()) == TeaVMProgressFeedback.CANCEL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
renderClassMetadata(classes);
|
renderClassMetadata(sequence);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderDeclaration(PreparedClass cls) throws RenderingException {
|
private void orderClasses(ClassHolderSource classes, String className, Set<String> visited,
|
||||||
ScopedName jsName = naming.getNameFor(cls.getName());
|
List<ClassHolder> order) {
|
||||||
debugEmitter.addClass(jsName.value, cls.getName(), cls.getParentName());
|
if (!visited.add(className)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ClassHolder cls = classes.get(className);
|
||||||
|
if (cls == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cls.getParent() != null) {
|
||||||
|
orderClasses(classes, cls.getParent(), visited, order);
|
||||||
|
}
|
||||||
|
for (String iface : cls.getInterfaces()) {
|
||||||
|
orderClasses(classes, iface, visited, order);
|
||||||
|
}
|
||||||
|
order.add(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderDeclaration(ClassHolder cls) throws RenderingException {
|
||||||
List<FieldHolder> nonStaticFields = new ArrayList<>();
|
List<FieldHolder> nonStaticFields = new ArrayList<>();
|
||||||
List<FieldHolder> staticFields = new ArrayList<>();
|
List<FieldHolder> staticFields = new ArrayList<>();
|
||||||
for (FieldHolder field : cls.getClassHolder().getFields()) {
|
for (FieldHolder field : cls.getFields()) {
|
||||||
if (field.getModifiers().contains(ElementModifier.STATIC)) {
|
if (field.getModifiers().contains(ElementModifier.STATIC)) {
|
||||||
staticFields.add(field);
|
staticFields.add(field);
|
||||||
} else {
|
} else {
|
||||||
|
@ -252,12 +259,13 @@ public class Renderer implements RenderingManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nonStaticFields.isEmpty() && !cls.getClassHolder().getName().equals("java.lang.Object")) {
|
if (nonStaticFields.isEmpty() && !cls.getName().equals("java.lang.Object")) {
|
||||||
renderShortClassFunctionDeclaration(cls, jsName);
|
renderShortClassFunctionDeclaration(cls);
|
||||||
} else {
|
} else {
|
||||||
renderFullClassFunctionDeclaration(cls, jsName, nonStaticFields);
|
renderFullClassFunctionDeclaration(cls, nonStaticFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasLet = false;
|
||||||
for (FieldHolder field : staticFields) {
|
for (FieldHolder field : staticFields) {
|
||||||
Object value = field.getInitialValue();
|
Object value = field.getInitialValue();
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
|
@ -270,30 +278,30 @@ public class Renderer implements RenderingManager {
|
||||||
value = null;
|
value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedName fieldName = naming.getFullNameFor(fieldRef);
|
if (!hasLet) {
|
||||||
if (fieldName.scoped) {
|
|
||||||
writer.append(naming.getScopeName()).append(".");
|
|
||||||
} else {
|
|
||||||
writer.append("let ");
|
writer.append("let ");
|
||||||
|
hasLet = true;
|
||||||
|
} else {
|
||||||
|
writer.append(",").ws();
|
||||||
}
|
}
|
||||||
writer.append(fieldName.value).ws().append("=").ws();
|
writer.appendStaticField(fieldRef).ws().append("=").ws();
|
||||||
context.constantToString(writer, value);
|
context.constantToString(writer, value);
|
||||||
writer.append(";").softNewLine();
|
}
|
||||||
|
if (hasLet) {
|
||||||
|
writer.append(";").newLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderFullClassFunctionDeclaration(PreparedClass cls, ScopedName jsName,
|
private void renderFullClassFunctionDeclaration(ClassReader cls, List<FieldHolder> nonStaticFields) {
|
||||||
List<FieldHolder> nonStaticFields) {
|
|
||||||
boolean thisAliased = false;
|
boolean thisAliased = false;
|
||||||
renderFunctionDeclaration(jsName);
|
writer.append("function ").appendClass(cls.getName()).append("()").ws().append("{").indent().softNewLine();
|
||||||
writer.append("()").ws().append("{").indent().softNewLine();
|
|
||||||
if (nonStaticFields.size() > 1) {
|
if (nonStaticFields.size() > 1) {
|
||||||
thisAliased = true;
|
thisAliased = true;
|
||||||
writer.append("let a").ws().append("=").ws().append("this;").ws();
|
writer.append("let a").ws().append("=").ws().append("this;").ws();
|
||||||
}
|
}
|
||||||
if (!cls.getClassHolder().getModifiers().contains(ElementModifier.INTERFACE)
|
if (!cls.readModifiers().contains(ElementModifier.INTERFACE)
|
||||||
&& cls.getParentName() != null) {
|
&& cls.getParent() != null) {
|
||||||
writer.appendClass(cls.getParentName()).append(".call(").append(thisAliased ? "a" : "this")
|
writer.appendClass(cls.getParent()).append(".call(").append(thisAliased ? "a" : "this")
|
||||||
.append(");").softNewLine();
|
.append(");").softNewLine();
|
||||||
}
|
}
|
||||||
for (FieldHolder field : nonStaticFields) {
|
for (FieldHolder field : nonStaticFields) {
|
||||||
|
@ -306,7 +314,6 @@ public class Renderer implements RenderingManager {
|
||||||
.append("=").ws();
|
.append("=").ws();
|
||||||
context.constantToString(writer, value);
|
context.constantToString(writer, value);
|
||||||
writer.append(";").softNewLine();
|
writer.append(";").softNewLine();
|
||||||
debugEmitter.addField(field.getName(), naming.getNameFor(fieldRef));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cls.getName().equals("java.lang.Object")) {
|
if (cls.getName().equals("java.lang.Object")) {
|
||||||
|
@ -314,65 +321,83 @@ public class Renderer implements RenderingManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.outdent().append("}");
|
writer.outdent().append("}");
|
||||||
if (jsName.scoped) {
|
|
||||||
writer.append(";");
|
|
||||||
}
|
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderShortClassFunctionDeclaration(PreparedClass cls, ScopedName jsName) {
|
private void renderShortClassFunctionDeclaration(ClassReader cls) {
|
||||||
if (jsName.scoped) {
|
writer.append("let ").appendClass(cls.getName()).ws().append("=").ws()
|
||||||
writer.append(naming.getScopeName()).append(".");
|
.appendFunction("$rt_classWithoutFields").append("(");
|
||||||
} else {
|
if (cls.hasModifier(ElementModifier.INTERFACE)) {
|
||||||
writer.append("let ");
|
|
||||||
}
|
|
||||||
writer.append(jsName.value).ws().append("=").ws().appendFunction("$rt_classWithoutFields").append("(");
|
|
||||||
if (cls.getClassHolder().hasModifier(ElementModifier.INTERFACE)) {
|
|
||||||
writer.append("0");
|
writer.append("0");
|
||||||
} else if (!cls.getParentName().equals("java.lang.Object")) {
|
} else if (!cls.getParent().equals("java.lang.Object")) {
|
||||||
writer.appendClass(cls.getParentName());
|
writer.appendClass(cls.getParent());
|
||||||
}
|
}
|
||||||
writer.append(");").newLine();
|
writer.append(");").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderMethodBodies(PreparedClass cls) throws RenderingException {
|
private void renderMethodBodies(ClassHolder cls, Decompiler decompiler) {
|
||||||
debugEmitter.emitClass(cls.getName());
|
writer.emitClass(cls.getName());
|
||||||
|
|
||||||
MethodReader clinit = classSource.get(cls.getName()).getMethod(CLINIT_METHOD);
|
MethodReader clinit = classSource.get(cls.getName()).getMethod(CLINIT_METHOD);
|
||||||
|
|
||||||
if (clinit != null && context.isDynamicInitializer(cls.getName())) {
|
if (clinit != null && context.isDynamicInitializer(cls.getName())) {
|
||||||
renderCallClinit(clinit, cls);
|
renderCallClinit(clinit, cls);
|
||||||
}
|
}
|
||||||
if (!cls.getClassHolder().hasModifier(ElementModifier.INTERFACE)
|
if (!cls.hasModifier(ElementModifier.INTERFACE)
|
||||||
&& !cls.getClassHolder().hasModifier(ElementModifier.ABSTRACT)) {
|
&& !cls.hasModifier(ElementModifier.ABSTRACT)) {
|
||||||
for (PreparedMethod method : cls.getMethods()) {
|
for (var method : cls.getMethods()) {
|
||||||
if (!method.modifiers.contains(ElementModifier.STATIC)) {
|
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||||
if (method.reference.getName().equals("<init>")) {
|
if (method.getName().equals("<init>")) {
|
||||||
renderInitializer(method);
|
renderInitializer(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PreparedMethod method : cls.getMethods()) {
|
var hasLet = false;
|
||||||
renderBody(method);
|
for (var method : cls.getMethods()) {
|
||||||
|
if (!filterMethod(method)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!hasLet) {
|
||||||
|
writer.append("let ");
|
||||||
|
hasLet = true;
|
||||||
|
} else {
|
||||||
|
writer.append(",").newLine();
|
||||||
|
}
|
||||||
|
renderBody(method, decompiler);
|
||||||
|
}
|
||||||
|
if (hasLet) {
|
||||||
|
writer.append(";").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
debugEmitter.emitClass(null);
|
writer.emitClass(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderCallClinit(MethodReader clinit, PreparedClass cls) {
|
private boolean filterMethod(MethodReader method) {
|
||||||
|
if (method.hasModifier(ElementModifier.ABSTRACT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (method.getAnnotations().get(InjectedBy.class.getName()) != null
|
||||||
|
|| context.getInjector(method.getReference()) != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!method.hasModifier(ElementModifier.NATIVE) && method.getProgram() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderCallClinit(MethodReader clinit, ClassReader cls) {
|
||||||
boolean isAsync = asyncMethods.contains(clinit.getReference());
|
boolean isAsync = asyncMethods.contains(clinit.getReference());
|
||||||
|
|
||||||
ScopedName className = naming.getNameFor(cls.getName());
|
var clinitCalledField = new FieldReference(cls.getName(), "$_teavm_clinitCalled_$");
|
||||||
String clinitCalled = (className.scoped ? naming.getScopeName() + "_" : "") + className.value
|
|
||||||
+ "_$clinitCalled";
|
|
||||||
if (isAsync) {
|
if (isAsync) {
|
||||||
writer.append("let ").append(clinitCalled).ws().append("=").ws().append("false;").softNewLine();
|
writer.append("let ").appendStaticField(clinitCalledField).ws().append("=").ws().append("false;")
|
||||||
|
.softNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedName name = naming.getNameForClassInit(cls.getName());
|
writer.append("let ").appendClassInit(cls.getName()).ws().append("=").ws();
|
||||||
renderLambdaDeclaration(name);
|
|
||||||
writer.append("()").sameLineWs().append("=>").ws().append("{").softNewLine().indent();
|
writer.append("()").sameLineWs().append("=>").ws().append("{").softNewLine().indent();
|
||||||
|
|
||||||
if (isAsync) {
|
if (isAsync) {
|
||||||
|
@ -383,7 +408,7 @@ public class Renderer implements RenderingManager {
|
||||||
writer.append(context.pointerName()).ws().append("=").ws().appendFunction("$rt_nativeThread")
|
writer.append(context.pointerName()).ws().append("=").ws().appendFunction("$rt_nativeThread")
|
||||||
.append("().pop();").softNewLine();
|
.append("().pop();").softNewLine();
|
||||||
writer.outdent().append("}").ws();
|
writer.outdent().append("}").ws();
|
||||||
writer.append("else if").ws().append("(").append(clinitCalled).append(")").ws()
|
writer.append("else if").ws().append("(").appendStaticField(clinitCalledField).append(")").ws()
|
||||||
.append("{").indent().softNewLine();
|
.append("{").indent().softNewLine();
|
||||||
writer.append("return;").softNewLine();
|
writer.append("return;").softNewLine();
|
||||||
writer.outdent().append("}").softNewLine();
|
writer.outdent().append("}").softNewLine();
|
||||||
|
@ -391,7 +416,7 @@ public class Renderer implements RenderingManager {
|
||||||
renderAsyncPrologue(writer, context);
|
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.appendStaticField(clinitCalledField).ws().append('=').ws().append("true;").softNewLine();
|
||||||
} else {
|
} else {
|
||||||
renderEraseClinit(cls);
|
renderEraseClinit(cls);
|
||||||
}
|
}
|
||||||
|
@ -416,55 +441,48 @@ public class Renderer implements RenderingManager {
|
||||||
writer.appendFunction("$rt_nativeThread").append("().push(" + context.pointerName() + ");").softNewLine();
|
writer.appendFunction("$rt_nativeThread").append("().push(" + context.pointerName() + ");").softNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.outdent().append("}");
|
writer.outdent().append("};");
|
||||||
if (name.scoped) {
|
|
||||||
writer.append(";");
|
|
||||||
}
|
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderEraseClinit(PreparedClass cls) {
|
private void renderEraseClinit(ClassReader cls) {
|
||||||
writer.appendClassInit(cls.getName()).ws().append("=").ws()
|
writer.appendClassInit(cls.getName()).ws().append("=").ws()
|
||||||
.appendFunction("$rt_eraseClinit").append("(")
|
.appendFunction("$rt_eraseClinit").append("(")
|
||||||
.appendClass(cls.getName()).append(");").softNewLine();
|
.appendClass(cls.getName()).append(");").softNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderClassMetadata(List<PreparedClass> classes) {
|
private void renderClassMetadata(List<? extends ClassReader> classReaders) {
|
||||||
if (classes.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassMetadataRequirements metadataRequirements = new ClassMetadataRequirements(context.getDependencyInfo());
|
ClassMetadataRequirements metadataRequirements = new ClassMetadataRequirements(context.getDependencyInfo());
|
||||||
|
|
||||||
int start = writer.getOffset();
|
writer.markSectionStart(SECTION_METADATA);
|
||||||
|
|
||||||
writer.appendFunction("$rt_packages").append("([");
|
writer.appendFunction("$rt_packages").append("([");
|
||||||
ObjectIntMap<String> packageIndexes = generatePackageMetadata(classes, metadataRequirements);
|
ObjectIntMap<String> packageIndexes = generatePackageMetadata(classReaders, metadataRequirements);
|
||||||
writer.append("]);").newLine();
|
writer.append("]);").newLine();
|
||||||
|
|
||||||
for (int i = 0; i < classes.size(); i += 50) {
|
for (int i = 0; i < classReaders.size(); i += 50) {
|
||||||
int j = Math.min(i + 50, classes.size());
|
int j = Math.min(i + 50, classReaders.size());
|
||||||
renderClassMetadataPortion(classes.subList(i, j), packageIndexes, metadataRequirements);
|
renderClassMetadataPortion(classReaders.subList(i, j), packageIndexes, metadataRequirements);
|
||||||
}
|
}
|
||||||
|
|
||||||
metadataSize = writer.getOffset() - start;
|
writer.markSectionEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderClassMetadataPortion(List<PreparedClass> classes, ObjectIntMap<String> packageIndexes,
|
private void renderClassMetadataPortion(List<? extends ClassReader> classes, ObjectIntMap<String> packageIndexes,
|
||||||
ClassMetadataRequirements metadataRequirements) {
|
ClassMetadataRequirements metadataRequirements) {
|
||||||
writer.appendFunction("$rt_metadata").append("([");
|
writer.appendFunction("$rt_metadata").append("([");
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
for (PreparedClass cls : classes) {
|
for (var cls : classes) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
writer.append(',').softNewLine();
|
writer.append(',').softNewLine();
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
debugEmitter.emitClass(cls.getName());
|
writer.emitClass(cls.getName());
|
||||||
writer.appendClass(cls.getName()).append(",").ws();
|
writer.appendClass(cls.getName()).append(",").ws();
|
||||||
|
|
||||||
ClassMetadataRequirements.Info requiredMetadata = metadataRequirements.getInfo(cls.getName());
|
var className = cls.getName();
|
||||||
|
var requiredMetadata = metadataRequirements.getInfo(className);
|
||||||
if (requiredMetadata.name()) {
|
if (requiredMetadata.name()) {
|
||||||
String className = cls.getName();
|
|
||||||
int dotIndex = className.lastIndexOf('.') + 1;
|
int dotIndex = className.lastIndexOf('.') + 1;
|
||||||
String packageName = className.substring(0, dotIndex);
|
String packageName = className.substring(0, dotIndex);
|
||||||
className = className.substring(dotIndex);
|
className = className.substring(dotIndex);
|
||||||
|
@ -475,14 +493,14 @@ public class Renderer implements RenderingManager {
|
||||||
}
|
}
|
||||||
writer.append(",").ws();
|
writer.append(",").ws();
|
||||||
|
|
||||||
if (cls.getParentName() != null) {
|
if (cls.getParent() != null) {
|
||||||
writer.appendClass(cls.getParentName());
|
writer.appendClass(cls.getParent());
|
||||||
} else {
|
} else {
|
||||||
writer.append("0");
|
writer.append("0");
|
||||||
}
|
}
|
||||||
writer.append(',').ws();
|
writer.append(',').ws();
|
||||||
writer.append("[");
|
writer.append("[");
|
||||||
List<String> interfaces = new ArrayList<>(cls.getClassHolder().getInterfaces());
|
var interfaces = new ArrayList<>(cls.getInterfaces());
|
||||||
for (int i = 0; i < interfaces.size(); ++i) {
|
for (int i = 0; i < interfaces.size(); ++i) {
|
||||||
String iface = interfaces.get(i);
|
String iface = interfaces.get(i);
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
|
@ -492,28 +510,28 @@ public class Renderer implements RenderingManager {
|
||||||
}
|
}
|
||||||
writer.append("],").ws();
|
writer.append("],").ws();
|
||||||
|
|
||||||
writer.append(ElementModifier.pack(cls.getClassHolder().getModifiers())).append(',').ws();
|
writer.append(ElementModifier.pack(cls.readModifiers())).append(',').ws();
|
||||||
writer.append(cls.getClassHolder().getLevel().ordinal()).append(',').ws();
|
writer.append(cls.getLevel().ordinal()).append(',').ws();
|
||||||
|
|
||||||
if (!requiredMetadata.enclosingClass() && !requiredMetadata.declaringClass()
|
if (!requiredMetadata.enclosingClass() && !requiredMetadata.declaringClass()
|
||||||
&& !requiredMetadata.simpleName()) {
|
&& !requiredMetadata.simpleName()) {
|
||||||
writer.append("0");
|
writer.append("0");
|
||||||
} else {
|
} else {
|
||||||
writer.append('[');
|
writer.append('[');
|
||||||
if (requiredMetadata.enclosingClass() && cls.getClassHolder().getOwnerName() != null) {
|
if (requiredMetadata.enclosingClass() && cls.getOwnerName() != null) {
|
||||||
writer.appendClass(cls.getClassHolder().getOwnerName());
|
writer.appendClass(cls.getOwnerName());
|
||||||
} else {
|
} else {
|
||||||
writer.append('0');
|
writer.append('0');
|
||||||
}
|
}
|
||||||
writer.append(',');
|
writer.append(',');
|
||||||
if (requiredMetadata.declaringClass() && cls.getClassHolder().getDeclaringClassName() != null) {
|
if (requiredMetadata.declaringClass() && cls.getDeclaringClassName() != null) {
|
||||||
writer.appendClass(cls.getClassHolder().getDeclaringClassName());
|
writer.appendClass(cls.getDeclaringClassName());
|
||||||
} else {
|
} else {
|
||||||
writer.append('0');
|
writer.append('0');
|
||||||
}
|
}
|
||||||
writer.append(',');
|
writer.append(',');
|
||||||
if (requiredMetadata.simpleName() && cls.getClassHolder().getSimpleName() != null) {
|
if (requiredMetadata.simpleName() && cls.getSimpleName() != null) {
|
||||||
writer.append("\"").append(RenderingUtil.escapeString(cls.getClassHolder().getSimpleName()))
|
writer.append("\"").append(RenderingUtil.escapeString(cls.getSimpleName()))
|
||||||
.append("\"");
|
.append("\"");
|
||||||
} else {
|
} else {
|
||||||
writer.append('0');
|
writer.append('0');
|
||||||
|
@ -532,30 +550,30 @@ 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 (var method : cls.getMethods()) {
|
||||||
if (!method.modifiers.contains(ElementModifier.STATIC)
|
if (filterMethod(method) && !method.readModifiers().contains(ElementModifier.STATIC)
|
||||||
&& method.accessLevel != AccessLevel.PRIVATE) {
|
&& method.getLevel() != AccessLevel.PRIVATE) {
|
||||||
virtualMethods.put(method.reference.getDescriptor(), method.reference);
|
virtualMethods.put(method.getDescriptor(), method.getReference());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderVirtualDeclarations(virtualMethods.values());
|
renderVirtualDeclarations(virtualMethods.values());
|
||||||
debugEmitter.emitClass(null);
|
writer.emitClass(null);
|
||||||
}
|
}
|
||||||
writer.append("]);").newLine();
|
writer.append("]);").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObjectIntMap<String> generatePackageMetadata(List<PreparedClass> classes,
|
private ObjectIntMap<String> generatePackageMetadata(List<? extends ClassReader> classes,
|
||||||
ClassMetadataRequirements metadataRequirements) {
|
ClassMetadataRequirements metadataRequirements) {
|
||||||
PackageNode root = new PackageNode(null);
|
PackageNode root = new PackageNode(null);
|
||||||
|
|
||||||
for (PreparedClass classNode : classes) {
|
for (var cls : classes) {
|
||||||
String className = classNode.getName();
|
var requiredMetadata = metadataRequirements.getInfo(cls.getName());
|
||||||
ClassMetadataRequirements.Info requiredMetadata = metadataRequirements.getInfo(className);
|
|
||||||
if (!requiredMetadata.name()) {
|
if (!requiredMetadata.name()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var className = cls.getName();
|
||||||
int dotIndex = className.lastIndexOf('.');
|
int dotIndex = className.lastIndexOf('.');
|
||||||
if (dotIndex < 0) {
|
if (dotIndex < 0) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -593,14 +611,6 @@ public class Renderer implements RenderingManager {
|
||||||
PackageNode(String name) {
|
PackageNode(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
int count() {
|
|
||||||
int result = 0;
|
|
||||||
for (PackageNode child : children.values()) {
|
|
||||||
result += 1 + child.count();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPackageName(PackageNode node, String name) {
|
private void addPackageName(PackageNode node, String name) {
|
||||||
|
@ -686,11 +696,10 @@ public class Renderer implements RenderingManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderInitializer(PreparedMethod method) {
|
private void renderInitializer(MethodReader method) {
|
||||||
MethodReference ref = method.reference;
|
MethodReference ref = method.getReference();
|
||||||
debugEmitter.emitMethod(ref.getDescriptor());
|
writer.emitMethod(ref.getDescriptor());
|
||||||
ScopedName name = naming.getNameForInit(ref);
|
writer.append("let ").appendInit(ref).ws().append("=").ws();
|
||||||
renderLambdaDeclaration(name);
|
|
||||||
if (ref.parameterCount() != 1) {
|
if (ref.parameterCount() != 1) {
|
||||||
writer.append("(");
|
writer.append("(");
|
||||||
}
|
}
|
||||||
|
@ -715,16 +724,13 @@ public class Renderer implements RenderingManager {
|
||||||
}
|
}
|
||||||
writer.append(");").softNewLine();
|
writer.append(");").softNewLine();
|
||||||
writer.append("return " + instanceName + ";").softNewLine();
|
writer.append("return " + instanceName + ";").softNewLine();
|
||||||
writer.outdent().append("}");
|
writer.outdent().append("};");
|
||||||
if (name.scoped) {
|
|
||||||
writer.append(";");
|
|
||||||
}
|
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
debugEmitter.emitMethod(null);
|
writer.emitMethod(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String variableNameForInitializer(int index) {
|
private String variableNameForInitializer(int index) {
|
||||||
return minifying ? RenderingUtil.indexToId(index) : "var_" + index;
|
return context.isMinifying() ? RenderingUtil.indexToId(index) : "var_" + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderVirtualDeclarations(Collection<MethodReference> methods) {
|
private void renderVirtualDeclarations(Collection<MethodReference> methods) {
|
||||||
|
@ -739,19 +745,19 @@ public class Renderer implements RenderingManager {
|
||||||
if (!isVirtual(method)) {
|
if (!isVirtual(method)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
debugEmitter.emitMethod(method.getDescriptor());
|
writer.emitMethod(method.getDescriptor());
|
||||||
if (!first) {
|
if (!first) {
|
||||||
writer.append(",").ws();
|
writer.append(",").ws();
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
emitVirtualDeclaration(method);
|
emitVirtualDeclaration(method);
|
||||||
debugEmitter.emitMethod(null);
|
writer.emitMethod(null);
|
||||||
}
|
}
|
||||||
writer.append("]");
|
writer.append("]");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void emitVirtualDeclaration(MethodReference ref) {
|
private void emitVirtualDeclaration(MethodReference ref) {
|
||||||
String methodName = naming.getNameFor(ref.getDescriptor());
|
String methodName = context.getNaming().getNameFor(ref.getDescriptor());
|
||||||
writer.append("\"").append(methodName).append("\"");
|
writer.append("\"").append(methodName).append("\"");
|
||||||
writer.append(",").ws();
|
writer.append(",").ws();
|
||||||
emitVirtualFunctionWrapper(ref);
|
emitVirtualFunctionWrapper(ref);
|
||||||
|
@ -787,47 +793,142 @@ public class Renderer implements RenderingManager {
|
||||||
writer.append(");").ws().append("}");
|
writer.append(");").ws().append("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderBody(PreparedMethod method) {
|
private void renderBody(MethodHolder method, Decompiler decompiler) {
|
||||||
MethodReference ref = method.reference;
|
MethodReference ref = method.getReference();
|
||||||
debugEmitter.emitMethod(ref.getDescriptor());
|
writer.emitMethod(ref.getDescriptor());
|
||||||
ScopedName name = naming.getFullNameFor(ref);
|
|
||||||
|
|
||||||
renderLambdaDeclaration(name);
|
writer.appendMethodBody(ref).ws().append("=").ws();
|
||||||
method.parameters.replay(writer, RememberedSource.FILTER_ALL);
|
methodBodyRenderer.renderParameters(ref, method.getModifiers());
|
||||||
if (method.variables != null) {
|
|
||||||
for (var variable : method.variables) {
|
|
||||||
variable.emit(debugEmitter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writer.sameLineWs().append("=>").ws().append("{").indent().softNewLine();
|
writer.sameLineWs().append("=>").ws().append("{").indent().softNewLine();
|
||||||
method.body.replay(writer, RememberedSource.FILTER_ALL);
|
if (method.hasModifier(ElementModifier.NATIVE)) {
|
||||||
|
renderNativeBody(method, classSource);
|
||||||
|
} else {
|
||||||
|
renderRegularBody(method, decompiler);
|
||||||
|
}
|
||||||
|
|
||||||
writer.outdent().append("}");
|
writer.outdent().append("}");
|
||||||
if (name.scoped) {
|
writer.emitMethod(null);
|
||||||
writer.append(";");
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.newLine();
|
|
||||||
debugEmitter.emitMethod(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderLambdaDeclaration(ScopedName name) {
|
private void renderNativeBody(MethodHolder method, ClassReaderSource classes) {
|
||||||
if (name.scoped) {
|
var reference = method.getReference();
|
||||||
writer.append(naming.getScopeName()).append(".").append(name.value);
|
var generator = generators.get(reference);
|
||||||
|
if (generator == null) {
|
||||||
|
AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName());
|
||||||
|
if (annotHolder == null) {
|
||||||
|
throw new DecompilationException("Method " + method.getOwnerName() + "." + method.getDescriptor()
|
||||||
|
+ " is native, but no " + GeneratedBy.class.getName() + " annotation found");
|
||||||
|
}
|
||||||
|
ValueType annotValue = annotHolder.getValues().get("value").getJavaClass();
|
||||||
|
String generatorClassName = ((ValueType.Object) annotValue).getClassName();
|
||||||
|
generator = generatorCache.computeIfAbsent(generatorClassName,
|
||||||
|
name -> createGenerator(name, method, classes));
|
||||||
|
}
|
||||||
|
|
||||||
|
var async = asyncMethods.contains(reference);
|
||||||
|
methodBodyRenderer.renderNative(generator, async, reference);
|
||||||
|
threadLibraryUsed |= methodBodyRenderer.isThreadLibraryUsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Generator createGenerator(String name, MethodHolder method, ClassReaderSource classes) {
|
||||||
|
Class<?> generatorClass;
|
||||||
|
try {
|
||||||
|
generatorClass = Class.forName(name, true, context.getClassLoader());
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new DecompilationException("Error instantiating generator " + name
|
||||||
|
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
var constructors = generatorClass.getConstructors();
|
||||||
|
if (constructors.length != 1) {
|
||||||
|
throw new DecompilationException("Error instantiating generator " + name
|
||||||
|
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
var constructor = constructors[0];
|
||||||
|
var parameterTypes = constructor.getParameterTypes();
|
||||||
|
var arguments = new Object[parameterTypes.length];
|
||||||
|
for (var i = 0; i < arguments.length; ++i) {
|
||||||
|
var parameterType = parameterTypes[i];
|
||||||
|
if (parameterType.equals(ClassReaderSource.class)) {
|
||||||
|
arguments[i] = classes;
|
||||||
|
} else if (parameterType.equals(Properties.class)) {
|
||||||
|
arguments[i] = context.getProperties();
|
||||||
|
} else if (parameterType.equals(DependencyInfo.class)) {
|
||||||
|
arguments[i] = context.getDependencyInfo();
|
||||||
|
} else if (parameterType.equals(ServiceRepository.class)) {
|
||||||
|
arguments[i] = context.getServices();
|
||||||
|
} else if (parameterType.equals(JavaScriptTemplateFactory.class)) {
|
||||||
|
arguments[i] = templateFactory;
|
||||||
|
} else {
|
||||||
|
var service = context.getServices().getService(parameterType);
|
||||||
|
if (service == null) {
|
||||||
|
throw new DecompilationException("Error instantiating generator " + name
|
||||||
|
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor() + ". "
|
||||||
|
+ "Its constructor requires " + parameterType + " as its parameter #" + (i + 1)
|
||||||
|
+ " which is not available.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (Generator) constructor.newInstance(arguments);
|
||||||
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new DecompilationException("Error instantiating generator " + name
|
||||||
|
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderRegularBody(MethodHolder method, Decompiler decompiler) {
|
||||||
|
MethodReference reference = method.getReference();
|
||||||
|
MethodNode node;
|
||||||
|
var async = asyncMethods.contains(reference);
|
||||||
|
if (async) {
|
||||||
|
node = decompileAsync(decompiler, method);
|
||||||
} else {
|
} else {
|
||||||
writer.append("let ").append(name.value);
|
var entry = decompileRegular(decompiler, method);
|
||||||
|
node = entry.method;
|
||||||
}
|
}
|
||||||
writer.ws().append("=").ws();
|
methodBodyRenderer.render(node, async);
|
||||||
|
threadLibraryUsed |= methodBodyRenderer.isThreadLibraryUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderFunctionDeclaration(ScopedName name) {
|
private AstCacheEntry decompileRegular(Decompiler decompiler, MethodHolder method) {
|
||||||
if (name.scoped) {
|
if (astCache == null) {
|
||||||
writer.append(naming.getScopeName()).append(".").append(name.value).ws().append("=").ws();
|
return decompileRegularCacheMiss(decompiler, method);
|
||||||
}
|
}
|
||||||
writer.append("function");
|
|
||||||
if (!name.scoped) {
|
AstCacheEntry entry = !cacheStatus.isStaleMethod(method.getReference())
|
||||||
writer.append(" ").append(name.value);
|
? astCache.get(method.getReference(), cacheStatus)
|
||||||
|
: null;
|
||||||
|
if (entry == null) {
|
||||||
|
entry = decompileRegularCacheMiss(decompiler, method);
|
||||||
|
RegularMethodNode finalNode = entry.method;
|
||||||
|
astCache.store(method.getReference(), entry, () -> dependencyExtractor.extract(finalNode));
|
||||||
}
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AstCacheEntry decompileRegularCacheMiss(Decompiler decompiler, MethodHolder method) {
|
||||||
|
RegularMethodNode node = decompiler.decompileRegular(method);
|
||||||
|
ControlFlowEntry[] cfg = LocationGraphBuilder.build(node.getBody());
|
||||||
|
return new AstCacheEntry(node, cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AsyncMethodNode decompileAsync(Decompiler decompiler, MethodHolder method) {
|
||||||
|
if (astCache == null) {
|
||||||
|
return decompiler.decompileAsync(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncMethodNode node = !cacheStatus.isStaleMethod(method.getReference())
|
||||||
|
? astCache.getAsync(method.getReference(), cacheStatus)
|
||||||
|
: null;
|
||||||
|
if (node == null) {
|
||||||
|
node = decompiler.decompileAsync(method);
|
||||||
|
AsyncMethodNode finalNode = node;
|
||||||
|
astCache.storeAsync(method.getReference(), node, () -> dependencyExtractor.extract(finalNode));
|
||||||
|
}
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void renderAsyncPrologue(SourceWriter writer, RenderingContext context) {
|
static void renderAsyncPrologue(SourceWriter writer, RenderingContext context) {
|
||||||
|
|
|
@ -22,12 +22,8 @@ import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.model.ListableClassReaderSource;
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
|
|
||||||
public interface RenderingManager extends ServiceRepository {
|
public interface RenderingManager extends ServiceRepository {
|
||||||
NamingStrategy getNaming();
|
|
||||||
|
|
||||||
SourceWriter getWriter();
|
SourceWriter getWriter();
|
||||||
|
|
||||||
boolean isMinifying();
|
|
||||||
|
|
||||||
ListableClassReaderSource getClassSource();
|
ListableClassReaderSource getClassSource();
|
||||||
|
|
||||||
ClassLoader getClassLoader();
|
ClassLoader getClassLoader();
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
|
|
||||||
let logging = false;
|
let logging = false;
|
||||||
let deobfuscation = false;
|
let deobfuscation = false;
|
||||||
deobfuscator();
|
if (typeof deobfuscator !== 'undefined') {
|
||||||
|
deobfuscator();
|
||||||
|
}
|
||||||
|
|
||||||
function tryConnect() {
|
function tryConnect() {
|
||||||
let ws = new WebSocket("ws://localhost:{{PORT}}/ws");
|
let ws = new WebSocket("ws://localhost:{{PORT}}/ws");
|
||||||
|
|
Loading…
Reference in New Issue
Block a user