Files
sdk/pkg/kernel/lib/binary/ast_from_binary.dart
T
Martin Kustermann d354a28cb2 [kernel] Add kernel2kernel constant evaluation, binary format as well as vm support
The introduced "constants" transformation can evaluate constant expressions.  The
original use-sites of constant expressions are replaced by a new [ConstantExpression]
node, which points to a subclass of a new [Constant] class hierarchy.  Constant
[Field]s and [VariableDeclarations]s will be removed, since all use-sites are
re-written.

The [Constant] class hierarchy is, similarly to the [DartType] class hierarchy, not
part of the AST tree (also has no parent pointer).  The constants form a
DAG (directed acyclic graph).

There is no canonicalization requirement of the [Constant] objects referenced by the
AST (via [ConstantExpression]).  Although it is beneficial to canonicalize them during
construction, since it reduces time spent in operator==/hashCode.

This CL furthermore adds support for a constant table in the binary format.  Similarly
to [String]s, we canonicalize the constants before writing the table to the binary.
The constant table entries in the binary are written in a post-order way, to ensure
easy construction on the backend side.

The text format will be augmented with a "constants { ... }" section at the end,
which lists the constants in the same order as in the binary format.

The transformation can be used by those backends who choose to do so.  It is not
enabled by default atm.  It should therefore not affect analyzer, fasta or other
components.

Change-Id: I57cd9624fedcf537ab6870db76246149647bed21
Reviewed-on: https://dart-review.googlesource.com/14382
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Kevin Millikin <kmillikin@google.com>
2017-11-16 11:08:02 +00:00

