mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Refactor things around decompiler, JS backend and control flow debug info
This commit is contained in:
parent
3be32a5851
commit
95092c4d82
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
28
core/src/main/java/org/teavm/ast/ControlFlowEntry.java
Normal file
28
core/src/main/java/org/teavm/ast/ControlFlowEntry.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
29
core/src/main/java/org/teavm/cache/AstCacheEntry.java
vendored
Normal file
29
core/src/main/java/org/teavm/cache/AstCacheEntry.java
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
51
core/src/main/java/org/teavm/cache/AstIO.java
vendored
51
core/src/main/java/org/teavm/cache/AstIO.java
vendored
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user