blob: 3ae6e1ccc26db13e9079663bc0b189ddbd41f766 [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 'bytecode_serialization.dart' show BufferedWriter, BufferedReader;
/*
In kernel binary, try blocks are encoded in the following way
(using notation from pkg/kernel/binary.md):
// Offset of a bytecode instruction.
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 write(BufferedWriter writer) {
writer.writePackedUInt30(outerTryIndex + 1);
writer.writePackedUInt30(startPC);
writer.writePackedUInt30(endPC);
writer.writePackedUInt30(handlerPC);
writer.writeByte(flags);
writer.writePackedUInt30(types.length);
types.forEach(writer.writePackedUInt30);
}
factory TryBlock.read(BufferedReader reader, int tryIndex) {
final outerTryIndex = reader.readPackedUInt30() - 1;
final startPC = reader.readPackedUInt30();
final tryBlock = new TryBlock._(tryIndex, outerTryIndex, startPC);
tryBlock.endPC = reader.readPackedUInt30();
tryBlock.handlerPC = reader.readPackedUInt30();
tryBlock.flags = reader.readByte();
tryBlock.types = new List<int>.generate(
reader.readPackedUInt30(), (_) => reader.readPackedUInt30());
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 write(BufferedWriter writer) {
writer.writePackedUInt30(blocks.length);
blocks.forEach((b) => b.write(writer));
}
ExceptionsTable.read(BufferedReader reader)
: blocks = new List<TryBlock>.generate(reader.readPackedUInt30(),
(int index) => new TryBlock.read(reader, 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();
}
}