f09edd1d32
Add an incremental serializer that can be used to avoid re-serializing the same thing again and again. It does this by grouping libraries into Components / bundles, serializing them individually and using the concatenated dill feature to output the wanted data --- just potentially faster if we had the data in cache. Note that the serialized output might contain *more* than the input given if we cached the wanted data into a bigger bundle. The output will always be "closed" though, i.e. if the stuff that is included that is too much added a new dependency, that dependency will be included as well. This should generally make it safe, although one can imagine situations where it could pull in lots of dependencies that it wouldn't otherwise have. It is being driven by the incremental compiler which makes sure to invalidate the cache when the data changes. Except for situations where the libraries are changed externally after being serialized, but before being serialized again, where one then wants the updated library serialized, the feature should be safe. Change-Id: I2a504abe6dbb68434c3b04abff13480ef72a6a6e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/120786 Reviewed-by: Johnni Winther <johnniwinther@google.com> Commit-Queue: Jens Johansen <jensj@google.com>
2587 lines
88 KiB
Dart
2587 lines
88 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:core' hide MapEntry;
|
|
import 'dart:convert';
|
|
import 'dart:developer';
|
|
import 'dart:typed_data';
|
|
|
|
import '../ast.dart';
|
|
import '../src/bounds_checks.dart' show computeVariance;
|
|
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 InvalidKernelVersionError {
|
|
final int version;
|
|
|
|
InvalidKernelVersionError(this.version);
|
|
|
|
String toString() {
|
|
return 'Unexpected Kernel version ${version} '
|
|
'(expected ${Tag.BinaryFormatVersion}).';
|
|
}
|
|
}
|
|
|
|
class CanonicalNameError {
|
|
final String message;
|
|
|
|
CanonicalNameError(this.message);
|
|
}
|
|
|
|
class CanonicalNameSdkError extends CanonicalNameError {
|
|
CanonicalNameSdkError(String message) : super(message);
|
|
}
|
|
|
|
class _ComponentIndex {
|
|
static const numberOfFixedFields = 9;
|
|
|
|
int binaryOffsetForSourceTable;
|
|
int binaryOffsetForCanonicalNames;
|
|
int binaryOffsetForMetadataPayloads;
|
|
int binaryOffsetForMetadataMappings;
|
|
int binaryOffsetForStringTable;
|
|
int binaryOffsetForConstantTable;
|
|
int mainMethodReference;
|
|
List<int> libraryOffsets;
|
|
int libraryCount;
|
|
int componentFileSizeInBytes;
|
|
}
|
|
|
|
class SubComponentView {
|
|
final List<Library> libraries;
|
|
final int componentStartOffset;
|
|
final int componentFileSize;
|
|
|
|
SubComponentView(
|
|
this.libraries, this.componentStartOffset, this.componentFileSize);
|
|
}
|
|
|
|
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<Uri> _sourceUriTable = <Uri>[];
|
|
Map<int, Constant> _constantTable = <int, Constant>{};
|
|
List<CanonicalName> _linkTable;
|
|
int _transformerFlags = 0;
|
|
Library _currentLibrary;
|
|
int _componentStartOffset = 0;
|
|
|
|
// If something goes wrong, this list should indicate what library,
|
|
// class, and member was being built.
|
|
List<String> debugPath = <String>[];
|
|
|
|
bool _isReadingLibraryImplementation = false;
|
|
final bool alwaysCreateNewNamedNodes;
|
|
|
|
/// 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;
|
|
|
|
/// 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 _disableLazyClassReading = false;
|
|
|
|
/// Note that [disableLazyClassReading] is incompatible
|
|
/// with checkCanonicalNames on readComponent.
|
|
BinaryBuilder(this._bytes,
|
|
{this.filename,
|
|
bool disableLazyReading = false,
|
|
bool disableLazyClassReading = false,
|
|
bool alwaysCreateNewNamedNodes})
|
|
: _disableLazyReading = disableLazyReading,
|
|
_disableLazyClassReading =
|
|
disableLazyReading || disableLazyClassReading,
|
|
this.alwaysCreateNewNamedNodes = alwaysCreateNewNamedNodes ?? false;
|
|
|
|
fail(String message) {
|
|
throw ParseError(message,
|
|
byteIndex: _byteOffset, filename: filename, path: debugPath.join('::'));
|
|
}
|
|
|
|
int get byteOffset => _byteOffset;
|
|
|
|
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();
|
|
}
|
|
|
|
final Float64List _doubleBuffer = new Float64List(1);
|
|
Uint8List _doubleBufferUint8;
|
|
|
|
double readDouble() {
|
|
_doubleBufferUint8 ??= _doubleBuffer.buffer.asUint8List();
|
|
_doubleBufferUint8[0] = readByte();
|
|
_doubleBufferUint8[1] = readByte();
|
|
_doubleBufferUint8[2] = readByte();
|
|
_doubleBufferUint8[3] = readByte();
|
|
_doubleBufferUint8[4] = readByte();
|
|
_doubleBufferUint8[5] = readByte();
|
|
_doubleBufferUint8[6] = readByte();
|
|
_doubleBufferUint8[7] = readByte();
|
|
return _doubleBuffer[0];
|
|
}
|
|
|
|
List<int> readBytes(int length) {
|
|
List<int> bytes = new Uint8List(length);
|
|
bytes.setRange(0, bytes.length, _bytes, _byteOffset);
|
|
_byteOffset += bytes.length;
|
|
return bytes;
|
|
}
|
|
|
|
List<int> readByteList() {
|
|
return readBytes(readUInt());
|
|
}
|
|
|
|
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.
|
|
void _readMetadataMappings(
|
|
Component component, int binaryOffsetForMetadataPayloads) {
|
|
// Default reader ignores metadata section entirely.
|
|
}
|
|
|
|
/// Reads metadata for the given [node].
|
|
Node _associateMetadata(Node node, int nodeOffset) {
|
|
// Default reader ignores metadata section entirely.
|
|
return node;
|
|
}
|
|
|
|
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();
|
|
final int startOffset = byteOffset;
|
|
for (int i = 0; i < length; i++) {
|
|
_constantTable[byteOffset - startOffset] = 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(readDouble());
|
|
case ConstantTag.StringConstant:
|
|
return new StringConstant(readStringReference());
|
|
case ConstantTag.SymbolConstant:
|
|
Reference libraryReference = readLibraryReference(allowNull: true);
|
|
return new SymbolConstant(readStringReference(), libraryReference);
|
|
case ConstantTag.MapConstant:
|
|
final DartType keyType = readDartType();
|
|
final DartType valueType = readDartType();
|
|
final int length = readUInt();
|
|
final List<ConstantMapEntry> entries =
|
|
new List<ConstantMapEntry>.filled(length, null, growable: true);
|
|
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>.filled(length, null, growable: true);
|
|
for (int i = 0; i < length; i++) {
|
|
entries[i] = readConstantReference();
|
|
}
|
|
return new ListConstant(typeArgument, entries);
|
|
case ConstantTag.SetConstant:
|
|
final DartType typeArgument = readDartType();
|
|
final int length = readUInt();
|
|
final List<Constant> entries =
|
|
new List<Constant>.filled(length, null, growable: true);
|
|
for (int i = 0; i < length; i++) {
|
|
entries[i] = readConstantReference();
|
|
}
|
|
return new SetConstant(typeArgument, entries);
|
|
case ConstantTag.InstanceConstant:
|
|
final Reference classReference = readClassReference();
|
|
final int typeArgumentCount = readUInt();
|
|
final List<DartType> typeArguments =
|
|
new List<DartType>.filled(typeArgumentCount, null, growable: true);
|
|
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.PartialInstantiationConstant:
|
|
final tearOffConstant = readConstantReference() as TearOffConstant;
|
|
final int length = readUInt();
|
|
final List<DartType> types = new List<DartType>(length);
|
|
for (int i = 0; i < length; i++) {
|
|
types[i] = readDartType();
|
|
}
|
|
return new PartialInstantiationConstant(tearOffConstant, types);
|
|
case ConstantTag.TearOffConstant:
|
|
final Reference reference = readCanonicalNameReference().getReference();
|
|
return new TearOffConstant.byReference(reference);
|
|
case ConstantTag.TypeLiteralConstant:
|
|
final DartType type = readDartType();
|
|
return new TypeLiteralConstant(type);
|
|
case ConstantTag.UnevaluatedConstant:
|
|
final Expression expression = readExpression();
|
|
return new UnevaluatedConstant(expression);
|
|
}
|
|
|
|
throw fail('unexpected constant tag: $constantTag');
|
|
}
|
|
|
|
Constant readConstantReference() {
|
|
final int offset = readUInt();
|
|
Constant constant = _constantTable[offset];
|
|
assert(constant != null);
|
|
return constant;
|
|
}
|
|
|
|
Uri readUriReference() {
|
|
return _sourceUriTable[readUInt()];
|
|
}
|
|
|
|
String readStringReference() {
|
|
return _stringTable[readUInt()];
|
|
}
|
|
|
|
List<String> readStringReferenceList() {
|
|
int length = readUInt();
|
|
List<String> result = new List<String>.filled(length, null, growable: true);
|
|
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('unexpected option tag: $tag');
|
|
}
|
|
}
|
|
|
|
List<Expression> readAnnotationList(TreeNode parent) {
|
|
int length = readUInt();
|
|
if (length == 0) return const <Expression>[];
|
|
List<Expression> list =
|
|
new List<Expression>.filled(length, null, growable: true);
|
|
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> _indexComponents() {
|
|
_checkEmptyInput();
|
|
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 fail("indicated size does not match file size");
|
|
}
|
|
index.add(size);
|
|
_byteOffset = start - 4;
|
|
}
|
|
_byteOffset = savedByteOffset;
|
|
return new List.from(index.reversed);
|
|
}
|
|
|
|
void _checkEmptyInput() {
|
|
if (_bytes.length == 0) throw new StateError("Empty input given.");
|
|
}
|
|
|
|
/// Deserializes a kernel component and stores it in [component].
|
|
///
|
|
/// When linking with a non-empty component, canonical names must have been
|
|
/// computed ahead of time.
|
|
///
|
|
/// The input bytes may contain multiple files concatenated.
|
|
///
|
|
/// If [createView] is true, returns a list of [SubComponentView] - one for
|
|
/// each concatenated dill - each of which knowing where in the combined dill
|
|
/// it came from. If [createView] is false null will be returned.
|
|
List<SubComponentView> readComponent(Component component,
|
|
{bool checkCanonicalNames: false, bool createView: false}) {
|
|
return Timeline.timeSync<List<SubComponentView>>(
|
|
"BinaryBuilder.readComponent", () {
|
|
_checkEmptyInput();
|
|
|
|
// Check that we have a .dill file and it has the correct version before we
|
|
// start decoding it. Otherwise we will fail for cryptic reasons.
|
|
int offset = _byteOffset;
|
|
int magic = readUint32();
|
|
if (magic != Tag.ComponentFile) {
|
|
throw ArgumentError('Not a .dill file (wrong magic number).');
|
|
}
|
|
int version = readUint32();
|
|
if (version != Tag.BinaryFormatVersion) {
|
|
throw InvalidKernelVersionError(version);
|
|
}
|
|
_byteOffset = offset;
|
|
List<int> componentFileSizes = _indexComponents();
|
|
if (componentFileSizes.length > 1) {
|
|
_disableLazyReading = true;
|
|
_disableLazyClassReading = true;
|
|
}
|
|
int componentFileIndex = 0;
|
|
List<SubComponentView> views;
|
|
if (createView) {
|
|
views = new List<SubComponentView>();
|
|
}
|
|
while (_byteOffset < _bytes.length) {
|
|
SubComponentView view = _readOneComponent(
|
|
component, componentFileSizes[componentFileIndex],
|
|
createView: createView);
|
|
if (createView) {
|
|
views.add(view);
|
|
}
|
|
++componentFileIndex;
|
|
}
|
|
|
|
if (checkCanonicalNames) {
|
|
_checkCanonicalNameChildren(component.root);
|
|
}
|
|
return views;
|
|
});
|
|
}
|
|
|
|
/// Deserializes the source and stores it in [component].
|
|
///
|
|
/// The input bytes may contain multiple files concatenated.
|
|
void readComponentSource(Component component) {
|
|
List<int> componentFileSizes = _indexComponents();
|
|
if (componentFileSizes.length > 1) {
|
|
_disableLazyReading = true;
|
|
_disableLazyClassReading = true;
|
|
}
|
|
int componentFileIndex = 0;
|
|
while (_byteOffset < _bytes.length) {
|
|
_readOneComponentSource(
|
|
component, componentFileSizes[componentFileIndex]);
|
|
++componentFileIndex;
|
|
}
|
|
}
|
|
|
|
/// Reads a single component file from the input and loads it into [component],
|
|
/// overwriting and reusing any existing data in the component.
|
|
///
|
|
/// When linking with a non-empty component, 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 readSingleFileComponent(Component component,
|
|
{bool checkCanonicalNames: false}) {
|
|
List<int> componentFileSizes = _indexComponents();
|
|
if (componentFileSizes.isEmpty) throw fail("invalid component data");
|
|
_readOneComponent(component, componentFileSizes[0]);
|
|
if (_byteOffset < _bytes.length) {
|
|
if (_byteOffset + 3 < _bytes.length) {
|
|
int magic = readUint32();
|
|
if (magic == Tag.ComponentFile) {
|
|
throw 'Concatenated component file given when a single component '
|
|
'was expected.';
|
|
}
|
|
}
|
|
throw 'Unrecognized bytes following component data';
|
|
}
|
|
|
|
if (checkCanonicalNames) {
|
|
_checkCanonicalNameChildren(component.root);
|
|
}
|
|
}
|
|
|
|
void _checkCanonicalNameChildren(CanonicalName parent) {
|
|
Iterable<CanonicalName> parentChildren = parent.childrenOrNull;
|
|
if (parentChildren != null) {
|
|
for (CanonicalName child in parentChildren) {
|
|
if (child.name != '@methods' &&
|
|
child.name != '@typedefs' &&
|
|
child.name != '@fields' &&
|
|
child.name != '@getters' &&
|
|
child.name != '@setters' &&
|
|
child.name != '@factories' &&
|
|
child.name != '@constructors') {
|
|
bool checkReferenceNode = true;
|
|
if (child.reference == null) {
|
|
// OK for "if private: URI of library" part of "Qualified name"...
|
|
Iterable<CanonicalName> children = child.childrenOrNull;
|
|
if (parent.parent != null &&
|
|
children != null &&
|
|
children.isNotEmpty &&
|
|
children.first.name.startsWith("_")) {
|
|
// OK then.
|
|
checkReferenceNode = false;
|
|
} else {
|
|
throw buildCanonicalNameError(
|
|
"Null reference (${child.name}) ($child).", child);
|
|
}
|
|
}
|
|
if (checkReferenceNode) {
|
|
if (child.reference.canonicalName != child) {
|
|
throw new CanonicalNameError(
|
|
"Canonical name and reference doesn't agree.");
|
|
}
|
|
if (child.reference.node == null) {
|
|
throw buildCanonicalNameError(
|
|
"Reference is null (${child.name}) ($child).", child);
|
|
}
|
|
}
|
|
}
|
|
_checkCanonicalNameChildren(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
CanonicalNameError buildCanonicalNameError(
|
|
String message, CanonicalName problemNode) {
|
|
// Special-case missing sdk entries as that is probably a change to the
|
|
// platform - that's something we might want to react differently to.
|
|
String libraryUri = problemNode?.nonRootTop?.name ?? "";
|
|
if (libraryUri.startsWith("dart:")) {
|
|
return new CanonicalNameSdkError(message);
|
|
}
|
|
return new CanonicalNameError(message);
|
|
}
|
|
|
|
_ComponentIndex _readComponentIndex(int componentFileSize) {
|
|
int savedByteIndex = _byteOffset;
|
|
|
|
_ComponentIndex result = new _ComponentIndex();
|
|
|
|
// There are two fields: file size and library count.
|
|
_byteOffset = _componentStartOffset + componentFileSize - (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.componentFileSizeInBytes = readUint32();
|
|
if (result.componentFileSizeInBytes != componentFileSize) {
|
|
throw "Malformed binary: This component file's component index indicates that"
|
|
" the file size should be $componentFileSize but other component indexes"
|
|
" has indicated that the size should be "
|
|
"${result.componentFileSizeInBytes}.";
|
|
}
|
|
|
|
// Skip to the start of the index.
|
|
_byteOffset -=
|
|
((result.libraryCount + 1) + _ComponentIndex.numberOfFixedFields) * 4;
|
|
|
|
// Now read the component index.
|
|
result.binaryOffsetForSourceTable = _componentStartOffset + readUint32();
|
|
result.binaryOffsetForCanonicalNames = _componentStartOffset + readUint32();
|
|
result.binaryOffsetForMetadataPayloads =
|
|
_componentStartOffset + readUint32();
|
|
result.binaryOffsetForMetadataMappings =
|
|
_componentStartOffset + readUint32();
|
|
result.binaryOffsetForStringTable = _componentStartOffset + readUint32();
|
|
result.binaryOffsetForConstantTable = _componentStartOffset + readUint32();
|
|
result.mainMethodReference = readUint32();
|
|
for (int i = 0; i < result.libraryCount + 1; ++i) {
|
|
result.libraryOffsets[i] = _componentStartOffset + readUint32();
|
|
}
|
|
|
|
_byteOffset = savedByteIndex;
|
|
|
|
return result;
|
|
}
|
|
|
|
void _readOneComponentSource(Component component, int componentFileSize) {
|
|
_componentStartOffset = _byteOffset;
|
|
|
|
final int magic = readUint32();
|
|
if (magic != Tag.ComponentFile) {
|
|
throw ArgumentError('Not a .dill file (wrong magic number).');
|
|
}
|
|
|
|
final int formatVersion = readUint32();
|
|
if (formatVersion != Tag.BinaryFormatVersion) {
|
|
throw InvalidKernelVersionError(formatVersion);
|
|
}
|
|
|
|
// Read component index from the end of this ComponentFiles serialized data.
|
|
_ComponentIndex index = _readComponentIndex(componentFileSize);
|
|
|
|
_byteOffset = index.binaryOffsetForSourceTable;
|
|
Map<Uri, Source> uriToSource = readUriToSource();
|
|
_mergeUriToSource(component.uriToSource, uriToSource);
|
|
|
|
_byteOffset = _componentStartOffset + componentFileSize;
|
|
}
|
|
|
|
SubComponentView _readOneComponent(Component component, int componentFileSize,
|
|
{bool createView: false}) {
|
|
_componentStartOffset = _byteOffset;
|
|
|
|
final int magic = readUint32();
|
|
if (magic != Tag.ComponentFile) {
|
|
throw ArgumentError('Not a .dill file (wrong magic number).');
|
|
}
|
|
|
|
final int formatVersion = readUint32();
|
|
if (formatVersion != Tag.BinaryFormatVersion) {
|
|
throw InvalidKernelVersionError(formatVersion);
|
|
}
|
|
|
|
List<String> problemsAsJson = readListOfStrings();
|
|
if (problemsAsJson != null) {
|
|
component.problemsAsJson ??= <String>[];
|
|
component.problemsAsJson.addAll(problemsAsJson);
|
|
}
|
|
|
|
// Read component index from the end of this ComponentFiles serialized data.
|
|
_ComponentIndex index = _readComponentIndex(componentFileSize);
|
|
|
|
_byteOffset = index.binaryOffsetForStringTable;
|
|
readStringTable(_stringTable);
|
|
|
|
_byteOffset = index.binaryOffsetForCanonicalNames;
|
|
readLinkTable(component.root);
|
|
|
|
// TODO(alexmarkov): reverse metadata mappings and read forwards
|
|
_byteOffset = index.binaryOffsetForStringTable; // Read backwards.
|
|
_readMetadataMappings(component, index.binaryOffsetForMetadataPayloads);
|
|
|
|
_associateMetadata(component, _componentStartOffset);
|
|
|
|
_byteOffset = index.binaryOffsetForSourceTable;
|
|
Map<Uri, Source> uriToSource = readUriToSource();
|
|
_mergeUriToSource(component.uriToSource, uriToSource);
|
|
|
|
_byteOffset = index.binaryOffsetForConstantTable;
|
|
readConstantTable();
|
|
|
|
int numberOfLibraries = index.libraryCount;
|
|
|
|
SubComponentView result;
|
|
if (createView) {
|
|
result = new SubComponentView(new List<Library>(numberOfLibraries),
|
|
_componentStartOffset, componentFileSize);
|
|
}
|
|
|
|
for (int i = 0; i < numberOfLibraries; ++i) {
|
|
_byteOffset = index.libraryOffsets[i];
|
|
Library library = readLibrary(component, index.libraryOffsets[i + 1]);
|
|
if (createView) {
|
|
result.libraries[i] = library;
|
|
}
|
|
}
|
|
|
|
var mainMethod =
|
|
getMemberReferenceFromInt(index.mainMethodReference, allowNull: true);
|
|
component.mainMethodName ??= mainMethod;
|
|
|
|
_byteOffset = _componentStartOffset + componentFileSize;
|
|
|
|
assert(typeParameterStack.isEmpty);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// Read a list of strings. If the list is empty, [null] is returned.
|
|
List<String> readListOfStrings() {
|
|
int length = readUInt();
|
|
if (length == 0) return null;
|
|
List<String> strings =
|
|
new List<String>.filled(length, null, growable: true);
|
|
for (int i = 0; i < length; i++) {
|
|
String s = const Utf8Decoder().convert(readByteList());
|
|
strings[i] = s;
|
|
}
|
|
return strings;
|
|
}
|
|
|
|
Map<Uri, Source> readUriToSource() {
|
|
int length = readUint32();
|
|
|
|
// Read data.
|
|
_sourceUriTable.length = length;
|
|
Map<Uri, Source> uriToSource = <Uri, Source>{};
|
|
for (int i = 0; i < length; ++i) {
|
|
List<int> uriBytes = readByteList();
|
|
Uri uri = uriBytes.isEmpty
|
|
? null
|
|
: Uri.parse(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;
|
|
}
|
|
List<int> importUriBytes = readByteList();
|
|
Uri importUri = importUriBytes.isEmpty
|
|
? null
|
|
: Uri.parse(const Utf8Decoder().convert(importUriBytes));
|
|
uriToSource[uri] = new Source(lineStarts, sourceCode, importUri, uri);
|
|
}
|
|
|
|
// Read index.
|
|
for (int i = 0; i < length; ++i) {
|
|
readUint32();
|
|
}
|
|
return uriToSource;
|
|
}
|
|
|
|
// Add everything from [src] into [dst], but don't overwrite a non-empty
|
|
// source with an empty source. Empty sources may be introduced by
|
|
// synthetic, copy-down implementations such as mixin applications or
|
|
// noSuchMethod forwarders.
|
|
void _mergeUriToSource(Map<Uri, Source> dst, Map<Uri, Source> src) {
|
|
if (dst.isEmpty) {
|
|
// Fast path for the common case of one component per binary.
|
|
dst.addAll(src);
|
|
} else {
|
|
src.forEach((Uri key, Source value) {
|
|
if (value.source.isNotEmpty || !dst.containsKey(key)) {
|
|
dst[key] = value;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
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({bool allowNull: false}) {
|
|
CanonicalName canonicalName = readCanonicalNameReference();
|
|
if (canonicalName != null) return canonicalName.getReference();
|
|
if (allowNull) return null;
|
|
throw 'Expected a library reference to be valid but was `null`.';
|
|
}
|
|
|
|
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(Component component, 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] = _componentStartOffset + 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] = _componentStartOffset + readUint32();
|
|
}
|
|
_byteOffset = savedByteOffset;
|
|
|
|
int flags = readByte();
|
|
bool isExternal = (flags & Library.ExternalFlag) != 0;
|
|
_isReadingLibraryImplementation = !isExternal;
|
|
|
|
int languageVersionMajor = readUInt();
|
|
int languageVersionMinor = readUInt();
|
|
|
|
var canonicalName = readCanonicalNameReference();
|
|
Reference reference = canonicalName.getReference();
|
|
Library library = reference.node;
|
|
if (alwaysCreateNewNamedNodes) {
|
|
library = null;
|
|
}
|
|
bool shouldWriteData = library == null || _isReadingLibraryImplementation;
|
|
if (library == null) {
|
|
library =
|
|
new Library(Uri.parse(canonicalName.name), reference: reference);
|
|
component.libraries.add(library..parent = component);
|
|
}
|
|
_currentLibrary = library;
|
|
String name = readStringOrNullIfEmpty();
|
|
|
|
// TODO(jensj): We currently save (almost the same) uri twice.
|
|
Uri fileUri = readUriReference();
|
|
|
|
List<String> problemsAsJson = readListOfStrings();
|
|
|
|
if (shouldWriteData) {
|
|
library.flags = flags;
|
|
library.setLanguageVersion(languageVersionMajor, languageVersionMinor);
|
|
library.name = name;
|
|
library.fileUri = fileUri;
|
|
library.problemsAsJson = problemsAsJson;
|
|
}
|
|
|
|
assert(() {
|
|
debugPath.add(library.name ?? library.importUri?.toString() ?? 'library');
|
|
return true;
|
|
}());
|
|
|
|
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.extensions, (index) {
|
|
return readExtension();
|
|
}, library);
|
|
|
|
_mergeNamedNodeList(library.fields, (index) => readField(), library);
|
|
_mergeNamedNodeList(library.procedures, (index) {
|
|
_byteOffset = procedureOffsets[index];
|
|
return readProcedure(procedureOffsets[index + 1]);
|
|
}, library);
|
|
_byteOffset = procedureOffsets.last;
|
|
|
|
assert(((_) => true)(debugPath.removeLast()));
|
|
_currentLibrary = null;
|
|
return library;
|
|
}
|
|
|
|
void _readLibraryDependencies(Library library) {
|
|
int length = readUInt();
|
|
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(allowNull: true);
|
|
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) {
|
|
library.additionalExports.clear();
|
|
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>.filled(length, null, growable: true);
|
|
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) {
|
|
List<Expression> annotations = readExpressionList();
|
|
String partUri = readStringReference();
|
|
return new LibraryPart(annotations, partUri)..parent = library;
|
|
}
|
|
|
|
Typedef readTypedef() {
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Typedef node = reference.node;
|
|
if (alwaysCreateNewNamedNodes) {
|
|
node = null;
|
|
}
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Typedef(null, null, reference: reference);
|
|
}
|
|
Uri fileUri = readUriReference();
|
|
int fileOffset = readOffset();
|
|
String name = readStringReference();
|
|
node.annotations = readAnnotationList(node);
|
|
readAndPushTypeParameterList(node.typeParameters, node);
|
|
var type = readDartType();
|
|
readAndPushTypeParameterList(node.typeParametersOfFunctionType, node);
|
|
node.positionalParameters.clear();
|
|
node.positionalParameters.addAll(readAndPushVariableDeclarationList());
|
|
node.namedParameters.clear();
|
|
node.namedParameters.addAll(readAndPushVariableDeclarationList());
|
|
typeParameterStack.length = 0;
|
|
variableStack.length = 0;
|
|
for (int i = 0; i < node.typeParameters.length; ++i) {
|
|
node.typeParameters[i].variance =
|
|
computeVariance(node.typeParameters[i], type);
|
|
}
|
|
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] = _componentStartOffset + readUint32();
|
|
}
|
|
_byteOffset = savedByteOffset;
|
|
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Class node = reference.node;
|
|
if (alwaysCreateNewNamedNodes) {
|
|
node = null;
|
|
}
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Class(reference: reference)
|
|
..level = ClassLevel.Temporary
|
|
..dirty = false;
|
|
}
|
|
|
|
var fileUri = readUriReference();
|
|
node.startFileOffset = readOffset();
|
|
node.fileOffset = readOffset();
|
|
node.fileEndOffset = readOffset();
|
|
int flags = readByte();
|
|
node.flags = flags & ~Class.LevelMask;
|
|
int levelIndex = flags & Class.LevelMask;
|
|
var level = ClassLevel.values[levelIndex + 1];
|
|
if (level.index >= node.level.index) {
|
|
node.level = level;
|
|
}
|
|
var name = readStringOrNullIfEmpty();
|
|
var annotations = readAnnotationList(node);
|
|
assert(() {
|
|
debugPath.add(node.name ?? 'normal-class');
|
|
return true;
|
|
}());
|
|
|
|
assert(typeParameterStack.length == 0);
|
|
|
|
readAndPushTypeParameterList(node.typeParameters, node);
|
|
var supertype = readSupertypeOption();
|
|
var mixedInType = readSupertypeOption();
|
|
if (shouldWriteData) {
|
|
_fillNonTreeNodeList(node.implementedTypes, readSupertype);
|
|
} else {
|
|
_skipNodeList(readSupertype);
|
|
}
|
|
if (_disableLazyClassReading) {
|
|
readClassPartialContent(node, procedureOffsets);
|
|
} else {
|
|
_setLazyLoadClass(node, procedureOffsets);
|
|
}
|
|
|
|
typeParameterStack.length = 0;
|
|
assert(debugPath.removeLast() != null);
|
|
if (shouldWriteData) {
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
node.annotations = annotations;
|
|
node.supertype = supertype;
|
|
node.mixedInType = mixedInType;
|
|
}
|
|
|
|
_byteOffset = endOffset;
|
|
|
|
return node;
|
|
}
|
|
|
|
Extension readExtension() {
|
|
int tag = readByte();
|
|
assert(tag == Tag.Extension);
|
|
|
|
CanonicalName canonicalName = readCanonicalNameReference();
|
|
Reference reference = canonicalName.getReference();
|
|
Extension node = reference.node;
|
|
if (alwaysCreateNewNamedNodes) {
|
|
node = null;
|
|
}
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Extension(reference: reference);
|
|
}
|
|
|
|
String name = readStringOrNullIfEmpty();
|
|
assert(() {
|
|
debugPath.add(node.name ?? 'extension');
|
|
return true;
|
|
}());
|
|
|
|
Uri fileUri = readUriReference();
|
|
node.fileOffset = readOffset();
|
|
|
|
readAndPushTypeParameterList(node.typeParameters, node);
|
|
DartType onType = readDartType();
|
|
typeParameterStack.length = 0;
|
|
|
|
if (shouldWriteData) {
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
node.onType = onType;
|
|
}
|
|
|
|
int length = readUInt();
|
|
for (int i = 0; i < length; i++) {
|
|
Name name = readName();
|
|
int kind = readByte();
|
|
int flags = readByte();
|
|
CanonicalName canonicalName = readCanonicalNameReference();
|
|
if (shouldWriteData) {
|
|
node.members.add(new ExtensionMemberDescriptor(
|
|
name: name,
|
|
kind: ExtensionMemberKind.values[kind],
|
|
member: canonicalName.getReference())
|
|
..flags = flags);
|
|
}
|
|
}
|
|
return node;
|
|
}
|
|
|
|
/// Reads the partial content of a class, namely fields, procedures,
|
|
/// constructors and redirecting factory constructors.
|
|
void readClassPartialContent(Class node, List<int> procedureOffsets) {
|
|
_mergeNamedNodeList(node.fieldsInternal, (index) => readField(), node);
|
|
_mergeNamedNodeList(
|
|
node.constructorsInternal, (index) => readConstructor(), node);
|
|
|
|
_mergeNamedNodeList(node.proceduresInternal, (index) {
|
|
_byteOffset = procedureOffsets[index];
|
|
return readProcedure(procedureOffsets[index + 1]);
|
|
}, node);
|
|
_byteOffset = procedureOffsets.last;
|
|
_mergeNamedNodeList(node.redirectingFactoryConstructorsInternal,
|
|
(index) => readRedirectingFactoryConstructor(), node);
|
|
}
|
|
|
|
/// Set the lazyBuilder on the class so it can be lazy loaded in the future.
|
|
void _setLazyLoadClass(Class node, List<int> procedureOffsets) {
|
|
final int savedByteOffset = _byteOffset;
|
|
final int componentStartOffset = _componentStartOffset;
|
|
final Library currentLibrary = _currentLibrary;
|
|
node.lazyBuilder = () {
|
|
_byteOffset = savedByteOffset;
|
|
_currentLibrary = currentLibrary;
|
|
assert(typeParameterStack.isEmpty);
|
|
_componentStartOffset = componentStartOffset;
|
|
typeParameterStack.addAll(node.typeParameters);
|
|
|
|
readClassPartialContent(node, procedureOffsets);
|
|
typeParameterStack.length = 0;
|
|
};
|
|
}
|
|
|
|
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;
|
|
if (alwaysCreateNewNamedNodes) {
|
|
node = null;
|
|
}
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Field(null, reference: reference);
|
|
}
|
|
var fileUri = readUriReference();
|
|
int fileOffset = readOffset();
|
|
int fileEndOffset = readOffset();
|
|
int flags = readUInt();
|
|
var name = readName();
|
|
var annotations = readAnnotationList(node);
|
|
assert(() {
|
|
debugPath.add(node.name?.name ?? 'field');
|
|
return true;
|
|
}());
|
|
var type = readDartType();
|
|
var initializer = readExpressionOption();
|
|
int transformerFlags = getAndResetTransformerFlags();
|
|
assert(((_) => true)(debugPath.removeLast()));
|
|
if (shouldWriteData) {
|
|
node.fileOffset = fileOffset;
|
|
node.fileEndOffset = fileEndOffset;
|
|
node.flags = flags;
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
node.annotations = annotations;
|
|
node.type = type;
|
|
node.initializer = initializer;
|
|
node.initializer?.parent = node;
|
|
node.transformerFlags = transformerFlags;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
Constructor readConstructor() {
|
|
int tag = readByte();
|
|
assert(tag == Tag.Constructor);
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Constructor node = reference.node;
|
|
if (alwaysCreateNewNamedNodes) {
|
|
node = null;
|
|
}
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Constructor(null, reference: reference);
|
|
}
|
|
var fileUri = readUriReference();
|
|
var startFileOffset = readOffset();
|
|
var fileOffset = readOffset();
|
|
var fileEndOffset = readOffset();
|
|
var flags = readByte();
|
|
var name = readName();
|
|
var annotations = readAnnotationList(node);
|
|
assert(() {
|
|
debugPath.add(node.name?.name ?? 'constructor');
|
|
return true;
|
|
}());
|
|
var function = readFunctionNode();
|
|
pushVariableDeclarations(function.positionalParameters);
|
|
pushVariableDeclarations(function.namedParameters);
|
|
if (shouldWriteData) {
|
|
_fillTreeNodeList(node.initializers, (index) => readInitializer(), node);
|
|
} else {
|
|
_skipNodeList(readInitializer);
|
|
}
|
|
variableStack.length = 0;
|
|
var transformerFlags = getAndResetTransformerFlags();
|
|
assert(((_) => true)(debugPath.removeLast()));
|
|
if (shouldWriteData) {
|
|
node.startFileOffset = startFileOffset;
|
|
node.fileOffset = fileOffset;
|
|
node.fileEndOffset = fileEndOffset;
|
|
node.flags = flags;
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
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;
|
|
if (alwaysCreateNewNamedNodes) {
|
|
node = null;
|
|
}
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Procedure(null, null, null, reference: reference);
|
|
}
|
|
var fileUri = readUriReference();
|
|
var startFileOffset = readOffset();
|
|
var fileOffset = readOffset();
|
|
var fileEndOffset = readOffset();
|
|
int kindIndex = readByte();
|
|
var kind = ProcedureKind.values[kindIndex];
|
|
var flags = readUInt();
|
|
var name = readName();
|
|
var annotations = readAnnotationList(node);
|
|
assert(() {
|
|
debugPath.add(node.name?.name ?? 'procedure');
|
|
return true;
|
|
}());
|
|
int functionNodeSize = endOffset - _byteOffset;
|
|
// Read small factories up front. Postpone everything else.
|
|
bool readFunctionNodeNow =
|
|
(kind == ProcedureKind.Factory && functionNodeSize <= 50) ||
|
|
_disableLazyReading;
|
|
var forwardingStubSuperTargetReference =
|
|
readMemberReference(allowNull: true);
|
|
var forwardingStubInterfaceTargetReference =
|
|
readMemberReference(allowNull: true);
|
|
var function = readFunctionNodeOption(!readFunctionNodeNow, endOffset);
|
|
var transformerFlags = getAndResetTransformerFlags();
|
|
assert(((_) => true)(debugPath.removeLast()));
|
|
if (shouldWriteData) {
|
|
node.startFileOffset = startFileOffset;
|
|
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.setTransformerFlagsWithoutLazyLoading(transformerFlags);
|
|
node.forwardingStubSuperTargetReference =
|
|
forwardingStubSuperTargetReference;
|
|
node.forwardingStubInterfaceTargetReference =
|
|
forwardingStubInterfaceTargetReference;
|
|
|
|
assert((node.forwardingStubSuperTargetReference != null) ||
|
|
!(node.isForwardingStub && node.function.body != null));
|
|
}
|
|
_byteOffset = endOffset;
|
|
return node;
|
|
}
|
|
|
|
RedirectingFactoryConstructor readRedirectingFactoryConstructor() {
|
|
int tag = readByte();
|
|
assert(tag == Tag.RedirectingFactoryConstructor);
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
RedirectingFactoryConstructor node = reference.node;
|
|
if (alwaysCreateNewNamedNodes) {
|
|
node = null;
|
|
}
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new RedirectingFactoryConstructor(null, reference: reference);
|
|
}
|
|
var fileUri = readUriReference();
|
|
var fileOffset = readOffset();
|
|
var fileEndOffset = readOffset();
|
|
var flags = readByte();
|
|
var name = readName();
|
|
var annotations = readAnnotationList(node);
|
|
assert(() {
|
|
debugPath.add(node.name?.name ?? 'redirecting-factory-constructor');
|
|
return true;
|
|
}());
|
|
var targetReference = readMemberReference();
|
|
var typeArguments = readDartTypeList();
|
|
int typeParameterStackHeight = typeParameterStack.length;
|
|
var typeParameters = readAndPushTypeParameterList();
|
|
readUInt(); // Total parameter count.
|
|
var requiredParameterCount = readUInt();
|
|
int variableStackHeight = variableStack.length;
|
|
var positional = readAndPushVariableDeclarationList();
|
|
var named = readAndPushVariableDeclarationList();
|
|
variableStack.length = variableStackHeight;
|
|
typeParameterStack.length = typeParameterStackHeight;
|
|
debugPath.removeLast();
|
|
if (shouldWriteData) {
|
|
node.fileOffset = fileOffset;
|
|
node.fileEndOffset = fileEndOffset;
|
|
node.flags = flags;
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
node.annotations = annotations;
|
|
node.targetReference = targetReference;
|
|
node.typeArguments.addAll(typeArguments);
|
|
node.typeParameters = typeParameters;
|
|
node.requiredParameterCount = requiredParameterCount;
|
|
node.positionalParameters = positional;
|
|
node.namedParameters = named;
|
|
}
|
|
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:
|
|
int offset = readOffset();
|
|
var reference = readMemberReference();
|
|
var arguments = readArguments();
|
|
return new SuperInitializer.byReference(reference, arguments)
|
|
..isSynthetic = isSynthetic
|
|
..fileOffset = offset;
|
|
case Tag.RedirectingInitializer:
|
|
int offset = readOffset();
|
|
return new RedirectingInitializer.byReference(
|
|
readMemberReference(), readArguments())
|
|
..fileOffset = offset;
|
|
case Tag.LocalInitializer:
|
|
return new LocalInitializer(readAndPushVariableDeclaration());
|
|
case Tag.AssertInitializer:
|
|
return new AssertInitializer(readStatement());
|
|
default:
|
|
throw fail('unexpected initializer tag: $tag');
|
|
}
|
|
}
|
|
|
|
FunctionNode readFunctionNodeOption(bool lazyLoadBody, int outerEndOffset) {
|
|
return readAndCheckOptionTag()
|
|
? readFunctionNode(
|
|
lazyLoadBody: lazyLoadBody, outerEndOffset: outerEndOffset)
|
|
: null;
|
|
}
|
|
|
|
FunctionNode readFunctionNode(
|
|
{bool lazyLoadBody: false, int outerEndOffset: -1}) {
|
|
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);
|
|
}
|
|
|
|
labelStackBase = oldLabelStackBase;
|
|
variableStack.length = variableStackHeight;
|
|
typeParameterStack.length = typeParameterStackHeight;
|
|
|
|
return result;
|
|
}
|
|
|
|
void _setLazyLoadFunction(
|
|
FunctionNode result, int oldLabelStackBase, int variableStackHeight) {
|
|
final int savedByteOffset = _byteOffset;
|
|
final int componentStartOffset = _componentStartOffset;
|
|
final List<TypeParameter> typeParameters = typeParameterStack.toList();
|
|
final List<VariableDeclaration> variables = variableStack.toList();
|
|
final Library currentLibrary = _currentLibrary;
|
|
result.lazyBuilder = () {
|
|
_byteOffset = savedByteOffset;
|
|
_currentLibrary = currentLibrary;
|
|
typeParameterStack.clear();
|
|
typeParameterStack.addAll(typeParameters);
|
|
variableStack.clear();
|
|
variableStack.addAll(variables);
|
|
_componentStartOffset = componentStartOffset;
|
|
|
|
result.body = readStatementOption();
|
|
result.body?.parent = result;
|
|
labelStackBase = oldLabelStackBase;
|
|
variableStack.length = variableStackHeight;
|
|
typeParameterStack.clear();
|
|
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('unexpected variable index: $index');
|
|
}
|
|
return variableStack[index];
|
|
}
|
|
|
|
String logicalOperatorToString(int index) {
|
|
switch (index) {
|
|
case 0:
|
|
return '&&';
|
|
case 1:
|
|
return '||';
|
|
default:
|
|
throw fail('unexpected logical operator index: $index');
|
|
}
|
|
}
|
|
|
|
List<Expression> readExpressionList() {
|
|
int length = readUInt();
|
|
List<Expression> result =
|
|
new List<Expression>.filled(length, null, growable: true);
|
|
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:
|
|
int offset = readOffset();
|
|
return new InvalidExpression(readStringOrNullIfEmpty())
|
|
..fileOffset = offset;
|
|
case Tag.VariableGet:
|
|
int offset = readOffset();
|
|
readUInt(); // offset of the variable declaration in the binary.
|
|
return new VariableGet(readVariableReference(), readDartTypeOption())
|
|
..fileOffset = offset;
|
|
case Tag.SpecializedVariableGet:
|
|
int index = tagByte & Tag.SpecializedPayloadMask;
|
|
int offset = readOffset();
|
|
readUInt(); // offset of the variable declaration in the binary.
|
|
return new VariableGet(variableStack[index])..fileOffset = offset;
|
|
case Tag.VariableSet:
|
|
int offset = readOffset();
|
|
readUInt(); // offset of the variable declaration in the binary.
|
|
return new VariableSet(readVariableReference(), readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.SpecializedVariableSet:
|
|
int index = tagByte & Tag.SpecializedPayloadMask;
|
|
int offset = readOffset();
|
|
readUInt(); // offset of the variable declaration in the binary.
|
|
return new VariableSet(variableStack[index], readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.PropertyGet:
|
|
int offset = readOffset();
|
|
return new PropertyGet.byReference(
|
|
readExpression(), readName(), readMemberReference(allowNull: true))
|
|
..fileOffset = offset;
|
|
case Tag.PropertySet:
|
|
int offset = readOffset();
|
|
return new PropertySet.byReference(readExpression(), readName(),
|
|
readExpression(), readMemberReference(allowNull: true))
|
|
..fileOffset = offset;
|
|
case Tag.SuperPropertyGet:
|
|
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();
|
|
return new DirectPropertyGet.byReference(
|
|
readExpression(), readMemberReference())
|
|
..fileOffset = offset;
|
|
case Tag.DirectPropertySet:
|
|
int offset = readOffset();
|
|
return new DirectPropertySet.byReference(
|
|
readExpression(), readMemberReference(), readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.StaticGet:
|
|
int offset = readOffset();
|
|
return new StaticGet.byReference(readMemberReference())
|
|
..fileOffset = offset;
|
|
case Tag.StaticSet:
|
|
int offset = readOffset();
|
|
return new StaticSet.byReference(
|
|
readMemberReference(), readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.MethodInvocation:
|
|
int offset = readOffset();
|
|
return new MethodInvocation.byReference(readExpression(), readName(),
|
|
readArguments(), readMemberReference(allowNull: true))
|
|
..fileOffset = offset;
|
|
case Tag.SuperMethodInvocation:
|
|
int offset = readOffset();
|
|
addTransformerFlag(TransformerFlag.superCalls);
|
|
return new SuperMethodInvocation.byReference(
|
|
readName(), readArguments(), readMemberReference(allowNull: true))
|
|
..fileOffset = offset;
|
|
case Tag.DirectMethodInvocation:
|
|
int offset = readOffset();
|
|
return new DirectMethodInvocation.byReference(
|
|
readExpression(), readMemberReference(), readArguments())
|
|
..fileOffset = offset;
|
|
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.NullCheck:
|
|
int offset = readOffset();
|
|
return new NullCheck(readExpression())..fileOffset = offset;
|
|
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.ListConcatenation:
|
|
int offset = readOffset();
|
|
var typeArgument = readDartType();
|
|
return new ListConcatenation(readExpressionList(),
|
|
typeArgument: typeArgument)
|
|
..fileOffset = offset;
|
|
case Tag.SetConcatenation:
|
|
int offset = readOffset();
|
|
var typeArgument = readDartType();
|
|
return new SetConcatenation(readExpressionList(),
|
|
typeArgument: typeArgument)
|
|
..fileOffset = offset;
|
|
case Tag.MapConcatenation:
|
|
int offset = readOffset();
|
|
var keyType = readDartType();
|
|
var valueType = readDartType();
|
|
return new MapConcatenation(readExpressionList(),
|
|
keyType: keyType, valueType: valueType)
|
|
..fileOffset = offset;
|
|
case Tag.InstanceCreation:
|
|
int offset = readOffset();
|
|
Reference classReference = readClassReference();
|
|
List<DartType> typeArguments = readDartTypeList();
|
|
int fieldValueCount = readUInt();
|
|
Map<Reference, Expression> fieldValues = <Reference, Expression>{};
|
|
for (int i = 0; i < fieldValueCount; i++) {
|
|
final Reference fieldRef =
|
|
readCanonicalNameReference().getReference();
|
|
final Expression value = readExpression();
|
|
fieldValues[fieldRef] = value;
|
|
}
|
|
int assertCount = readUInt();
|
|
List<AssertStatement> asserts = new List<AssertStatement>(assertCount);
|
|
for (int i = 0; i < assertCount; i++) {
|
|
asserts[i] = readStatement();
|
|
}
|
|
List<Expression> unusedArguments = readExpressionList();
|
|
return new InstanceCreation(classReference, typeArguments, fieldValues,
|
|
asserts, unusedArguments)
|
|
..fileOffset = offset;
|
|
case Tag.FileUriExpression:
|
|
Uri fileUri = readUriReference();
|
|
int offset = readOffset();
|
|
return new FileUriExpression(readExpression(), fileUri)
|
|
..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(readDouble());
|
|
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.SetLiteral:
|
|
int offset = readOffset();
|
|
var typeArgument = readDartType();
|
|
return new SetLiteral(readExpressionList(),
|
|
typeArgument: typeArgument, isConst: false)
|
|
..fileOffset = offset;
|
|
case Tag.ConstSetLiteral:
|
|
int offset = readOffset();
|
|
var typeArgument = readDartType();
|
|
return new SetLiteral(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())..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.BlockExpression:
|
|
int stackHeight = variableStack.length;
|
|
var statements = readStatementList();
|
|
var value = readExpression();
|
|
variableStack.length = stackHeight;
|
|
return new BlockExpression(new Block(statements), value);
|
|
case Tag.Instantiation:
|
|
var expression = readExpression();
|
|
var typeArguments = readDartTypeList();
|
|
return new Instantiation(expression, typeArguments);
|
|
case Tag.ConstantExpression:
|
|
int offset = readOffset();
|
|
DartType type = readDartType();
|
|
Constant constant = readConstantReference();
|
|
return new ConstantExpression(constant, type)..fileOffset = offset;
|
|
default:
|
|
throw fail('unexpected expression tag: $tag');
|
|
}
|
|
}
|
|
|
|
List<MapEntry> readMapEntryList() {
|
|
int length = readUInt();
|
|
List<MapEntry> result =
|
|
new List<MapEntry>.filled(length, null, growable: true);
|
|
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 =
|
|
new List<Statement>.filled(length, null, growable: true);
|
|
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.ExpressionStatement:
|
|
return new ExpressionStatement(readExpression());
|
|
case Tag.Block:
|
|
return readBlock();
|
|
case Tag.AssertBlock:
|
|
return readAssertBlock();
|
|
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>.filled(count, null, growable: true);
|
|
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();
|
|
int flags = readByte();
|
|
return new TryCatch(body, readCatchList(), isSynthetic: flags & 2 == 2);
|
|
case Tag.TryFinally:
|
|
return new TryFinally(readStatement(), readStatement());
|
|
case Tag.YieldStatement:
|
|
int offset = readOffset();
|
|
int flags = readByte();
|
|
return new YieldStatement(readExpression(),
|
|
isYieldStar: flags & YieldStatement.FlagYieldStar != 0,
|
|
isNative: flags & YieldStatement.FlagNative != 0)
|
|
..fileOffset = offset;
|
|
case Tag.VariableDeclaration:
|
|
var variable = readVariableDeclaration();
|
|
variableStack.add(variable); // Will be popped by the enclosing scope.
|
|
return variable;
|
|
case Tag.FunctionDeclaration:
|
|
int offset = readOffset();
|
|
var variable = readVariableDeclaration();
|
|
variableStack.add(variable); // Will be popped by the enclosing scope.
|
|
var function = readFunctionNode();
|
|
return new FunctionDeclaration(variable, function)..fileOffset = offset;
|
|
default:
|
|
throw fail('unexpected 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>.filled(length, null, growable: true);
|
|
for (int i = 0; i < length; ++i) {
|
|
result[i] = readCatch();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Catch readCatch() {
|
|
int variableStackHeight = variableStack.length;
|
|
var offset = readOffset();
|
|
var guard = readDartType();
|
|
var exception = readAndPushVariableDeclarationOption();
|
|
var stackTrace = readAndPushVariableDeclarationOption();
|
|
var body = readStatement();
|
|
variableStack.length = variableStackHeight;
|
|
return new Catch(exception, body, guard: guard, stackTrace: stackTrace)
|
|
..fileOffset = offset;
|
|
}
|
|
|
|
Block readBlock() {
|
|
int stackHeight = variableStack.length;
|
|
var body = readStatementList();
|
|
variableStack.length = stackHeight;
|
|
return new Block(body);
|
|
}
|
|
|
|
AssertBlock readAssertBlock() {
|
|
int stackHeight = variableStack.length;
|
|
var body = readStatementList();
|
|
variableStack.length = stackHeight;
|
|
return new AssertBlock(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>.filled(length, null, growable: true);
|
|
for (int i = 0; i < length; ++i) {
|
|
result[i] = readSupertype();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
List<DartType> readDartTypeList() {
|
|
int length = readUInt();
|
|
List<DartType> result =
|
|
new List<DartType>.filled(length, null, growable: true);
|
|
for (int i = 0; i < length; ++i) {
|
|
result[i] = readDartType();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
List<NamedType> readNamedTypeList() {
|
|
int length = readUInt();
|
|
List<NamedType> result =
|
|
new List<NamedType>.filled(length, null, growable: true);
|
|
for (int i = 0; i < length; ++i) {
|
|
result[i] = readNamedType();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
NamedType readNamedType() {
|
|
String name = readStringReference();
|
|
DartType type = readDartType();
|
|
int flags = readByte();
|
|
return new NamedType(name, type,
|
|
isRequired: (flags & NamedType.FlagRequiredNamedType) != 0);
|
|
}
|
|
|
|
DartType readDartTypeOption() {
|
|
return readAndCheckOptionTag() ? readDartType() : null;
|
|
}
|
|
|
|
DartType readDartType() {
|
|
int tag = readByte();
|
|
switch (tag) {
|
|
case Tag.TypedefType:
|
|
int nullabilityIndex = readByte();
|
|
return new TypedefType.byReference(readTypedefReference(),
|
|
readDartTypeList(), Nullability.values[nullabilityIndex]);
|
|
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:
|
|
int nullabilityIndex = readByte();
|
|
return new InterfaceType.byReference(readClassReference(),
|
|
readDartTypeList(), Nullability.values[nullabilityIndex]);
|
|
case Tag.SimpleInterfaceType:
|
|
int nullabilityIndex = readByte();
|
|
return new InterfaceType.byReference(readClassReference(),
|
|
const <DartType>[], Nullability.values[nullabilityIndex]);
|
|
case Tag.FunctionType:
|
|
int typeParameterStackHeight = typeParameterStack.length;
|
|
int nullabilityIndex = readByte();
|
|
var typeParameters = readAndPushTypeParameterList();
|
|
var requiredParameterCount = readUInt();
|
|
var totalParameterCount = readUInt();
|
|
var positional = readDartTypeList();
|
|
var named = readNamedTypeList();
|
|
var typedefType = readDartTypeOption();
|
|
assert(positional.length + named.length == totalParameterCount);
|
|
var returnType = readDartType();
|
|
typeParameterStack.length = typeParameterStackHeight;
|
|
return new FunctionType(positional, returnType,
|
|
typeParameters: typeParameters,
|
|
requiredParameterCount: requiredParameterCount,
|
|
namedParameters: named,
|
|
typedefType: typedefType,
|
|
nullability: Nullability.values[nullabilityIndex]);
|
|
case Tag.SimpleFunctionType:
|
|
int nullabilityIndex = readByte();
|
|
var positional = readDartTypeList();
|
|
var returnType = readDartType();
|
|
return new FunctionType(positional, returnType,
|
|
nullability: Nullability.values[nullabilityIndex]);
|
|
case Tag.TypeParameterType:
|
|
int declaredNullabilityIndex = readByte();
|
|
int index = readUInt();
|
|
var bound = readDartTypeOption();
|
|
return new TypeParameterType(typeParameterStack[index], bound,
|
|
Nullability.values[declaredNullabilityIndex]);
|
|
default:
|
|
throw fail('unexpected 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>.filled(length, null, growable: true);
|
|
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.variance = readByte();
|
|
node.name = readStringOrNullIfEmpty();
|
|
node.bound = readDartType();
|
|
node.defaultType = readDartTypeOption();
|
|
}
|
|
|
|
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>.filled(length, null, growable: true);
|
|
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>.filled(length, null, growable: true);
|
|
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;
|
|
}
|
|
node.annotations = annotations;
|
|
}
|
|
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;
|
|
|
|
BinaryBuilderWithMetadata(List<int> bytes,
|
|
{String filename,
|
|
bool disableLazyReading = false,
|
|
bool disableLazyClassReading = false,
|
|
bool alwaysCreateNewNamedNodes})
|
|
: super(bytes,
|
|
filename: filename,
|
|
disableLazyReading: disableLazyReading,
|
|
disableLazyClassReading: disableLazyClassReading,
|
|
alwaysCreateNewNamedNodes: alwaysCreateNewNamedNodes);
|
|
|
|
@override
|
|
void _readMetadataMappings(
|
|
Component component, int binaryOffsetForMetadataPayloads) {
|
|
// 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<Pair<UInt32, UInt32>> nodeOffsetToMetadataOffset
|
|
_byteOffset = endOffset - 4;
|
|
final mappingLength = readUint32();
|
|
final mappingStart = (endOffset - 4) - 4 * 2 * mappingLength;
|
|
_byteOffset = mappingStart - 4;
|
|
|
|
// UInt32 tag (fixed size StringReference)
|
|
final tag = _stringTable[readUint32()];
|
|
|
|
final repository = component.metadata[tag];
|
|
if (repository != null) {
|
|
// Read nodeOffsetToMetadataOffset mapping.
|
|
final mapping = <int, int>{};
|
|
_byteOffset = mappingStart;
|
|
for (var j = 0; j < mappingLength; j++) {
|
|
final nodeOffset = readUint32();
|
|
final metadataOffset = binaryOffsetForMetadataPayloads + readUint32();
|
|
mapping[nodeOffset] = metadataOffset;
|
|
}
|
|
|
|
_subsections ??= <_MetadataSubsection>[];
|
|
_subsections.add(new _MetadataSubsection(repository, mapping));
|
|
}
|
|
|
|
// Start of the subsection and the end of the previous one.
|
|
endOffset = mappingStart - 4;
|
|
}
|
|
}
|
|
|
|
Object _readMetadata(Node node, MetadataRepository repository, int offset) {
|
|
final int savedOffset = _byteOffset;
|
|
_byteOffset = offset;
|
|
|
|
final metadata = repository.readFromBinary(node, this);
|
|
|
|
_byteOffset = savedOffset;
|
|
return metadata;
|
|
}
|
|
|
|
@override
|
|
void enterScope({List<TypeParameter> typeParameters}) {
|
|
if (typeParameters != null) {
|
|
typeParameterStack.addAll(typeParameters);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void leaveScope({List<TypeParameter> typeParameters}) {
|
|
if (typeParameters != null) {
|
|
typeParameterStack.length -= typeParameters.length;
|
|
}
|
|
}
|
|
|
|
@override
|
|
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) {
|
|
subsection.repository.mapping[node] =
|
|
_readMetadata(node, subsection.repository, metadataOffset);
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
@override
|
|
DartType readDartType() {
|
|
final nodeOffset = _byteOffset;
|
|
final result = super.readDartType();
|
|
return _associateMetadata(result, nodeOffset);
|
|
}
|
|
|
|
@override
|
|
Library readLibrary(Component component, int endOffset) {
|
|
final nodeOffset = _byteOffset;
|
|
final result = super.readLibrary(component, 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
|
|
Extension readExtension() {
|
|
final nodeOffset = _byteOffset;
|
|
final result = super.readExtension();
|
|
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
|
|
RedirectingFactoryConstructor readRedirectingFactoryConstructor() {
|
|
final nodeOffset = _byteOffset;
|
|
final result = super.readRedirectingFactoryConstructor();
|
|
return _associateMetadata(result, nodeOffset);
|
|
}
|
|
|
|
@override
|
|
Initializer readInitializer() {
|
|
final nodeOffset = _byteOffset;
|
|
final result = super.readInitializer();
|
|
return _associateMetadata(result, nodeOffset);
|
|
}
|
|
|
|
@override
|
|
FunctionNode readFunctionNode(
|
|
{bool lazyLoadBody: false, int outerEndOffset: -1}) {
|
|
final nodeOffset = _byteOffset;
|
|
final result = super.readFunctionNode(
|
|
lazyLoadBody: lazyLoadBody, outerEndOffset: 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;
|
|
}
|
|
|
|
/// 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;
|
|
|
|
_MetadataSubsection(this.repository, this.mapping);
|
|
}
|