d26558b7be
This CL allows for streaming big parts of the binary,
i.e. without using the AST nodes.
It is thus a stepping-stone in getting rid of the AST nodes in the VM.
Generally, all Expressions except "FunctionExpression",
and all Statements except "FunctionDeclaration" can be streamed.
There are currently not streamed because they create new functions,
which has a pointer to an AstNode (which we don't have when streaming).
Once we no longer need AstNodes at all these can be streamed as well.
This is, I think, mostly a matter of streaming the ScopeBuilder as well,
something that is not currently done.
The way the streaming is build, one has to stream an entire subtree.
That means, that if an expression (or statement), A, that is generally
streamable contains an expression or a statement, B, that is not streamable,
A cannot be streamed.
The way this is build is by marking AstNodes as streamable or not
("cannot_stream_" field). That way we know up front whether we can stream
a subtree or not.
The streaming is done via "kernel_binary_flowgraph".
In this file there are many obvious comments, e.g.
```
TokenPosition position = ReadPosition(); // read position.
```
This has been done in an attempt to add a comment to everything that
reads from the binary to make it stand out more.
All changes from kernel_to_il up to and including May 2nd 2017
should be included.
R=kmillikin@google.com
Review-Url: https://codereview.chromium.org/2854393002 .
1182 lines
41 KiB
Dart
1182 lines
41 KiB
Dart
// Copyright (c) 2016, 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 kernel.ast_from_binary;
|
|
|
|
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
|
|
import '../ast.dart';
|
|
import '../transformations/flags.dart';
|
|
import 'tag.dart';
|
|
|
|
class ParseError {
|
|
String filename;
|
|
int byteIndex;
|
|
String message;
|
|
String path;
|
|
|
|
ParseError(this.message, {this.filename, this.byteIndex, this.path});
|
|
|
|
String toString() => '$filename:$byteIndex: $message at $path';
|
|
}
|
|
|
|
class BinaryBuilder {
|
|
final List<VariableDeclaration> variableStack = <VariableDeclaration>[];
|
|
final List<LabeledStatement> labelStack = <LabeledStatement>[];
|
|
int labelStackBase = 0;
|
|
final List<SwitchCase> switchCaseStack = <SwitchCase>[];
|
|
final List<TypeParameter> typeParameterStack = <TypeParameter>[];
|
|
final String filename;
|
|
final List<int> _bytes;
|
|
int _byteIndex = 0;
|
|
final List<String> _stringTable = <String>[];
|
|
final List<String> _sourceUriTable = <String>[];
|
|
List<CanonicalName> _linkTable;
|
|
int _transformerFlags = 0;
|
|
Library _currentLibrary;
|
|
|
|
// If something goes wrong, this list should indicate what library,
|
|
// class, and member was being built.
|
|
List<String> debugPath = <String>[];
|
|
|
|
bool _isReadingLibraryImplementation = false;
|
|
|
|
BinaryBuilder(this._bytes, [this.filename]);
|
|
|
|
fail(String message) {
|
|
throw new ParseError(message,
|
|
byteIndex: _byteIndex, filename: filename, path: debugPath.join('::'));
|
|
}
|
|
|
|
int readByte() => _bytes[_byteIndex++];
|
|
|
|
int readUInt() {
|
|
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 readMagicWord() {
|
|
return (readByte() << 24) |
|
|
(readByte() << 16) |
|
|
(readByte() << 8) |
|
|
readByte();
|
|
}
|
|
|
|
List<int> readUtf8Bytes() {
|
|
List<int> bytes = new Uint8List(readUInt());
|
|
bytes.setRange(0, bytes.length, _bytes, _byteIndex);
|
|
_byteIndex += bytes.length;
|
|
return bytes;
|
|
}
|
|
|
|
String readStringEntry(int numBytes) {
|
|
// Utf8Decoder will skip leading BOM characters, but we must preserve them.
|
|
// Collect leading BOMs before passing the bytes onto Utf8Decoder.
|
|
int numByteOrderMarks = 0;
|
|
while (_byteIndex + 2 < _bytes.length &&
|
|
_bytes[_byteIndex] == 0xef &&
|
|
_bytes[_byteIndex + 1] == 0xbb &&
|
|
_bytes[_byteIndex + 2] == 0xbf) {
|
|
++numByteOrderMarks;
|
|
_byteIndex += 3;
|
|
numBytes -= 3;
|
|
}
|
|
String string =
|
|
const Utf8Decoder().convert(_bytes, _byteIndex, _byteIndex + numBytes);
|
|
_byteIndex += numBytes;
|
|
if (numByteOrderMarks > 0) {
|
|
return '\ufeff' * numByteOrderMarks + string;
|
|
}
|
|
return string;
|
|
}
|
|
|
|
void readStringTable(List<String> table) {
|
|
// Read the table of end offsets.
|
|
int length = readUInt();
|
|
List<int> endOffsets = new List<int>(length);
|
|
for (int i = 0; i < length; ++i) {
|
|
endOffsets[i] = readUInt();
|
|
}
|
|
// Read the UTF-8 encoded strings.
|
|
table.length = length;
|
|
int startOffset = 0;
|
|
for (int i = 0; i < length; ++i) {
|
|
table[i] = readStringEntry(endOffsets[i] - startOffset);
|
|
startOffset = endOffsets[i];
|
|
}
|
|
}
|
|
|
|
String readUriReference() {
|
|
return _sourceUriTable[readUInt()];
|
|
}
|
|
|
|
String readStringReference() {
|
|
return _stringTable[readUInt()];
|
|
}
|
|
|
|
String readStringOrNullIfEmpty() {
|
|
var string = readStringReference();
|
|
return string.isEmpty ? null : string;
|
|
}
|
|
|
|
bool readAndCheckOptionTag() {
|
|
int tag = readByte();
|
|
if (tag == Tag.Nothing) {
|
|
return false;
|
|
} else if (tag == Tag.Something) {
|
|
return true;
|
|
} else {
|
|
throw fail('Invalid Option tag: $tag');
|
|
}
|
|
}
|
|
|
|
List<Expression> readAnnotationList(TreeNode parent) {
|
|
int length = readUInt();
|
|
if (length == 0) return const <Expression>[];
|
|
List<Expression> list = new List<Expression>(length);
|
|
for (int i = 0; i < length; ++i) {
|
|
list[i] = readExpression()..parent = parent;
|
|
}
|
|
return list;
|
|
}
|
|
|
|
void _fillTreeNodeList(
|
|
List<TreeNode> list, TreeNode buildObject(), TreeNode parent) {
|
|
list.length = readUInt();
|
|
for (int i = 0; i < list.length; ++i) {
|
|
list[i] = buildObject()..parent = parent;
|
|
}
|
|
}
|
|
|
|
void _fillNonTreeNodeList(List<Node> list, Node buildObject()) {
|
|
list.length = readUInt();
|
|
for (int i = 0; i < list.length; ++i) {
|
|
list[i] = buildObject();
|
|
}
|
|
}
|
|
|
|
/// Reads a list of named nodes, reusing any existing objects already in the
|
|
/// linking tree. The nodes are merged into [list], and if reading the library
|
|
/// implementation, the order is corrected.
|
|
///
|
|
/// [readObject] should read the object definition and its canonical name.
|
|
/// If an existing object is bound to the canonical name, the existing object
|
|
/// must be reused and returned.
|
|
void _mergeNamedNodeList(
|
|
List<NamedNode> list, NamedNode readObject(), TreeNode parent) {
|
|
if (_isReadingLibraryImplementation) {
|
|
// When reading the library implementation, overwrite the whole list
|
|
// with the new one.
|
|
_fillTreeNodeList(list, readObject, parent);
|
|
} else {
|
|
// When reading an external library, the results should either be:
|
|
// - merged with the existing external library definition (if any)
|
|
// - ignored if the library implementation is already in memory
|
|
int numberOfNodes = readUInt();
|
|
for (int i = 0; i < numberOfNodes; ++i) {
|
|
var value = readObject();
|
|
// We use the parent pointer of a node to determine if it already is in
|
|
// the AST and hence should not be added again.
|
|
if (value.parent == null) {
|
|
list.add(value..parent = parent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void readLinkTable(CanonicalName linkRoot) {
|
|
int length = readUInt();
|
|
_linkTable = new List<CanonicalName>(length);
|
|
for (int i = 0; i < length; ++i) {
|
|
int biasedParentIndex = readUInt();
|
|
String name = readStringReference();
|
|
var parent =
|
|
biasedParentIndex == 0 ? linkRoot : _linkTable[biasedParentIndex - 1];
|
|
_linkTable[i] = parent.getChild(name);
|
|
}
|
|
}
|
|
|
|
/// Deserializes a kernel program and stores it in [program].
|
|
///
|
|
/// When linking with a non-empty program, canonical names must have been
|
|
/// computed ahead of time.
|
|
///
|
|
/// The input bytes may contain multiple files concatenated.
|
|
void readProgram(Program program) {
|
|
while (_byteIndex < _bytes.length) {
|
|
_readOneProgram(program);
|
|
}
|
|
}
|
|
|
|
/// Reads a single program file from the input and loads it into [program],
|
|
/// overwriting and reusing any existing data in the program.
|
|
///
|
|
/// When linking with a non-empty program, canonical names must have been
|
|
/// computed ahead of time.
|
|
///
|
|
/// This should *only* be used when there is a reason to not allow
|
|
/// concatenated files.
|
|
void readSingleFileProgram(Program program) {
|
|
_readOneProgram(program);
|
|
if (_byteIndex < _bytes.length) {
|
|
if (_byteIndex + 3 < _bytes.length) {
|
|
int magic = readMagicWord();
|
|
if (magic == Tag.ProgramFile) {
|
|
throw 'Concatenated program file given when a single program '
|
|
'was expected.';
|
|
}
|
|
}
|
|
throw 'Unrecognized bytes following program data';
|
|
}
|
|
}
|
|
|
|
void _readOneProgram(Program program) {
|
|
int magic = readMagicWord();
|
|
if (magic != Tag.ProgramFile) {
|
|
throw fail('This is not a binary dart file. '
|
|
'Magic number was: ${magic.toRadixString(16)}');
|
|
}
|
|
readStringTable(_stringTable);
|
|
Map<String, Source> uriToSource = readUriToSource();
|
|
program.uriToSource.addAll(uriToSource);
|
|
readLinkTable(program.root);
|
|
int numberOfLibraries = readUInt();
|
|
List<Library> libraries = new List<Library>(numberOfLibraries);
|
|
for (int i = 0; i < numberOfLibraries; ++i) {
|
|
libraries[i] = readLibrary(program);
|
|
}
|
|
var mainMethod = readMemberReference(allowNull: true);
|
|
program.mainMethodName ??= mainMethod;
|
|
}
|
|
|
|
Map<String, Source> readUriToSource() {
|
|
readStringTable(_sourceUriTable);
|
|
int length = _sourceUriTable.length;
|
|
Map<String, Source> uriToSource = <String, Source>{};
|
|
for (int i = 0; i < length; ++i) {
|
|
String uri = _sourceUriTable[i];
|
|
List<int> sourceCode = readUtf8Bytes();
|
|
int lineCount = readUInt();
|
|
List<int> lineStarts = new List<int>(lineCount);
|
|
int previousLineStart = 0;
|
|
for (int j = 0; j < lineCount; ++j) {
|
|
int lineStart = readUInt() + previousLineStart;
|
|
lineStarts[j] = lineStart;
|
|
previousLineStart = lineStart;
|
|
}
|
|
uriToSource[uri] = new Source(lineStarts, sourceCode);
|
|
}
|
|
return uriToSource;
|
|
}
|
|
|
|
CanonicalName readCanonicalNameReference() {
|
|
var index = readUInt();
|
|
if (index == 0) return null;
|
|
return _linkTable[index - 1];
|
|
}
|
|
|
|
Reference readLibraryReference() {
|
|
return readCanonicalNameReference().getReference();
|
|
}
|
|
|
|
DeferredImport readDeferredImportReference() {
|
|
int index = readUInt();
|
|
return _currentLibrary.deferredImports[index];
|
|
}
|
|
|
|
Reference readClassReference({bool allowNull: false}) {
|
|
var name = readCanonicalNameReference();
|
|
if (name == null && !allowNull) {
|
|
throw 'Expected a class reference to be valid but was `null`.';
|
|
}
|
|
return name?.getReference();
|
|
}
|
|
|
|
Reference readMemberReference({bool allowNull: false}) {
|
|
var name = readCanonicalNameReference();
|
|
if (name == null && !allowNull) {
|
|
throw 'Expected a member reference to be valid but was `null`.';
|
|
}
|
|
return name?.getReference();
|
|
}
|
|
|
|
Reference readTypedefReference() {
|
|
return readCanonicalNameReference().getReference();
|
|
}
|
|
|
|
Name readName() {
|
|
String text = readStringReference();
|
|
if (text.isNotEmpty && text[0] == '_') {
|
|
return new Name.byReference(text, readLibraryReference());
|
|
} else {
|
|
return new Name(text);
|
|
}
|
|
}
|
|
|
|
Library readLibrary(Program program) {
|
|
int flags = readByte();
|
|
bool isExternal = (flags & 0x1) != 0;
|
|
_isReadingLibraryImplementation = !isExternal;
|
|
var canonicalName = readCanonicalNameReference();
|
|
Reference reference = canonicalName.getReference();
|
|
Library library = reference.node;
|
|
bool shouldWriteData = library == null || _isReadingLibraryImplementation;
|
|
if (library == null) {
|
|
library =
|
|
new Library(Uri.parse(canonicalName.name), reference: reference);
|
|
program.libraries.add(library..parent = program);
|
|
}
|
|
_currentLibrary = library;
|
|
String name = readStringOrNullIfEmpty();
|
|
// TODO(jensj): We currently save (almost the same) uri twice.
|
|
String fileUri = readUriReference();
|
|
|
|
if (shouldWriteData) {
|
|
library.isExternal = isExternal;
|
|
library.name = name;
|
|
library.fileUri = fileUri;
|
|
}
|
|
|
|
_readDeferredImports(library);
|
|
|
|
debugPath.add(library.name ?? library.importUri?.toString() ?? 'library');
|
|
|
|
_mergeNamedNodeList(library.typedefs, readTypedef, library);
|
|
_mergeNamedNodeList(library.classes, readClass, library);
|
|
_mergeNamedNodeList(library.fields, readField, library);
|
|
_mergeNamedNodeList(library.procedures, readProcedure, library);
|
|
|
|
debugPath.removeLast();
|
|
_currentLibrary = null;
|
|
return library;
|
|
}
|
|
|
|
void _readDeferredImports(Library library) {
|
|
int length = readUInt();
|
|
if (library.isExternal) {
|
|
assert(length == 0);
|
|
return;
|
|
}
|
|
library.deferredImports.length = length;
|
|
for (int i = 0; i < length; ++i) {
|
|
library.deferredImports[i] = new DeferredImport.byReference(
|
|
readLibraryReference(), readStringReference())
|
|
..parent = library;
|
|
}
|
|
}
|
|
|
|
Typedef readTypedef() {
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Typedef node = reference.node;
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Typedef(null, null, reference: reference);
|
|
}
|
|
int fileOffset = readOffset();
|
|
String name = readStringReference();
|
|
String fileUri = readUriReference();
|
|
readAndPushTypeParameterList(node.typeParameters, node);
|
|
var type = readDartType();
|
|
typeParameterStack.length = 0;
|
|
if (shouldWriteData) {
|
|
node.fileOffset = fileOffset;
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
node.type = type;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
Class readClass() {
|
|
int tag = readByte();
|
|
assert(tag == Tag.Class);
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Class node = reference.node;
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Class(reference: reference)..level = ClassLevel.Temporary;
|
|
}
|
|
node.fileOffset = readOffset();
|
|
int flags = readByte();
|
|
node.isAbstract = flags & 0x1 != 0;
|
|
int levelIndex = (flags >> 1) & 0x3;
|
|
var level = ClassLevel.values[levelIndex + 1];
|
|
if (level.index >= node.level.index) {
|
|
node.level = level;
|
|
}
|
|
var name = readStringOrNullIfEmpty();
|
|
var fileUri = readUriReference();
|
|
var annotations = readAnnotationList(node);
|
|
debugPath.add(node.name ?? 'normal-class');
|
|
readAndPushTypeParameterList(node.typeParameters, node);
|
|
var supertype = readSupertypeOption();
|
|
var mixedInType = readSupertypeOption();
|
|
_fillNonTreeNodeList(node.implementedTypes, readSupertype);
|
|
_mergeNamedNodeList(node.fields, readField, node);
|
|
_mergeNamedNodeList(node.constructors, readConstructor, node);
|
|
_mergeNamedNodeList(node.procedures, readProcedure, node);
|
|
typeParameterStack.length = 0;
|
|
debugPath.removeLast();
|
|
if (shouldWriteData) {
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
node.annotations = annotations;
|
|
node.supertype = supertype;
|
|
node.mixedInType = mixedInType;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
int getAndResetTransformerFlags() {
|
|
int flags = _transformerFlags;
|
|
_transformerFlags = 0;
|
|
return flags;
|
|
}
|
|
|
|
/// Adds the given flag to the current [Member.transformerFlags].
|
|
void addTransformerFlag(int flags) {
|
|
_transformerFlags |= flags;
|
|
}
|
|
|
|
Field readField() {
|
|
int tag = readByte();
|
|
assert(tag == Tag.Field);
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Field node = reference.node;
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Field(null, reference: reference);
|
|
}
|
|
int fileOffset = readOffset();
|
|
int fileEndOffset = readOffset();
|
|
int flags = readByte();
|
|
var name = readName();
|
|
var fileUri = readUriReference();
|
|
var annotations = readAnnotationList(node);
|
|
debugPath.add(node.name?.name ?? 'field');
|
|
var type = readDartType();
|
|
var initializer = readExpressionOption();
|
|
int transformerFlags = getAndResetTransformerFlags();
|
|
debugPath.removeLast();
|
|
if (shouldWriteData) {
|
|
node.fileOffset = fileOffset;
|
|
node.fileEndOffset = fileEndOffset;
|
|
node.flags = flags;
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
node.annotations = annotations;
|
|
node.type = type;
|
|
node.initializer = initializer;
|
|
node.initializer?.parent = node;
|
|
node.transformerFlags = transformerFlags;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
Constructor readConstructor() {
|
|
int tag = readByte();
|
|
assert(tag == Tag.Constructor);
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Constructor node = reference.node;
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Constructor(null, reference: reference);
|
|
}
|
|
var fileOffset = readOffset();
|
|
var fileEndOffset = readOffset();
|
|
var flags = readByte();
|
|
var name = readName();
|
|
var annotations = readAnnotationList(node);
|
|
debugPath.add(node.name?.name ?? 'constructor');
|
|
var function = readFunctionNode();
|
|
pushVariableDeclarations(function.positionalParameters);
|
|
pushVariableDeclarations(function.namedParameters);
|
|
_fillTreeNodeList(node.initializers, readInitializer, node);
|
|
variableStack.length = 0;
|
|
var transformerFlags = getAndResetTransformerFlags();
|
|
debugPath.removeLast();
|
|
if (shouldWriteData) {
|
|
node.fileOffset = fileOffset;
|
|
node.fileEndOffset = fileEndOffset;
|
|
node.flags = flags;
|
|
node.name = name;
|
|
node.annotations = annotations;
|
|
node.function = function..parent = node;
|
|
node.transformerFlags = transformerFlags;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
Procedure readProcedure() {
|
|
int tag = readByte();
|
|
assert(tag == Tag.Procedure);
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Procedure node = reference.node;
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Procedure(null, null, null, reference: reference);
|
|
}
|
|
var fileOffset = readOffset();
|
|
var fileEndOffset = readOffset();
|
|
int kindIndex = readByte();
|
|
var kind = ProcedureKind.values[kindIndex];
|
|
var flags = readByte();
|
|
var name = readName();
|
|
var fileUri = readUriReference();
|
|
var annotations = readAnnotationList(node);
|
|
debugPath.add(node.name?.name ?? 'procedure');
|
|
var function = readFunctionNodeOption();
|
|
var transformerFlags = getAndResetTransformerFlags();
|
|
debugPath.removeLast();
|
|
if (shouldWriteData) {
|
|
node.fileOffset = fileOffset;
|
|
node.fileEndOffset = fileEndOffset;
|
|
node.kind = kind;
|
|
node.flags = flags;
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
node.annotations = annotations;
|
|
node.function = function;
|
|
node.function?.parent = node;
|
|
node.transformerFlags = transformerFlags;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
Initializer readInitializer() {
|
|
int tag = readByte();
|
|
switch (tag) {
|
|
case Tag.InvalidInitializer:
|
|
return new InvalidInitializer();
|
|
case Tag.FieldInitializer:
|
|
return new FieldInitializer.byReference(
|
|
readMemberReference(), readExpression());
|
|
case Tag.SuperInitializer:
|
|
return new SuperInitializer.byReference(
|
|
readMemberReference(), readArguments());
|
|
case Tag.RedirectingInitializer:
|
|
return new RedirectingInitializer.byReference(
|
|
readMemberReference(), readArguments());
|
|
case Tag.LocalInitializer:
|
|
return new LocalInitializer(readAndPushVariableDeclaration());
|
|
default:
|
|
throw fail('Invalid initializer tag: $tag');
|
|
}
|
|
}
|
|
|
|
FunctionNode readFunctionNodeOption() {
|
|
return readAndCheckOptionTag() ? readFunctionNode() : null;
|
|
}
|
|
|
|
FunctionNode readFunctionNode() {
|
|
int offset = readOffset();
|
|
int endOffset = readOffset();
|
|
AsyncMarker asyncMarker = AsyncMarker.values[readByte()];
|
|
AsyncMarker dartAsyncMarker = AsyncMarker.values[readByte()];
|
|
int typeParameterStackHeight = typeParameterStack.length;
|
|
var typeParameters = readAndPushTypeParameterList();
|
|
var requiredParameterCount = readUInt();
|
|
int variableStackHeight = variableStack.length;
|
|
var positional = readAndPushVariableDeclarationList();
|
|
var named = readAndPushVariableDeclarationList();
|
|
var returnType = readDartType();
|
|
int oldLabelStackBase = labelStackBase;
|
|
labelStackBase = labelStack.length;
|
|
var body = readStatementOption();
|
|
labelStackBase = oldLabelStackBase;
|
|
variableStack.length = variableStackHeight;
|
|
typeParameterStack.length = typeParameterStackHeight;
|
|
return new FunctionNode(body,
|
|
typeParameters: typeParameters,
|
|
requiredParameterCount: requiredParameterCount,
|
|
positionalParameters: positional,
|
|
namedParameters: named,
|
|
returnType: returnType,
|
|
asyncMarker: asyncMarker,
|
|
dartAsyncMarker: dartAsyncMarker)
|
|
..fileOffset = offset
|
|
..fileEndOffset = endOffset;
|
|
}
|
|
|
|
void pushVariableDeclaration(VariableDeclaration variable) {
|
|
variableStack.add(variable);
|
|
}
|
|
|
|
void pushVariableDeclarations(List<VariableDeclaration> variables) {
|
|
variableStack.addAll(variables);
|
|
}
|
|
|
|
VariableDeclaration readVariableReference() {
|
|
int index = readUInt();
|
|
if (index >= variableStack.length) {
|
|
throw fail('Invalid variable index: $index');
|
|
}
|
|
return variableStack[index];
|
|
}
|
|
|
|
String logicalOperatorToString(int index) {
|
|
switch (index) {
|
|
case 0:
|
|
return '&&';
|
|
case 1:
|
|
return '||';
|
|
default:
|
|
throw fail('Invalid logical operator index: $index');
|
|
}
|
|
}
|
|
|
|
List<Expression> readExpressionList() {
|
|
return new List<Expression>.generate(readUInt(), (i) => readExpression());
|
|
}
|
|
|
|
Expression readExpressionOption() {
|
|
return readAndCheckOptionTag() ? readExpression() : null;
|
|
}
|
|
|
|
Expression readExpression() {
|
|
int tagByte = readByte();
|
|
int tag = tagByte & Tag.SpecializedTagHighBit == 0
|
|
? tagByte
|
|
: (tagByte & Tag.SpecializedTagMask);
|
|
switch (tag) {
|
|
case Tag.LoadLibrary:
|
|
return new LoadLibrary(readDeferredImportReference());
|
|
case Tag.CheckLibraryIsLoaded:
|
|
return new CheckLibraryIsLoaded(readDeferredImportReference());
|
|
case Tag.InvalidExpression:
|
|
return new InvalidExpression();
|
|
case Tag.VariableGet:
|
|
int offset = readOffset();
|
|
readUInt(); // offset of the variable declaration in the binary.
|
|
return new VariableGet(readVariableReference(), readDartTypeOption())
|
|
..fileOffset = offset;
|
|
case Tag.SpecializedVariableGet:
|
|
int index = tagByte & Tag.SpecializedPayloadMask;
|
|
int offset = readOffset();
|
|
readUInt(); // offset of the variable declaration in the binary.
|
|
return new VariableGet(variableStack[index])..fileOffset = offset;
|
|
case Tag.VariableSet:
|
|
int offset = readOffset();
|
|
readUInt(); // offset of the variable declaration in the binary.
|
|
return new VariableSet(readVariableReference(), readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.SpecializedVariableSet:
|
|
int index = tagByte & Tag.SpecializedPayloadMask;
|
|
int offset = readOffset();
|
|
readUInt(); // offset of the variable declaration in the binary.
|
|
return new VariableSet(variableStack[index], readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.PropertyGet:
|
|
int offset = readOffset();
|
|
return new PropertyGet.byReference(
|
|
readExpression(), readName(), readMemberReference(allowNull: true))
|
|
..fileOffset = offset;
|
|
case Tag.PropertySet:
|
|
int offset = readOffset();
|
|
return new PropertySet.byReference(readExpression(), readName(),
|
|
readExpression(), readMemberReference(allowNull: true))
|
|
..fileOffset = offset;
|
|
case Tag.SuperPropertyGet:
|
|
addTransformerFlag(TransformerFlag.superCalls);
|
|
return new SuperPropertyGet.byReference(
|
|
readName(), readMemberReference(allowNull: true));
|
|
case Tag.SuperPropertySet:
|
|
addTransformerFlag(TransformerFlag.superCalls);
|
|
return new SuperPropertySet.byReference(
|
|
readName(), readExpression(), readMemberReference(allowNull: true));
|
|
case Tag.DirectPropertyGet:
|
|
int offset = readOffset();
|
|
return new DirectPropertyGet.byReference(
|
|
readExpression(), readMemberReference())
|
|
..fileOffset = offset;
|
|
case Tag.DirectPropertySet:
|
|
int offset = readOffset();
|
|
return new DirectPropertySet.byReference(
|
|
readExpression(), readMemberReference(), readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.StaticGet:
|
|
int offset = readOffset();
|
|
return new StaticGet.byReference(readMemberReference())
|
|
..fileOffset = offset;
|
|
case Tag.StaticSet:
|
|
int offset = readOffset();
|
|
return new StaticSet.byReference(
|
|
readMemberReference(), readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.MethodInvocation:
|
|
int offset = readOffset();
|
|
return new MethodInvocation.byReference(readExpression(), readName(),
|
|
readArguments(), readMemberReference(allowNull: true))
|
|
..fileOffset = offset;
|
|
case Tag.SuperMethodInvocation:
|
|
int offset = readOffset();
|
|
addTransformerFlag(TransformerFlag.superCalls);
|
|
return new SuperMethodInvocation.byReference(
|
|
readName(), readArguments(), readMemberReference(allowNull: true))
|
|
..fileOffset = offset;
|
|
case Tag.DirectMethodInvocation:
|
|
return new DirectMethodInvocation.byReference(
|
|
readExpression(), readMemberReference(), readArguments());
|
|
case Tag.StaticInvocation:
|
|
int offset = readOffset();
|
|
return new StaticInvocation.byReference(
|
|
readMemberReference(), readArguments(),
|
|
isConst: false)
|
|
..fileOffset = offset;
|
|
case Tag.ConstStaticInvocation:
|
|
int offset = readOffset();
|
|
return new StaticInvocation.byReference(
|
|
readMemberReference(), readArguments(),
|
|
isConst: true)
|
|
..fileOffset = offset;
|
|
case Tag.ConstructorInvocation:
|
|
int offset = readOffset();
|
|
return new ConstructorInvocation.byReference(
|
|
readMemberReference(), readArguments(),
|
|
isConst: false)
|
|
..fileOffset = offset;
|
|
case Tag.ConstConstructorInvocation:
|
|
int offset = readOffset();
|
|
return new ConstructorInvocation.byReference(
|
|
readMemberReference(), readArguments(),
|
|
isConst: true)
|
|
..fileOffset = offset;
|
|
case Tag.Not:
|
|
return new Not(readExpression());
|
|
case Tag.LogicalExpression:
|
|
return new LogicalExpression(readExpression(),
|
|
logicalOperatorToString(readByte()), readExpression());
|
|
case Tag.ConditionalExpression:
|
|
return new ConditionalExpression(readExpression(), readExpression(),
|
|
readExpression(), readDartTypeOption());
|
|
case Tag.StringConcatenation:
|
|
int offset = readOffset();
|
|
return new StringConcatenation(readExpressionList())
|
|
..fileOffset = offset;
|
|
case Tag.IsExpression:
|
|
int offset = readOffset();
|
|
return new IsExpression(readExpression(), readDartType())
|
|
..fileOffset = offset;
|
|
case Tag.AsExpression:
|
|
int offset = readOffset();
|
|
return new AsExpression(readExpression(), readDartType())
|
|
..fileOffset = offset;
|
|
case Tag.StringLiteral:
|
|
return new StringLiteral(readStringReference());
|
|
case Tag.SpecializedIntLiteral:
|
|
int biasedValue = tagByte & Tag.SpecializedPayloadMask;
|
|
return new IntLiteral(biasedValue - Tag.SpecializedIntLiteralBias);
|
|
case Tag.PositiveIntLiteral:
|
|
return new IntLiteral(readUInt());
|
|
case Tag.NegativeIntLiteral:
|
|
return new IntLiteral(-readUInt());
|
|
case Tag.BigIntLiteral:
|
|
return new IntLiteral(int.parse(readStringReference()));
|
|
case Tag.DoubleLiteral:
|
|
return new DoubleLiteral(double.parse(readStringReference()));
|
|
case Tag.TrueLiteral:
|
|
return new BoolLiteral(true);
|
|
case Tag.FalseLiteral:
|
|
return new BoolLiteral(false);
|
|
case Tag.NullLiteral:
|
|
return new NullLiteral();
|
|
case Tag.SymbolLiteral:
|
|
return new SymbolLiteral(readStringReference());
|
|
case Tag.TypeLiteral:
|
|
return new TypeLiteral(readDartType());
|
|
case Tag.ThisExpression:
|
|
return new ThisExpression();
|
|
case Tag.Rethrow:
|
|
int offset = readOffset();
|
|
return new Rethrow()..fileOffset = offset;
|
|
case Tag.Throw:
|
|
int offset = readOffset();
|
|
return new Throw(readExpression())..fileOffset = offset;
|
|
case Tag.ListLiteral:
|
|
int offset = readOffset();
|
|
var typeArgument = readDartType();
|
|
return new ListLiteral(readExpressionList(),
|
|
typeArgument: typeArgument, isConst: false)
|
|
..fileOffset = offset;
|
|
case Tag.ConstListLiteral:
|
|
int offset = readOffset();
|
|
var typeArgument = readDartType();
|
|
return new ListLiteral(readExpressionList(),
|
|
typeArgument: typeArgument, isConst: true)
|
|
..fileOffset = offset;
|
|
case Tag.MapLiteral:
|
|
int offset = readOffset();
|
|
var keyType = readDartType();
|
|
var valueType = readDartType();
|
|
return new MapLiteral(readMapEntryList(),
|
|
keyType: keyType, valueType: valueType, isConst: false)
|
|
..fileOffset = offset;
|
|
case Tag.ConstMapLiteral:
|
|
int offset = readOffset();
|
|
var keyType = readDartType();
|
|
var valueType = readDartType();
|
|
return new MapLiteral(readMapEntryList(),
|
|
keyType: keyType, valueType: valueType, isConst: true)
|
|
..fileOffset = offset;
|
|
case Tag.AwaitExpression:
|
|
return new AwaitExpression(readExpression());
|
|
case Tag.FunctionExpression:
|
|
return new FunctionExpression(readFunctionNode());
|
|
case Tag.Let:
|
|
var variable = readVariableDeclaration();
|
|
int stackHeight = variableStack.length;
|
|
pushVariableDeclaration(variable);
|
|
var body = readExpression();
|
|
variableStack.length = stackHeight;
|
|
return new Let(variable, body);
|
|
case Tag.VectorCreation:
|
|
var length = readUInt();
|
|
return new VectorCreation(length);
|
|
case Tag.VectorGet:
|
|
var vectorExpression = readExpression();
|
|
var index = readUInt();
|
|
return new VectorGet(vectorExpression, index);
|
|
case Tag.VectorSet:
|
|
var vectorExpression = readExpression();
|
|
var index = readUInt();
|
|
var value = readExpression();
|
|
return new VectorSet(vectorExpression, index, value);
|
|
case Tag.VectorCopy:
|
|
var vectorExpression = readExpression();
|
|
return new VectorCopy(vectorExpression);
|
|
case Tag.ClosureCreation:
|
|
var topLevelFunctionReference = readMemberReference();
|
|
var contextVector = readExpression();
|
|
var functionType = readDartType();
|
|
return new ClosureCreation.byReference(
|
|
topLevelFunctionReference, contextVector, functionType);
|
|
default:
|
|
throw fail('Invalid expression tag: $tag');
|
|
}
|
|
}
|
|
|
|
List<MapEntry> readMapEntryList() {
|
|
return new List<MapEntry>.generate(readUInt(), (i) => readMapEntry());
|
|
}
|
|
|
|
MapEntry readMapEntry() {
|
|
return new MapEntry(readExpression(), readExpression());
|
|
}
|
|
|
|
List<Statement> readStatementList() {
|
|
return new List<Statement>.generate(readUInt(), (i) => readStatement());
|
|
}
|
|
|
|
Statement readStatementOrNullIfEmpty() {
|
|
var node = readStatement();
|
|
if (node is EmptyStatement) {
|
|
return null;
|
|
} else {
|
|
return node;
|
|
}
|
|
}
|
|
|
|
Statement readStatementOption() {
|
|
return readAndCheckOptionTag() ? readStatement() : null;
|
|
}
|
|
|
|
Statement readStatement() {
|
|
int tag = readByte();
|
|
switch (tag) {
|
|
case Tag.InvalidStatement:
|
|
return new InvalidStatement();
|
|
case Tag.ExpressionStatement:
|
|
return new ExpressionStatement(readExpression());
|
|
case Tag.Block:
|
|
return readBlock();
|
|
case Tag.EmptyStatement:
|
|
return new EmptyStatement();
|
|
case Tag.AssertStatement:
|
|
return new AssertStatement(readExpression(), readExpressionOption());
|
|
case Tag.LabeledStatement:
|
|
var label = new LabeledStatement(null);
|
|
labelStack.add(label);
|
|
label.body = readStatement()..parent = label;
|
|
labelStack.removeLast();
|
|
return label;
|
|
case Tag.BreakStatement:
|
|
int offset = readOffset();
|
|
int index = readUInt();
|
|
return new BreakStatement(labelStack[labelStackBase + index])
|
|
..fileOffset = offset;
|
|
case Tag.WhileStatement:
|
|
return new WhileStatement(readExpression(), readStatement());
|
|
case Tag.DoStatement:
|
|
return new DoStatement(readStatement(), readExpression());
|
|
case Tag.ForStatement:
|
|
int variableStackHeight = variableStack.length;
|
|
var variables = readAndPushVariableDeclarationList();
|
|
var condition = readExpressionOption();
|
|
var updates = readExpressionList();
|
|
var body = readStatement();
|
|
variableStack.length = variableStackHeight;
|
|
return new ForStatement(variables, condition, updates, body);
|
|
case Tag.ForInStatement:
|
|
case Tag.AsyncForInStatement:
|
|
bool isAsync = tag == Tag.AsyncForInStatement;
|
|
int variableStackHeight = variableStack.length;
|
|
var offset = readOffset();
|
|
var variable = readAndPushVariableDeclaration();
|
|
var iterable = readExpression();
|
|
var body = readStatement();
|
|
variableStack.length = variableStackHeight;
|
|
return new ForInStatement(variable, iterable, body, isAsync: isAsync)
|
|
..fileOffset = offset;
|
|
case Tag.SwitchStatement:
|
|
var expression = readExpression();
|
|
int count = readUInt();
|
|
List<SwitchCase> cases =
|
|
new List<SwitchCase>.generate(count, (i) => new SwitchCase.empty());
|
|
switchCaseStack.addAll(cases);
|
|
for (int i = 0; i < cases.length; ++i) {
|
|
var caseNode = cases[i];
|
|
int length = readUInt();
|
|
caseNode.expressions.length = length;
|
|
caseNode.expressionOffsets.length = length;
|
|
for (int i = 0; i < length; ++i) {
|
|
caseNode.expressionOffsets[i] = readOffset();
|
|
caseNode.expressions[i] = readExpression()..parent = caseNode;
|
|
}
|
|
caseNode.isDefault = readByte() == 1;
|
|
caseNode.body = readStatement()..parent = caseNode;
|
|
}
|
|
switchCaseStack.length -= count;
|
|
return new SwitchStatement(expression, cases);
|
|
case Tag.ContinueSwitchStatement:
|
|
int index = readUInt();
|
|
return new ContinueSwitchStatement(switchCaseStack[index]);
|
|
case Tag.IfStatement:
|
|
return new IfStatement(
|
|
readExpression(), readStatement(), readStatementOrNullIfEmpty());
|
|
case Tag.ReturnStatement:
|
|
int offset = readOffset();
|
|
return new ReturnStatement(readExpressionOption())..fileOffset = offset;
|
|
case Tag.TryCatch:
|
|
Statement body = readStatement();
|
|
readByte(); // whether any catch needs a stacktrace.
|
|
return new TryCatch(body, readCatchList());
|
|
case Tag.TryFinally:
|
|
return new TryFinally(readStatement(), readStatement());
|
|
case Tag.YieldStatement:
|
|
int offset = readOffset();
|
|
int flags = readByte();
|
|
return new YieldStatement(readExpression(),
|
|
isYieldStar: flags & YieldStatement.FlagYieldStar != 0,
|
|
isNative: flags & YieldStatement.FlagNative != 0)
|
|
..fileOffset = offset;
|
|
case Tag.VariableDeclaration:
|
|
var variable = readVariableDeclaration();
|
|
variableStack.add(variable); // Will be popped by the enclosing scope.
|
|
return variable;
|
|
case Tag.FunctionDeclaration:
|
|
int offset = readOffset();
|
|
var variable = readVariableDeclaration();
|
|
variableStack.add(variable); // Will be popped by the enclosing scope.
|
|
var function = readFunctionNode();
|
|
return new FunctionDeclaration(variable, function)..fileOffset = offset;
|
|
default:
|
|
throw fail('Invalid statement tag: $tag');
|
|
}
|
|
}
|
|
|
|
List<Catch> readCatchList() {
|
|
return new List<Catch>.generate(readUInt(), (i) => readCatch());
|
|
}
|
|
|
|
Catch readCatch() {
|
|
int variableStackHeight = variableStack.length;
|
|
var guard = readDartType();
|
|
var exception = readAndPushVariableDeclarationOption();
|
|
var stackTrace = readAndPushVariableDeclarationOption();
|
|
var body = readStatement();
|
|
variableStack.length = variableStackHeight;
|
|
return new Catch(exception, body, guard: guard, stackTrace: stackTrace);
|
|
}
|
|
|
|
Block readBlock() {
|
|
int stackHeight = variableStack.length;
|
|
var body = readStatementList();
|
|
variableStack.length = stackHeight;
|
|
return new Block(body);
|
|
}
|
|
|
|
Supertype readSupertype() {
|
|
InterfaceType type = readDartType();
|
|
return new Supertype.byReference(type.className, type.typeArguments);
|
|
}
|
|
|
|
Supertype readSupertypeOption() {
|
|
return readAndCheckOptionTag() ? readSupertype() : null;
|
|
}
|
|
|
|
List<Supertype> readSupertypeList() {
|
|
return new List<Supertype>.generate(readUInt(), (i) => readSupertype());
|
|
}
|
|
|
|
List<DartType> readDartTypeList() {
|
|
return new List<DartType>.generate(readUInt(), (i) => readDartType());
|
|
}
|
|
|
|
List<NamedType> readNamedTypeList() {
|
|
return new List<NamedType>.generate(readUInt(), (i) => readNamedType());
|
|
}
|
|
|
|
NamedType readNamedType() {
|
|
return new NamedType(readStringReference(), readDartType());
|
|
}
|
|
|
|
DartType readDartTypeOption() {
|
|
return readAndCheckOptionTag() ? readDartType() : null;
|
|
}
|
|
|
|
DartType readDartType() {
|
|
int tag = readByte();
|
|
switch (tag) {
|
|
case Tag.TypedefType:
|
|
return new TypedefType.byReference(
|
|
readTypedefReference(), readDartTypeList());
|
|
case Tag.VectorType:
|
|
return const VectorType();
|
|
case Tag.BottomType:
|
|
return const BottomType();
|
|
case Tag.InvalidType:
|
|
return const InvalidType();
|
|
case Tag.DynamicType:
|
|
return const DynamicType();
|
|
case Tag.VoidType:
|
|
return const VoidType();
|
|
case Tag.InterfaceType:
|
|
return new InterfaceType.byReference(
|
|
readClassReference(), readDartTypeList());
|
|
case Tag.SimpleInterfaceType:
|
|
return new InterfaceType.byReference(
|
|
readClassReference(), const <DartType>[]);
|
|
case Tag.FunctionType:
|
|
int typeParameterStackHeight = typeParameterStack.length;
|
|
var typeParameters = readAndPushTypeParameterList();
|
|
var requiredParameterCount = readUInt();
|
|
var totalParameterCount = readUInt();
|
|
var positional = readDartTypeList();
|
|
var named = readNamedTypeList();
|
|
assert(positional.length + named.length == totalParameterCount);
|
|
var returnType = readDartType();
|
|
typeParameterStack.length = typeParameterStackHeight;
|
|
return new FunctionType(positional, returnType,
|
|
typeParameters: typeParameters,
|
|
requiredParameterCount: requiredParameterCount,
|
|
namedParameters: named);
|
|
case Tag.SimpleFunctionType:
|
|
var positional = readDartTypeList();
|
|
var returnType = readDartType();
|
|
return new FunctionType(positional, returnType);
|
|
case Tag.TypeParameterType:
|
|
int index = readUInt();
|
|
readUInt(); // offset of the TypeParameter declaration in the binary.
|
|
var bound = readDartTypeOption();
|
|
return new TypeParameterType(typeParameterStack[index], bound);
|
|
default:
|
|
throw fail('Invalid dart type tag: $tag');
|
|
}
|
|
}
|
|
|
|
List<TypeParameter> readAndPushTypeParameterList(
|
|
[List<TypeParameter> list, TreeNode parent]) {
|
|
int length = readUInt();
|
|
if (length == 0) return list ?? <TypeParameter>[];
|
|
if (list == null) {
|
|
list = new List<TypeParameter>.generate(
|
|
length, (i) => new TypeParameter(null, null)..parent = parent);
|
|
} else if (list.length != length) {
|
|
list.length = length;
|
|
for (int i = 0; i < length; ++i) {
|
|
list[i] = new TypeParameter(null, null)..parent = parent;
|
|
}
|
|
}
|
|
typeParameterStack.addAll(list);
|
|
for (int i = 0; i < list.length; ++i) {
|
|
readTypeParameter(list[i]);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
void readTypeParameter(TypeParameter node) {
|
|
node.name = readStringOrNullIfEmpty();
|
|
node.bound = readDartType();
|
|
}
|
|
|
|
Arguments readArguments() {
|
|
var numArguments = readUInt();
|
|
var typeArguments = readDartTypeList();
|
|
var positional = readExpressionList();
|
|
var named = readNamedExpressionList();
|
|
assert(numArguments == positional.length + named.length);
|
|
return new Arguments(positional, types: typeArguments, named: named);
|
|
}
|
|
|
|
List<NamedExpression> readNamedExpressionList() {
|
|
return new List<NamedExpression>.generate(
|
|
readUInt(), (i) => readNamedExpression());
|
|
}
|
|
|
|
NamedExpression readNamedExpression() {
|
|
return new NamedExpression(readStringReference(), readExpression());
|
|
}
|
|
|
|
List<VariableDeclaration> readAndPushVariableDeclarationList() {
|
|
return new List<VariableDeclaration>.generate(
|
|
readUInt(), (i) => readAndPushVariableDeclaration());
|
|
}
|
|
|
|
VariableDeclaration readAndPushVariableDeclarationOption() {
|
|
return readAndCheckOptionTag() ? readAndPushVariableDeclaration() : null;
|
|
}
|
|
|
|
VariableDeclaration readAndPushVariableDeclaration() {
|
|
var variable = readVariableDeclaration();
|
|
variableStack.add(variable);
|
|
return variable;
|
|
}
|
|
|
|
VariableDeclaration readVariableDeclaration() {
|
|
int offset = readOffset();
|
|
int fileEqualsOffset = readOffset();
|
|
int flags = readByte();
|
|
return new VariableDeclaration(readStringOrNullIfEmpty(),
|
|
type: readDartType(),
|
|
initializer: readExpressionOption(),
|
|
isFinal: flags & 0x1 != 0,
|
|
isConst: flags & 0x2 != 0)
|
|
..fileOffset = offset
|
|
..fileEqualsOffset = fileEqualsOffset;
|
|
}
|
|
|
|
int readOffset() {
|
|
// Offset is saved as unsigned,
|
|
// but actually ranges from -1 and up (thus the -1)
|
|
return readUInt() - 1;
|
|
}
|
|
}
|