C: generate '#line' preprocessor directive

This commit is contained in:
Alexey Andreev 2019-05-16 11:12:35 +03:00
parent 7a99258cab
commit 0003ed0bb2
9 changed files with 425 additions and 145 deletions

View File

@ -144,6 +144,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private Characteristics characteristics; private Characteristics characteristics;
private Set<MethodReference> asyncMethods; private Set<MethodReference> asyncMethods;
private boolean incremental; private boolean incremental;
private boolean lineNumbersGenerated;
private StringPool stringPool; private StringPool stringPool;
public CTarget() { public CTarget() {
@ -162,6 +163,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
this.incremental = incremental; this.incremental = incremental;
} }
public void setLineNumbersGenerated(boolean lineNumbersGenerated) {
this.lineNumbersGenerated = lineNumbersGenerated;
}
@Override @Override
public List<ClassHolderTransformer> getTransformers() { public List<ClassHolderTransformer> getTransformers() {
List<ClassHolderTransformer> transformers = new ArrayList<>(); List<ClassHolderTransformer> transformers = new ArrayList<>();
@ -332,8 +337,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes, controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes,
intrinsics, generators, asyncMethods::contains, buildTarget, incremental); intrinsics, generators, asyncMethods::contains, buildTarget, incremental);
BufferedCodeWriter runtimeWriter = new BufferedCodeWriter(); BufferedCodeWriter runtimeWriter = new BufferedCodeWriter(false);
BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter(); BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter(false);
emitResource(runtimeWriter, "runtime.c"); emitResource(runtimeWriter, "runtime.c");
runtimeHeaderWriter.println("#pragma once"); runtimeHeaderWriter.println("#pragma once");
@ -393,8 +398,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
classGenerator.prepare(classes); classGenerator.prepare(classes);
for (String className : classNames) { for (String className : classNames) {
BufferedCodeWriter writer = new BufferedCodeWriter(); BufferedCodeWriter writer = new BufferedCodeWriter(lineNumbersGenerated);
BufferedCodeWriter headerWriter = new BufferedCodeWriter(); BufferedCodeWriter headerWriter = new BufferedCodeWriter(false);
ClassHolder cls = classes.get(className); ClassHolder cls = classes.get(className);
if (cls != null) { if (cls != null) {
classGenerator.generateClass(writer, headerWriter, cls); classGenerator.generateClass(writer, headerWriter, cls);
@ -408,8 +413,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
if (type instanceof ValueType.Object) { if (type instanceof ValueType.Object) {
continue; continue;
} }
BufferedCodeWriter writer = new BufferedCodeWriter(); BufferedCodeWriter writer = new BufferedCodeWriter(false);
BufferedCodeWriter headerWriter = new BufferedCodeWriter(); BufferedCodeWriter headerWriter = new BufferedCodeWriter(false);
classGenerator.generateType(writer, headerWriter, type); classGenerator.generateType(writer, headerWriter, type);
String name = ClassGenerator.fileName(type); String name = ClassGenerator.fileName(type);
OutputFileUtil.write(writer, name + ".c", buildTarget); OutputFileUtil.write(writer, name + ".c", buildTarget);
@ -451,8 +456,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private void generateCallSites(BuildTarget buildTarget, GenerationContext context, private void generateCallSites(BuildTarget buildTarget, GenerationContext context,
Collection<? extends String> classNames) throws IOException { Collection<? extends String> classNames) throws IOException {
BufferedCodeWriter writer = new BufferedCodeWriter(); BufferedCodeWriter writer = new BufferedCodeWriter(false);
BufferedCodeWriter headerWriter = new BufferedCodeWriter(); BufferedCodeWriter headerWriter = new BufferedCodeWriter(false);
IncludeManager includes = new SimpleIncludeManager(writer); IncludeManager includes = new SimpleIncludeManager(writer);
includes.init("callsites.c"); includes.init("callsites.c");
@ -491,8 +496,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
} }
private void generateStrings(BuildTarget buildTarget, StringPool stringPool) throws IOException { private void generateStrings(BuildTarget buildTarget, StringPool stringPool) throws IOException {
BufferedCodeWriter writer = new BufferedCodeWriter(); BufferedCodeWriter writer = new BufferedCodeWriter(false);
BufferedCodeWriter headerWriter = new BufferedCodeWriter(); BufferedCodeWriter headerWriter = new BufferedCodeWriter(false);
headerWriter.println("#pragma once"); headerWriter.println("#pragma once");
headerWriter.println("#include \"runtime.h\""); headerWriter.println("#include \"runtime.h\"");
@ -588,7 +593,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private void generateMainFile(GenerationContext context, ListableClassHolderSource classes, private void generateMainFile(GenerationContext context, ListableClassHolderSource classes,
List<? extends ValueType> types, BuildTarget buildTarget) throws IOException { List<? extends ValueType> types, BuildTarget buildTarget) throws IOException {
BufferedCodeWriter writer = new BufferedCodeWriter(); BufferedCodeWriter writer = new BufferedCodeWriter(false);
IncludeManager includes = new SimpleIncludeManager(writer); IncludeManager includes = new SimpleIncludeManager(writer);
includes.init("main.c"); includes.init("main.c");
includes.includePath("runtime.h"); includes.includePath("runtime.h");
@ -601,7 +606,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private void generateAllFile(ListableClassHolderSource classes, List<? extends ValueType> types, private void generateAllFile(ListableClassHolderSource classes, List<? extends ValueType> types,
BuildTarget buildTarget) throws IOException { BuildTarget buildTarget) throws IOException {
BufferedCodeWriter writer = new BufferedCodeWriter(); BufferedCodeWriter writer = new BufferedCodeWriter(false);
IncludeManager includes = new SimpleIncludeManager(writer); IncludeManager includes = new SimpleIncludeManager(writer);
includes.init("all.c"); includes.init("all.c");
includes.includePath("runtime.c"); includes.includePath("runtime.c");

View File

@ -18,18 +18,24 @@ package org.teavm.backend.c.generate;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
public class BufferedCodeWriter extends CodeWriter { public class BufferedCodeWriter extends CodeWriter {
private List<Fragment> fragments = new ArrayList<>(); private List<Fragment> fragments = new ArrayList<>();
private int currentIndent; private int currentIndent;
private int lastIndent; private int lastIndent;
private StringBuilder buffer = new StringBuilder(); 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) { public void writeTo(PrintWriter writer, String fileName) {
WriterWithContext writerWithContext = new WriterWithContext(writer); WriterWithContext writerWithContext = new WriterWithContext(writer, fileName);
for (Fragment fragment : fragments) { for (Fragment fragment : fragments) {
fragment.writeTo(writerWithContext); fragment.writeTo(writerWithContext);
} }
@ -38,8 +44,12 @@ public class BufferedCodeWriter extends CodeWriter {
@Override @Override
public CodeWriter fragment() { public CodeWriter fragment() {
flush(); 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)); fragments.add(new InnerWriterFragment(innerWriter.fragments));
locationDirty = true;
return innerWriter; return innerWriter;
} }
@ -49,6 +59,9 @@ public class BufferedCodeWriter extends CodeWriter {
buffer.setLength(0); buffer.setLength(0);
lastIndent = currentIndent; lastIndent = currentIndent;
currentIndent = 0; currentIndent = 0;
if (lineNumbersEmitted) {
lastLineNumber++;
}
} }
@Override @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 @Override
public void flush() { public void flush() {
fragments.add(new SimpleFragment(false, lastIndent, buffer.toString())); if (buffer.length() > 0 || lastIndent != 0 || currentIndent != 0) {
lastIndent = currentIndent; fragments.add(new SimpleFragment(false, lastIndent, buffer.toString()));
currentIndent = 0; lastIndent = currentIndent;
buffer.setLength(0); currentIndent = 0;
buffer.setLength(0);
}
} }
static class WriterWithContext { static class WriterWithContext {
PrintWriter writer; PrintWriter writer;
boolean isNewLine = true; boolean isNewLine = true;
int indentLevel; 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.writer = writer;
this.fileName = fileName;
initialFileName = fileName;
lineNumber = 1;
} }
void append(String text) { void append(String text) {
if (text.isEmpty()) {
return;
}
if (isNewLine) { if (isNewLine) {
for (int i = 0; i < indentLevel; ++i) { if (pendingFileName != null || pendingLineNumber >= 0) {
writer.print(" "); printLineDirective(pendingFileName, pendingLineNumber);
pendingLineNumber = -1;
pendingFileName = null;
} }
printIndent();
isNewLine = false; isNewLine = false;
} }
writer.print(text); 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() { void newLine() {
lineNumber++;
absLineNumber++;
writer.println(); writer.println();
isNewLine = true; 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 { 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 { static class InnerWriterFragment extends Fragment {
List<Fragment> fragments; List<Fragment> fragments;

View File

@ -22,7 +22,9 @@ import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.nio.LongBuffer; import java.nio.LongBuffer;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -78,6 +80,7 @@ import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.TextLocation;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.classes.VirtualTable; import org.teavm.model.classes.VirtualTable;
import org.teavm.runtime.Allocator; import org.teavm.runtime.Allocator;
@ -116,6 +119,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
private IncludeManager includes; private IncludeManager includes;
private boolean end; private boolean end;
private boolean async; private boolean async;
private final Deque<LocationStackEntry> locationStack = new ArrayDeque<>();
static { static {
BUFFER_TYPES.put(ByteBuffer.class.getName(), "int8_t"); BUFFER_TYPES.put(ByteBuffer.class.getName(), "int8_t");
@ -148,137 +152,143 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(BinaryExpr expr) { public void visit(BinaryExpr expr) {
switch (expr.getOperation()) { pushLocation(expr.getLocation());
case COMPARE: try {
writer.print("teavm_compare_"); switch (expr.getOperation()) {
switch (expr.getType()) { case COMPARE:
case INT: writer.print("teavm_compare_");
writer.print("i32"); switch (expr.getType()) {
break; case INT:
case LONG: writer.print("i32");
writer.print("i64"); break;
break; case LONG:
case FLOAT: writer.print("i64");
writer.print("float"); break;
break; case FLOAT:
case DOUBLE: writer.print("float");
writer.print("double"); break;
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); case MODULO: {
writer.print(" >> "); switch (expr.getType()) {
expr.getSecondOperand().acceptVisitor(this); 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("))"); default:
return; break;
} }
case MODULO: { writer.print("(");
switch (expr.getType()) { expr.getFirstOperand().acceptVisitor(this);
case FLOAT:
writer.print("fmodf("); String op;
expr.getFirstOperand().acceptVisitor(this); switch (expr.getOperation()) {
writer.print(", "); case ADD:
expr.getSecondOperand().acceptVisitor(this); op = "+";
writer.print(")"); break;
return; case SUBTRACT:
case DOUBLE: op = "-";
writer.print("fmod("); break;
expr.getFirstOperand().acceptVisitor(this); case MULTIPLY:
writer.print(", "); op = "*";
expr.getSecondOperand().acceptVisitor(this); break;
writer.print(")"); case DIVIDE:
return; op = "/";
default: break;
break; case MODULO:
} op = "%";
break; 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: writer.print(" ").print(op).print(" ");
break; 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 @Override
public void visit(UnaryExpr expr) { public void visit(UnaryExpr expr) {
pushLocation(expr.getLocation());
switch (expr.getOperation()) { switch (expr.getOperation()) {
case NOT: case NOT:
writer.print("("); writer.print("(");
@ -316,10 +326,12 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
writer.print(")"); writer.print(")");
break; break;
} }
popLocation(expr.getLocation());
} }
@Override @Override
public void visit(ConditionalExpr expr) { public void visit(ConditionalExpr expr) {
pushLocation(expr.getLocation());
writer.print("("); writer.print("(");
expr.getCondition().acceptVisitor(this); expr.getCondition().acceptVisitor(this);
writer.print(" ? "); writer.print(" ? ");
@ -327,34 +339,43 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
writer.print(" : "); writer.print(" : ");
expr.getAlternative().acceptVisitor(this); expr.getAlternative().acceptVisitor(this);
writer.print(")"); writer.print(")");
popLocation(expr.getLocation());
} }
@Override @Override
public void visit(ConstantExpr expr) { public void visit(ConstantExpr expr) {
pushLocation(expr.getLocation());
CodeGeneratorUtil.writeValue(writer, context, includes, expr.getValue()); CodeGeneratorUtil.writeValue(writer, context, includes, expr.getValue());
popLocation(expr.getLocation());
} }
@Override @Override
public void visit(VariableExpr expr) { public void visit(VariableExpr expr) {
pushLocation(expr.getLocation());
if (expr.getIndex() == 0) { if (expr.getIndex() == 0) {
writer.print("teavm_this_"); writer.print("teavm_this_");
} else { } else {
writer.print("teavm_local_" + expr.getIndex()); writer.print("teavm_local_" + expr.getIndex());
} }
popLocation(expr.getLocation());
} }
@Override @Override
public void visit(SubscriptExpr expr) { public void visit(SubscriptExpr expr) {
pushLocation(expr.getLocation());
writer.print("TEAVM_ARRAY_AT("); writer.print("TEAVM_ARRAY_AT(");
expr.getArray().acceptVisitor(this); expr.getArray().acceptVisitor(this);
writer.print(", ").print(getArrayType(expr.getType())).print(", "); writer.print(", ").print(getArrayType(expr.getType())).print(", ");
expr.getIndex().acceptVisitor(this); expr.getIndex().acceptVisitor(this);
writer.print(")"); writer.print(")");
popLocation(expr.getLocation());
} }
@Override @Override
public void visit(UnwrapArrayExpr expr) { public void visit(UnwrapArrayExpr expr) {
pushLocation(expr.getLocation());
expr.getArray().acceptVisitor(this); expr.getArray().acceptVisitor(this);
popLocation(expr.getLocation());
} }
private static String getArrayType(ArrayType type) { private static String getArrayType(ArrayType type) {
@ -393,10 +414,13 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
Intrinsic intrinsic = context.getIntrinsic(expr.getMethod()); Intrinsic intrinsic = context.getIntrinsic(expr.getMethod());
if (intrinsic != null) { if (intrinsic != null) {
pushLocation(expr.getLocation());
intrinsic.apply(intrinsicContext, expr); intrinsic.apply(intrinsicContext, expr);
popLocation(expr.getLocation());
return; return;
} }
pushLocation(expr.getLocation());
switch (expr.getType()) { switch (expr.getType()) {
case CONSTRUCTOR: { case CONSTRUCTOR: {
String receiver = allocTemporaryVariable(CVariableType.PTR); String receiver = allocTemporaryVariable(CVariableType.PTR);
@ -482,6 +506,8 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
break; break;
} }
} }
popLocation(expr.getLocation());
} }
private void generateWrappedNativeCall(MethodReader method, InvocationExpr expr) { private void generateWrappedNativeCall(MethodReader method, InvocationExpr expr) {
@ -647,6 +673,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
public void visit(QualificationExpr expr) { public void visit(QualificationExpr expr) {
FieldReference field = expr.getField(); FieldReference field = expr.getField();
if (isMonitorField(field)) { if (isMonitorField(field)) {
pushLocation(expr.getLocation());
String tmp = allocTemporaryVariable(CVariableType.INT); String tmp = allocTemporaryVariable(CVariableType.INT);
writer.print("(" + tmp + " = TEAVM_FIELD("); writer.print("(" + tmp + " = TEAVM_FIELD(");
expr.getQualified().acceptVisitor(this); expr.getQualified().acceptVisitor(this);
@ -654,9 +681,11 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
writer.print(", ").print(names.forClass(field.getClassName()) + ", " writer.print(", ").print(names.forClass(field.getClassName()) + ", "
+ names.forMemberField(field) + ")"); + names.forMemberField(field) + ")");
writer.print(", TEAVM_UNPACK_MONITOR(" + tmp + "))"); writer.print(", TEAVM_UNPACK_MONITOR(" + tmp + "))");
popLocation(expr.getLocation());
return; return;
} }
pushLocation(expr.getLocation());
includes.includeClass(field.getClassName()); includes.includeClass(field.getClassName());
if (expr.getQualified() != null) { if (expr.getQualified() != null) {
writer.print("TEAVM_FIELD("); writer.print("TEAVM_FIELD(");
@ -665,6 +694,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
} else { } else {
writer.print(names.forStaticField(field)); writer.print(names.forStaticField(field));
} }
popLocation(expr.getLocation());
} }
private boolean isMonitorField(FieldReference field) { private boolean isMonitorField(FieldReference field) {
@ -673,9 +703,11 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(NewExpr expr) { public void visit(NewExpr expr) {
pushLocation(expr.getLocation());
includes.includeClass(expr.getConstructedClass()); includes.includeClass(expr.getConstructedClass());
includes.includeClass(ALLOC_METHOD.getClassName()); includes.includeClass(ALLOC_METHOD.getClassName());
allocObject(expr.getConstructedClass()); allocObject(expr.getConstructedClass());
popLocation(expr.getLocation());
} }
private void allocObject(String className) { private void allocObject(String className) {
@ -686,6 +718,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(NewArrayExpr expr) { public void visit(NewArrayExpr expr) {
pushLocation(expr.getLocation());
ValueType type = ValueType.arrayOf(expr.getType()); ValueType type = ValueType.arrayOf(expr.getType());
writer.print(names.forMethod(ALLOC_ARRAY_METHOD)).print("(&") writer.print(names.forMethod(ALLOC_ARRAY_METHOD)).print("(&")
.print(names.forClassInstance(type)).print(", "); .print(names.forClassInstance(type)).print(", ");
@ -693,6 +726,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
includes.includeType(type); includes.includeType(type);
expr.getLength().acceptVisitor(this); expr.getLength().acceptVisitor(this);
writer.print(")"); writer.print(")");
popLocation(expr.getLocation());
} }
@Override @Override
@ -714,10 +748,12 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(InstanceOfExpr expr) { public void visit(InstanceOfExpr expr) {
pushLocation(expr.getLocation());
writer.print("teavm_instanceof("); writer.print("teavm_instanceof(");
expr.getExpr().acceptVisitor(this); expr.getExpr().acceptVisitor(this);
includes.includeType(expr.getType()); includes.includeType(expr.getType());
writer.print(", ").print(names.forSupertypeFunction(expr.getType())).print(")"); writer.print(", ").print(names.forSupertypeFunction(expr.getType())).print(")");
popLocation(expr.getLocation());
} }
@Override @Override
@ -730,14 +766,17 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
return; return;
} }
} }
pushLocation(expr.getLocation());
writer.print("teavm_checkcast("); writer.print("teavm_checkcast(");
expr.getValue().acceptVisitor(this); expr.getValue().acceptVisitor(this);
includes.includeType(expr.getTarget()); includes.includeType(expr.getTarget());
writer.print(", ").print(names.forSupertypeFunction(expr.getTarget())).print(")"); writer.print(", ").print(names.forSupertypeFunction(expr.getTarget())).print(")");
popLocation(expr.getLocation());
} }
@Override @Override
public void visit(PrimitiveCastExpr expr) { public void visit(PrimitiveCastExpr expr) {
pushLocation(expr.getLocation());
writer.print("(("); writer.print("((");
switch (expr.getTarget()) { switch (expr.getTarget()) {
case INT: case INT:
@ -756,10 +795,12 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
writer.print(") "); writer.print(") ");
expr.getValue().acceptVisitor(this); expr.getValue().acceptVisitor(this);
writer.print(")"); writer.print(")");
popLocation(expr.getLocation());
} }
@Override @Override
public void visit(AssignmentStatement statement) { public void visit(AssignmentStatement statement) {
pushLocation(statement.getLocation());
if (statement.getLeftValue() != null) { if (statement.getLeftValue() != null) {
if (statement.getLeftValue() instanceof QualificationExpr) { if (statement.getLeftValue() instanceof QualificationExpr) {
QualificationExpr qualification = (QualificationExpr) statement.getLeftValue(); QualificationExpr qualification = (QualificationExpr) statement.getLeftValue();
@ -772,6 +813,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
+ names.forMemberField(field) + ") = TEAVM_PACK_MONITOR("); + names.forMemberField(field) + ") = TEAVM_PACK_MONITOR(");
statement.getRightValue().acceptVisitor(this); statement.getRightValue().acceptVisitor(this);
writer.println(");"); writer.println(");");
popLocation(statement.getLocation());
return; return;
} }
} }
@ -785,6 +827,8 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
if (statement.isAsync()) { if (statement.isAsync()) {
emitSuspendChecker(); emitSuspendChecker();
} }
popLocation(statement.getLocation());
} }
@Override @Override
@ -809,9 +853,11 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(ConditionalStatement statement) { public void visit(ConditionalStatement statement) {
while (true) { while (true) {
pushLocation(statement.getCondition().getLocation());
writer.print("if ("); writer.print("if (");
statement.getCondition().acceptVisitor(this); statement.getCondition().acceptVisitor(this);
writer.println(") {").indent(); writer.println(") {").indent();
popLocation(statement.getCondition().getLocation());
visitMany(statement.getConsequent()); visitMany(statement.getConsequent());
writer.outdent().print("}"); writer.outdent().print("}");
@ -836,9 +882,11 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(SwitchStatement statement) { public void visit(SwitchStatement statement) {
pushLocation(statement.getValue().getLocation());
writer.print("switch ("); writer.print("switch (");
statement.getValue().acceptVisitor(this); statement.getValue().acceptVisitor(this);
writer.print(") {").println().indent(); writer.print(") {").println().indent();
popLocation(statement.getValue().getLocation());
for (SwitchClause clause : statement.getClauses()) { for (SwitchClause clause : statement.getClauses()) {
for (int condition : clause.getConditions()) { for (int condition : clause.getConditions()) {
@ -906,44 +954,54 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(BreakStatement statement) { public void visit(BreakStatement statement) {
pushLocation(statement.getLocation());
if (statement.getTarget() == null || statement.getTarget().getId() == null) { if (statement.getTarget() == null || statement.getTarget().getId() == null) {
writer.println("break;"); writer.println("break;");
} else { } else {
writer.println("goto teavm_label_" + statement.getTarget().getId() + ";"); writer.println("goto teavm_label_" + statement.getTarget().getId() + ";");
} }
popLocation(statement.getLocation());
} }
@Override @Override
public void visit(ContinueStatement statement) { public void visit(ContinueStatement statement) {
pushLocation(statement.getLocation());
if (statement.getTarget() == null || statement.getTarget().getId() == null) { if (statement.getTarget() == null || statement.getTarget().getId() == null) {
writer.println("continue;"); writer.println("continue;");
} else { } else {
writer.println("goto teavm_cnt_" + statement.getTarget().getId() + ";"); writer.println("goto teavm_cnt_" + statement.getTarget().getId() + ";");
} }
popLocation(statement.getLocation());
} }
@Override @Override
public void visit(ReturnStatement statement) { public void visit(ReturnStatement statement) {
pushLocation(statement.getLocation());
writer.print("return"); writer.print("return");
if (statement.getResult() != null) { if (statement.getResult() != null) {
writer.print(" "); writer.print(" ");
statement.getResult().acceptVisitor(this); statement.getResult().acceptVisitor(this);
} }
writer.println(";"); writer.println(";");
popLocation(statement.getLocation());
} }
@Override @Override
public void visit(ThrowStatement statement) { public void visit(ThrowStatement statement) {
pushLocation(statement.getLocation());
includes.includeClass(THROW_EXCEPTION_METHOD.getClassName()); includes.includeClass(THROW_EXCEPTION_METHOD.getClassName());
writer.print(names.forMethod(THROW_EXCEPTION_METHOD)).print("("); writer.print(names.forMethod(THROW_EXCEPTION_METHOD)).print("(");
statement.getException().acceptVisitor(this); statement.getException().acceptVisitor(this);
writer.println(");"); writer.println(");");
popLocation(statement.getLocation());
} }
@Override @Override
public void visit(InitClassStatement statement) { public void visit(InitClassStatement statement) {
pushLocation(statement.getLocation());
includes.includeClass(statement.getClassName()); includes.includeClass(statement.getClassName());
writer.println(names.forClassInitializer(statement.getClassName()) + "();"); writer.println(names.forClassInitializer(statement.getClassName()) + "();");
popLocation(statement.getLocation());
} }
@Override @Override
@ -956,18 +1014,22 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(MonitorEnterStatement statement) { public void visit(MonitorEnterStatement statement) {
pushLocation(statement.getLocation());
includes.includeClass("java.lang.Object"); includes.includeClass("java.lang.Object");
writer.print(names.forMethod(async ? MONITOR_ENTER : MONITOR_ENTER_SYNC)).print("("); writer.print(names.forMethod(async ? MONITOR_ENTER : MONITOR_ENTER_SYNC)).print("(");
statement.getObjectRef().acceptVisitor(this); statement.getObjectRef().acceptVisitor(this);
writer.println(");"); writer.println(");");
popLocation(statement.getLocation());
} }
@Override @Override
public void visit(MonitorExitStatement statement) { public void visit(MonitorExitStatement statement) {
pushLocation(statement.getLocation());
includes.includeClass("java.lang.Object"); includes.includeClass("java.lang.Object");
writer.print(names.forMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)).print("("); writer.print(names.forMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)).print("(");
statement.getObjectRef().acceptVisitor(this); statement.getObjectRef().acceptVisitor(this);
writer.println(");"); writer.println(");");
popLocation(statement.getLocation());
} }
public void emitSuspendChecker() { public void emitSuspendChecker() {
@ -1043,4 +1105,46 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
} }
return CVariableType.PTR; 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;
}
}
} }

View File

@ -137,4 +137,8 @@ public abstract class CodeWriter {
protected abstract void indentBy(int amount); protected abstract void indentBy(int amount);
public abstract void flush(); public abstract void flush();
public abstract void source(String fileName, int lineNumber);
public abstract void nosource();
} }

View File

@ -92,12 +92,12 @@ class GeneratorContextImpl implements GeneratorContext {
@Override @Override
public FileGenerator createSourceFile(String path) { public FileGenerator createSourceFile(String path) {
return createFile(new BufferedCodeWriter(), path); return createFile(new BufferedCodeWriter(false), path);
} }
@Override @Override
public FileGenerator createHeaderFile(String path) { public FileGenerator createHeaderFile(String path) {
BufferedCodeWriter writer = new BufferedCodeWriter(); BufferedCodeWriter writer = new BufferedCodeWriter(false);
writer.println("#pragma once"); writer.println("#pragma once");
return createFile(writer, path); return createFile(writer, path);
} }

View File

@ -28,7 +28,7 @@ public final class OutputFileUtil {
public static void write(BufferedCodeWriter code, String name, BuildTarget buildTarget) throws IOException { public static void write(BufferedCodeWriter code, String name, BuildTarget buildTarget) throws IOException {
try (PrintWriter writer = new PrintWriter(new OutputStreamWriter( try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(
buildTarget.createResource(name), StandardCharsets.UTF_8))) { buildTarget.createResource(name), StandardCharsets.UTF_8))) {
code.writeTo(writer); code.writeTo(writer, name);
} }
} }
} }

View File

@ -64,6 +64,7 @@ public class IncrementalCBuilder {
private String mainClass; private String mainClass;
private String[] classPath; private String[] classPath;
private int minHeapSize = 32; private int minHeapSize = 32;
private boolean lineNumbersGenerated;
private String targetPath; private String targetPath;
private String externalTool; private String externalTool;
private String externalToolWorkingDir; private String externalToolWorkingDir;
@ -107,6 +108,10 @@ public class IncrementalCBuilder {
this.minHeapSize = minHeapSize; this.minHeapSize = minHeapSize;
} }
public void setLineNumbersGenerated(boolean lineNumbersGenerated) {
this.lineNumbersGenerated = lineNumbersGenerated;
}
public void setTargetPath(String targetPath) { public void setTargetPath(String targetPath) {
this.targetPath = targetPath; this.targetPath = targetPath;
} }
@ -316,6 +321,7 @@ public class IncrementalCBuilder {
cTarget.setIncremental(true); cTarget.setIncremental(true);
cTarget.setMinHeapSize(minHeapSize * 1024 * 1024); cTarget.setMinHeapSize(minHeapSize * 1024 * 1024);
cTarget.setLineNumbersGenerated(lineNumbersGenerated);
vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE); vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
vm.setCacheStatus(classSource); vm.setCacheStatus(classSource);
vm.addVirtualMethods(m -> true); vm.addVirtualMethods(m -> true);

View File

@ -52,6 +52,10 @@ public class TeaVMCBuilderRunner {
.withDescription("display more messages on server log") .withDescription("display more messages on server log")
.withLongOpt("verbose") .withLongOpt("verbose")
.create('v')); .create('v'));
options.addOption(OptionBuilder
.withDescription("generate debugger-friendly code")
.withLongOpt("debug")
.create('g'));
options.addOption(OptionBuilder options.addOption(OptionBuilder
.withLongOpt("min-heap") .withLongOpt("min-heap")
.withArgName("size") .withArgName("size")
@ -103,6 +107,7 @@ public class TeaVMCBuilderRunner {
parseExternalTool(); parseExternalTool();
builder.setLog(new ConsoleTeaVMToolLog(commandLine.hasOption('v'))); builder.setLog(new ConsoleTeaVMToolLog(commandLine.hasOption('v')));
builder.setLineNumbersGenerated(commandLine.hasOption('g'));
String[] args = commandLine.getArgs(); String[] args = commandLine.getArgs();
if (args.length != 1) { if (args.length != 1) {

View File

@ -321,6 +321,7 @@ public class TeaVMTool {
private CTarget prepareCTarget() { private CTarget prepareCTarget() {
cTarget = new CTarget(); cTarget = new CTarget();
cTarget.setMinHeapSize(minHeapSize); cTarget.setMinHeapSize(minHeapSize);
cTarget.setLineNumbersGenerated(debugInformationGenerated);
return cTarget; return cTarget;
} }