diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 657ec713a..c8789196e 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -144,6 +144,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private Characteristics characteristics; private Set asyncMethods; private boolean incremental; + private boolean lineNumbersGenerated; private StringPool stringPool; public CTarget() { @@ -162,6 +163,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { this.incremental = incremental; } + public void setLineNumbersGenerated(boolean lineNumbersGenerated) { + this.lineNumbersGenerated = lineNumbersGenerated; + } + @Override public List getTransformers() { List transformers = new ArrayList<>(); @@ -332,8 +337,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes, intrinsics, generators, asyncMethods::contains, buildTarget, incremental); - BufferedCodeWriter runtimeWriter = new BufferedCodeWriter(); - BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter(); + BufferedCodeWriter runtimeWriter = new BufferedCodeWriter(false); + BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter(false); emitResource(runtimeWriter, "runtime.c"); runtimeHeaderWriter.println("#pragma once"); @@ -393,8 +398,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { classGenerator.prepare(classes); for (String className : classNames) { - BufferedCodeWriter writer = new BufferedCodeWriter(); - BufferedCodeWriter headerWriter = new BufferedCodeWriter(); + BufferedCodeWriter writer = new BufferedCodeWriter(lineNumbersGenerated); + BufferedCodeWriter headerWriter = new BufferedCodeWriter(false); ClassHolder cls = classes.get(className); if (cls != null) { classGenerator.generateClass(writer, headerWriter, cls); @@ -408,8 +413,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { if (type instanceof ValueType.Object) { continue; } - BufferedCodeWriter writer = new BufferedCodeWriter(); - BufferedCodeWriter headerWriter = new BufferedCodeWriter(); + BufferedCodeWriter writer = new BufferedCodeWriter(false); + BufferedCodeWriter headerWriter = new BufferedCodeWriter(false); classGenerator.generateType(writer, headerWriter, type); String name = ClassGenerator.fileName(type); OutputFileUtil.write(writer, name + ".c", buildTarget); @@ -451,8 +456,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private void generateCallSites(BuildTarget buildTarget, GenerationContext context, Collection classNames) throws IOException { - BufferedCodeWriter writer = new BufferedCodeWriter(); - BufferedCodeWriter headerWriter = new BufferedCodeWriter(); + BufferedCodeWriter writer = new BufferedCodeWriter(false); + BufferedCodeWriter headerWriter = new BufferedCodeWriter(false); IncludeManager includes = new SimpleIncludeManager(writer); includes.init("callsites.c"); @@ -491,8 +496,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { } private void generateStrings(BuildTarget buildTarget, StringPool stringPool) throws IOException { - BufferedCodeWriter writer = new BufferedCodeWriter(); - BufferedCodeWriter headerWriter = new BufferedCodeWriter(); + BufferedCodeWriter writer = new BufferedCodeWriter(false); + BufferedCodeWriter headerWriter = new BufferedCodeWriter(false); headerWriter.println("#pragma once"); headerWriter.println("#include \"runtime.h\""); @@ -588,7 +593,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private void generateMainFile(GenerationContext context, ListableClassHolderSource classes, List types, BuildTarget buildTarget) throws IOException { - BufferedCodeWriter writer = new BufferedCodeWriter(); + BufferedCodeWriter writer = new BufferedCodeWriter(false); IncludeManager includes = new SimpleIncludeManager(writer); includes.init("main.c"); includes.includePath("runtime.h"); @@ -601,7 +606,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private void generateAllFile(ListableClassHolderSource classes, List types, BuildTarget buildTarget) throws IOException { - BufferedCodeWriter writer = new BufferedCodeWriter(); + BufferedCodeWriter writer = new BufferedCodeWriter(false); IncludeManager includes = new SimpleIncludeManager(writer); includes.init("all.c"); includes.includePath("runtime.c"); diff --git a/core/src/main/java/org/teavm/backend/c/generate/BufferedCodeWriter.java b/core/src/main/java/org/teavm/backend/c/generate/BufferedCodeWriter.java index c4841a54a..1caaecbc5 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/BufferedCodeWriter.java +++ b/core/src/main/java/org/teavm/backend/c/generate/BufferedCodeWriter.java @@ -18,18 +18,24 @@ package org.teavm.backend.c.generate; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class BufferedCodeWriter extends CodeWriter { private List fragments = new ArrayList<>(); private int currentIndent; private int lastIndent; private StringBuilder buffer = new StringBuilder(); + private boolean lineNumbersEmitted; + private String lastFileName; + private int lastLineNumber; + private boolean locationDirty; - public BufferedCodeWriter() { + public BufferedCodeWriter(boolean lineNumbersEmitted) { + this.lineNumbersEmitted = lineNumbersEmitted; } - public void writeTo(PrintWriter writer) { - WriterWithContext writerWithContext = new WriterWithContext(writer); + public void writeTo(PrintWriter writer, String fileName) { + WriterWithContext writerWithContext = new WriterWithContext(writer, fileName); for (Fragment fragment : fragments) { fragment.writeTo(writerWithContext); } @@ -38,8 +44,12 @@ public class BufferedCodeWriter extends CodeWriter { @Override public CodeWriter fragment() { flush(); - BufferedCodeWriter innerWriter = new BufferedCodeWriter(); + BufferedCodeWriter innerWriter = new BufferedCodeWriter(lineNumbersEmitted); + innerWriter.lastFileName = lastFileName; + innerWriter.lastLineNumber = lastLineNumber; + innerWriter.locationDirty = locationDirty; fragments.add(new InnerWriterFragment(innerWriter.fragments)); + locationDirty = true; return innerWriter; } @@ -49,6 +59,9 @@ public class BufferedCodeWriter extends CodeWriter { buffer.setLength(0); lastIndent = currentIndent; currentIndent = 0; + if (lineNumbersEmitted) { + lastLineNumber++; + } } @Override @@ -65,37 +78,123 @@ public class BufferedCodeWriter extends CodeWriter { } } + @Override + public void source(String fileName, int lineNumber) { + if (!lineNumbersEmitted) { + return; + } + + if (!Objects.equals(lastFileName, fileName) || lastLineNumber != lineNumber || locationDirty) { + flush(); + fragments.add(new SourceFragment(fileName, lineNumber)); + lastFileName = fileName; + lastLineNumber = lineNumber; + locationDirty = false; + } + } + + @Override + public void nosource() { + source(null, 0); + } + @Override public void flush() { - fragments.add(new SimpleFragment(false, lastIndent, buffer.toString())); - lastIndent = currentIndent; - currentIndent = 0; - buffer.setLength(0); + if (buffer.length() > 0 || lastIndent != 0 || currentIndent != 0) { + fragments.add(new SimpleFragment(false, lastIndent, buffer.toString())); + lastIndent = currentIndent; + currentIndent = 0; + buffer.setLength(0); + } } static class WriterWithContext { PrintWriter writer; boolean isNewLine = true; int indentLevel; + String initialFileName; + String fileName; + int lineNumber; + int absLineNumber = 1; + String pendingFileName; + int pendingLineNumber = -1; - WriterWithContext(PrintWriter writer) { + WriterWithContext(PrintWriter writer, String fileName) { this.writer = writer; + this.fileName = fileName; + initialFileName = fileName; + lineNumber = 1; } void append(String text) { + if (text.isEmpty()) { + return; + } if (isNewLine) { - for (int i = 0; i < indentLevel; ++i) { - writer.print(" "); + if (pendingFileName != null || pendingLineNumber >= 0) { + printLineDirective(pendingFileName, pendingLineNumber); + pendingLineNumber = -1; + pendingFileName = null; } + printIndent(); isNewLine = false; } writer.print(text); } + private void printLineDirective(String fileName, int lineNumber) { + if (Objects.equals(this.fileName, fileName) && lineNumber == this.lineNumber) { + return; + } + + printIndent(); + writer.print("#line "); + if (Objects.equals(fileName, initialFileName)) { + lineNumber = absLineNumber + 1; + } + writer.print(lineNumber); + if (!Objects.equals(fileName, this.fileName)) { + writer.print(" \""); + escape(writer, fileName); + writer.print("\""); + } + writer.println(); + absLineNumber++; + this.fileName = fileName; + this.lineNumber = lineNumber; + } + void newLine() { + lineNumber++; + absLineNumber++; writer.println(); isNewLine = true; } + + private void printIndent() { + for (int i = 0; i < indentLevel; ++i) { + writer.print(" "); + } + } + + void source(String fileName, int lineNumber) { + if (fileName == null) { + fileName = initialFileName; + lineNumber = absLineNumber; + } + if (!Objects.equals(this.fileName, fileName) || this.lineNumber != lineNumber) { + if (isNewLine) { + pendingFileName = fileName; + pendingLineNumber = lineNumber; + } else { + this.lineNumber++; + absLineNumber++; + writer.println(); + printLineDirective(fileName, lineNumber); + isNewLine = true; + } + } + } } static abstract class Fragment { @@ -123,6 +222,62 @@ public class BufferedCodeWriter extends CodeWriter { } } + static class SourceFragment extends Fragment { + private String fileName; + private int lineNumber; + + SourceFragment(String fileName, int lineNumber) { + this.fileName = fileName; + this.lineNumber = lineNumber; + } + + @Override + void writeTo(WriterWithContext writer) { + writer.source(fileName, lineNumber); + } + } + + private static void escape(PrintWriter writer, String string) { + int chunkSize = 256; + for (int i = 0; i < string.length(); i += chunkSize) { + int last = Math.min(i + chunkSize, string.length()); + + for (int j = i; j < last; ++j) { + char c = string.charAt(j); + switch (c) { + case '\\': + writer.print("\\\\"); + break; + case '"': + writer.print("\\\""); + break; + case '\r': + writer.print("\\r"); + break; + case '\n': + writer.print("\\n"); + break; + case '\t': + writer.print("\\t"); + break; + default: + if (c < 32) { + writer.print("\\0" + Character.forDigit(c >> 3, 8) + Character.forDigit(c & 0x7, 8)); + } else if (c > 127) { + writer.print("\\u" + + Character.forDigit(c >> 12, 16) + + Character.forDigit((c >> 8) & 15, 16) + + Character.forDigit((c >> 4) & 15, 16) + + Character.forDigit(c & 15, 16)); + } else { + writer.print(c); + } + break; + } + } + } + } + static class InnerWriterFragment extends Fragment { List fragments; diff --git a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java index e854d7967..1dd02b3c6 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java @@ -22,7 +22,9 @@ import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.ShortBuffer; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -78,6 +80,7 @@ import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; +import org.teavm.model.TextLocation; import org.teavm.model.ValueType; import org.teavm.model.classes.VirtualTable; import org.teavm.runtime.Allocator; @@ -116,6 +119,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { private IncludeManager includes; private boolean end; private boolean async; + private final Deque locationStack = new ArrayDeque<>(); static { BUFFER_TYPES.put(ByteBuffer.class.getName(), "int8_t"); @@ -148,137 +152,143 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { @Override public void visit(BinaryExpr expr) { - switch (expr.getOperation()) { - case COMPARE: - writer.print("teavm_compare_"); - switch (expr.getType()) { - case INT: - writer.print("i32"); - break; - case LONG: - writer.print("i64"); - break; - case FLOAT: - writer.print("float"); - break; - case DOUBLE: - writer.print("double"); - break; + pushLocation(expr.getLocation()); + try { + switch (expr.getOperation()) { + case COMPARE: + writer.print("teavm_compare_"); + switch (expr.getType()) { + case INT: + writer.print("i32"); + break; + case LONG: + writer.print("i64"); + break; + case FLOAT: + writer.print("float"); + break; + case DOUBLE: + writer.print("double"); + break; + } + writer.print("("); + expr.getFirstOperand().acceptVisitor(this); + writer.print(", "); + expr.getSecondOperand().acceptVisitor(this); + writer.print(")"); + return; + case UNSIGNED_RIGHT_SHIFT: { + String type = expr.getType() == OperationType.LONG ? "int64_t" : "int32_t"; + writer.print("((" + type + ") ((u" + type + ") "); + + expr.getFirstOperand().acceptVisitor(this); + writer.print(" >> "); + expr.getSecondOperand().acceptVisitor(this); + + writer.print("))"); + return; } - writer.print("("); - expr.getFirstOperand().acceptVisitor(this); - writer.print(", "); - expr.getSecondOperand().acceptVisitor(this); - writer.print(")"); - return; - case UNSIGNED_RIGHT_SHIFT: { - String type = expr.getType() == OperationType.LONG ? "int64_t" : "int32_t"; - writer.print("((" + type + ") ((u" + type + ") "); - expr.getFirstOperand().acceptVisitor(this); - writer.print(" >> "); - expr.getSecondOperand().acceptVisitor(this); + case MODULO: { + switch (expr.getType()) { + case FLOAT: + writer.print("fmodf("); + expr.getFirstOperand().acceptVisitor(this); + writer.print(", "); + expr.getSecondOperand().acceptVisitor(this); + writer.print(")"); + return; + case DOUBLE: + writer.print("fmod("); + expr.getFirstOperand().acceptVisitor(this); + writer.print(", "); + expr.getSecondOperand().acceptVisitor(this); + writer.print(")"); + return; + default: + break; + } + break; + } - writer.print("))"); - return; + default: + break; } - case MODULO: { - switch (expr.getType()) { - case FLOAT: - writer.print("fmodf("); - expr.getFirstOperand().acceptVisitor(this); - writer.print(", "); - expr.getSecondOperand().acceptVisitor(this); - writer.print(")"); - return; - case DOUBLE: - writer.print("fmod("); - expr.getFirstOperand().acceptVisitor(this); - writer.print(", "); - expr.getSecondOperand().acceptVisitor(this); - writer.print(")"); - return; - default: - break; - } - break; + writer.print("("); + expr.getFirstOperand().acceptVisitor(this); + + String op; + switch (expr.getOperation()) { + case ADD: + op = "+"; + break; + case SUBTRACT: + op = "-"; + break; + case MULTIPLY: + op = "*"; + break; + case DIVIDE: + op = "/"; + break; + case MODULO: + op = "%"; + break; + case BITWISE_AND: + op = "&"; + break; + case BITWISE_OR: + op = "|"; + break; + case BITWISE_XOR: + op = "^"; + break; + case LEFT_SHIFT: + op = "<<"; + break; + case RIGHT_SHIFT: + op = ">>"; + break; + case EQUALS: + op = "=="; + break; + case NOT_EQUALS: + op = "!="; + break; + case GREATER: + op = ">"; + break; + case GREATER_OR_EQUALS: + op = ">="; + break; + case LESS: + op = "<"; + break; + case LESS_OR_EQUALS: + op = "<="; + break; + case AND: + op = "&&"; + break; + case OR: + op = "||"; + break; + default: + throw new AssertionError(); } - default: - break; + writer.print(" ").print(op).print(" "); + expr.getSecondOperand().acceptVisitor(this); + writer.print(")"); + } finally { + popLocation(expr.getLocation()); } - - writer.print("("); - expr.getFirstOperand().acceptVisitor(this); - - String op; - switch (expr.getOperation()) { - case ADD: - op = "+"; - break; - case SUBTRACT: - op = "-"; - break; - case MULTIPLY: - op = "*"; - break; - case DIVIDE: - op = "/"; - break; - case MODULO: - op = "%"; - break; - case BITWISE_AND: - op = "&"; - break; - case BITWISE_OR: - op = "|"; - break; - case BITWISE_XOR: - op = "^"; - break; - case LEFT_SHIFT: - op = "<<"; - break; - case RIGHT_SHIFT: - op = ">>"; - break; - case EQUALS: - op = "=="; - break; - case NOT_EQUALS: - op = "!="; - break; - case GREATER: - op = ">"; - break; - case GREATER_OR_EQUALS: - op = ">="; - break; - case LESS: - op = "<"; - break; - case LESS_OR_EQUALS: - op = "<="; - break; - case AND: - op = "&&"; - break; - case OR: - op = "||"; - break; - default: - throw new AssertionError(); - } - - writer.print(" ").print(op).print(" "); - expr.getSecondOperand().acceptVisitor(this); - writer.print(")"); } @Override public void visit(UnaryExpr expr) { + pushLocation(expr.getLocation()); switch (expr.getOperation()) { case NOT: writer.print("("); @@ -316,10 +326,12 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { writer.print(")"); break; } + popLocation(expr.getLocation()); } @Override public void visit(ConditionalExpr expr) { + pushLocation(expr.getLocation()); writer.print("("); expr.getCondition().acceptVisitor(this); writer.print(" ? "); @@ -327,34 +339,43 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { writer.print(" : "); expr.getAlternative().acceptVisitor(this); writer.print(")"); + popLocation(expr.getLocation()); } @Override public void visit(ConstantExpr expr) { + pushLocation(expr.getLocation()); CodeGeneratorUtil.writeValue(writer, context, includes, expr.getValue()); + popLocation(expr.getLocation()); } @Override public void visit(VariableExpr expr) { + pushLocation(expr.getLocation()); if (expr.getIndex() == 0) { writer.print("teavm_this_"); } else { writer.print("teavm_local_" + expr.getIndex()); } + popLocation(expr.getLocation()); } @Override public void visit(SubscriptExpr expr) { + pushLocation(expr.getLocation()); writer.print("TEAVM_ARRAY_AT("); expr.getArray().acceptVisitor(this); writer.print(", ").print(getArrayType(expr.getType())).print(", "); expr.getIndex().acceptVisitor(this); writer.print(")"); + popLocation(expr.getLocation()); } @Override public void visit(UnwrapArrayExpr expr) { + pushLocation(expr.getLocation()); expr.getArray().acceptVisitor(this); + popLocation(expr.getLocation()); } private static String getArrayType(ArrayType type) { @@ -393,10 +414,13 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { Intrinsic intrinsic = context.getIntrinsic(expr.getMethod()); if (intrinsic != null) { + pushLocation(expr.getLocation()); intrinsic.apply(intrinsicContext, expr); + popLocation(expr.getLocation()); return; } + pushLocation(expr.getLocation()); switch (expr.getType()) { case CONSTRUCTOR: { String receiver = allocTemporaryVariable(CVariableType.PTR); @@ -482,6 +506,8 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { break; } } + + popLocation(expr.getLocation()); } private void generateWrappedNativeCall(MethodReader method, InvocationExpr expr) { @@ -647,6 +673,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { public void visit(QualificationExpr expr) { FieldReference field = expr.getField(); if (isMonitorField(field)) { + pushLocation(expr.getLocation()); String tmp = allocTemporaryVariable(CVariableType.INT); writer.print("(" + tmp + " = TEAVM_FIELD("); expr.getQualified().acceptVisitor(this); @@ -654,9 +681,11 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { writer.print(", ").print(names.forClass(field.getClassName()) + ", " + names.forMemberField(field) + ")"); writer.print(", TEAVM_UNPACK_MONITOR(" + tmp + "))"); + popLocation(expr.getLocation()); return; } + pushLocation(expr.getLocation()); includes.includeClass(field.getClassName()); if (expr.getQualified() != null) { writer.print("TEAVM_FIELD("); @@ -665,6 +694,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { } else { writer.print(names.forStaticField(field)); } + popLocation(expr.getLocation()); } private boolean isMonitorField(FieldReference field) { @@ -673,9 +703,11 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { @Override public void visit(NewExpr expr) { + pushLocation(expr.getLocation()); includes.includeClass(expr.getConstructedClass()); includes.includeClass(ALLOC_METHOD.getClassName()); allocObject(expr.getConstructedClass()); + popLocation(expr.getLocation()); } private void allocObject(String className) { @@ -686,6 +718,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { @Override public void visit(NewArrayExpr expr) { + pushLocation(expr.getLocation()); ValueType type = ValueType.arrayOf(expr.getType()); writer.print(names.forMethod(ALLOC_ARRAY_METHOD)).print("(&") .print(names.forClassInstance(type)).print(", "); @@ -693,6 +726,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { includes.includeType(type); expr.getLength().acceptVisitor(this); writer.print(")"); + popLocation(expr.getLocation()); } @Override @@ -714,10 +748,12 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { @Override public void visit(InstanceOfExpr expr) { + pushLocation(expr.getLocation()); writer.print("teavm_instanceof("); expr.getExpr().acceptVisitor(this); includes.includeType(expr.getType()); writer.print(", ").print(names.forSupertypeFunction(expr.getType())).print(")"); + popLocation(expr.getLocation()); } @Override @@ -730,14 +766,17 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { return; } } + pushLocation(expr.getLocation()); writer.print("teavm_checkcast("); expr.getValue().acceptVisitor(this); includes.includeType(expr.getTarget()); writer.print(", ").print(names.forSupertypeFunction(expr.getTarget())).print(")"); + popLocation(expr.getLocation()); } @Override public void visit(PrimitiveCastExpr expr) { + pushLocation(expr.getLocation()); writer.print("(("); switch (expr.getTarget()) { case INT: @@ -756,10 +795,12 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { writer.print(") "); expr.getValue().acceptVisitor(this); writer.print(")"); + popLocation(expr.getLocation()); } @Override public void visit(AssignmentStatement statement) { + pushLocation(statement.getLocation()); if (statement.getLeftValue() != null) { if (statement.getLeftValue() instanceof QualificationExpr) { QualificationExpr qualification = (QualificationExpr) statement.getLeftValue(); @@ -772,6 +813,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { + names.forMemberField(field) + ") = TEAVM_PACK_MONITOR("); statement.getRightValue().acceptVisitor(this); writer.println(");"); + popLocation(statement.getLocation()); return; } } @@ -785,6 +827,8 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { if (statement.isAsync()) { emitSuspendChecker(); } + + popLocation(statement.getLocation()); } @Override @@ -809,9 +853,11 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { @Override public void visit(ConditionalStatement statement) { while (true) { + pushLocation(statement.getCondition().getLocation()); writer.print("if ("); statement.getCondition().acceptVisitor(this); writer.println(") {").indent(); + popLocation(statement.getCondition().getLocation()); visitMany(statement.getConsequent()); writer.outdent().print("}"); @@ -836,9 +882,11 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { @Override public void visit(SwitchStatement statement) { + pushLocation(statement.getValue().getLocation()); writer.print("switch ("); statement.getValue().acceptVisitor(this); writer.print(") {").println().indent(); + popLocation(statement.getValue().getLocation()); for (SwitchClause clause : statement.getClauses()) { for (int condition : clause.getConditions()) { @@ -906,44 +954,54 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { @Override public void visit(BreakStatement statement) { + pushLocation(statement.getLocation()); if (statement.getTarget() == null || statement.getTarget().getId() == null) { writer.println("break;"); } else { writer.println("goto teavm_label_" + statement.getTarget().getId() + ";"); } + popLocation(statement.getLocation()); } @Override public void visit(ContinueStatement statement) { + pushLocation(statement.getLocation()); if (statement.getTarget() == null || statement.getTarget().getId() == null) { writer.println("continue;"); } else { writer.println("goto teavm_cnt_" + statement.getTarget().getId() + ";"); } + popLocation(statement.getLocation()); } @Override public void visit(ReturnStatement statement) { + pushLocation(statement.getLocation()); writer.print("return"); if (statement.getResult() != null) { writer.print(" "); statement.getResult().acceptVisitor(this); } writer.println(";"); + popLocation(statement.getLocation()); } @Override public void visit(ThrowStatement statement) { + pushLocation(statement.getLocation()); includes.includeClass(THROW_EXCEPTION_METHOD.getClassName()); writer.print(names.forMethod(THROW_EXCEPTION_METHOD)).print("("); statement.getException().acceptVisitor(this); writer.println(");"); + popLocation(statement.getLocation()); } @Override public void visit(InitClassStatement statement) { + pushLocation(statement.getLocation()); includes.includeClass(statement.getClassName()); writer.println(names.forClassInitializer(statement.getClassName()) + "();"); + popLocation(statement.getLocation()); } @Override @@ -956,18 +1014,22 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { @Override public void visit(MonitorEnterStatement statement) { + pushLocation(statement.getLocation()); includes.includeClass("java.lang.Object"); writer.print(names.forMethod(async ? MONITOR_ENTER : MONITOR_ENTER_SYNC)).print("("); statement.getObjectRef().acceptVisitor(this); writer.println(");"); + popLocation(statement.getLocation()); } @Override public void visit(MonitorExitStatement statement) { + pushLocation(statement.getLocation()); includes.includeClass("java.lang.Object"); writer.print(names.forMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)).print("("); statement.getObjectRef().acceptVisitor(this); writer.println(");"); + popLocation(statement.getLocation()); } public void emitSuspendChecker() { @@ -1043,4 +1105,46 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { } return CVariableType.PTR; } + + private void pushLocation(TextLocation location) { + if (location == null) { + return; + } + LocationStackEntry prevEntry = locationStack.peek(); + if (prevEntry == null || !location.equals(prevEntry.location)) { + if (location.getFileName() == null) { + writer.nosource(); + } else { + writer.source(location.getFileName(), location.getLine()); + } + } + locationStack.push(new LocationStackEntry(location)); + } + + private void popLocation(TextLocation location) { + if (location == null) { + return; + } + LocationStackEntry prevEntry = locationStack.pop(); + LocationStackEntry entry = locationStack.peek(); + if (entry != null) { + if (!entry.location.equals(prevEntry.location)) { + if (entry.location.getFileName() == null) { + writer.nosource(); + } else { + writer.source(entry.location.getFileName(), entry.location.getLine()); + } + } + } else { + writer.nosource(); + } + } + + static class LocationStackEntry { + final TextLocation location; + + LocationStackEntry(TextLocation location) { + this.location = location; + } + } } diff --git a/core/src/main/java/org/teavm/backend/c/generate/CodeWriter.java b/core/src/main/java/org/teavm/backend/c/generate/CodeWriter.java index 95312aad4..a33190b99 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CodeWriter.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CodeWriter.java @@ -137,4 +137,8 @@ public abstract class CodeWriter { protected abstract void indentBy(int amount); public abstract void flush(); + + public abstract void source(String fileName, int lineNumber); + + public abstract void nosource(); } diff --git a/core/src/main/java/org/teavm/backend/c/generate/GeneratorContextImpl.java b/core/src/main/java/org/teavm/backend/c/generate/GeneratorContextImpl.java index b7df5f747..298afc35e 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/GeneratorContextImpl.java +++ b/core/src/main/java/org/teavm/backend/c/generate/GeneratorContextImpl.java @@ -92,12 +92,12 @@ class GeneratorContextImpl implements GeneratorContext { @Override public FileGenerator createSourceFile(String path) { - return createFile(new BufferedCodeWriter(), path); + return createFile(new BufferedCodeWriter(false), path); } @Override public FileGenerator createHeaderFile(String path) { - BufferedCodeWriter writer = new BufferedCodeWriter(); + BufferedCodeWriter writer = new BufferedCodeWriter(false); writer.println("#pragma once"); return createFile(writer, path); } diff --git a/core/src/main/java/org/teavm/backend/c/generate/OutputFileUtil.java b/core/src/main/java/org/teavm/backend/c/generate/OutputFileUtil.java index ba684d12c..e5d963cc4 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/OutputFileUtil.java +++ b/core/src/main/java/org/teavm/backend/c/generate/OutputFileUtil.java @@ -28,7 +28,7 @@ public final class OutputFileUtil { public static void write(BufferedCodeWriter code, String name, BuildTarget buildTarget) throws IOException { try (PrintWriter writer = new PrintWriter(new OutputStreamWriter( buildTarget.createResource(name), StandardCharsets.UTF_8))) { - code.writeTo(writer); + code.writeTo(writer, name); } } } diff --git a/tools/c-incremental/src/main/java/org/teavm/tooling/c/incremental/IncrementalCBuilder.java b/tools/c-incremental/src/main/java/org/teavm/tooling/c/incremental/IncrementalCBuilder.java index 2bd17b121..c23b63678 100644 --- a/tools/c-incremental/src/main/java/org/teavm/tooling/c/incremental/IncrementalCBuilder.java +++ b/tools/c-incremental/src/main/java/org/teavm/tooling/c/incremental/IncrementalCBuilder.java @@ -64,6 +64,7 @@ public class IncrementalCBuilder { private String mainClass; private String[] classPath; private int minHeapSize = 32; + private boolean lineNumbersGenerated; private String targetPath; private String externalTool; private String externalToolWorkingDir; @@ -107,6 +108,10 @@ public class IncrementalCBuilder { this.minHeapSize = minHeapSize; } + public void setLineNumbersGenerated(boolean lineNumbersGenerated) { + this.lineNumbersGenerated = lineNumbersGenerated; + } + public void setTargetPath(String targetPath) { this.targetPath = targetPath; } @@ -316,6 +321,7 @@ public class IncrementalCBuilder { cTarget.setIncremental(true); cTarget.setMinHeapSize(minHeapSize * 1024 * 1024); + cTarget.setLineNumbersGenerated(lineNumbersGenerated); vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE); vm.setCacheStatus(classSource); vm.addVirtualMethods(m -> true); diff --git a/tools/cli/src/main/java/org/teavm/cli/TeaVMCBuilderRunner.java b/tools/cli/src/main/java/org/teavm/cli/TeaVMCBuilderRunner.java index 366d4f036..b47814ca2 100644 --- a/tools/cli/src/main/java/org/teavm/cli/TeaVMCBuilderRunner.java +++ b/tools/cli/src/main/java/org/teavm/cli/TeaVMCBuilderRunner.java @@ -52,6 +52,10 @@ public class TeaVMCBuilderRunner { .withDescription("display more messages on server log") .withLongOpt("verbose") .create('v')); + options.addOption(OptionBuilder + .withDescription("generate debugger-friendly code") + .withLongOpt("debug") + .create('g')); options.addOption(OptionBuilder .withLongOpt("min-heap") .withArgName("size") @@ -103,6 +107,7 @@ public class TeaVMCBuilderRunner { parseExternalTool(); builder.setLog(new ConsoleTeaVMToolLog(commandLine.hasOption('v'))); + builder.setLineNumbersGenerated(commandLine.hasOption('g')); String[] args = commandLine.getArgs(); if (args.length != 1) { diff --git a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java index 56d4ac881..3d160777f 100644 --- a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java +++ b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java @@ -321,6 +321,7 @@ public class TeaVMTool { private CTarget prepareCTarget() { cTarget = new CTarget(); cTarget.setMinHeapSize(minHeapSize); + cTarget.setLineNumbersGenerated(debugInformationGenerated); return cTarget; }