mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
Fix various issues in debugger
This commit is contained in:
parent
46ebc2d694
commit
db97b7f732
|
@ -29,7 +29,6 @@
|
|||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="KEEP_LINE_BREAKS" value="false" />
|
||||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
|
||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
|
|
|
@ -40,7 +40,7 @@ script:
|
|||
- pushd tests/src/test/js
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- sleep 3
|
||||
- sleep 10
|
||||
- firefox index.html &
|
||||
- FIREFOX_PID=$!
|
||||
- node start.js $BASE_PATH/tests/target/js-tests
|
||||
|
|
|
@ -91,14 +91,16 @@ public class Decompiler {
|
|||
private List<TryCatchBookmark> tryCatchBookmarks = new ArrayList<>();
|
||||
private Deque<Block> stack;
|
||||
private Program program;
|
||||
private boolean friendlyToDebugger;
|
||||
|
||||
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, Set<MethodReference> asyncMethods,
|
||||
Set<MethodReference> asyncFamilyMethods) {
|
||||
Set<MethodReference> asyncFamilyMethods, boolean friendlyToDebugger) {
|
||||
this.classSource = classSource;
|
||||
this.classLoader = classLoader;
|
||||
this.asyncMethods = asyncMethods;
|
||||
splitMethods.addAll(asyncMethods);
|
||||
splitMethods.addAll(asyncFamilyMethods);
|
||||
this.friendlyToDebugger = friendlyToDebugger;
|
||||
}
|
||||
|
||||
public MethodNodeCache getRegularMethodCache() {
|
||||
|
@ -275,7 +277,7 @@ public class Decompiler {
|
|||
}
|
||||
|
||||
Optimizer optimizer = new Optimizer();
|
||||
optimizer.optimize(methodNode, method.getProgram());
|
||||
optimizer.optimize(methodNode, method.getProgram(), friendlyToDebugger);
|
||||
methodNode.getModifiers().addAll(method.getModifiers());
|
||||
|
||||
return methodNode;
|
||||
|
@ -339,7 +341,7 @@ public class Decompiler {
|
|||
}
|
||||
|
||||
Optimizer optimizer = new Optimizer();
|
||||
optimizer.optimize(node, splitter);
|
||||
optimizer.optimize(node, splitter, friendlyToDebugger);
|
||||
node.getModifiers().addAll(method.getModifiers());
|
||||
|
||||
return node;
|
||||
|
|
|
@ -18,7 +18,9 @@ package org.teavm.ast.optimization;
|
|||
import java.util.BitSet;
|
||||
import org.teavm.ast.AsyncMethodNode;
|
||||
import org.teavm.ast.AsyncMethodPart;
|
||||
import org.teavm.ast.MethodNode;
|
||||
import org.teavm.ast.RegularMethodNode;
|
||||
import org.teavm.ast.VariableNode;
|
||||
import org.teavm.common.Graph;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.Instruction;
|
||||
|
@ -31,13 +33,14 @@ import org.teavm.model.util.ProgramUtils;
|
|||
import org.teavm.model.util.UsageExtractor;
|
||||
|
||||
public class Optimizer {
|
||||
public void optimize(RegularMethodNode method, Program program) {
|
||||
public void optimize(RegularMethodNode method, Program program, boolean friendlyToDebugger) {
|
||||
ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariables().size());
|
||||
stats.analyze(program);
|
||||
boolean[] preservedVars = new boolean[stats.writes.length];
|
||||
BreakEliminator breakEliminator = new BreakEliminator();
|
||||
breakEliminator.eliminate(method.getBody());
|
||||
OptimizingVisitor optimizer = new OptimizingVisitor(preservedVars, stats.writes, stats.reads);
|
||||
OptimizingVisitor optimizer = new OptimizingVisitor(preservedVars, stats.writes, stats.reads,
|
||||
friendlyToDebugger);
|
||||
method.getBody().acceptVisitor(optimizer);
|
||||
method.setBody(optimizer.resultStmt);
|
||||
int paramCount = method.getReference().parameterCount();
|
||||
|
@ -55,7 +58,7 @@ public class Optimizer {
|
|||
}
|
||||
}
|
||||
|
||||
public void optimize(AsyncMethodNode method, AsyncProgramSplitter splitter) {
|
||||
public void optimize(AsyncMethodNode method, AsyncProgramSplitter splitter, boolean friendlyToDebugger) {
|
||||
LivenessAnalyzer liveness = new LivenessAnalyzer();
|
||||
liveness.analyze(splitter.getOriginalProgram());
|
||||
|
||||
|
@ -70,7 +73,8 @@ public class Optimizer {
|
|||
BreakEliminator breakEliminator = new BreakEliminator();
|
||||
breakEliminator.eliminate(part.getStatement());
|
||||
findEscapingLiveVars(liveness, cfg, splitter, i, preservedVars);
|
||||
OptimizingVisitor optimizer = new OptimizingVisitor(preservedVars, stats.writes, stats.reads);
|
||||
OptimizingVisitor optimizer = new OptimizingVisitor(preservedVars, stats.writes, stats.reads,
|
||||
friendlyToDebugger);
|
||||
part.getStatement().acceptVisitor(optimizer);
|
||||
part.setStatement(optimizer.resultStmt);
|
||||
}
|
||||
|
@ -93,6 +97,14 @@ public class Optimizer {
|
|||
}
|
||||
}
|
||||
|
||||
private void preserveDebuggableVars(boolean[] variablesToPreserve, MethodNode methodNode) {
|
||||
for (VariableNode varNode : methodNode.getVariables()) {
|
||||
if (varNode.getName() != null) {
|
||||
variablesToPreserve[varNode.getIndex()] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void findEscapingLiveVars(LivenessAnalyzer liveness, Graph cfg, AsyncProgramSplitter splitter,
|
||||
int partIndex, boolean[] output) {
|
||||
Program originalProgram = splitter.getOriginalProgram();
|
||||
|
|
|
@ -15,8 +15,11 @@
|
|||
*/
|
||||
package org.teavm.ast.optimization;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -58,6 +61,7 @@ import org.teavm.ast.UnaryOperation;
|
|||
import org.teavm.ast.UnwrapArrayExpr;
|
||||
import org.teavm.ast.VariableExpr;
|
||||
import org.teavm.ast.WhileStatement;
|
||||
import org.teavm.model.TextLocation;
|
||||
|
||||
class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
||||
private Expr resultExpr;
|
||||
|
@ -66,11 +70,17 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
|||
private final int[] writeFrequencies;
|
||||
private final int[] readFrequencies;
|
||||
private List<Statement> resultSequence;
|
||||
private boolean friendlyToDebugger;
|
||||
private TextLocation currentLocation;
|
||||
private Deque<TextLocation> locationStack = new LinkedList<>();
|
||||
private Deque<TextLocation> notNullLocationStack = new ArrayDeque<>();
|
||||
|
||||
OptimizingVisitor(boolean[] preservedVars, int[] writeFrequencies, int[] readFrequencies) {
|
||||
OptimizingVisitor(boolean[] preservedVars, int[] writeFrequencies, int[] readFrequencies,
|
||||
boolean friendlyToDebugger) {
|
||||
this.preservedVars = preservedVars;
|
||||
this.writeFrequencies = writeFrequencies;
|
||||
this.readFrequencies = readFrequencies;
|
||||
this.friendlyToDebugger = friendlyToDebugger;
|
||||
}
|
||||
|
||||
private static boolean isZero(Expr expr) {
|
||||
|
@ -81,74 +91,100 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
|||
return expr instanceof BinaryExpr && ((BinaryExpr) expr).getOperation() == BinaryOperation.COMPARE;
|
||||
}
|
||||
|
||||
private void pushLocation(TextLocation location) {
|
||||
locationStack.push(location);
|
||||
if (location != null) {
|
||||
if (currentLocation != null) {
|
||||
notNullLocationStack.push(currentLocation);
|
||||
}
|
||||
currentLocation = location;
|
||||
}
|
||||
}
|
||||
|
||||
private void popLocation() {
|
||||
if (locationStack.pop() != null) {
|
||||
currentLocation = notNullLocationStack.pollFirst();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BinaryExpr expr) {
|
||||
switch (expr.getOperation()) {
|
||||
case AND:
|
||||
case OR:
|
||||
resultExpr = expr;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
expr.getSecondOperand().acceptVisitor(this);
|
||||
Expr b = resultExpr;
|
||||
if (b instanceof ConstantExpr && expr.getOperation() == BinaryOperation.SUBTRACT) {
|
||||
if (tryMakePositive((ConstantExpr) b)) {
|
||||
expr.setOperation(BinaryOperation.ADD);
|
||||
}
|
||||
}
|
||||
expr.getFirstOperand().acceptVisitor(this);
|
||||
Expr a = resultExpr;
|
||||
Expr p = a;
|
||||
Expr q = b;
|
||||
boolean invert = false;
|
||||
if (isZero(p)) {
|
||||
Expr tmp = p;
|
||||
p = q;
|
||||
q = tmp;
|
||||
invert = true;
|
||||
}
|
||||
if (isComparison(p) && isZero(q)) {
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
switch (expr.getOperation()) {
|
||||
case EQUALS:
|
||||
case NOT_EQUALS:
|
||||
case LESS:
|
||||
case LESS_OR_EQUALS:
|
||||
case GREATER:
|
||||
case GREATER_OR_EQUALS: {
|
||||
BinaryExpr comparison = (BinaryExpr) p;
|
||||
Expr result = BinaryExpr.binary(expr.getOperation(), comparison.getType(),
|
||||
comparison.getFirstOperand(), comparison.getSecondOperand());
|
||||
result.setLocation(comparison.getLocation());
|
||||
if (invert) {
|
||||
result = ExprOptimizer.invert(result);
|
||||
}
|
||||
resultExpr = result;
|
||||
case AND:
|
||||
case OR:
|
||||
resultExpr = expr;
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
expr.getSecondOperand().acceptVisitor(this);
|
||||
Expr b = resultExpr;
|
||||
if (b instanceof ConstantExpr && expr.getOperation() == BinaryOperation.SUBTRACT) {
|
||||
if (tryMakePositive((ConstantExpr) b)) {
|
||||
expr.setOperation(BinaryOperation.ADD);
|
||||
}
|
||||
}
|
||||
expr.getFirstOperand().acceptVisitor(this);
|
||||
Expr a = resultExpr;
|
||||
Expr p = a;
|
||||
Expr q = b;
|
||||
boolean invert = false;
|
||||
if (isZero(p)) {
|
||||
Expr tmp = p;
|
||||
p = q;
|
||||
q = tmp;
|
||||
invert = true;
|
||||
}
|
||||
if (isComparison(p) && isZero(q)) {
|
||||
switch (expr.getOperation()) {
|
||||
case EQUALS:
|
||||
case NOT_EQUALS:
|
||||
case LESS:
|
||||
case LESS_OR_EQUALS:
|
||||
case GREATER:
|
||||
case GREATER_OR_EQUALS: {
|
||||
BinaryExpr comparison = (BinaryExpr) p;
|
||||
Expr result = BinaryExpr.binary(expr.getOperation(), comparison.getType(),
|
||||
comparison.getFirstOperand(), comparison.getSecondOperand());
|
||||
result.setLocation(comparison.getLocation());
|
||||
if (invert) {
|
||||
result = ExprOptimizer.invert(result);
|
||||
}
|
||||
resultExpr = result;
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
expr.setFirstOperand(a);
|
||||
expr.setSecondOperand(b);
|
||||
resultExpr = expr;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
expr.setFirstOperand(a);
|
||||
expr.setSecondOperand(b);
|
||||
resultExpr = expr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(UnaryExpr expr) {
|
||||
expr.getOperand().acceptVisitor(this);
|
||||
Expr operand = resultExpr;
|
||||
if (expr.getOperation() == UnaryOperation.NEGATE && operand instanceof ConstantExpr) {
|
||||
ConstantExpr constantExpr = (ConstantExpr) operand;
|
||||
if (tryMakePositive(constantExpr)) {
|
||||
resultExpr = expr;
|
||||
return;
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
expr.getOperand().acceptVisitor(this);
|
||||
Expr operand = resultExpr;
|
||||
if (expr.getOperation() == UnaryOperation.NEGATE && operand instanceof ConstantExpr) {
|
||||
ConstantExpr constantExpr = (ConstantExpr) operand;
|
||||
if (tryMakePositive(constantExpr)) {
|
||||
resultExpr = expr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
expr.setOperand(operand);
|
||||
resultExpr = expr;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
expr.setOperand(operand);
|
||||
resultExpr = expr;
|
||||
}
|
||||
|
||||
private boolean tryMakePositive(ConstantExpr constantExpr) {
|
||||
|
@ -177,16 +213,21 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(ConditionalExpr expr) {
|
||||
expr.getCondition().acceptVisitor(this);
|
||||
Expr cond = resultExpr;
|
||||
expr.getConsequent().acceptVisitor(this);
|
||||
Expr consequent = resultExpr;
|
||||
expr.getAlternative().acceptVisitor(this);
|
||||
Expr alternative = resultExpr;
|
||||
expr.setCondition(cond);
|
||||
expr.setConsequent(consequent);
|
||||
expr.setAlternative(alternative);
|
||||
resultExpr = expr;
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
expr.getCondition().acceptVisitor(this);
|
||||
Expr cond = resultExpr;
|
||||
expr.getConsequent().acceptVisitor(this);
|
||||
Expr consequent = resultExpr;
|
||||
expr.getAlternative().acceptVisitor(this);
|
||||
Expr alternative = resultExpr;
|
||||
expr.setCondition(cond);
|
||||
expr.setConsequent(consequent);
|
||||
expr.setAlternative(alternative);
|
||||
resultExpr = expr;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -196,70 +237,92 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(VariableExpr expr) {
|
||||
int index = expr.getIndex();
|
||||
resultExpr = expr;
|
||||
if (writeFrequencies[index] != 1) {
|
||||
return;
|
||||
}
|
||||
if (readFrequencies[index] != 1 || preservedVars[index]) {
|
||||
return;
|
||||
}
|
||||
if (resultSequence.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Statement last = resultSequence.get(resultSequence.size() - 1);
|
||||
if (!(last instanceof AssignmentStatement)) {
|
||||
return;
|
||||
}
|
||||
AssignmentStatement assignment = (AssignmentStatement) last;
|
||||
if (assignment.isAsync()) {
|
||||
return;
|
||||
}
|
||||
if (!(assignment.getLeftValue() instanceof VariableExpr)) {
|
||||
return;
|
||||
}
|
||||
VariableExpr var = (VariableExpr) assignment.getLeftValue();
|
||||
if (var.getLocation() != null && assignment.getLocation() != null
|
||||
&& !assignment.getLocation().equals(var.getLocation())) {
|
||||
return;
|
||||
}
|
||||
if (var.getIndex() == index) {
|
||||
resultSequence.remove(resultSequence.size() - 1);
|
||||
assignment.getRightValue().setLocation(assignment.getLocation());
|
||||
assignment.getRightValue().acceptVisitor(this);
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
int index = expr.getIndex();
|
||||
resultExpr = expr;
|
||||
if (writeFrequencies[index] != 1) {
|
||||
return;
|
||||
}
|
||||
if (readFrequencies[index] != 1 || preservedVars[index]) {
|
||||
return;
|
||||
}
|
||||
if (resultSequence.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Statement last = resultSequence.get(resultSequence.size() - 1);
|
||||
if (!(last instanceof AssignmentStatement)) {
|
||||
return;
|
||||
}
|
||||
AssignmentStatement assignment = (AssignmentStatement) last;
|
||||
if (assignment.isAsync()) {
|
||||
return;
|
||||
}
|
||||
if (!(assignment.getLeftValue() instanceof VariableExpr)) {
|
||||
return;
|
||||
}
|
||||
VariableExpr var = (VariableExpr) assignment.getLeftValue();
|
||||
if (friendlyToDebugger) {
|
||||
if (currentLocation != null && assignment.getLocation() != null
|
||||
&& !assignment.getLocation().equals(currentLocation)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (var.getIndex() == index) {
|
||||
resultSequence.remove(resultSequence.size() - 1);
|
||||
assignment.getRightValue().setLocation(assignment.getLocation());
|
||||
assignment.getRightValue().acceptVisitor(this);
|
||||
}
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SubscriptExpr expr) {
|
||||
expr.getIndex().acceptVisitor(this);
|
||||
Expr index = resultExpr;
|
||||
expr.getArray().acceptVisitor(this);
|
||||
Expr array = resultExpr;
|
||||
expr.setArray(array);
|
||||
expr.setIndex(index);
|
||||
resultExpr = expr;
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
expr.getIndex().acceptVisitor(this);
|
||||
Expr index = resultExpr;
|
||||
expr.getArray().acceptVisitor(this);
|
||||
Expr array = resultExpr;
|
||||
expr.setArray(array);
|
||||
expr.setIndex(index);
|
||||
resultExpr = expr;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(UnwrapArrayExpr expr) {
|
||||
expr.getArray().acceptVisitor(this);
|
||||
Expr arrayExpr = resultExpr;
|
||||
expr.setArray(arrayExpr);
|
||||
resultExpr = expr;
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
expr.getArray().acceptVisitor(this);
|
||||
Expr arrayExpr = resultExpr;
|
||||
expr.setArray(arrayExpr);
|
||||
resultExpr = expr;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvocationExpr expr) {
|
||||
Expr[] args = new Expr[expr.getArguments().size()];
|
||||
for (int i = expr.getArguments().size() - 1; i >= 0; --i) {
|
||||
expr.getArguments().get(i).acceptVisitor(this);
|
||||
args[i] = resultExpr;
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
Expr[] args = new Expr[expr.getArguments().size()];
|
||||
for (int i = expr.getArguments().size() - 1; i >= 0; --i) {
|
||||
expr.getArguments().get(i).acceptVisitor(this);
|
||||
args[i] = resultExpr;
|
||||
}
|
||||
for (int i = 0; i < args.length; ++i) {
|
||||
expr.getArguments().set(i, args[i]);
|
||||
}
|
||||
resultExpr = expr;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
for (int i = 0; i < args.length; ++i) {
|
||||
expr.getArguments().set(i, args[i]);
|
||||
}
|
||||
resultExpr = expr;
|
||||
}
|
||||
|
||||
private boolean tryApplyConstructor(InvocationExpr expr) {
|
||||
|
@ -303,12 +366,17 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(QualificationExpr expr) {
|
||||
if (expr.getQualified() != null) {
|
||||
expr.getQualified().acceptVisitor(this);
|
||||
Expr qualified = resultExpr;
|
||||
expr.setQualified(qualified);
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
if (expr.getQualified() != null) {
|
||||
expr.getQualified().acceptVisitor(this);
|
||||
Expr qualified = resultExpr;
|
||||
expr.setQualified(qualified);
|
||||
}
|
||||
resultExpr = expr;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
resultExpr = expr;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -318,64 +386,94 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(NewArrayExpr expr) {
|
||||
expr.getLength().acceptVisitor(this);
|
||||
Expr length = resultExpr;
|
||||
expr.setLength(length);
|
||||
resultExpr = expr;
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
expr.getLength().acceptVisitor(this);
|
||||
Expr length = resultExpr;
|
||||
expr.setLength(length);
|
||||
resultExpr = expr;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(NewMultiArrayExpr expr) {
|
||||
for (int i = 0; i < expr.getDimensions().size(); ++i) {
|
||||
Expr dimension = expr.getDimensions().get(i);
|
||||
dimension.acceptVisitor(this);
|
||||
expr.getDimensions().set(i, resultExpr);
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
for (int i = 0; i < expr.getDimensions().size(); ++i) {
|
||||
Expr dimension = expr.getDimensions().get(i);
|
||||
dimension.acceptVisitor(this);
|
||||
expr.getDimensions().set(i, resultExpr);
|
||||
}
|
||||
resultExpr = expr;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
resultExpr = expr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InstanceOfExpr expr) {
|
||||
expr.getExpr().acceptVisitor(this);
|
||||
expr.setExpr(resultExpr);
|
||||
resultExpr = expr;
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
expr.getExpr().acceptVisitor(this);
|
||||
expr.setExpr(resultExpr);
|
||||
resultExpr = expr;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(CastExpr expr) {
|
||||
expr.getValue().acceptVisitor(this);
|
||||
expr.setValue(resultExpr);
|
||||
resultExpr = expr;
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
expr.getValue().acceptVisitor(this);
|
||||
expr.setValue(resultExpr);
|
||||
resultExpr = expr;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(PrimitiveCastExpr expr) {
|
||||
expr.getValue().acceptVisitor(this);
|
||||
expr.setValue(resultExpr);
|
||||
resultExpr = expr;
|
||||
pushLocation(expr.getLocation());
|
||||
try {
|
||||
expr.getValue().acceptVisitor(this);
|
||||
expr.setValue(resultExpr);
|
||||
resultExpr = expr;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(AssignmentStatement statement) {
|
||||
if (statement.getLeftValue() == null) {
|
||||
statement.getRightValue().acceptVisitor(this);
|
||||
if (resultExpr instanceof InvocationExpr && tryApplyConstructor((InvocationExpr) resultExpr)) {
|
||||
resultStmt = new SequentialStatement();
|
||||
pushLocation(statement.getLocation());
|
||||
try {
|
||||
if (statement.getLeftValue() == null) {
|
||||
statement.getRightValue().acceptVisitor(this);
|
||||
if (resultExpr instanceof InvocationExpr && tryApplyConstructor((InvocationExpr) resultExpr)) {
|
||||
resultStmt = new SequentialStatement();
|
||||
} else {
|
||||
statement.setRightValue(resultExpr);
|
||||
resultStmt = statement;
|
||||
}
|
||||
} else {
|
||||
statement.setRightValue(resultExpr);
|
||||
statement.getRightValue().acceptVisitor(this);
|
||||
Expr right = resultExpr;
|
||||
Expr left = statement.getLeftValue();
|
||||
if (!(statement.getLeftValue() instanceof VariableExpr)) {
|
||||
statement.getLeftValue().acceptVisitor(this);
|
||||
left = resultExpr;
|
||||
}
|
||||
statement.setLeftValue(left);
|
||||
statement.setRightValue(right);
|
||||
resultStmt = statement;
|
||||
}
|
||||
} else {
|
||||
statement.getRightValue().acceptVisitor(this);
|
||||
Expr right = resultExpr;
|
||||
Expr left = statement.getLeftValue();
|
||||
if (!(statement.getLeftValue() instanceof VariableExpr)) {
|
||||
statement.getLeftValue().acceptVisitor(this);
|
||||
left = resultExpr;
|
||||
}
|
||||
statement.setLeftValue(left);
|
||||
statement.setRightValue(right);
|
||||
resultStmt = statement;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -724,23 +822,38 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(ReturnStatement statement) {
|
||||
if (statement.getResult() != null) {
|
||||
statement.getResult().acceptVisitor(this);
|
||||
statement.setResult(resultExpr);
|
||||
pushLocation(statement.getLocation());
|
||||
try {
|
||||
if (statement.getResult() != null) {
|
||||
statement.getResult().acceptVisitor(this);
|
||||
statement.setResult(resultExpr);
|
||||
}
|
||||
resultStmt = statement;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
resultStmt = statement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ThrowStatement statement) {
|
||||
statement.getException().acceptVisitor(this);
|
||||
statement.setException(resultExpr);
|
||||
resultStmt = statement;
|
||||
pushLocation(statement.getLocation());
|
||||
try {
|
||||
statement.getException().acceptVisitor(this);
|
||||
statement.setException(resultExpr);
|
||||
resultStmt = statement;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InitClassStatement statement) {
|
||||
resultStmt = statement;
|
||||
pushLocation(statement.getLocation());
|
||||
try {
|
||||
resultStmt = statement;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -761,15 +874,25 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(MonitorEnterStatement statement) {
|
||||
statement.getObjectRef().acceptVisitor(this);
|
||||
statement.setObjectRef(resultExpr);
|
||||
resultStmt = statement;
|
||||
pushLocation(statement.getLocation());
|
||||
try {
|
||||
statement.getObjectRef().acceptVisitor(this);
|
||||
statement.setObjectRef(resultExpr);
|
||||
resultStmt = statement;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MonitorExitStatement statement) {
|
||||
statement.getObjectRef().acceptVisitor(this);
|
||||
statement.setObjectRef(resultExpr);
|
||||
resultStmt = statement;
|
||||
pushLocation(statement.getLocation());
|
||||
try {
|
||||
statement.getObjectRef().acceptVisitor(this);
|
||||
statement.setObjectRef(resultExpr);
|
||||
resultStmt = statement;
|
||||
} finally {
|
||||
popLocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -294,7 +294,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
asyncMethods.addAll(asyncFinder.getAsyncMethods());
|
||||
asyncFamilyMethods.addAll(asyncFinder.getAsyncFamilyMethods());
|
||||
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), asyncMethods, asyncFamilyMethods);
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), asyncMethods, asyncFamilyMethods,
|
||||
controller.isFriendlyToDebugger());
|
||||
decompiler.setRegularMethodCache(controller.isIncremental() ? astCache : null);
|
||||
|
||||
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {
|
||||
|
|
|
@ -926,7 +926,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
|||
break;
|
||||
}
|
||||
if (expr.getLocation() != null) {
|
||||
pushLocation(expr.getLocation());
|
||||
popLocation();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RenderingException("IO error occured", e);
|
||||
|
|
|
@ -291,7 +291,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
classes, vtableProvider, tagRegistry, binaryWriter);
|
||||
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
|
||||
new HashSet<>());
|
||||
new HashSet<>(), false);
|
||||
WasmStringPool stringPool = classGenerator.getStringPool();
|
||||
WasmGenerationContext context = new WasmGenerationContext(classes, module, controller.getDiagnostics(),
|
||||
vtableProvider, tagRegistry, stringPool);
|
||||
|
|
|
@ -34,8 +34,8 @@ public class Debugger {
|
|||
private ConcurrentMap<String, ConcurrentMap<DebugInformation, Object>> debugInformationFileMap =
|
||||
new ConcurrentHashMap<>();
|
||||
private ConcurrentMap<DebugInformation, String> scriptMap = new ConcurrentHashMap<>();
|
||||
final ConcurrentMap<JavaScriptBreakpoint, Breakpoint> breakpointMap = new ConcurrentHashMap<>();
|
||||
ConcurrentMap<Breakpoint, Object> breakpoints = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<JavaScriptBreakpoint, Breakpoint> breakpointMap = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Breakpoint, Object> breakpoints = new ConcurrentHashMap<>();
|
||||
private volatile CallFrame[] callStack;
|
||||
|
||||
public Debugger(JavaScriptDebugger javaScriptDebugger, DebugInformationProvider debugInformationProvider) {
|
||||
|
@ -126,12 +126,12 @@ public class Debugger {
|
|||
javaScriptDebugger.resume();
|
||||
}
|
||||
|
||||
private static class CallSiteSuccessorFinder implements DebuggerCallSiteVisitor {
|
||||
static class CallSiteSuccessorFinder implements DebuggerCallSiteVisitor {
|
||||
private DebugInformation debugInfo;
|
||||
private String script;
|
||||
Set<JavaScriptLocation> locations;
|
||||
|
||||
public CallSiteSuccessorFinder(DebugInformation debugInfo, String script, Set<JavaScriptLocation> locations) {
|
||||
CallSiteSuccessorFinder(DebugInformation debugInfo, String script, Set<JavaScriptLocation> locations) {
|
||||
this.debugInfo = debugInfo;
|
||||
this.script = script;
|
||||
this.locations = locations;
|
||||
|
@ -185,7 +185,7 @@ public class Debugger {
|
|||
|
||||
private List<DebugInformation> debugInformationBySource(String sourceFile) {
|
||||
Map<DebugInformation, Object> list = debugInformationFileMap.get(sourceFile);
|
||||
return list != null ? new ArrayList<>(list.keySet()) : Collections.<DebugInformation>emptyList();
|
||||
return list != null ? new ArrayList<>(list.keySet()) : Collections.emptyList();
|
||||
}
|
||||
|
||||
public void continueToLocation(SourceLocation location) {
|
||||
|
@ -218,6 +218,10 @@ public class Debugger {
|
|||
return createBreakpoint(new SourceLocation(file, line));
|
||||
}
|
||||
|
||||
public Collection<? extends String> getSourceFiles() {
|
||||
return debugInformationFileMap.keySet();
|
||||
}
|
||||
|
||||
public Breakpoint createBreakpoint(SourceLocation location) {
|
||||
synchronized (breakpointMap) {
|
||||
Breakpoint breakpoint = new Breakpoint(this, location);
|
||||
|
@ -232,7 +236,7 @@ public class Debugger {
|
|||
return new HashSet<>(breakpoints.keySet());
|
||||
}
|
||||
|
||||
void updateInternalBreakpoints(Breakpoint breakpoint) {
|
||||
private void updateInternalBreakpoints(Breakpoint breakpoint) {
|
||||
if (breakpoint.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
@ -259,7 +263,7 @@ public class Debugger {
|
|||
return listeners.keySet().toArray(new DebuggerListener[0]);
|
||||
}
|
||||
|
||||
void updateBreakpointStatus(Breakpoint breakpoint, boolean fireEvent) {
|
||||
private void updateBreakpointStatus(Breakpoint breakpoint, boolean fireEvent) {
|
||||
boolean valid = false;
|
||||
for (JavaScriptBreakpoint jsBreakpoint : breakpoint.jsBreakpoints) {
|
||||
if (jsBreakpoint.isValid()) {
|
||||
|
|
|
@ -63,6 +63,9 @@ class VariableMap extends AbstractMap<String, Variable> {
|
|||
String[] names = debugger.mapVariable(entry.getKey(), location);
|
||||
Value value = new Value(debugger, jsVar.getValue());
|
||||
for (String name : names) {
|
||||
if (name == null) {
|
||||
name = "js:" + jsVar.getName();
|
||||
}
|
||||
vars.put(name, new Variable(name, value));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -216,7 +216,7 @@ public class DebugInformation {
|
|||
int[] valueIndexes = mapping.get(keyIndex).getArray(0);
|
||||
String[] result = new String[valueIndexes.length];
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
result[i] = variableNames[valueIndexes[i]];
|
||||
result[i] = valueIndexes[i] >= 0 ? variableNames[valueIndexes[i]] : null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
private RecordArrayBuilder.Record add(RecordArrayBuilder builder) {
|
||||
private RecordArrayBuilder.Record add(RecordArrayBuilder builder) {
|
||||
if (builder.size() > 1) {
|
||||
RecordArrayBuilder.Record lastRecord = builder.get(builder.size() - 1);
|
||||
if (lastRecord.get(0) == locationProvider.getLine() && lastRecord.get(1) == locationProvider.getColumn()) {
|
||||
|
@ -132,11 +132,8 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
}
|
||||
Arrays.sort(sourceIndexes);
|
||||
int generatedIndex = variableNames.index(generatedName);
|
||||
RecordArrayBuilder mapping = variableMappings.get(generatedIndex);
|
||||
if (mapping == null) {
|
||||
mapping = new RecordArrayBuilder(2, 1);
|
||||
variableMappings.put(generatedIndex, mapping);
|
||||
}
|
||||
RecordArrayBuilder mapping = variableMappings.computeIfAbsent(generatedIndex,
|
||||
k -> new RecordArrayBuilder(2, 1));
|
||||
|
||||
RecordArrayBuilder.Record record = add(mapping);
|
||||
RecordArrayBuilder.SubArray array = record.getArray(0);
|
||||
|
|
|
@ -661,5 +661,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
public Map<String, String> getExportedClasses() {
|
||||
return readonlyExportedClasses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFriendlyToDebugger() {
|
||||
return optimizationLevel == TeaVMOptimizationLevel.SIMPLE;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ public interface TeaVMTargetController {
|
|||
|
||||
boolean isIncremental();
|
||||
|
||||
boolean isFriendlyToDebugger();
|
||||
|
||||
Map<String, TeaVMEntryPoint> getEntryPoints();
|
||||
|
||||
Map<String, String> getExportedClasses();
|
||||
|
|
|
@ -24,6 +24,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
@ -119,7 +121,12 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
|||
}
|
||||
}
|
||||
CompletableFuture<Object> future = futures.remove(response.getId());
|
||||
responseHandlers.remove(response.getId()).received(response.getResult(), future);
|
||||
try {
|
||||
responseHandlers.remove(response.getId()).received(response.getResult(), future);
|
||||
} catch (RuntimeException e) {
|
||||
logger.warn("Error processing message ${}", response.getId(), e);
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
} else {
|
||||
Message message = mapper.reader(Message.class).readValue(messageText);
|
||||
if (message.getMethod() == null) {
|
||||
|
@ -386,7 +393,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
|||
|
||||
try {
|
||||
return read(sync);
|
||||
} catch (InterruptedException e) {
|
||||
} catch (InterruptedException | TimeoutException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
@ -421,6 +428,8 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
|||
return result.isEmpty() ? null : result;
|
||||
} catch (InterruptedException e) {
|
||||
return null;
|
||||
} catch (TimeoutException e) {
|
||||
return "<timed out>";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -454,6 +463,8 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
|||
return result.repr;
|
||||
} catch (InterruptedException e) {
|
||||
return null;
|
||||
} catch (TimeoutException e) {
|
||||
return "<timed out>";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -559,9 +570,9 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
|||
void received(JsonNode node, CompletableFuture<T> out) throws IOException;
|
||||
}
|
||||
|
||||
private static <T> T read(Future<T> future) throws InterruptedException {
|
||||
private static <T> T read(Future<T> future) throws InterruptedException, TimeoutException {
|
||||
try {
|
||||
return future.get();
|
||||
return future.get(1500, TimeUnit.MILLISECONDS);
|
||||
} catch (ExecutionException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof RuntimeException) {
|
||||
|
|
|
@ -51,6 +51,11 @@ public final class ChromeRDPRunner {
|
|||
new Thread(server::start).start();
|
||||
debugger = new Debugger(jsDebugger, new URLDebugInformationProvider(""));
|
||||
debugger.addListener(listener);
|
||||
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
|
||||
System.err.println("Uncaught exception in thread " + t);
|
||||
e.printStackTrace();
|
||||
});
|
||||
}
|
||||
|
||||
private DebuggerListener listener = new DebuggerListener() {
|
||||
|
@ -221,12 +226,55 @@ public final class ChromeRDPRunner {
|
|||
System.out.println("Expected 2 arguments");
|
||||
return;
|
||||
}
|
||||
Breakpoint bp = debugger.createBreakpoint(args[1], Integer.parseInt(args[2]));
|
||||
|
||||
String[] fileNames = resolveFileName(args[1]);
|
||||
if (fileNames.length == 0) {
|
||||
System.out.println("Unknown file: " + args[1]);
|
||||
return;
|
||||
} else if (fileNames.length > 1) {
|
||||
System.out.println("Ambiguous file name: " + args[1] + ". Possible names are: "
|
||||
+ Arrays.toString(fileNames));
|
||||
return;
|
||||
}
|
||||
|
||||
Breakpoint bp = debugger.createBreakpoint(fileNames[0], Integer.parseInt(args[2]));
|
||||
int id = breakpointIdGen++;
|
||||
breakpointIds.put(bp, id);
|
||||
System.out.println("Breakpoint #" + id + " was set at " + bp.getLocation());
|
||||
};
|
||||
|
||||
private String[] resolveFileName(String fileName) {
|
||||
if (debugger.getSourceFiles().contains(fileName)) {
|
||||
return new String[] { fileName };
|
||||
}
|
||||
|
||||
String[] result = debugger.getSourceFiles().stream()
|
||||
.filter(f -> f.endsWith(fileName) && isPrecededByPathSeparator(f, fileName))
|
||||
.toArray(String[]::new);
|
||||
if (result.length == 1) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return debugger.getSourceFiles().stream()
|
||||
.filter(f -> {
|
||||
int index = f.lastIndexOf('.');
|
||||
if (index <= 0) {
|
||||
return false;
|
||||
}
|
||||
String nameWithoutExt = f.substring(0, index);
|
||||
return nameWithoutExt.endsWith(fileName) && isPrecededByPathSeparator(nameWithoutExt, fileName);
|
||||
})
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
private static boolean isPrecededByPathSeparator(String actualName, String specifiedName) {
|
||||
if (actualName.length() < specifiedName.length() + 1) {
|
||||
return false;
|
||||
}
|
||||
char c = actualName.charAt(actualName.length() - specifiedName.length() - 1);
|
||||
return c == '/' || c == '\\';
|
||||
}
|
||||
|
||||
private Command backtraceCommand = args -> {
|
||||
CallFrame[] callStack = debugger.getCallStack();
|
||||
for (int i = 0; i < callStack.length; ++i) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user