Refactor things around decompiler, JS backend and control flow debug info

This commit is contained in:
Alexey Andreev 2019-02-25 16:10:35 +03:00
parent 3be32a5851
commit 95092c4d82
24 changed files with 462 additions and 548 deletions

View File

@ -1,70 +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.ast;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import org.teavm.model.AccessLevel;
import org.teavm.model.ElementModifier;
public class ClassNode {
private String name;
private String parentName;
private Set<ElementModifier> modifiers = EnumSet.noneOf(ElementModifier.class);
private AccessLevel accessLevel = AccessLevel.PUBLIC;
private List<FieldNode> fields = new ArrayList<>();
private List<MethodNode> methods = new ArrayList<>();
private List<String> interfaces = new ArrayList<>();
public ClassNode(String name, String parentName) {
this.name = name;
this.parentName = parentName;
}
public String getName() {
return name;
}
public String getParentName() {
return parentName;
}
public List<FieldNode> getFields() {
return fields;
}
public List<MethodNode> getMethods() {
return methods;
}
public List<String> getInterfaces() {
return interfaces;
}
public Set<ElementModifier> getModifiers() {
return modifiers;
}
public AccessLevel getAccessLevel() {
return accessLevel;
}
public void setAccessLevel(AccessLevel accessLevel) {
this.accessLevel = accessLevel;
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.ast;
import org.teavm.model.TextLocation;
public class ControlFlowEntry {
public final TextLocation from;
public final TextLocation[] to;
public ControlFlowEntry(TextLocation from, TextLocation[] to) {
this.from = from;
this.to = to.clone();
}
}

View File

@ -1,58 +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.ast;
import java.util.EnumSet;
import java.util.Set;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference;
import org.teavm.model.ValueType;
public class FieldNode {
private FieldReference reference;
private ValueType type;
private Set<ElementModifier> modifiers = EnumSet.noneOf(ElementModifier.class);
private Object initialValue;
public FieldNode(FieldReference reference, ValueType type) {
this.reference = reference;
this.type = type;
}
public FieldReference getReference() {
return reference;
}
public String getName() {
return reference.getFieldName();
}
public Set<ElementModifier> getModifiers() {
return modifiers;
}
public ValueType getType() {
return type;
}
public Object getInitialValue() {
return initialValue;
}
public void setInitialValue(Object initialValue) {
this.initialValue = initialValue;
}
}

View File

@ -19,6 +19,4 @@ public interface MethodNodeVisitor {
void visit(RegularMethodNode methodNode);
void visit(AsyncMethodNode methodNode);
void visit(NativeMethodNode methodNode);
}

View File

@ -1,57 +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.ast;
import java.util.Collections;
import java.util.List;
import org.teavm.backend.javascript.spi.Generator;
import org.teavm.model.MethodReference;
public class NativeMethodNode extends MethodNode {
private Generator generator;
private boolean async;
public NativeMethodNode(MethodReference reference) {
super(reference);
}
public Generator getGenerator() {
return generator;
}
public void setGenerator(Generator generator) {
this.generator = generator;
}
@Override
public boolean isAsync() {
return async;
}
public void setAsync(boolean async) {
this.async = async;
}
@Override
public void acceptVisitor(MethodNodeVisitor visitor) {
visitor.visit(this);
}
@Override
public List<VariableNode> getVariables() {
return Collections.emptyList();
}
}

View File

@ -35,6 +35,7 @@ import org.teavm.ast.BreakStatement;
import org.teavm.ast.ConditionalExpr;
import org.teavm.ast.ConditionalStatement;
import org.teavm.ast.ContinueStatement;
import org.teavm.ast.ControlFlowEntry;
import org.teavm.ast.Expr;
import org.teavm.ast.IdentifiedStatement;
import org.teavm.ast.InitClassStatement;
@ -56,7 +57,7 @@ public final class LocationGraphBuilder {
private LocationGraphBuilder() {
}
public static Map<TextLocation, TextLocation[]> build(Statement node) {
public static ControlFlowEntry[] build(Statement node) {
Visitor visitor = new Visitor();
node.acceptVisitor(visitor);
Graph graph = visitor.builder.build();
@ -82,9 +83,10 @@ public final class LocationGraphBuilder {
}
}
Map<TextLocation, TextLocation[]> result = new LinkedHashMap<>();
ControlFlowEntry[] result = new ControlFlowEntry[builder.size()];
int index = 0;
for (Map.Entry<TextLocation, Set<TextLocation>> entry : builder.entrySet()) {
result.put(entry.getKey(), entry.getValue().toArray(new TextLocation[0]));
result[index++] = new ControlFlowEntry(entry.getKey(), entry.getValue().toArray(new TextLocation[0]));
}
return result;
}

View File

@ -18,24 +18,16 @@ package org.teavm.ast.decompilation;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.AsyncMethodPart;
import org.teavm.ast.BlockStatement;
import org.teavm.ast.ClassNode;
import org.teavm.ast.FieldNode;
import org.teavm.ast.GotoPartStatement;
import org.teavm.ast.IdentifiedStatement;
import org.teavm.ast.MethodNode;
import org.teavm.ast.NativeMethodNode;
import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.SequentialStatement;
import org.teavm.ast.Statement;
@ -43,31 +35,19 @@ import org.teavm.ast.TryCatchStatement;
import org.teavm.ast.VariableNode;
import org.teavm.ast.WhileStatement;
import org.teavm.ast.optimization.Optimizer;
import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.backend.javascript.spi.Generator;
import org.teavm.backend.javascript.spi.InjectedBy;
import org.teavm.cache.AstDependencyExtractor;
import org.teavm.cache.CacheStatus;
import org.teavm.cache.MethodNodeCache;
import org.teavm.common.Graph;
import org.teavm.common.GraphIndexer;
import org.teavm.common.Loop;
import org.teavm.common.LoopGraph;
import org.teavm.common.RangeTree;
import org.teavm.interop.PlatformMarker;
import org.teavm.model.AnnotationHolder;
import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.Instruction;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.TextLocation;
import org.teavm.model.TryCatchBlock;
import org.teavm.model.ValueType;
import org.teavm.model.instructions.BinaryBranchingInstruction;
import org.teavm.model.instructions.BranchingInstruction;
import org.teavm.model.instructions.JumpInstruction;
@ -78,8 +58,6 @@ import org.teavm.model.util.TypeInferer;
public class Decompiler {
private ClassHolderSource classSource;
private ClassLoader classLoader;
private CacheStatus cacheStatus;
private Graph graph;
private LoopGraph loopGraph;
private GraphIndexer indexer;
@ -91,43 +69,21 @@ public class Decompiler {
private RangeTree codeTree;
private RangeTree.Node currentNode;
private RangeTree.Node parentNode;
private Map<MethodReference, Generator> generators = new HashMap<>();
private Set<MethodReference> methodsToSkip = new HashSet<>();
private MethodNodeCache regularMethodCache;
private Set<MethodReference> asyncMethods;
private Set<MethodReference> splitMethods = new HashSet<>();
private Set<MethodReference> splitMethods;
private List<TryCatchBookmark> tryCatchBookmarks = new ArrayList<>();
private final AstDependencyExtractor astDependencyExtractor = new AstDependencyExtractor();
private Deque<Block> stack;
private Program program;
private boolean friendlyToDebugger;
private boolean moveConstants;
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader,
CacheStatus cacheStatus, Set<MethodReference> asyncMethods, Set<MethodReference> asyncFamilyMethods,
boolean friendlyToDebugger, boolean moveConstants) {
public Decompiler(ClassHolderSource classSource, Set<MethodReference> splitMethods, boolean friendlyToDebugger,
boolean moveConstants) {
this.classSource = classSource;
this.classLoader = classLoader;
this.cacheStatus = cacheStatus;
this.asyncMethods = asyncMethods;
splitMethods.addAll(asyncMethods);
splitMethods.addAll(asyncFamilyMethods);
this.splitMethods = splitMethods;
this.friendlyToDebugger = friendlyToDebugger;
this.moveConstants = moveConstants;
}
public MethodNodeCache getRegularMethodCache() {
return regularMethodCache;
}
public void setRegularMethodCache(MethodNodeCache regularMethodCache) {
this.regularMethodCache = regularMethodCache;
}
public int getGraphSize() {
return this.graph.size();
}
static class Block {
Block parent;
int parentOffset;
@ -175,134 +131,7 @@ public class Decompiler {
int exceptionHandler;
}
public List<ClassNode> decompile(Collection<String> classNames) {
List<String> sequence = new ArrayList<>();
Set<String> visited = new HashSet<>();
for (String className : classNames) {
orderClasses(className, visited, sequence);
}
final List<ClassNode> result = new ArrayList<>();
for (int i = 0; i < sequence.size(); ++i) {
final String className = sequence.get(i);
result.add(decompile(classSource.get(className)));
}
return result;
}
public List<String> getClassOrdering(Collection<String> classNames) {
List<String> sequence = new ArrayList<>();
Set<String> visited = new HashSet<>();
for (String className : classNames) {
orderClasses(className, visited, sequence);
}
return sequence;
}
public void addGenerator(MethodReference method, Generator generator) {
generators.put(method, generator);
}
public void addMethodToSkip(MethodReference method) {
methodsToSkip.add(method);
}
private void orderClasses(String className, Set<String> visited, List<String> order) {
if (!visited.add(className)) {
return;
}
ClassHolder cls = classSource.get(className);
if (cls == null) {
return;
}
if (cls.getParent() != null) {
orderClasses(cls.getParent(), visited, order);
}
for (String iface : cls.getInterfaces()) {
orderClasses(iface, visited, order);
}
order.add(className);
}
public ClassNode decompile(ClassHolder cls) {
ClassNode clsNode = new ClassNode(cls.getName(), cls.getParent());
for (FieldHolder field : cls.getFields()) {
FieldNode fieldNode = new FieldNode(field.getReference(), field.getType());
fieldNode.getModifiers().addAll(field.getModifiers());
fieldNode.setInitialValue(field.getInitialValue());
clsNode.getFields().add(fieldNode);
}
for (MethodHolder method : cls.getMethods()) {
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
continue;
}
if ((!isBootstrap() && method.getAnnotations().get(InjectedBy.class.getName()) != null)
|| methodsToSkip.contains(method.getReference())) {
continue;
}
if (!method.hasModifier(ElementModifier.NATIVE) && method.getProgram() == null) {
continue;
}
MethodNode methodNode = decompile(method);
clsNode.getMethods().add(methodNode);
}
clsNode.getInterfaces().addAll(cls.getInterfaces());
clsNode.getModifiers().addAll(cls.getModifiers());
clsNode.setAccessLevel(cls.getLevel());
return clsNode;
}
public MethodNode decompile(MethodHolder method) {
return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method)
: !asyncMethods.contains(method.getReference()) ? decompileRegular(method) : decompileAsync(method);
}
public NativeMethodNode decompileNative(MethodHolder method) {
Generator generator = generators.get(method.getReference());
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();
try {
Class<?> generatorClass = Class.forName(generatorClassName, true, classLoader);
generator = (Generator) generatorClass.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new DecompilationException("Error instantiating generator " + generatorClassName
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
}
}
NativeMethodNode methodNode = new NativeMethodNode(method.getReference());
methodNode.getModifiers().addAll(method.getModifiers());
methodNode.setGenerator(generator);
methodNode.setAsync(asyncMethods.contains(method.getReference()));
return methodNode;
}
@PlatformMarker
private static boolean isBootstrap() {
return false;
}
public RegularMethodNode decompileRegular(MethodHolder method) {
if (regularMethodCache == null) {
return decompileRegularCacheMiss(method);
}
RegularMethodNode node = !cacheStatus.isStaleMethod(method.getReference())
? regularMethodCache.get(method.getReference(), cacheStatus)
: null;
if (node == null) {
node = decompileRegularCacheMiss(method);
RegularMethodNode finalNode = node;
regularMethodCache.store(method.getReference(), node, () -> astDependencyExtractor.extract(finalNode));
}
return node;
}
public RegularMethodNode decompileRegularCacheMiss(MethodHolder method) {
RegularMethodNode methodNode = new RegularMethodNode(method.getReference());
Program program = method.getProgram();
int[] targetBlocks = new int[program.basicBlockCount()];
@ -331,21 +160,6 @@ public class Decompiler {
}
public AsyncMethodNode decompileAsync(MethodHolder method) {
if (regularMethodCache == null) {
return decompileAsyncCacheMiss(method);
}
AsyncMethodNode node = !cacheStatus.isStaleMethod(method.getReference())
? regularMethodCache.getAsync(method.getReference(), cacheStatus)
: null;
if (node == null) {
node = decompileAsyncCacheMiss(method);
AsyncMethodNode finalNode = node;
regularMethodCache.storeAsync(method.getReference(), node, () -> astDependencyExtractor.extract(finalNode));
}
return node;
}
private AsyncMethodNode decompileAsyncCacheMiss(MethodHolder method) {
AsyncMethodNode node = new AsyncMethodNode(method.getReference());
AsyncProgramSplitter splitter = new AsyncProgramSplitter(classSource, splitMethods);
splitter.split(method.getProgram());
@ -461,7 +275,6 @@ public class Decompiler {
block.body.addAll(generator.statements);
}
List<Runnable> deferred = new ArrayList<>();
while (block.end == i + 1) {
Block oldBlock = block;
stack.pop();
@ -493,9 +306,6 @@ public class Decompiler {
oldBlock.tryCatches.clear();
oldBlock.removeFrom(blockMap);
}
for (Runnable r : deferred) {
r.run();
}
if (generator.nextBlock != null && !isTrivialBlock(generator.nextBlock)) {
closeExpiredBookmarks(generator, generator.nextBlock.getTryCatchBlocks());

View File

@ -61,7 +61,6 @@ import org.teavm.backend.c.intrinsic.PlatformObjectIntrinsic;
import org.teavm.backend.c.intrinsic.RuntimeClassIntrinsic;
import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic;
import org.teavm.backend.c.intrinsic.StructureIntrinsic;
import org.teavm.cache.AlwaysStaleCacheStatus;
import org.teavm.dependency.ClassDependency;
import org.teavm.dependency.DependencyAnalyzer;
import org.teavm.dependency.DependencyListener;
@ -219,8 +218,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
TagRegistry tagRegistry = new TagRegistry(classes);
StringPool stringPool = new StringPool();
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(),
AlwaysStaleCacheStatus.INSTANCE, new HashSet<>(), new HashSet<>(), false, true);
Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false, true);
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
NameProvider nameProvider = new NameProvider(controller.getUnprocessedClassSource());

View File

@ -34,11 +34,11 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.teavm.ast.ClassNode;
import org.teavm.ast.MethodNode;
import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.ControlFlowEntry;
import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.Statement;
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.DefaultAliasProvider;
@ -46,6 +46,8 @@ import org.teavm.backend.javascript.codegen.DefaultNamingStrategy;
import org.teavm.backend.javascript.codegen.MinifyingAliasProvider;
import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.backend.javascript.codegen.SourceWriterBuilder;
import org.teavm.backend.javascript.decompile.PreparedClass;
import org.teavm.backend.javascript.decompile.PreparedMethod;
import org.teavm.backend.javascript.rendering.Renderer;
import org.teavm.backend.javascript.rendering.RenderingContext;
import org.teavm.backend.javascript.rendering.RuntimeRenderer;
@ -55,6 +57,9 @@ import org.teavm.backend.javascript.spi.InjectedBy;
import org.teavm.backend.javascript.spi.Injector;
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
import org.teavm.cache.AstCacheEntry;
import org.teavm.cache.AstDependencyExtractor;
import org.teavm.cache.CacheStatus;
import org.teavm.cache.EmptyMethodNodeCache;
import org.teavm.cache.MethodNodeCache;
import org.teavm.debugging.information.DebugInformationEmitter;
@ -68,9 +73,11 @@ import org.teavm.dependency.DependencyType;
import org.teavm.dependency.MethodDependency;
import org.teavm.interop.PlatformMarker;
import org.teavm.interop.PlatformMarkers;
import org.teavm.model.AnnotationHolder;
import org.teavm.model.BasicBlock;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderSource;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
@ -120,6 +127,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
private ClassInitializerInsertionTransformer clinitInsertionTransformer;
private List<VirtualMethodContributor> customVirtualMethods = new ArrayList<>();
private int topLevelNameLimit = 10000;
private AstDependencyExtractor dependencyExtractor = new AstDependencyExtractor();
@Override
public List<ClassHolderTransformer> getTransformers() {
@ -320,7 +328,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
}
private void emit(ListableClassHolderSource classes, Writer writer, BuildTarget target) {
List<ClassNode> clsNodes = modelToAst(classes);
List<PreparedClass> clsNodes = modelToAst(classes);
if (controller.wasCancelled()) {
return;
}
@ -351,16 +359,10 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
renderer.setMinifying(minifying);
renderer.setProgressConsumer(controller::reportProgress);
if (debugEmitter != null) {
for (ClassNode classNode : clsNodes) {
ClassHolder cls = classes.get(classNode.getName());
for (MethodNode methodNode : classNode.getMethods()) {
if (methodNode instanceof RegularMethodNode) {
emitCFG(debugEmitter, ((RegularMethodNode) methodNode).getBody());
} else {
MethodHolder method = cls.getMethod(methodNode.getReference().getDescriptor());
if (method != null && method.getProgram() != null) {
emitCFG(debugEmitter, method.getProgram());
}
for (PreparedClass preparedClass : clsNodes) {
for (PreparedMethod preparedMethod : preparedClass.getMethods()) {
if (preparedMethod.cfg != null) {
emitCFG(debugEmitter, preparedMethod.cfg);
}
}
if (controller.wasCancelled()) {
@ -462,44 +464,155 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
return STATS_NUM_FORMAT.format(size) + " (" + STATS_PERCENT_FORMAT.format((double) size / totalSize) + ")";
}
static class PackageNode {
String name;
Map<String, PackageNode> children = new HashMap<>();
}
private List<ClassNode> modelToAst(ListableClassHolderSource classes) {
private List<PreparedClass> modelToAst(ListableClassHolderSource classes) {
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(),
controller.getDiagnostics());
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, controller.getClassLoader(), controller.getCacheStatus(),
asyncMethods, asyncFamilyMethods, controller.isFriendlyToDebugger(), false);
decompiler.setRegularMethodCache(astCache);
Decompiler decompiler = new Decompiler(classes, splitMethods, controller.isFriendlyToDebugger(), false);
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {
decompiler.addGenerator(entry.getKey(), entry.getValue());
}
for (MethodReference injectedMethod : methodInjectors.keySet()) {
decompiler.addMethodToSkip(injectedMethod);
}
List<String> classOrder = decompiler.getClassOrdering(classes.getClassNames());
List<ClassNode> classNodes = new ArrayList<>();
for (String className : classOrder) {
List<PreparedClass> classNodes = new ArrayList<>();
for (String className : getClassOrdering(classes)) {
ClassHolder cls = classes.get(className);
for (MethodHolder method : cls.getMethods()) {
preprocessNativeMethod(method, decompiler);
preprocessNativeMethod(method);
if (controller.wasCancelled()) {
break;
}
}
classNodes.add(decompiler.decompile(cls));
classNodes.add(decompile(decompiler, cls));
}
return classNodes;
}
private void preprocessNativeMethod(MethodHolder method, Decompiler decompiler) {
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 decompile(Decompiler decompiler, ClassHolder cls) {
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.getProgram() == null) {
continue;
}
PreparedMethod preparedMethod = method.hasModifier(ElementModifier.NATIVE)
? decompileNative(method)
: decompile(decompiler, method);
clsNode.getMethods().add(preparedMethod);
}
return clsNode;
}
private PreparedMethod decompileNative(MethodHolder method) {
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();
try {
Class<?> generatorClass = Class.forName(generatorClassName, true, controller.getClassLoader());
generator = (Generator) generatorClass.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new DecompilationException("Error instantiating generator " + generatorClassName
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
}
}
return new PreparedMethod(method, null, generator, asyncMethods.contains(reference), null);
}
private PreparedMethod decompile(Decompiler decompiler, MethodHolder method) {
MethodReference reference = method.getReference();
if (asyncMethods.contains(reference)) {
AsyncMethodNode node = decompileAsync(decompiler, method);
ControlFlowEntry[] cfg = ProgramUtils.getLocationCFG(method.getProgram());
return new PreparedMethod(method, node, null, false, cfg);
} else {
AstCacheEntry entry = decompileRegular(decompiler, method);
return new PreparedMethod(method, entry.method, null, false, entry.cfg);
}
}
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) {
if (!method.getModifiers().contains(ElementModifier.NATIVE)
|| methodGenerators.get(method.getReference()) != null
|| methodInjectors.get(method.getReference()) != null) {
@ -512,7 +625,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
Generator generator = provider.apply(context);
if (generator != null) {
methodGenerators.put(method.getReference(), generator);
decompiler.addGenerator(method.getReference(), generator);
found = true;
break;
}
@ -521,7 +633,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
Injector injector = provider.apply(context);
if (injector != null) {
methodInjectors.put(method.getReference(), injector);
decompiler.addMethodToSkip(method.getReference());
found = true;
break;
}
@ -597,20 +708,12 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
return false;
}
private void emitCFG(DebugInformationEmitter emitter, Program program) {
emitCFG(emitter, ProgramUtils.getLocationCFG(program));
}
private void emitCFG(DebugInformationEmitter emitter, Statement program) {
emitCFG(emitter, LocationGraphBuilder.build(program));
}
private void emitCFG(DebugInformationEmitter emitter, Map<TextLocation, TextLocation[]> cfg) {
for (Map.Entry<TextLocation, TextLocation[]> entry : cfg.entrySet()) {
SourceLocation location = map(entry.getKey());
SourceLocation[] successors = new SourceLocation[entry.getValue().length];
for (int i = 0; i < entry.getValue().length; ++i) {
successors[i] = map(entry.getValue()[i]);
private void emitCFG(DebugInformationEmitter emitter, ControlFlowEntry[] cfg) {
for (ControlFlowEntry entry : cfg) {
SourceLocation location = map(entry.from);
SourceLocation[] successors = new SourceLocation[entry.to.length];
for (int i = 0; i < entry.to.length; ++i) {
successors[i] = map(entry.to[i]);
}
emitter.addSuccessors(location, successors);
}

View File

@ -0,0 +1,45 @@
/*
* 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;
}
}

View File

@ -0,0 +1,41 @@
/*
* 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 org.teavm.ast.ControlFlowEntry;
import org.teavm.ast.MethodNode;
import org.teavm.backend.javascript.spi.Generator;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference;
public class PreparedMethod {
public final MethodHolder methodHolder;
public final MethodReference reference;
public final MethodNode node;
public final Generator generator;
public final boolean async;
public final ControlFlowEntry[] cfg;
public PreparedMethod(MethodHolder method, MethodNode node, Generator generator, boolean async,
ControlFlowEntry[] cfg) {
this.reference = method.getReference();
this.methodHolder = method;
this.node = node;
this.generator = generator;
this.async = async;
this.cfg = cfg;
}
}

View File

@ -20,17 +20,13 @@ import org.teavm.ast.AssignmentStatement;
import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.AsyncMethodPart;
import org.teavm.ast.BinaryExpr;
import org.teavm.ast.ClassNode;
import org.teavm.ast.ConstantExpr;
import org.teavm.ast.FieldNode;
import org.teavm.ast.InitClassStatement;
import org.teavm.ast.InstanceOfExpr;
import org.teavm.ast.InvocationExpr;
import org.teavm.ast.MethodNode;
import org.teavm.ast.MethodNodeVisitor;
import org.teavm.ast.MonitorEnterStatement;
import org.teavm.ast.MonitorExitStatement;
import org.teavm.ast.NativeMethodNode;
import org.teavm.ast.NewArrayExpr;
import org.teavm.ast.NewExpr;
import org.teavm.ast.NewMultiArrayExpr;
@ -42,9 +38,12 @@ import org.teavm.ast.ThrowStatement;
import org.teavm.ast.TryCatchStatement;
import org.teavm.ast.UnaryExpr;
import org.teavm.backend.javascript.codegen.NameFrequencyConsumer;
import org.teavm.backend.javascript.decompile.PreparedClass;
import org.teavm.backend.javascript.decompile.PreparedMethod;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
@ -75,13 +74,13 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
this.asyncFamilyMethods = asyncFamilyMethods;
}
public void estimate(ClassNode cls) {
public void estimate(PreparedClass cls) {
// Declaration
consumer.consume(cls.getName());
if (cls.getParentName() != null) {
consumer.consume(cls.getParentName());
}
for (FieldNode field : cls.getFields()) {
for (FieldHolder field : cls.getClassHolder().getFields()) {
consumer.consume(new FieldReference(cls.getName(), field.getName()));
if (field.getModifiers().contains(ElementModifier.STATIC)) {
consumer.consume(cls.getName());
@ -91,26 +90,29 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
// Methods
MethodReader clinit = classSource.get(cls.getName()).getMethod(
new MethodDescriptor("<clinit>", ValueType.VOID));
for (MethodNode method : cls.getMethods()) {
consumer.consume(method.getReference());
if (asyncFamilyMethods.contains(method.getReference())) {
consumer.consume(method.getReference());
for (PreparedMethod method : cls.getMethods()) {
consumer.consume(method.reference);
if (asyncFamilyMethods.contains(method.reference)) {
consumer.consume(method.reference);
}
if (clinit != null && (method.getModifiers().contains(ElementModifier.STATIC)
|| method.getReference().getName().equals("<init>"))) {
consumer.consume(method.getReference());
if (clinit != null && (method.methodHolder.getModifiers().contains(ElementModifier.STATIC)
|| method.reference.getName().equals("<init>"))) {
consumer.consume(method.reference);
}
if (!method.getModifiers().contains(ElementModifier.STATIC)) {
consumer.consume(method.getReference().getDescriptor());
consumer.consume(method.getReference());
if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) {
consumer.consume(method.reference.getDescriptor());
consumer.consume(method.reference);
}
if (method.isAsync()) {
if (method.async) {
consumer.consumeFunction("$rt_nativeThread");
consumer.consumeFunction("$rt_nativeThread");
consumer.consumeFunction("$rt_resuming");
consumer.consumeFunction("$rt_invalidPointer");
}
method.acceptVisitor(this);
if (method.node != null) {
method.node.acceptVisitor(this);
}
}
if (clinit != null) {
@ -123,7 +125,7 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
if (cls.getParentName() != null) {
consumer.consume(cls.getParentName());
}
for (String iface : cls.getInterfaces()) {
for (String iface : cls.getClassHolder().getInterfaces()) {
consumer.consume(iface);
}
}
@ -142,10 +144,6 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
}
}
@Override
public void visit(NativeMethodNode methodNode) {
}
@Override
public void visit(AssignmentStatement statement) {
super.visit(statement);

View File

@ -31,17 +31,16 @@ import java.util.function.IntFunction;
import java.util.stream.Collectors;
import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.AsyncMethodPart;
import org.teavm.ast.ClassNode;
import org.teavm.ast.FieldNode;
import org.teavm.ast.MethodNode;
import org.teavm.ast.MethodNodeVisitor;
import org.teavm.ast.NativeMethodNode;
import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.VariableNode;
import org.teavm.backend.javascript.codegen.NamingOrderer;
import org.teavm.backend.javascript.codegen.NamingStrategy;
import org.teavm.backend.javascript.codegen.ScopedName;
import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.backend.javascript.decompile.PreparedClass;
import org.teavm.backend.javascript.decompile.PreparedMethod;
import org.teavm.backend.javascript.spi.GeneratorContext;
import org.teavm.common.ServiceRepository;
import org.teavm.debugging.information.DebugInformationEmitter;
@ -52,6 +51,7 @@ import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReference;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor;
@ -262,12 +262,12 @@ public class Renderer implements RenderingManager {
writer.newLine();
}
public void prepare(List<ClassNode> classes) {
public void prepare(List<PreparedClass> classes) {
if (minifying) {
NamingOrderer orderer = new NamingOrderer();
NameFrequencyEstimator estimator = new NameFrequencyEstimator(orderer, classSource, asyncMethods,
asyncFamilyMethods);
for (ClassNode cls : classes) {
for (PreparedClass cls : classes) {
estimator.estimate(cls);
}
naming.getScopeName();
@ -275,7 +275,7 @@ public class Renderer implements RenderingManager {
}
}
public boolean render(List<ClassNode> classes) throws RenderingException {
public boolean render(List<PreparedClass> classes) throws RenderingException {
if (minifying) {
try {
renderRuntimeAliases();
@ -284,7 +284,7 @@ public class Renderer implements RenderingManager {
}
}
int index = 0;
for (ClassNode cls : classes) {
for (PreparedClass cls : classes) {
int start = writer.getOffset();
renderDeclaration(cls);
renderMethodBodies(cls);
@ -297,7 +297,7 @@ public class Renderer implements RenderingManager {
return true;
}
private void renderDeclaration(ClassNode cls) throws RenderingException {
private void renderDeclaration(PreparedClass cls) throws RenderingException {
ScopedName jsName = naming.getNameFor(cls.getName());
debugEmitter.addClass(jsName.value, cls.getName(), cls.getParentName());
try {
@ -305,9 +305,9 @@ public class Renderer implements RenderingManager {
writer.append("()").ws().append("{")
.indent().softNewLine();
boolean thisAliased = false;
List<FieldNode> nonStaticFields = new ArrayList<>();
List<FieldNode> staticFields = new ArrayList<>();
for (FieldNode field : cls.getFields()) {
List<FieldHolder> nonStaticFields = new ArrayList<>();
List<FieldHolder> staticFields = new ArrayList<>();
for (FieldHolder field : cls.getClassHolder().getFields()) {
if (field.getModifiers().contains(ElementModifier.STATIC)) {
staticFields.add(field);
} else {
@ -318,11 +318,12 @@ public class Renderer implements RenderingManager {
thisAliased = true;
writer.append("var a").ws().append("=").ws().append("this;").ws();
}
if (!cls.getModifiers().contains(ElementModifier.INTERFACE) && cls.getParentName() != null) {
if (!cls.getClassHolder().getModifiers().contains(ElementModifier.INTERFACE)
&& cls.getParentName() != null) {
writer.appendClass(cls.getParentName()).append(".call(").append(thisAliased ? "a" : "this")
.append(");").softNewLine();
}
for (FieldNode field : nonStaticFields) {
for (FieldHolder field : nonStaticFields) {
Object value = field.getInitialValue();
if (value == null) {
value = getDefaultValue(field.getType());
@ -345,7 +346,7 @@ public class Renderer implements RenderingManager {
}
writer.newLine();
for (FieldNode field : staticFields) {
for (FieldHolder field : staticFields) {
Object value = field.getInitialValue();
if (value == null) {
value = getDefaultValue(field.getType());
@ -372,7 +373,7 @@ public class Renderer implements RenderingManager {
}
}
private void renderMethodBodies(ClassNode cls) throws RenderingException {
private void renderMethodBodies(PreparedClass cls) throws RenderingException {
debugEmitter.emitClass(cls.getName());
try {
MethodReader clinit = classSource.get(cls.getName()).getMethod(
@ -381,17 +382,17 @@ public class Renderer implements RenderingManager {
if (clinit != null) {
renderCallClinit(clinit, cls);
}
if (!cls.getModifiers().contains(ElementModifier.INTERFACE)) {
for (MethodNode method : cls.getMethods()) {
if (!method.getModifiers().contains(ElementModifier.STATIC)) {
if (method.getReference().getName().equals("<init>")) {
if (!cls.getClassHolder().getModifiers().contains(ElementModifier.INTERFACE)) {
for (PreparedMethod method : cls.getMethods()) {
if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) {
if (method.reference.getName().equals("<init>")) {
renderInitializer(method);
}
}
}
}
for (MethodNode method : cls.getMethods()) {
for (PreparedMethod method : cls.getMethods()) {
renderBody(method);
}
} catch (IOException e) {
@ -400,7 +401,7 @@ public class Renderer implements RenderingManager {
debugEmitter.emitClass(null);
}
private void renderCallClinit(MethodReader clinit, ClassNode cls)
private void renderCallClinit(MethodReader clinit, PreparedClass cls)
throws IOException {
boolean isAsync = asyncMethods.contains(clinit.getReference());
@ -464,13 +465,13 @@ public class Renderer implements RenderingManager {
writer.newLine();
}
private void renderEraseClinit(ClassNode cls) throws IOException {
private void renderEraseClinit(PreparedClass cls) throws IOException {
writer.appendClassInit(cls.getName()).ws().append("=").ws()
.appendFunction("$rt_eraseClinit").append("(")
.appendClass(cls.getName()).append(");").softNewLine();
}
private void renderClassMetadata(List<ClassNode> classes) {
private void renderClassMetadata(List<PreparedClass> classes) {
if (classes.isEmpty()) {
return;
}
@ -495,11 +496,11 @@ public class Renderer implements RenderingManager {
metadataSize = writer.getOffset() - start;
}
private void renderClassMetadataPortion(List<ClassNode> classes, ObjectIntMap<String> packageIndexes,
private void renderClassMetadataPortion(List<PreparedClass> classes, ObjectIntMap<String> packageIndexes,
Set<String> classesRequiringName) throws IOException {
writer.append("$rt_metadata([");
boolean first = true;
for (ClassNode cls : classes) {
for (PreparedClass cls : classes) {
if (!first) {
writer.append(',').softNewLine();
}
@ -526,8 +527,9 @@ public class Renderer implements RenderingManager {
}
writer.append(',').ws();
writer.append("[");
for (int i = 0; i < cls.getInterfaces().size(); ++i) {
String iface = cls.getInterfaces().get(i);
List<String> interfaces = new ArrayList<>(cls.getClassHolder().getInterfaces());
for (int i = 0; i < interfaces.size(); ++i) {
String iface = interfaces.get(i);
if (i > 0) {
writer.append(",").ws();
}
@ -535,8 +537,8 @@ public class Renderer implements RenderingManager {
}
writer.append("],").ws();
writer.append(ElementModifier.pack(cls.getModifiers())).append(',').ws();
writer.append(cls.getAccessLevel().ordinal()).append(',').ws();
writer.append(ElementModifier.pack(cls.getClassHolder().getModifiers())).append(',').ws();
writer.append(cls.getClassHolder().getLevel().ordinal()).append(',').ws();
MethodReader clinit = classSource.get(cls.getName()).getMethod(
new MethodDescriptor("<clinit>", ValueType.VOID));
@ -548,9 +550,9 @@ public class Renderer implements RenderingManager {
writer.append(',').ws();
List<MethodReference> virtualMethods = new ArrayList<>();
for (MethodNode method : cls.getMethods()) {
if (!method.getModifiers().contains(ElementModifier.STATIC)) {
virtualMethods.add(method.getReference());
for (PreparedMethod method : cls.getMethods()) {
if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) {
virtualMethods.add(method.reference);
}
}
collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods);
@ -561,11 +563,11 @@ public class Renderer implements RenderingManager {
writer.append("]);").newLine();
}
private ObjectIntMap<String> generatePackageMetadata(List<ClassNode> classes, Set<String> classesRequiringName)
private ObjectIntMap<String> generatePackageMetadata(List<PreparedClass> classes, Set<String> classesRequiringName)
throws IOException {
PackageNode root = new PackageNode(null);
for (ClassNode classNode : classes) {
for (PreparedClass classNode : classes) {
String className = classNode.getName();
if (!classesRequiringName.contains(className)) {
continue;
@ -704,8 +706,8 @@ public class Renderer implements RenderingManager {
return null;
}
private void renderInitializer(MethodNode method) throws IOException {
MethodReference ref = method.getReference();
private void renderInitializer(PreparedMethod method) throws IOException {
MethodReference ref = method.reference;
debugEmitter.emitMethod(ref.getDescriptor());
ScopedName name = naming.getNameForInit(ref);
renderFunctionDeclaration(name);
@ -789,18 +791,17 @@ public class Renderer implements RenderingManager {
writer.append(");").ws().append("}");
}
private void renderBody(MethodNode method) throws IOException {
private void renderBody(PreparedMethod method) throws IOException {
StatementRenderer statementRenderer = new StatementRenderer(context, writer);
statementRenderer.setCurrentMethod(method);
MethodReference ref = method.getReference();
MethodReference ref = method.reference;
debugEmitter.emitMethod(ref.getDescriptor());
ScopedName name = naming.getFullNameFor(ref);
renderFunctionDeclaration(name);
writer.append("(");
int startParam = 0;
if (method.getModifiers().contains(ElementModifier.STATIC)) {
if (method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) {
startParam = 1;
}
for (int i = startParam; i <= ref.parameterCount(); ++i) {
@ -811,7 +812,14 @@ public class Renderer implements RenderingManager {
}
writer.append(")").ws().append("{").softNewLine().indent();
method.acceptVisitor(new MethodBodyRenderer(statementRenderer));
MethodBodyRenderer renderer = new MethodBodyRenderer(statementRenderer);
statementRenderer.setCurrentMethod(method.node);
if (method.node != null) {
method.node.acceptVisitor(renderer);
} else {
renderer.renderNative(method);
}
writer.outdent().append("}");
if (name.scoped) {
writer.append(";");
@ -858,12 +866,11 @@ public class Renderer implements RenderingManager {
return context.getDependencyInfo();
}
@Override
public void visit(NativeMethodNode methodNode) {
public void renderNative(PreparedMethod method) {
try {
this.async = methodNode.isAsync();
statementRenderer.setAsync(methodNode.isAsync());
methodNode.getGenerator().generate(this, writer, methodNode.getReference());
this.async = method.async;
statementRenderer.setAsync(method.async);
method.generator.generate(this, writer, method.reference);
} catch (IOException e) {
throw new RenderingException("IO error occurred", e);
}

View File

@ -497,7 +497,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
private String generateVariableName(int index) {
if (!minifying) {
VariableNode variable = index < currentMethod.getVariables().size()
VariableNode variable = currentMethod != null && index < currentMethod.getVariables().size()
? currentMethod.getVariables().get(index)
: null;
if (variable != null && variable.getName() != null) {

View File

@ -91,7 +91,6 @@ import org.teavm.backend.wasm.render.WasmCRenderer;
import org.teavm.backend.wasm.render.WasmRenderer;
import org.teavm.backend.wasm.transformation.IndirectCallTraceTransformation;
import org.teavm.backend.wasm.transformation.MemoryAccessTraceTransformation;
import org.teavm.cache.AlwaysStaleCacheStatus;
import org.teavm.common.ServiceRepository;
import org.teavm.dependency.ClassDependency;
import org.teavm.dependency.DependencyAnalyzer;
@ -325,8 +324,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(),
vtableProvider, tagRegistry, binaryWriter, names);
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(),
AlwaysStaleCacheStatus.INSTANCE, new HashSet<>(), new HashSet<>(), false, true);
Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false, true);
WasmStringPool stringPool = classGenerator.getStringPool();
WasmGenerationContext context = new WasmGenerationContext(classes, module, controller.getDiagnostics(),
vtableProvider, tagRegistry, stringPool, names);

View File

@ -0,0 +1,29 @@
/*
* 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.cache;
import org.teavm.ast.ControlFlowEntry;
import org.teavm.ast.RegularMethodNode;
public class AstCacheEntry {
public RegularMethodNode method;
public ControlFlowEntry[] cfg;
public AstCacheEntry(RegularMethodNode method, ControlFlowEntry[] cfg) {
this.method = method;
this.cfg = cfg;
}
}

View File

@ -36,6 +36,7 @@ import org.teavm.ast.ConditionalExpr;
import org.teavm.ast.ConditionalStatement;
import org.teavm.ast.ConstantExpr;
import org.teavm.ast.ContinueStatement;
import org.teavm.ast.ControlFlowEntry;
import org.teavm.ast.Expr;
import org.teavm.ast.ExprVisitor;
import org.teavm.ast.GotoPartStatement;
@ -91,6 +92,23 @@ public class AstIO {
this.fileTable = fileTable;
}
public void write(DataOutput output, ControlFlowEntry[] cfg) throws IOException {
output.writeShort(cfg.length);
for (ControlFlowEntry entry : cfg) {
writeLocation(output, entry.from);
output.writeShort(entry.to.length);
for (TextLocation loc : entry.to) {
if (loc != null) {
output.writeByte(1);
writeLocation(output, loc);
} else {
output.writeByte(0);
}
}
}
}
public void write(DataOutput output, RegularMethodNode method) throws IOException {
output.writeInt(ElementModifier.pack(method.getModifiers()));
output.writeShort(method.getVariables().size());
@ -110,6 +128,23 @@ public class AstIO {
output.writeUTF(variable.getName() != null ? variable.getName() : "");
}
public ControlFlowEntry[] readControlFlow(DataInput input) throws IOException {
short size = input.readShort();
ControlFlowEntry[] result = new ControlFlowEntry[size];
for (int i = 0; i < size; ++i) {
TextLocation from = readLocation(input);
int toSize = input.readShort();
TextLocation[] to = new TextLocation[toSize];
for (int j = 0; j < toSize; ++j) {
if (input.readByte() != 0) {
to[j] = readLocation(input);
}
}
result[i] = new ControlFlowEntry(from, to);
}
return result;
}
public RegularMethodNode read(DataInput input, MethodReference method) throws IOException {
RegularMethodNode node = new RegularMethodNode(method);
node.getModifiers().addAll(unpackModifiers(input.readInt()));
@ -177,6 +212,15 @@ public class AstIO {
return modifiers;
}
private void writeLocation(DataOutput output, TextLocation location) throws IOException {
if (location == null || location.getFileName() == null) {
output.writeShort(-1);
} else {
output.writeShort(fileTable.lookup(location.getFileName()));
output.writeShort(location.getLine());
}
}
private class NodeWriter implements ExprVisitor, StatementVisitor {
private final DataOutput output;
@ -191,12 +235,7 @@ public class AstIO {
}
private void writeLocation(TextLocation location) throws IOException {
if (location == null || location.getFileName() == null) {
output.writeShort(-1);
} else {
output.writeShort(fileTable.lookup(location.getFileName()));
output.writeShort(location.getLine());
}
AstIO.this.writeLocation(output, location);
}
private void writeSequence(List<Statement> sequence) throws IOException {

View File

@ -31,6 +31,7 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.ControlFlowEntry;
import org.teavm.ast.RegularMethodNode;
import org.teavm.model.MethodReference;
@ -48,7 +49,7 @@ public class DiskMethodNodeCache implements MethodNodeCache {
}
@Override
public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) {
public AstCacheEntry get(MethodReference methodReference, CacheStatus cacheStatus) {
Item item = cache.get(methodReference);
if (item == null) {
item = new Item();
@ -58,20 +59,22 @@ public class DiskMethodNodeCache implements MethodNodeCache {
try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) {
DataInput input = new DataInputStream(stream);
if (!checkIfDependenciesChanged(input, cacheStatus)) {
item.node = astIO.read(input, methodReference);
RegularMethodNode node = astIO.read(input, methodReference);
ControlFlowEntry[] cfg = astIO.readControlFlow(input);
item.entry = new AstCacheEntry(node, cfg);
}
} catch (IOException e) {
// we could not read program, just leave it empty
}
}
}
return item.node;
return item.entry;
}
@Override
public void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies) {
public void store(MethodReference methodReference, AstCacheEntry entry, Supplier<String[]> dependencies) {
Item item = new Item();
item.node = node;
item.entry = entry;
item.dependencies = dependencies.get().clone();
cache.put(methodReference, item);
newMethods.add(methodReference);
@ -127,7 +130,8 @@ public class DiskMethodNodeCache implements MethodNodeCache {
for (String dependency : item.dependencies) {
output.writeUTF(dependency);
}
astIO.write(output, item.node);
astIO.write(output, item.entry.method);
astIO.write(output, item.entry.cfg);
}
}
for (MethodReference method : newAsyncMethods) {
@ -150,7 +154,7 @@ public class DiskMethodNodeCache implements MethodNodeCache {
}
private static class Item {
RegularMethodNode node;
AstCacheEntry entry;
String[] dependencies;
}

View File

@ -17,7 +17,6 @@ package org.teavm.cache;
import java.util.function.Supplier;
import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.RegularMethodNode;
import org.teavm.model.MethodReference;
public class EmptyMethodNodeCache implements MethodNodeCache {
@ -27,12 +26,12 @@ public class EmptyMethodNodeCache implements MethodNodeCache {
}
@Override
public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) {
public AstCacheEntry get(MethodReference methodReference, CacheStatus cacheStatus) {
return null;
}
@Override
public void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies) {
public void store(MethodReference methodReference, AstCacheEntry node, Supplier<String[]> dependencies) {
}
@Override

View File

@ -20,7 +20,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.RegularMethodNode;
import org.teavm.model.MethodReference;
public class InMemoryMethodNodeCache implements MethodNodeCache {
@ -30,7 +29,7 @@ public class InMemoryMethodNodeCache implements MethodNodeCache {
private Map<MethodReference, AsyncItem> newAsyncItems = new HashMap<>();
@Override
public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) {
public AstCacheEntry get(MethodReference methodReference, CacheStatus cacheStatus) {
RegularItem item = cache.get(methodReference);
if (item == null) {
return null;
@ -40,12 +39,12 @@ public class InMemoryMethodNodeCache implements MethodNodeCache {
return null;
}
return item.node;
return item.entry;
}
@Override
public void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies) {
newItems.put(methodReference, new RegularItem(node, dependencies.get().clone()));
public void store(MethodReference methodReference, AstCacheEntry entry, Supplier<String[]> dependencies) {
newItems.put(methodReference, new RegularItem(entry, dependencies.get().clone()));
}
@Override
@ -87,11 +86,11 @@ public class InMemoryMethodNodeCache implements MethodNodeCache {
}
static final class RegularItem {
final RegularMethodNode node;
final AstCacheEntry entry;
final String[] dependencies;
RegularItem(RegularMethodNode node, String[] dependencies) {
this.node = node;
RegularItem(AstCacheEntry entry, String[] dependencies) {
this.entry = entry;
this.dependencies = dependencies;
}
}

View File

@ -17,13 +17,12 @@ package org.teavm.cache;
import java.util.function.Supplier;
import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.RegularMethodNode;
import org.teavm.model.MethodReference;
public interface MethodNodeCache {
RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus);
AstCacheEntry get(MethodReference methodReference, CacheStatus cacheStatus);
void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies);
void store(MethodReference methodReference, AstCacheEntry entry, Supplier<String[]> dependencies);
AsyncMethodNode getAsync(MethodReference methodReference, CacheStatus cacheStatus);

View File

@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.teavm.ast.ControlFlowEntry;
import org.teavm.common.Graph;
import org.teavm.model.BasicBlock;
import org.teavm.model.Instruction;
@ -38,7 +39,7 @@ class LocationGraphBuilder {
private List<Set<TextLocation>> startLocations;
private List<AdditionalConnection> additionalConnections;
public Map<TextLocation, TextLocation[]> build(Program program) {
public ControlFlowEntry[] build(Program program) {
graphBuilder = new HashMap<>();
Graph graph = ProgramUtils.buildControlFlowGraph(program);
dfs(graph, program);
@ -100,13 +101,14 @@ class LocationGraphBuilder {
}
}
private Map<TextLocation, TextLocation[]> assemble() {
private ControlFlowEntry[] assemble() {
for (AdditionalConnection additionalConn : additionalConnections) {
for (TextLocation succ : additionalConn.successors) {
addEdge(additionalConn.location, succ);
}
}
Map<TextLocation, TextLocation[]> locationGraph = new HashMap<>();
ControlFlowEntry[] locationGraph = new ControlFlowEntry[graphBuilder.size()];
int index = 0;
for (Map.Entry<TextLocation, Set<TextLocation>> entry : graphBuilder.entrySet()) {
TextLocation[] successors = entry.getValue().toArray(new TextLocation[0]);
for (int i = 0; i < successors.length; ++i) {
@ -114,7 +116,7 @@ class LocationGraphBuilder {
successors[i] = null;
}
}
locationGraph.put(entry.getKey(), successors);
locationGraph[index++] = new ControlFlowEntry(entry.getKey(), successors);
}
return locationGraph;
}
@ -132,7 +134,7 @@ class LocationGraphBuilder {
TextLocation location;
Set<TextLocation> startLocations;
int block;
public Step(TextLocation location, Set<TextLocation> startLocations, int block) {
Step(TextLocation location, Set<TextLocation> startLocations, int block) {
this.location = location;
this.startLocations = startLocations;
this.block = block;
@ -142,7 +144,7 @@ class LocationGraphBuilder {
static class AdditionalConnection {
TextLocation location;
Set<TextLocation> successors;
public AdditionalConnection(TextLocation location, Set<TextLocation> successors) {
AdditionalConnection(TextLocation location, Set<TextLocation> successors) {
this.location = location;
this.successors = successors;
}

View File

@ -19,8 +19,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.ast.ControlFlowEntry;
import org.teavm.common.Graph;
import org.teavm.common.GraphBuilder;
import org.teavm.model.BasicBlock;
@ -72,7 +72,7 @@ public final class ProgramUtils {
return graphBuilder.build();
}
public static Map<TextLocation, TextLocation[]> getLocationCFG(Program program) {
public static ControlFlowEntry[] getLocationCFG(Program program) {
return new LocationGraphBuilder().build(program);
}

View File

@ -42,9 +42,9 @@ import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.typedarrays.NativeUint16Array;
import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.RegularMethodNode;
import org.teavm.backend.javascript.JavaScriptTarget;
import org.teavm.cache.AlwaysStaleCacheStatus;
import org.teavm.cache.AstCacheEntry;
import org.teavm.cache.CacheStatus;
import org.teavm.cache.InMemoryMethodNodeCache;
import org.teavm.cache.InMemoryProgramCache;
@ -237,7 +237,7 @@ public class IncrementalTest {
boolean capturing;
@Override
public void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies) {
public void store(MethodReference methodReference, AstCacheEntry node, Supplier<String[]> dependencies) {
super.store(methodReference, node, dependencies);
if (capturing) {
updatedMethods.add(methodReference);