07b6ec3425
The kernel format has been designed so that one can concatenate several dill files into one file and then load it. For instance the dart function BinaryBuilder.readProgram supports this. Currently a dill file contains one or more programs. In the VM each of these programs are called either program or subprogram. Technically a dill "program" isn't necessarily a program at all (e.g. it could be missing a library). This naming snafu should probably be cleaned up at some point, but that's for another CL. When loading a dill file via BinaryBuilder.readProgram what happens is this: - Each program in the dill file ends in 4 bytes that indicates the size of the program. - Reading the input from the end one can then read the size, skip back that amount of bytes, if we have more data (i.e. there's another program), read another size and so on, and continue until we have accounted for all bytes in the input. - We then read each program from the start, and basically overwrite any library, class, procedure etc. we find. The first main reference found is the one used though. (Saying that we overwrite is not completely true, but when the library is a non-external library that's basically what happens). This CL introduces (some) support on the C++ side for the same thing. So far the C++ side could only handle single-program-dills, and trying to load anything else would probably crash the VM. The support added is this: - Assume the SDK (i.e. vm_platform.dill) is not a concatenated file (error out if it is). - For user provided input, loop over each contained program one-by-one, for each individual one behave as normal. - The way LibraryLoad is implemented (i.e. it skips if the library is already loaded) this means that it currently would behave differently than the dart version (i.e. the first one is used, not the last one). For now it is assumed that that's not a problem. - There is a possibly snafu if the same script is included several times. This could probably mostly be remedied by not creating scripts up front, but only as needed. By the "keep only one" (and fixing the above point, probably by simply loading in the opposite order, i.e. last program in the binary first) the (theoretical) problem would probably do away. Note that we will have separate string tables, canonical name tables etc per "sub program" and that there might be some duplication. The implementation was tested as indicated below, but introduces no tests. $ cat test_lib1.dart import "test_lib2.dart" as lib2; String lib1field = "lib #1 field!!"; main() { foo(); lib2.foo(); print("From lib2: ${lib2.lib2field}"); } foo() { print("Hello, Foo, from test_lib1!"); var x = 42; print(x); } $ cat test_lib2.dart String lib2field = "Lib #2 field!!!!"; foo() { print("Hello, Foo, from test_lib2!"); var y = 34; print(y); } $ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart --packages=.packages --platform=out/ReleaseX64/vm_platform.dill test_lib1.dart $ ls -lha test_lib1.dart.dill [...] 4.2M Oct 26 14:42 test_lib1.dart.dill $ dart pkg/kernel/bin/split.dart test_lib1.dart.dill Wrote test_lib1.dart.dill.part1.dill Wrote test_lib1.dart.dill.part2.dill $ ls -lha test_lib1.dart.dill.part{1,2}.dill [...] 811 Oct 26 14:42 test_lib1.dart.dill.part1.dill [...] 582 Oct 26 14:42 test_lib1.dart.dill.part2.dill $ cat test_lib1.dart.dill.part1.dill test_lib1.dart.dill.part2.dill > test_lib1.dart.dill.concat.dill $ ls -lha test_lib1.dart.dill.concat.dill [...] 1.4K Oct 26 14:44 test_lib1.dart.dill.concat.dill $ out/ReleaseX64/dart --kernel-binaries=out/ReleaseX64 --packages=.packages test_lib1.dart.dill.concat.dill Hello, Foo, from test_lib1! 42 Hello, Foo, from test_lib2! 34 From lib2: Lib #2 field!!!! Change-Id: I233a033aa3042b202dd4708908a5be3089474588 Reviewed-on: https://dart-review.googlesource.com/16820 Commit-Queue: Jens Johansen <jensj@google.com> Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
1882 lines
64 KiB
Dart
1882 lines
64 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 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<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];
|
|
}
|
|
}
|
|
|
|
String readUriReference() {
|
|
return _sourceUriTable[readUInt()];
|
|
}
|
|
|
|
String readStringReference() {
|
|
return _stringTable[readUInt()];
|
|
}
|
|
|
|
List<String> readStringReferenceList() {
|
|
return new List<String>.generate(readUInt(), (i) => readStringReference());
|
|
}
|
|
|
|
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 + 7) * 4;
|
|
|
|
// Now read the program index.
|
|
result.binaryOffsetForSourceTable = _programStartOffset + readUint32();
|
|
result.binaryOffsetForCanonicalNames = _programStartOffset + readUint32();
|
|
result.binaryOffsetForStringTable = _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);
|
|
|
|
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() {
|
|
return new List<Combinator>.generate(readUInt(), (i) => readCombinator());
|
|
}
|
|
|
|
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);
|
|
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);
|
|
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) {
|
|
return readAndCheckOptionTag() ? readFunctionNode(lazyLoadBody) : null;
|
|
}
|
|
|
|
FunctionNode readFunctionNode(bool lazyLoadBody) {
|
|
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;
|
|
|
|
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) {
|
|
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();
|
|
}
|
|
};
|
|
}
|
|
|
|
labelStackBase = oldLabelStackBase;
|
|
variableStack.length = variableStackHeight;
|
|
typeParameterStack.length = typeParameterStackHeight;
|
|
|
|
return result;
|
|
}
|
|
|
|
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(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:
|
|
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();
|
|
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 flags = readByte();
|
|
return new DirectMethodInvocation.byReference(
|
|
readExpression(), readMemberReference(), readArguments())
|
|
..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))
|
|
..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);
|
|
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(),
|
|
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>.generate(count, (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);
|
|
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() {
|
|
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();
|
|
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>.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.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() {
|
|
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();
|
|
// 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;
|
|
for (var annotation in annotations) {
|
|
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) {
|
|
final nodeOffset = _byteOffset;
|
|
final result = super.readFunctionNode(lazyLoadBody);
|
|
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);
|
|
}
|