// 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.source_positions; import 'dart:io' show BytesBuilder; /// Maintains mapping between bytecode instructions and source positions. class SourcePositions { final Map mapping = {}; // PC -> fileOffset int _lastPc = 0; int _lastOffset = 0; SourcePositions(); void add(int pc, int fileOffset) { assert(pc > _lastPc); assert(fileOffset >= 0); if (fileOffset != _lastOffset) { mapping[pc] = fileOffset; _lastPc = pc; _lastOffset = fileOffset; } } List toBytes() { final write = new BufferedWriter(); write.writePackedUInt30(mapping.length); final encodePC = new PackedUInt30DeltaEncoder(); final encodeOffset = new SLEB128DeltaEncoder(); mapping.forEach((int pc, int fileOffset) { encodePC.write(write, pc); encodeOffset.write(write, fileOffset); }); return write.buffer.takeBytes(); } SourcePositions.fromBytes(List bytes) { final reader = new BufferedReader(bytes); final int length = reader.readPackedUInt30(); final decodePC = new PackedUInt30DeltaDecoder(); final decodeOffset = new SLEB128DeltaDecoder(); for (int i = 0; i < length; ++i) { int pc = decodePC.read(reader); int fileOffset = decodeOffset.read(reader); add(pc, fileOffset); } } @override String toString() => mapping.toString(); Map getBytecodeAnnotations() { return mapping.map((int pc, int fileOffset) => new MapEntry(pc, 'source position $fileOffset')); } } class BufferedWriter { final BytesBuilder buffer = new BytesBuilder(); void writePackedUInt30(int value) { if ((value >> 30) != 0) { throw 'Value $value is out of range'; } if (value < 0x80) { buffer.addByte(value); } else if (value < 0x4000) { buffer.addByte((value >> 8) | 0x80); buffer.addByte(value & 0xFF); } else { buffer.addByte((value >> 24) | 0xC0); buffer.addByte((value >> 16) & 0xFF); buffer.addByte((value >> 8) & 0xFF); buffer.addByte(value & 0xFF); } } void writeSLEB128(int value) { bool last = false; do { int part = value & 0x7f; value >>= 7; if ((value == 0 && (part & 0x40) == 0) || (value == -1 && (part & 0x40) != 0)) { last = true; } else { part |= 0x80; } buffer.addByte(part); } while (!last); } } class BufferedReader { final List _buffer; int _pos = 0; BufferedReader(this._buffer); int readByte() => _buffer[_pos++]; int readPackedUInt30() { var byte = readByte(); if (byte & 0x80 == 0) { // 0xxxxxxx return byte; } else if (byte & 0x40 == 0) { // 10xxxxxx return ((byte & 0x3F) << 8) | readByte(); } else { // 11xxxxxx return ((byte & 0x3F) << 24) | (readByte() << 16) | (readByte() << 8) | readByte(); } } int readSLEB128() { int value = 0; int shift = 0; int part = 0; do { part = readByte(); value |= (part & 0x7f) << shift; shift += 7; } while ((part & 0x80) != 0); const int kBitsPerInt = 64; if ((shift < kBitsPerInt) && ((part & 0x40) != 0)) { value |= (-1) << shift; } return value; } } class PackedUInt30DeltaEncoder { int _last = 0; void write(BufferedWriter write, int value) { write.writePackedUInt30(value - _last); _last = value; } } class PackedUInt30DeltaDecoder { int _last = 0; int read(BufferedReader reader) { int value = reader.readPackedUInt30() + _last; _last = value; return value; } } class SLEB128DeltaEncoder { int _last = 0; void write(BufferedWriter writer, int value) { writer.writeSLEB128(value - _last); _last = value; } } class SLEB128DeltaDecoder { int _last = 0; int read(BufferedReader reader) { int value = reader.readSLEB128() + _last; _last = value; return value; } }