blob: 7c955a32c970d1b0301b795a375a032e584f5f84 [file] [log] [blame]
// 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();
}
}