diff --git a/teavm-cli/src/main/java/org/teavm/cli/TeaVMRunner.java b/teavm-cli/src/main/java/org/teavm/cli/TeaVMRunner.java index be4ddbf0a..acd1bb2da 100644 --- a/teavm-cli/src/main/java/org/teavm/cli/TeaVMRunner.java +++ b/teavm-cli/src/main/java/org/teavm/cli/TeaVMRunner.java @@ -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"); diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java index a1a4ba273..140405c7a 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java @@ -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; diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java index 9abfd8ccf..8c7d8df98 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java @@ -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; } diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java new file mode 100644 index 000000000..f6abcc6da --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java @@ -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 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")); + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index a4e40f209..dee19bd00 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -212,6 +212,10 @@ class DependencyGraphBuilder { } private InstructionReader reader = new InstructionReader() { + @Override + public void location(InstructionLocation location) { + } + @Override public void nop() { } diff --git a/teavm-core/src/main/java/org/teavm/javascript/BreakToContinueReplacer.java b/teavm-core/src/main/java/org/teavm/javascript/BreakToContinueReplacer.java index 048749de6..3bbefeec2 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/BreakToContinueReplacer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/BreakToContinueReplacer.java @@ -88,6 +88,7 @@ class BreakToContinueReplacer implements StatementVisitor { if (statement.getTarget() == replacedBreak) { replaceBy = new ContinueStatement(); replaceBy.setTarget(replacement); + replaceBy.setLocation(statement.getLocation()); } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index d67bb8ce2..065dd1a81 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -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()) { diff --git a/teavm-core/src/main/java/org/teavm/javascript/ExprOptimizer.java b/teavm-core/src/main/java/org/teavm/javascript/ExprOptimizer.java index e1c6300d5..01780ad23 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ExprOptimizer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ExprOptimizer.java @@ -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; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java index 1e4624c67..e5789f98d 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java @@ -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); diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index 64efd13dd..6f04762af 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -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 stringPool = new ArrayList<>(); private Properties properties = new Properties(); private ServiceRepository services; + private DebugInformationEmitter debugEmitter = new DummyDebugInformationEmitter(); + private Deque 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 diff --git a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java index 91ae06a61..fd1d0e5d3 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java @@ -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 diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/Expr.java b/teavm-core/src/main/java/org/teavm/javascript/ast/Expr.java index 4e846bd28..02f137cb4 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/Expr.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/Expr.java @@ -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); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/InitClassStatement.java b/teavm-core/src/main/java/org/teavm/javascript/ast/InitClassStatement.java index fd1e81151..07657952a 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/InitClassStatement.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/InitClassStatement.java @@ -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); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/NodeLocation.java b/teavm-core/src/main/java/org/teavm/javascript/ast/NodeLocation.java index cab9f859c..3a2356bc6 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/NodeLocation.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/NodeLocation.java @@ -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; } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/Statement.java b/teavm-core/src/main/java/org/teavm/javascript/ast/Statement.java index 0a2219f63..1f11e113d 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/Statement.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/Statement.java @@ -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.emptyList()); } - public static Statement initClass(String className) { + public static InitClassStatement initClass(String className) { InitClassStatement stmt = new InitClassStatement(); stmt.setClassName(className); return stmt; diff --git a/teavm-core/src/main/java/org/teavm/model/InstructionLocation.java b/teavm-core/src/main/java/org/teavm/model/InstructionLocation.java index 6c205fd09..531c0da23 100644 --- a/teavm-core/src/main/java/org/teavm/model/InstructionLocation.java +++ b/teavm-core/src/main/java/org/teavm/model/InstructionLocation.java @@ -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; } } diff --git a/teavm-core/src/main/java/org/teavm/model/instructions/InstructionReader.java b/teavm-core/src/main/java/org/teavm/model/instructions/InstructionReader.java index 05b67da70..c2c191b66 100644 --- a/teavm-core/src/main/java/org/teavm/model/instructions/InstructionReader.java +++ b/teavm-core/src/main/java/org/teavm/model/instructions/InstructionReader.java @@ -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); diff --git a/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java b/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java index d69189773..be6d17556 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java +++ b/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java @@ -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() : "") + ":" + + (location.getLine() >= 0 ? String.valueOf(location.getLine()) : "")); + } else { + sb.append(""); + } + this.location = location; + } + } + @Override public void nop() { sb.append("nop"); diff --git a/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java b/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java index 7ebe718d9..7ec26790e 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java +++ b/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java @@ -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); } } } diff --git a/teavm-core/src/main/java/org/teavm/parsing/Parser.java b/teavm-core/src/main/java/org/teavm/parsing/Parser.java index fb1ac25b7..9ff34de53 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/Parser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/Parser.java @@ -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('/', '.')); diff --git a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java index 84bcb90d1..43842868c 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java @@ -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 labelIndexes; + private Map lineNumbers; private List> targetInstructions; private List builder = new ArrayList<>(); private List 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.>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; } } diff --git a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java index 75a909243..5da670eaa 100644 --- a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java +++ b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java @@ -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 transformers = new ArrayList<>(); private List 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"); diff --git a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java index 5957dbd33..24e0570e7 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java @@ -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 rendererListeners = new ArrayList<>(); private Map, 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; + } + /** *

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 entry : methodInjectors.entrySet()) { renderer.addInjector(entry.getKey(), entry.getValue()); } diff --git a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java index a5f57c210..e709154a1 100644 --- a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java +++ b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java @@ -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); diff --git a/teavm-samples/pom.xml b/teavm-samples/pom.xml index 1707768f3..37fd07637 100644 --- a/teavm-samples/pom.xml +++ b/teavm-samples/pom.xml @@ -60,6 +60,7 @@ false org.teavm.samples.HelloWorld true + true ${project.build.directory}/javascript/hello @@ -73,6 +74,7 @@ false org.teavm.samples.MatrixMultiplication true + true ${project.build.directory}/javascript/matrix @@ -86,6 +88,7 @@ false org.teavm.samples.DateTime ${project.build.directory}/javascript/datetime + true en_US,en_GB,ru_RU,ru_UA,nl_NL,nl_BE