372b02d97f
Change-Id: I22bbd3b7ea0cccc0a8d721c3766a80ae1c72060d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/87701 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Régis Crelier <regis@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
235 lines
6.7 KiB
Dart
235 lines
6.7 KiB
Dart
// Copyright (c) 2019, 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.local_variable_table;
|
|
|
|
import 'bytecode_serialization.dart';
|
|
|
|
enum LocalVariableEntryKind {
|
|
invalid,
|
|
scope,
|
|
variableDeclaration,
|
|
contextVariable,
|
|
}
|
|
|
|
abstract class LocalVariableEntry {
|
|
static const KindMask = 0x0F;
|
|
static const IsCapturedFlag = 1 << 4;
|
|
|
|
LocalVariableEntryKind get kind;
|
|
int get flags => 0;
|
|
final int startPC;
|
|
void writeContents(BufferedWriter writer);
|
|
|
|
LocalVariableEntry(this.startPC);
|
|
}
|
|
|
|
class Scope extends LocalVariableEntry {
|
|
int endPC;
|
|
int contextLevel;
|
|
int position;
|
|
int endPosition;
|
|
final List<VariableDeclaration> variables = <VariableDeclaration>[];
|
|
|
|
Scope(int startPC, this.endPC, this.contextLevel, this.position,
|
|
this.endPosition)
|
|
: super(startPC);
|
|
|
|
@override
|
|
LocalVariableEntryKind get kind => LocalVariableEntryKind.scope;
|
|
|
|
@override
|
|
void writeContents(BufferedWriter writer) {
|
|
if (endPC == null) {
|
|
throw '$this is not closed';
|
|
}
|
|
writer.writePackedUInt30(endPC - startPC);
|
|
writer.writeSLEB128(contextLevel);
|
|
writer.writePackedUInt30(position + 1);
|
|
writer.writePackedUInt30(endPosition + 1);
|
|
}
|
|
|
|
Scope.readContents(BufferedReader reader, int startPC) : super(startPC) {
|
|
endPC = startPC = reader.readPackedUInt30();
|
|
contextLevel = reader.readSLEB128();
|
|
position = reader.readPackedUInt30() - 1;
|
|
endPosition = reader.readPackedUInt30() - 1;
|
|
}
|
|
|
|
@override
|
|
String toString() =>
|
|
'scope (pc $startPC-$endPC pos $position-$endPosition context-level $contextLevel)';
|
|
}
|
|
|
|
class VariableDeclaration extends LocalVariableEntry {
|
|
bool isCaptured;
|
|
int index;
|
|
int name;
|
|
int type;
|
|
int position;
|
|
int initializedPosition;
|
|
|
|
VariableDeclaration(int startPC, this.isCaptured, this.index, this.name,
|
|
this.type, this.position, this.initializedPosition)
|
|
: super(startPC);
|
|
|
|
@override
|
|
LocalVariableEntryKind get kind => LocalVariableEntryKind.variableDeclaration;
|
|
|
|
@override
|
|
int get flags => (isCaptured ? LocalVariableEntry.IsCapturedFlag : 0);
|
|
|
|
@override
|
|
void writeContents(BufferedWriter writer) {
|
|
writer.writeSLEB128(index);
|
|
writer.writePackedUInt30(name);
|
|
writer.writePackedUInt30(type);
|
|
writer.writePackedUInt30(position + 1);
|
|
writer.writePackedUInt30(initializedPosition + 1);
|
|
}
|
|
|
|
VariableDeclaration.readContents(
|
|
BufferedReader reader, int startPC, int flags)
|
|
: super(startPC) {
|
|
isCaptured = (flags & LocalVariableEntry.IsCapturedFlag) != 0;
|
|
index = reader.readSLEB128();
|
|
name = reader.readPackedUInt30();
|
|
type = reader.readPackedUInt30();
|
|
position = reader.readPackedUInt30() - 1;
|
|
initializedPosition = reader.readPackedUInt30() - 1;
|
|
}
|
|
|
|
@override
|
|
String toString() =>
|
|
'variable $index${isCaptured ? ' (captured)' : ''} pc $startPC, name CP#$name, type CP#$type, pos $position, init-pos $initializedPosition';
|
|
}
|
|
|
|
class ContextVariable extends LocalVariableEntry {
|
|
int index;
|
|
|
|
ContextVariable(int startPC, this.index) : super(startPC);
|
|
|
|
@override
|
|
LocalVariableEntryKind get kind => LocalVariableEntryKind.contextVariable;
|
|
|
|
@override
|
|
void writeContents(BufferedWriter writer) {
|
|
writer.writeSLEB128(index);
|
|
}
|
|
|
|
ContextVariable.readContents(BufferedReader reader, int startPC)
|
|
: super(startPC) {
|
|
index = reader.readSLEB128();
|
|
}
|
|
|
|
@override
|
|
String toString() => 'context variable $index';
|
|
}
|
|
|
|
/// Keeps information about declared local variables.
|
|
class LocalVariableTable {
|
|
final scopes = <Scope>[];
|
|
final activeScopes = <Scope>[];
|
|
ContextVariable contextVariable;
|
|
|
|
LocalVariableTable();
|
|
|
|
void enterScope(int pc, int contextLevel, int position) {
|
|
final scope = new Scope(pc, null, contextLevel, position, null);
|
|
activeScopes.add(scope);
|
|
scopes.add(scope);
|
|
}
|
|
|
|
void declareVariable(int pc, bool isCaptured, int index, int nameCpIndex,
|
|
int typeCpIndex, int position, int initializedPosition) {
|
|
final variable = new VariableDeclaration(pc, isCaptured, index, nameCpIndex,
|
|
typeCpIndex, position, initializedPosition);
|
|
activeScopes.last.variables.add(variable);
|
|
}
|
|
|
|
void leaveScope(int pc, int endPosition) {
|
|
final scope = activeScopes.removeLast();
|
|
scope.endPC = pc;
|
|
scope.endPosition = endPosition;
|
|
if (scope.variables.isEmpty) {
|
|
scopes.remove(scope);
|
|
}
|
|
}
|
|
|
|
void recordContextVariable(int pc, int index) {
|
|
assert(contextVariable == null);
|
|
contextVariable = new ContextVariable(pc, index);
|
|
}
|
|
|
|
bool get isEmpty => scopes.isEmpty && contextVariable == null;
|
|
|
|
bool get isNotEmpty => !isEmpty;
|
|
|
|
bool get hasActiveScopes => activeScopes.isNotEmpty;
|
|
|
|
List<LocalVariableEntry> getEntries() {
|
|
final entries = <LocalVariableEntry>[];
|
|
if (contextVariable != null) {
|
|
entries.add(contextVariable);
|
|
}
|
|
for (Scope scope in scopes) {
|
|
entries.add(scope);
|
|
entries.addAll(scope.variables);
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
void write(BufferedWriter writer) {
|
|
final entries = getEntries();
|
|
writer.writePackedUInt30(entries.length);
|
|
final encodeStartPC = new SLEB128DeltaEncoder();
|
|
for (var entry in entries) {
|
|
writer.writeByte(entry.kind.index | entry.flags);
|
|
encodeStartPC.write(writer, entry.startPC);
|
|
entry.writeContents(writer);
|
|
}
|
|
}
|
|
|
|
LocalVariableTable.read(BufferedReader reader) {
|
|
final int numEntries = reader.readPackedUInt30();
|
|
final decodeStartPC = new SLEB128DeltaDecoder();
|
|
Scope scope;
|
|
for (int i = 0; i < numEntries; ++i) {
|
|
final int kindAndFlags = reader.readByte();
|
|
final LocalVariableEntryKind kind = LocalVariableEntryKind
|
|
.values[kindAndFlags & LocalVariableEntry.KindMask];
|
|
final int flags = kindAndFlags & ~LocalVariableEntry.KindMask;
|
|
final int startPC = decodeStartPC.read(reader);
|
|
switch (kind) {
|
|
case LocalVariableEntryKind.scope:
|
|
scope = new Scope.readContents(reader, startPC);
|
|
scopes.add(scope);
|
|
break;
|
|
case LocalVariableEntryKind.variableDeclaration:
|
|
scope.variables.add(
|
|
new VariableDeclaration.readContents(reader, startPC, flags));
|
|
break;
|
|
case LocalVariableEntryKind.contextVariable:
|
|
contextVariable = new ContextVariable.readContents(reader, startPC);
|
|
break;
|
|
default:
|
|
throw 'Unexpected entry kind ${kind}';
|
|
}
|
|
}
|
|
}
|
|
|
|
Map<int, String> getBytecodeAnnotations() {
|
|
final map = <int, String>{};
|
|
for (var entry in getEntries()) {
|
|
final pc = entry.startPC;
|
|
if (map[pc] == null) {
|
|
map[pc] = entry.toString();
|
|
} else {
|
|
map[pc] = "${map[pc]}; $entry";
|
|
}
|
|
}
|
|
return map;
|
|
}
|
|
}
|