// 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 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 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.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 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 write(BufferedWriter writer) { writer.writePackedUInt30(blocks.length); blocks.forEach((b) => b.write(writer)); } ExceptionsTable.read(BufferedReader reader) : blocks = new List.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(); } }