[vm/kernel/bytecode] Support try-catch-finally in bytecode generator
Change-Id: I2882546741b1dd8f6e03a21d85b5487fb754b742
Reviewed-on: https://dart-review.googlesource.com/55324
Reviewed-by: RĂ©gis Crelier <regis@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/vm/lib/bytecode/assembler.dart b/pkg/vm/lib/bytecode/assembler.dart
index ac3b157..fdf239e 100644
--- a/pkg/vm/lib/bytecode/assembler.dart
+++ b/pkg/vm/lib/bytecode/assembler.dart
@@ -7,6 +7,7 @@
import 'dart:typed_data';
import 'package:vm/bytecode/dbc.dart';
+import 'package:vm/bytecode/exceptions.dart' show ExceptionsTable;
class Label {
List<int> _jumps = <int>[];
@@ -42,6 +43,7 @@
final List<int> bytecode = new List<int>();
final Uint32List _encodeBufferIn;
final Uint8List _encodeBufferOut;
+ final ExceptionsTable exceptionsTable = new ExceptionsTable();
BytecodeAssembler._(this._encodeBufferIn, this._encodeBufferOut);
@@ -51,6 +53,7 @@
}
int get offset => bytecode.length;
+ int get offsetInWords => bytecode.length >> kLog2BytesPerBytecode;
void bind(Label label) {
final List<int> jumps = label.bind(offset);
@@ -819,8 +822,8 @@
emitWord(_encode0(Opcode.kCloneContext));
}
- void emitMoveSpecial(int ra, int rd) {
- emitWord(_encodeAD(Opcode.kMoveSpecial, ra, rd));
+ void emitMoveSpecial(int ra, SpecialIndex rd) {
+ emitWord(_encodeAD(Opcode.kMoveSpecial, ra, rd.index));
}
void emitInstantiateType(int rd) {
diff --git a/pkg/vm/lib/bytecode/dbc.dart b/pkg/vm/lib/bytecode/dbc.dart
index 9e3bb31..6f48e18 100644
--- a/pkg/vm/lib/bytecode/dbc.dart
+++ b/pkg/vm/lib/bytecode/dbc.dart
@@ -235,6 +235,7 @@
reg, // register (unsigned FP relative local)
xeg, // x-register (signed FP relative local)
tgt, // jump target relative to the PC of the current instruction
+ spe, // SpecialIndex
}
class Format {
@@ -589,7 +590,7 @@
Opcode.kCloneContext: const Format(
Encoding.k0, const [Operand.none, Operand.none, Operand.none]),
Opcode.kMoveSpecial: const Format(
- Encoding.kAD, const [Operand.reg, Operand.imm, Operand.none]),
+ Encoding.kAD, const [Operand.reg, Operand.spe, Operand.none]),
Opcode.kInstantiateType: const Format(
Encoding.kD, const [Operand.lit, Operand.none, Operand.none]),
Opcode.kInstantiateTypeArgumentsTOS: const Format(
@@ -650,3 +651,8 @@
// Prefix used to distinguish setters in ICData target names.
// Should match constant in runtime/vm/object.cc.
const String kSetterPrefix = 'set:';
+
+enum SpecialIndex {
+ exception,
+ stackTrace,
+}
diff --git a/pkg/vm/lib/bytecode/disassembler.dart b/pkg/vm/lib/bytecode/disassembler.dart
index 9230268..279d492 100644
--- a/pkg/vm/lib/bytecode/disassembler.dart
+++ b/pkg/vm/lib/bytecode/disassembler.dart
@@ -7,6 +7,7 @@
import 'dart:typed_data';
import 'package:vm/bytecode/dbc.dart';
+import 'package:vm/bytecode/exceptions.dart';
class _Instruction {
final Opcode opcode;
@@ -20,11 +21,13 @@
List<_Instruction> _instructions;
int _labelCount;
- Map<int, int> _labels;
+ Map<int, String> _labels;
+ Map<int, List<String>> _markers;
- String disassemble(List<int> bytecode) {
+ String disassemble(List<int> bytecode, ExceptionsTable exceptionsTable) {
_init(bytecode);
_scanForJumpTargets();
+ _markTryBlocks(exceptionsTable);
return _disasm();
}
@@ -39,7 +42,8 @@
}
_labelCount = 0;
- _labels = <int, int>{};
+ _labels = <int, String>{};
+ _markers = <int, List<String>>{};
}
_Instruction _decodeInstruction(int word) {
@@ -94,17 +98,35 @@
if (instr.opcode == Opcode.kJump) {
final target = i + instr.operands[0];
assert(0 <= target && target < _instructions.length);
- _labels[target] ??= (++_labelCount);
+ if (!_labels.containsKey(target)) {
+ final label = 'L${++_labelCount}';
+ _labels[target] = label;
+ _addMarker(target, '$label:');
+ }
}
}
}
+ void _markTryBlocks(ExceptionsTable exceptionsTable) {
+ for (var tryBlock in exceptionsTable.blocks) {
+ final int tryIndex = tryBlock.tryIndex;
+ _addMarker(tryBlock.startPC, 'Try #$tryIndex start:');
+ _addMarker(tryBlock.endPC, 'Try #$tryIndex end:');
+ _addMarker(tryBlock.handlerPC, 'Try #$tryIndex handler:');
+ }
+ }
+
+ void _addMarker(int pc, String marker) {
+ final markers = (_markers[pc] ??= <String>[]);
+ markers.add(marker);
+ }
+
String _disasm() {
StringBuffer out = new StringBuffer();
for (int i = 0; i < _instructions.length; i++) {
- int label = _labels[i];
- if (label != null) {
- out.writeln('L$label:');
+ List<String> markers = _markers[i];
+ if (markers != null) {
+ markers.forEach(out.writeln);
}
_writeInstruction(out, i, _instructions[i]);
}
@@ -158,7 +180,11 @@
case Operand.xeg:
return (value < 0) ? 'FP[$value]' : 'r$value';
case Operand.tgt:
- return 'L${_labels[bci + value] ?? (throw 'Label not found')}';
+ return _labels[bci + value] ?? (throw 'Label not found');
+ case Operand.spe:
+ return SpecialIndex.values[value]
+ .toString()
+ .substring('SpecialIndex.'.length);
}
throw 'Unexpected operand format $fmt';
}
diff --git a/pkg/vm/lib/bytecode/exceptions.dart b/pkg/vm/lib/bytecode/exceptions.dart
new file mode 100644
index 0000000..7c955a3
--- /dev/null
+++ b/pkg/vm/lib/bytecode/exceptions.dart
@@ -0,0 +1,139 @@
+// 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.exceptions;
+
+import 'package:kernel/ast.dart' show BinarySink, BinarySource;
+
+/*
+
+In kernel binary, try blocks are encoded in the following way
+(using notation from pkg/kernel/binary.md):
+
+// Offset of a bytecode instruction, in DBC words.
+type BytecodeOffset = UInt;
+
+type TryBlock {
+ UInt outerTryIndexPlus1;
+ BytecodeOffset startPC; // Inclusive.
+ BytecodeOffset endPC; // Exclusive.
+ BytecodeOffset handlerPC;
+ Byte flags (needsStackTrace, isSynthetic);
+ List<ConstantIndex> types;
+}
+
+type ExceptionsTable {
+ // Ordered by startPC, then by nesting (outer precedes inner).
+ // Try blocks are properly nested. It means there are no partially
+ // overlapping try blocks - each pair of try block regions either
+ // has no intersection or one try block region encloses another.
+ List<TryBlock> tryBlocks;
+}
+
+*/
+
+class TryBlock {
+ static const int flagNeedsStackTrace = 1 << 0;
+ static const int flagIsSynthetic = 1 << 1;
+
+ final int tryIndex;
+ final int outerTryIndex;
+ final int startPC;
+ int endPC;
+ int handlerPC;
+ int flags = 0;
+ List<int> types = <int>[];
+
+ TryBlock._(this.tryIndex, this.outerTryIndex, this.startPC);
+
+ bool get needsStackTrace => (flags & flagNeedsStackTrace) != 0;
+
+ void set needsStackTrace(bool value) {
+ flags = (flags & ~flagNeedsStackTrace) | (value ? flagNeedsStackTrace : 0);
+ }
+
+ bool get isSynthetic => (flags & flagIsSynthetic) != 0;
+
+ void set isSynthetic(bool value) {
+ flags = (flags & ~flagIsSynthetic) | (value ? flagIsSynthetic : 0);
+ }
+
+ void writeToBinary(BinarySink sink) {
+ sink.writeUInt30(outerTryIndex + 1);
+ sink.writeUInt30(startPC);
+ sink.writeUInt30(endPC);
+ sink.writeUInt30(handlerPC);
+ sink.writeByte(flags);
+ sink.writeUInt30(types.length);
+ types.forEach(sink.writeUInt30);
+ }
+
+ factory TryBlock.readFromBinary(BinarySource source, int tryIndex) {
+ final outerTryIndex = source.readUInt() - 1;
+ final startPC = source.readUInt();
+ final tryBlock = new TryBlock._(tryIndex, outerTryIndex, startPC);
+
+ tryBlock.endPC = source.readUInt();
+ tryBlock.handlerPC = source.readUInt();
+ tryBlock.flags = source.readByte();
+ tryBlock.types =
+ new List<int>.generate(source.readUInt(), (_) => source.readUInt());
+
+ return tryBlock;
+ }
+
+ @override
+ String toString() => 'try-index $tryIndex, outer $outerTryIndex, '
+ 'start $startPC, end $endPC, handler $handlerPC, '
+ '${needsStackTrace ? 'needs-stack-trace, ' : ''}'
+ '${isSynthetic ? 'synthetic, ' : ''}'
+ 'types ${types.map((t) => 'CP#$t').toList()}';
+}
+
+class ExceptionsTable {
+ List<TryBlock> blocks = <TryBlock>[];
+
+ ExceptionsTable();
+
+ TryBlock enterTryBlock(int startPC) {
+ assert(blocks.isEmpty || blocks.last.startPC <= startPC);
+ final tryBlock =
+ new TryBlock._(blocks.length, _outerTryBlockIndex(startPC), startPC);
+ blocks.add(tryBlock);
+ return tryBlock;
+ }
+
+ int _outerTryBlockIndex(int startPC) {
+ for (int i = blocks.length - 1; i >= 0; --i) {
+ final tryBlock = blocks[i];
+ if (tryBlock.endPC == null || tryBlock.endPC > startPC) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ void writeToBinary(BinarySink sink) {
+ sink.writeUInt30(blocks.length);
+ blocks.forEach((b) => b.writeToBinary(sink));
+ }
+
+ ExceptionsTable.readFromBinary(BinarySource source)
+ : blocks = new List<TryBlock>.generate(source.readUInt(),
+ (int index) => new TryBlock.readFromBinary(source, index));
+
+ @override
+ String toString() {
+ if (blocks.isEmpty) {
+ return '';
+ }
+ StringBuffer sb = new StringBuffer();
+ sb.writeln('ExceptionsTable {');
+ for (var tryBlock in blocks) {
+ sb.writeln(' $tryBlock');
+ }
+ sb.writeln('}');
+ return sb.toString();
+ }
+}
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index 29ea146..8d189b7 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -17,6 +17,7 @@
import 'package:vm/bytecode/assembler.dart';
import 'package:vm/bytecode/constant_pool.dart';
import 'package:vm/bytecode/dbc.dart';
+import 'package:vm/bytecode/exceptions.dart';
import 'package:vm/bytecode/local_vars.dart' show LocalVariables;
import 'package:vm/metadata/bytecode.dart';
@@ -60,6 +61,9 @@
ConstantEvaluator constantEvaluator;
Map<LabeledStatement, Label> labeledStatements;
Map<SwitchCase, Label> switchCases;
+ Map<TryCatch, TryBlock> tryCatches;
+ Map<TryFinally, List<FinallyBlock>> finallyBlocks;
+ Map<TreeNode, int> contextLevels;
List<ClosureBytecode> closures;
ConstantPool cp;
ConstantEmitter constantEmitter;
@@ -401,6 +405,22 @@
asm.emitPush(tempIndexInFrame);
}
+ /// Generates is-test for the value at TOS.
+ void _genInstanceOf(DartType type) {
+ // TODO(alexmarkov): generate _simpleInstanceOf if possible
+
+ if (hasTypeParameters([type])) {
+ _genPushInstantiatorAndFunctionTypeArguments([type]);
+ } else {
+ _genPushNull(); // Instantiator type arguments.
+ _genPushNull(); // Function type arguments.
+ }
+ asm.emitPushConstant(cp.add(new ConstantType(type)));
+ final argDescIndex = cp.add(new ConstantArgDesc(4));
+ final icdataIndex = cp.add(new ConstantICData('_instanceOf', argDescIndex));
+ asm.emitInstanceCall1(4, icdataIndex);
+ }
+
void start(Member node) {
enclosingClass = node.enclosingClass;
enclosingMember = node;
@@ -411,6 +431,9 @@
..env = new EvaluationEnvironment();
labeledStatements = <LabeledStatement, Label>{};
switchCases = <SwitchCase, Label>{};
+ tryCatches = <TryCatch, TryBlock>{};
+ finallyBlocks = <TryFinally, List<FinallyBlock>>{};
+ contextLevels = <TreeNode, int>{};
closures = <ClosureBytecode>[];
cp = new ConstantPool();
constantEmitter = new ConstantEmitter(cp);
@@ -446,7 +469,8 @@
}
void end(Member node) {
- metadata.mapping[node] = new BytecodeMetadata(asm.bytecode, cp, closures);
+ metadata.mapping[node] =
+ new BytecodeMetadata(asm.bytecode, asm.exceptionsTable, cp, closures);
if (isTraceEnabled) {
print('Generated bytecode for $node');
@@ -458,6 +482,9 @@
constantEvaluator = null;
labeledStatements = null;
switchCases = null;
+ tryCatches = null;
+ finallyBlocks = null;
+ contextLevels = null;
closures = null;
cp = null;
constantEmitter = null;
@@ -578,7 +605,8 @@
locals.leaveScope();
- closures.add(new ClosureBytecode(closureFunctionIndex, asm.bytecode));
+ closures.add(new ClosureBytecode(
+ closureFunctionIndex, asm.bytecode, asm.exceptionsTable));
_popAssemblerState();
return closureFunctionIndex;
@@ -643,12 +671,74 @@
void _leaveScope() {
if (locals.currentContextSize > 0) {
+ _genUnwindContext(locals.currentContextLevel - 1);
+ }
+ locals.leaveScope();
+ }
+
+ void _genUnwindContext(int targetContextLevel) {
+ int currentContextLevel = locals.currentContextLevel;
+ assert(currentContextLevel >= targetContextLevel);
+ while (currentContextLevel > targetContextLevel) {
asm.emitPush(locals.contextVarIndexInFrame);
asm.emitLoadFieldTOS(cp.add(new ConstantContextOffset.parent()));
asm.emitPopLocal(locals.contextVarIndexInFrame);
+ --currentContextLevel;
+ }
+ }
+
+ /// Returns the list of try-finally blocks between [from] and [to],
+ /// ordered from inner to outer. If [to] is null, returns all enclosing
+ /// try-finally blocks up to the function boundary.
+ List<TryFinally> _getEnclosingTryFinallyBlocks(TreeNode from, TreeNode to) {
+ List<TryFinally> blocks = <TryFinally>[];
+ TreeNode node = from;
+ for (;;) {
+ if (node == to) {
+ return blocks;
+ }
+ if (node == null || node is FunctionNode || node is Member) {
+ if (to == null) {
+ return blocks;
+ } else {
+ throw 'Unable to find node $to up from $from';
+ }
+ }
+ // Inspect parent as we only need try-finally blocks enclosing [node]
+ // in the body, and not in the finally-block.
+ final parent = node.parent;
+ if (parent is TryFinally && parent.body == node) {
+ blocks.add(parent);
+ }
+ node = parent;
+ }
+ }
+
+ /// Generates non-local transfer from inner node [from] into the outer
+ /// node, executing finally blocks on the way out. [to] can be null,
+ /// in such case all enclosing finally blocks are executed.
+ /// [continuation] is invoked to generate control transfer code following
+ /// the last finally block.
+ void _generateNonLocalControlTransfer(
+ TreeNode from, TreeNode to, GenerateContinuation continuation) {
+ List<TryFinally> tryFinallyBlocks = _getEnclosingTryFinallyBlocks(from, to);
+
+ // Add finally blocks to all try-finally from outer to inner.
+ // The outermost finally block should generate continuation, each inner
+ // finally block should proceed to a corresponding outer block.
+ for (var tryFinally in tryFinallyBlocks.reversed) {
+ final finallyBlock = new FinallyBlock(continuation);
+ finallyBlocks[tryFinally].add(finallyBlock);
+
+ final Label nextFinally = finallyBlock.entry;
+ continuation = () {
+ asm.emitJump(nextFinally);
+ };
}
- locals.leaveScope();
+ // Generate jump to the innermost finally (or to the original
+ // continuation if there are no try-finally blocks).
+ continuation();
}
@override
@@ -785,20 +875,7 @@
@override
visitIsExpression(IsExpression node) {
node.operand.accept(this);
-
- // TODO(alexmarkov): generate _simpleInstanceOf if possible
-
- if (hasTypeParameters([node.type])) {
- _genPushInstantiatorAndFunctionTypeArguments([node.type]);
- } else {
- _genPushNull(); // Instantiator type arguments.
- _genPushNull(); // Function type arguments.
- }
- final typeIndex = cp.add(new ConstantType(node.type));
- asm.emitPushConstant(typeIndex);
- final argDescIndex = cp.add(new ConstantArgDesc(4));
- final icdataIndex = cp.add(new ConstantICData('_instanceOf', argDescIndex));
- asm.emitInstanceCall1(4, icdataIndex);
+ _genInstanceOf(node.type);
}
@override
@@ -1001,9 +1078,21 @@
asm.emitPushConstant(cpIndex);
}
-// @override
-// visitRethrow(Rethrow node) {
-// }
+ @override
+ visitRethrow(Rethrow node) {
+ TryCatch tryCatch;
+ for (var parent = node.parent;; parent = parent.parent) {
+ if (parent is Catch) {
+ tryCatch = parent.parent as TryCatch;
+ break;
+ }
+ if (parent == null || parent is FunctionNode) {
+ throw 'Unable to find enclosing catch for $node';
+ }
+ }
+ tryCatches[tryCatch].needsStackTrace = true;
+ _genRethrow(tryCatch);
+ }
bool _hasTrivialInitializer(Field field) =>
(field.initializer == null) ||
@@ -1205,18 +1294,26 @@
@override
visitBreakStatement(BreakStatement node) {
- // TODO(alexmarkov): execute all finally blocks on the way out.
- final label = labeledStatements[node.target] ??
+ final targetLabel = labeledStatements[node.target] ??
(throw 'Target label ${node.target} was not registered for break $node');
- asm.emitJump(label);
+ final targetContextLevel = contextLevels[node.target];
+
+ _generateNonLocalControlTransfer(node, node.target, () {
+ _genUnwindContext(targetContextLevel);
+ asm.emitJump(targetLabel);
+ });
}
@override
visitContinueSwitchStatement(ContinueSwitchStatement node) {
- // TODO(alexmarkov): execute all finally blocks on the way out.
- final label = switchCases[node.target] ??
+ final targetLabel = switchCases[node.target] ??
(throw 'Target label ${node.target} was not registered for continue-switch $node');
- asm.emitJump(label);
+ final targetContextLevel = contextLevels[node.target.parent];
+
+ _generateNonLocalControlTransfer(node, node.target.parent, () {
+ _genUnwindContext(targetContextLevel);
+ asm.emitJump(targetLabel);
+ });
}
@override
@@ -1358,9 +1455,11 @@
visitLabeledStatement(LabeledStatement node) {
final label = new Label();
labeledStatements[node] = label;
+ contextLevels[node] = locals.currentContextLevel;
node.body.accept(this);
asm.bind(label);
- labeledStatements[node] = null;
+ labeledStatements.remove(node);
+ contextLevels.remove(node);
}
@override
@@ -1370,11 +1469,18 @@
} else {
_genPushNull();
}
- asm.emitReturnTOS();
+
+ // TODO(alexmarkov): Do we need to save return value
+ // to a variable?
+ _generateNonLocalControlTransfer(node, null, () {
+ asm.emitReturnTOS();
+ });
}
@override
visitSwitchStatement(SwitchStatement node) {
+ contextLevels[node] = locals.currentContextLevel;
+
node.expression.accept(this);
final int temp = locals.tempIndexInFrame(node);
@@ -1419,15 +1525,169 @@
asm.bind(done);
node.cases.forEach(switchCases.remove);
+ contextLevels.remove(node);
}
-// @override
-// visitTryCatch(TryCatch node) {
-// }
-//
-// @override
-// visitTryFinally(TryFinally node) {
-// }
+ bool _isTryBlock(TreeNode node) => node is TryCatch || node is TryFinally;
+
+ int _savedContextVar(TreeNode node) {
+ assert(_isTryBlock(node));
+ return locals.tempIndexInFrame(node, tempIndex: 0);
+ }
+
+ // Exception var occupies the same slot as saved context, so context
+ // should be restored first, before loading exception.
+ int _exceptionVar(TreeNode node) {
+ assert(_isTryBlock(node));
+ return locals.tempIndexInFrame(node, tempIndex: 0);
+ }
+
+ int _stackTraceVar(TreeNode node) {
+ assert(_isTryBlock(node));
+ return locals.tempIndexInFrame(node, tempIndex: 1);
+ }
+
+ _saveContextForTryBlock(TreeNode node) {
+ if (locals.hasContextVar) {
+ asm.emitPush(locals.contextVarIndexInFrame);
+ asm.emitPopLocal(_savedContextVar(node));
+ }
+ }
+
+ _restoreContextForTryBlock(TreeNode node) {
+ if (locals.hasContextVar) {
+ asm.emitPush(_savedContextVar(node));
+ asm.emitPopLocal(locals.contextVarIndexInFrame);
+ }
+ }
+
+ /// Start try block
+ TryBlock _startTryBlock(TreeNode node) {
+ assert(_isTryBlock(node));
+
+ _saveContextForTryBlock(node);
+
+ return asm.exceptionsTable.enterTryBlock(asm.offsetInWords);
+ }
+
+ /// End try block and start its handler.
+ void _endTryBlock(TreeNode node, TryBlock tryBlock) {
+ tryBlock.endPC = asm.offsetInWords;
+ tryBlock.handlerPC = asm.offsetInWords;
+
+ // TODO(alexmarkov): Consider emitting SetFrame to cut expression stack.
+ // In such case, we need to save return value to a variable in visitReturn.
+
+ _restoreContextForTryBlock(node);
+
+ asm.emitMoveSpecial(_exceptionVar(node), SpecialIndex.exception);
+ asm.emitMoveSpecial(_stackTraceVar(node), SpecialIndex.stackTrace);
+ }
+
+ void _genRethrow(TreeNode node) {
+ asm.emitPush(_exceptionVar(node));
+ asm.emitPush(_stackTraceVar(node));
+ asm.emitThrow(1);
+ }
+
+ @override
+ visitTryCatch(TryCatch node) {
+ final Label done = new Label();
+
+ final TryBlock tryBlock = _startTryBlock(node);
+ tryBlock.isSynthetic = node.isSynthetic;
+ tryCatches[node] = tryBlock; // Used by rethrow.
+
+ node.body.accept(this);
+ asm.emitJump(done);
+
+ _endTryBlock(node, tryBlock);
+
+ final int exception = _exceptionVar(node);
+ final int stackTrace = _stackTraceVar(node);
+
+ bool hasCatchAll = false;
+
+ for (Catch catchClause in node.catches) {
+ tryBlock.types.add(cp.add(new ConstantType(catchClause.guard)));
+
+ Label skipCatch;
+ if (catchClause.guard == const DynamicType()) {
+ hasCatchAll = true;
+ } else {
+ asm.emitPush(exception);
+ _genInstanceOf(catchClause.guard);
+
+ skipCatch = new Label();
+ _genJumpIfFalse(/* negated = */ false, skipCatch);
+ }
+
+ _enterScope(catchClause);
+
+ if (catchClause.exception != null) {
+ _genPushContextIfCaptured(catchClause.exception);
+ asm.emitPush(exception);
+ _genStoreVar(catchClause.exception);
+ }
+
+ if (catchClause.stackTrace != null) {
+ tryBlock.needsStackTrace = true;
+ _genPushContextIfCaptured(catchClause.stackTrace);
+ asm.emitPush(stackTrace);
+ _genStoreVar(catchClause.stackTrace);
+ }
+
+ catchClause.body.accept(this);
+
+ _leaveScope();
+ asm.emitJump(done);
+
+ if (skipCatch != null) {
+ asm.bind(skipCatch);
+ }
+ }
+
+ if (!hasCatchAll) {
+ tryBlock.needsStackTrace = true;
+ _genRethrow(node);
+ }
+
+ asm.bind(done);
+ tryCatches.remove(node);
+ }
+
+ @override
+ visitTryFinally(TryFinally node) {
+ final TryBlock tryBlock = _startTryBlock(node);
+ finallyBlocks[node] = <FinallyBlock>[];
+
+ node.body.accept(this);
+
+ // TODO(alexmarkov): Do not generate normal continuation if control
+ // does not return from body.
+ final normalContinuation =
+ new FinallyBlock(() {/* do nothing (fall through) */});
+ finallyBlocks[node].add(normalContinuation);
+ asm.emitJump(normalContinuation.entry);
+
+ _endTryBlock(node, tryBlock);
+
+ tryBlock.types.add(cp.add(new ConstantType(const DynamicType())));
+
+ node.finalizer.accept(this);
+
+ tryBlock.needsStackTrace = true; // For rethrowing.
+ _genRethrow(node);
+
+ for (var finallyBlock in finallyBlocks[node]) {
+ asm.bind(finallyBlock.entry);
+ _restoreContextForTryBlock(node);
+ node.finalizer.accept(this);
+ finallyBlock.generateContinuation();
+ }
+
+ finallyBlocks.remove(node);
+ }
@override
visitVariableDeclaration(VariableDeclaration node) {
@@ -1665,3 +1925,12 @@
bool _hasBytecode(Member node) => metadata.mapping.containsKey(node);
}
+
+typedef void GenerateContinuation();
+
+class FinallyBlock {
+ final Label entry = new Label();
+ final GenerateContinuation generateContinuation;
+
+ FinallyBlock(this.generateContinuation);
+}
diff --git a/pkg/vm/lib/bytecode/local_vars.dart b/pkg/vm/lib/bytecode/local_vars.dart
index bb717f2..1bae25a 100644
--- a/pkg/vm/lib/bytecode/local_vars.dart
+++ b/pkg/vm/lib/bytecode/local_vars.dart
@@ -13,7 +13,7 @@
final Map<TreeNode, Scope> _scopes = <TreeNode, Scope>{};
final Map<VariableDeclaration, VarDesc> _vars =
<VariableDeclaration, VarDesc>{};
- final Map<TreeNode, int> _temps = <TreeNode, int>{};
+ final Map<TreeNode, List<int>> _temps = <TreeNode, List<int>>{};
Scope _currentScope;
Frame _currentFrame;
@@ -43,9 +43,13 @@
_getVarDesc(variable).originalParamSlotIndex ??
(throw 'Variablie $variable does not have originalParamSlotIndex');
- int tempIndexInFrame(TreeNode node) =>
- _temps[node] ??
- (throw 'Temp is not allocated for node ${node.runtimeType} $node');
+ int tempIndexInFrame(TreeNode node, {int tempIndex: 0}) {
+ final temps = _temps[node];
+ if (temps == null) {
+ throw 'Temp is not allocated for node ${node.runtimeType} $node';
+ }
+ return temps[tempIndex];
+ }
int get currentContextSize => _currentScope.contextSize;
int get currentContextLevel => _currentScope.contextLevel;
@@ -64,6 +68,8 @@
.contextVar ??
(throw 'Context variable is not declared in ${_currentFrame.function}'));
+ bool get hasContextVar => _currentFrame.contextVar != null;
+
int get scratchVarIndexInFrame => getVarIndexInFrame(_currentFrame
.scratchVar ??
(throw 'Scratch variable is not declared in ${_currentFrame.function}'));
@@ -452,23 +458,31 @@
max(_currentFrame.frameSize, _currentScope.localsUsed);
}
- void _allocateTemp(TreeNode node) {
+ void _allocateTemp(TreeNode node, {int count: 1}) {
assert(locals._temps[node] == null);
- if (_currentScope.tempsUsed >= _currentFrame.temporaries.length) {
- // Allocate a new local slot for temporary variable.
- int local = _currentScope.localsUsed++;
+ if (_currentScope.tempsUsed + count > _currentFrame.temporaries.length) {
+ // Allocate new local slots for temporary variables.
+ final int newSlots =
+ (_currentScope.tempsUsed + count) - _currentFrame.temporaries.length;
+ int local = _currentScope.localsUsed;
+ _currentScope.localsUsed += newSlots;
_updateFrameSize();
- _currentFrame.temporaries.add(local);
+ for (int i = 0; i < newSlots; i++) {
+ _currentFrame.temporaries.add(local + i);
+ }
}
- int index = _currentFrame.temporaries[_currentScope.tempsUsed++];
- locals._temps[node] = index;
+ locals._temps[node] = _currentFrame.temporaries
+ .sublist(_currentScope.tempsUsed, _currentScope.tempsUsed + count);
+ _currentScope.tempsUsed += count;
}
- void _freeTemp(TreeNode node) {
- assert(_currentScope.tempsUsed > 0);
- assert(locals._temps[node] ==
- _currentFrame.temporaries[_currentScope.tempsUsed - 1]);
- --_currentScope.tempsUsed;
+ void _freeTemp(TreeNode node, {int count: 1}) {
+ assert(_currentScope.tempsUsed >= count);
+ _currentScope.tempsUsed -= count;
+ assert(listEquals(
+ locals._temps[node],
+ _currentFrame.temporaries.sublist(
+ _currentScope.tempsUsed, _currentScope.tempsUsed + count)));
}
void _allocateVariable(VariableDeclaration variable, {int paramSlotIndex}) {
@@ -598,18 +612,18 @@
_leaveScope();
}
- void _visit(TreeNode node, {scope: false, temp: false}) {
+ void _visit(TreeNode node, {bool scope: false, int temps: 0}) {
if (scope) {
_enterScope(node);
}
- if (temp) {
- _allocateTemp(node);
+ if (temps > 0) {
+ _allocateTemp(node, count: temps);
}
node.visitChildren(this);
- if (temp) {
- _freeTemp(node);
+ if (temps > 0) {
+ _freeTemp(node, count: temps);
}
if (scope) {
_leaveScope();
@@ -688,7 +702,7 @@
if (node.isConst) {
return;
}
- _visit(node, temp: true);
+ _visit(node, temps: 1);
}
@override
@@ -696,7 +710,7 @@
if (node.isConst) {
return;
}
- _visit(node, temp: true);
+ _visit(node, temps: 1);
}
@override
@@ -704,37 +718,37 @@
if (node.isConst) {
return;
}
- _visit(node, temp: true);
+ _visit(node, temps: 1);
}
@override
visitStringConcatenation(StringConcatenation node) {
- _visit(node, temp: true);
+ _visit(node, temps: 1);
}
@override
visitConditionalExpression(ConditionalExpression node) {
- _visit(node, temp: true);
+ _visit(node, temps: 1);
}
@override
visitLogicalExpression(LogicalExpression node) {
- _visit(node, temp: true);
+ _visit(node, temps: 1);
}
@override
visitPropertySet(PropertySet node) {
- _visit(node, temp: true);
+ _visit(node, temps: 1);
}
@override
visitSwitchStatement(SwitchStatement node) {
- _visit(node, temp: true);
+ _visit(node, temps: 1);
}
@override
visitVariableSet(VariableSet node) {
- _visit(node, temp: locals.isCaptured(node.variable));
+ _visit(node, temps: locals.isCaptured(node.variable) ? 1 : 0);
}
@override
@@ -742,4 +756,14 @@
_allocateTemp(node);
super.visitStaticSet(node);
}
+
+ @override
+ visitTryCatch(TryCatch node) {
+ _visit(node, temps: 2);
+ }
+
+ @override
+ visitTryFinally(TryFinally node) {
+ _visit(node, temps: 2);
+ }
}
diff --git a/pkg/vm/lib/metadata/bytecode.dart b/pkg/vm/lib/metadata/bytecode.dart
index 11fd237..3d5261f 100644
--- a/pkg/vm/lib/metadata/bytecode.dart
+++ b/pkg/vm/lib/metadata/bytecode.dart
@@ -7,6 +7,7 @@
import 'package:kernel/ast.dart';
import 'package:vm/bytecode/constant_pool.dart' show ConstantPool;
import 'package:vm/bytecode/disassembler.dart' show BytecodeDisassembler;
+import 'package:vm/bytecode/exceptions.dart' show ExceptionsTable;
/// Metadata containing bytecode.
///
@@ -14,6 +15,7 @@
///
/// type BytecodeMetadata {
/// List<Byte> bytecodes
+/// ExceptionsTable exceptionsTable
/// ConstantPool constantPool
/// List<ClosureBytecode> closures
/// }
@@ -21,22 +23,29 @@
/// type ClosureBytecode {
/// ConstantIndex closureFunction
/// List<Byte> bytecodes
+/// ExceptionsTable exceptionsTable
/// }
///
+/// Encoding of ExceptionsTable is described in
+/// pkg/vm/lib/bytecode/exceptions.dart.
+///
/// Encoding of ConstantPool is described in
/// pkg/vm/lib/bytecode/constant_pool.dart.
///
class BytecodeMetadata {
final List<int> bytecodes;
+ final ExceptionsTable exceptionsTable;
final ConstantPool constantPool;
final List<ClosureBytecode> closures;
- BytecodeMetadata(this.bytecodes, this.constantPool, this.closures);
+ BytecodeMetadata(
+ this.bytecodes, this.exceptionsTable, this.constantPool, this.closures);
@override
String toString() => "\n"
"Bytecode {\n"
- "${new BytecodeDisassembler().disassemble(bytecodes)}}\n"
+ "${new BytecodeDisassembler().disassemble(bytecodes, exceptionsTable)}}\n"
+ "$exceptionsTable"
"$constantPool"
"${closures.join('\n')}";
}
@@ -46,25 +55,31 @@
class ClosureBytecode {
final int closureFunctionConstantIndex;
final List<int> bytecodes;
+ final ExceptionsTable exceptionsTable;
- ClosureBytecode(this.closureFunctionConstantIndex, this.bytecodes);
+ ClosureBytecode(
+ this.closureFunctionConstantIndex, this.bytecodes, this.exceptionsTable);
void writeToBinary(BinarySink sink) {
sink.writeUInt30(closureFunctionConstantIndex);
sink.writeByteList(bytecodes);
+ exceptionsTable.writeToBinary(sink);
}
factory ClosureBytecode.readFromBinary(BinarySource source) {
final closureFunctionConstantIndex = source.readUInt();
final List<int> bytecodes = source.readByteList();
- return new ClosureBytecode(closureFunctionConstantIndex, bytecodes);
+ final exceptionsTable = new ExceptionsTable.readFromBinary(source);
+ return new ClosureBytecode(
+ closureFunctionConstantIndex, bytecodes, exceptionsTable);
}
@override
String toString() {
StringBuffer sb = new StringBuffer();
sb.writeln('Closure CP#$closureFunctionConstantIndex {');
- sb.writeln(new BytecodeDisassembler().disassemble(bytecodes));
+ sb.writeln(
+ new BytecodeDisassembler().disassemble(bytecodes, exceptionsTable));
sb.writeln('}');
return sb.toString();
}
@@ -82,6 +97,7 @@
@override
void writeToBinary(BytecodeMetadata metadata, Node node, BinarySink sink) {
sink.writeByteList(metadata.bytecodes);
+ metadata.exceptionsTable.writeToBinary(sink);
metadata.constantPool.writeToBinary(node, sink);
sink.writeUInt30(metadata.closures.length);
metadata.closures.forEach((c) => c.writeToBinary(sink));
@@ -90,10 +106,12 @@
@override
BytecodeMetadata readFromBinary(Node node, BinarySource source) {
final List<int> bytecodes = source.readByteList();
+ final exceptionsTable = new ExceptionsTable.readFromBinary(source);
final ConstantPool constantPool =
new ConstantPool.readFromBinary(node, source);
final List<ClosureBytecode> closures = new List<ClosureBytecode>.generate(
source.readUInt(), (_) => new ClosureBytecode.readFromBinary(source));
- return new BytecodeMetadata(bytecodes, constantPool, closures);
+ return new BytecodeMetadata(
+ bytecodes, exceptionsTable, constantPool, closures);
}
}
diff --git a/pkg/vm/testcases/bytecode/try_blocks.dart b/pkg/vm/testcases/bytecode/try_blocks.dart
new file mode 100644
index 0000000..d07c18f
--- /dev/null
+++ b/pkg/vm/testcases/bytecode/try_blocks.dart
@@ -0,0 +1,153 @@
+// 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.
+
+testTryCatch1() {
+ try {
+ print('danger!');
+ } catch (e) {
+ print('caught $e');
+ }
+}
+
+testTryCatch2() {
+ try {
+ print('danger!');
+ } on TypeError {
+ print('caught type error');
+ } on AssertionError catch (e) {
+ print('caught assertion error $e');
+ } on Error catch (e, st) {
+ print('caught error $e $st');
+ } catch (e, st) {
+ print('caught something $e $st');
+ }
+}
+
+testTryCatch3() {
+ int x = 1;
+ try {
+ int y = 2;
+ void foo() {
+ try {
+ print('danger foo');
+ } catch (e) {
+ print(x);
+ y = 3;
+ }
+ }
+
+ foo();
+ print(y);
+ } catch (e, st) {
+ print('caught $e $st');
+
+ void bar() {
+ try {
+ print('danger bar');
+ } on Error catch (e) {
+ print('error $e, captured stack trace: $st');
+ }
+ }
+
+ return bar;
+ }
+}
+
+testRethrow(bool cond) {
+ try {
+ try {
+ print('try 1 > try 2');
+ } catch (e) {
+ try {
+ print('try 1 > catch 2 > try 3');
+ if (cond) {
+ rethrow;
+ }
+ } catch (e) {
+ print('try 1 > catch 2 > catch 3');
+ }
+ }
+ } catch (e, st) {
+ print('catch 1');
+ print(st);
+ }
+}
+
+testTryFinally1() {
+ for (int i = 0; i < 10; i++) {
+ try {
+ if (i > 5) {
+ break;
+ }
+ } finally {
+ print(i);
+ }
+ }
+}
+
+testTryFinally2(int x) {
+ switch (x) {
+ case 1:
+ try {
+ print('before try 1');
+ int y = 3;
+ try {
+ print('try');
+ void foo() {
+ print(x);
+ print(y);
+ }
+
+ foo();
+ continue L;
+ } finally {
+ print('finally 1');
+ }
+ print('after try 1');
+ } finally {
+ print('finally 2');
+ }
+ break;
+ L:
+ case 2:
+ print('case 2');
+ break;
+ }
+}
+
+testTryFinally3() {
+ int x = 11;
+ var y;
+ try {
+ y = () {
+ print(x);
+ try {
+ print('try 1');
+ return 42;
+ } finally {
+ try {
+ print('try 2');
+ return 43;
+ } finally {
+ print(x);
+ }
+ }
+ };
+ } finally {
+ print(x);
+ y();
+ }
+}
+
+testTryCatchFinally() {
+ try {
+ print('try');
+ } catch (e) {
+ print('catch');
+ } finally {
+ print('finally');
+ }
+}
+
+main() {}
diff --git a/pkg/vm/testcases/bytecode/try_blocks.dart.expect b/pkg/vm/testcases/bytecode/try_blocks.dart.expect
new file mode 100644
index 0000000..0dbaad8
--- /dev/null
+++ b/pkg/vm/testcases/bytecode/try_blocks.dart.expect
@@ -0,0 +1,1379 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+
+[@vm.bytecode=
+Bytecode {
+ Entry 4
+ CheckStack
+Try #0 start:
+ PushConstant CP#0
+ PushConstant CP#2
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Jump L1
+Try #0 end:
+Try #0 handler:
+ MoveSpecial r0, exception
+ MoveSpecial r1, stackTrace
+ Push r0
+ PopLocal r2
+ PushConstant CP#4
+ PushConstant CP#5
+ CreateArrayTOS
+ StoreLocal r3
+ Push r3
+ PushConstant CP#6
+ PushConstant CP#7
+ StoreIndexedTOS
+ Push r3
+ PushConstant CP#8
+ Push r2
+ StoreIndexedTOS
+ PushConstant CP#9
+ IndirectStaticCall 1, CP#1
+ PushConstant CP#10
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Jump L1
+L1:
+ PushConstant CP#4
+ ReturnTOS
+}
+ExceptionsTable {
+ try-index 0, outer -1, start 2, end 7, handler 7, types [CP#3]
+}
+ConstantPool {
+ [0] = String 'danger!'
+ [1] = ArgDesc num-args 1, num-type-args 0, names []
+ [2] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [3] = Type dynamic
+ [4] = Null
+ [5] = Int 2
+ [6] = Int 0
+ [7] = String 'caught '
+ [8] = Int 1
+ [9] = StaticICData target 'dart.core::_StringBase::_interpolate', arg-desc CP#1
+ [10] = StaticICData target 'dart.core::print', arg-desc CP#1
+}
+]static method testTryCatch1() → dynamic {
+ try {
+ core::print("danger!");
+ }
+ on dynamic catch(final dynamic e) {
+ core::print("caught ${e}");
+ }
+}
+[@vm.bytecode=
+Bytecode {
+ Entry 5
+ CheckStack
+Try #0 start:
+ PushConstant CP#0
+ PushConstant CP#2
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Jump L1
+Try #0 end:
+Try #0 handler:
+ MoveSpecial r0, exception
+ MoveSpecial r1, stackTrace
+ Push r0
+ PushConstant CP#4
+ PushConstant CP#4
+ PushConstant CP#3
+ InstanceCall1 4, CP#6
+ PushConstant CP#7
+ IfNeStrictTOS
+ Jump L2
+ PushConstant CP#8
+ PushConstant CP#9
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Jump L1
+L2:
+ Push r0
+ PushConstant CP#4
+ PushConstant CP#4
+ PushConstant CP#10
+ InstanceCall1 4, CP#11
+ PushConstant CP#7
+ IfNeStrictTOS
+ Jump L3
+ Push r0
+ PopLocal r2
+ PushConstant CP#4
+ PushConstant CP#12
+ CreateArrayTOS
+ StoreLocal r3
+ Push r3
+ PushConstant CP#13
+ PushConstant CP#14
+ StoreIndexedTOS
+ Push r3
+ PushConstant CP#15
+ Push r2
+ StoreIndexedTOS
+ PushConstant CP#16
+ IndirectStaticCall 1, CP#1
+ PushConstant CP#17
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Jump L1
+L3:
+ Push r0
+ PushConstant CP#4
+ PushConstant CP#4
+ PushConstant CP#18
+ InstanceCall1 4, CP#19
+ PushConstant CP#7
+ IfNeStrictTOS
+ Jump L4
+ Push r0
+ PopLocal r2
+ Push r1
+ PopLocal r3
+ PushConstant CP#4
+ PushConstant CP#20
+ CreateArrayTOS
+ StoreLocal r4
+ Push r4
+ PushConstant CP#13
+ PushConstant CP#21
+ StoreIndexedTOS
+ Push r4
+ PushConstant CP#15
+ Push r2
+ StoreIndexedTOS
+ Push r4
+ PushConstant CP#12
+ PushConstant CP#22
+ StoreIndexedTOS
+ Push r4
+ PushConstant CP#23
+ Push r3
+ StoreIndexedTOS
+ PushConstant CP#24
+ IndirectStaticCall 1, CP#1
+ PushConstant CP#25
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Jump L1
+L4:
+ Push r0
+ PopLocal r2
+ Push r1
+ PopLocal r3
+ PushConstant CP#4
+ PushConstant CP#20
+ CreateArrayTOS
+ StoreLocal r4
+ Push r4
+ PushConstant CP#13
+ PushConstant CP#27
+ StoreIndexedTOS
+ Push r4
+ PushConstant CP#15
+ Push r2
+ StoreIndexedTOS
+ Push r4
+ PushConstant CP#12
+ PushConstant CP#22
+ StoreIndexedTOS
+ Push r4
+ PushConstant CP#23
+ Push r3
+ StoreIndexedTOS
+ PushConstant CP#28
+ IndirectStaticCall 1, CP#1
+ PushConstant CP#29
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Jump L1
+L1:
+ PushConstant CP#4
+ ReturnTOS
+}
+ExceptionsTable {
+ try-index 0, outer -1, start 2, end 7, handler 7, needs-stack-trace, types [CP#3, CP#10, CP#18, CP#26]
+}
+ConstantPool {
+ [0] = String 'danger!'
+ [1] = ArgDesc num-args 1, num-type-args 0, names []
+ [2] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [3] = Type dart.core::TypeError
+ [4] = Null
+ [5] = ArgDesc num-args 4, num-type-args 0, names []
+ [6] = ICData target-name '_instanceOf', arg-desc CP#5
+ [7] = Bool true
+ [8] = String 'caught type error'
+ [9] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [10] = Type dart.core::AssertionError
+ [11] = ICData target-name '_instanceOf', arg-desc CP#5
+ [12] = Int 2
+ [13] = Int 0
+ [14] = String 'caught assertion error '
+ [15] = Int 1
+ [16] = StaticICData target 'dart.core::_StringBase::_interpolate', arg-desc CP#1
+ [17] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [18] = Type dart.core::Error
+ [19] = ICData target-name '_instanceOf', arg-desc CP#5
+ [20] = Int 4
+ [21] = String 'caught error '
+ [22] = String ' '
+ [23] = Int 3
+ [24] = StaticICData target 'dart.core::_StringBase::_interpolate', arg-desc CP#1
+ [25] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [26] = Type dynamic
+ [27] = String 'caught something '
+ [28] = StaticICData target 'dart.core::_StringBase::_interpolate', arg-desc CP#1
+ [29] = StaticICData target 'dart.core::print', arg-desc CP#1
+}
+]static method testTryCatch2() → dynamic {
+ try {
+ core::print("danger!");
+ }
+ on core::TypeError catch(no-exception-var) {
+ core::print("caught type error");
+ }
+ on core::AssertionError catch(final core::AssertionError e) {
+ core::print("caught assertion error ${e}");
+ }
+ on core::Error catch(final core::Error e, final core::StackTrace st) {
+ core::print("caught error ${e} ${st}");
+ }
+ on dynamic catch(final dynamic e, final core::StackTrace st) {
+ core::print("caught something ${e} ${st}");
+ }
+}
+[@vm.bytecode=
+Bytecode {
+ Entry 7
+ CheckStack
+ AllocateContext 1
+ StoreLocal r1
+ Push r1
+ Push r0
+ StoreFieldTOS CP#0
+ PopLocal r0
+ Push r0
+ PushConstant CP#1
+ StoreFieldTOS CP#2
+ Push r0
+ PopLocal r2
+Try #0 start:
+ AllocateContext 1
+ StoreLocal r1
+ Push r1
+ Push r0
+ StoreFieldTOS CP#0
+ PopLocal r0
+ Push r0
+ PushConstant CP#3
+ StoreFieldTOS CP#2
+ Allocate CP#14
+ StoreLocal r5
+ Push r5
+ PushConstant CP#12
+ StoreFieldTOS CP#15
+ Push r5
+ PushConstant CP#12
+ StoreFieldTOS CP#16
+ Push r5
+ PushConstant CP#4
+ StoreFieldTOS CP#17
+ Push r5
+ Push r0
+ StoreFieldTOS CP#5
+ PopLocal r4
+ Push r4
+ InstanceCall1 1, CP#18
+ Drop1
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#19
+ IndirectStaticCall 1, CP#7
+ Drop1
+ Push r0
+ LoadFieldTOS CP#0
+ PopLocal r0
+ Jump L1
+Try #0 end:
+Try #0 handler:
+ Push r2
+ PopLocal r0
+ MoveSpecial r2, exception
+ MoveSpecial r3, stackTrace
+ AllocateContext 1
+ StoreLocal r1
+ Push r1
+ Push r0
+ StoreFieldTOS CP#0
+ PopLocal r0
+ Push r2
+ PopLocal r4
+ Push r0
+ Push r3
+ StoreFieldTOS CP#2
+ PushConstant CP#12
+ PushConstant CP#20
+ CreateArrayTOS
+ StoreLocal r5
+ Push r5
+ PushConstant CP#21
+ PushConstant CP#22
+ StoreIndexedTOS
+ Push r5
+ PushConstant CP#1
+ Push r4
+ StoreIndexedTOS
+ Push r5
+ PushConstant CP#3
+ PushConstant CP#23
+ StoreIndexedTOS
+ Push r5
+ PushConstant CP#11
+ Push r0
+ LoadFieldTOS CP#2
+ StoreIndexedTOS
+ PushConstant CP#24
+ IndirectStaticCall 1, CP#7
+ PushConstant CP#25
+ IndirectStaticCall 1, CP#7
+ Drop1
+ Allocate CP#14
+ StoreLocal r5
+ Push r5
+ PushConstant CP#12
+ StoreFieldTOS CP#15
+ Push r5
+ PushConstant CP#12
+ StoreFieldTOS CP#16
+ Push r5
+ PushConstant CP#26
+ StoreFieldTOS CP#17
+ Push r5
+ Push r0
+ StoreFieldTOS CP#5
+ PopLocal r6
+ Push r6
+ ReturnTOS
+ Push r0
+ LoadFieldTOS CP#0
+ PopLocal r0
+ Jump L1
+L1:
+ Push r0
+ LoadFieldTOS CP#0
+ PopLocal r0
+ PushConstant CP#12
+ ReturnTOS
+}
+ExceptionsTable {
+ try-index 0, outer -1, start 13, end 49, handler 49, needs-stack-trace, types [CP#9]
+}
+ConstantPool {
+ [0] = ContextOffset parent
+ [1] = Int 1
+ [2] = ContextOffset var [0]
+ [3] = Int 2
+ [4] = ClosureFunction foo () → void;
+ [5] = FieldOffset dart.core::_Closure::_context
+ [6] = String 'danger foo'
+ [7] = ArgDesc num-args 1, num-type-args 0, names []
+ [8] = StaticICData target 'dart.core::print', arg-desc CP#7
+ [9] = Type dynamic
+ [10] = StaticICData target 'dart.core::print', arg-desc CP#7
+ [11] = Int 3
+ [12] = Null
+ [13] = EndClosureFunctionScope
+ [14] = Class dart.core::_Closure
+ [15] = FieldOffset dart.core::_Closure::_instantiator_type_arguments
+ [16] = FieldOffset dart.core::_Closure::_function_type_arguments
+ [17] = FieldOffset dart.core::_Closure::_function
+ [18] = ICData target-name 'call', arg-desc CP#7
+ [19] = StaticICData target 'dart.core::print', arg-desc CP#7
+ [20] = Int 4
+ [21] = Int 0
+ [22] = String 'caught '
+ [23] = String ' '
+ [24] = StaticICData target 'dart.core::_StringBase::_interpolate', arg-desc CP#7
+ [25] = StaticICData target 'dart.core::print', arg-desc CP#7
+ [26] = ClosureFunction bar () → void;
+ [27] = String 'danger bar'
+ [28] = StaticICData target 'dart.core::print', arg-desc CP#7
+ [29] = Type dart.core::Error
+ [30] = ArgDesc num-args 4, num-type-args 0, names []
+ [31] = ICData target-name '_instanceOf', arg-desc CP#30
+ [32] = Bool true
+ [33] = String 'error '
+ [34] = String ', captured stack trace: '
+ [35] = StaticICData target 'dart.core::_StringBase::_interpolate', arg-desc CP#7
+ [36] = StaticICData target 'dart.core::print', arg-desc CP#7
+ [37] = EndClosureFunctionScope
+}
+Closure CP#4 {
+ Entry 6
+ CheckStack
+ Push FP[-5]
+ LoadFieldTOS CP#5
+ PopLocal r0
+ Push r0
+ PopLocal r2
+Try #0 start:
+ PushConstant CP#6
+ PushConstant CP#8
+ IndirectStaticCall 1, CP#7
+ Drop1
+ Jump L1
+Try #0 end:
+Try #0 handler:
+ Push r2
+ PopLocal r0
+ MoveSpecial r2, exception
+ MoveSpecial r3, stackTrace
+ Push r2
+ PopLocal r4
+ Push r0
+ LoadFieldTOS CP#0
+ LoadFieldTOS CP#2
+ PushConstant CP#10
+ IndirectStaticCall 1, CP#7
+ Drop1
+ Push r0
+ PushConstant CP#11
+ StoreLocal r5
+ StoreFieldTOS CP#2
+ Push r5
+ Drop1
+ Jump L1
+L1:
+ PushConstant CP#12
+ ReturnTOS
+
+}
+
+Closure CP#26 {
+ Entry 6
+ CheckStack
+ Push FP[-5]
+ LoadFieldTOS CP#5
+ PopLocal r0
+ Push r0
+ PopLocal r2
+Try #0 start:
+ PushConstant CP#27
+ PushConstant CP#28
+ IndirectStaticCall 1, CP#7
+ Drop1
+ Jump L1
+Try #0 end:
+Try #0 handler:
+ Push r2
+ PopLocal r0
+ MoveSpecial r2, exception
+ MoveSpecial r3, stackTrace
+ Push r2
+ PushConstant CP#12
+ PushConstant CP#12
+ PushConstant CP#29
+ InstanceCall1 4, CP#31
+ PushConstant CP#32
+ IfNeStrictTOS
+ Jump L2
+ Push r2
+ PopLocal r4
+ PushConstant CP#12
+ PushConstant CP#20
+ CreateArrayTOS
+ StoreLocal r5
+ Push r5
+ PushConstant CP#21
+ PushConstant CP#33
+ StoreIndexedTOS
+ Push r5
+ PushConstant CP#1
+ Push r4
+ StoreIndexedTOS
+ Push r5
+ PushConstant CP#3
+ PushConstant CP#34
+ StoreIndexedTOS
+ Push r5
+ PushConstant CP#11
+ Push r0
+ LoadFieldTOS CP#2
+ StoreIndexedTOS
+ PushConstant CP#35
+ IndirectStaticCall 1, CP#7
+ PushConstant CP#36
+ IndirectStaticCall 1, CP#7
+ Drop1
+ Jump L1
+L2:
+ Push r2
+ Push r3
+ Throw 1
+L1:
+ PushConstant CP#12
+ ReturnTOS
+
+}
+]static method testTryCatch3() → dynamic {
+ core::int x = 1;
+ try {
+ core::int y = 2;
+ function foo() → void {
+ try {
+ core::print("danger foo");
+ }
+ on dynamic catch(final dynamic e) {
+ core::print(x);
+ y = 3;
+ }
+ }
+ foo.call();
+ core::print(y);
+ }
+ on dynamic catch(final dynamic e, final core::StackTrace st) {
+ core::print("caught ${e} ${st}");
+ function bar() → void {
+ try {
+ core::print("danger bar");
+ }
+ on core::Error catch(final core::Error e) {
+ core::print("error ${e}, captured stack trace: ${st}");
+ }
+ }
+ return bar;
+ }
+}
+[@vm.bytecode=
+Bytecode {
+ Entry 8
+ CheckStack
+Try #0 start:
+Try #1 start:
+ PushConstant CP#0
+ PushConstant CP#2
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Jump L1
+Try #1 end:
+Try #1 handler:
+ MoveSpecial r2, exception
+ MoveSpecial r3, stackTrace
+ Push r2
+ PopLocal r4
+Try #2 start:
+ PushConstant CP#4
+ PushConstant CP#5
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Push FP[-5]
+ PushConstant CP#6
+ IfNeStrictTOS
+ Jump L2
+ Push r2
+ Push r3
+ Throw 1
+ Drop1
+L2:
+ Jump L3
+Try #2 end:
+Try #2 handler:
+ MoveSpecial r5, exception
+ MoveSpecial r6, stackTrace
+ Push r5
+ PopLocal r7
+ PushConstant CP#7
+ PushConstant CP#8
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Jump L3
+L3:
+ Jump L1
+L1:
+ Jump L4
+Try #0 end:
+Try #0 handler:
+ MoveSpecial r0, exception
+ MoveSpecial r1, stackTrace
+ Push r0
+ PopLocal r2
+ Push r1
+ PopLocal r3
+ PushConstant CP#9
+ PushConstant CP#10
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Push r3
+ PushConstant CP#11
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Jump L4
+L4:
+ PushConstant CP#12
+ ReturnTOS
+}
+ExceptionsTable {
+ try-index 0, outer -1, start 2, end 35, handler 35, needs-stack-trace, types [CP#3]
+ try-index 1, outer 0, start 2, end 7, handler 7, needs-stack-trace, types [CP#3]
+ try-index 2, outer 0, start 11, end 24, handler 24, types [CP#3]
+}
+ConstantPool {
+ [0] = String 'try 1 > try 2'
+ [1] = ArgDesc num-args 1, num-type-args 0, names []
+ [2] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [3] = Type dynamic
+ [4] = String 'try 1 > catch 2 > try 3'
+ [5] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [6] = Bool true
+ [7] = String 'try 1 > catch 2 > catch 3'
+ [8] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [9] = String 'catch 1'
+ [10] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [11] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [12] = Null
+}
+]static method testRethrow(core::bool cond) → dynamic {
+ try {
+ try {
+ core::print("try 1 > try 2");
+ }
+ on dynamic catch(final dynamic e) {
+ try {
+ core::print("try 1 > catch 2 > try 3");
+ if(cond) {
+ rethrow;
+ }
+ }
+ on dynamic catch(final dynamic e) {
+ core::print("try 1 > catch 2 > catch 3");
+ }
+ }
+ }
+ on dynamic catch(final dynamic e, final core::StackTrace st) {
+ core::print("catch 1");
+ core::print(st);
+ }
+}
+[@vm.bytecode=
+Bytecode {
+ Entry 3
+ CheckStack
+ PushConstant CP#0
+ PopLocal r0
+L5:
+ CheckStack
+ Push r0
+ PushConstant CP#1
+ InstanceCall1 2, CP#3
+ PushConstant CP#4
+ IfNeStrictTOS
+ Jump L1
+Try #0 start:
+ Push r0
+ PushConstant CP#5
+ InstanceCall1 2, CP#6
+ PushConstant CP#4
+ IfNeStrictTOS
+ Jump L2
+ Jump L3
+L2:
+ Jump L4
+Try #0 end:
+Try #0 handler:
+ MoveSpecial r1, exception
+ MoveSpecial r2, stackTrace
+ Push r0
+ PushConstant CP#9
+ IndirectStaticCall 1, CP#8
+ Drop1
+ Push r1
+ Push r2
+ Throw 1
+L3:
+ Push r0
+ PushConstant CP#10
+ IndirectStaticCall 1, CP#8
+ Drop1
+ Jump L1
+L4:
+ Push r0
+ PushConstant CP#11
+ IndirectStaticCall 1, CP#8
+ Drop1
+ Push r0
+ PushConstant CP#12
+ InstanceCall1 2, CP#13
+ StoreLocal r0
+ Drop1
+ Jump L5
+L1:
+ PushConstant CP#14
+ ReturnTOS
+}
+ExceptionsTable {
+ try-index 0, outer -1, start 11, end 19, handler 19, needs-stack-trace, types [CP#7]
+}
+ConstantPool {
+ [0] = Int 0
+ [1] = Int 10
+ [2] = ArgDesc num-args 2, num-type-args 0, names []
+ [3] = ICData target-name '<', arg-desc CP#2
+ [4] = Bool true
+ [5] = Int 5
+ [6] = ICData target-name '>', arg-desc CP#2
+ [7] = Type dynamic
+ [8] = ArgDesc num-args 1, num-type-args 0, names []
+ [9] = StaticICData target 'dart.core::print', arg-desc CP#8
+ [10] = StaticICData target 'dart.core::print', arg-desc CP#8
+ [11] = StaticICData target 'dart.core::print', arg-desc CP#8
+ [12] = Int 1
+ [13] = ICData target-name '+', arg-desc CP#2
+ [14] = Null
+}
+]static method testTryFinally1() → dynamic {
+ #L1:
+ for (core::int i = 0; i.{core::num::<}(10); i = i.{core::num::+}(1)) {
+ try {
+ if(i.{core::num::>}(5)) {
+ break #L1;
+ }
+ }
+ finally {
+ core::print(i);
+ }
+ }
+}
+[@vm.bytecode=
+Bytecode {
+ Entry 9
+ CheckStack
+ AllocateContext 1
+ StoreLocal r1
+ Push r1
+ Push r0
+ StoreFieldTOS CP#0
+ PopLocal r0
+ Push r0
+ Push FP[-5]
+ StoreFieldTOS CP#1
+ Push r0
+ LoadFieldTOS CP#1
+ PopLocal r2
+ Push r2
+ PushConstant CP#3
+ InstanceCall2 2, CP#4
+ PushConstant CP#5
+ IfEqStrictTOS
+ Jump L1
+ Push r2
+ PushConstant CP#6
+ InstanceCall2 2, CP#7
+ PushConstant CP#5
+ IfEqStrictTOS
+ Jump L2
+ Jump L3
+L1:
+ Push r0
+ PopLocal r3
+Try #0 start:
+ AllocateContext 1
+ StoreLocal r1
+ Push r1
+ Push r0
+ StoreFieldTOS CP#0
+ PopLocal r0
+ PushConstant CP#8
+ PushConstant CP#10
+ IndirectStaticCall 1, CP#9
+ Drop1
+ Push r0
+ PushConstant CP#11
+ StoreFieldTOS CP#1
+ Push r0
+ PopLocal r5
+Try #1 start:
+ PushConstant CP#12
+ PushConstant CP#13
+ IndirectStaticCall 1, CP#9
+ Drop1
+ Allocate CP#20
+ StoreLocal r8
+ Push r8
+ PushConstant CP#18
+ StoreFieldTOS CP#21
+ Push r8
+ PushConstant CP#18
+ StoreFieldTOS CP#22
+ Push r8
+ PushConstant CP#14
+ StoreFieldTOS CP#23
+ Push r8
+ Push r0
+ StoreFieldTOS CP#15
+ PopLocal r7
+ Push r7
+ InstanceCall1 1, CP#24
+ Drop1
+ Jump L4
+ Jump L5
+Try #1 end:
+Try #1 handler:
+ Push r5
+ PopLocal r0
+ MoveSpecial r5, exception
+ MoveSpecial r6, stackTrace
+ PushConstant CP#26
+ PushConstant CP#27
+ IndirectStaticCall 1, CP#9
+ Drop1
+ Push r5
+ Push r6
+ Throw 1
+L4:
+ Push r5
+ PopLocal r0
+ PushConstant CP#26
+ PushConstant CP#28
+ IndirectStaticCall 1, CP#9
+ Drop1
+ Jump L6
+L5:
+ Push r5
+ PopLocal r0
+ PushConstant CP#26
+ PushConstant CP#29
+ IndirectStaticCall 1, CP#9
+ Drop1
+ PushConstant CP#30
+ PushConstant CP#31
+ IndirectStaticCall 1, CP#9
+ Drop1
+ Push r0
+ LoadFieldTOS CP#0
+ PopLocal r0
+ Jump L7
+Try #0 end:
+Try #0 handler:
+ Push r3
+ PopLocal r0
+ MoveSpecial r3, exception
+ MoveSpecial r4, stackTrace
+ PushConstant CP#32
+ PushConstant CP#33
+ IndirectStaticCall 1, CP#9
+ Drop1
+ Push r3
+ Push r4
+ Throw 1
+L6:
+ Push r3
+ PopLocal r0
+ PushConstant CP#32
+ PushConstant CP#34
+ IndirectStaticCall 1, CP#9
+ Drop1
+ Jump L2
+L7:
+ Push r3
+ PopLocal r0
+ PushConstant CP#32
+ PushConstant CP#35
+ IndirectStaticCall 1, CP#9
+ Drop1
+ Jump L3
+L2:
+ PushConstant CP#36
+ PushConstant CP#37
+ IndirectStaticCall 1, CP#9
+ Drop1
+ Jump L3
+L3:
+ PushConstant CP#18
+ ReturnTOS
+}
+ExceptionsTable {
+ try-index 0, outer -1, start 29, end 100, handler 100, needs-stack-trace, types [CP#25]
+ try-index 1, outer 0, start 44, end 68, handler 68, needs-stack-trace, types [CP#25]
+}
+ConstantPool {
+ [0] = ContextOffset parent
+ [1] = ContextOffset var [0]
+ [2] = ArgDesc num-args 2, num-type-args 0, names []
+ [3] = Int 1
+ [4] = ICData target-name '==', arg-desc CP#2
+ [5] = Bool true
+ [6] = Int 2
+ [7] = ICData target-name '==', arg-desc CP#2
+ [8] = String 'before try 1'
+ [9] = ArgDesc num-args 1, num-type-args 0, names []
+ [10] = StaticICData target 'dart.core::print', arg-desc CP#9
+ [11] = Int 3
+ [12] = String 'try'
+ [13] = StaticICData target 'dart.core::print', arg-desc CP#9
+ [14] = ClosureFunction foo () → void;
+ [15] = FieldOffset dart.core::_Closure::_context
+ [16] = StaticICData target 'dart.core::print', arg-desc CP#9
+ [17] = StaticICData target 'dart.core::print', arg-desc CP#9
+ [18] = Null
+ [19] = EndClosureFunctionScope
+ [20] = Class dart.core::_Closure
+ [21] = FieldOffset dart.core::_Closure::_instantiator_type_arguments
+ [22] = FieldOffset dart.core::_Closure::_function_type_arguments
+ [23] = FieldOffset dart.core::_Closure::_function
+ [24] = ICData target-name 'call', arg-desc CP#9
+ [25] = Type dynamic
+ [26] = String 'finally 1'
+ [27] = StaticICData target 'dart.core::print', arg-desc CP#9
+ [28] = StaticICData target 'dart.core::print', arg-desc CP#9
+ [29] = StaticICData target 'dart.core::print', arg-desc CP#9
+ [30] = String 'after try 1'
+ [31] = StaticICData target 'dart.core::print', arg-desc CP#9
+ [32] = String 'finally 2'
+ [33] = StaticICData target 'dart.core::print', arg-desc CP#9
+ [34] = StaticICData target 'dart.core::print', arg-desc CP#9
+ [35] = StaticICData target 'dart.core::print', arg-desc CP#9
+ [36] = String 'case 2'
+ [37] = StaticICData target 'dart.core::print', arg-desc CP#9
+}
+Closure CP#14 {
+ Entry 2
+ CheckStack
+ Push FP[-5]
+ LoadFieldTOS CP#15
+ PopLocal r0
+ Push r0
+ LoadFieldTOS CP#0
+ LoadFieldTOS CP#1
+ PushConstant CP#16
+ IndirectStaticCall 1, CP#9
+ Drop1
+ Push r0
+ LoadFieldTOS CP#1
+ PushConstant CP#17
+ IndirectStaticCall 1, CP#9
+ Drop1
+ PushConstant CP#18
+ ReturnTOS
+
+}
+]static method testTryFinally2(core::int x) → dynamic {
+ #L2:
+ switch(x) {
+ #L3:
+ case 1:
+ {
+ try {
+ core::print("before try 1");
+ core::int y = 3;
+ try {
+ core::print("try");
+ function foo() → void {
+ core::print(x);
+ core::print(y);
+ }
+ foo.call();
+ continue #L4;
+ }
+ finally {
+ core::print("finally 1");
+ }
+ core::print("after try 1");
+ }
+ finally {
+ core::print("finally 2");
+ }
+ break #L2;
+ }
+ #L4:
+ case 2:
+ {
+ core::print("case 2");
+ break #L2;
+ }
+ }
+}
+[@vm.bytecode=
+Bytecode {
+ Entry 6
+ CheckStack
+ AllocateContext 1
+ StoreLocal r1
+ Push r1
+ Push r0
+ StoreFieldTOS CP#0
+ PopLocal r0
+ Push r0
+ PushConstant CP#1
+ StoreFieldTOS CP#2
+ PushConstant CP#3
+ PopLocal r2
+ Push r0
+ PopLocal r3
+Try #0 start:
+ Allocate CP#27
+ StoreLocal r5
+ Push r5
+ PushConstant CP#3
+ StoreFieldTOS CP#28
+ Push r5
+ PushConstant CP#3
+ StoreFieldTOS CP#29
+ Push r5
+ PushConstant CP#4
+ StoreFieldTOS CP#30
+ Push r5
+ Push r0
+ StoreFieldTOS CP#5
+ StoreLocal r2
+ Drop1
+ Jump L1
+Try #0 end:
+Try #0 handler:
+ Push r3
+ PopLocal r0
+ MoveSpecial r3, exception
+ MoveSpecial r4, stackTrace
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#31
+ IndirectStaticCall 1, CP#6
+ Drop1
+ Push r2
+ InstanceCall1 1, CP#32
+ Drop1
+ Push r3
+ Push r4
+ Throw 1
+L1:
+ Push r3
+ PopLocal r0
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#33
+ IndirectStaticCall 1, CP#6
+ Drop1
+ Push r2
+ InstanceCall1 1, CP#34
+ Drop1
+ Push r0
+ LoadFieldTOS CP#0
+ PopLocal r0
+ PushConstant CP#3
+ ReturnTOS
+}
+ExceptionsTable {
+ try-index 0, outer -1, start 15, end 32, handler 32, needs-stack-trace, types [CP#11]
+}
+ConstantPool {
+ [0] = ContextOffset parent
+ [1] = Int 11
+ [2] = ContextOffset var [0]
+ [3] = Null
+ [4] = ClosureFunction <anonymous closure> () → dart.core::int;
+ [5] = FieldOffset dart.core::_Closure::_context
+ [6] = ArgDesc num-args 1, num-type-args 0, names []
+ [7] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [8] = String 'try 1'
+ [9] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [10] = Int 42
+ [11] = Type dynamic
+ [12] = String 'try 2'
+ [13] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [14] = Int 43
+ [15] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [16] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [17] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [18] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [19] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [20] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [21] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [22] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [23] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [24] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [25] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [26] = EndClosureFunctionScope
+ [27] = Class dart.core::_Closure
+ [28] = FieldOffset dart.core::_Closure::_instantiator_type_arguments
+ [29] = FieldOffset dart.core::_Closure::_function_type_arguments
+ [30] = FieldOffset dart.core::_Closure::_function
+ [31] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [32] = ICData target-name 'call', arg-desc CP#6
+ [33] = StaticICData target 'dart.core::print', arg-desc CP#6
+ [34] = ICData target-name 'call', arg-desc CP#6
+}
+Closure CP#4 {
+ Entry 6
+ CheckStack
+ Push FP[-5]
+ LoadFieldTOS CP#5
+ PopLocal r0
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#7
+ IndirectStaticCall 1, CP#6
+ Drop1
+ Push r0
+ PopLocal r2
+Try #0 start:
+ PushConstant CP#8
+ PushConstant CP#9
+ IndirectStaticCall 1, CP#6
+ Drop1
+ PushConstant CP#10
+ Jump L1
+ Jump L2
+Try #0 end:
+Try #0 handler:
+ Push r2
+ PopLocal r0
+ MoveSpecial r2, exception
+ MoveSpecial r3, stackTrace
+ Push r0
+ PopLocal r4
+Try #1 start:
+ PushConstant CP#12
+ PushConstant CP#13
+ IndirectStaticCall 1, CP#6
+ Drop1
+ PushConstant CP#14
+ Jump L3
+ Jump L4
+Try #1 end:
+Try #1 handler:
+ Push r4
+ PopLocal r0
+ MoveSpecial r4, exception
+ MoveSpecial r5, stackTrace
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#15
+ IndirectStaticCall 1, CP#6
+ Drop1
+ Push r4
+ Push r5
+ Throw 1
+L3:
+ Push r4
+ PopLocal r0
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#16
+ IndirectStaticCall 1, CP#6
+ Drop1
+ ReturnTOS
+L4:
+ Push r4
+ PopLocal r0
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#17
+ IndirectStaticCall 1, CP#6
+ Drop1
+ Push r2
+ Push r3
+ Throw 1
+L1:
+ Push r2
+ PopLocal r0
+ Push r0
+ PopLocal r4
+Try #2 start:
+ PushConstant CP#12
+ PushConstant CP#18
+ IndirectStaticCall 1, CP#6
+ Drop1
+ PushConstant CP#14
+ Jump L5
+ Jump L6
+Try #2 end:
+Try #2 handler:
+ Push r4
+ PopLocal r0
+ MoveSpecial r4, exception
+ MoveSpecial r5, stackTrace
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#19
+ IndirectStaticCall 1, CP#6
+ Drop1
+ Push r4
+ Push r5
+ Throw 1
+L5:
+ Push r4
+ PopLocal r0
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#20
+ IndirectStaticCall 1, CP#6
+ Drop1
+ ReturnTOS
+L6:
+ Push r4
+ PopLocal r0
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#21
+ IndirectStaticCall 1, CP#6
+ Drop1
+ ReturnTOS
+L2:
+ Push r2
+ PopLocal r0
+ Push r0
+ PopLocal r4
+Try #3 start:
+ PushConstant CP#12
+ PushConstant CP#22
+ IndirectStaticCall 1, CP#6
+ Drop1
+ PushConstant CP#14
+ Jump L7
+ Jump L8
+Try #3 end:
+Try #3 handler:
+ Push r4
+ PopLocal r0
+ MoveSpecial r4, exception
+ MoveSpecial r5, stackTrace
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#23
+ IndirectStaticCall 1, CP#6
+ Drop1
+ Push r4
+ Push r5
+ Throw 1
+L7:
+ Push r4
+ PopLocal r0
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#24
+ IndirectStaticCall 1, CP#6
+ Drop1
+ ReturnTOS
+L8:
+ Push r4
+ PopLocal r0
+ Push r0
+ LoadFieldTOS CP#2
+ PushConstant CP#25
+ IndirectStaticCall 1, CP#6
+ Drop1
+ PushConstant CP#3
+ ReturnTOS
+
+}
+]static method testTryFinally3() → dynamic {
+ core::int x = 11;
+ dynamic y;
+ try {
+ y = () → core::int {
+ core::print(x);
+ try {
+ core::print("try 1");
+ return 42;
+ }
+ finally {
+ try {
+ core::print("try 2");
+ return 43;
+ }
+ finally {
+ core::print(x);
+ }
+ }
+ };
+ }
+ finally {
+ core::print(x);
+ y.call();
+ }
+}
+[@vm.bytecode=
+Bytecode {
+ Entry 5
+ CheckStack
+Try #0 start:
+Try #1 start:
+ PushConstant CP#0
+ PushConstant CP#2
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Jump L1
+Try #1 end:
+Try #1 handler:
+ MoveSpecial r2, exception
+ MoveSpecial r3, stackTrace
+ Push r2
+ PopLocal r4
+ PushConstant CP#4
+ PushConstant CP#5
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Jump L1
+L1:
+ Jump L2
+Try #0 end:
+Try #0 handler:
+ MoveSpecial r0, exception
+ MoveSpecial r1, stackTrace
+ PushConstant CP#6
+ PushConstant CP#7
+ IndirectStaticCall 1, CP#1
+ Drop1
+ Push r0
+ Push r1
+ Throw 1
+L2:
+ PushConstant CP#6
+ PushConstant CP#8
+ IndirectStaticCall 1, CP#1
+ Drop1
+ PushConstant CP#9
+ ReturnTOS
+}
+ExceptionsTable {
+ try-index 0, outer -1, start 2, end 17, handler 17, needs-stack-trace, types [CP#3]
+ try-index 1, outer 0, start 2, end 7, handler 7, types [CP#3]
+}
+ConstantPool {
+ [0] = String 'try'
+ [1] = ArgDesc num-args 1, num-type-args 0, names []
+ [2] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [3] = Type dynamic
+ [4] = String 'catch'
+ [5] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [6] = String 'finally'
+ [7] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [8] = StaticICData target 'dart.core::print', arg-desc CP#1
+ [9] = Null
+}
+]static method testTryCatchFinally() → dynamic {
+ try
+ try {
+ core::print("try");
+ }
+ on dynamic catch(final dynamic e) {
+ core::print("catch");
+ }
+ finally {
+ core::print("finally");
+ }
+}
+[@vm.bytecode=
+Bytecode {
+ Entry 0
+ CheckStack
+ PushConstant CP#0
+ ReturnTOS
+}
+ConstantPool {
+ [0] = Null
+}
+]static method main() → dynamic {}