| // 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 'bytecode_serialization.dart' |
| show |
| BufferedWriter, |
| BufferedReader, |
| BytecodeDeclaration, |
| PackedUInt30DeltaEncoder, |
| PackedUInt30DeltaDecoder, |
| SLEB128DeltaEncoder, |
| SLEB128DeltaDecoder; |
| |
| /// Maintains mapping between bytecode instructions and source positions. |
| class SourcePositions extends BytecodeDeclaration { |
| // Special value of fileOffset which marks synthetic code without source |
| // position. |
| static const syntheticCodeMarker = -1; |
| // Special value of fileOffset which marks yield point. |
| static const yieldPointMarker = -2; |
| |
| final List<int> _positions = <int>[]; // Pairs (PC, fileOffset). |
| int _lastPc = 0; |
| int _lastOffset = 0; |
| |
| SourcePositions(); |
| |
| void add(int pc, int fileOffset) { |
| assert(pc > _lastPc); |
| assert((fileOffset >= 0) || (fileOffset == syntheticCodeMarker)); |
| if (fileOffset != _lastOffset) { |
| _positions.add(pc); |
| _positions.add(fileOffset); |
| _lastPc = pc; |
| _lastOffset = fileOffset; |
| } |
| } |
| |
| void addYieldPoint(int pc, int fileOffset) { |
| assert(pc > _lastPc); |
| assert((fileOffset >= 0) || (fileOffset == syntheticCodeMarker)); |
| _positions.add(pc); |
| _positions.add(yieldPointMarker); |
| _positions.add(pc); |
| _positions.add(fileOffset); |
| _lastPc = pc; |
| _lastOffset = fileOffset; |
| } |
| |
| bool get isEmpty => _positions.isEmpty; |
| bool get isNotEmpty => !isEmpty; |
| |
| void write(BufferedWriter writer) { |
| writer.writePackedUInt30(_positions.length ~/ 2); |
| final encodePC = new PackedUInt30DeltaEncoder(); |
| final encodeOffset = new SLEB128DeltaEncoder(); |
| for (int i = 0; i < _positions.length; i += 2) { |
| final int pc = _positions[i]; |
| final int fileOffset = _positions[i + 1]; |
| encodePC.write(writer, pc); |
| encodeOffset.write(writer, fileOffset); |
| } |
| } |
| |
| SourcePositions.read(BufferedReader reader) { |
| 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); |
| _positions.add(pc); |
| _positions.add(fileOffset); |
| } |
| } |
| |
| @override |
| String toString() => _positions.toString(); |
| |
| Map<int, String> getBytecodeAnnotations() { |
| final map = <int, String>{}; |
| for (int i = 0; i < _positions.length; i += 2) { |
| final int pc = _positions[i]; |
| final int fileOffset = _positions[i + 1]; |
| final entry = (fileOffset == yieldPointMarker) |
| ? 'yield point' |
| : 'source position $fileOffset'; |
| if (map[pc] == null) { |
| map[pc] = entry; |
| } else { |
| map[pc] = "${map[pc]}; $entry"; |
| } |
| } |
| return map; |
| } |
| } |
| |
| /// Keeps file offsets of line starts. This information is used to |
| /// decode source positions to line/column. |
| class LineStarts extends BytecodeDeclaration { |
| final List<int> lineStarts; |
| |
| LineStarts(this.lineStarts); |
| |
| void write(BufferedWriter writer) { |
| writer.writePackedUInt30(lineStarts.length); |
| final encodeLineStarts = new PackedUInt30DeltaEncoder(); |
| for (int lineStart in lineStarts) { |
| encodeLineStarts.write(writer, lineStart); |
| } |
| } |
| |
| factory LineStarts.read(BufferedReader reader) { |
| final decodeLineStarts = new PackedUInt30DeltaDecoder(); |
| final lineStarts = new List<int>.generate( |
| reader.readPackedUInt30(), (_) => decodeLineStarts.read(reader)); |
| return new LineStarts(lineStarts); |
| } |
| |
| @override |
| String toString() => 'Line starts: $lineStarts'; |
| } |