68de477535
Corresponding VM changes: https://dart-review.googlesource.com/c/sdk/+/101062 On a large app, size of bytecode instructions: Before: 12115384 After: 6282376 (-48.1%) Total size of the app: Before: 29790240 After: 23681504 (-20.5%) Change-Id: Idd8f97e991236c25d663d1bcf18a51a53e73a2b7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99400 Reviewed-by: Ryan Macnak <rmacnak@google.com>
140 lines
4.0 KiB
Dart
140 lines
4.0 KiB
Dart
// 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();
|
|
}
|
|
}
|