// 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 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 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 types = []; 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.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 blocks = []; 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.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(); } }