[vm/bytecode] Emit source positions for bytecode instructions
Change-Id: I83963771db301089317823f6bf14eb4a2728dbfb
Reviewed-on: https://dart-review.googlesource.com/c/81740
Auto-Submit: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
diff --git a/pkg/vm/bin/gen_kernel.dart b/pkg/vm/bin/gen_kernel.dart
index 86333d7..bdb5df6 100644
--- a/pkg/vm/bin/gen_kernel.dart
+++ b/pkg/vm/bin/gen_kernel.dart
@@ -44,6 +44,8 @@
help: 'Whether kernel constant evaluation will be enabled.',
defaultsTo: true)
..addFlag('gen-bytecode', help: 'Generate bytecode', defaultsTo: false)
+ ..addFlag('emit-bytecode-source-positions',
+ help: 'Emit source positions in bytecode', defaultsTo: false)
..addFlag('drop-ast',
help: 'Drop AST for members with bytecode', defaultsTo: false)
..addFlag('use-future-bytecode-format',
@@ -83,6 +85,8 @@
final bool aot = options['aot'];
final bool tfa = options['tfa'];
final bool genBytecode = options['gen-bytecode'];
+ final bool emitBytecodeSourcePositions =
+ options['emit-bytecode-source-positions'];
final bool dropAST = options['drop-ast'];
final bool useFutureBytecodeFormat = options['use-future-bytecode-format'];
final bool enableAsserts = options['enable-asserts'];
@@ -115,6 +119,7 @@
useGlobalTypeFlowAnalysis: tfa,
environmentDefines: environmentDefines,
genBytecode: genBytecode,
+ emitBytecodeSourcePositions: emitBytecodeSourcePositions,
dropAST: dropAST,
useFutureBytecodeFormat: useFutureBytecodeFormat,
enableAsserts: enableAsserts,
diff --git a/pkg/vm/lib/bytecode/assembler.dart b/pkg/vm/lib/bytecode/assembler.dart
index 3309d43..2bf1127 100644
--- a/pkg/vm/lib/bytecode/assembler.dart
+++ b/pkg/vm/lib/bytecode/assembler.dart
@@ -6,8 +6,11 @@
import 'dart:typed_data';
+import 'package:kernel/ast.dart' show TreeNode;
+
import 'dbc.dart';
import 'exceptions.dart' show ExceptionsTable;
+import 'source_positions.dart' show SourcePositions;
class Label {
final bool allowsBackwardJumps;
@@ -48,7 +51,9 @@
final Uint32List _encodeBufferIn;
final Uint8List _encodeBufferOut;
final ExceptionsTable exceptionsTable = new ExceptionsTable();
+ final SourcePositions sourcePositions = new SourcePositions();
bool isUnreachable = false;
+ int currentSourcePosition = TreeNode.noOffset;
BytecodeAssembler._(this._encodeBufferIn, this._encodeBufferOut);
@@ -70,6 +75,12 @@
}
}
+ void emitSourcePosition() {
+ if (currentSourcePosition != TreeNode.noOffset && !isUnreachable) {
+ sourcePositions.add(offsetInWords, currentSourcePosition);
+ }
+ }
+
void emitWord(int word) {
if (isUnreachable) {
return;
@@ -150,6 +161,7 @@
void emitBytecode0(Opcode opcode) {
assert(BytecodeFormats[opcode].encoding == Encoding.k0);
+ emitSourcePosition();
emitWord(_encode0(opcode));
}
@@ -255,18 +267,22 @@
}
void emitIndirectStaticCall(int ra, int rd) {
+ emitSourcePosition();
emitWord(_encodeAD(Opcode.kIndirectStaticCall, ra, rd));
}
void emitInstanceCall(int ra, int rd) {
+ emitSourcePosition();
emitWord(_encodeAD(Opcode.kInstanceCall, ra, rd));
}
void emitNativeCall(int rd) {
+ emitSourcePosition();
emitWord(_encodeD(Opcode.kNativeCall, rd));
}
void emitStoreStaticTOS(int rd) {
+ emitSourcePosition();
emitWord(_encodeD(Opcode.kStoreStaticTOS, rd));
}
@@ -279,10 +295,12 @@
}
void emitAllocate(int rd) {
+ emitSourcePosition();
emitWord(_encodeD(Opcode.kAllocate, rd));
}
void emitAllocateT() {
+ emitSourcePosition();
emitWord(_encode0(Opcode.kAllocateT));
}
@@ -291,6 +309,7 @@
}
void emitStoreFieldTOS(int rd) {
+ emitSourcePosition();
emitWord(_encodeD(Opcode.kStoreFieldTOS, rd));
}
@@ -323,6 +342,7 @@
}
void emitThrow(int ra) {
+ emitSourcePosition();
emitWord(_encodeA(Opcode.kThrow, ra));
isUnreachable = true;
}
@@ -352,30 +372,37 @@
}
void emitInstantiateType(int rd) {
+ emitSourcePosition();
emitWord(_encodeD(Opcode.kInstantiateType, rd));
}
void emitInstantiateTypeArgumentsTOS(int ra, int rd) {
+ emitSourcePosition();
emitWord(_encodeAD(Opcode.kInstantiateTypeArgumentsTOS, ra, rd));
}
void emitAssertAssignable(int ra, int rd) {
+ emitSourcePosition();
emitWord(_encodeAD(Opcode.kAssertAssignable, ra, rd));
}
void emitAssertSubtype() {
+ emitSourcePosition();
emitWord(_encode0(Opcode.kAssertSubtype));
}
void emitAssertBoolean(int ra) {
+ emitSourcePosition();
emitWord(_encodeA(Opcode.kAssertBoolean, ra));
}
void emitCheckStack(int ra) {
+ emitSourcePosition();
emitWord(_encodeA(Opcode.kCheckStack, ra));
}
void emitCheckFunctionTypeArgs(int ra, int rd) {
+ emitSourcePosition();
emitWord(_encodeAD(Opcode.kCheckFunctionTypeArgs, ra, rd));
}
diff --git a/pkg/vm/lib/bytecode/disassembler.dart b/pkg/vm/lib/bytecode/disassembler.dart
index c9b6e46..afe5a82 100644
--- a/pkg/vm/lib/bytecode/disassembler.dart
+++ b/pkg/vm/lib/bytecode/disassembler.dart
@@ -24,10 +24,14 @@
Map<int, String> _labels;
Map<int, List<String>> _markers;
- String disassemble(List<int> bytecode, ExceptionsTable exceptionsTable) {
+ String disassemble(List<int> bytecode, ExceptionsTable exceptionsTable,
+ {List<Map<int, String>> annotations}) {
_init(bytecode);
_scanForJumpTargets();
_markTryBlocks(exceptionsTable);
+ if (annotations != null) {
+ _markAnnotations(annotations);
+ }
return _disasm();
}
@@ -116,6 +120,14 @@
}
}
+ void _markAnnotations(List<Map<int, String>> annotations) {
+ for (var map in annotations) {
+ map.forEach((int pc, String annotation) {
+ _addMarker(pc, '# $annotation');
+ });
+ }
+ }
+
void _addMarker(int pc, String marker) {
final markers = (_markers[pc] ??= <String>[]);
markers.add(marker);
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index 12c4793f..36ef9d4 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -38,7 +38,8 @@
void generateBytecode(Component component,
{bool dropAST: false,
- bool omitSourcePositions: false,
+ bool emitSourcePositions: false,
+ bool omitAssertSourcePositions: false,
bool useFutureBytecodeFormat: false,
Map<String, String> environmentDefines,
ErrorReporter errorReporter}) {
@@ -57,7 +58,8 @@
hierarchy,
typeEnvironment,
constantsBackend,
- omitSourcePositions,
+ emitSourcePositions,
+ omitAssertSourcePositions,
useFutureBytecodeFormat,
errorReporter)
.visitComponent(component);
@@ -72,7 +74,8 @@
final ClassHierarchy hierarchy;
final TypeEnvironment typeEnvironment;
final ConstantsBackend constantsBackend;
- final bool omitSourcePositions;
+ final bool emitSourcePositions;
+ final bool omitAssertSourcePositions;
final bool useFutureBytecodeFormat;
final ErrorReporter errorReporter;
final BytecodeMetadataRepository metadata = new BytecodeMetadataRepository();
@@ -111,7 +114,8 @@
this.hierarchy,
this.typeEnvironment,
this.constantsBackend,
- this.omitSourcePositions,
+ this.emitSourcePositions,
+ this.omitAssertSourcePositions,
this.useFutureBytecodeFormat,
this.errorReporter)
: recognizedMethods = new RecognizedMethods(typeEnvironment) {
@@ -150,7 +154,7 @@
if (node.isConst) {
_genPushConstExpr(node.initializer);
} else {
- node.initializer.accept(this);
+ _generateNode(node.initializer);
}
_genReturnTOS();
end(node);
@@ -168,7 +172,7 @@
}
_genNativeCall(nativeName);
} else {
- node.function?.body?.accept(this);
+ _generateNode(node.function?.body);
// BytecodeAssembler eliminates this bytecode if it is unreachable.
asm.emitPushNull();
}
@@ -287,6 +291,26 @@
Procedure get unsafeCast => _unsafeCast ??=
libraryIndex.getTopLevelMember('dart:_internal', 'unsafeCast');
+ void _recordSourcePosition(TreeNode node) {
+ if (emitSourcePositions) {
+ asm.currentSourcePosition = node.fileOffset;
+ }
+ }
+
+ void _generateNode(TreeNode node) {
+ if (node == null) {
+ return;
+ }
+ final savedSourcePosition = asm.currentSourcePosition;
+ _recordSourcePosition(node);
+ node.accept(this);
+ asm.currentSourcePosition = savedSourcePosition;
+ }
+
+ void _generateNodeList(List<TreeNode> nodes) {
+ nodes.forEach(_generateNode);
+ }
+
void _genConstructorInitializers(Constructor node) {
final bool isRedirecting =
node.initializers.any((init) => init is RedirectingInitializer);
@@ -300,7 +324,7 @@
}
}
- visitList(node.initializers, this);
+ _generateNodeList(node.initializers);
if (!isRedirecting) {
nullableFields = <Reference>[];
@@ -321,7 +345,7 @@
}
_genPushReceiver();
- initializer.accept(this);
+ _generateNode(initializer);
final int cpIndex = cp.add(new ConstantInstanceField(field));
asm.emitStoreFieldTOS(cpIndex);
@@ -333,9 +357,9 @@
if (arguments.types.isNotEmpty) {
_genTypeArguments(arguments.types);
}
- receiver?.accept(this);
- visitList(arguments.positional, this);
- arguments.named.forEach((NamedExpression ne) => ne.value.accept(this));
+ _generateNode(receiver);
+ _generateNodeList(arguments.positional);
+ arguments.named.forEach((NamedExpression ne) => _generateNode(ne.value));
}
void _genPushBool(bool value) {
@@ -636,7 +660,7 @@
condition = (condition as Not).operand;
negated = true;
}
- condition.accept(this);
+ _generateNode(condition);
asm.emitAssertBoolean(0);
return negated;
}
@@ -804,6 +828,7 @@
locals.enterScope(node);
assert(!locals.isSyncYieldingFrame);
+ _recordSourcePosition(node);
_genPrologue(node, node.function);
_setupInitialContext(node.function);
if (node is Procedure && node.isInstanceMember) {
@@ -836,8 +861,14 @@
final formatVersion = useFutureBytecodeFormat
? futureBytecodeFormatVersion
: stableBytecodeFormatVersion;
- metadata.mapping[node] = new BytecodeMetadata(formatVersion, cp,
- asm.bytecode, asm.exceptionsTable, nullableFields, closures);
+ metadata.mapping[node] = new BytecodeMetadata(
+ formatVersion,
+ cp,
+ asm.bytecode,
+ asm.exceptionsTable,
+ asm.sourcePositions,
+ nullableFields,
+ closures);
}
typeEnvironment.thisType = null;
@@ -1270,7 +1301,7 @@
// TODO(alexmarkov): support --causal_async_stacks.
- function.body.accept(this);
+ _generateNode(function.body);
// BytecodeAssembler eliminates this bytecode if it is unreachable.
asm.emitPushNull();
@@ -1295,8 +1326,8 @@
locals.leaveScope();
- closures.add(new ClosureBytecode(
- closureFunctionIndex, asm.bytecode, asm.exceptionsTable));
+ closures.add(new ClosureBytecode(closureFunctionIndex, asm.bytecode,
+ asm.exceptionsTable, asm.sourcePositions));
_popAssemblerState();
yieldPoints = savedYieldPoints;
@@ -1518,7 +1549,7 @@
for (Expression arg in args) {
asm.emitPush(temp);
_genPushInt(index++);
- arg.accept(this);
+ _generateNode(arg);
if (storeLastArgumentToTemp && index == totalCount) {
// Arguments array in 'temp' is replaced with the last argument
// in order to return result of RHS value in case of setter.
@@ -1560,7 +1591,7 @@
@override
visitAsExpression(AsExpression node) {
- node.operand.accept(this);
+ _generateNode(node.operand);
final type = node.type;
if (typeEnvironment.isTop(type)) {
@@ -1594,12 +1625,12 @@
_genConditionAndJumpIf(node.condition, false, otherwisePart);
- node.then.accept(this);
+ _generateNode(node.then);
asm.emitPopLocal(temp);
asm.emitJump(done);
asm.bind(otherwisePart);
- node.otherwise.accept(this);
+ _generateNode(node.otherwise);
asm.emitPopLocal(temp);
asm.bind(done);
@@ -1653,7 +1684,7 @@
@override
visitDirectPropertyGet(DirectPropertyGet node) {
- node.receiver.accept(this);
+ _generateNode(node.receiver);
final target = node.target;
if (target is Field || (target is Procedure && target.isGetter)) {
_genStaticCall(target, new ConstantArgDesc(1), 1, isGet: true);
@@ -1668,8 +1699,8 @@
final int temp = locals.tempIndexInFrame(node);
final bool hasResult = !isExpressionWithoutResult(node);
- node.receiver.accept(this);
- node.value.accept(this);
+ _generateNode(node.receiver);
+ _generateNode(node.value);
if (hasResult) {
asm.emitStoreLocal(temp);
@@ -1696,7 +1727,7 @@
final int newClosure = locals.tempIndexInFrame(node, tempIndex: 1);
final int typeArguments = locals.tempIndexInFrame(node, tempIndex: 2);
- node.expression.accept(this);
+ _generateNode(node.expression);
asm.emitStoreLocal(oldClosure);
_genTypeArguments(node.typeArguments);
@@ -1735,15 +1766,15 @@
@override
visitIsExpression(IsExpression node) {
- node.operand.accept(this);
+ _generateNode(node.operand);
_genInstanceOf(node.type);
}
@override
visitLet(Let node) {
_enterScope(node);
- node.variable.accept(this);
- node.body.accept(this);
+ _generateNode(node.variable);
+ _generateNode(node.body);
_leaveScope();
}
@@ -1767,7 +1798,7 @@
for (int i = 0; i < node.expressions.length; i++) {
asm.emitPush(temp);
_genPushInt(i);
- node.expressions[i].accept(this);
+ _generateNode(node.expressions[i]);
asm.emitStoreIndexedTOS();
}
@@ -1828,12 +1859,12 @@
// key
asm.emitPush(temp);
_genPushInt(i * 2);
- node.entries[i].key.accept(this);
+ _generateNode(node.entries[i].key);
asm.emitStoreIndexedTOS();
// value
asm.emitPush(temp);
_genPushInt(i * 2 + 1);
- node.entries[i].value.accept(this);
+ _generateNode(node.entries[i].value);
asm.emitStoreIndexedTOS();
}
}
@@ -1850,14 +1881,14 @@
switch (opcode) {
case Opcode.kEqualsNull:
if (node.receiver is NullLiteral) {
- node.arguments.positional.single.accept(this);
+ _generateNode(node.arguments.positional.single);
} else {
- node.receiver.accept(this);
+ _generateNode(node.receiver);
}
break;
case Opcode.kNegateInt:
- node.receiver.accept(this);
+ _generateNode(node.receiver);
break;
case Opcode.kAddInt:
@@ -1875,8 +1906,8 @@
case Opcode.kCompareIntLt:
case Opcode.kCompareIntGe:
case Opcode.kCompareIntLe:
- node.receiver.accept(this);
- node.arguments.positional.single.accept(this);
+ _generateNode(node.receiver);
+ _generateNode(node.arguments.positional.single);
break;
default:
@@ -1909,7 +1940,7 @@
@override
visitPropertyGet(PropertyGet node) {
- node.receiver.accept(this);
+ _generateNode(node.receiver);
final argDescIndex = cp.add(new ConstantArgDesc(1));
final icdataIndex = cp.add(new ConstantICData(
InvocationKind.getter, node.name, argDescIndex,
@@ -1922,8 +1953,8 @@
final int temp = locals.tempIndexInFrame(node);
final bool hasResult = !isExpressionWithoutResult(node);
- node.receiver.accept(this);
- node.value.accept(this);
+ _generateNode(node.receiver);
+ _generateNode(node.value);
if (hasResult) {
asm.emitStoreLocal(temp);
@@ -1989,7 +2020,7 @@
storeLastArgumentToTemp: hasResult);
} else {
_genPushReceiver();
- node.value.accept(this);
+ _generateNode(node.value);
if (hasResult) {
asm.emitStoreLocal(temp);
@@ -2081,7 +2112,7 @@
// The result of the unsafeCast() intrinsic method is its sole argument,
// without any additional checks or type casts.
assert(args.named.isEmpty);
- args.positional.single.accept(this);
+ _generateNode(args.positional.single);
return;
}
if (target.isFactory) {
@@ -2105,7 +2136,7 @@
visitStaticSet(StaticSet node) {
final bool hasResult = !isExpressionWithoutResult(node);
- node.value.accept(this);
+ _generateNode(node.value);
if (hasResult) {
_genDupTOS(locals.tempIndexInFrame(node));
@@ -2124,7 +2155,7 @@
@override
visitStringConcatenation(StringConcatenation node) {
if (node.expressions.length == 1) {
- node.expressions.single.accept(this);
+ _generateNode(node.expressions.single);
_genStaticCall(interpolateSingle, new ConstantArgDesc(1), 1);
} else {
asm.emitPushNull();
@@ -2137,7 +2168,7 @@
for (int i = 0; i < node.expressions.length; i++) {
asm.emitPush(temp);
_genPushInt(i);
- node.expressions[i].accept(this);
+ _generateNode(node.expressions[i]);
asm.emitStoreIndexedTOS();
}
@@ -2163,7 +2194,7 @@
@override
visitThrow(Throw node) {
- node.expression.accept(this);
+ _generateNode(node.expression);
asm.emitThrow(0);
}
@@ -2197,7 +2228,7 @@
if (locals.isCaptured(v)) {
_genPushContextForVariable(v);
- node.value.accept(this);
+ _generateNode(node.value);
final int temp = locals.tempIndexInFrame(node);
if (hasResult) {
@@ -2210,7 +2241,7 @@
asm.emitPush(temp);
}
} else {
- node.value.accept(this);
+ _generateNode(node.value);
final int localIndex = locals.getVarIndexInFrame(v);
if (hasResult) {
@@ -2243,11 +2274,11 @@
_genConditionAndJumpIf(node.condition, true, done);
- _genPushInt(omitSourcePositions ? 0 : node.conditionStartOffset);
- _genPushInt(omitSourcePositions ? 0 : node.conditionEndOffset);
+ _genPushInt(omitAssertSourcePositions ? 0 : node.conditionStartOffset);
+ _genPushInt(omitAssertSourcePositions ? 0 : node.conditionEndOffset);
if (node.message != null) {
- node.message.accept(this);
+ _generateNode(node.message);
} else {
asm.emitPushNull();
}
@@ -2261,7 +2292,7 @@
@override
visitBlock(Block node) {
_enterScope(node);
- visitList(node.statements, this);
+ _generateNodeList(node.statements);
_leaveScope();
}
@@ -2271,7 +2302,7 @@
asm.emitJumpIfNoAsserts(done);
_enterScope(node);
- visitList(node.statements, this);
+ _generateNodeList(node.statements);
_leaveScope();
asm.bind(done);
@@ -2314,7 +2345,7 @@
asm.emitCheckStack(++currentLoopDepth);
- node.body.accept(this);
+ _generateNode(node.body);
_genConditionAndJumpIf(node.condition, true, join);
@@ -2329,7 +2360,7 @@
@override
visitExpressionStatement(ExpressionStatement node) {
final expr = node.expression;
- expr.accept(this);
+ _generateNode(expr);
if (!isExpressionWithoutResult(expr)) {
asm.emitDrop1();
}
@@ -2337,7 +2368,7 @@
@override
visitForInStatement(ForInStatement node) {
- node.iterable.accept(this);
+ _generateNode(node.iterable);
const kIterator = 'iterator'; // Iterable.iterator
const kMoveNext = 'moveNext'; // Iterator.moveNext
@@ -2395,7 +2426,7 @@
_genStoreVar(node.variable);
- node.body.accept(this);
+ _generateNode(node.body);
_leaveScope();
asm.emitJump(join);
@@ -2408,7 +2439,7 @@
visitForStatement(ForStatement node) {
_enterScope(node);
try {
- visitList(node.variables, this);
+ _generateNodeList(node.variables);
if (asm.isUnreachable) {
// Bail out before binding a label which allows backward jumps,
@@ -2426,7 +2457,7 @@
_genConditionAndJumpIf(node.condition, false, done);
}
- node.body.accept(this);
+ _generateNode(node.body);
if (locals.currentContextSize > 0) {
asm.emitPush(locals.contextVarIndexInFrame);
@@ -2435,7 +2466,7 @@
}
for (var update in node.updates) {
- update.accept(this);
+ _generateNode(update);
asm.emitDrop1();
}
@@ -2461,13 +2492,13 @@
_genConditionAndJumpIf(node.condition, false, otherwisePart);
- node.then.accept(this);
+ _generateNode(node.then);
if (node.otherwise != null) {
final Label done = new Label();
asm.emitJump(done);
asm.bind(otherwisePart);
- node.otherwise.accept(this);
+ _generateNode(node.otherwise);
asm.bind(done);
} else {
asm.bind(otherwisePart);
@@ -2479,7 +2510,7 @@
final label = new Label();
labeledStatements[node] = label;
contextLevels[node] = locals.currentContextLevel;
- node.body.accept(this);
+ _generateNode(node.body);
asm.bind(label);
labeledStatements.remove(node);
contextLevels.remove(node);
@@ -2492,18 +2523,18 @@
final List<TryFinally> tryFinallyBlocks =
_getEnclosingTryFinallyBlocks(node, null);
if (tryFinallyBlocks.isEmpty) {
- expr.accept(this);
+ _generateNode(expr);
asm.emitReturnTOS();
} else {
if (expr is BasicLiteral) {
_addFinallyBlocks(tryFinallyBlocks, () {
- expr.accept(this);
+ _generateNode(expr);
asm.emitReturnTOS();
});
} else {
// Keep return value in a variable as try-catch statements
// inside finally can zap expression stack.
- node.expression.accept(this);
+ _generateNode(node.expression);
asm.emitPopLocal(locals.returnVarIndexInFrame);
_addFinallyBlocks(tryFinallyBlocks, () {
@@ -2518,7 +2549,7 @@
visitSwitchStatement(SwitchStatement node) {
contextLevels[node] = locals.currentContextLevel;
- node.expression.accept(this);
+ _generateNode(node.expression);
if (asm.isUnreachable) {
// Bail out before binding labels which allow backward jumps,
@@ -2562,7 +2593,7 @@
final Label caseLabel = caseLabels[i];
asm.bind(caseLabel);
- switchCase.body.accept(this);
+ _generateNode(switchCase.body);
// Front-end issues a compile-time error if there is a fallthrough
// between cases. Also, default case should be the last one.
@@ -2702,7 +2733,7 @@
tryBlock.isSynthetic = node.isSynthetic;
tryCatches[node] = tryBlock; // Used by rethrow.
- node.body.accept(this);
+ _generateNode(node.body);
asm.emitJump(done);
_endTryBlock(node, tryBlock);
@@ -2741,7 +2772,7 @@
_genStoreVar(catchClause.stackTrace);
}
- catchClause.body.accept(this);
+ _generateNode(catchClause.body);
_leaveScope();
asm.emitJump(done);
@@ -2769,7 +2800,7 @@
final TryBlock tryBlock = _startTryBlock(node);
finallyBlocks[node] = <FinallyBlock>[];
- node.body.accept(this);
+ _generateNode(node.body);
if (!asm.isUnreachable) {
final normalContinuation = new FinallyBlock(() {
@@ -2783,7 +2814,7 @@
tryBlock.types.add(cp.add(new ConstantType(const DynamicType())));
- node.finalizer.accept(this);
+ _generateNode(node.finalizer);
tryBlock.needsStackTrace = true; // For rethrowing.
_genRethrow(node);
@@ -2791,7 +2822,7 @@
for (var finallyBlock in finallyBlocks[node]) {
asm.bind(finallyBlock.entry);
_restoreContextForTryBlock(node);
- node.finalizer.accept(this);
+ _generateNode(node.finalizer);
finallyBlock.generateContinuation();
}
@@ -2809,7 +2840,7 @@
_genPushContextForVariable(node);
}
if (node.initializer != null) {
- node.initializer.accept(this);
+ _generateNode(node.initializer);
} else {
asm.emitPushNull();
}
@@ -2837,7 +2868,7 @@
_genConditionAndJumpIf(node.condition, false, done);
- node.body.accept(this);
+ _generateNode(node.body);
asm.emitJump(join);
--currentLoopDepth;
@@ -2874,7 +2905,7 @@
// return <expression>
// Note: finally blocks are *not* executed on the way out.
- node.expression.accept(this);
+ _generateNode(node.expression);
asm.emitReturnTOS();
asm.bind(continuationLabel);
@@ -2931,12 +2962,12 @@
@override
visitLocalInitializer(LocalInitializer node) {
- node.variable.accept(this);
+ _generateNode(node.variable);
}
@override
visitAssertInitializer(AssertInitializer node) {
- node.statement.accept(this);
+ _generateNode(node.statement);
}
@override
diff --git a/pkg/vm/lib/bytecode/source_positions.dart b/pkg/vm/lib/bytecode/source_positions.dart
new file mode 100644
index 0000000..055f394
--- /dev/null
+++ b/pkg/vm/lib/bytecode/source_positions.dart
@@ -0,0 +1,174 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library vm.bytecode.source_positions;
+
+import 'dart:io' show BytesBuilder;
+
+/// Maintains mapping between bytecode instructions and source positions.
+class SourcePositions {
+ final Map<int, int> mapping = <int, int>{}; // PC -> fileOffset
+ int _lastPc = 0;
+ int _lastOffset = 0;
+
+ SourcePositions();
+
+ void add(int pc, int fileOffset) {
+ assert(pc > _lastPc);
+ assert(fileOffset >= 0);
+ if (fileOffset != _lastOffset) {
+ mapping[pc] = fileOffset;
+ _lastPc = pc;
+ _lastOffset = fileOffset;
+ }
+ }
+
+ List<int> toBytes() {
+ final write = new BufferedWriter();
+ write.writePackedUInt30(mapping.length);
+ final encodePC = new PackedUInt30DeltaEncoder();
+ final encodeOffset = new SLEB128DeltaEncoder();
+ mapping.forEach((int pc, int fileOffset) {
+ encodePC.write(write, pc);
+ encodeOffset.write(write, fileOffset);
+ });
+ return write.buffer.takeBytes();
+ }
+
+ SourcePositions.fromBytes(List<int> bytes) {
+ final reader = new BufferedReader(bytes);
+ final int length = reader.readPackedUInt30();
+ final decodePC = new PackedUInt30DeltaDecoder();
+ final decodeOffset = new SLEB128DeltaDecoder();
+ for (int i = 0; i < length; ++i) {
+ int pc = decodePC.read(reader);
+ int fileOffset = decodeOffset.read(reader);
+ add(pc, fileOffset);
+ }
+ }
+
+ @override
+ String toString() => mapping.toString();
+
+ Map<int, String> getBytecodeAnnotations() {
+ return mapping.map((int pc, int fileOffset) =>
+ new MapEntry(pc, 'source position $fileOffset'));
+ }
+}
+
+class BufferedWriter {
+ final BytesBuilder buffer = new BytesBuilder();
+
+ void writePackedUInt30(int value) {
+ if ((value >> 30) != 0) {
+ throw 'Value $value is out of range';
+ }
+ if (value < 0x80) {
+ buffer.addByte(value);
+ } else if (value < 0x4000) {
+ buffer.addByte((value >> 8) | 0x80);
+ buffer.addByte(value & 0xFF);
+ } else {
+ buffer.addByte((value >> 24) | 0xC0);
+ buffer.addByte((value >> 16) & 0xFF);
+ buffer.addByte((value >> 8) & 0xFF);
+ buffer.addByte(value & 0xFF);
+ }
+ }
+
+ void writeSLEB128(int value) {
+ bool last = false;
+ do {
+ int part = value & 0x7f;
+ value >>= 7;
+ if ((value == 0 && (part & 0x40) == 0) ||
+ (value == -1 && (part & 0x40) != 0)) {
+ last = true;
+ } else {
+ part |= 0x80;
+ }
+ buffer.addByte(part);
+ } while (!last);
+ }
+}
+
+class BufferedReader {
+ final List<int> _buffer;
+ int _pos = 0;
+
+ BufferedReader(this._buffer);
+
+ int readByte() => _buffer[_pos++];
+
+ int readPackedUInt30() {
+ var byte = readByte();
+ if (byte & 0x80 == 0) {
+ // 0xxxxxxx
+ return byte;
+ } else if (byte & 0x40 == 0) {
+ // 10xxxxxx
+ return ((byte & 0x3F) << 8) | readByte();
+ } else {
+ // 11xxxxxx
+ return ((byte & 0x3F) << 24) |
+ (readByte() << 16) |
+ (readByte() << 8) |
+ readByte();
+ }
+ }
+
+ int readSLEB128() {
+ int value = 0;
+ int shift = 0;
+ int part = 0;
+ do {
+ part = readByte();
+ value |= (part & 0x7f) << shift;
+ shift += 7;
+ } while ((part & 0x80) != 0);
+ const int kBitsPerInt = 64;
+ if ((shift < kBitsPerInt) && ((part & 0x40) != 0)) {
+ value |= (-1) << shift;
+ }
+ return value;
+ }
+}
+
+class PackedUInt30DeltaEncoder {
+ int _last = 0;
+
+ void write(BufferedWriter write, int value) {
+ write.writePackedUInt30(value - _last);
+ _last = value;
+ }
+}
+
+class PackedUInt30DeltaDecoder {
+ int _last = 0;
+
+ int read(BufferedReader reader) {
+ int value = reader.readPackedUInt30() + _last;
+ _last = value;
+ return value;
+ }
+}
+
+class SLEB128DeltaEncoder {
+ int _last = 0;
+
+ void write(BufferedWriter writer, int value) {
+ writer.writeSLEB128(value - _last);
+ _last = value;
+ }
+}
+
+class SLEB128DeltaDecoder {
+ int _last = 0;
+
+ int read(BufferedReader reader) {
+ int value = reader.readSLEB128() + _last;
+ _last = value;
+ return value;
+ }
+}
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 03c4a51..be0184f 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -50,6 +50,7 @@
bool useGlobalTypeFlowAnalysis: false,
Map<String, String> environmentDefines,
bool genBytecode: false,
+ bool emitBytecodeSourcePositions: false,
bool dropAST: false,
bool useFutureBytecodeFormat: false,
bool enableAsserts: false,
@@ -95,6 +96,7 @@
await runWithFrontEndCompilerContext(source, options, component, () {
generateBytecode(component,
dropAST: dropAST,
+ emitSourcePositions: emitBytecodeSourcePositions,
useFutureBytecodeFormat: useFutureBytecodeFormat,
environmentDefines: environmentDefines);
});
diff --git a/pkg/vm/lib/metadata/bytecode.dart b/pkg/vm/lib/metadata/bytecode.dart
index ef52237..e333cfc 100644
--- a/pkg/vm/lib/metadata/bytecode.dart
+++ b/pkg/vm/lib/metadata/bytecode.dart
@@ -10,6 +10,7 @@
show stableBytecodeFormatVersion, futureBytecodeFormatVersion;
import '../bytecode/disassembler.dart' show BytecodeDisassembler;
import '../bytecode/exceptions.dart' show ExceptionsTable;
+import '../bytecode/source_positions.dart' show SourcePositions;
/// Metadata containing bytecode.
///
@@ -17,8 +18,8 @@
///
/// type BytecodeMetadata {
/// UInt bytecodeFormatVersion
-/// UInt flags (HasExceptionsTable, HasNullableFields, HasClosures)
-///
+/// UInt flags (HasExceptionsTable, HasSourcePositions, HasNullableFields,
+/// HasClosures)
/// ConstantPool constantPool
///
/// UInt bytecodeSizeInBytes
@@ -29,6 +30,9 @@
/// (optional, present if HasExceptionsTable)
/// ExceptionsTable exceptionsTable
///
+/// (optional, present if HasSourcePositions)
+/// SourcePositions sourcePositionsTabe
+///
/// (optional, present if HasNullableFields)
/// List<CanonicalName> nullableFields
///
@@ -38,13 +42,18 @@
///
/// type ClosureBytecode {
/// ConstantIndex closureFunction
+/// UInt flags (HasExceptionsTable, HasSourcePositions)
///
/// UInt bytecodeSizeInBytes
/// Byte paddingSizeInBytes
/// Byte[paddingSizeInBytes] padding
/// Byte[bytecodeSizeInBytes] bytecodes
///
+/// (optional, present if HasExceptionsTable)
/// ExceptionsTable exceptionsTable
+///
+/// (optional, present if HasSourcePositions)
+/// SourcePositions sourcePositionsTabe
/// }
///
/// Encoding of ExceptionsTable is described in
@@ -55,27 +64,37 @@
///
class BytecodeMetadata {
static const hasExceptionsTableFlag = 1 << 0;
- static const hasNullableFieldsFlag = 1 << 1;
- static const hasClosuresFlag = 1 << 2;
+ static const hasSourcePositionsFlag = 1 << 1;
+ static const hasNullableFieldsFlag = 1 << 2;
+ static const hasClosuresFlag = 1 << 3;
final int version;
final ConstantPool constantPool;
final List<int> bytecodes;
final ExceptionsTable exceptionsTable;
+ final SourcePositions sourcePositions;
final List<Reference> nullableFields;
final List<ClosureBytecode> closures;
bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty;
+ bool get hasSourcePositions => sourcePositions.mapping.isNotEmpty;
bool get hasNullableFields => nullableFields.isNotEmpty;
bool get hasClosures => closures.isNotEmpty;
int get flags =>
(hasExceptionsTable ? hasExceptionsTableFlag : 0) |
+ (hasSourcePositions ? hasSourcePositionsFlag : 0) |
(hasNullableFields ? hasNullableFieldsFlag : 0) |
(hasClosures ? hasClosuresFlag : 0);
- BytecodeMetadata(this.version, this.constantPool, this.bytecodes,
- this.exceptionsTable, this.nullableFields, this.closures);
+ BytecodeMetadata(
+ this.version,
+ this.constantPool,
+ this.bytecodes,
+ this.exceptionsTable,
+ this.sourcePositions,
+ this.nullableFields,
+ this.closures);
// TODO(alexmarkov): Consider printing constant pool before bytecode.
@override
@@ -84,7 +103,9 @@
" (version: "
"${version == stableBytecodeFormatVersion ? 'stable' : version == futureBytecodeFormatVersion ? 'future' : "v$version"}"
") {\n"
- "${new BytecodeDisassembler().disassemble(bytecodes, exceptionsTable)}}\n"
+ "${new BytecodeDisassembler().disassemble(bytecodes, exceptionsTable, annotations: [
+ sourcePositions.getBytecodeAnnotations()
+ ])}}\n"
"$exceptionsTable"
"${nullableFields.isEmpty ? '' : 'Nullable fields: ${nullableFields.map((ref) => ref.asField).toList()}\n'}"
"$constantPool"
@@ -97,30 +118,53 @@
final int closureFunctionConstantIndex;
final List<int> bytecodes;
final ExceptionsTable exceptionsTable;
+ final SourcePositions sourcePositions;
- ClosureBytecode(
- this.closureFunctionConstantIndex, this.bytecodes, this.exceptionsTable);
+ bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty;
+ bool get hasSourcePositions => sourcePositions.mapping.isNotEmpty;
+
+ int get flags =>
+ (hasExceptionsTable ? BytecodeMetadata.hasExceptionsTableFlag : 0) |
+ (hasSourcePositions ? BytecodeMetadata.hasSourcePositionsFlag : 0);
+
+ ClosureBytecode(this.closureFunctionConstantIndex, this.bytecodes,
+ this.exceptionsTable, this.sourcePositions);
void writeToBinary(BinarySink sink) {
sink.writeUInt30(closureFunctionConstantIndex);
+ sink.writeUInt30(flags);
_writeBytecodeInstructions(sink, bytecodes);
- exceptionsTable.writeToBinary(sink);
+ if (hasExceptionsTable) {
+ exceptionsTable.writeToBinary(sink);
+ }
+ if (hasSourcePositions) {
+ sink.writeByteList(sourcePositions.toBytes());
+ }
}
factory ClosureBytecode.readFromBinary(BinarySource source) {
final closureFunctionConstantIndex = source.readUInt();
+ final int flags = source.readUInt();
final List<int> bytecodes = _readBytecodeInstructions(source);
- final exceptionsTable = new ExceptionsTable.readFromBinary(source);
- return new ClosureBytecode(
- closureFunctionConstantIndex, bytecodes, exceptionsTable);
+ final exceptionsTable =
+ ((flags & BytecodeMetadata.hasExceptionsTableFlag) != 0)
+ ? new ExceptionsTable.readFromBinary(source)
+ : new ExceptionsTable();
+ final sourcePositions =
+ ((flags & BytecodeMetadata.hasSourcePositionsFlag) != 0)
+ ? new SourcePositions.fromBytes(source.readByteList())
+ : new SourcePositions();
+ return new ClosureBytecode(closureFunctionConstantIndex, bytecodes,
+ exceptionsTable, sourcePositions);
}
@override
String toString() {
StringBuffer sb = new StringBuffer();
sb.writeln('Closure CP#$closureFunctionConstantIndex {');
- sb.writeln(
- new BytecodeDisassembler().disassemble(bytecodes, exceptionsTable));
+ sb.writeln(new BytecodeDisassembler().disassemble(
+ bytecodes, exceptionsTable,
+ annotations: [sourcePositions.getBytecodeAnnotations()]));
sb.writeln('}');
return sb.toString();
}
@@ -144,6 +188,9 @@
if (metadata.hasExceptionsTable) {
metadata.exceptionsTable.writeToBinary(sink);
}
+ if (metadata.hasSourcePositions) {
+ sink.writeByteList(metadata.sourcePositions.toBytes());
+ }
if (metadata.hasNullableFields) {
sink.writeUInt30(metadata.nullableFields.length);
metadata.nullableFields.forEach((ref) => sink
@@ -165,12 +212,15 @@
int flags = source.readUInt();
final ConstantPool constantPool =
new ConstantPool.readFromBinary(node, source);
- _readBytecodePadding(source);
final List<int> bytecodes = _readBytecodeInstructions(source);
final exceptionsTable =
((flags & BytecodeMetadata.hasExceptionsTableFlag) != 0)
? new ExceptionsTable.readFromBinary(source)
: new ExceptionsTable();
+ final sourcePositions =
+ ((flags & BytecodeMetadata.hasSourcePositionsFlag) != 0)
+ ? new SourcePositions.fromBytes(source.readByteList())
+ : new SourcePositions();
final List<Reference> nullableFields =
((flags & BytecodeMetadata.hasNullableFieldsFlag) != 0)
? new List<Reference>.generate(source.readUInt(),
@@ -182,7 +232,7 @@
(_) => new ClosureBytecode.readFromBinary(source))
: const <ClosureBytecode>[];
return new BytecodeMetadata(version, constantPool, bytecodes,
- exceptionsTable, nullableFields, closures);
+ exceptionsTable, sourcePositions, nullableFields, closures);
}
}
diff --git a/pkg/vm/test/bytecode/gen_bytecode_test.dart b/pkg/vm/test/bytecode/gen_bytecode_test.dart
index 758f47f..1cb36de 100644
--- a/pkg/vm/test/bytecode/gen_bytecode_test.dart
+++ b/pkg/vm/test/bytecode/gen_bytecode_test.dart
@@ -31,7 +31,7 @@
await runWithFrontEndCompilerContext(source, options, component, () {
// Need to omit source positions from bytecode as they are different on
// Linux and Windows (due to differences in newline characters).
- generateBytecode(component, omitSourcePositions: true);
+ generateBytecode(component, omitAssertSourcePositions: true);
});
final actual = kernelLibraryToString(component.mainMethod.enclosingLibrary);
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index d543349..0f80fbc 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -74,11 +74,13 @@
}
const int kHasExceptionsTableFlag = 1 << 0;
- const int kHasNullableFieldsFlag = 1 << 1;
- const int kHasClosuresFlag = 1 << 2;
+ const int kHasSourcePositionsFlag = 1 << 1;
+ const int kHasNullableFieldsFlag = 1 << 2;
+ const int kHasClosuresFlag = 1 << 3;
const intptr_t flags = helper_->reader_.ReadUInt();
const bool has_exceptions_table = (flags & kHasExceptionsTableFlag) != 0;
+ const bool has_source_positions = (flags & kHasSourcePositionsFlag) != 0;
const bool has_nullable_fields = (flags & kHasNullableFieldsFlag) != 0;
const bool has_closures = (flags & kHasClosuresFlag) != 0;
@@ -101,9 +103,10 @@
const Code& bytecode = Code::Handle(helper_->zone_, ReadBytecode(pool));
function.AttachBytecode(bytecode);
- // Read exceptions table.
ReadExceptionsTable(bytecode, has_exceptions_table);
+ ReadSourcePositions(bytecode, has_source_positions);
+
if (FLAG_dump_kernel_bytecode) {
KernelBytecodeDisassembler::Disassemble(function);
}
@@ -137,12 +140,18 @@
ASSERT(closure_index < obj_count);
closure ^= pool.ObjectAt(closure_index);
+ const intptr_t flags = helper_->reader_.ReadUInt();
+ const bool has_exceptions_table = (flags & kHasExceptionsTableFlag) != 0;
+ const bool has_source_positions = (flags & kHasSourcePositionsFlag) != 0;
+
// Read closure bytecode and attach to closure function.
closure_bytecode = ReadBytecode(pool);
closure.AttachBytecode(closure_bytecode);
// Read closure exceptions table.
- ReadExceptionsTable(closure_bytecode);
+ ReadExceptionsTable(closure_bytecode, has_exceptions_table);
+
+ ReadSourcePositions(closure_bytecode, has_source_positions);
if (FLAG_dump_kernel_bytecode) {
KernelBytecodeDisassembler::Disassemble(closure);
@@ -716,6 +725,16 @@
}
}
+void BytecodeMetadataHelper::ReadSourcePositions(const Code& bytecode,
+ bool has_source_positions) {
+ if (!has_source_positions) {
+ return;
+ }
+
+ // TODO(alexmarkov): store offset of source positions into Bytecode object.
+ helper_->SkipBytes(helper_->reader_.ReadUInt());
+}
+
RawTypedData* BytecodeMetadataHelper::NativeEntry(const Function& function,
const String& external_name) {
Zone* zone = helper_->zone_;
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.h b/runtime/vm/compiler/frontend/bytecode_reader.h
index bbde906..0e9d730 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.h
+++ b/runtime/vm/compiler/frontend/bytecode_reader.h
@@ -33,8 +33,8 @@
const ObjectPool& pool,
intptr_t from_index);
RawCode* ReadBytecode(const ObjectPool& pool);
- void ReadExceptionsTable(const Code& bytecode,
- bool has_exceptions_table = true);
+ void ReadExceptionsTable(const Code& bytecode, bool has_exceptions_table);
+ void ReadSourcePositions(const Code& bytecode, bool has_source_positions);
RawTypedData* NativeEntry(const Function& function,
const String& external_name);