2046 lines
69 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 _ProgramIndex {
int binaryOffsetForSourceTable;
int binaryOffsetForStringTable;
int binaryOffsetForCanonicalNames;
int binaryOffsetForConstantTable;
int mainMethodReference;
List<int> libraryOffsets;
int libraryCount;
int programFileSizeInBytes;
}
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 _byteOffset = 0;
final List<String> _stringTable = <String>[];
final List<String> _sourceUriTable = <String>[];
List<Constant> _constantTable;
List<CanonicalName> _linkTable;
int _transformerFlags = 0;
Library _currentLibrary;
int _programStartOffset = 0;
// If something goes wrong, this list should indicate what library,
// class, and member was being built.
List<String> debugPath = <String>[];
bool _isReadingLibraryImplementation = false;
/// If binary contains metadata section with payloads referencing other nodes
/// such Kernel binary can't be read lazily because metadata cross references
/// will not be resolved correctly.
bool _disableLazyReading = false;
BinaryBuilder(this._bytes, [this.filename, this._disableLazyReading = false]);
fail(String message) {
throw new ParseError(message,
byteIndex: _byteOffset, filename: filename, path: debugPath.join('::'));
}
int readByte() => _bytes[_byteOffset++];
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 readUint32() {
return (readByte() << 24) |
(readByte() << 16) |
(readByte() << 8) |
readByte();
}
List<int> readByteList() {
List<int> bytes = new Uint8List(readUInt());
bytes.setRange(0, bytes.length, _bytes, _byteOffset);
_byteOffset += 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 (_byteOffset + 2 < _bytes.length &&
_bytes[_byteOffset] == 0xef &&
_bytes[_byteOffset + 1] == 0xbb &&
_bytes[_byteOffset + 2] == 0xbf) {
++numByteOrderMarks;
_byteOffset += 3;
numBytes -= 3;
}
String string = const Utf8Decoder()
.convert(_bytes, _byteOffset, _byteOffset + numBytes);
_byteOffset += numBytes;
if (numByteOrderMarks > 0) {
return '\ufeff' * numByteOrderMarks + string;
}
return string;
}
/// Read metadataMappings section from the binary. Return [true] if
/// any metadata mapping contains metadata with node references.
/// In this case we need to disable lazy loading of the binary.
bool _readMetadataSection(Program program) {
// Default reader ignores metadata section entirely.
return false;
}
/// Process any pending metadata associations. Called once the Program
/// is fully deserialized and metadata containing references to nodes can
/// be safely parsed.
void _processPendingMetadataAssociations(Program program) {
// Default reader ignores metadata section entirely.
}
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];
}
}
void readConstantTable() {
final int length = readUInt();
_constantTable = new List<Constant>(length);
for (int i = 0; i < length; i++) {
_constantTable[i] = readConstantTableEntry();
}
}
Constant readConstantTableEntry() {
final int constantTag = readByte();
switch (constantTag) {
case ConstantTag.NullConstant:
return new NullConstant();
case ConstantTag.BoolConstant:
return new BoolConstant(readByte() == 1);
case ConstantTag.IntConstant:
return new IntConstant((readExpression() as IntLiteral).value);
case ConstantTag.DoubleConstant:
return new DoubleConstant(double.parse(readStringReference()));
case ConstantTag.StringConstant:
return new StringConstant(readStringReference());
case ConstantTag.MapConstant:
final DartType keyType = readDartType();
final DartType valueType = readDartType();
final int length = readUInt();
final List<ConstantMapEntry> entries =
new List<ConstantMapEntry>(length);
for (int i = 0; i < length; i++) {
final Constant key = readConstantReference();
final Constant value = readConstantReference();
entries[i] = new ConstantMapEntry(key, value);
}
return new MapConstant(keyType, valueType, entries);
case ConstantTag.ListConstant:
final DartType typeArgument = readDartType();
final int length = readUInt();
final List<Constant> entries = new List<Constant>(length);
for (int i = 0; i < length; i++) {
entries[i] = readConstantReference();
}
return new ListConstant(typeArgument, entries);
case ConstantTag.InstanceConstant:
final Reference classReference = readClassReference();
final int typeArgumentCount = readUInt();
final List<DartType> typeArguments =
new List<DartType>(typeArgumentCount);
for (int i = 0; i < typeArgumentCount; i++) {
typeArguments[i] = readDartType();
}
final int fieldValueCount = readUInt();
final Map<Reference, Constant> fieldValues = <Reference, Constant>{};
for (int i = 0; i < fieldValueCount; i++) {
final Reference fieldRef =
readCanonicalNameReference().getReference();
final Constant constant = readConstantReference();
fieldValues[fieldRef] = constant;
}
return new InstanceConstant(classReference, typeArguments, fieldValues);
case ConstantTag.TearOffConstant:
final Reference reference = readCanonicalNameReference().getReference();
return new TearOffConstant.byReference(reference);
case ConstantTag.TypeLiteralConstant:
final DartType type = readDartType();
return new TypeLiteralConstant(type);
}
throw 'Invalid constant tag $constantTag';
}
Constant readConstantReference() {
final int index = readUInt();
Constant constant = _constantTable[index];
assert(constant != null);
return constant;
}
String readUriReference() {
return _sourceUriTable[readUInt()];
}
String readStringReference() {
return _stringTable[readUInt()];
}
List<String> readStringReferenceList() {
int length = readUInt();
List<String> result = new List<String>(length);
for (int i = 0; i < length; ++i) {
result[i] = readStringReference();
}
return result;
}
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(int index), TreeNode parent) {
var length = readUInt();
list.length = length;
for (int i = 0; i < length; ++i) {
TreeNode object = buildObject(i);
list[i] = object..parent = parent;
}
}
void _fillNonTreeNodeList(List<Node> list, Node buildObject()) {
var length = readUInt();
list.length = length;
for (int i = 0; i < length; ++i) {
Node object = buildObject();
list[i] = object;
}
}
void _skipNodeList(Node skipObject()) {
var length = readUInt();
for (int i = 0; i < length; ++i) {
skipObject();
}
}
/// 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(int index), 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(i);
// 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);
}
}
List<int> _indexPrograms() {
int savedByteOffset = _byteOffset;
_byteOffset = _bytes.length - 4;
List<int> index = <int>[];
while (_byteOffset > 0) {
int size = readUint32();
int start = _byteOffset - size;
if (start < 0) throw "Invalid program file: Indicated size is invalid.";
index.add(size);
_byteOffset = start - 4;
}
_byteOffset = savedByteOffset;
return new List.from(index.reversed);
}
/// 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) {
List<int> programFileSizes = _indexPrograms();
if (programFileSizes.length > 1) {
_disableLazyReading = true;
}
int programFileIndex = 0;
while (_byteOffset < _bytes.length) {
_readOneProgram(program, programFileSizes[programFileIndex]);
++programFileIndex;
}
}
/// 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) {
List<int> programFileSizes = _indexPrograms();
if (programFileSizes.isEmpty) throw "Invalid program data.";
_readOneProgram(program, programFileSizes[0]);
if (_byteOffset < _bytes.length) {
if (_byteOffset + 3 < _bytes.length) {
int magic = readUint32();
if (magic == Tag.ProgramFile) {
throw 'Concatenated program file given when a single program '
'was expected.';
}
}
throw 'Unrecognized bytes following program data';
}
}
_ProgramIndex _readProgramIndex(int programFileSize) {
int savedByteIndex = _byteOffset;
_ProgramIndex result = new _ProgramIndex();
// There are two fields: file size and library count.
_byteOffset = _programStartOffset + programFileSize - (2) * 4;
result.libraryCount = readUint32();
// Library offsets are used for start and end offsets, so there is one extra
// element that this the end offset of the last library
result.libraryOffsets = new List<int>(result.libraryCount + 1);
result.programFileSizeInBytes = readUint32();
if (result.programFileSizeInBytes != programFileSize) {
throw 'Malformed binary: This program files program index indicates that'
' the filesize should be $programFileSize but other program indexes'
' has indicated that the size should be '
'${result.programFileSizeInBytes}.';
}
// Skip to the start of the index.
// There are these fields: file size, library count, library count + 1
// offsets, main reference, string table offset, canonical name offset and
// source table offset. That's 6 fields + number of libraries.
_byteOffset -= (result.libraryCount + 8) * 4;
// Now read the program index.
result.binaryOffsetForSourceTable = _programStartOffset + readUint32();
result.binaryOffsetForCanonicalNames = _programStartOffset + readUint32();
result.binaryOffsetForStringTable = _programStartOffset + readUint32();
result.binaryOffsetForConstantTable = _programStartOffset + readUint32();
result.mainMethodReference = readUint32();
for (int i = 0; i < result.libraryCount + 1; ++i) {
result.libraryOffsets[i] = _programStartOffset + readUint32();
}
_byteOffset = savedByteIndex;
return result;
}
void _readOneProgram(Program program, int programFileSize) {
_programStartOffset = _byteOffset;
final int magic = readUint32();
if (magic != Tag.ProgramFile) {
throw fail('This is not a binary dart file. '
'Magic number was: ${magic.toRadixString(16)}');
}
final int formatVersion = readUint32();
if (formatVersion != Tag.BinaryFormatVersion) {
throw fail('Invalid kernel binary format version '
'(found ${formatVersion}, expected ${Tag.BinaryFormatVersion})');
}
// Read program index from the end of this ProgramFiles serialized data.
_ProgramIndex index = _readProgramIndex(programFileSize);
_byteOffset = index.binaryOffsetForStringTable;
readStringTable(_stringTable);
_byteOffset = index.binaryOffsetForCanonicalNames;
readLinkTable(program.root);
_byteOffset = index.binaryOffsetForStringTable;
_disableLazyReading = _readMetadataSection(program) || _disableLazyReading;
_byteOffset = index.binaryOffsetForSourceTable;
Map<String, Source> uriToSource = readUriToSource();
program.uriToSource.addAll(uriToSource);
_byteOffset = index.binaryOffsetForConstantTable;
readConstantTable();
int numberOfLibraries = index.libraryCount;
for (int i = 0; i < numberOfLibraries; ++i) {
_byteOffset = index.libraryOffsets[i];
readLibrary(program, index.libraryOffsets[i + 1]);
}
var mainMethod =
getMemberReferenceFromInt(index.mainMethodReference, allowNull: true);
program.mainMethodName ??= mainMethod;
_processPendingMetadataAssociations(program);
_byteOffset = _programStartOffset + programFileSize;
}
Map<String, Source> readUriToSource() {
int length = readUint32();
// Read data.
_sourceUriTable.length = length;
Map<String, Source> uriToSource = <String, Source>{};
for (int i = 0; i < length; ++i) {
List<int> uriBytes = readByteList();
String uri = const Utf8Decoder().convert(uriBytes);
_sourceUriTable[i] = uri;
List<int> sourceCode = readByteList();
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);
}
// Read index.
for (int i = 0; i < length; ++i) {
readUint32();
}
return uriToSource;
}
CanonicalName readCanonicalNameReference() {
var index = readUInt();
if (index == 0) return null;
return _linkTable[index - 1];
}
CanonicalName getCanonicalNameReferenceFromInt(int index) {
if (index == 0) return null;
return _linkTable[index - 1];
}
Reference readLibraryReference() {
return readCanonicalNameReference().getReference();
}
LibraryDependency readLibraryDependencyReference() {
int index = readUInt();
return _currentLibrary.dependencies[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 getMemberReferenceFromInt(int index, {bool allowNull: false}) {
var name = getCanonicalNameReferenceFromInt(index);
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 endOffset) {
// Read index.
int savedByteOffset = _byteOffset;
// There is a field for the procedure count.
_byteOffset = endOffset - (1) * 4;
int procedureCount = readUint32();
List<int> procedureOffsets = new List<int>(procedureCount + 1);
// There is a field for the procedure count, that number + 1 (for the end)
// offsets, and then the class count (i.e. procedure count + 3 fields).
_byteOffset = endOffset - (procedureCount + 3) * 4;
int classCount = readUint32();
for (int i = 0; i < procedureCount + 1; i++) {
procedureOffsets[i] = _programStartOffset + readUint32();
}
List<int> classOffsets = new List<int>(classCount + 1);
// There is a field for the procedure count, that number + 1 (for the end)
// offsets, then the class count and that number + 1 (for the end) offsets.
// (i.e. procedure count + class count + 4 fields).
_byteOffset = endOffset - (procedureCount + classCount + 4) * 4;
for (int i = 0; i < classCount + 1; i++) {
classOffsets[i] = _programStartOffset + readUint32();
}
_byteOffset = savedByteOffset;
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;
}
debugPath.add(library.name ?? library.importUri?.toString() ?? 'library');
if (shouldWriteData) {
_fillTreeNodeList(
library.annotations, (index) => readExpression(), library);
} else {
_skipNodeList(readExpression);
}
_readLibraryDependencies(library);
_readAdditionalExports(library);
_readLibraryParts(library);
_mergeNamedNodeList(library.typedefs, (index) => readTypedef(), library);
_mergeNamedNodeList(library.classes, (index) {
_byteOffset = classOffsets[index];
return readClass(classOffsets[index + 1]);
}, library);
_byteOffset = classOffsets.last;
_mergeNamedNodeList(library.fields, (index) => readField(), library);
_mergeNamedNodeList(library.procedures, (index) {
_byteOffset = procedureOffsets[index];
return readProcedure(procedureOffsets[index + 1]);
}, library);
_byteOffset = procedureOffsets.last;
debugPath.removeLast();
_currentLibrary = null;
return library;
}
void _readLibraryDependencies(Library library) {
int length = readUInt();
if (library.isExternal) {
assert(length == 0);
return;
}
library.dependencies.length = length;
for (int i = 0; i < length; ++i) {
library.dependencies[i] = readLibraryDependency(library);
}
}
LibraryDependency readLibraryDependency(Library library) {
var fileOffset = readOffset();
var flags = readByte();
var annotations = readExpressionList();
var targetLibrary = readLibraryReference();
var prefixName = readStringOrNullIfEmpty();
var names = readCombinatorList();
return new LibraryDependency.byReference(
flags, annotations, targetLibrary, prefixName, names)
..fileOffset = fileOffset
..parent = library;
}
void _readAdditionalExports(Library library) {
int numExportedReference = readUInt();
if (numExportedReference != 0) {
for (int i = 0; i < numExportedReference; i++) {
CanonicalName exportedName = readCanonicalNameReference();
Reference reference = exportedName.getReference();
library.additionalExports.add(reference);
}
}
}
Combinator readCombinator() {
var isShow = readByte() == 1;
var names = readStringReferenceList();
return new Combinator(isShow, names);
}
List<Combinator> readCombinatorList() {
int length = readUInt();
List<Combinator> result = new List<Combinator>(length);
for (int i = 0; i < length; ++i) {
result[i] = readCombinator();
}
return result;
}
void _readLibraryParts(Library library) {
int length = readUInt();
library.parts.length = length;
for (int i = 0; i < length; ++i) {
library.parts[i] = readLibraryPart(library);
}
}
LibraryPart readLibraryPart(Library library) {
var annotations = readExpressionList();
var fileUri = readStringOrNullIfEmpty();
return new LibraryPart(annotations, fileUri)..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();
node.annotations = readAnnotationList(node);
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 endOffset) {
int tag = readByte();
assert(tag == Tag.Class);
// Read index.
int savedByteOffset = _byteOffset;
// There is a field for the procedure count.
_byteOffset = endOffset - (1) * 4;
int procedureCount = readUint32();
List<int> procedureOffsets = new List<int>(procedureCount + 1);
// There is a field for the procedure count, that number + 1 (for the end)
// offsets (i.e. procedure count + 2 fields).
_byteOffset = endOffset - (procedureCount + 2) * 4;
for (int i = 0; i < procedureCount + 1; i++) {
procedureOffsets[i] = _programStartOffset + readUint32();
}
_byteOffset = savedByteOffset;
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();
node.fileEndOffset = readOffset();
int flags = readByte();
node.isAbstract = flags & 0x1 != 0;
node.isEnum = flags & 0x2 != 0;
node.isSyntheticMixinImplementation = flags & 0x4 != 0;
int levelIndex = (flags >> 3) & 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();
if (shouldWriteData) {
_fillNonTreeNodeList(node.implementedTypes, readSupertype);
} else {
_skipNodeList(readSupertype);
}
_mergeNamedNodeList(node.fields, (index) => readField(), node);
_mergeNamedNodeList(node.constructors, (index) => readConstructor(), node);
_mergeNamedNodeList(node.procedures, (index) {
_byteOffset = procedureOffsets[index];
return readProcedure(procedureOffsets[index + 1]);
}, node);
_byteOffset = procedureOffsets.last;
typeParameterStack.length = 0;
debugPath.removeLast();
if (shouldWriteData) {
node.name = name;
node.fileUri = fileUri;
node.annotations = annotations;
node.supertype = supertype;
node.mixedInType = mixedInType;
}
_byteOffset = endOffset;
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();
int flags2 = 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.flags2 = flags2;
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(false, -1);
pushVariableDeclarations(function.positionalParameters);
pushVariableDeclarations(function.namedParameters);
if (shouldWriteData) {
_fillTreeNodeList(node.initializers, (index) => readInitializer(), node);
} else {
_skipNodeList(readInitializer);
}
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 endOffset) {
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');
int functionNodeSize = endOffset - _byteOffset;
// Read small factories up front. Postpone everything else.
bool readFunctionNodeNow =
(kind == ProcedureKind.Factory && functionNodeSize <= 50) ||
_disableLazyReading;
var function = readFunctionNodeOption(!readFunctionNodeNow, endOffset);
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;
function?.parent = node;
node.transformerFlags = transformerFlags;
}
_byteOffset = endOffset;
return node;
}
Initializer readInitializer() {
int tag = readByte();
bool isSynthetic = readByte() == 1;
switch (tag) {
case Tag.InvalidInitializer:
return new InvalidInitializer();
case Tag.FieldInitializer:
var reference = readMemberReference();
var value = readExpression();
return new FieldInitializer.byReference(reference, value)
..isSynthetic = isSynthetic;
case Tag.SuperInitializer:
var reference = readMemberReference();
var arguments = readArguments();
return new SuperInitializer.byReference(reference, arguments)
..isSynthetic = isSynthetic;
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(bool lazyLoadBody, int outerEndOffset) {
return readAndCheckOptionTag()
? readFunctionNode(lazyLoadBody, outerEndOffset)
: null;
}
FunctionNode readFunctionNode(bool lazyLoadBody, int outerEndOffset) {
int tag = readByte();
assert(tag == Tag.FunctionNode);
int offset = readOffset();
int endOffset = readOffset();
AsyncMarker asyncMarker = AsyncMarker.values[readByte()];
AsyncMarker dartAsyncMarker = AsyncMarker.values[readByte()];
int typeParameterStackHeight = typeParameterStack.length;
var typeParameters = readAndPushTypeParameterList();
readUInt(); // total parameter count.
var requiredParameterCount = readUInt();
int variableStackHeight = variableStack.length;
var positional = readAndPushVariableDeclarationList();
var named = readAndPushVariableDeclarationList();
var returnType = readDartType();
int oldLabelStackBase = labelStackBase;
if (lazyLoadBody && outerEndOffset > 0) {
lazyLoadBody = outerEndOffset - _byteOffset >
2; // e.g. outline has Tag.Something and Tag.EmptyStatement
}
var body;
if (!lazyLoadBody) {
labelStackBase = labelStack.length;
body = readStatementOption();
}
FunctionNode result = new FunctionNode(body,
typeParameters: typeParameters,
requiredParameterCount: requiredParameterCount,
positionalParameters: positional,
namedParameters: named,
returnType: returnType,
asyncMarker: asyncMarker,
dartAsyncMarker: dartAsyncMarker)
..fileOffset = offset
..fileEndOffset = endOffset;
if (lazyLoadBody) {
_setLazyLoadFunction(result, oldLabelStackBase, variableStackHeight,
typeParameterStackHeight);
}
labelStackBase = oldLabelStackBase;
variableStack.length = variableStackHeight;
typeParameterStack.length = typeParameterStackHeight;
return result;
}
void _setLazyLoadFunction(FunctionNode result, int oldLabelStackBase,
int variableStackHeight, int typeParameterStackHeight) {
final int savedByteOffset = _byteOffset;
final int programStartOffset = _programStartOffset;
final List<TypeParameter> typeParameters = typeParameterStack.toList();
final List<VariableDeclaration> variables = variableStack.toList();
result.lazyBuilder = () {
_byteOffset = savedByteOffset;
typeParameterStack.clear();
typeParameterStack.addAll(typeParameters);
variableStack.clear();
variableStack.addAll(variables);
_programStartOffset = programStartOffset;
result.body = readStatementOption();
result.body?.parent = result;
labelStackBase = oldLabelStackBase;
variableStack.length = variableStackHeight;
typeParameterStack.length = typeParameterStackHeight;
if (result.parent is Procedure) {
Procedure parent = result.parent;
parent.transformerFlags |= getAndResetTransformerFlags();
}
};
}
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() {
int length = readUInt();
List<Expression> result = new List<Expression>(length);
for (int i = 0; i < length; ++i) {
result[i] = readExpression();
}
return result;
}
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(readLibraryDependencyReference());
case Tag.CheckLibraryIsLoaded:
return new CheckLibraryIsLoaded(readLibraryDependencyReference());
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();
int flags = readByte();
return new PropertyGet.byReference(
readExpression(), readName(), readMemberReference(allowNull: true))
..fileOffset = offset
..flags = flags;
case Tag.PropertySet:
int offset = readOffset();
return new PropertySet.byReference(readExpression(), readName(),
readExpression(), readMemberReference(allowNull: true))
..fileOffset = offset;
case Tag.SuperPropertyGet:
int offset = readOffset();
addTransformerFlag(TransformerFlag.superCalls);
return new SuperPropertyGet.byReference(
readName(), readMemberReference(allowNull: true))
..fileOffset = offset;
case Tag.SuperPropertySet:
int offset = readOffset();
addTransformerFlag(TransformerFlag.superCalls);
return new SuperPropertySet.byReference(
readName(), readExpression(), readMemberReference(allowNull: true))
..fileOffset = offset;
case Tag.DirectPropertyGet:
int offset = readOffset();
int flags = readByte();
return new DirectPropertyGet.byReference(
readExpression(), readMemberReference())
..fileOffset = offset
..flags = flags;
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();
int flags = readByte();
return new MethodInvocation.byReference(readExpression(), readName(),
readArguments(), readMemberReference(allowNull: true))
..fileOffset = offset
..flags = flags;
case Tag.SuperMethodInvocation:
int offset = readOffset();
addTransformerFlag(TransformerFlag.superCalls);
return new SuperMethodInvocation.byReference(
readName(), readArguments(), readMemberReference(allowNull: true))
..fileOffset = offset;
case Tag.DirectMethodInvocation:
int offset = readOffset();
int flags = readByte();
return new DirectMethodInvocation.byReference(
readExpression(), readMemberReference(), readArguments())
..fileOffset = offset
..flags = flags;
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();
int flags = readByte();
return new AsExpression(readExpression(), readDartType())
..fileOffset = offset
..flags = flags;
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:
int offset = readOffset();
return new FunctionExpression(readFunctionNode(false, -1))
..fileOffset = offset;
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();
var typeArgs = readDartTypeList();
return new ClosureCreation.byReference(
topLevelFunctionReference, contextVector, functionType, typeArgs);
case Tag.ConstantExpression:
return new ConstantExpression(readConstantReference());
default:
throw fail('Invalid expression tag: $tag');
}
}
List<MapEntry> readMapEntryList() {
int length = readUInt();
List<MapEntry> result = new List<MapEntry>(length);
for (int i = 0; i < length; ++i) {
result[i] = readMapEntry();
}
return result;
}
MapEntry readMapEntry() {
return new MapEntry(readExpression(), readExpression());
}
List<Statement> readStatementList() {
int length = readUInt();
List<Statement> result = <Statement>[];
result.length = length;
for (int i = 0; i < length; ++i) {
result[i] = readStatement();
}
return result;
}
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(),
conditionStartOffset: readOffset(),
conditionEndOffset: readOffset(),
message: 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:
var offset = readOffset();
return new WhileStatement(readExpression(), readStatement())
..fileOffset = offset;
case Tag.DoStatement:
var offset = readOffset();
return new DoStatement(readStatement(), readExpression())
..fileOffset = offset;
case Tag.ForStatement:
int variableStackHeight = variableStack.length;
var offset = readOffset();
var variables = readAndPushVariableDeclarationList();
var condition = readExpressionOption();
var updates = readExpressionList();
var body = readStatement();
variableStack.length = variableStackHeight;
return new ForStatement(variables, condition, updates, body)
..fileOffset = offset;
case Tag.ForInStatement:
case Tag.AsyncForInStatement:
bool isAsync = tag == Tag.AsyncForInStatement;
int variableStackHeight = variableStack.length;
var offset = readOffset();
var bodyOffset = readOffset();
var variable = readAndPushVariableDeclaration();
var iterable = readExpression();
var body = readStatement();
variableStack.length = variableStackHeight;
return new ForInStatement(variable, iterable, body, isAsync: isAsync)
..fileOffset = offset
..bodyOffset = bodyOffset;
case Tag.SwitchStatement:
var offset = readOffset();
var expression = readExpression();
int count = readUInt();
List<SwitchCase> cases = new List<SwitchCase>(count);
for (int i = 0; i < count; ++i) {
cases[i] = new SwitchCase.empty();
}
switchCaseStack.addAll(cases);
for (int i = 0; i < cases.length; ++i) {
readSwitchCaseInto(cases[i]);
}
switchCaseStack.length -= count;
return new SwitchStatement(expression, cases)..fileOffset = offset;
case Tag.ContinueSwitchStatement:
int offset = readOffset();
int index = readUInt();
return new ContinueSwitchStatement(switchCaseStack[index])
..fileOffset = offset;
case Tag.IfStatement:
int offset = readOffset();
return new IfStatement(
readExpression(), readStatement(), readStatementOrNullIfEmpty())
..fileOffset = offset;
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(false, -1);
return new FunctionDeclaration(variable, function)..fileOffset = offset;
default:
throw fail('Invalid statement tag: $tag');
}
}
void readSwitchCaseInto(SwitchCase caseNode) {
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;
}
List<Catch> readCatchList() {
int length = readUInt();
List<Catch> result = new List<Catch>(length);
for (int i = 0; i < length; ++i) {
result[i] = readCatch();
}
return result;
}
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() {
int length = readUInt();
List<Supertype> result = new List<Supertype>(length);
for (int i = 0; i < length; ++i) {
result[i] = readSupertype();
}
return result;
}
List<DartType> readDartTypeList() {
int length = readUInt();
List<DartType> result = new List<DartType>(length);
for (int i = 0; i < length; ++i) {
result[i] = readDartType();
}
return result;
}
List<NamedType> readNamedTypeList() {
int length = readUInt();
List<NamedType> result = new List<NamedType>(length);
for (int i = 0; i < length; ++i) {
result[i] = readNamedType();
}
return result;
}
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();
var positionalNames = readStringReferenceList();
var typedefReference = readTypedefReference();
assert(positional.length + named.length == totalParameterCount);
var returnType = readDartType();
typeParameterStack.length = typeParameterStackHeight;
return new FunctionType(positional, returnType,
typeParameters: typeParameters,
requiredParameterCount: requiredParameterCount,
namedParameters: named,
positionalParameterNames: positionalNames,
typedefReference: typedefReference);
case Tag.SimpleFunctionType:
var positional = readDartTypeList();
var positionalNames = readStringReferenceList();
var returnType = readDartType();
return new FunctionType(positional, returnType,
positionalParameterNames: positionalNames);
case Tag.TypeParameterType:
int index = readUInt();
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>(length);
for (int i = 0; i < length; ++i) {
list[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.flags = readByte();
node.annotations = readAnnotationList(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() {
int length = readUInt();
List<NamedExpression> result = new List<NamedExpression>(length);
for (int i = 0; i < length; ++i) {
result[i] = readNamedExpression();
}
return result;
}
NamedExpression readNamedExpression() {
return new NamedExpression(readStringReference(), readExpression());
}
List<VariableDeclaration> readAndPushVariableDeclarationList() {
int length = readUInt();
List<VariableDeclaration> result = new List<VariableDeclaration>(length);
for (int i = 0; i < length; ++i) {
result[i] = readAndPushVariableDeclaration();
}
return result;
}
VariableDeclaration readAndPushVariableDeclarationOption() {
return readAndCheckOptionTag() ? readAndPushVariableDeclaration() : null;
}
VariableDeclaration readAndPushVariableDeclaration() {
var variable = readVariableDeclaration();
variableStack.add(variable);
return variable;
}
VariableDeclaration readVariableDeclaration() {
int offset = readOffset();
int fileEqualsOffset = readOffset();
// The [VariableDeclaration] instance is not created at this point yet,
// so `null` is temporarily set as the parent of the annotation nodes.
var annotations = readAnnotationList(null);
int flags = readByte();
var node = new VariableDeclaration(readStringOrNullIfEmpty(),
type: readDartType(), initializer: readExpressionOption(), flags: flags)
..fileOffset = offset
..fileEqualsOffset = fileEqualsOffset;
if (annotations.isNotEmpty) {
for (int i = 0; i < annotations.length; ++i) {
var annotation = annotations[i];
annotation.parent = node;
}
}
return node;
}
int readOffset() {
// Offset is saved as unsigned,
// but actually ranges from -1 and up (thus the -1)
return readUInt() - 1;
}
}
class BinaryBuilderWithMetadata extends BinaryBuilder implements BinarySource {
/// List of metadata subsections that have corresponding [MetadataRepository]
/// and are awaiting to be parsed and attached to nodes.
List<_MetadataSubsection> _subsections;
/// Current mapping from node references to nodes used by readNodeReference.
/// Note: each metadata subsection has its own mapping.
List<Node> _referencedNodes;
BinaryBuilderWithMetadata(bytes, [filename]) : super(bytes, filename);
@override
bool _readMetadataSection(Program program) {
bool containsNodeReferences = false;
// At the beginning of this function _byteOffset points right past
// metadataMappings to string table.
// Read the length of metadataMappings.
_byteOffset -= 4;
final subSectionCount = readUint32();
int endOffset = _byteOffset - 4; // End offset of the current subsection.
for (var i = 0; i < subSectionCount; i++) {
// RList<UInt32> nodeReferences
_byteOffset = endOffset - 4;
final referencesLength = readUint32();
final referencesStart = (endOffset - 4) - 4 * referencesLength;
// RList<Pair<UInt32, UInt32>> nodeOffsetToMetadataOffset
_byteOffset = referencesStart - 4;
final mappingLength = readUint32();
final mappingStart = (referencesStart - 4) - 4 * 2 * mappingLength;
_byteOffset = mappingStart - 4;
// UInt32 tag (fixed size StringReference)
final tag = _stringTable[readUint32()];
final repository = program.metadata[tag];
if (repository != null) {
// Read nodeReferences (if any).
Map<int, int> offsetToReferenceId;
List<Node> referencedNodes;
if (referencesLength > 0) {
offsetToReferenceId = <int, int>{};
_byteOffset = referencesStart;
for (var j = 0; j < referencesLength; j++) {
final nodeOffset = readUint32();
offsetToReferenceId[nodeOffset] = j;
}
referencedNodes = new List<Node>(referencesLength);
containsNodeReferences = true;
}
// Read nodeOffsetToMetadataOffset mapping.
final mapping = <int, int>{};
_byteOffset = mappingStart;
for (var j = 0; j < mappingLength; j++) {
final nodeOffset = readUint32();
final metadataOffset = readUint32();
mapping[nodeOffset] = metadataOffset;
}
_subsections ??= <_MetadataSubsection>[];
_subsections.add(new _MetadataSubsection(
repository, mapping, offsetToReferenceId, referencedNodes));
}
// Start of the subsection and the end of the previous one.
endOffset = mappingStart - 4;
}
return containsNodeReferences;
}
@override
void _processPendingMetadataAssociations(Program program) {
if (_subsections == null) {
return;
}
_associateMetadata(program, _programStartOffset);
for (var subsection in _subsections) {
if (subsection.pending == null) {
continue;
}
_referencedNodes = subsection.referencedNodes;
for (var i = 0; i < subsection.pending.length; i += 2) {
final Node node = subsection.pending[i];
final int metadataOffset = subsection.pending[i + 1];
subsection.repository.mapping[node] =
_readMetadata(subsection.repository, metadataOffset);
}
}
}
Object _readMetadata(MetadataRepository repository, int offset) {
final int savedOffset = _byteOffset;
_byteOffset = offset;
final metadata = repository.readFromBinary(this);
_byteOffset = savedOffset;
return metadata;
}
Node _associateMetadata(Node node, int nodeOffset) {
if (_subsections == null) {
return node;
}
for (var subsection in _subsections) {
// First check if there is any metadata associated with this node.
final metadataOffset = subsection.mapping[nodeOffset];
if (metadataOffset != null) {
if (subsection.nodeOffsetToReferenceId == null) {
// This subsection does not contain any references to nodes from
// inside the payload. In this case we can deserialize metadata
// eagerly.
subsection.repository.mapping[node] =
_readMetadata(subsection.repository, metadataOffset);
} else {
// Metadata payload might contain references to nodes that
// are not yet deserialized. Postpone association of metadata
// with this node.
subsection.pending ??= <Object>[];
subsection.pending..add(node)..add(metadataOffset);
}
}
// Check if this node is referenced from this section and update
// referencedNodes array if that is the case.
if (subsection.nodeOffsetToReferenceId != null) {
final id = subsection.nodeOffsetToReferenceId[nodeOffset];
if (id != null) {
subsection.referencedNodes[id] = node;
}
}
}
return node;
}
@override
DartType readDartType() {
final nodeOffset = _byteOffset;
final result = super.readDartType();
return _associateMetadata(result, nodeOffset);
}
@override
Library readLibrary(Program program, int endOffset) {
final nodeOffset = _byteOffset;
final result = super.readLibrary(program, endOffset);
return _associateMetadata(result, nodeOffset);
}
@override
Typedef readTypedef() {
final nodeOffset = _byteOffset;
final result = super.readTypedef();
return _associateMetadata(result, nodeOffset);
}
@override
Class readClass(int endOffset) {
final nodeOffset = _byteOffset;
final result = super.readClass(endOffset);
return _associateMetadata(result, nodeOffset);
}
@override
Field readField() {
final nodeOffset = _byteOffset;
final result = super.readField();
return _associateMetadata(result, nodeOffset);
}
@override
Constructor readConstructor() {
final nodeOffset = _byteOffset;
final result = super.readConstructor();
return _associateMetadata(result, nodeOffset);
}
@override
Procedure readProcedure(int endOffset) {
final nodeOffset = _byteOffset;
final result = super.readProcedure(endOffset);
return _associateMetadata(result, nodeOffset);
}
@override
Initializer readInitializer() {
final nodeOffset = _byteOffset;
final result = super.readInitializer();
return _associateMetadata(result, nodeOffset);
}
@override
FunctionNode readFunctionNode(bool lazyLoadBody, int outerEndOffset) {
final nodeOffset = _byteOffset;
final result = super.readFunctionNode(lazyLoadBody, outerEndOffset);
return _associateMetadata(result, nodeOffset);
}
@override
Expression readExpression() {
final nodeOffset = _byteOffset;
final result = super.readExpression();
return _associateMetadata(result, nodeOffset);
}
@override
Arguments readArguments() {
final nodeOffset = _byteOffset;
final result = super.readArguments();
return _associateMetadata(result, nodeOffset);
}
@override
NamedExpression readNamedExpression() {
final nodeOffset = _byteOffset;
final result = super.readNamedExpression();
return _associateMetadata(result, nodeOffset);
}
@override
VariableDeclaration readVariableDeclaration() {
final nodeOffset = _byteOffset;
final result = super.readVariableDeclaration();
return _associateMetadata(result, nodeOffset);
}
@override
Statement readStatement() {
final nodeOffset = _byteOffset;
final result = super.readStatement();
return _associateMetadata(result, nodeOffset);
}
@override
Combinator readCombinator() {
final nodeOffset = _byteOffset;
final result = super.readCombinator();
return _associateMetadata(result, nodeOffset);
}
@override
LibraryDependency readLibraryDependency(Library library) {
final nodeOffset = _byteOffset;
final result = super.readLibraryDependency(library);
return _associateMetadata(result, nodeOffset);
}
@override
LibraryPart readLibraryPart(Library library) {
final nodeOffset = _byteOffset;
final result = super.readLibraryPart(library);
return _associateMetadata(result, nodeOffset);
}
@override
void readSwitchCaseInto(SwitchCase caseNode) {
_associateMetadata(caseNode, _byteOffset);
super.readSwitchCaseInto(caseNode);
}
@override
void readTypeParameter(TypeParameter param) {
_associateMetadata(param, _byteOffset);
super.readTypeParameter(param);
}
@override
Supertype readSupertype() {
final nodeOffset = _byteOffset;
InterfaceType type = super.readDartType();
return _associateMetadata(
new Supertype.byReference(type.className, type.typeArguments),
nodeOffset);
}
@override
Name readName() {
final nodeOffset = _byteOffset;
final result = super.readName();
return _associateMetadata(result, nodeOffset);
}
@override
int get currentOffset => _byteOffset;
@override
List<int> get bytes => _bytes;
@override
Node readNodeReference() {
final id = readUInt();
return id == 0 ? null : _referencedNodes[id - 1];
}
}
/// Deserialized MetadataMapping corresponding to the given metadata repository.
class _MetadataSubsection {
/// [MetadataRepository] that can read this subsection.
final MetadataRepository repository;
/// Deserialized mapping from node offsets to metadata offsets.
final Map<int, int> mapping;
/// Deserialized mapping from node offset to node reference ids.
final Map<int, int> nodeOffsetToReferenceId;
/// Array mapping node reference ids to corresponding nodes.
/// Will be gradually filled as nodes are deserialized.
final List<Node> referencedNodes;
/// A list of pairs (Node, int metadataOffset) that describes pending
/// metadata associations which will be processed once all nodes
/// are parsed.
List<Object> pending;
_MetadataSubsection(this.repository, this.mapping,
this.nodeOffsetToReferenceId, this.referencedNodes);
}