Files
sdk/pkg/kernel/lib/binary/ast_from_binary.dart
T
Johnni Winther 56490f9776 [cfe] Add Procedure.signatureType
The added field holds the interface signature type for a Procedure when
this is different from the class signature type (implied by the function
parameters and return type). This is needed for forwarding semi stubs
when its declared type is different from the called super member.

Closes https://github.com/dart-lang/sdk/issues/47072

TEST=pkg/front_end/testcases/general/implement_semi_stub.dart and more

Change-Id: I4a7cdc9d564fb0a59e7b06e83646b5410d8d82f5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/216420
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Chloe Stefantsova <dmitryas@google.com>
2021-10-13 11:31:16 +00:00

3553 lines
118 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:developer';
import 'dart:convert';
import 'dart:typed_data';
import '../ast.dart';
import '../transformations/flags.dart';
import 'tag.dart';
class ParseError {
final String? filename;
final int byteIndex;
final String message;
final String path;
ParseError(this.message,
{required this.filename, required this.byteIndex, required this.path});
@override
String toString() => '$filename:$byteIndex: $message at $path';
}
class InvalidKernelVersionError {
final String? filename;
final int version;
InvalidKernelVersionError(this.filename, this.version);
@override
String toString() {
StringBuffer sb = new StringBuffer();
sb.write('Unexpected Kernel Format Version ${version} '
'(expected ${Tag.BinaryFormatVersion})');
if (filename != null) {
sb.write(' when reading $filename.');
}
return '$sb';
}
}
class InvalidKernelSdkVersionError {
final String version;
InvalidKernelSdkVersionError(this.version);
@override
String toString() {
return 'Unexpected Kernel SDK Version ${version} '
'(expected ${expectedSdkHash}).';
}
}
class CompilationModeError {
final String message;
CompilationModeError(this.message);
@override
String toString() => "CompilationModeError[$message]";
}
class _ComponentIndex {
static const int numberOfFixedFields = 12;
final int binaryOffsetForSourceTable;
final int binaryOffsetForCanonicalNames;
final int binaryOffsetForMetadataPayloads;
final int binaryOffsetForMetadataMappings;
final int binaryOffsetForStringTable;
final int binaryOffsetForConstantTable;
final int binaryOffsetForConstantTableIndex;
final int binaryOffsetForStartOfComponentIndex;
final int mainMethodReference;
final NonNullableByDefaultCompiledMode compiledMode;
final List<int> libraryOffsets;
final int libraryCount;
final int componentFileSizeInBytes;
_ComponentIndex(
{required this.binaryOffsetForSourceTable,
required this.binaryOffsetForCanonicalNames,
required this.binaryOffsetForMetadataPayloads,
required this.binaryOffsetForMetadataMappings,
required this.binaryOffsetForStringTable,
required this.binaryOffsetForConstantTable,
required this.binaryOffsetForConstantTableIndex,
required this.binaryOffsetForStartOfComponentIndex,
required this.mainMethodReference,
required this.compiledMode,
required this.libraryOffsets,
required this.libraryCount,
required this.componentFileSizeInBytes});
}
class SubComponentView {
final List<Library> libraries;
final int componentStartOffset;
final int componentFileSize;
SubComponentView(
this.libraries, this.componentStartOffset, this.componentFileSize);
}
/// [StringInterner] allows Strings created from the binary to be shared with
/// other components.
///
/// The [StringInterner] is an optional parameter to [BinaryBuilder], so sharing
/// should not be required for correctness, and an implementation of
/// [StringInterner] method may be partial (sometimes not finding an existing
/// object) or trivial (returning the argument).
abstract class StringInterner {
/// Returns a string with the same contents as [string].
String internString(String string);
}
class BinaryBuilder {
final List<VariableDeclaration> variableStack = <VariableDeclaration>[];
final List<LabeledStatement> labelStack = <LabeledStatement>[];
int labelStackBase = 0;
int switchCaseStackBase = 0;
final List<SwitchCase> switchCaseStack = <SwitchCase>[];
final List<TypeParameter> typeParameterStack = <TypeParameter>[];
final String? filename;
final List<int> _bytes;
int _byteOffset = 0;
List<String> _stringTable = const [];
List<Uri> _sourceUriTable = const [];
List<Constant> _constantTable = const <Constant>[];
late List<CanonicalName> _linkTable;
int _transformerFlags = 0;
Library? _currentLibrary;
int _componentStartOffset = 0;
NonNullableByDefaultCompiledMode? compilationMode;
// If something goes wrong, this list should indicate what library,
// class, and member was being built.
List<String> debugPath = <String>[];
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;
/// [stringInterner] (optional) may be used to allow components to share
/// instances of [String] that have the same contents.
final StringInterner? stringInterner;
/// When creating lists that *might* be growable, use this boolean as the
/// setting to pass to `growable` so the dill can be loaded in a more compact
/// manner if the caller knows that the growability isn't needed.
final bool useGrowableLists;
/// Note that [disableLazyClassReading] is incompatible
/// with checkCanonicalNames on readComponent.
BinaryBuilder(this._bytes,
{this.filename,
bool disableLazyReading = false,
bool disableLazyClassReading = false,
bool? alwaysCreateNewNamedNodes,
this.stringInterner,
this.useGrowableLists = true})
: _disableLazyReading = disableLazyReading,
_disableLazyClassReading = disableLazyReading ||
disableLazyClassReading ||
// Disable lazy class reading when forcing the creation of new named
// nodes as it is a logical "relink" to the new version (overwriting
// the old one) - which doesn't play well with lazy loading class
// content as old loaded references will then potentially still
// point to the old content until the new class has been lazy
// loaded.
(alwaysCreateNewNamedNodes == true),
this.alwaysCreateNewNamedNodes = alwaysCreateNewNamedNodes ?? false;
Never fail(String message) {
throw ParseError(message,
byteIndex: _byteOffset, filename: filename, path: debugPath.join('::'));
}
int get byteOffset => _byteOffset;
int readByte() => _bytes[_byteOffset++];
int readUInt30() {
int 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() {
Uint8List doubleBufferUint8 =
_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];
}
Uint8List readBytes(int length) {
Uint8List bytes = new Uint8List(length);
bytes.setRange(0, bytes.length, _bytes, _byteOffset);
_byteOffset += bytes.length;
return bytes;
}
Uint8List readByteList() {
return readBytes(readUInt30());
}
String readString() {
return readStringEntry(readUInt30());
}
String readStringEntry(int numBytes) {
String string = _readStringEntry(numBytes);
if (stringInterner == null) return string;
return stringInterner!.internString(string);
}
String _readStringEntry(int numBytes) {
int start = _byteOffset;
int end = start + numBytes;
_byteOffset = end;
for (int i = start; i < end; i++) {
if (_bytes[i] > 127) {
return _decodeWtf8(start, end);
}
}
return new String.fromCharCodes(_bytes, start, end);
}
String _decodeWtf8(int start, int end) {
// WTF-8 decoder that trusts its input, meaning that the correctness of
// the code depends on the bytes from start to end being valid and
// complete WTF-8. Instead of masking off the control bits from every
// byte, it simply xor's the byte values together at their appropriate
// bit shifts, and then xor's out all of the control bits at once.
Uint16List charCodes = new Uint16List(end - start);
int i = start;
int j = 0;
while (i < end) {
int byte = _bytes[i++];
if (byte < 0x80) {
// ASCII.
charCodes[j++] = byte;
} else if (byte < 0xE0) {
// Two-byte sequence (11-bit unicode value).
int byte2 = _bytes[i++];
int value = (byte << 6) ^ byte2 ^ 0x3080;
assert(value >= 0x80 && value < 0x800);
charCodes[j++] = value;
} else if (byte < 0xF0) {
// Three-byte sequence (16-bit unicode value).
int byte2 = _bytes[i++];
int byte3 = _bytes[i++];
int value = (byte << 12) ^ (byte2 << 6) ^ byte3 ^ 0xE2080;
assert(value >= 0x800 && value < 0x10000);
charCodes[j++] = value;
} else {
// Four-byte sequence (non-BMP unicode value).
int byte2 = _bytes[i++];
int byte3 = _bytes[i++];
int byte4 = _bytes[i++];
int value =
(byte << 18) ^ (byte2 << 12) ^ (byte3 << 6) ^ byte4 ^ 0x3C82080;
assert(value >= 0x10000 && value < 0x110000);
charCodes[j++] = 0xD7C0 + (value >> 10);
charCodes[j++] = 0xDC00 + (value & 0x3FF);
}
}
assert(i == end);
return new String.fromCharCodes(charCodes, 0, j);
}
/// Read metadataMappings section from the binary.
void _readMetadataMappings(
Component component, int binaryOffsetForMetadataPayloads) {
// Default reader ignores metadata section entirely.
}
/// Reads metadata for the given [node].
T _associateMetadata<T extends Node>(T node, int nodeOffset) {
// Default reader ignores metadata section entirely.
return node;
}
void readStringTable() {
// Read the table of end offsets.
int length = readUInt30();
List<int> endOffsets =
new List<int>.generate(length, (_) => readUInt30(), growable: false);
// Read the WTF-8 encoded strings.
int startOffset = 0;
_stringTable = new List<String>.generate(length, (int index) {
String result = readStringEntry(endOffsets[index] - startOffset);
startOffset = endOffsets[index];
return result;
}, growable: false);
}
void readConstantTable() {
final int length = readUInt30();
// Because of "back-references" (e.g. the 10th constant referencing the 3rd
// constant) we can't use List.generate.
_constantTable =
new List<Constant>.filled(length, dummyConstant, growable: false);
for (int i = 0; i < length; i++) {
_constantTable[i] = readConstantTableEntry();
}
}
Constant readConstantTableEntry() {
final int constantTag = readByte();
switch (constantTag) {
case ConstantTag.NullConstant:
return _readNullConstant();
case ConstantTag.BoolConstant:
return _readBoolConstant();
case ConstantTag.IntConstant:
return _readIntConstant();
case ConstantTag.DoubleConstant:
return _readDoubleConstant();
case ConstantTag.StringConstant:
return _readStringConstant();
case ConstantTag.SymbolConstant:
return _readSymbolConstant();
case ConstantTag.MapConstant:
return _readMapConstant();
case ConstantTag.ListConstant:
return _readListConstant();
case ConstantTag.SetConstant:
return _readSetConstant();
case ConstantTag.InstanceConstant:
return _readInstanceConstant();
case ConstantTag.InstantiationConstant:
return _readInstantiationConstant();
case ConstantTag.TypedefTearOffConstant:
return _readTypedefTearOffConstant();
case ConstantTag.StaticTearOffConstant:
return _readStaticTearOffConstant();
case ConstantTag.ConstructorTearOffConstant:
return _readConstructorTearOffConstant();
case ConstantTag.RedirectingFactoryTearOffConstant:
return _readRedirectingFactoryTearOffConstant();
case ConstantTag.TypeLiteralConstant:
return _readTypeLiteralConstant();
case ConstantTag.UnevaluatedConstant:
return _readUnevaluatedConstant();
}
throw fail('unexpected constant tag: $constantTag');
}
Constant _readNullConstant() {
return new NullConstant();
}
Constant _readBoolConstant() {
return new BoolConstant(readByte() == 1);
}
Constant _readIntConstant() {
return new IntConstant((readExpression() as IntLiteral).value);
}
Constant _readDoubleConstant() {
return new DoubleConstant(readDouble());
}
Constant _readStringConstant() {
return new StringConstant(readStringReference());
}
Constant _readSymbolConstant() {
Reference? libraryReference = readNullableLibraryReference();
return new SymbolConstant(readStringReference(), libraryReference);
}
Constant _readMapConstant() {
final DartType keyType = readDartType();
final DartType valueType = readDartType();
final int length = readUInt30();
final List<ConstantMapEntry> entries =
new List<ConstantMapEntry>.generate(length, (_) {
final Constant key = readConstantReference();
final Constant value = readConstantReference();
return new ConstantMapEntry(key, value);
}, growable: useGrowableLists);
return new MapConstant(keyType, valueType, entries);
}
Constant _readListConstant() {
final DartType typeArgument = readDartType();
List<Constant> entries = _readConstantReferenceList();
return new ListConstant(typeArgument, entries);
}
Constant _readSetConstant() {
final DartType typeArgument = readDartType();
List<Constant> entries = _readConstantReferenceList();
return new SetConstant(typeArgument, entries);
}
Constant _readInstanceConstant() {
final Reference classReference = readNonNullClassReference();
final List<DartType> typeArguments = readDartTypeList();
final int fieldValueCount = readUInt30();
final Map<Reference, Constant> fieldValues = <Reference, Constant>{};
for (int i = 0; i < fieldValueCount; i++) {
final Reference fieldRef = readNonNullCanonicalNameReference().reference;
final Constant constant = readConstantReference();
fieldValues[fieldRef] = constant;
}
return new InstanceConstant(classReference, typeArguments, fieldValues);
}
Constant _readInstantiationConstant() {
final Constant tearOffConstant = readConstantReference();
final List<DartType> types = readDartTypeList();
return new InstantiationConstant(tearOffConstant, types);
}
Constant _readTypedefTearOffConstant() {
final List<TypeParameter> parameters = readAndPushTypeParameterList();
final TearOffConstant tearOffConstant =
readConstantReference() as TearOffConstant;
final List<DartType> types = readDartTypeList();
typeParameterStack.length -= parameters.length;
return new TypedefTearOffConstant(parameters, tearOffConstant, types);
}
Constant _readStaticTearOffConstant() {
final Reference reference = readNonNullCanonicalNameReference().reference;
return new StaticTearOffConstant.byReference(reference);
}
Constant _readConstructorTearOffConstant() {
final Reference reference = readNonNullCanonicalNameReference().reference;
return new ConstructorTearOffConstant.byReference(reference);
}
Constant _readRedirectingFactoryTearOffConstant() {
final Reference reference = readNonNullCanonicalNameReference().reference;
return new RedirectingFactoryTearOffConstant.byReference(reference);
}
Constant _readTypeLiteralConstant() {
final DartType type = readDartType();
return new TypeLiteralConstant(type);
}
Constant _readUnevaluatedConstant() {
final Expression expression = readExpression();
return new UnevaluatedConstant(expression);
}
Constant readConstantReference() {
final int index = readUInt30();
Constant constant = _constantTable[index];
assert(!identical(constant, dummyConstant),
"No constant found at index $index.");
return constant;
}
List<Constant> _readConstantReferenceList() {
final int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
return emptyListOfConstant;
}
return new List<Constant>.generate(length, (_) => readConstantReference(),
growable: useGrowableLists);
}
Uri readUriReference() {
return _sourceUriTable[readUInt30()];
}
String readStringReference() {
return _stringTable[readUInt30()];
}
List<String> readStringReferenceList() {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
return emptyListOfString;
}
return new List<String>.generate(length, (_) => readStringReference(),
growable: useGrowableLists);
}
List<Reference> readNonNullReferenceList(List<Reference> result) {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
return emptyListOfReference;
}
for (int i = 0; i < length; ++i) {
result.add(readNonNullMemberReference());
}
return result;
}
String? readStringOrNullIfEmpty() {
String 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 = readUInt30();
if (length == 0) return const <Expression>[];
return new List<Expression>.generate(
length, (_) => readExpression()..parent = parent,
growable: useGrowableLists);
}
void readLinkTable(CanonicalName linkRoot) {
int length = readUInt30();
_linkTable = new List<CanonicalName>.filled(
length,
// Use [linkRoot] as a dummy default value.
linkRoot,
growable: false);
for (int i = 0; i < length; ++i) {
int biasedParentIndex = readUInt30();
String name = readStringReference();
CanonicalName 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();
if (size <= 0) {
throw fail("invalid size '$size' reported at offset $byteOffset");
}
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.");
}
void _readAndVerifySdkHash() {
final String sdkHash = ascii.decode(readBytes(sdkHashLength));
if (!isValidSdkHash(sdkHash)) {
throw InvalidKernelSdkVersionError(sdkHash);
}
}
/// 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(filename, version);
}
_readAndVerifySdkHash();
_byteOffset = offset;
List<int> componentFileSizes = _indexComponents();
if (componentFileSizes.length > 1) {
_disableLazyReading = true;
_disableLazyClassReading = true;
}
int componentFileIndex = 0;
List<SubComponentView>? views;
if (createView) {
views = <SubComponentView>[];
}
while (_byteOffset < _bytes.length) {
SubComponentView? view = _readOneComponent(
component, componentFileSizes[componentFileIndex],
createView: createView);
if (createView) {
views!.add(view!);
}
++componentFileIndex;
}
if (checkCanonicalNames) {
component.root.checkCanonicalNameChildren();
}
return views;
});
}
/// Deserializes the source and stores it in [component].
/// Note that the _coverage_ normally included in the source in the
/// uri-to-source mapping is _not_ included.
///
/// 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) {
component.root.checkCanonicalNameChildren();
}
}
_ComponentIndex _readComponentIndex(int componentFileSize) {
int savedByteIndex = _byteOffset;
// There are two fields: file size and library count.
_byteOffset = _componentStartOffset + componentFileSize - (2) * 4;
int 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
List<int> libraryOffsets = new List<int>.filled(
libraryCount + 1,
// Use `-1` as a dummy default value.
-1,
growable: false);
int componentFileSizeInBytes = readUint32();
if (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 "
"${componentFileSizeInBytes}.";
}
// Skip to the start of the index.
_byteOffset -=
((libraryCount + 1) + _ComponentIndex.numberOfFixedFields) * 4;
// Now read the component index.
int binaryOffsetForSourceTable = _componentStartOffset + readUint32();
int binaryOffsetForConstantTable = _componentStartOffset + readUint32();
int binaryOffsetForConstantTableIndex =
_componentStartOffset + readUint32();
int binaryOffsetForCanonicalNames = _componentStartOffset + readUint32();
int binaryOffsetForMetadataPayloads = _componentStartOffset + readUint32();
int binaryOffsetForMetadataMappings = _componentStartOffset + readUint32();
int binaryOffsetForStringTable = _componentStartOffset + readUint32();
int binaryOffsetForStartOfComponentIndex =
_componentStartOffset + readUint32();
int mainMethodReference = readUint32();
NonNullableByDefaultCompiledMode compiledMode =
NonNullableByDefaultCompiledMode.values[readUint32()];
for (int i = 0; i < libraryCount + 1; ++i) {
libraryOffsets[i] = _componentStartOffset + readUint32();
}
_byteOffset = savedByteIndex;
return new _ComponentIndex(
libraryCount: libraryCount,
libraryOffsets: libraryOffsets,
componentFileSizeInBytes: componentFileSizeInBytes,
binaryOffsetForSourceTable: binaryOffsetForSourceTable,
binaryOffsetForCanonicalNames: binaryOffsetForCanonicalNames,
binaryOffsetForMetadataPayloads: binaryOffsetForMetadataPayloads,
binaryOffsetForMetadataMappings: binaryOffsetForMetadataMappings,
binaryOffsetForStringTable: binaryOffsetForStringTable,
binaryOffsetForConstantTable: binaryOffsetForConstantTable,
binaryOffsetForConstantTableIndex: binaryOffsetForConstantTableIndex,
binaryOffsetForStartOfComponentIndex:
binaryOffsetForStartOfComponentIndex,
mainMethodReference: mainMethodReference,
compiledMode: compiledMode);
}
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(filename, formatVersion);
}
_readAndVerifySdkHash();
// Read component index from the end of this ComponentFiles serialized data.
_ComponentIndex index = _readComponentIndex(componentFileSize);
_byteOffset = index.binaryOffsetForSourceTable;
Map<Uri, Source> uriToSource = readUriToSource(readCoverage: false);
_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(filename, formatVersion);
}
_readAndVerifySdkHash();
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);
if (compilationMode == null) {
compilationMode = component.modeRaw;
}
compilationMode =
mergeCompilationModeOrThrow(compilationMode, index.compiledMode);
_byteOffset = index.binaryOffsetForStringTable;
readStringTable();
_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(readCoverage: true);
_mergeUriToSource(component.uriToSource, uriToSource);
_byteOffset = index.binaryOffsetForConstantTable;
readConstantTable();
// We don't need the constant table index on the dart side.
int numberOfLibraries = index.libraryCount;
SubComponentView? result;
if (createView) {
result = new SubComponentView(
new List<Library>.generate(numberOfLibraries, (int i) {
_byteOffset = index.libraryOffsets[i];
return readLibrary(component, index.libraryOffsets[i + 1]);
}, growable: false),
_componentStartOffset,
componentFileSize);
} else {
for (int i = 0; i < numberOfLibraries; ++i) {
_byteOffset = index.libraryOffsets[i];
readLibrary(component, index.libraryOffsets[i + 1]);
}
}
Reference? mainMethod =
getNullableMemberReferenceFromInt(index.mainMethodReference);
component.setMainMethodAndMode(mainMethod, false, compilationMode!);
_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 = readUInt30();
if (length == 0) return null;
return new List<String>.generate(length, (_) => readString(),
growable: useGrowableLists);
}
/// Read the uri-to-source part of the binary.
/// Note that this can include coverage, but that it is only included if
/// [readCoverage] is true, otherwise coverage will be skipped. Note also that
/// if [readCoverage] is true, references are read and that the link table
/// thus has to be read first.
Map<Uri, Source> readUriToSource({required bool readCoverage}) {
// ignore: unnecessary_null_comparison
assert(!readCoverage || (readCoverage && _linkTable != null));
int length = readUint32();
// Read data.
_sourceUriTable = new List<Uri>.filled(length, dummyUri, growable: false);
Map<Uri, Source> uriToSource = <Uri, Source>{};
for (int i = 0; i < length; ++i) {
String uriString = readString();
Uri uri = Uri.parse(uriString);
_sourceUriTable[i] = uri;
Uint8List sourceCode = readByteList();
int lineCount = readUInt30();
List<int> lineStarts = new List<int>.filled(
lineCount,
// Use `-1` as a dummy default value.
-1,
growable: false);
int previousLineStart = 0;
for (int j = 0; j < lineCount; ++j) {
int lineStart = readUInt30() + previousLineStart;
lineStarts[j] = lineStart;
previousLineStart = lineStart;
}
String importUriString = readString();
Uri importUri = Uri.parse(importUriString);
Set<Reference>? coverageConstructors;
{
int constructorCoverageCount = readUInt30();
if (constructorCoverageCount > 0) {
if (readCoverage) {
coverageConstructors = new Set<Reference>();
for (int j = 0; j < constructorCoverageCount; ++j) {
coverageConstructors.add(readNonNullMemberReference());
}
} else {
for (int j = 0; j < constructorCoverageCount; ++j) {
skipMemberReference();
}
}
}
}
uriToSource[uri] = new Source(lineStarts, sourceCode, importUri, uri)
..constantCoverageConstructors = coverageConstructors;
}
// 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) {
Source? originalDestinationSource = dst[key];
Source? mergeFrom;
Source mergeTo;
if (value.source.isNotEmpty || originalDestinationSource == null) {
dst[key] = value;
mergeFrom = originalDestinationSource;
mergeTo = value;
} else {
mergeFrom = value;
mergeTo = originalDestinationSource;
}
// TODO(jensj): Find out what the right thing to do is --- it probably
// depends on what we do if read the same library twice - do we merge or
// do we overwrite, and should we even support such a thing?
// Merge coverage. Note that mergeFrom might be null.
if (mergeTo.constantCoverageConstructors == null) {
mergeTo.constantCoverageConstructors =
mergeFrom?.constantCoverageConstructors;
} else if (mergeFrom?.constantCoverageConstructors == null) {
// Nothing to do.
} else {
// Both are non-null: Merge.
mergeTo.constantCoverageConstructors!
.addAll(mergeFrom!.constantCoverageConstructors!);
}
});
}
}
void skipCanonicalNameReference() {
readUInt30();
}
CanonicalName? readNullableCanonicalNameReference() {
int index = readUInt30();
if (index == 0) return null;
return _linkTable[index - 1];
}
CanonicalName readNonNullCanonicalNameReference() {
CanonicalName? canonicalName = readNullableCanonicalNameReference();
if (canonicalName == null) {
throw new StateError('No canonical name found.');
}
return canonicalName;
}
CanonicalName? getNullableCanonicalNameReferenceFromInt(int index) {
if (index == 0) return null;
return _linkTable[index - 1];
}
Reference? readNullableLibraryReference() {
CanonicalName? canonicalName = readNullableCanonicalNameReference();
return canonicalName?.reference;
}
Reference readNonNullLibraryReference() {
CanonicalName? canonicalName = readNullableCanonicalNameReference();
if (canonicalName != null) return canonicalName.reference;
throw 'Expected a library reference to be valid but was `null`.';
}
LibraryDependency readLibraryDependencyReference() {
int index = readUInt30();
return _currentLibrary!.dependencies[index];
}
Reference? readNullableClassReference() {
CanonicalName? name = readNullableCanonicalNameReference();
return name?.reference;
}
Reference readNonNullClassReference() {
CanonicalName? name = readNullableCanonicalNameReference();
if (name == null) {
throw 'Expected a class reference to be valid but was `null`.';
}
return name.reference;
}
void skipMemberReference() {
skipCanonicalNameReference();
}
Reference? readNullableMemberReference() {
CanonicalName? name = readNullableCanonicalNameReference();
return name?.reference;
}
Reference readNonNullMemberReference() {
CanonicalName? name = readNullableCanonicalNameReference();
if (name == null) {
throw 'Expected a member reference to be valid but was `null`.';
}
return name.reference;
}
Reference? readNullableInstanceMemberReference() {
Reference? reference = readNullableMemberReference();
readNullableMemberReference(); // Skip origin
return reference;
}
Reference readNonNullInstanceMemberReference() {
Reference reference = readNonNullMemberReference();
readNullableMemberReference(); // Skip origin
return reference;
}
Reference? getNullableMemberReferenceFromInt(int index) {
return getNullableCanonicalNameReferenceFromInt(index)?.reference;
}
Reference? readNullableTypedefReference() {
return readNullableCanonicalNameReference()?.reference;
}
Reference readNonNullTypedefReference() {
return readNonNullCanonicalNameReference().reference;
}
Name readName() {
String text = readStringReference();
if (text.isNotEmpty && text[0] == '_') {
return new Name.byReference(text, readNonNullLibraryReference());
} 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();
// 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();
List<int> procedureOffsets = new List<int>.generate(
procedureCount + 1, (int index) => _componentStartOffset + readUint32(),
growable: false);
// 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;
List<int> classOffsets = new List<int>.generate(
classCount + 1, (int index) => _componentStartOffset + readUint32(),
growable: false);
_byteOffset = savedByteOffset;
int flags = readByte();
int languageVersionMajor = readUInt30();
int languageVersionMinor = readUInt30();
CanonicalName canonicalName = readNonNullCanonicalNameReference();
Reference reference = canonicalName.reference;
Library? library = reference.node as Library?;
String? name = readStringOrNullIfEmpty();
// TODO(jensj): We currently save (almost the same) uri twice.
Uri fileUri = readUriReference();
if (alwaysCreateNewNamedNodes) {
library = null;
}
if (library == null) {
library = new Library(Uri.parse(canonicalName.name),
reference: reference, fileUri: fileUri);
component.libraries.add(library..parent = component);
}
_currentLibrary = library;
List<String>? problemsAsJson = readListOfStrings();
library.flags = flags;
library.setLanguageVersion(
new Version(languageVersionMajor, languageVersionMinor));
library.name = name;
library.fileUri = fileUri;
library.problemsAsJson = problemsAsJson;
assert(
mergeCompilationModeOrThrow(
compilationMode, library.nonNullableByDefaultCompiledMode) ==
compilationMode,
"Cannot load ${library.nonNullableByDefaultCompiledMode} "
"into component with mode $compilationMode");
assert(() {
debugPath.add(library!.name ?? library.importUri.toString());
return true;
}());
library.annotations = readAnnotationList(library);
_readLibraryDependencies(library);
_readAdditionalExports(library);
_readLibraryParts(library);
_readTypedefList(library);
_readClassList(library, classOffsets);
_readExtensionList(library);
library.fieldsInternal = _readFieldList(library);
library.proceduresInternal = _readProcedureList(library, procedureOffsets);
assert(((_) => true)(debugPath.removeLast()));
_currentLibrary = null;
return library;
}
void _readTypedefList(Library library) {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
library.typedefsInternal = emptyListOfTypedef;
} else {
library.typedefsInternal = new List<Typedef>.generate(
length, (int index) => readTypedef()..parent = library,
growable: useGrowableLists);
}
}
void _readClassList(Library library, List<int> classOffsets) {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
library.classesInternal = emptyListOfClass;
} else {
library.classesInternal = new List<Class>.generate(length, (int index) {
_byteOffset = classOffsets[index];
return readClass(classOffsets[index + 1])..parent = library;
}, growable: useGrowableLists);
_byteOffset = classOffsets.last;
}
}
void _readExtensionList(Library library) {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
library.extensionsInternal = emptyListOfExtension;
} else {
library.extensionsInternal = new List<Extension>.generate(
length, (int index) => readExtension()..parent = library,
growable: useGrowableLists);
}
}
List<Field> _readFieldList(TreeNode parent) {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
return emptyListOfField;
}
return new List<Field>.generate(
length, (int index) => readField()..parent = parent,
growable: useGrowableLists);
}
List<Procedure> _readProcedureList(
TreeNode parent, List<int> procedureOffsets) {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
return emptyListOfProcedure;
}
List<Procedure> list = new List<Procedure>.generate(length, (int index) {
_byteOffset = procedureOffsets[index];
return readProcedure(procedureOffsets[index + 1])..parent = parent;
}, growable: useGrowableLists);
_byteOffset = procedureOffsets.last;
return list;
}
void _readLibraryDependencies(Library library) {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
library.dependencies = emptyListOfLibraryDependency;
} else {
library.dependencies = new List<LibraryDependency>.generate(
length, (int index) => readLibraryDependency()..parent = library,
growable: useGrowableLists);
}
}
LibraryDependency readLibraryDependency() {
int fileOffset = readOffset();
int flags = readByte();
List<Expression> annotations = readExpressionList();
Reference targetLibrary = readNonNullLibraryReference();
String? prefixName = readStringOrNullIfEmpty();
List<Combinator> names = readCombinatorList();
return new LibraryDependency.byReference(
flags, annotations, targetLibrary, prefixName, names)
..fileOffset = fileOffset;
}
void _readAdditionalExports(Library library) {
int numExportedReference = readUInt30();
if (numExportedReference != 0) {
library.additionalExports.clear();
for (int i = 0; i < numExportedReference; i++) {
CanonicalName exportedName = readNonNullCanonicalNameReference();
Reference reference = exportedName.reference;
library.additionalExports.add(reference);
}
}
}
Combinator readCombinator() {
bool isShow = readByte() == 1;
List<String> names = readStringReferenceList();
return new Combinator(isShow, names);
}
List<Combinator> readCombinatorList() {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
return emptyListOfCombinator;
}
return new List<Combinator>.generate(length, (_) => readCombinator(),
growable: useGrowableLists);
}
void _readLibraryParts(Library library) {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
library.parts = emptyListOfLibraryPart;
} else {
library.parts = new List<LibraryPart>.generate(
length, (int index) => readLibraryPart()..parent = library,
growable: useGrowableLists);
}
}
LibraryPart readLibraryPart() {
List<Expression> annotations = readExpressionList();
String partUri = readStringReference();
return new LibraryPart(annotations, partUri);
}
Typedef readTypedef() {
CanonicalName canonicalName = readNonNullCanonicalNameReference();
Reference reference = canonicalName.reference;
Typedef? node = reference.node as Typedef?;
if (alwaysCreateNewNamedNodes) {
node = null;
}
Uri fileUri = readUriReference();
int fileOffset = readOffset();
String name = readStringReference();
if (node == null) {
node = new Typedef(name, null, reference: reference, fileUri: fileUri);
}
node.annotations = readAnnotationList(node);
readAndPushTypeParameterList(node.typeParameters, node);
DartType type = readDartType();
readAndPushTypeParameterList(node.typeParametersOfFunctionType, node);
node.positionalParameters.clear();
node.positionalParameters.addAll(readAndPushVariableDeclarationList());
setParents(node.positionalParameters, node);
node.namedParameters.clear();
node.namedParameters.addAll(readAndPushVariableDeclarationList());
setParents(node.namedParameters, node);
typeParameterStack.length = 0;
variableStack.length = 0;
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();
// 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;
List<int> procedureOffsets = new List<int>.generate(
procedureCount + 1, (_) => _componentStartOffset + readUint32(),
growable: false);
_byteOffset = savedByteOffset;
CanonicalName canonicalName = readNonNullCanonicalNameReference();
Reference reference = canonicalName.reference;
Class? node = reference.node as Class?;
if (alwaysCreateNewNamedNodes) {
node = null;
}
Uri fileUri = readUriReference();
int startFileOffset = readOffset();
int fileOffset = readOffset();
int fileEndOffset = readOffset();
int flags = readByte();
String name = readStringReference();
if (node == null) {
node = new Class(name: name, reference: reference, fileUri: fileUri)
..dirty = false;
}
node.startFileOffset = startFileOffset;
node.fileOffset = fileOffset;
node.fileEndOffset = fileEndOffset;
node.flags = flags;
List<Expression> annotations = readAnnotationList(node);
assert(() {
debugPath.add(name);
return true;
}());
assert(typeParameterStack.length == 0);
readAndPushTypeParameterList(node.typeParameters, node);
Supertype? supertype = readSupertypeOption();
Supertype? mixedInType = readSupertypeOption();
node.implementedTypes = readSupertypeList();
if (_disableLazyClassReading) {
readClassPartialContent(node, procedureOffsets);
} else {
_setLazyLoadClass(node, procedureOffsets);
}
typeParameterStack.length = 0;
// ignore: unnecessary_null_comparison
assert(debugPath.removeLast() != null);
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 = readNonNullCanonicalNameReference();
Reference reference = canonicalName.reference;
Extension? node = reference.node as Extension?;
if (alwaysCreateNewNamedNodes) {
node = null;
}
String name = readStringReference();
assert(() {
debugPath.add(name);
return true;
}());
List<Expression> annotations = readAnnotationList();
Uri fileUri = readUriReference();
if (node == null) {
node = new Extension(name: name, reference: reference, fileUri: fileUri);
}
node.annotations = annotations;
setParents(annotations, node);
node.fileOffset = readOffset();
node.flags = readByte();
readAndPushTypeParameterList(node.typeParameters, node);
DartType onType = readDartType();
if (readAndCheckOptionTag()) {
ExtensionTypeShowHideClause showHideClause =
node.showHideClause = new ExtensionTypeShowHideClause();
readSupertypeList(showHideClause.shownSupertypes);
readNonNullReferenceList(showHideClause.shownMethods);
readNonNullReferenceList(showHideClause.shownGetters);
readNonNullReferenceList(showHideClause.shownSetters);
readNonNullReferenceList(showHideClause.shownOperators);
readSupertypeList(showHideClause.hiddenSupertypes);
readNonNullReferenceList(showHideClause.hiddenMethods);
readNonNullReferenceList(showHideClause.hiddenGetters);
readNonNullReferenceList(showHideClause.hiddenSetters);
readNonNullReferenceList(showHideClause.hiddenOperators);
}
typeParameterStack.length = 0;
node.name = name;
node.fileUri = fileUri;
node.onType = onType;
node.members = _readExtensionMemberDescriptorList();
return node;
}
List<ExtensionMemberDescriptor> _readExtensionMemberDescriptorList() {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use a
// constant one for the empty list.
return emptyListOfExtensionMemberDescriptor;
}
return new List<ExtensionMemberDescriptor>.generate(
length, (_) => _readExtensionMemberDescriptor(),
growable: useGrowableLists);
}
ExtensionMemberDescriptor _readExtensionMemberDescriptor() {
Name name = readName();
int kind = readByte();
int flags = readByte();
CanonicalName canonicalName = readNonNullCanonicalNameReference();
return new ExtensionMemberDescriptor(
name: name,
kind: ExtensionMemberKind.values[kind],
member: canonicalName.reference)
..flags = flags;
}
/// Reads the partial content of a class, namely fields, procedures,
/// constructors and redirecting factory constructors.
void readClassPartialContent(Class node, List<int> procedureOffsets) {
node.fieldsInternal = _readFieldList(node);
_readConstructorList(node);
node.proceduresInternal = _readProcedureList(node, procedureOffsets);
_readRedirectingFactoryList(node);
}
void _readConstructorList(Class node) {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use a
// constant one for the empty list.
node.constructorsInternal = emptyListOfConstructor;
} else {
node.constructorsInternal = new List<Constructor>.generate(
length, (int index) => readConstructor()..parent = node,
growable: useGrowableLists);
}
}
void _readRedirectingFactoryList(Class node) {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use a
// constant one for the empty list.
node.redirectingFactoryConstructorsInternal =
emptyListOfRedirectingFactory;
} else {
node.redirectingFactoryConstructorsInternal =
new List<RedirectingFactory>.generate(
length, (int index) => readRedirectingFactory()..parent = node,
growable: useGrowableLists);
}
}
/// 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);
CanonicalName fieldCanonicalName = readNonNullCanonicalNameReference();
Reference fieldReference = fieldCanonicalName.reference;
CanonicalName getterCanonicalName = readNonNullCanonicalNameReference();
Reference getterReference = getterCanonicalName.reference;
CanonicalName? setterCanonicalName = readNullableCanonicalNameReference();
Reference? setterReference = setterCanonicalName?.reference;
Field? node = fieldReference.node as Field?;
if (alwaysCreateNewNamedNodes) {
node = null;
}
Uri fileUri = readUriReference();
int fileOffset = readOffset();
int fileEndOffset = readOffset();
int flags = readUInt30();
Name name = readName();
if (node == null) {
if (setterReference != null) {
node = new Field.mutable(name,
fieldReference: fieldReference,
getterReference: getterReference,
setterReference: setterReference,
fileUri: fileUri);
} else {
node = new Field.immutable(name,
fieldReference: fieldReference,
getterReference: getterReference,
fileUri: fileUri);
}
}
List<Expression> annotations = readAnnotationList(node);
assert(() {
debugPath.add(name.text);
return true;
}());
DartType type = readDartType();
Expression? initializer = readExpressionOption();
int transformerFlags = getAndResetTransformerFlags();
assert(((_) => true)(debugPath.removeLast()));
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);
CanonicalName canonicalName = readNonNullCanonicalNameReference();
Reference reference = canonicalName.reference;
Constructor? node = reference.node as Constructor?;
if (alwaysCreateNewNamedNodes) {
node = null;
}
Uri fileUri = readUriReference();
int startFileOffset = readOffset();
int fileOffset = readOffset();
int fileEndOffset = readOffset();
int flags = readByte();
Name name = readName();
List<Expression> annotations = readAnnotationList();
assert(() {
debugPath.add(name.text);
return true;
}());
FunctionNode function = readFunctionNode();
if (node == null) {
node = new Constructor(function,
reference: reference, name: name, fileUri: fileUri);
}
pushVariableDeclarations(function.positionalParameters);
pushVariableDeclarations(function.namedParameters);
_readInitializers(node);
variableStack.length = 0;
int transformerFlags = getAndResetTransformerFlags();
assert(((_) => true)(debugPath.removeLast()));
node.startFileOffset = startFileOffset;
node.fileOffset = fileOffset;
node.fileEndOffset = fileEndOffset;
node.flags = flags;
node.name = name;
node.fileUri = fileUri;
node.annotations = annotations;
setParents(annotations, node);
node.function = function..parent = node;
node.transformerFlags = transformerFlags;
return node;
}
Procedure readProcedure(int endOffset) {
int tag = readByte();
assert(tag == Tag.Procedure);
CanonicalName canonicalName = readNonNullCanonicalNameReference();
Reference reference = canonicalName.reference;
Procedure? node = reference.node as Procedure?;
if (alwaysCreateNewNamedNodes) {
node = null;
}
Uri fileUri = readUriReference();
int startFileOffset = readOffset();
int fileOffset = readOffset();
int fileEndOffset = readOffset();
int kindIndex = readByte();
ProcedureKind kind = ProcedureKind.values[kindIndex];
ProcedureStubKind stubKind = ProcedureStubKind.values[readByte()];
int flags = readUInt30();
Name name = readName();
List<Expression> annotations = readAnnotationList();
assert(() {
debugPath.add(name.text);
return true;
}());
int functionNodeSize = endOffset - _byteOffset;
// Read small factories up front. Postpone everything else.
bool readFunctionNodeNow =
(kind == ProcedureKind.Factory && functionNodeSize <= 50) ||
_disableLazyReading;
Reference? stubTargetReference = readNullableMemberReference();
FunctionType? signatureType = readDartTypeOption() as FunctionType?;
FunctionNode function = readFunctionNode(
lazyLoadBody: !readFunctionNodeNow, outerEndOffset: endOffset);
if (node == null) {
node = new Procedure(name, kind, function,
reference: reference, fileUri: fileUri);
} else {
assert(node.kind == kind);
}
int transformerFlags = getAndResetTransformerFlags();
assert(((_) => true)(debugPath.removeLast()));
node.startFileOffset = startFileOffset;
node.fileOffset = fileOffset;
node.fileEndOffset = fileEndOffset;
node.flags = flags;
node.name = name;
node.fileUri = fileUri;
node.annotations = annotations;
setParents(annotations, node);
node.function = function..parent = node;
node.setTransformerFlagsWithoutLazyLoading(transformerFlags);
node.stubKind = stubKind;
node.stubTargetReference = stubTargetReference;
node.signatureType = signatureType;
assert((node.stubKind == ProcedureStubKind.ConcreteForwardingStub &&
node.stubTargetReference != null) ||
!(node.isForwardingStub && node.function.body != null));
assert(!(node.isMemberSignature && node.stubTargetReference == null),
"No member signature origin for member signature $node.");
return node;
}
RedirectingFactory readRedirectingFactory() {
int tag = readByte();
assert(tag == Tag.RedirectingFactory);
CanonicalName canonicalName = readNonNullCanonicalNameReference();
Reference reference = canonicalName.reference;
RedirectingFactory? node = reference.node as RedirectingFactory?;
if (alwaysCreateNewNamedNodes) {
node = null;
}
Uri fileUri = readUriReference();
int fileOffset = readOffset();
int fileEndOffset = readOffset();
int flags = readByte();
Name name = readName();
assert(() {
debugPath.add(name.text);
return true;
}());
List<Expression> annotations = readAnnotationList();
Reference targetReference = readNonNullMemberReference();
List<DartType> typeArguments = readDartTypeList();
FunctionNode function = readFunctionNode(outerEndOffset: fileEndOffset);
if (node == null) {
node = new RedirectingFactory(targetReference,
reference: reference,
name: name,
fileUri: fileUri,
function: function,
typeArguments: typeArguments);
} else {
node.name = name;
node.fileUri = fileUri;
node.targetReference = targetReference;
node.typeArguments.addAll(typeArguments);
node.function = function..parent = node;
}
node.fileOffset = fileOffset;
node.fileEndOffset = fileEndOffset;
node.flags = flags;
node.annotations = annotations;
setParents(annotations, node);
debugPath.removeLast();
return node;
}
void _readInitializers(Constructor constructor) {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use a
// constant one for the empty list.
constructor.initializers = emptyListOfInitializer;
} else {
constructor.initializers = new List<Initializer>.generate(
length, (int index) => readInitializer()..parent = constructor,
growable: useGrowableLists);
}
}
Initializer readInitializer() {
int tag = readByte();
bool isSynthetic = readByte() == 1;
switch (tag) {
case Tag.InvalidInitializer:
return _readInvalidInitializer();
case Tag.FieldInitializer:
return _readFieldInitializer(isSynthetic);
case Tag.SuperInitializer:
return _readSuperInitializer(isSynthetic);
case Tag.RedirectingInitializer:
return _readRedirectingInitializer();
case Tag.LocalInitializer:
return _readLocalInitializer();
case Tag.AssertInitializer:
return _readAssertInitializer();
default:
throw fail('unexpected initializer tag: $tag');
}
}
Initializer _readInvalidInitializer() {
return new InvalidInitializer();
}
Initializer _readFieldInitializer(bool isSynthetic) {
Reference reference = readNonNullMemberReference();
Expression value = readExpression();
return new FieldInitializer.byReference(reference, value)
..isSynthetic = isSynthetic;
}
Initializer _readSuperInitializer(bool isSynthetic) {
int offset = readOffset();
Reference reference = readNonNullMemberReference();
Arguments arguments = readArguments();
return new SuperInitializer.byReference(reference, arguments)
..isSynthetic = isSynthetic
..fileOffset = offset;
}
Initializer _readRedirectingInitializer() {
int offset = readOffset();
return new RedirectingInitializer.byReference(
readNonNullMemberReference(), readArguments())
..fileOffset = offset;
}
Initializer _readLocalInitializer() {
return new LocalInitializer(readAndPushVariableDeclaration());
}
Initializer _readAssertInitializer() {
return new AssertInitializer(readStatement() as AssertStatement);
}
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;
List<TypeParameter> typeParameters = readAndPushTypeParameterList();
readUInt30(); // total parameter count.
int requiredParameterCount = readUInt30();
int variableStackHeight = variableStack.length;
List<VariableDeclaration> positional = readAndPushVariableDeclarationList();
List<VariableDeclaration> named = readAndPushVariableDeclarationList();
DartType returnType = readDartType();
DartType? futureValueType = readDartTypeOption();
int oldLabelStackBase = labelStackBase;
int oldSwitchCaseStackBase = switchCaseStackBase;
if (lazyLoadBody && outerEndOffset > 0) {
lazyLoadBody = outerEndOffset - _byteOffset >
2; // e.g. outline has Tag.Something and Tag.EmptyStatement
}
Statement? body;
if (!lazyLoadBody) {
labelStackBase = labelStack.length;
switchCaseStackBase = switchCaseStack.length;
body = readStatementOption();
}
FunctionNode result = new FunctionNode(body,
typeParameters: typeParameters,
requiredParameterCount: requiredParameterCount,
positionalParameters: positional,
namedParameters: named,
returnType: returnType,
asyncMarker: asyncMarker,
dartAsyncMarker: dartAsyncMarker,
futureValueType: futureValueType)
..fileOffset = offset
..fileEndOffset = endOffset;
if (lazyLoadBody) {
_setLazyLoadFunction(result, oldLabelStackBase, oldSwitchCaseStackBase,
variableStackHeight);
}
labelStackBase = oldLabelStackBase;
switchCaseStackBase = oldSwitchCaseStackBase;
variableStack.length = variableStackHeight;
typeParameterStack.length = typeParameterStackHeight;
return result;
}
void _setLazyLoadFunction(FunctionNode result, int oldLabelStackBase,
int oldSwitchCaseStackBase, 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;
switchCaseStackBase = oldSwitchCaseStackBase;
variableStack.length = variableStackHeight;
typeParameterStack.clear();
TreeNode? parent = result.parent;
if (parent is Procedure) {
parent.transformerFlags |= getAndResetTransformerFlags();
}
};
}
void pushVariableDeclaration(VariableDeclaration variable) {
variableStack.add(variable);
}
void pushVariableDeclarations(List<VariableDeclaration> variables) {
variableStack.addAll(variables);
}
VariableDeclaration readVariableReference() {
int index = readUInt30();
if (index >= variableStack.length) {
throw fail('Unexpected variable index: $index. '
'Current variable count: ${variableStack.length}.');
}
return variableStack[index];
}
LogicalExpressionOperator logicalOperatorToEnum(int index) {
switch (index) {
case 0:
return LogicalExpressionOperator.AND;
case 1:
return LogicalExpressionOperator.OR;
default:
throw fail('unexpected logical operator index: $index');
}
}
List<Expression> readExpressionList() {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use a
// constant one for the empty list.
return emptyListOfExpression;
}
return new List<Expression>.generate(length, (_) => readExpression(),
growable: useGrowableLists);
}
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 _readLoadLibrary();
case Tag.CheckLibraryIsLoaded:
return _readCheckLibraryIsLoaded();
case Tag.InvalidExpression:
return _readInvalidExpression();
case Tag.VariableGet:
return _readVariableGet();
case Tag.SpecializedVariableGet:
return _readSpecializedVariableGet(tagByte);
case Tag.VariableSet:
return _readVariableSet();
case Tag.SpecializedVariableSet:
return _readSpecializedVariableSet(tagByte);
case Tag.InstanceGet:
return _readInstanceGet();
case Tag.InstanceTearOff:
return _readInstanceTearOff();
case Tag.DynamicGet:
return _readDynamicGet();
case Tag.InstanceSet:
return _readInstanceSet();
case Tag.DynamicSet:
return _readDynamicSet();
case Tag.SuperPropertyGet:
return _readSuperPropertyGet();
case Tag.SuperPropertySet:
return _readSuperPropertySet();
case Tag.StaticGet:
return _readStaticGet();
case Tag.StaticTearOff:
return _readStaticTearOff();
case Tag.StaticSet:
return _readStaticSet();
case Tag.ConstructorTearOff:
return _readConstructorTearOff();
case Tag.TypedefTearOff:
return _readTypedefTearOff();
case Tag.RedirectingFactoryTearOff:
return _readRedirectingFactoryTearOff();
case Tag.InstanceInvocation:
return _readInstanceInvocation();
case Tag.InstanceGetterInvocation:
return _readInstanceGetterInvocation();
case Tag.DynamicInvocation:
return _readDynamicInvocation();
case Tag.FunctionInvocation:
return _readFunctionInvocation();
case Tag.FunctionTearOff:
return _readFunctionTearOff();
case Tag.LocalFunctionInvocation:
return _readLocalFunctionInvocation();
case Tag.EqualsNull:
return _readEqualsNull();
case Tag.EqualsCall:
return _readEqualsCall();
case Tag.SuperMethodInvocation:
return _readSuperMethodInvocation();
case Tag.StaticInvocation:
return _readStaticInvocation();
case Tag.ConstStaticInvocation:
return _readConstStaticInvocation();
case Tag.ConstructorInvocation:
return _readConstructorInvocation();
case Tag.ConstConstructorInvocation:
return _readConstConstructorInvocation();
case Tag.Not:
return _readNot();
case Tag.NullCheck:
return _readNullCheck();
case Tag.LogicalExpression:
return _readLogicalExpression();
case Tag.ConditionalExpression:
return _readConditionalExpression();
case Tag.StringConcatenation:
return _readStringConcatenation();
case Tag.ListConcatenation:
return _readListConcatenation();
case Tag.SetConcatenation:
return _readSetConcatenation();
case Tag.MapConcatenation:
return _readMapConcatenation();
case Tag.InstanceCreation:
return _readInstanceCreation();
case Tag.FileUriExpression:
return _readFileUriExpression();
case Tag.IsExpression:
return _readIsExpression();
case Tag.AsExpression:
return _readAsExpression();
case Tag.StringLiteral:
return _readStringLiteral();
case Tag.SpecializedIntLiteral:
return _readSpecializedIntLiteral(tagByte);
case Tag.PositiveIntLiteral:
return _readPositiveIntLiteral();
case Tag.NegativeIntLiteral:
return _readNegativeIntLiteral();
case Tag.BigIntLiteral:
return _readBigIntLiteral();
case Tag.DoubleLiteral:
return _readDoubleLiteral();
case Tag.TrueLiteral:
return _readTrueLiteral();
case Tag.FalseLiteral:
return _readFalseLiteral();
case Tag.NullLiteral:
return _readNullLiteral();
case Tag.SymbolLiteral:
return _readSymbolLiteral();
case Tag.TypeLiteral:
return _readTypeLiteral();
case Tag.ThisExpression:
return _readThisLiteral();
case Tag.Rethrow:
return _readRethrow();
case Tag.Throw:
return _readThrow();
case Tag.ListLiteral:
return _readListLiteral();
case Tag.ConstListLiteral:
return _readConstListLiteral();
case Tag.SetLiteral:
return _readSetLiteral();
case Tag.ConstSetLiteral:
return _readConstSetLiteral();
case Tag.MapLiteral:
return _readMapLiteral();
case Tag.ConstMapLiteral:
return _readConstMapLiteral();
case Tag.AwaitExpression:
return _readAwaitExpression();
case Tag.FunctionExpression:
return _readFunctionExpression();
case Tag.Let:
return _readLet();
case Tag.BlockExpression:
return _readBlockExpression();
case Tag.Instantiation:
return _readInstantiation();
case Tag.ConstantExpression:
return _readConstantExpression();
default:
throw fail('unexpected expression tag: $tag');
}
}
Expression _readLoadLibrary() {
return new LoadLibrary(readLibraryDependencyReference());
}
Expression _readCheckLibraryIsLoaded() {
return new CheckLibraryIsLoaded(readLibraryDependencyReference());
}
Expression _readInvalidExpression() {
int offset = readOffset();
return new InvalidExpression(
readStringOrNullIfEmpty(), readExpressionOption())
..fileOffset = offset;
}
Expression _readVariableGet() {
int offset = readOffset();
readUInt30(); // offset of the variable declaration in the binary.
return new VariableGet(readVariableReference(), readDartTypeOption())
..fileOffset = offset;
}
Expression _readSpecializedVariableGet(int tagByte) {
int index = tagByte & Tag.SpecializedPayloadMask;
int offset = readOffset();
readUInt30(); // offset of the variable declaration in the binary.
return new VariableGet(variableStack[index])..fileOffset = offset;
}
Expression _readVariableSet() {
int offset = readOffset();
readUInt30(); // offset of the variable declaration in the binary.
return new VariableSet(readVariableReference(), readExpression())
..fileOffset = offset;
}
Expression _readSpecializedVariableSet(int tagByte) {
int index = tagByte & Tag.SpecializedPayloadMask;
int offset = readOffset();
readUInt30(); // offset of the variable declaration in the binary.
return new VariableSet(variableStack[index], readExpression())
..fileOffset = offset;
}
Expression _readInstanceGet() {
InstanceAccessKind kind = InstanceAccessKind.values[readByte()];
int offset = readOffset();
return new InstanceGet.byReference(kind, readExpression(), readName(),
resultType: readDartType(),
interfaceTargetReference: readNonNullInstanceMemberReference())
..fileOffset = offset;
}
Expression _readInstanceTearOff() {
InstanceAccessKind kind = InstanceAccessKind.values[readByte()];
int offset = readOffset();
return new InstanceTearOff.byReference(kind, readExpression(), readName(),
resultType: readDartType(),
interfaceTargetReference: readNonNullInstanceMemberReference())
..fileOffset = offset;
}
Expression _readDynamicGet() {
DynamicAccessKind kind = DynamicAccessKind.values[readByte()];
int offset = readOffset();
return new DynamicGet(kind, readExpression(), readName())
..fileOffset = offset;
}
Expression _readInstanceSet() {
InstanceAccessKind kind = InstanceAccessKind.values[readByte()];
int offset = readOffset();
return new InstanceSet.byReference(
kind, readExpression(), readName(), readExpression(),
interfaceTargetReference: readNonNullInstanceMemberReference())
..fileOffset = offset;
}
Expression _readDynamicSet() {
DynamicAccessKind kind = DynamicAccessKind.values[readByte()];
int offset = readOffset();
return new DynamicSet(kind, readExpression(), readName(), readExpression())
..fileOffset = offset;
}
Expression _readSuperPropertyGet() {
int offset = readOffset();
addTransformerFlag(TransformerFlag.superCalls);
return new SuperPropertyGet.byReference(
readName(), readNullableInstanceMemberReference())
..fileOffset = offset;
}
Expression _readSuperPropertySet() {
int offset = readOffset();
addTransformerFlag(TransformerFlag.superCalls);
return new SuperPropertySet.byReference(
readName(), readExpression(), readNullableInstanceMemberReference())
..fileOffset = offset;
}
Expression _readStaticGet() {
int offset = readOffset();
return new StaticGet.byReference(readNonNullMemberReference())
..fileOffset = offset;
}
Expression _readConstructorTearOff() {
int offset = readOffset();
Reference constructorReference = readNonNullMemberReference();
return new ConstructorTearOff.byReference(constructorReference)
..fileOffset = offset;
}
Expression _readTypedefTearOff() {
List<TypeParameter> typeParameters = readAndPushTypeParameterList();
Expression expression = readExpression();
List<DartType> typeArguments = readDartTypeList();
typeParameterStack.length -= typeParameters.length;
return new TypedefTearOff(typeParameters, expression, typeArguments);
}
Expression _readRedirectingFactoryTearOff() {
int offset = readOffset();
Reference constructorReference = readNonNullMemberReference();
return new RedirectingFactoryTearOff.byReference(constructorReference)
..fileOffset = offset;
}
Expression _readStaticTearOff() {
int offset = readOffset();
return new StaticTearOff.byReference(readNonNullMemberReference())
..fileOffset = offset;
}
Expression _readStaticSet() {
int offset = readOffset();
return new StaticSet.byReference(
readNonNullMemberReference(), readExpression())
..fileOffset = offset;
}
Expression _readInstanceInvocation() {
InstanceAccessKind kind = InstanceAccessKind.values[readByte()];
int flags = readByte();
int offset = readOffset();
return new InstanceInvocation.byReference(
kind, readExpression(), readName(), readArguments(),
functionType: readDartType() as FunctionType,
interfaceTargetReference: readNonNullInstanceMemberReference())
..fileOffset = offset
..flags = flags;
}
Expression _readInstanceGetterInvocation() {
InstanceAccessKind kind = InstanceAccessKind.values[readByte()];
int flags = readByte();
int offset = readOffset();
Expression receiver = readExpression();
Name name = readName();
Arguments arguments = readArguments();
DartType functionType = readDartType();
// `const DynamicType()` is used to encode a missing function type.
assert(functionType is FunctionType || functionType is DynamicType,
"Unexpected function type $functionType for InstanceGetterInvocation");
Reference interfaceTargetReference = readNonNullInstanceMemberReference();
return new InstanceGetterInvocation.byReference(
kind, receiver, name, arguments,
functionType: functionType is FunctionType ? functionType : null,
interfaceTargetReference: interfaceTargetReference)
..fileOffset = offset
..flags = flags;
}
Expression _readDynamicInvocation() {
DynamicAccessKind kind = DynamicAccessKind.values[readByte()];
int offset = readOffset();
return new DynamicInvocation(
kind, readExpression(), readName(), readArguments())
..fileOffset = offset;
}
Expression _readFunctionInvocation() {
FunctionAccessKind kind = FunctionAccessKind.values[readByte()];
int offset = readOffset();
Expression receiver = readExpression();
Arguments arguments = readArguments();
DartType functionType = readDartType();
// `const DynamicType()` is used to encode a missing function type.
assert(functionType is FunctionType || functionType is DynamicType,
"Unexpected function type $functionType for FunctionInvocation");
return new FunctionInvocation(kind, receiver, arguments,
functionType: functionType is FunctionType ? functionType : null)
..fileOffset = offset;
}
Expression _readFunctionTearOff() {
int offset = readOffset();
return new FunctionTearOff(readExpression())..fileOffset = offset;
}
Expression _readLocalFunctionInvocation() {
int offset = readOffset();
readUInt30(); // offset of the variable declaration in the binary.
VariableDeclaration variable = readVariableReference();
return new LocalFunctionInvocation(variable, readArguments(),
functionType: readDartType() as FunctionType)
..fileOffset = offset;
}
Expression _readEqualsNull() {
int offset = readOffset();
return new EqualsNull(readExpression())..fileOffset = offset;
}
Expression _readEqualsCall() {
int offset = readOffset();
return new EqualsCall.byReference(readExpression(), readExpression(),
functionType: readDartType() as FunctionType,
interfaceTargetReference: readNonNullInstanceMemberReference())
..fileOffset = offset;
}
Expression _readSuperMethodInvocation() {
int offset = readOffset();
addTransformerFlag(TransformerFlag.superCalls);
return new SuperMethodInvocation.byReference(
readName(), readArguments(), readNullableInstanceMemberReference())
..fileOffset = offset;
}
Expression _readStaticInvocation() {
int offset = readOffset();
return new StaticInvocation.byReference(
readNonNullMemberReference(), readArguments(),
isConst: false)
..fileOffset = offset;
}
Expression _readConstStaticInvocation() {
int offset = readOffset();
return new StaticInvocation.byReference(
readNonNullMemberReference(), readArguments(),
isConst: true)
..fileOffset = offset;
}
Expression _readConstructorInvocation() {
int offset = readOffset();
return new ConstructorInvocation.byReference(
readNonNullMemberReference(), readArguments(),
isConst: false)
..fileOffset = offset;
}
Expression _readConstConstructorInvocation() {
int offset = readOffset();
return new ConstructorInvocation.byReference(
readNonNullMemberReference(), readArguments(),
isConst: true)
..fileOffset = offset;
}
Expression _readNot() {
return new Not(readExpression());
}
Expression _readNullCheck() {
int offset = readOffset();
return new NullCheck(readExpression())..fileOffset = offset;
}
Expression _readLogicalExpression() {
return new LogicalExpression(
readExpression(), logicalOperatorToEnum(readByte()), readExpression());
}
Expression _readConditionalExpression() {
return new ConditionalExpression(
readExpression(),
readExpression(),
readExpression(),
// TODO(johnniwinther): Change this to use `readDartType`.
readDartTypeOption()!);
}
Expression _readStringConcatenation() {
int offset = readOffset();
return new StringConcatenation(readExpressionList())..fileOffset = offset;
}
Expression _readListConcatenation() {
int offset = readOffset();
DartType typeArgument = readDartType();
return new ListConcatenation(readExpressionList(),
typeArgument: typeArgument)
..fileOffset = offset;
}
Expression _readSetConcatenation() {
int offset = readOffset();
DartType typeArgument = readDartType();
return new SetConcatenation(readExpressionList(),
typeArgument: typeArgument)
..fileOffset = offset;
}
Expression _readMapConcatenation() {
int offset = readOffset();
DartType keyType = readDartType();
DartType valueType = readDartType();
return new MapConcatenation(readExpressionList(),
keyType: keyType, valueType: valueType)
..fileOffset = offset;
}
Expression _readInstanceCreation() {
int offset = readOffset();
Reference classReference = readNonNullClassReference();
List<DartType> typeArguments = readDartTypeList();
int fieldValueCount = readUInt30();
Map<Reference, Expression> fieldValues = <Reference, Expression>{};
for (int i = 0; i < fieldValueCount; i++) {
final Reference fieldRef = readNonNullCanonicalNameReference().reference;
final Expression value = readExpression();
fieldValues[fieldRef] = value;
}
int assertCount = readUInt30();
List<AssertStatement> asserts;
if (!useGrowableLists && assertCount == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
asserts = emptyListOfAssertStatement;
} else {
asserts = new List<AssertStatement>.generate(
assertCount, (_) => readStatement() as AssertStatement,
growable: false);
}
List<Expression> unusedArguments = readExpressionList();
return new InstanceCreation(
classReference, typeArguments, fieldValues, asserts, unusedArguments)
..fileOffset = offset;
}
Expression _readFileUriExpression() {
Uri fileUri = readUriReference();
int offset = readOffset();
return new FileUriExpression(readExpression(), fileUri)
..fileOffset = offset;
}
Expression _readIsExpression() {
int offset = readOffset();
int flags = readByte();
return new IsExpression(readExpression(), readDartType())
..fileOffset = offset
..flags = flags;
}
Expression _readAsExpression() {
int offset = readOffset();
int flags = readByte();
return new AsExpression(readExpression(), readDartType())
..fileOffset = offset
..flags = flags;
}
Expression _readStringLiteral() {
return new StringLiteral(readStringReference());
}
Expression _readSpecializedIntLiteral(int tagByte) {
int biasedValue = tagByte & Tag.SpecializedPayloadMask;
return new IntLiteral(biasedValue - Tag.SpecializedIntLiteralBias);
}
Expression _readPositiveIntLiteral() {
return new IntLiteral(readUInt30());
}
Expression _readNegativeIntLiteral() {
return new IntLiteral(-readUInt30());
}
Expression _readBigIntLiteral() {
return new IntLiteral(int.parse(readStringReference()));
}
Expression _readDoubleLiteral() {
return new DoubleLiteral(readDouble());
}
Expression _readTrueLiteral() {
return new BoolLiteral(true);
}
Expression _readFalseLiteral() {
return new BoolLiteral(false);
}
Expression _readNullLiteral() {
return new NullLiteral();
}
Expression _readSymbolLiteral() {
return new SymbolLiteral(readStringReference());
}
Expression _readTypeLiteral() {
return new TypeLiteral(readDartType());
}
Expression _readThisLiteral() {
return new ThisExpression();
}
Expression _readRethrow() {
int offset = readOffset();
return new Rethrow()..fileOffset = offset;
}
Expression _readThrow() {
int offset = readOffset();
return new Throw(readExpression())..fileOffset = offset;
}
Expression _readListLiteral() {
int offset = readOffset();
DartType typeArgument = readDartType();
return new ListLiteral(readExpressionList(),
typeArgument: typeArgument, isConst: false)
..fileOffset = offset;
}
Expression _readConstListLiteral() {
int offset = readOffset();
DartType typeArgument = readDartType();
return new ListLiteral(readExpressionList(),
typeArgument: typeArgument, isConst: true)
..fileOffset = offset;
}
Expression _readSetLiteral() {
int offset = readOffset();
DartType typeArgument = readDartType();
return new SetLiteral(readExpressionList(),
typeArgument: typeArgument, isConst: false)
..fileOffset = offset;
}
Expression _readConstSetLiteral() {
int offset = readOffset();
DartType typeArgument = readDartType();
return new SetLiteral(readExpressionList(),
typeArgument: typeArgument, isConst: true)
..fileOffset = offset;
}
Expression _readMapLiteral() {
int offset = readOffset();
DartType keyType = readDartType();
DartType valueType = readDartType();
return new MapLiteral(readMapLiteralEntryList(),
keyType: keyType, valueType: valueType, isConst: false)
..fileOffset = offset;
}
Expression _readConstMapLiteral() {
int offset = readOffset();
DartType keyType = readDartType();
DartType valueType = readDartType();
return new MapLiteral(readMapLiteralEntryList(),
keyType: keyType, valueType: valueType, isConst: true)
..fileOffset = offset;
}
Expression _readAwaitExpression() {
return new AwaitExpression(readExpression());
}
Expression _readFunctionExpression() {
int offset = readOffset();
return new FunctionExpression(readFunctionNode())..fileOffset = offset;
}
Expression _readLet() {
int offset = readOffset();
VariableDeclaration variable = readVariableDeclaration();
int stackHeight = variableStack.length;
pushVariableDeclaration(variable);
Expression body = readExpression();
variableStack.length = stackHeight;
return new Let(variable, body)..fileOffset = offset;
}
Expression _readBlockExpression() {
int stackHeight = variableStack.length;
List<Statement> statements = readStatementListAlwaysGrowable();
Expression value = readExpression();
variableStack.length = stackHeight;
return new BlockExpression(new Block(statements), value);
}
Expression _readInstantiation() {
Expression expression = readExpression();
List<DartType> typeArguments = readDartTypeList();
return new Instantiation(expression, typeArguments);
}
Expression _readConstantExpression() {
int offset = readOffset();
DartType type = readDartType();
Constant constant = readConstantReference();
return new ConstantExpression(constant, type)..fileOffset = offset;
}
List<MapLiteralEntry> readMapLiteralEntryList() {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
return emptyListOfMapLiteralEntry;
}
return new List<MapLiteralEntry>.generate(length, (_) => readMapEntry(),
growable: useGrowableLists);
}
MapLiteralEntry readMapEntry() {
return new MapLiteralEntry(readExpression(), readExpression());
}
List<Statement> readStatementList() {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
return emptyListOfStatement;
}
return new List<Statement>.generate(length, (_) => readStatement(),
growable: useGrowableLists);
}
List<Statement> readStatementListAlwaysGrowable() {
int length = readUInt30();
return new List<Statement>.generate(length, (_) => readStatement(),
growable: true);
}
Statement? readStatementOrNullIfEmpty() {
Statement 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 _readExpressionStatement();
case Tag.Block:
return _readBlock();
case Tag.AssertBlock:
return _readAssertBlock();
case Tag.EmptyStatement:
return _readEmptyStatement();
case Tag.AssertStatement:
return _readAssertStatement();
case Tag.LabeledStatement:
return _readLabeledStatement();
case Tag.BreakStatement:
return _readBreakStatement();
case Tag.WhileStatement:
return _readWhileStatement();
case Tag.DoStatement:
return _readDoStatement();
case Tag.ForStatement:
return _readForStatement();
case Tag.ForInStatement:
case Tag.AsyncForInStatement:
return _readForInStatement(tag);
case Tag.SwitchStatement:
return _readSwitchStatement();
case Tag.ContinueSwitchStatement:
return _readContinueSwitchStatement();
case Tag.IfStatement:
return _readIfStatement();
case Tag.ReturnStatement:
return _readReturnStatement();
case Tag.TryCatch:
return _readTryCatch();
case Tag.TryFinally:
return _readTryFinally();
case Tag.YieldStatement:
return _readYieldStatement();
case Tag.VariableDeclaration:
return _readVariableDeclaration();
case Tag.FunctionDeclaration:
return _readFunctionDeclaration();
default:
throw fail('unexpected statement tag: $tag');
}
}
Statement _readExpressionStatement() {
return new ExpressionStatement(readExpression());
}
Statement _readEmptyStatement() {
return new EmptyStatement();
}
Statement _readAssertStatement() {
return new AssertStatement(readExpression(),
conditionStartOffset: readOffset(),
conditionEndOffset: readOffset(),
message: readExpressionOption());
}
Statement _readLabeledStatement() {
LabeledStatement label = new LabeledStatement(null);
labelStack.add(label);
label.body = readStatement()..parent = label;
labelStack.removeLast();
return label;
}
Statement _readBreakStatement() {
int offset = readOffset();
int index = readUInt30();
return new BreakStatement(labelStack[labelStackBase + index])
..fileOffset = offset;
}
Statement _readWhileStatement() {
int offset = readOffset();
return new WhileStatement(readExpression(), readStatement())
..fileOffset = offset;
}
Statement _readDoStatement() {
int offset = readOffset();
return new DoStatement(readStatement(), readExpression())
..fileOffset = offset;
}
Statement _readForStatement() {
int variableStackHeight = variableStack.length;
int offset = readOffset();
List<VariableDeclaration> variables = readAndPushVariableDeclarationList();
Expression? condition = readExpressionOption();
List<Expression> updates = readExpressionList();
Statement body = readStatement();
variableStack.length = variableStackHeight;
return new ForStatement(variables, condition, updates, body)
..fileOffset = offset;
}
Statement _readForInStatement(int tag) {
bool isAsync = tag == Tag.AsyncForInStatement;
int variableStackHeight = variableStack.length;
int offset = readOffset();
int bodyOffset = readOffset();
VariableDeclaration variable = readAndPushVariableDeclaration();
Expression iterable = readExpression();
Statement body = readStatement();
variableStack.length = variableStackHeight;
return new ForInStatement(variable, iterable, body, isAsync: isAsync)
..fileOffset = offset
..bodyOffset = bodyOffset;
}
Statement _readSwitchStatement() {
int offset = readOffset();
Expression expression = readExpression();
int count = readUInt30();
List<SwitchCase> cases;
if (!useGrowableLists && count == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
cases = emptyListOfSwitchCase;
} else {
cases = new List<SwitchCase>.generate(
count,
(_) => new SwitchCase(<Expression>[], <int>[], dummyStatement,
isDefault: false),
growable: useGrowableLists);
}
switchCaseStack.addAll(cases);
for (int i = 0; i < cases.length; ++i) {
_readSwitchCaseInto(cases[i]);
}
switchCaseStack.length -= count;
return new SwitchStatement(expression, cases)..fileOffset = offset;
}
Statement _readContinueSwitchStatement() {
int offset = readOffset();
int index = readUInt30();
return new ContinueSwitchStatement(
switchCaseStack[switchCaseStackBase + index])
..fileOffset = offset;
}
Statement _readIfStatement() {
int offset = readOffset();
return new IfStatement(
readExpression(), readStatement(), readStatementOrNullIfEmpty())
..fileOffset = offset;
}
Statement _readReturnStatement() {
int offset = readOffset();
return new ReturnStatement(readExpressionOption())..fileOffset = offset;
}
Statement _readTryCatch() {
Statement body = readStatement();
int flags = readByte();
return new TryCatch(body, readCatchList(), isSynthetic: flags & 2 == 2);
}
Statement _readTryFinally() {
return new TryFinally(readStatement(), readStatement());
}
Statement _readYieldStatement() {
int offset = readOffset();
int flags = readByte();
return new YieldStatement(readExpression(),
isYieldStar: flags & YieldStatement.FlagYieldStar != 0,
isNative: flags & YieldStatement.FlagNative != 0)
..fileOffset = offset;
}
Statement _readVariableDeclaration() {
VariableDeclaration variable = readVariableDeclaration();
variableStack.add(variable); // Will be popped by the enclosing scope.
return variable;
}
Statement _readFunctionDeclaration() {
int offset = readOffset();
VariableDeclaration variable = readVariableDeclaration();
variableStack.add(variable); // Will be popped by the enclosing scope.
return new FunctionDeclaration(variable, readFunctionNode())
..fileOffset = offset;
}
void _readSwitchCaseInto(SwitchCase caseNode) {
int length = readUInt30();
for (int i = 0; i < length; ++i) {
caseNode.expressionOffsets.add(readOffset());
caseNode.expressions.add(readExpression()..parent = caseNode);
}
caseNode.isDefault = readByte() == 1;
caseNode.body = readStatement()..parent = caseNode;
}
List<Catch> readCatchList() {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
return emptyListOfCatch;
}
return new List<Catch>.generate(length, (_) => readCatch(),
growable: useGrowableLists);
}
Catch readCatch() {
int variableStackHeight = variableStack.length;
int offset = readOffset();
DartType guard = readDartType();
VariableDeclaration? exception = readAndPushVariableDeclarationOption();
VariableDeclaration? stackTrace = readAndPushVariableDeclarationOption();
Statement body = readStatement();
variableStack.length = variableStackHeight;
return new Catch(exception, body, guard: guard, stackTrace: stackTrace)
..fileOffset = offset;
}
Block _readBlock() {
int stackHeight = variableStack.length;
int offset = readOffset();
int endOffset = readOffset();
List<Statement> body = readStatementListAlwaysGrowable();
variableStack.length = stackHeight;
return new Block(body)
..fileOffset = offset
..fileEndOffset = endOffset;
}
AssertBlock _readAssertBlock() {
int stackHeight = variableStack.length;
List<Statement> body = readStatementListAlwaysGrowable();
variableStack.length = stackHeight;
return new AssertBlock(body);
}
Supertype readSupertype() {
InterfaceType type = readDartType(forSupertype: true) as InterfaceType;
assert(
type.nullability == _currentLibrary!.nonNullable,
"In serialized form supertypes should have Nullability.legacy if they "
"are in a library that is opted out of the NNBD feature. If they are "
"in an opted-in library, they should have Nullability.nonNullable.");
return new Supertype.byReference(type.className, type.typeArguments);
}
Supertype? readSupertypeOption() {
return readAndCheckOptionTag() ? readSupertype() : null;
}
List<Supertype> readSupertypeList([List<Supertype>? result]) {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
return emptyListOfSupertype;
}
if (result != null) {
for (int i = 0; i < length; ++i) {
result.add(readSupertype());
}
return result;
} else {
return new List<Supertype>.generate(length, (_) => readSupertype(),
growable: useGrowableLists);
}
}
List<DartType> readDartTypeList() {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
return emptyListOfDartType;
}
return new List<DartType>.generate(length, (_) => readDartType(),
growable: useGrowableLists);
}
List<NamedType> readNamedTypeList() {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use a
// constant one for the empty list.
return emptyListOfNamedType;
}
return new List<NamedType>.generate(length, (_) => readNamedType(),
growable: useGrowableLists);
}
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({bool forSupertype: false}) {
int tag = readByte();
switch (tag) {
case Tag.TypedefType:
return _readTypedefType();
case Tag.InvalidType:
return _readInvalidType();
case Tag.DynamicType:
return _readDynamicType();
case Tag.VoidType:
return _readVoidType();
case Tag.NeverType:
return _readNeverType();
case Tag.InterfaceType:
return _readInterfaceType();
case Tag.SimpleInterfaceType:
return _readSimpleInterfaceType(forSupertype);
case Tag.FunctionType:
return _readFunctionType();
case Tag.SimpleFunctionType:
return _readSimpleFunctionType();
case Tag.TypeParameterType:
return _readTypeParameterType();
default:
throw fail('unexpected dart type tag: $tag');
}
}
DartType _readTypedefType() {
int nullabilityIndex = readByte();
return new TypedefType.byReference(readNonNullTypedefReference(),
Nullability.values[nullabilityIndex], readDartTypeList());
}
DartType _readInvalidType() {
return const InvalidType();
}
DartType _readDynamicType() {
return const DynamicType();
}
DartType _readVoidType() {
return const VoidType();
}
DartType _readNeverType() {
int nullabilityIndex = readByte();
return NeverType.fromNullability(Nullability.values[nullabilityIndex]);
}
DartType _readInterfaceType() {
int nullabilityIndex = readByte();
Reference reference = readNonNullClassReference();
List<DartType> typeArguments = readDartTypeList();
CanonicalName? canonicalName = reference.canonicalName;
if (canonicalName != null &&
canonicalName.name == "FutureOr" &&
canonicalName.parent!.name == "dart:async" &&
canonicalName.parent!.parent != null &&
canonicalName.parent!.parent!.isRoot) {
return new FutureOrType(
typeArguments.single, Nullability.values[nullabilityIndex]);
}
return new InterfaceType.byReference(
reference, Nullability.values[nullabilityIndex], typeArguments);
}
DartType _readSimpleInterfaceType(bool forSupertype) {
int nullabilityIndex = readByte();
Reference classReference = readNonNullClassReference();
CanonicalName? canonicalName = classReference.canonicalName;
if (canonicalName != null &&
!forSupertype &&
canonicalName.name == "Null" &&
canonicalName.parent!.name == "dart:core" &&
canonicalName.parent!.parent!.isRoot) {
return const NullType();
}
return new InterfaceType.byReference(classReference,
Nullability.values[nullabilityIndex], const <DartType>[]);
}
DartType _readFunctionType() {
int typeParameterStackHeight = typeParameterStack.length;
int nullabilityIndex = readByte();
List<TypeParameter> typeParameters = readAndPushTypeParameterList();
int requiredParameterCount = readUInt30();
int totalParameterCount = readUInt30();
List<DartType> positional = readDartTypeList();
List<NamedType> named = readNamedTypeList();
TypedefType? typedefType = readDartTypeOption() as TypedefType?;
assert(positional.length + named.length == totalParameterCount);
DartType returnType = readDartType();
typeParameterStack.length = typeParameterStackHeight;
return new FunctionType(
positional, returnType, Nullability.values[nullabilityIndex],
typeParameters: typeParameters,
requiredParameterCount: requiredParameterCount,
namedParameters: named,
typedefType: typedefType);
}
DartType _readSimpleFunctionType() {
int nullabilityIndex = readByte();
List<DartType> positional = readDartTypeList();
DartType returnType = readDartType();
return new FunctionType(
positional, returnType, Nullability.values[nullabilityIndex]);
}
DartType _readTypeParameterType() {
int declaredNullabilityIndex = readByte();
int index = readUInt30();
DartType? bound = readDartTypeOption();
return new TypeParameterType(typeParameterStack[index],
Nullability.values[declaredNullabilityIndex], bound);
}
List<TypeParameter> readAndPushTypeParameterList(
[List<TypeParameter>? list, TreeNode? parent]) {
int length = readUInt30();
if (length == 0) {
if (list != null) return list;
if (useGrowableLists) {
return <TypeParameter>[];
} else {
return emptyListOfTypeParameter;
}
}
if (list == null) {
list = new List<TypeParameter>.generate(
length, (_) => new TypeParameter(null, null)..parent = parent,
growable: useGrowableLists);
} else if (list.length != length) {
for (int i = 0; i < length; ++i) {
list.add(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);
int variance = readByte();
if (variance == TypeParameter.legacyCovariantSerializationMarker) {
node.variance = null;
} else {
node.variance = variance;
}
node.name = readStringOrNullIfEmpty();
node.bound = readDartType();
node.defaultType = readDartType();
}
Arguments readArguments() {
int numArguments = readUInt30();
List<DartType> typeArguments = readDartTypeList();
List<Expression> positional = readExpressionList();
List<NamedExpression> named = readNamedExpressionList();
assert(numArguments == positional.length + named.length);
return new Arguments(positional, types: typeArguments, named: named);
}
List<NamedExpression> readNamedExpressionList() {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost-constant one for the empty list.
return emptyListOfNamedExpression;
}
return new List<NamedExpression>.generate(
length, (_) => readNamedExpression(),
growable: useGrowableLists);
}
NamedExpression readNamedExpression() {
return new NamedExpression(readStringReference(), readExpression());
}
List<VariableDeclaration> readAndPushVariableDeclarationList() {
int length = readUInt30();
if (!useGrowableLists && length == 0) {
// When lists don't have to be growable anyway, we might as well use an
// almost constant one for the empty list.
return emptyListOfVariableDeclaration;
}
return new List<VariableDeclaration>.generate(
length, (_) => readAndPushVariableDeclaration(),
growable: useGrowableLists);
}
VariableDeclaration? readAndPushVariableDeclarationOption() {
return readAndCheckOptionTag() ? readAndPushVariableDeclaration() : null;
}
VariableDeclaration readAndPushVariableDeclaration() {
VariableDeclaration 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.
List<Expression> annotations = readAnnotationList(null);
int flags = readByte();
VariableDeclaration 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) {
Expression 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 readUInt30() - 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 int subSectionCount = readUint32();
int endOffset = _byteOffset - 4; // End offset of the current subsection.
for (int i = 0; i < subSectionCount; i++) {
// RList<Pair<UInt32, UInt32>> nodeOffsetToMetadataOffset
_byteOffset = endOffset - 4;
final int mappingLength = readUint32();
final int mappingStart = (endOffset - 4) - 4 * 2 * mappingLength;
_byteOffset = mappingStart - 4;
// UInt32 tag (fixed size StringReference)
final String tag = _stringTable[readUint32()];
final MetadataRepository<dynamic>? repository = component.metadata[tag];
if (repository != null) {
// Read nodeOffsetToMetadataOffset mapping.
final Map<int, int> mapping = <int, int>{};
_byteOffset = mappingStart;
for (int j = 0; j < mappingLength; j++) {
final int nodeOffset = readUint32();
final int metadataOffset =
binaryOffsetForMetadataPayloads + readUint32();
mapping[nodeOffset] = metadataOffset;
}
(_subsections ??= <_MetadataSubsection>[])
.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 Object 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
T _associateMetadata<T extends Node>(T node, int nodeOffset) {
if (_subsections == null) {
return node;
}
for (_MetadataSubsection subsection in _subsections!) {
// First check if there is any metadata associated with this node.
final int? metadataOffset = subsection.mapping[nodeOffset];
if (metadataOffset != null) {
subsection.repository.mapping[node] =
_readMetadata(node, subsection.repository, metadataOffset);
}
}
return node;
}
@override
DartType readDartType({bool forSupertype = false}) {
final int nodeOffset = _byteOffset;
final DartType result = super.readDartType(forSupertype: forSupertype);
return _associateMetadata(result, nodeOffset);
}
@override
Library readLibrary(Component component, int endOffset) {
final int nodeOffset = _byteOffset;
final Library result = super.readLibrary(component, endOffset);
return _associateMetadata(result, nodeOffset);
}
@override
Typedef readTypedef() {
final int nodeOffset = _byteOffset;
final Typedef result = super.readTypedef();
return _associateMetadata(result, nodeOffset);
}
@override
Class readClass(int endOffset) {
final int nodeOffset = _byteOffset;
final Class result = super.readClass(endOffset);
return _associateMetadata(result, nodeOffset);
}
@override
Extension readExtension() {
final int nodeOffset = _byteOffset;
final Extension result = super.readExtension();
return _associateMetadata(result, nodeOffset);
}
@override
Field readField() {
final int nodeOffset = _byteOffset;
final Field result = super.readField();
return _associateMetadata(result, nodeOffset);
}
@override
Constructor readConstructor() {
final int nodeOffset = _byteOffset;
final Constructor result = super.readConstructor();
return _associateMetadata(result, nodeOffset);
}
@override
Procedure readProcedure(int endOffset) {
final int nodeOffset = _byteOffset;
final Procedure result = super.readProcedure(endOffset);
return _associateMetadata(result, nodeOffset);
}
@override
RedirectingFactory readRedirectingFactory() {
final int nodeOffset = _byteOffset;
final RedirectingFactory result = super.readRedirectingFactory();
return _associateMetadata(result, nodeOffset);
}
@override
Initializer readInitializer() {
final int nodeOffset = _byteOffset;
final Initializer result = super.readInitializer();
return _associateMetadata(result, nodeOffset);
}
@override
FunctionNode readFunctionNode(
{bool lazyLoadBody: false, int outerEndOffset: -1}) {
final int nodeOffset = _byteOffset;
final FunctionNode result = super.readFunctionNode(
lazyLoadBody: lazyLoadBody, outerEndOffset: outerEndOffset);
return _associateMetadata(result, nodeOffset);
}
@override
Expression readExpression() {
final int nodeOffset = _byteOffset;
final Expression result = super.readExpression();
return _associateMetadata(result, nodeOffset);
}
@override
Arguments readArguments() {
final int nodeOffset = _byteOffset;
final Arguments result = super.readArguments();
return _associateMetadata(result, nodeOffset);
}
@override
NamedExpression readNamedExpression() {
final int nodeOffset = _byteOffset;
final NamedExpression result = super.readNamedExpression();
return _associateMetadata(result, nodeOffset);
}
@override
VariableDeclaration readVariableDeclaration() {
final int nodeOffset = _byteOffset;
final VariableDeclaration result = super.readVariableDeclaration();
return _associateMetadata(result, nodeOffset);
}
@override
Statement readStatement() {
final int nodeOffset = _byteOffset;
final Statement result = super.readStatement();
return _associateMetadata(result, nodeOffset);
}
@override
Combinator readCombinator() {
final int nodeOffset = _byteOffset;
final Combinator result = super.readCombinator();
return _associateMetadata(result, nodeOffset);
}
@override
LibraryDependency readLibraryDependency() {
final int nodeOffset = _byteOffset;
final LibraryDependency result = super.readLibraryDependency();
return _associateMetadata(result, nodeOffset);
}
@override
LibraryPart readLibraryPart() {
final int nodeOffset = _byteOffset;
final LibraryPart result = super.readLibraryPart();
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 int nodeOffset = _byteOffset;
InterfaceType type =
super.readDartType(forSupertype: true) as InterfaceType;
return _associateMetadata(
new Supertype.byReference(type.className, type.typeArguments),
nodeOffset);
}
@override
Name readName() {
final int nodeOffset = _byteOffset;
final Name 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);
}
/// Merges two compilation modes or throws if they are not compatible.
NonNullableByDefaultCompiledMode mergeCompilationModeOrThrow(
NonNullableByDefaultCompiledMode? a, NonNullableByDefaultCompiledMode b) {
if (a == null || a == b) {
return b;
}
// If something is invalid, it should always merge as invalid.
if (a == NonNullableByDefaultCompiledMode.Invalid) {
return a;
}
if (b == NonNullableByDefaultCompiledMode.Invalid) {
return b;
}
if (a == NonNullableByDefaultCompiledMode.Agnostic) {
return b;
}
if (b == NonNullableByDefaultCompiledMode.Agnostic) {
// Keep as-is.
return a;
}
// Mixed mode where agnostic isn't involved.
throw new CompilationModeError("Mixed compilation mode found: $a and $b");
}