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(RegularMethodNode methodNode);
void visit(AsyncMethodNode 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.ConditionalExpr;
import org.teavm.ast.ConditionalStatement; import org.teavm.ast.ConditionalStatement;
import org.teavm.ast.ContinueStatement; import org.teavm.ast.ContinueStatement;
import org.teavm.ast.ControlFlowEntry;
import org.teavm.ast.Expr; import org.teavm.ast.Expr;
import org.teavm.ast.IdentifiedStatement; import org.teavm.ast.IdentifiedStatement;
import org.teavm.ast.InitClassStatement; import org.teavm.ast.InitClassStatement;
@ -56,7 +57,7 @@ public final class LocationGraphBuilder {
private LocationGraphBuilder() { private LocationGraphBuilder() {
} }
public static Map<TextLocation, TextLocation[]> build(Statement node) { public static ControlFlowEntry[] build(Statement node) {
Visitor visitor = new Visitor(); Visitor visitor = new Visitor();
node.acceptVisitor(visitor); node.acceptVisitor(visitor);
Graph graph = visitor.builder.build(); 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()) { 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; return result;
} }

View File

@ -18,24 +18,16 @@ package org.teavm.ast.decompilation;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Deque; import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.AsyncMethodPart; import org.teavm.ast.AsyncMethodPart;
import org.teavm.ast.BlockStatement; import org.teavm.ast.BlockStatement;
import org.teavm.ast.ClassNode;
import org.teavm.ast.FieldNode;
import org.teavm.ast.GotoPartStatement; import org.teavm.ast.GotoPartStatement;
import org.teavm.ast.IdentifiedStatement; import org.teavm.ast.IdentifiedStatement;
import org.teavm.ast.MethodNode;
import org.teavm.ast.NativeMethodNode;
import org.teavm.ast.RegularMethodNode; import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.SequentialStatement; import org.teavm.ast.SequentialStatement;
import org.teavm.ast.Statement; import org.teavm.ast.Statement;
@ -43,31 +35,19 @@ import org.teavm.ast.TryCatchStatement;
import org.teavm.ast.VariableNode; import org.teavm.ast.VariableNode;
import org.teavm.ast.WhileStatement; import org.teavm.ast.WhileStatement;
import org.teavm.ast.optimization.Optimizer; 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.Graph;
import org.teavm.common.GraphIndexer; import org.teavm.common.GraphIndexer;
import org.teavm.common.Loop; import org.teavm.common.Loop;
import org.teavm.common.LoopGraph; import org.teavm.common.LoopGraph;
import org.teavm.common.RangeTree; import org.teavm.common.RangeTree;
import org.teavm.interop.PlatformMarker;
import org.teavm.model.AnnotationHolder;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderSource; import org.teavm.model.ClassHolderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.TextLocation; import org.teavm.model.TextLocation;
import org.teavm.model.TryCatchBlock; import org.teavm.model.TryCatchBlock;
import org.teavm.model.ValueType;
import org.teavm.model.instructions.BinaryBranchingInstruction; import org.teavm.model.instructions.BinaryBranchingInstruction;
import org.teavm.model.instructions.BranchingInstruction; import org.teavm.model.instructions.BranchingInstruction;
import org.teavm.model.instructions.JumpInstruction; import org.teavm.model.instructions.JumpInstruction;
@ -78,8 +58,6 @@ import org.teavm.model.util.TypeInferer;
public class Decompiler { public class Decompiler {
private ClassHolderSource classSource; private ClassHolderSource classSource;
private ClassLoader classLoader;
private CacheStatus cacheStatus;
private Graph graph; private Graph graph;
private LoopGraph loopGraph; private LoopGraph loopGraph;
private GraphIndexer indexer; private GraphIndexer indexer;
@ -91,43 +69,21 @@ public class Decompiler {
private RangeTree codeTree; private RangeTree codeTree;
private RangeTree.Node currentNode; private RangeTree.Node currentNode;
private RangeTree.Node parentNode; private RangeTree.Node parentNode;
private Map<MethodReference, Generator> generators = new HashMap<>(); private Set<MethodReference> splitMethods;
private Set<MethodReference> methodsToSkip = new HashSet<>();
private MethodNodeCache regularMethodCache;
private Set<MethodReference> asyncMethods;
private Set<MethodReference> splitMethods = new HashSet<>();
private List<TryCatchBookmark> tryCatchBookmarks = new ArrayList<>(); private List<TryCatchBookmark> tryCatchBookmarks = new ArrayList<>();
private final AstDependencyExtractor astDependencyExtractor = new AstDependencyExtractor();
private Deque<Block> stack; private Deque<Block> stack;
private Program program; private Program program;
private boolean friendlyToDebugger; private boolean friendlyToDebugger;
private boolean moveConstants; private boolean moveConstants;
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, public Decompiler(ClassHolderSource classSource, Set<MethodReference> splitMethods, boolean friendlyToDebugger,
CacheStatus cacheStatus, Set<MethodReference> asyncMethods, Set<MethodReference> asyncFamilyMethods, boolean moveConstants) {
boolean friendlyToDebugger, boolean moveConstants) {
this.classSource = classSource; this.classSource = classSource;
this.classLoader = classLoader; this.splitMethods = splitMethods;
this.cacheStatus = cacheStatus;
this.asyncMethods = asyncMethods;
splitMethods.addAll(asyncMethods);
splitMethods.addAll(asyncFamilyMethods);
this.friendlyToDebugger = friendlyToDebugger; this.friendlyToDebugger = friendlyToDebugger;
this.moveConstants = moveConstants; 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 { static class Block {
Block parent; Block parent;
int parentOffset; int parentOffset;
@ -175,134 +131,7 @@ public class Decompiler {
int exceptionHandler; 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) { 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()); RegularMethodNode methodNode = new RegularMethodNode(method.getReference());
Program program = method.getProgram(); Program program = method.getProgram();
int[] targetBlocks = new int[program.basicBlockCount()]; int[] targetBlocks = new int[program.basicBlockCount()];
@ -331,21 +160,6 @@ public class Decompiler {
} }
public AsyncMethodNode decompileAsync(MethodHolder method) { 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()); AsyncMethodNode node = new AsyncMethodNode(method.getReference());
AsyncProgramSplitter splitter = new AsyncProgramSplitter(classSource, splitMethods); AsyncProgramSplitter splitter = new AsyncProgramSplitter(classSource, splitMethods);
splitter.split(method.getProgram()); splitter.split(method.getProgram());
@ -461,7 +275,6 @@ public class Decompiler {
block.body.addAll(generator.statements); block.body.addAll(generator.statements);
} }
List<Runnable> deferred = new ArrayList<>();
while (block.end == i + 1) { while (block.end == i + 1) {
Block oldBlock = block; Block oldBlock = block;
stack.pop(); stack.pop();
@ -493,9 +306,6 @@ public class Decompiler {
oldBlock.tryCatches.clear(); oldBlock.tryCatches.clear();
oldBlock.removeFrom(blockMap); oldBlock.removeFrom(blockMap);
} }
for (Runnable r : deferred) {
r.run();
}
if (generator.nextBlock != null && !isTrivialBlock(generator.nextBlock)) { if (generator.nextBlock != null && !isTrivialBlock(generator.nextBlock)) {
closeExpiredBookmarks(generator, generator.nextBlock.getTryCatchBlocks()); 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.RuntimeClassIntrinsic;
import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic; import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic;
import org.teavm.backend.c.intrinsic.StructureIntrinsic; import org.teavm.backend.c.intrinsic.StructureIntrinsic;
import org.teavm.cache.AlwaysStaleCacheStatus;
import org.teavm.dependency.ClassDependency; import org.teavm.dependency.ClassDependency;
import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyAnalyzer;
import org.teavm.dependency.DependencyListener; import org.teavm.dependency.DependencyListener;
@ -219,8 +218,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
TagRegistry tagRegistry = new TagRegistry(classes); TagRegistry tagRegistry = new TagRegistry(classes);
StringPool stringPool = new StringPool(); StringPool stringPool = new StringPool();
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false, true);
AlwaysStaleCacheStatus.INSTANCE, new HashSet<>(), new HashSet<>(), false, true);
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource()); Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
NameProvider nameProvider = new NameProvider(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.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import org.teavm.ast.ClassNode; import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.MethodNode; import org.teavm.ast.ControlFlowEntry;
import org.teavm.ast.RegularMethodNode; import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.Statement;
import org.teavm.ast.analysis.LocationGraphBuilder; import org.teavm.ast.analysis.LocationGraphBuilder;
import org.teavm.ast.decompilation.DecompilationException;
import org.teavm.ast.decompilation.Decompiler; 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;
@ -46,6 +46,8 @@ 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.SourceWriter; import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.backend.javascript.codegen.SourceWriterBuilder; 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.Renderer;
import org.teavm.backend.javascript.rendering.RenderingContext; import org.teavm.backend.javascript.rendering.RenderingContext;
import org.teavm.backend.javascript.rendering.RuntimeRenderer; 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.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.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.debugging.information.DebugInformationEmitter; import org.teavm.debugging.information.DebugInformationEmitter;
@ -68,9 +73,11 @@ 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.PlatformMarkers; import org.teavm.interop.PlatformMarkers;
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.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;
@ -120,6 +127,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
private ClassInitializerInsertionTransformer clinitInsertionTransformer; private ClassInitializerInsertionTransformer clinitInsertionTransformer;
private List<VirtualMethodContributor> customVirtualMethods = new ArrayList<>(); private List<VirtualMethodContributor> customVirtualMethods = new ArrayList<>();
private int topLevelNameLimit = 10000; private int topLevelNameLimit = 10000;
private AstDependencyExtractor dependencyExtractor = new AstDependencyExtractor();
@Override @Override
public List<ClassHolderTransformer> getTransformers() { public List<ClassHolderTransformer> getTransformers() {
@ -320,7 +328,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
} }
private void emit(ListableClassHolderSource classes, Writer writer, BuildTarget target) { private void emit(ListableClassHolderSource classes, Writer writer, BuildTarget target) {
List<ClassNode> clsNodes = modelToAst(classes); List<PreparedClass> clsNodes = modelToAst(classes);
if (controller.wasCancelled()) { if (controller.wasCancelled()) {
return; return;
} }
@ -351,16 +359,10 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
renderer.setMinifying(minifying); renderer.setMinifying(minifying);
renderer.setProgressConsumer(controller::reportProgress); renderer.setProgressConsumer(controller::reportProgress);
if (debugEmitter != null) { if (debugEmitter != null) {
for (ClassNode classNode : clsNodes) { for (PreparedClass preparedClass : clsNodes) {
ClassHolder cls = classes.get(classNode.getName()); for (PreparedMethod preparedMethod : preparedClass.getMethods()) {
for (MethodNode methodNode : classNode.getMethods()) { if (preparedMethod.cfg != null) {
if (methodNode instanceof RegularMethodNode) { emitCFG(debugEmitter, preparedMethod.cfg);
emitCFG(debugEmitter, ((RegularMethodNode) methodNode).getBody());
} else {
MethodHolder method = cls.getMethod(methodNode.getReference().getDescriptor());
if (method != null && method.getProgram() != null) {
emitCFG(debugEmitter, method.getProgram());
}
} }
} }
if (controller.wasCancelled()) { 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) + ")"; return STATS_NUM_FORMAT.format(size) + " (" + STATS_PERCENT_FORMAT.format((double) size / totalSize) + ")";
} }
static class PackageNode { private List<PreparedClass> modelToAst(ListableClassHolderSource classes) {
String name;
Map<String, PackageNode> children = new HashMap<>();
}
private List<ClassNode> modelToAst(ListableClassHolderSource classes) {
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(), AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(),
controller.getDiagnostics()); controller.getDiagnostics());
asyncFinder.find(classes); asyncFinder.find(classes);
asyncMethods.addAll(asyncFinder.getAsyncMethods()); asyncMethods.addAll(asyncFinder.getAsyncMethods());
asyncFamilyMethods.addAll(asyncFinder.getAsyncFamilyMethods()); asyncFamilyMethods.addAll(asyncFinder.getAsyncFamilyMethods());
Set<MethodReference> splitMethods = new HashSet<>(asyncMethods);
splitMethods.addAll(asyncFamilyMethods);
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), controller.getCacheStatus(), Decompiler decompiler = new Decompiler(classes, splitMethods, controller.isFriendlyToDebugger(), false);
asyncMethods, asyncFamilyMethods, controller.isFriendlyToDebugger(), false);
decompiler.setRegularMethodCache(astCache);
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) { List<PreparedClass> classNodes = new ArrayList<>();
decompiler.addGenerator(entry.getKey(), entry.getValue()); for (String className : getClassOrdering(classes)) {
}
for (MethodReference injectedMethod : methodInjectors.keySet()) {
decompiler.addMethodToSkip(injectedMethod);
}
List<String> classOrder = decompiler.getClassOrdering(classes.getClassNames());
List<ClassNode> classNodes = new ArrayList<>();
for (String className : classOrder) {
ClassHolder cls = classes.get(className); ClassHolder cls = classes.get(className);
for (MethodHolder method : cls.getMethods()) { for (MethodHolder method : cls.getMethods()) {
preprocessNativeMethod(method, decompiler); preprocessNativeMethod(method);
if (controller.wasCancelled()) { if (controller.wasCancelled()) {
break; break;
} }
} }
classNodes.add(decompiler.decompile(cls)); classNodes.add(decompile(decompiler, cls));
} }
return classNodes; 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) if (!method.getModifiers().contains(ElementModifier.NATIVE)
|| methodGenerators.get(method.getReference()) != null || methodGenerators.get(method.getReference()) != null
|| methodInjectors.get(method.getReference()) != null) { || methodInjectors.get(method.getReference()) != null) {
@ -512,7 +625,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
Generator generator = provider.apply(context); Generator generator = provider.apply(context);
if (generator != null) { if (generator != null) {
methodGenerators.put(method.getReference(), generator); methodGenerators.put(method.getReference(), generator);
decompiler.addGenerator(method.getReference(), generator);
found = true; found = true;
break; break;
} }
@ -521,7 +633,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
Injector injector = provider.apply(context); Injector injector = provider.apply(context);
if (injector != null) { if (injector != null) {
methodInjectors.put(method.getReference(), injector); methodInjectors.put(method.getReference(), injector);
decompiler.addMethodToSkip(method.getReference());
found = true; found = true;
break; break;
} }
@ -597,20 +708,12 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
return false; return false;
} }
private void emitCFG(DebugInformationEmitter emitter, Program program) { private void emitCFG(DebugInformationEmitter emitter, ControlFlowEntry[] cfg) {
emitCFG(emitter, ProgramUtils.getLocationCFG(program)); for (ControlFlowEntry entry : cfg) {
} SourceLocation location = map(entry.from);
SourceLocation[] successors = new SourceLocation[entry.to.length];
private void emitCFG(DebugInformationEmitter emitter, Statement program) { for (int i = 0; i < entry.to.length; ++i) {
emitCFG(emitter, LocationGraphBuilder.build(program)); successors[i] = map(entry.to[i]);
}
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]);
} }
emitter.addSuccessors(location, successors); 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.AsyncMethodNode;
import org.teavm.ast.AsyncMethodPart; import org.teavm.ast.AsyncMethodPart;
import org.teavm.ast.BinaryExpr; import org.teavm.ast.BinaryExpr;
import org.teavm.ast.ClassNode;
import org.teavm.ast.ConstantExpr; import org.teavm.ast.ConstantExpr;
import org.teavm.ast.FieldNode;
import org.teavm.ast.InitClassStatement; import org.teavm.ast.InitClassStatement;
import org.teavm.ast.InstanceOfExpr; import org.teavm.ast.InstanceOfExpr;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.ast.MethodNode;
import org.teavm.ast.MethodNodeVisitor; import org.teavm.ast.MethodNodeVisitor;
import org.teavm.ast.MonitorEnterStatement; import org.teavm.ast.MonitorEnterStatement;
import org.teavm.ast.MonitorExitStatement; import org.teavm.ast.MonitorExitStatement;
import org.teavm.ast.NativeMethodNode;
import org.teavm.ast.NewArrayExpr; import org.teavm.ast.NewArrayExpr;
import org.teavm.ast.NewExpr; import org.teavm.ast.NewExpr;
import org.teavm.ast.NewMultiArrayExpr; import org.teavm.ast.NewMultiArrayExpr;
@ -42,9 +38,12 @@ import org.teavm.ast.ThrowStatement;
import org.teavm.ast.TryCatchStatement; import org.teavm.ast.TryCatchStatement;
import org.teavm.ast.UnaryExpr; import org.teavm.ast.UnaryExpr;
import org.teavm.backend.javascript.codegen.NameFrequencyConsumer; 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.ClassReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.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.MethodReader;
@ -75,13 +74,13 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
this.asyncFamilyMethods = asyncFamilyMethods; this.asyncFamilyMethods = asyncFamilyMethods;
} }
public void estimate(ClassNode cls) { public void estimate(PreparedClass cls) {
// Declaration // Declaration
consumer.consume(cls.getName()); consumer.consume(cls.getName());
if (cls.getParentName() != null) { if (cls.getParentName() != null) {
consumer.consume(cls.getParentName()); consumer.consume(cls.getParentName());
} }
for (FieldNode field : cls.getFields()) { for (FieldHolder field : cls.getClassHolder().getFields()) {
consumer.consume(new FieldReference(cls.getName(), field.getName())); consumer.consume(new FieldReference(cls.getName(), field.getName()));
if (field.getModifiers().contains(ElementModifier.STATIC)) { if (field.getModifiers().contains(ElementModifier.STATIC)) {
consumer.consume(cls.getName()); consumer.consume(cls.getName());
@ -91,26 +90,29 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
// Methods // Methods
MethodReader clinit = classSource.get(cls.getName()).getMethod( MethodReader clinit = classSource.get(cls.getName()).getMethod(
new MethodDescriptor("<clinit>", ValueType.VOID)); new MethodDescriptor("<clinit>", ValueType.VOID));
for (MethodNode method : cls.getMethods()) { for (PreparedMethod method : cls.getMethods()) {
consumer.consume(method.getReference()); consumer.consume(method.reference);
if (asyncFamilyMethods.contains(method.getReference())) { if (asyncFamilyMethods.contains(method.reference)) {
consumer.consume(method.getReference()); consumer.consume(method.reference);
} }
if (clinit != null && (method.getModifiers().contains(ElementModifier.STATIC) if (clinit != null && (method.methodHolder.getModifiers().contains(ElementModifier.STATIC)
|| method.getReference().getName().equals("<init>"))) { || method.reference.getName().equals("<init>"))) {
consumer.consume(method.getReference()); consumer.consume(method.reference);
} }
if (!method.getModifiers().contains(ElementModifier.STATIC)) { if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) {
consumer.consume(method.getReference().getDescriptor()); consumer.consume(method.reference.getDescriptor());
consumer.consume(method.getReference()); consumer.consume(method.reference);
} }
if (method.isAsync()) { if (method.async) {
consumer.consumeFunction("$rt_nativeThread"); consumer.consumeFunction("$rt_nativeThread");
consumer.consumeFunction("$rt_nativeThread"); consumer.consumeFunction("$rt_nativeThread");
consumer.consumeFunction("$rt_resuming"); consumer.consumeFunction("$rt_resuming");
consumer.consumeFunction("$rt_invalidPointer"); consumer.consumeFunction("$rt_invalidPointer");
} }
method.acceptVisitor(this);
if (method.node != null) {
method.node.acceptVisitor(this);
}
} }
if (clinit != null) { if (clinit != null) {
@ -123,7 +125,7 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
if (cls.getParentName() != null) { if (cls.getParentName() != null) {
consumer.consume(cls.getParentName()); consumer.consume(cls.getParentName());
} }
for (String iface : cls.getInterfaces()) { for (String iface : cls.getClassHolder().getInterfaces()) {
consumer.consume(iface); consumer.consume(iface);
} }
} }
@ -142,10 +144,6 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
} }
} }
@Override
public void visit(NativeMethodNode methodNode) {
}
@Override @Override
public void visit(AssignmentStatement statement) { public void visit(AssignmentStatement statement) {
super.visit(statement); super.visit(statement);

View File

@ -31,17 +31,16 @@ import java.util.function.IntFunction;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.AsyncMethodPart; import org.teavm.ast.AsyncMethodPart;
import org.teavm.ast.ClassNode;
import org.teavm.ast.FieldNode;
import org.teavm.ast.MethodNode; import org.teavm.ast.MethodNode;
import org.teavm.ast.MethodNodeVisitor; import org.teavm.ast.MethodNodeVisitor;
import org.teavm.ast.NativeMethodNode;
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.NamingOrderer; import org.teavm.backend.javascript.codegen.NamingOrderer;
import org.teavm.backend.javascript.codegen.NamingStrategy; import org.teavm.backend.javascript.codegen.NamingStrategy;
import org.teavm.backend.javascript.codegen.ScopedName; import org.teavm.backend.javascript.codegen.ScopedName;
import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.backend.javascript.decompile.PreparedClass;
import org.teavm.backend.javascript.decompile.PreparedMethod;
import org.teavm.backend.javascript.spi.GeneratorContext; import org.teavm.backend.javascript.spi.GeneratorContext;
import org.teavm.common.ServiceRepository; import org.teavm.common.ServiceRepository;
import org.teavm.debugging.information.DebugInformationEmitter; import org.teavm.debugging.information.DebugInformationEmitter;
@ -52,6 +51,7 @@ import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
@ -262,12 +262,12 @@ public class Renderer implements RenderingManager {
writer.newLine(); writer.newLine();
} }
public void prepare(List<ClassNode> classes) { public void prepare(List<PreparedClass> classes) {
if (minifying) { if (minifying) {
NamingOrderer orderer = new NamingOrderer(); NamingOrderer orderer = new NamingOrderer();
NameFrequencyEstimator estimator = new NameFrequencyEstimator(orderer, classSource, asyncMethods, NameFrequencyEstimator estimator = new NameFrequencyEstimator(orderer, classSource, asyncMethods,
asyncFamilyMethods); asyncFamilyMethods);
for (ClassNode cls : classes) { for (PreparedClass cls : classes) {
estimator.estimate(cls); estimator.estimate(cls);
} }
naming.getScopeName(); 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) { if (minifying) {
try { try {
renderRuntimeAliases(); renderRuntimeAliases();
@ -284,7 +284,7 @@ public class Renderer implements RenderingManager {
} }
} }
int index = 0; int index = 0;
for (ClassNode cls : classes) { for (PreparedClass cls : classes) {
int start = writer.getOffset(); int start = writer.getOffset();
renderDeclaration(cls); renderDeclaration(cls);
renderMethodBodies(cls); renderMethodBodies(cls);
@ -297,7 +297,7 @@ public class Renderer implements RenderingManager {
return true; return true;
} }
private void renderDeclaration(ClassNode cls) throws RenderingException { private void renderDeclaration(PreparedClass cls) throws RenderingException {
ScopedName jsName = naming.getNameFor(cls.getName()); ScopedName jsName = naming.getNameFor(cls.getName());
debugEmitter.addClass(jsName.value, cls.getName(), cls.getParentName()); debugEmitter.addClass(jsName.value, cls.getName(), cls.getParentName());
try { try {
@ -305,9 +305,9 @@ public class Renderer implements RenderingManager {
writer.append("()").ws().append("{") writer.append("()").ws().append("{")
.indent().softNewLine(); .indent().softNewLine();
boolean thisAliased = false; boolean thisAliased = false;
List<FieldNode> nonStaticFields = new ArrayList<>(); List<FieldHolder> nonStaticFields = new ArrayList<>();
List<FieldNode> staticFields = new ArrayList<>(); List<FieldHolder> staticFields = new ArrayList<>();
for (FieldNode field : cls.getFields()) { for (FieldHolder field : cls.getClassHolder().getFields()) {
if (field.getModifiers().contains(ElementModifier.STATIC)) { if (field.getModifiers().contains(ElementModifier.STATIC)) {
staticFields.add(field); staticFields.add(field);
} else { } else {
@ -318,11 +318,12 @@ public class Renderer implements RenderingManager {
thisAliased = true; thisAliased = true;
writer.append("var a").ws().append("=").ws().append("this;").ws(); 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") writer.appendClass(cls.getParentName()).append(".call(").append(thisAliased ? "a" : "this")
.append(");").softNewLine(); .append(");").softNewLine();
} }
for (FieldNode field : nonStaticFields) { for (FieldHolder field : nonStaticFields) {
Object value = field.getInitialValue(); Object value = field.getInitialValue();
if (value == null) { if (value == null) {
value = getDefaultValue(field.getType()); value = getDefaultValue(field.getType());
@ -345,7 +346,7 @@ public class Renderer implements RenderingManager {
} }
writer.newLine(); writer.newLine();
for (FieldNode field : staticFields) { for (FieldHolder field : staticFields) {
Object value = field.getInitialValue(); Object value = field.getInitialValue();
if (value == null) { if (value == null) {
value = getDefaultValue(field.getType()); 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()); debugEmitter.emitClass(cls.getName());
try { try {
MethodReader clinit = classSource.get(cls.getName()).getMethod( MethodReader clinit = classSource.get(cls.getName()).getMethod(
@ -381,17 +382,17 @@ public class Renderer implements RenderingManager {
if (clinit != null) { if (clinit != null) {
renderCallClinit(clinit, cls); renderCallClinit(clinit, cls);
} }
if (!cls.getModifiers().contains(ElementModifier.INTERFACE)) { if (!cls.getClassHolder().getModifiers().contains(ElementModifier.INTERFACE)) {
for (MethodNode method : cls.getMethods()) { for (PreparedMethod method : cls.getMethods()) {
if (!method.getModifiers().contains(ElementModifier.STATIC)) { if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) {
if (method.getReference().getName().equals("<init>")) { if (method.reference.getName().equals("<init>")) {
renderInitializer(method); renderInitializer(method);
} }
} }
} }
} }
for (MethodNode method : cls.getMethods()) { for (PreparedMethod method : cls.getMethods()) {
renderBody(method); renderBody(method);
} }
} catch (IOException e) { } catch (IOException e) {
@ -400,7 +401,7 @@ public class Renderer implements RenderingManager {
debugEmitter.emitClass(null); debugEmitter.emitClass(null);
} }
private void renderCallClinit(MethodReader clinit, ClassNode cls) private void renderCallClinit(MethodReader clinit, PreparedClass cls)
throws IOException { throws IOException {
boolean isAsync = asyncMethods.contains(clinit.getReference()); boolean isAsync = asyncMethods.contains(clinit.getReference());
@ -464,13 +465,13 @@ public class Renderer implements RenderingManager {
writer.newLine(); writer.newLine();
} }
private void renderEraseClinit(ClassNode cls) throws IOException { private void renderEraseClinit(PreparedClass cls) throws IOException {
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<ClassNode> classes) { private void renderClassMetadata(List<PreparedClass> classes) {
if (classes.isEmpty()) { if (classes.isEmpty()) {
return; return;
} }
@ -495,11 +496,11 @@ public class Renderer implements RenderingManager {
metadataSize = writer.getOffset() - start; 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 { Set<String> classesRequiringName) throws IOException {
writer.append("$rt_metadata(["); writer.append("$rt_metadata([");
boolean first = true; boolean first = true;
for (ClassNode cls : classes) { for (PreparedClass cls : classes) {
if (!first) { if (!first) {
writer.append(',').softNewLine(); writer.append(',').softNewLine();
} }
@ -526,8 +527,9 @@ public class Renderer implements RenderingManager {
} }
writer.append(',').ws(); writer.append(',').ws();
writer.append("["); writer.append("[");
for (int i = 0; i < cls.getInterfaces().size(); ++i) { List<String> interfaces = new ArrayList<>(cls.getClassHolder().getInterfaces());
String iface = cls.getInterfaces().get(i); for (int i = 0; i < interfaces.size(); ++i) {
String iface = interfaces.get(i);
if (i > 0) { if (i > 0) {
writer.append(",").ws(); writer.append(",").ws();
} }
@ -535,8 +537,8 @@ public class Renderer implements RenderingManager {
} }
writer.append("],").ws(); writer.append("],").ws();
writer.append(ElementModifier.pack(cls.getModifiers())).append(',').ws(); writer.append(ElementModifier.pack(cls.getClassHolder().getModifiers())).append(',').ws();
writer.append(cls.getAccessLevel().ordinal()).append(',').ws(); writer.append(cls.getClassHolder().getLevel().ordinal()).append(',').ws();
MethodReader clinit = classSource.get(cls.getName()).getMethod( MethodReader clinit = classSource.get(cls.getName()).getMethod(
new MethodDescriptor("<clinit>", ValueType.VOID)); new MethodDescriptor("<clinit>", ValueType.VOID));
@ -548,9 +550,9 @@ public class Renderer implements RenderingManager {
writer.append(',').ws(); writer.append(',').ws();
List<MethodReference> virtualMethods = new ArrayList<>(); List<MethodReference> virtualMethods = new ArrayList<>();
for (MethodNode method : cls.getMethods()) { for (PreparedMethod method : cls.getMethods()) {
if (!method.getModifiers().contains(ElementModifier.STATIC)) { if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) {
virtualMethods.add(method.getReference()); virtualMethods.add(method.reference);
} }
} }
collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods); collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods);
@ -561,11 +563,11 @@ public class Renderer implements RenderingManager {
writer.append("]);").newLine(); 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 { throws IOException {
PackageNode root = new PackageNode(null); PackageNode root = new PackageNode(null);
for (ClassNode classNode : classes) { for (PreparedClass classNode : classes) {
String className = classNode.getName(); String className = classNode.getName();
if (!classesRequiringName.contains(className)) { if (!classesRequiringName.contains(className)) {
continue; continue;
@ -704,8 +706,8 @@ public class Renderer implements RenderingManager {
return null; return null;
} }
private void renderInitializer(MethodNode method) throws IOException { private void renderInitializer(PreparedMethod method) throws IOException {
MethodReference ref = method.getReference(); MethodReference ref = method.reference;
debugEmitter.emitMethod(ref.getDescriptor()); debugEmitter.emitMethod(ref.getDescriptor());
ScopedName name = naming.getNameForInit(ref); ScopedName name = naming.getNameForInit(ref);
renderFunctionDeclaration(name); renderFunctionDeclaration(name);
@ -789,18 +791,17 @@ public class Renderer implements RenderingManager {
writer.append(");").ws().append("}"); 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 statementRenderer = new StatementRenderer(context, writer);
statementRenderer.setCurrentMethod(method);
MethodReference ref = method.getReference(); MethodReference ref = method.reference;
debugEmitter.emitMethod(ref.getDescriptor()); debugEmitter.emitMethod(ref.getDescriptor());
ScopedName name = naming.getFullNameFor(ref); ScopedName name = naming.getFullNameFor(ref);
renderFunctionDeclaration(name); renderFunctionDeclaration(name);
writer.append("("); writer.append("(");
int startParam = 0; int startParam = 0;
if (method.getModifiers().contains(ElementModifier.STATIC)) { if (method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) {
startParam = 1; startParam = 1;
} }
for (int i = startParam; i <= ref.parameterCount(); ++i) { for (int i = startParam; i <= ref.parameterCount(); ++i) {
@ -811,7 +812,14 @@ public class Renderer implements RenderingManager {
} }
writer.append(")").ws().append("{").softNewLine().indent(); 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("}"); writer.outdent().append("}");
if (name.scoped) { if (name.scoped) {
writer.append(";"); writer.append(";");
@ -858,12 +866,11 @@ public class Renderer implements RenderingManager {
return context.getDependencyInfo(); return context.getDependencyInfo();
} }
@Override public void renderNative(PreparedMethod method) {
public void visit(NativeMethodNode methodNode) {
try { try {
this.async = methodNode.isAsync(); this.async = method.async;
statementRenderer.setAsync(methodNode.isAsync()); statementRenderer.setAsync(method.async);
methodNode.getGenerator().generate(this, writer, methodNode.getReference()); method.generator.generate(this, writer, method.reference);
} catch (IOException e) { } catch (IOException e) {
throw new RenderingException("IO error occurred", 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) { private String generateVariableName(int index) {
if (!minifying) { if (!minifying) {
VariableNode variable = index < currentMethod.getVariables().size() VariableNode variable = currentMethod != null && index < currentMethod.getVariables().size()
? currentMethod.getVariables().get(index) ? currentMethod.getVariables().get(index)
: null; : null;
if (variable != null && variable.getName() != 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.render.WasmRenderer;
import org.teavm.backend.wasm.transformation.IndirectCallTraceTransformation; import org.teavm.backend.wasm.transformation.IndirectCallTraceTransformation;
import org.teavm.backend.wasm.transformation.MemoryAccessTraceTransformation; import org.teavm.backend.wasm.transformation.MemoryAccessTraceTransformation;
import org.teavm.cache.AlwaysStaleCacheStatus;
import org.teavm.common.ServiceRepository; import org.teavm.common.ServiceRepository;
import org.teavm.dependency.ClassDependency; import org.teavm.dependency.ClassDependency;
import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyAnalyzer;
@ -325,8 +324,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(), WasmClassGenerator classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(),
vtableProvider, tagRegistry, binaryWriter, names); vtableProvider, tagRegistry, binaryWriter, names);
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false, true);
AlwaysStaleCacheStatus.INSTANCE, new HashSet<>(), new HashSet<>(), false, true);
WasmStringPool stringPool = classGenerator.getStringPool(); WasmStringPool stringPool = classGenerator.getStringPool();
WasmGenerationContext context = new WasmGenerationContext(classes, module, controller.getDiagnostics(), WasmGenerationContext context = new WasmGenerationContext(classes, module, controller.getDiagnostics(),
vtableProvider, tagRegistry, stringPool, names); 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.ConditionalStatement;
import org.teavm.ast.ConstantExpr; import org.teavm.ast.ConstantExpr;
import org.teavm.ast.ContinueStatement; import org.teavm.ast.ContinueStatement;
import org.teavm.ast.ControlFlowEntry;
import org.teavm.ast.Expr; import org.teavm.ast.Expr;
import org.teavm.ast.ExprVisitor; import org.teavm.ast.ExprVisitor;
import org.teavm.ast.GotoPartStatement; import org.teavm.ast.GotoPartStatement;
@ -91,6 +92,23 @@ public class AstIO {
this.fileTable = fileTable; 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 { public void write(DataOutput output, RegularMethodNode method) throws IOException {
output.writeInt(ElementModifier.pack(method.getModifiers())); output.writeInt(ElementModifier.pack(method.getModifiers()));
output.writeShort(method.getVariables().size()); output.writeShort(method.getVariables().size());
@ -110,6 +128,23 @@ public class AstIO {
output.writeUTF(variable.getName() != null ? variable.getName() : ""); 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 { public RegularMethodNode read(DataInput input, MethodReference method) throws IOException {
RegularMethodNode node = new RegularMethodNode(method); RegularMethodNode node = new RegularMethodNode(method);
node.getModifiers().addAll(unpackModifiers(input.readInt())); node.getModifiers().addAll(unpackModifiers(input.readInt()));
@ -177,6 +212,15 @@ public class AstIO {
return modifiers; 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 class NodeWriter implements ExprVisitor, StatementVisitor {
private final DataOutput output; private final DataOutput output;
@ -191,12 +235,7 @@ public class AstIO {
} }
private void writeLocation(TextLocation location) throws IOException { private void writeLocation(TextLocation location) throws IOException {
if (location == null || location.getFileName() == null) { AstIO.this.writeLocation(output, location);
output.writeShort(-1);
} else {
output.writeShort(fileTable.lookup(location.getFileName()));
output.writeShort(location.getLine());
}
} }
private void writeSequence(List<Statement> sequence) throws IOException { 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.Set;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.ControlFlowEntry;
import org.teavm.ast.RegularMethodNode; import org.teavm.ast.RegularMethodNode;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -48,7 +49,7 @@ public class DiskMethodNodeCache implements MethodNodeCache {
} }
@Override @Override
public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) { public AstCacheEntry get(MethodReference methodReference, CacheStatus cacheStatus) {
Item item = cache.get(methodReference); Item item = cache.get(methodReference);
if (item == null) { if (item == null) {
item = new Item(); item = new Item();
@ -58,20 +59,22 @@ public class DiskMethodNodeCache implements MethodNodeCache {
try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) { try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) {
DataInput input = new DataInputStream(stream); DataInput input = new DataInputStream(stream);
if (!checkIfDependenciesChanged(input, cacheStatus)) { 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) { } catch (IOException e) {
// we could not read program, just leave it empty // we could not read program, just leave it empty
} }
} }
} }
return item.node; return item.entry;
} }
@Override @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 item = new Item();
item.node = node; item.entry = entry;
item.dependencies = dependencies.get().clone(); item.dependencies = dependencies.get().clone();
cache.put(methodReference, item); cache.put(methodReference, item);
newMethods.add(methodReference); newMethods.add(methodReference);
@ -127,7 +130,8 @@ public class DiskMethodNodeCache implements MethodNodeCache {
for (String dependency : item.dependencies) { for (String dependency : item.dependencies) {
output.writeUTF(dependency); output.writeUTF(dependency);
} }
astIO.write(output, item.node); astIO.write(output, item.entry.method);
astIO.write(output, item.entry.cfg);
} }
} }
for (MethodReference method : newAsyncMethods) { for (MethodReference method : newAsyncMethods) {
@ -150,7 +154,7 @@ public class DiskMethodNodeCache implements MethodNodeCache {
} }
private static class Item { private static class Item {
RegularMethodNode node; AstCacheEntry entry;
String[] dependencies; String[] dependencies;
} }

View File

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

View File

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

View File

@ -17,13 +17,12 @@ package org.teavm.cache;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.RegularMethodNode;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
public interface MethodNodeCache { 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); AsyncMethodNode getAsync(MethodReference methodReference, CacheStatus cacheStatus);

View File

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

View File

@ -19,8 +19,8 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.teavm.ast.ControlFlowEntry;
import org.teavm.common.Graph; import org.teavm.common.Graph;
import org.teavm.common.GraphBuilder; import org.teavm.common.GraphBuilder;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
@ -72,7 +72,7 @@ public final class ProgramUtils {
return graphBuilder.build(); return graphBuilder.build();
} }
public static Map<TextLocation, TextLocation[]> getLocationCFG(Program program) { public static ControlFlowEntry[] getLocationCFG(Program program) {
return new LocationGraphBuilder().build(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.Undefined;
import org.mozilla.javascript.typedarrays.NativeUint16Array; import org.mozilla.javascript.typedarrays.NativeUint16Array;
import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.RegularMethodNode;
import org.teavm.backend.javascript.JavaScriptTarget; import org.teavm.backend.javascript.JavaScriptTarget;
import org.teavm.cache.AlwaysStaleCacheStatus; import org.teavm.cache.AlwaysStaleCacheStatus;
import org.teavm.cache.AstCacheEntry;
import org.teavm.cache.CacheStatus; import org.teavm.cache.CacheStatus;
import org.teavm.cache.InMemoryMethodNodeCache; import org.teavm.cache.InMemoryMethodNodeCache;
import org.teavm.cache.InMemoryProgramCache; import org.teavm.cache.InMemoryProgramCache;
@ -237,7 +237,7 @@ public class IncrementalTest {
boolean capturing; boolean capturing;
@Override @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); super.store(methodReference, node, dependencies);
if (capturing) { if (capturing) {
updatedMethods.add(methodReference); updatedMethods.add(methodReference);