Adds debugging information file generation

This commit is contained in:
Alexey Andreev 2014-07-27 19:13:39 +04:00
parent 3b939d0853
commit 94b9b001cd
25 changed files with 686 additions and 166 deletions

View File

@ -57,6 +57,10 @@ public class TeaVMRunner {
options.addOption(OptionBuilder
.withDescription("causes TeaVM to log bytecode")
.create("logbytecode"));
options.addOption(OptionBuilder
.withDescription("Generate debug information")
.withLongOpt("debug")
.create('D'));
options.addOption(OptionBuilder
.withArgName("number")
.hasArg()
@ -119,6 +123,9 @@ public class TeaVMRunner {
return;
}
}
if (commandLine.hasOption('D')) {
tool.setDebugInformation(new File(tool.getTargetDirectory(), tool.getTargetFileName() + ".teavmdbg"));
}
args = commandLine.getArgs();
if (args.length > 1) {
System.err.println("Unexpected arguments");

View File

@ -15,7 +15,13 @@
*/
package org.teavm.debugging;
import java.util.*;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.teavm.model.MethodReference;
/**
@ -92,6 +98,11 @@ public class DebugInformation {
return index >= 0 ? index : -index - 2;
}
public void write(OutputStream output) throws IOException {
DebugInformationWriter writer = new DebugInformationWriter(new DataOutputStream(output));
writer.write(this);
}
static class FileDescription {
GeneratedLocation[][] generatedLocations;
MethodReference[] methodMap;

View File

@ -49,6 +49,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
@Override
public void emitLocation(String fileName, int line) {
debugInformation = null;
Integer fileIndex;
if (fileName != null) {
fileIndex = fileNameMap.get(fileName);
@ -76,6 +77,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
@Override
public void emitMethod(MethodReference method) {
debugInformation = null;
currentMethod = method;
}

View File

@ -0,0 +1,136 @@
/*
* Copyright 2014 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.debugging;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.teavm.common.IntegerArray;
import org.teavm.debugging.DebugInformation.FileDescription;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
class DebugInformationWriter {
private DataOutput output;
private int lastNumber;
public DebugInformationWriter(DataOutput output) {
this.output = output;
}
public void write(DebugInformation debugInfo) throws IOException {
writeNumber(debugInfo.fileNames.length);
for (int i = 0; i < debugInfo.fileNames.length; ++i) {
String fileName = debugInfo.fileNames[i];
writeString(fileName);
writeMethods(debugInfo.fileDescriptions[i]);
}
writeNumber(debugInfo.fileNameKeys.length);
resetRelativeNumber();
for (int i = 0; i < debugInfo.fileNameKeys.length; ++i) {
writeRelativeNumber(debugInfo.fileNameKeys[i].getLine());
}
resetRelativeNumber();
for (int i = 0; i < debugInfo.fileNameKeys.length; ++i) {
writeRelativeNumber(debugInfo.fileNameKeys[i].getColumn());
}
resetRelativeNumber();
for (int i = 0; i < debugInfo.fileNameValues.length; ++i) {
writeRelativeNumber(debugInfo.fileNameValues[i]);
}
writeNumber(debugInfo.lineNumberKeys.length);
resetRelativeNumber();
resetRelativeNumber();
for (int i = 0; i < debugInfo.lineNumberKeys.length; ++i) {
writeRelativeNumber(debugInfo.lineNumberKeys[i].getLine());
}
resetRelativeNumber();
for (int i = 0; i < debugInfo.lineNumberKeys.length; ++i) {
writeRelativeNumber(debugInfo.lineNumberKeys[i].getColumn());
}
resetRelativeNumber();
for (int i = 0; i < debugInfo.fileNameValues.length; ++i) {
writeRelativeNumber(debugInfo.lineNumberValues[i]);
}
}
private void writeMethods(FileDescription fileDesc) throws IOException {
Map<MethodReference, IntegerArray> methodLineMap = new HashMap<>();
for (int i = 0; i < fileDesc.methodMap.length; ++i) {
MethodReference method = fileDesc.methodMap[i];
if (method == null) {
continue;
}
IntegerArray lines = methodLineMap.get(method);
if (lines == null) {
lines = new IntegerArray(1);
methodLineMap.put(method, lines);
}
lines.add(i);
}
writeNumber(methodLineMap.size());
for (MethodReference method : methodLineMap.keySet()) {
writeString(method.toString());
int[] lines = methodLineMap.get(method).getAll();
Arrays.sort(lines);
for (int i = 0; i < lines.length;) {
writeRelativeNumber(i);
int j = i;
int last = lines[i];
++i;
while (i < lines.length && lines[i] == last + 1) {
++i;
++last;
}
writeNumber(i - j);
}
writeRelativeNumber(-1);
}
}
private void writeNumber(int number) throws IOException {
do {
number = (number << 1) | (number >>> 31);
byte b = (byte)(number & 0x7F);
if ((number & 0xFFFFFF80) != 0) {
b |= 0x80;
}
number >>>= 7;
output.writeByte(b);
} while (number != 0);
}
private void writeRelativeNumber(int number) throws IOException {
writeNumber(number - lastNumber);
lastNumber = number;
}
private void resetRelativeNumber() {
lastNumber = 0;
}
private void writeString(String str) throws IOException {
writeNumber(str.length());
output.write(str.getBytes("UTF-8"));
}
}

View File

@ -212,6 +212,10 @@ class DependencyGraphBuilder {
}
private InstructionReader reader = new InstructionReader() {
@Override
public void location(InstructionLocation location) {
}
@Override
public void nop() {
}

View File

@ -88,6 +88,7 @@ class BreakToContinueReplacer implements StatementVisitor {
if (statement.getTarget() == replacedBreak) {
replaceBy = new ContinueStatement();
replaceBy.setTarget(replacement);
replaceBy.setLocation(statement.getLocation());
}
}

View File

@ -229,7 +229,16 @@ public class Decompiler {
int tmp = indexer.nodeAt(next);
generator.nextBlock = next < indexer.size() ? program.basicBlockAt(tmp) : null;
generator.statements.clear();
InstructionLocation lastLocation = null;
NodeLocation nodeLocation = null;
for (Instruction insn : generator.currentBlock.getInstructions()) {
if (lastLocation != insn.getLocation()) {
lastLocation = insn.getLocation();
nodeLocation = new NodeLocation(lastLocation.getFileName(), lastLocation.getLine());
}
if (insn.getLocation() != null) {
generator.setCurrentLocation(nodeLocation);
}
insn.acceptVisitor(generator);
}
for (TryCatchBlock tryCatch : generator.currentBlock.getTryCatchBlocks()) {

View File

@ -37,21 +37,21 @@ final class ExprOptimizer {
Expr b = binary.getSecondOperand();
switch (binary.getOperation()) {
case EQUALS:
return Expr.binary(BinaryOperation.NOT_EQUALS, a, b);
return Expr.binary(BinaryOperation.NOT_EQUALS, a, b, expr.getLocation());
case NOT_EQUALS:
return Expr.binary(BinaryOperation.EQUALS, a, b);
return Expr.binary(BinaryOperation.EQUALS, a, b, expr.getLocation());
case LESS:
return Expr.binary(BinaryOperation.GREATER_OR_EQUALS, a, b);
return Expr.binary(BinaryOperation.GREATER_OR_EQUALS, a, b, expr.getLocation());
case LESS_OR_EQUALS:
return Expr.binary(BinaryOperation.GREATER, a, b);
case GREATER:
return Expr.binary(BinaryOperation.LESS_OR_EQUALS, a, b);
return Expr.binary(BinaryOperation.LESS_OR_EQUALS, a, b, expr.getLocation());
case GREATER_OR_EQUALS:
return Expr.binary(BinaryOperation.LESS, a, b);
return Expr.binary(BinaryOperation.LESS, a, b, expr.getLocation());
case STRICT_EQUALS:
return Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, a, b);
return Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, a, b, expr.getLocation());
case STRICT_NOT_EQUALS:
return Expr.binary(BinaryOperation.STRICT_EQUALS, a, b);
return Expr.binary(BinaryOperation.STRICT_EQUALS, a, b, expr.getLocation());
default:
break;
}

View File

@ -33,13 +33,11 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
}
private static boolean isZero(Expr expr) {
return expr instanceof ConstantExpr &&
Integer.valueOf(0).equals(((ConstantExpr)expr).getValue());
return expr instanceof ConstantExpr && Integer.valueOf(0).equals(((ConstantExpr)expr).getValue());
}
private static boolean isComparison(Expr expr) {
return expr instanceof BinaryExpr &&
((BinaryExpr)expr).getOperation() == BinaryOperation.COMPARE;
return expr instanceof BinaryExpr && ((BinaryExpr)expr).getOperation() == BinaryOperation.COMPARE;
}
@Override
@ -76,6 +74,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
BinaryExpr comparison = (BinaryExpr)p;
Expr result = BinaryExpr.binary(expr.getOperation(),
comparison.getFirstOperand(), comparison.getSecondOperand());
result.setLocation(comparison.getLocation());
if (invert) {
result = ExprOptimizer.invert(result);
}
@ -139,6 +138,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
VariableExpr var = (VariableExpr)assignment.getLeftValue();
if (var.getIndex() == index) {
resultSequence.remove(resultSequence.size() - 1);
assignment.getRightValue().setLocation(assignment.getLocation());
assignment.getRightValue().acceptVisitor(this);
}
}
@ -207,7 +207,9 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
}
Expr[] args = expr.getArguments().toArray(new Expr[0]);
args = Arrays.copyOfRange(args, 1, args.length);
assignment.setRightValue(Expr.constructObject(expr.getMethod(), args));
Expr constructrExpr = Expr.constructObject(expr.getMethod(), args);
constructrExpr.setLocation(expr.getLocation());
assignment.setRightValue(constructrExpr);
stats.reads[var.getIndex()]--;
return true;
}
@ -259,8 +261,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
public void visit(AssignmentStatement statement) {
if (statement.getLeftValue() == null) {
statement.getRightValue().acceptVisitor(this);
if (resultExpr instanceof InvocationExpr &&
tryApplyConstructor((InvocationExpr)resultExpr)) {
if (resultExpr instanceof InvocationExpr && tryApplyConstructor((InvocationExpr)resultExpr)) {
resultStmt = new SequentialStatement();
} else {
statement.setRightValue(resultExpr);

View File

@ -23,6 +23,8 @@ import org.teavm.codegen.NamingException;
import org.teavm.codegen.NamingStrategy;
import org.teavm.codegen.SourceWriter;
import org.teavm.common.ServiceRepository;
import org.teavm.debugging.DebugInformationEmitter;
import org.teavm.debugging.DummyDebugInformationEmitter;
import org.teavm.javascript.ast.*;
import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.javascript.ni.InjectedBy;
@ -46,6 +48,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
private List<String> stringPool = new ArrayList<>();
private Properties properties = new Properties();
private ServiceRepository services;
private DebugInformationEmitter debugEmitter = new DummyDebugInformationEmitter();
private Deque<NodeLocation> locationStack = new ArrayDeque<>();
private static class InjectorHolder {
public final Injector injector;
@ -102,6 +106,14 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
return new Properties(properties);
}
public DebugInformationEmitter getDebugEmitter() {
return debugEmitter;
}
public void setDebugEmitter(DebugInformationEmitter debugEmitter) {
this.debugEmitter = debugEmitter;
}
public void setProperties(Properties properties) {
this.properties.clear();
this.properties.putAll(properties);
@ -462,6 +474,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
public void renderBody(MethodNode method, boolean inner) throws IOException {
MethodReference ref = method.getReference();
debugEmitter.emitMethod(ref);
if (inner) {
writer.appendMethodBody(ref).ws().append("=").ws().append("function(");
} else {
@ -480,6 +493,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append(")").ws().append("{").softNewLine().indent();
method.acceptVisitor(new MethodBodyRenderer());
writer.outdent().append("}").newLine();
debugEmitter.emitMethod(null);
}
private class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
@ -545,15 +559,40 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
}
}
private void pushLocation(NodeLocation location) {
locationStack.push(location);
if (location != null) {
debugEmitter.emitLocation(location.getFileName(), location.getLine());
} else {
debugEmitter.emitLocation(null, -1);
}
}
private void popLocation() {
locationStack.pop();
NodeLocation location = locationStack.peek();
if (location != null) {
debugEmitter.emitLocation(location.getFileName(), location.getLine());
} else {
debugEmitter.emitLocation(null, -1);
}
}
@Override
public void visit(AssignmentStatement statement) throws RenderingException {
try {
if (statement.getLocation() != null) {
pushLocation(statement.getLocation());
}
if (statement.getLeftValue() != null) {
statement.getLeftValue().acceptVisitor(this);
writer.ws().append("=").ws();
}
statement.getRightValue().acceptVisitor(this);
writer.append(";").softNewLine();
if (statement.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -672,11 +711,17 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(BreakStatement statement) {
try {
if (statement.getLocation() != null) {
pushLocation(statement.getLocation());
}
writer.append("break");
if (statement.getTarget() != null) {
writer.append(' ').append(statement.getTarget().getId());
}
writer.append(";").softNewLine();
if (statement.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -685,11 +730,17 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(ContinueStatement statement) {
try {
if (statement.getLocation() != null) {
pushLocation(statement.getLocation());
}
writer.append("continue");
if (statement.getTarget() != null) {
writer.append(' ').append(statement.getTarget().getId());
}
writer.append(";").softNewLine();
if (statement.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -698,12 +749,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(ReturnStatement statement) {
try {
if (statement.getLocation() != null) {
pushLocation(statement.getLocation());
}
writer.append("return");
if (statement.getResult() != null) {
writer.append(' ');
statement.getResult().acceptVisitor(this);
}
writer.append(";").softNewLine();
if (statement.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -712,9 +769,15 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(ThrowStatement statement) {
try {
if (statement.getLocation() != null) {
pushLocation(statement.getLocation());
}
writer.append("$rt_throw(");
statement.getException().acceptVisitor(this);
writer.append(");").softNewLine();
if (statement.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -746,7 +809,13 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(InitClassStatement statement) {
try {
if (statement.getLocation() != null) {
pushLocation(statement.getLocation());
}
writer.appendClass(statement.getClassName()).append("_$clinit();").softNewLine();
if (statement.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -767,11 +836,17 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
private void visitBinary(BinaryExpr expr, String op) {
try {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
writer.append('(');
expr.getFirstOperand().acceptVisitor(this);
writer.ws().append(op).ws();
expr.getSecondOperand().acceptVisitor(this);
writer.append(')');
if (expr.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -779,12 +854,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
private void visitBinaryFunction(BinaryExpr expr, String function) {
try {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
writer.append(function);
writer.append('(');
expr.getFirstOperand().acceptVisitor(this);
writer.append(",").ws();
expr.getSecondOperand().acceptVisitor(this);
writer.append(')');
if (expr.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -900,6 +981,9 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(UnaryExpr expr) {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
try {
switch (expr.getOperation()) {
case NOT:
@ -964,11 +1048,17 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
if (expr.getLocation() != null) {
popLocation();
}
}
@Override
public void visit(ConditionalExpr expr) {
try {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
writer.append('(');
expr.getCondition().acceptVisitor(this);
writer.ws().append("?").ws();
@ -976,6 +1066,9 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.ws().append(":").ws();
expr.getAlternative().acceptVisitor(this);
writer.append(')');
if (expr.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -984,7 +1077,13 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(ConstantExpr expr) {
try {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
writer.append(constantToString(expr.getValue()));
if (expr.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -1113,7 +1212,13 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(VariableExpr expr) {
try {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
writer.append(variableName(expr.getIndex()));
if (expr.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -1122,10 +1227,16 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(SubscriptExpr expr) {
try {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
expr.getArray().acceptVisitor(this);
writer.append('[');
expr.getIndex().acceptVisitor(this);
writer.append(']');
if (expr.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -1134,8 +1245,14 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(UnwrapArrayExpr expr) {
try {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
expr.getArray().acceptVisitor(this);
writer.append(".data");
if (expr.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -1144,55 +1261,61 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(InvocationExpr expr) {
try {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
Injector injector = getInjector(expr.getMethod());
if (injector != null) {
injector.generate(new InjectorContextImpl(expr.getArguments()), expr.getMethod());
return;
} else {
String className = naming.getNameFor(expr.getMethod().getClassName());
String name = naming.getNameFor(expr.getMethod());
String fullName = naming.getFullNameFor(expr.getMethod());
switch (expr.getType()) {
case STATIC:
writer.append(fullName).append("(");
for (int i = 0; i < expr.getArguments().size(); ++i) {
if (i > 0) {
writer.append(",").ws();
}
expr.getArguments().get(i).acceptVisitor(this);
}
writer.append(')');
break;
case SPECIAL:
writer.append(fullName).append("(");
expr.getArguments().get(0).acceptVisitor(this);
for (int i = 1; i < expr.getArguments().size(); ++i) {
writer.append(",").ws();
expr.getArguments().get(i).acceptVisitor(this);
}
writer.append(")");
break;
case DYNAMIC:
expr.getArguments().get(0).acceptVisitor(this);
writer.append(".").append(name).append("(");
for (int i = 1; i < expr.getArguments().size(); ++i) {
if (i > 1) {
writer.append(",").ws();
}
expr.getArguments().get(i).acceptVisitor(this);
}
writer.append(')');
break;
case CONSTRUCTOR:
writer.append(className).append(".").append(name).append("(");
for (int i = 0; i < expr.getArguments().size(); ++i) {
if (i > 0) {
writer.append(",").ws();
}
expr.getArguments().get(i).acceptVisitor(this);
}
writer.append(')');
break;
}
}
String className = naming.getNameFor(expr.getMethod().getClassName());
String name = naming.getNameFor(expr.getMethod());
String fullName = naming.getFullNameFor(expr.getMethod());
switch (expr.getType()) {
case STATIC:
writer.append(fullName).append("(");
for (int i = 0; i < expr.getArguments().size(); ++i) {
if (i > 0) {
writer.append(",").ws();
}
expr.getArguments().get(i).acceptVisitor(this);
}
writer.append(')');
break;
case SPECIAL:
writer.append(fullName).append("(");
expr.getArguments().get(0).acceptVisitor(this);
for (int i = 1; i < expr.getArguments().size(); ++i) {
writer.append(",").ws();
expr.getArguments().get(i).acceptVisitor(this);
}
writer.append(")");
break;
case DYNAMIC:
expr.getArguments().get(0).acceptVisitor(this);
writer.append(".").append(name).append("(");
for (int i = 1; i < expr.getArguments().size(); ++i) {
if (i > 1) {
writer.append(",").ws();
}
expr.getArguments().get(i).acceptVisitor(this);
}
writer.append(')');
break;
case CONSTRUCTOR:
writer.append(className).append(".").append(name).append("(");
for (int i = 0; i < expr.getArguments().size(); ++i) {
if (i > 0) {
writer.append(",").ws();
}
expr.getArguments().get(i).acceptVisitor(this);
}
writer.append(')');
break;
if (expr.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
@ -1202,8 +1325,14 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(QualificationExpr expr) {
try {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
expr.getQualified().acceptVisitor(this);
writer.append('.').appendField(expr.getField());
if (expr.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -1212,7 +1341,13 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(NewExpr expr) {
try {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
writer.append("new ").append(naming.getNameFor(expr.getConstructedClass())).append("()");
if (expr.getLocation() != null) {
popLocation();
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -1220,6 +1355,9 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(NewArrayExpr expr) {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
try {
ValueType type = expr.getType();
if (type instanceof ValueType.Primitive) {
@ -1273,10 +1411,16 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
if (expr.getLocation() != null) {
popLocation();
}
}
@Override
public void visit(NewMultiArrayExpr expr) {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
try {
ValueType type = expr.getType();
for (int i = 0; i < expr.getDimensions().size(); ++i) {
@ -1328,10 +1472,16 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
if (expr.getLocation() != null) {
popLocation();
}
}
@Override
public void visit(InstanceOfExpr expr) {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
try {
if (expr.getType() instanceof ValueType.Object) {
String clsName = ((ValueType.Object)expr.getType()).getClassName();
@ -1349,15 +1499,24 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
if (expr.getLocation() != null) {
popLocation();
}
}
@Override
public void visit(StaticClassExpr expr) {
if (expr.getLocation() != null) {
pushLocation(expr.getLocation());
}
try {
writer.append(typeToClsString(naming, expr.getType()));
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
if (expr.getLocation() != null) {
popLocation();
}
}
@Override

View File

@ -36,6 +36,11 @@ class StatementGenerator implements InstructionVisitor {
Decompiler.Block[] blockMap;
Program program;
ClassHolderSource classSource;
private NodeLocation currentLocation;
public void setCurrentLocation(NodeLocation currentLocation) {
this.currentLocation = currentLocation;
}
@Override
public void visit(EmptyInstruction insn) {
@ -237,8 +242,10 @@ class StatementGenerator implements InstructionVisitor {
@Override
public void visit(AssignInstruction insn) {
statements.add(Statement.assign(Expr.var(insn.getReceiver().getIndex()),
Expr.var(insn.getAssignee().getIndex())));
AssignmentStatement stmt = Statement.assign(Expr.var(insn.getReceiver().getIndex()),
Expr.var(insn.getAssignee().getIndex()));
stmt.setLocation(currentLocation);
statements.add(stmt);
}
@Override
@ -365,21 +372,29 @@ class StatementGenerator implements InstructionVisitor {
BasicBlock alternative = insn.getAlternative();
switch (insn.getCondition()) {
case EQUAL:
branch(Expr.binary(BinaryOperation.EQUALS, Expr.var(a), Expr.var(b)), consequent, alternative);
branch(withLocation(Expr.binary(BinaryOperation.EQUALS, Expr.var(a), Expr.var(b))),
consequent, alternative);
break;
case REFERENCE_EQUAL:
branch(Expr.binary(BinaryOperation.STRICT_EQUALS, Expr.var(a), Expr.var(b)), consequent, alternative);
branch(withLocation(Expr.binary(BinaryOperation.STRICT_EQUALS, Expr.var(a), Expr.var(b))),
consequent, alternative);
break;
case NOT_EQUAL:
branch(Expr.binary(BinaryOperation.NOT_EQUALS, Expr.var(a), Expr.var(b)), consequent, alternative);
branch(withLocation(Expr.binary(BinaryOperation.NOT_EQUALS, Expr.var(a), Expr.var(b))),
consequent, alternative);
break;
case REFERENCE_NOT_EQUAL:
branch(Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, Expr.var(a), Expr.var(b)),
branch(withLocation(Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, Expr.var(a), Expr.var(b))),
consequent, alternative);
break;
}
}
private Expr withLocation(Expr expr) {
expr.setLocation(currentLocation);
return expr;
}
@Override
public void visit(JumpInstruction insn) {
Statement stmt = generateJumpStatement(insn.getTarget());
@ -428,13 +443,16 @@ class StatementGenerator implements InstructionVisitor {
@Override
public void visit(ExitInstruction insn) {
statements.add(Statement.exitFunction(insn.getValueToReturn() != null ?
Expr.var(insn.getValueToReturn().getIndex()) : null));
ReturnStatement stmt = Statement.exitFunction(insn.getValueToReturn() != null ?
Expr.var(insn.getValueToReturn().getIndex()) : null);
stmt.setLocation(currentLocation);
statements.add(stmt);
}
@Override
public void visit(RaiseInstruction insn) {
ThrowStatement stmt = new ThrowStatement();
stmt.setLocation(currentLocation);
stmt.setException(Expr.var(insn.getException().getIndex()));
statements.add(stmt);
}
@ -462,24 +480,27 @@ class StatementGenerator implements InstructionVisitor {
@Override
public void visit(GetFieldInstruction insn) {
if (insn.getInstance() != null) {
statements.add(Statement.assign(Expr.var(insn.getReceiver().getIndex()),
Expr.qualify(Expr.var(insn.getInstance().getIndex()), insn.getField())));
AssignmentStatement stmt = Statement.assign(Expr.var(insn.getReceiver().getIndex()),
Expr.qualify(Expr.var(insn.getInstance().getIndex()), insn.getField()));
stmt.setLocation(currentLocation);
statements.add(stmt);
} else {
Expr fieldExpr = Expr.qualify(Expr.staticClass(ValueType.object(insn.getField().getClassName())),
insn.getField());
statements.add(Statement.assign(Expr.var(insn.getReceiver().getIndex()), fieldExpr));
AssignmentStatement stmt = Statement.assign(Expr.var(insn.getReceiver().getIndex()), fieldExpr);
stmt.setLocation(currentLocation);
statements.add(stmt);
}
}
@Override
public void visit(PutFieldInstruction insn) {
if (insn.getInstance() != null) {
statements.add(Statement.assign(Expr.qualify(Expr.var(insn.getInstance().getIndex()), insn.getField()),
Expr.var(insn.getValue().getIndex())));
assign(Expr.qualify(Expr.var(insn.getInstance().getIndex()), insn.getField()), insn.getValue().getIndex());
} else {
Expr fieldExpr = Expr.qualify(Expr.staticClass(ValueType.object(insn.getField().getClassName())),
insn.getField());
statements.add(Statement.assign(fieldExpr, Expr.var(insn.getValue().getIndex())));
assign(fieldExpr, insn.getValue().getIndex());
}
}
@ -511,8 +532,10 @@ class StatementGenerator implements InstructionVisitor {
@Override
public void visit(PutElementInstruction insn) {
statements.add(Statement.assign(Expr.subscript(Expr.var(insn.getArray().getIndex()),
Expr.var(insn.getIndex().getIndex())), Expr.var(insn.getValue().getIndex())));
AssignmentStatement stmt = Statement.assign(Expr.subscript(Expr.var(insn.getArray().getIndex()),
Expr.var(insn.getIndex().getIndex())), Expr.var(insn.getValue().getIndex()));
stmt.setLocation(currentLocation);
statements.add(stmt);
}
@Override
@ -535,7 +558,9 @@ class StatementGenerator implements InstructionVisitor {
if (insn.getReceiver() != null) {
assign(invocationExpr, insn.getReceiver().getIndex());
} else {
statements.add(Statement.assign(null, invocationExpr));
AssignmentStatement stmt = Statement.assign(null, invocationExpr);
stmt.setLocation(currentLocation);
statements.add(stmt);
}
}
@ -546,7 +571,9 @@ class StatementGenerator implements InstructionVisitor {
}
private void assign(Expr source, int target) {
statements.add(Statement.assign(Expr.var(target), source));
AssignmentStatement stmt = Statement.assign(Expr.var(target), source);
stmt.setLocation(currentLocation);
statements.add(stmt);
}
private Expr castToInteger(Expr value) {
@ -584,10 +611,12 @@ class StatementGenerator implements InstructionVisitor {
Decompiler.Block block = blockMap[target.getIndex()];
if (target.getIndex() == indexer.nodeAt(block.end)) {
BreakStatement breakStmt = new BreakStatement();
breakStmt.setLocation(currentLocation);
breakStmt.setTarget(block.statement);
return breakStmt;
} else {
ContinueStatement contStmt = new ContinueStatement();
contStmt.setLocation(currentLocation);
contStmt.setTarget(block.statement);
return contStmt;
}
@ -601,6 +630,7 @@ class StatementGenerator implements InstructionVisitor {
}
return body;
}
private void branch(Expr condition, BasicBlock consequentBlock, BasicBlock alternativeBlock) {
Statement consequent = generateJumpStatement(consequentBlock);
Statement alternative = generateJumpStatement(alternativeBlock);
@ -610,12 +640,16 @@ class StatementGenerator implements InstructionVisitor {
}
private Expr compare(BinaryOperation op, Variable value) {
return Expr.binary(op, Expr.var(value.getIndex()), Expr.constant(0));
Expr expr = Expr.binary(op, Expr.var(value.getIndex()), Expr.constant(0));
expr.setLocation(currentLocation);
return expr;
}
@Override
public void visit(InitClassInstruction insn) {
statements.add(Statement.initClass(insn.getClassName()));
InitClassStatement stmt = Statement.initClass(insn.getClassName());
stmt.setLocation(currentLocation);
statements.add(stmt);
}
@Override

View File

@ -56,6 +56,12 @@ public abstract class Expr implements Cloneable {
return expr;
}
public static Expr binary(BinaryOperation op, Expr first, Expr second, NodeLocation loc) {
Expr expr = binary(op, first, second);
expr.setLocation(loc);
return expr;
}
public static Expr unary(UnaryOperation op, Expr arg) {
UnaryExpr expr = new UnaryExpr();
expr.setOperand(arg);

View File

@ -20,6 +20,7 @@ package org.teavm.javascript.ast;
* @author Alexey Andreev
*/
public class InitClassStatement extends Statement {
private NodeLocation location;
private String className;
public String getClassName() {
@ -30,6 +31,14 @@ public class InitClassStatement extends Statement {
this.className = className;
}
public NodeLocation getLocation() {
return location;
}
public void setLocation(NodeLocation location) {
this.location = location;
}
@Override
public void acceptVisitor(StatementVisitor visitor) {
visitor.visit(this);

View File

@ -15,32 +15,24 @@
*/
package org.teavm.javascript.ast;
import org.teavm.model.MethodDescriptor;
/**
*
* @author Alexey Andreev
*/
public class NodeLocation {
private String className;
private MethodDescriptor method;
private int lineNumber;
private String fileName;
private int line;
public NodeLocation(String className, MethodDescriptor method, int lineNumber) {
this.className = className;
this.method = method;
this.lineNumber = lineNumber;
public NodeLocation(String fileName, int line) {
this.fileName = fileName;
this.line = line;
}
public String getClassName() {
return className;
public String getFileName() {
return fileName;
}
public MethodDescriptor getMethod() {
return method;
}
public int getLineNumber() {
return lineNumber;
public int getLine() {
return line;
}
}

View File

@ -29,26 +29,26 @@ public abstract class Statement {
return new SequentialStatement();
}
public static Statement assign(Expr left, Expr right) {
public static AssignmentStatement assign(Expr left, Expr right) {
AssignmentStatement stmt = new AssignmentStatement();
stmt.setLeftValue(left);
stmt.setRightValue(right);
return stmt;
}
public static Statement exitFunction(Expr result) {
public static ReturnStatement exitFunction(Expr result) {
ReturnStatement stmt = new ReturnStatement();
stmt.setResult(result);
return stmt;
}
public static Statement raiseException(Expr exception) {
public static ThrowStatement raiseException(Expr exception) {
ThrowStatement stmt = new ThrowStatement();
stmt.setException(exception);
return stmt;
}
public static Statement increment(int var, int amount) {
public static IncrementStatement increment(int var, int amount) {
IncrementStatement stmt = new IncrementStatement();
stmt.setVar(var);
stmt.setAmount(amount);
@ -67,7 +67,7 @@ public abstract class Statement {
return cond(predicate, consequent, Collections.<Statement>emptyList());
}
public static Statement initClass(String className) {
public static InitClassStatement initClass(String className) {
InitClassStatement stmt = new InitClassStatement();
stmt.setClassName(className);
return stmt;

View File

@ -20,19 +20,19 @@ package org.teavm.model;
* @author Alexey Andreev
*/
public class InstructionLocation {
private String className;
private MethodDescriptor method;
private int lineNumber = -1;
private String fileName;
private int line = -1;
public String getClassName() {
return className;
public InstructionLocation(String fileName, int line) {
this.fileName = fileName;
this.line = line;
}
public MethodDescriptor getMethod() {
return method;
public String getFileName() {
return fileName;
}
public int getLineNumber() {
return lineNumber;
public int getLine() {
return line;
}
}

View File

@ -23,6 +23,8 @@ import org.teavm.model.*;
* @author Alexey Andreev
*/
public interface InstructionReader {
void location(InstructionLocation location);
void nop();
void classConstant(VariableReader receiver, ValueType cst);

View File

@ -24,12 +24,26 @@ import org.teavm.model.instructions.*;
* @author Alexey Andreev
*/
public class InstructionStringifier implements InstructionReader {
private InstructionLocation location;
private StringBuilder sb;
public InstructionStringifier(StringBuilder sb) {
this.sb = sb;
}
@Override
public void location(InstructionLocation location) {
if (this.location != location) {
if (location != null) {
sb.append("at " + (location.getFileName() != null ? location.getFileName() : "<unknown>") + ":" +
(location.getLine() >= 0 ? String.valueOf(location.getLine()) : "<unknown>"));
} else {
sb.append("<unkwnown>");
}
this.location = location;
}
}
@Override
public void nop() {
sb.append("nop");

View File

@ -110,6 +110,12 @@ public final class ProgramUtils {
private static class InstructionCopyReader implements InstructionReader {
Instruction copy;
Program programCopy;
InstructionLocation location;
@Override
public void location(InstructionLocation location) {
this.location = location;
}
private Variable copyVar(VariableReader var) {
return programCopy.variableAt(var.getIndex());
@ -122,6 +128,7 @@ public final class ProgramUtils {
@Override
public void nop() {
copy = new EmptyInstruction();
copy.setLocation(location);
}
@Override
@ -130,6 +137,7 @@ public final class ProgramUtils {
insnCopy.setConstant(cst);
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -137,6 +145,7 @@ public final class ProgramUtils {
NullConstantInstruction insnCopy = new NullConstantInstruction();
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -145,6 +154,7 @@ public final class ProgramUtils {
insnCopy.setConstant(cst);
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -153,6 +163,7 @@ public final class ProgramUtils {
insnCopy.setConstant(cst);
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -161,6 +172,7 @@ public final class ProgramUtils {
insnCopy.setConstant(cst);
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -169,6 +181,7 @@ public final class ProgramUtils {
insnCopy.setConstant(cst);
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -177,6 +190,7 @@ public final class ProgramUtils {
insnCopy.setConstant(cst);
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -187,6 +201,7 @@ public final class ProgramUtils {
insnCopy.setSecondOperand(copyVar(second));
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -195,6 +210,7 @@ public final class ProgramUtils {
insnCopy.setOperand(copyVar(operand));
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -203,6 +219,7 @@ public final class ProgramUtils {
insnCopy.setAssignee(copyVar(assignee));
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -212,6 +229,7 @@ public final class ProgramUtils {
insnCopy.setReceiver(copyVar(receiver));
insnCopy.setTargetType(targetType);
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -221,6 +239,7 @@ public final class ProgramUtils {
insnCopy.setValue(copyVar(value));
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -230,6 +249,7 @@ public final class ProgramUtils {
insnCopy.setValue(copyVar(value));
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -240,6 +260,7 @@ public final class ProgramUtils {
insnCopy.setConsequent(copyBlock(consequent));
insnCopy.setAlternative(copyBlock(alternative));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -251,6 +272,7 @@ public final class ProgramUtils {
insnCopy.setConsequent(copyBlock(consequent));
insnCopy.setAlternative(copyBlock(alternative));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -258,6 +280,7 @@ public final class ProgramUtils {
JumpInstruction insnCopy = new JumpInstruction();
insnCopy.setTarget(copyBlock(target));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -273,6 +296,7 @@ public final class ProgramUtils {
insnCopy.getEntries().add(entryCopy);
}
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -280,6 +304,7 @@ public final class ProgramUtils {
ExitInstruction insnCopy = new ExitInstruction();
insnCopy.setValueToReturn(valueToReturn != null ? copyVar(valueToReturn) : null);
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -287,6 +312,7 @@ public final class ProgramUtils {
RaiseInstruction insnCopy = new RaiseInstruction();
insnCopy.setException(copyVar(exception));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -296,6 +322,7 @@ public final class ProgramUtils {
insnCopy.setSize(copyVar(size));
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -308,6 +335,7 @@ public final class ProgramUtils {
insnCopy.getDimensions().add(copyVar(dim));
}
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -316,6 +344,7 @@ public final class ProgramUtils {
insnCopy.setType(type);
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -327,6 +356,7 @@ public final class ProgramUtils {
insnCopy.setInstance(instance != null ? copyVar(instance) : null);
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -336,6 +366,7 @@ public final class ProgramUtils {
insnCopy.setInstance(instance != null ? copyVar(instance) : null);
insnCopy.setValue(copyVar(value));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -344,6 +375,7 @@ public final class ProgramUtils {
insnCopy.setArray(copyVar(array));
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -352,6 +384,7 @@ public final class ProgramUtils {
insnCopy.setArray(copyVar(array));
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -360,6 +393,7 @@ public final class ProgramUtils {
insnCopy.setArray(copyVar(array));
insnCopy.setReceiver(copyVar(receiver));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -369,6 +403,7 @@ public final class ProgramUtils {
insnCopy.setReceiver(copyVar(receiver));
insnCopy.setIndex(copyVar(index));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -378,6 +413,7 @@ public final class ProgramUtils {
insnCopy.setValue(copyVar(value));
insnCopy.setIndex(copyVar(index));
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -392,6 +428,7 @@ public final class ProgramUtils {
insnCopy.getArguments().add(copyVar(arg));
}
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -401,6 +438,7 @@ public final class ProgramUtils {
insnCopy.setReceiver(copyVar(receiver));
insnCopy.setType(type);
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -408,6 +446,7 @@ public final class ProgramUtils {
InitClassInstruction insnCopy = new InitClassInstruction();
insnCopy.setClassName(className);
copy = insnCopy;
copy.setLocation(location);
}
@Override
@ -416,6 +455,7 @@ public final class ProgramUtils {
insnCopy.setReceiver(copyVar(receiver));
insnCopy.setValue(copyVar(value));
copy = insnCopy;
copy.setLocation(location);
}
}
}

View File

@ -31,11 +31,12 @@ public final class Parser {
private Parser() {
}
public static MethodHolder parseMethod(MethodNode node, String className) {
public static MethodHolder parseMethod(MethodNode node, String className, String fileName) {
ValueType[] signature = MethodDescriptor.parseSignature(node.desc);
MethodHolder method = new MethodHolder(node.name, signature);
parseModifiers(node.access, method);
ProgramParser programParser = new ProgramParser();
programParser.setFileName(fileName);
Program program = programParser.parse(node, className);
new UnreachableBasicBlockEliminator().optimize(program);
SSATransformer ssaProducer = new SSATransformer();
@ -65,7 +66,7 @@ public final class Parser {
}
for (Object obj : node.methods) {
MethodNode methodNode = (MethodNode)obj;
cls.addMethod(parseMethod(methodNode, node.name));
cls.addMethod(parseMethod(methodNode, node.name, node.sourceFile));
}
if (node.outerClass != null) {
cls.setOwnerName(node.outerClass.replace('/', '.'));

View File

@ -31,18 +31,23 @@ public class ProgramParser {
static final byte SINGLE = 1;
static final byte DOUBLE_FIRST_HALF = 2;
static final byte DOUBLE_SECOND_HALF = 3;
private String fileName;
private StackFrame[] stackBefore;
private StackFrame[] stackAfter;
private StackFrame stack;
private int index;
private int[] nextIndexes;
private Map<Label, Integer> labelIndexes;
private Map<Label, Integer> lineNumbers;
private List<List<Instruction>> targetInstructions;
private List<Instruction> builder = new ArrayList<>();
private List<BasicBlock> basicBlocks = new ArrayList<>();
private int minLocal;
private Program program;
private String currentClassName;
private int currentLineNumber;
private boolean lineNumberChanged;
private InstructionLocation lastInsnLocation;
private static class Step {
public final int source;
@ -72,7 +77,17 @@ public class ProgramParser {
}
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public Program parse(MethodNode method, String className) {
currentLineNumber = -1;
lineNumberChanged = true;
program = new Program();
this.currentClassName = className;
InsnList instructions = method.instructions;
@ -151,11 +166,16 @@ public class ProgramParser {
minLocal = 1;
}
labelIndexes = new HashMap<>();
lineNumbers = new HashMap<>();
for (int i = 0; i < instructions.size(); ++i) {
AbstractInsnNode node = instructions.get(i);
if (node instanceof LabelNode) {
labelIndexes.put(((LabelNode)node).getLabel(), i);
}
if (node instanceof LineNumberNode) {
LineNumberNode lineNumberNode = (LineNumberNode)node;
lineNumbers.put(lineNumberNode.start.getLabel(), lineNumberNode.line);
}
}
targetInstructions = new ArrayList<>(instructions.size());
targetInstructions.addAll(Collections.<List<Instruction>>nCopies(instructions.size(), null));
@ -289,6 +309,15 @@ public class ProgramParser {
AssignInstruction insn = new AssignInstruction();
insn.setAssignee(getVariable(source));
insn.setReceiver(getVariable(target));
addInstruction(insn);
}
private void addInstruction(Instruction insn) {
if (lineNumberChanged) {
lastInsnLocation = new InstructionLocation(fileName, currentLineNumber);
lineNumberChanged = false;
}
insn.setLocation(lastInsnLocation);
builder.add(insn);
}
@ -338,7 +367,7 @@ public class ProgramParser {
ConstructInstruction insn = new ConstructInstruction();
insn.setReceiver(getVariable(pushSingle()));
insn.setType(cls);
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.ANEWARRAY: {
@ -347,7 +376,7 @@ public class ProgramParser {
insn.setSize(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
insn.setItemType(valueType);
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.INSTANCEOF: {
@ -355,7 +384,7 @@ public class ProgramParser {
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
insn.setType(parseType(type));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.CHECKCAST: {
@ -363,7 +392,7 @@ public class ProgramParser {
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
insn.setTargetType(parseType(type));
builder.add(insn);
addInstruction(insn);
break;
}
}
@ -389,15 +418,14 @@ public class ProgramParser {
SwitchInstruction insn = new SwitchInstruction();
insn.setCondition(getVariable(popSingle()));
insn.getEntries().addAll(Arrays.asList(table));
builder.add(insn);
addInstruction(insn);
int defaultIndex = labelIndexes.get(dflt);
insn.setDefaultTarget(getBasicBlock(defaultIndex));
nextIndexes[labels.length] = defaultIndex;
}
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
boolean visible) {
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
return null;
}
@ -412,7 +440,7 @@ public class ProgramParser {
insn.setItemType(arrayType);
insn.setReceiver(getVariable(pushSingle()));
insn.getDimensions().addAll(Arrays.asList(dimensions));
builder.add(insn);
addInstruction(insn);
}
@Override
@ -428,7 +456,7 @@ public class ProgramParser {
CloneArrayInstruction insn = new CloneArrayInstruction();
insn.setArray(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
break;
}
ownerCls = "java.lang.Object";
@ -458,7 +486,7 @@ public class ProgramParser {
insn.setReceiver(getVariable(result));
}
insn.getArguments().addAll(Arrays.asList(args));
builder.add(insn);
addInstruction(insn);
} else {
InvokeInstruction insn = new InvokeInstruction();
if (opcode == Opcodes.INVOKESPECIAL) {
@ -472,7 +500,7 @@ public class ProgramParser {
}
insn.setInstance(getVariable(instance));
insn.getArguments().addAll(Arrays.asList(args));
builder.add(insn);
addInstruction(insn);
}
break;
}
@ -499,15 +527,14 @@ public class ProgramParser {
SwitchInstruction insn = new SwitchInstruction();
insn.setCondition(getVariable(popSingle()));
insn.getEntries().addAll(Arrays.asList(table));
builder.add(insn);
addInstruction(insn);
int defaultTarget = labelIndexes.get(dflt);
insn.setDefaultTarget(getBasicBlock(defaultTarget));
nextIndexes[labels.length] = labelIndexes.get(dflt);
}
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start,
Label end, int index) {
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
}
@Override
@ -528,12 +555,12 @@ public class ProgramParser {
StringConstantInstruction insn = new StringConstantInstruction();
insn.setConstant((String)cst);
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
} else if (cst instanceof Type) {
ClassConstantInstruction insn = new ClassConstantInstruction();
insn.setConstant(ValueType.parse(((Type)cst).getDescriptor()));
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
} else {
throw new IllegalArgumentException();
}
@ -541,6 +568,11 @@ public class ProgramParser {
@Override
public void visitLabel(Label label) {
Integer lineNumber = lineNumbers.get(label);
if (lineNumber != null) {
currentLineNumber = lineNumber;
lineNumberChanged = true;
}
}
private void emitBranching(BranchingCondition condition, int value, int target) {
@ -548,7 +580,7 @@ public class ProgramParser {
insn.setOperand(getVariable(value));
insn.setConsequent(getBasicBlock(target));
insn.setAlternative(getBasicBlock(index + 1));
builder.add(insn);
addInstruction(insn);
}
private void emitBranching(BinaryBranchingCondition condition, int first, int second,
@ -558,7 +590,7 @@ public class ProgramParser {
insn.setSecondOperand(getVariable(second));
insn.setConsequent(getBasicBlock(target));
insn.setAlternative(getBasicBlock(index + 1));
builder.add(insn);
addInstruction(insn);
}
private void emitBinary(BinaryOperation operation, NumericOperandType operandType,
@ -567,21 +599,21 @@ public class ProgramParser {
insn.setFirstOperand(getVariable(first));
insn.setSecondOperand(getVariable(second));
insn.setReceiver(getVariable(receiver));
builder.add(insn);
addInstruction(insn);
}
private void emitNeg(NumericOperandType operandType, int operand, int receiver) {
NegateInstruction insn = new NegateInstruction(operandType);
insn.setOperand(getVariable(operand));
insn.setReceiver(getVariable(receiver));
builder.add(insn);
addInstruction(insn);
}
private void emitNumberCast(NumericOperandType source, NumericOperandType target, int value, int result) {
CastNumberInstruction insn = new CastNumberInstruction(source, target);
insn.setReceiver(getVariable(result));
insn.setValue(getVariable(value));
builder.add(insn);
addInstruction(insn);
}
@Override
@ -669,7 +701,7 @@ public class ProgramParser {
case Opcodes.GOTO: {
JumpInstruction insn = new JumpInstruction();
insn.setTarget(getBasicBlock(target));
builder.add(insn);
addInstruction(insn);
nextIndexes = new int[] { labelIndexes.get(label) };
return;
}
@ -686,14 +718,14 @@ public class ProgramParser {
IntegerConstantInstruction insn = new IntegerConstantInstruction();
insn.setConstant(operand);
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.SIPUSH: {
IntegerConstantInstruction insn = new IntegerConstantInstruction();
insn.setConstant(operand);
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.NEWARRAY: {
@ -730,7 +762,7 @@ public class ProgramParser {
insn.setSize(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
insn.setItemType(itemType);
builder.add(insn);
addInstruction(insn);
break;
}
}
@ -740,28 +772,28 @@ public class ProgramParser {
IntegerConstantInstruction insn = new IntegerConstantInstruction();
insn.setConstant(value);
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
}
private void pushConstant(long value) {
LongConstantInstruction insn = new LongConstantInstruction();
insn.setConstant(value);
insn.setReceiver(getVariable(pushDouble()));
builder.add(insn);
addInstruction(insn);
}
private void pushConstant(double value) {
DoubleConstantInstruction insn = new DoubleConstantInstruction();
insn.setConstant(value);
insn.setReceiver(getVariable(pushDouble()));
builder.add(insn);
addInstruction(insn);
}
private void pushConstant(float value) {
FloatConstantInstruction insn = new FloatConstantInstruction();
insn.setConstant(value);
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
}
private void loadArrayElement(int sz, ArrayElementType type) {
@ -771,12 +803,12 @@ public class ProgramParser {
UnwrapArrayInstruction unwrapInsn = new UnwrapArrayInstruction(type);
unwrapInsn.setArray(getVariable(array));
unwrapInsn.setReceiver(unwrapInsn.getArray());
builder.add(unwrapInsn);
addInstruction(unwrapInsn);
GetElementInstruction insn = new GetElementInstruction();
insn.setArray(getVariable(array));
insn.setIndex(getVariable(arrIndex));
insn.setReceiver(getVariable(var));
builder.add(insn);
addInstruction(insn);
}
private void storeArrayElement(int sz, ArrayElementType type) {
@ -786,12 +818,12 @@ public class ProgramParser {
UnwrapArrayInstruction unwrapInsn = new UnwrapArrayInstruction(type);
unwrapInsn.setArray(getVariable(array));
unwrapInsn.setReceiver(unwrapInsn.getArray());
builder.add(unwrapInsn);
addInstruction(unwrapInsn);
PutElementInstruction insn = new PutElementInstruction();
insn.setArray(getVariable(array));
insn.setIndex(getVariable(arrIndex));
insn.setValue(getVariable(value));
builder.add(insn);
addInstruction(insn);
}
@Override
@ -800,7 +832,7 @@ public class ProgramParser {
case Opcodes.ACONST_NULL: {
NullConstantInstruction insn = new NullConstantInstruction();
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.ICONST_M1:
@ -851,7 +883,7 @@ public class ProgramParser {
CastIntegerDirection.TO_INTEGER);
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.IALOAD:
@ -866,7 +898,7 @@ public class ProgramParser {
CastIntegerDirection.TO_INTEGER);
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.CALOAD: {
@ -875,7 +907,7 @@ public class ProgramParser {
CastIntegerDirection.TO_INTEGER);
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.AALOAD:
@ -1353,7 +1385,7 @@ public class ProgramParser {
CastIntegerDirection.FROM_INTEGER);
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.I2C: {
@ -1361,7 +1393,7 @@ public class ProgramParser {
CastIntegerDirection.FROM_INTEGER);
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.I2S: {
@ -1369,7 +1401,7 @@ public class ProgramParser {
CastIntegerDirection.FROM_INTEGER);
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.I2F: {
@ -1449,7 +1481,7 @@ public class ProgramParser {
case Opcodes.ARETURN: {
ExitInstruction insn = new ExitInstruction();
insn.setValueToReturn(getVariable(popSingle()));
builder.add(insn);
addInstruction(insn);
nextIndexes = new int[0];
return;
}
@ -1457,13 +1489,13 @@ public class ProgramParser {
case Opcodes.DRETURN: {
ExitInstruction insn = new ExitInstruction();
insn.setValueToReturn(getVariable(popDouble()));
builder.add(insn);
addInstruction(insn);
nextIndexes = new int[0];
return;
}
case Opcodes.RETURN: {
ExitInstruction insn = new ExitInstruction();
builder.add(insn);
addInstruction(insn);
nextIndexes = new int[0];
return;
}
@ -1473,17 +1505,17 @@ public class ProgramParser {
UnwrapArrayInstruction unwrapInsn = new UnwrapArrayInstruction(ArrayElementType.OBJECT);
unwrapInsn.setArray(getVariable(a));
unwrapInsn.setReceiver(getVariable(r));
builder.add(unwrapInsn);
addInstruction(unwrapInsn);
ArrayLengthInstruction insn = new ArrayLengthInstruction();
insn.setArray(getVariable(a));
insn.setReceiver(getVariable(r));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.ATHROW: {
RaiseInstruction insn = new RaiseInstruction();
insn.setException(getVariable(popSingle()));
builder.add(insn);
addInstruction(insn);
nextIndexes = new int[0];
return;
}
@ -1502,7 +1534,7 @@ public class ProgramParser {
IntegerConstantInstruction intInsn = new IntegerConstantInstruction();
intInsn.setConstant(increment);
intInsn.setReceiver(getVariable(tmp));
builder.add(intInsn);
addInstruction(intInsn);
emitBinary(BinaryOperation.ADD, NumericOperandType.INT, var, tmp, var);
}
@ -1523,7 +1555,7 @@ public class ProgramParser {
insn.setField(new FieldReference(ownerCls, name));
insn.setFieldType(type);
insn.setReceiver(getVariable(value));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.PUTFIELD: {
@ -1533,7 +1565,7 @@ public class ProgramParser {
insn.setInstance(getVariable(instance));
insn.setField(new FieldReference(ownerCls, name));
insn.setValue(getVariable(value));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.GETSTATIC: {
@ -1542,26 +1574,26 @@ public class ProgramParser {
if (!owner.equals(currentClassName)) {
InitClassInstruction initInsn = new InitClassInstruction();
initInsn.setClassName(ownerCls);
builder.add(initInsn);
addInstruction(initInsn);
}
GetFieldInstruction insn = new GetFieldInstruction();
insn.setField(new FieldReference(ownerCls, name));
insn.setFieldType(type);
insn.setReceiver(getVariable(value));
builder.add(insn);
addInstruction(insn);
break;
}
case Opcodes.PUTSTATIC: {
if (!owner.equals(currentClassName)) {
InitClassInstruction initInsn = new InitClassInstruction();
initInsn.setClassName(ownerCls);
builder.add(initInsn);
addInstruction(initInsn);
}
int value = desc.equals("D") || desc.equals("J") ? popDouble() : popSingle();
PutFieldInstruction insn = new PutFieldInstruction();
insn.setField(new FieldReference(ownerCls, name));
insn.setValue(getVariable(value));
builder.add(insn);
addInstruction(insn);
break;
}
}

View File

@ -21,6 +21,7 @@ import java.util.List;
import java.util.Properties;
import org.apache.commons.io.IOUtils;
import org.teavm.common.ThreadPoolFiniteExecutor;
import org.teavm.debugging.DebugInformationBuilder;
import org.teavm.javascript.RenderingContext;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.MethodDescriptor;
@ -43,6 +44,7 @@ public class TeaVMTool {
private Properties properties = new Properties();
private boolean mainPageIncluded;
private boolean bytecodeLogging;
private File debugInformation;
private int numThreads = 1;
private List<ClassHolderTransformer> transformers = new ArrayList<>();
private List<ClassAlias> classAliases = new ArrayList<>();
@ -106,6 +108,14 @@ public class TeaVMTool {
this.bytecodeLogging = bytecodeLogging;
}
public File getDebugInformation() {
return debugInformation;
}
public void setDebugInformation(File debugInformation) {
this.debugInformation = debugInformation;
}
public int getNumThreads() {
return numThreads;
}
@ -166,6 +176,8 @@ public class TeaVMTool {
vm.setMinifying(minifying);
vm.setBytecodeLogging(bytecodeLogging);
vm.setProperties(properties);
DebugInformationBuilder debugEmitter = debugInformation != null ? new DebugInformationBuilder() : null;
vm.setDebugEmitter(debugEmitter);
vm.installPlugins();
for (ClassHolderTransformer transformer : transformers) {
vm.add(transformer);
@ -205,6 +217,12 @@ public class TeaVMTool {
vm.build(writer, new DirectoryBuildTarget(targetDirectory));
vm.checkForMissingItems();
log.info("JavaScript file successfully built");
if (debugInformation != null) {
try (OutputStream debugInfoOut = new FileOutputStream(debugInformation)) {
debugEmitter.getDebugInformation().write(debugInfoOut);
}
log.info("Debug information successfully written");
}
}
if (runtime == RuntimeCopyOperation.SEPARATE) {
resourceToFile("org/teavm/javascript/runtime.js", "runtime.js");

View File

@ -20,6 +20,7 @@ import java.util.*;
import org.teavm.codegen.*;
import org.teavm.common.FiniteExecutor;
import org.teavm.common.ServiceRepository;
import org.teavm.debugging.DebugInformationEmitter;
import org.teavm.dependency.*;
import org.teavm.javascript.Decompiler;
import org.teavm.javascript.Renderer;
@ -81,6 +82,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
private List<RendererListener> rendererListeners = new ArrayList<>();
private Map<Class<?>, Object> services = new HashMap<>();
private Properties properties = new Properties();
private DebugInformationEmitter debugEmitter;
TeaVM(ClassReaderSource classSource, ClassLoader classLoader, FiniteExecutor executor) {
this.classSource = classSource;
@ -267,6 +269,14 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
dependencyChecker.checkForMissingItems();
}
public DebugInformationEmitter getDebugEmitter() {
return debugEmitter;
}
public void setDebugEmitter(DebugInformationEmitter debugEmitter) {
this.debugEmitter = debugEmitter;
}
/**
* <p>Does actual build. Call this method after TeaVM is fully configured and all entry points
* are specified. This method may fail if there are items (classes, methods and fields)
@ -338,6 +348,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
builder.setMinified(minifying);
SourceWriter sourceWriter = builder.build(writer);
Renderer renderer = new Renderer(sourceWriter, classSet, classLoader, this);
if (debugEmitter != null) {
renderer.setDebugEmitter(debugEmitter);
}
for (Map.Entry<MethodReference, Injector> entry : methodInjectors.entrySet()) {
renderer.addInjector(entry.getKey(), entry.getValue());
}

View File

@ -74,6 +74,12 @@ public class BuildJavascriptMojo extends AbstractMojo {
@Parameter
private boolean bytecodeLogging;
@Parameter
private boolean debugInformationGenerated;
@Parameter
private File debugInformationFile;
@Parameter(required = false)
private int numThreads = 1;
@ -144,6 +150,22 @@ public class BuildJavascriptMojo extends AbstractMojo {
this.methodAliases = methodAliases;
}
public boolean isDebugInformationGenerated() {
return debugInformationGenerated;
}
public void setDebugInformationGenerated(boolean debugInformationGenerated) {
this.debugInformationGenerated = debugInformationGenerated;
}
public File getDebugInformationFile() {
return debugInformationFile;
}
public void setDebugInformationFile(File debugInformationFile) {
this.debugInformationFile = debugInformationFile;
}
@Override
public void execute() throws MojoExecutionException {
Log log = getLog();
@ -169,6 +191,10 @@ public class BuildJavascriptMojo extends AbstractMojo {
if (properties != null) {
tool.getProperties().putAll(properties);
}
if (isDebugInformationGenerated()) {
tool.setDebugInformation(debugInformationFile != null ? debugInformationFile :
new File(targetDirectory, targetFileName + ".teavmdbg"));
}
tool.generate();
} catch (RuntimeException e) {
throw new MojoExecutionException("Unexpected error occured", e);

View File

@ -60,6 +60,7 @@
<minifying>false</minifying>
<mainClass>org.teavm.samples.HelloWorld</mainClass>
<mainPageIncluded>true</mainPageIncluded>
<debugInformationGenerated>true</debugInformationGenerated>
<targetDirectory>${project.build.directory}/javascript/hello</targetDirectory>
</configuration>
</execution>
@ -73,6 +74,7 @@
<minifying>false</minifying>
<mainClass>org.teavm.samples.MatrixMultiplication</mainClass>
<mainPageIncluded>true</mainPageIncluded>
<debugInformationGenerated>true</debugInformationGenerated>
<targetDirectory>${project.build.directory}/javascript/matrix</targetDirectory>
</configuration>
</execution>
@ -86,6 +88,7 @@
<minifying>false</minifying>
<mainClass>org.teavm.samples.DateTime</mainClass>
<targetDirectory>${project.build.directory}/javascript/datetime</targetDirectory>
<debugInformationGenerated>true</debugInformationGenerated>
<properties>
<java.util.Locale.available>en_US,en_GB,ru_RU,ru_UA,nl_NL,nl_BE</java.util.Locale.available>
</properties>