| // 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 'tag.dart'; | |
| const int $_ = 95; | |
| class ParseError { | |
| final String? filename; | |
| final int byteIndex; | |
| final String message; | |
| final String path; | |
| new( | |
| 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; | |
| new(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; | |
| new(this.version); | |
| @override | |
| String toString() { | |
| return 'Unexpected Kernel SDK Version ${version} ' | |
| '(expected ${expectedSdkHash}).'; | |
| } | |
| } | |
| class CompilationModeError { | |
| final String message; | |
| new(this.message); | |
| @override | |
| String toString() => "CompilationModeError[$message]"; | |
| } | |
| class _ComponentIndex { | |
| 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 List<int> libraryOffsets; | |
| final int libraryCount; | |
| final int componentFileSizeInBytes; | |
| new({ | |
| 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.libraryOffsets, | |
| required this.libraryCount, | |
| required this.componentFileSizeInBytes, | |
| }); | |
| } | |
| class SubComponentView { | |
| final List<Library> libraries; | |
| final int componentStartOffset; | |
| final int componentFileSize; | |
| new(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); | |
| } | |
| /// Helper used to trigger the read of a late variable in asserts. | |
| bool _lateIsInitialized(dynamic value) { | |
| return true; | |
| } | |
| class BinaryBuilder { | |
| final List<Variable> variableStack = <Variable>[]; | |
| final List<LabeledStatement> labelStack = <LabeledStatement>[]; | |
| int labelStackBase = 0; | |
| int switchCaseStackBase = 0; | |
| final List<SwitchCase> switchCaseStack = <SwitchCase>[]; | |
| final List</* TypeParameter | StructuralParameter */ Object> | |
| typeParameterStack = <Object>[]; | |
| final List<VariableContext> variableContextStack = <VariableContext>[]; | |
| final String? filename; | |
| final Uint8List _bytes; | |
| int _byteOffset = 0; | |
| List<String> _stringTable = const []; | |
| late Map<int, Name?> _nameCache; | |
| List<Uri> _sourceUriTable = const []; | |
| List<Constant> _constantTable = const <Constant>[]; | |
| late List<CanonicalName> _linkTable; | |
| /// Advanced use only. Coordinate with the kernel team. | |
| List<CanonicalName> get linkTable => _linkTable; | |
| late Map<int, DartType?> _cachedSimpleInterfaceTypes; | |
| List<FunctionType?> _voidFunctionFunctionTypesCache = [null, null, null]; | |
| Library? _currentLibrary; | |
| int _componentStartOffset = 0; | |
| // If something goes wrong, this list should indicate what library, | |
| // class, and member was being built. | |
| List<String> debugPath = <String>[]; | |
| 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. | |
| new( | |
| 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 peekByte() => _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()); | |
| } | |
| Uint8List readOrViewByteList() { | |
| int length = readUInt30(); | |
| List<int> source = _bytes; | |
| if (source is Uint8List) { | |
| Uint8List view = source.buffer.asUint8List( | |
| source.offsetInBytes + _byteOffset, | |
| length, | |
| ); | |
| _byteOffset += length; | |
| return view; | |
| } | |
| return readBytes(length); | |
| } | |
| 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; | |
| // Reset name cache here to make any index into it always | |
| // be about the corresponding string table entry. | |
| _nameCache = {}; | |
| _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.RecordConstant: | |
| return _readRecordConstant(); | |
| 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 ConstantMapEntryList entries = _readConstantMapEntryList(); | |
| return new MapConstant(keyType, valueType, entries); | |
| } | |
| Constant _readListConstant() { | |
| final DartType typeArgument = readDartType(); | |
| ConstantList entries = _readConstantReferenceList(); | |
| return new ListConstant(typeArgument, entries); | |
| } | |
| Constant _readSetConstant() { | |
| final DartType typeArgument = readDartType(); | |
| ConstantList entries = _readConstantReferenceList(); | |
| return new SetConstant(typeArgument, entries); | |
| } | |
| Constant _readRecordConstant() { | |
| ConstantList positional = _readConstantReferenceList(); | |
| final int namedLength = readUInt30(); | |
| final List<MapEntry<String, Constant>> named = | |
| new List<MapEntry<String, Constant>>.generate(namedLength, (_) { | |
| final String name = readStringReference(); | |
| final Constant value = readConstantReference(); | |
| return new MapEntry<String, Constant>(name, value); | |
| }, growable: useGrowableLists); | |
| final RecordType recordType = readDartType() as RecordType; | |
| return new RecordConstant( | |
| positional, | |
| new Map<String, Constant>.fromEntries(named), | |
| recordType, | |
| ); | |
| } | |
| Constant _readInstanceConstant() { | |
| final Reference classReference = readNonNullClassReference(); | |
| final DartTypeList 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 DartTypeList types = readDartTypeList(); | |
| return new InstantiationConstant(tearOffConstant, types); | |
| } | |
| Constant _readTypedefTearOffConstant() { | |
| final StructuralParameterList parameters = | |
| readAndPushStructuralParameterList(); | |
| final TearOffConstant tearOffConstant = | |
| readConstantReference() as TearOffConstant; | |
| final DartTypeList 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); | |
| } | |
| ConstantMapEntryList _readConstantMapEntryList() { | |
| final int length = readUInt30(); | |
| if (length == 0) return ConstantMapEntryList.empty; | |
| if (length == 1) return new ConstantMapEntryList(_readConstantMapEntry()); | |
| return new ConstantMapEntryList.generate( | |
| length, | |
| (_) => _readConstantMapEntry(), | |
| ); | |
| } | |
| ConstantMapEntry _readConstantMapEntry() { | |
| final Constant key = readConstantReference(); | |
| final Constant value = readConstantReference(); | |
| return new ConstantMapEntry(key, value); | |
| } | |
| Constant readConstantReference() { | |
| final int index = readUInt30(); | |
| Constant constant = _constantTable[index]; | |
| assert( | |
| !identical(constant, dummyConstant), | |
| "No constant found at index $index.", | |
| ); | |
| return constant; | |
| } | |
| ConstantList _readConstantReferenceList() { | |
| final int length = readUInt30(); | |
| if (length == 0) return ConstantList.empty; | |
| if (length == 1) return new ConstantList(readConstantReference()); | |
| return new ConstantList.generate(length, (_) => readConstantReference()); | |
| } | |
| 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, | |
| ); | |
| } | |
| 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, | |
| ); | |
| // Reset simple interface type cache here to make any index into it always | |
| // be about the corresponding link table entry. | |
| _cachedSimpleInterfaceTypes = {}; | |
| 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.of(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); | |
| } | |
| } | |
| /// Splits the input into views of the sub-components. | |
| /// | |
| /// Note that the result will not have the libraries filled out. | |
| static List<SubComponentView> index(Uint8List bytes) { | |
| BinaryBuilder bb = new BinaryBuilder(bytes); | |
| bb._verifyComponentInitialBytes(resetOffset: true); | |
| List<int> componentFileSizes = bb._indexComponents(); | |
| int componentFileIndex = 0; | |
| List<SubComponentView> views = []; | |
| while (bb._byteOffset < bb._bytes.length) { | |
| int componentStartOffset = bb._byteOffset; | |
| int componentFileSize = componentFileSizes[componentFileIndex]; | |
| bb._verifyComponentInitialBytes(resetOffset: true); | |
| views.add( | |
| new SubComponentView(const [], componentStartOffset, componentFileSize), | |
| ); | |
| bb._byteOffset = componentStartOffset + componentFileSize; | |
| ++componentFileIndex; | |
| } | |
| return views; | |
| } | |
| /// 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", | |
| () { | |
| _verifyComponentInitialBytes(resetOffset: true); | |
| 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 -= numberOfFixedFields(libraryCount) * 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(); | |
| 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, | |
| ); | |
| } | |
| void _readOneComponentSource(Component component, int componentFileSize) { | |
| _componentStartOffset = _byteOffset; | |
| _verifyComponentInitialBytes(resetOffset: false); | |
| // 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; | |
| } | |
| /// Verify the initial bytes could correspond to a valid component. | |
| /// | |
| /// * Checks we have non-empty input. | |
| /// * Verifies that the magic number is correct. | |
| /// * Verifies the binary format version. | |
| /// * Verifies the sdk hash. | |
| /// | |
| /// If [resetOffset] is true the [_byteOffset] will be reset to match what it | |
| /// was before this method was called. If false it will be so we read passed | |
| /// the sdk hash. | |
| void _verifyComponentInitialBytes({required bool resetOffset}) { | |
| // 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. | |
| _checkEmptyInput(); | |
| 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(); | |
| if (resetOffset) { | |
| _byteOffset = offset; | |
| } | |
| } | |
| SubComponentView? _readOneComponent( | |
| Component component, | |
| int componentFileSize, { | |
| bool createView = false, | |
| }) { | |
| _componentStartOffset = _byteOffset; | |
| _verifyComponentInitialBytes(resetOffset: false); | |
| List<String>? problemsAsJson = readListOfStrings(); | |
| if (problemsAsJson != null) { | |
| component.problemsAsJson ??= <String>[]; | |
| component.problemsAsJson!.addAll(problemsAsJson); | |
| } | |
| // Read component index from the end of this ComponentFiles serialized data. | |
| _ComponentIndex index = _readComponentIndex(componentFileSize); | |
| _byteOffset = index.binaryOffsetForStringTable; | |
| readStringTable(); | |
| _byteOffset = index.binaryOffsetForCanonicalNames; | |
| readLinkTable(component.root); | |
| _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. | |
| // TODO(alexmarkov): reverse metadata mappings and read forwards | |
| // Ensure constant table is loaded before metadata is read as it may contain | |
| // references to the constant table. | |
| _byteOffset = index.binaryOffsetForStringTable; // Read backwards. | |
| _readMetadataMappings(component, index.binaryOffsetForMetadataPayloads); | |
| _associateMetadata(component, _componentStartOffset); | |
| 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); | |
| _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, | |
| ); | |
| } | |
| int readScopeSizeAndAllocateContexts() { | |
| int scopeSize = readUInt30(); | |
| for (int index = 0; index < scopeSize; index++) { | |
| variableContextStack.add( | |
| new VariableContext( | |
| captureKind: CaptureKind.notCaptured, | |
| // The list of variables will be updated later, in | |
| // [_readVariableContext]. | |
| variables: const [], | |
| ), | |
| ); | |
| } | |
| return scopeSize; | |
| } | |
| Scope? readOptionalScope(int scopeSize) { | |
| return readAndCheckOptionTag() ? _readScope(scopeSize) : null; | |
| } | |
| Scope _readScope(int scopeSize) { | |
| int contextCount = readUInt30(); | |
| assert(scopeSize == contextCount); | |
| List<VariableContext> contexts = new List<VariableContext>.generate( | |
| contextCount, | |
| (int variableContextIndex) => | |
| _readVariableContext(variableContextIndex, scopeSize), | |
| growable: useGrowableLists, | |
| ); | |
| variableContextStack.length -= scopeSize; | |
| return new Scope(contexts: contexts); | |
| } | |
| VariableContext _readVariableContext( | |
| int variableContextIndex, | |
| int scopeSize, | |
| ) { | |
| int captureKindIndex = readByte(); | |
| CaptureKind captureKind = CaptureKind.values[captureKindIndex]; | |
| int variableCount = readUInt30(); | |
| List<VariableBase> variables = new List<VariableBase>.generate( | |
| variableCount, | |
| (_) => readVariableReference(), | |
| growable: useGrowableLists, | |
| ); | |
| VariableContext variableContext = | |
| variableContextStack[variableContextStack.length - | |
| scopeSize + | |
| variableContextIndex]; | |
| variableContext.captureKind = captureKind; | |
| variableContext.variables = variables; | |
| return variableContext; | |
| } | |
| /// 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}) { | |
| assert(!readCoverage || (readCoverage && _lateIsInitialized(_linkTable))); | |
| 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 = readOrViewByteList(); | |
| 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 readNonNullClassReference() { | |
| CanonicalName? name = readNullableCanonicalNameReference(); | |
| if (name == null) { | |
| throw 'Expected a class reference to be valid but was `null`.'; | |
| } | |
| return name.reference; | |
| } | |
| Reference readNonNullExtensionTypeDeclarationReference() { | |
| CanonicalName? name = readNullableCanonicalNameReference(); | |
| if (name == null) { | |
| throw 'Expected an extension type declaration 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 readNonNullInstanceMemberReference() { | |
| Reference reference = readNonNullMemberReference(); | |
| readNullableMemberReference(); // Skip origin | |
| return reference; | |
| } | |
| Reference? getNullableMemberReferenceFromInt(int index) { | |
| return getNullableCanonicalNameReferenceFromInt(index)?.reference; | |
| } | |
| Reference readNonNullTypedefReference() { | |
| return readNonNullCanonicalNameReference().reference; | |
| } | |
| Name readName() { | |
| final int stringReference = readUInt30(); | |
| assert(stringReference < (1 << 30)); | |
| final String text = _stringTable[stringReference]; | |
| final bool isPrivate = text.isNotEmpty && text.codeUnitAt(0) == $_; | |
| final int libraryReferenceIndex; | |
| final int nameCacheIndex; | |
| if (isPrivate) { | |
| // "Raw" reference index of 0 means null which we don't allow. | |
| libraryReferenceIndex = readUInt30(); | |
| if (libraryReferenceIndex == 0) { | |
| throw 'Expected a library reference to be valid but was `null`.'; | |
| } | |
| // Check cache using the upper bits for the library reference. | |
| nameCacheIndex = stringReference | ((libraryReferenceIndex) << 30); | |
| } else { | |
| // the 0 will be unused but we need to assign it. | |
| libraryReferenceIndex = 0; | |
| nameCacheIndex = stringReference; | |
| } | |
| final Name? cached = _nameCache[nameCacheIndex]; | |
| if (cached != null) { | |
| return cached; | |
| } | |
| // Not in cache. Create it and cache it. | |
| final Name name; | |
| if (isPrivate) { | |
| // libraryReferenceIndex was checked to be > 0 so we get a canonical name. | |
| final CanonicalName canonicalName = | |
| getNullableCanonicalNameReferenceFromInt(libraryReferenceIndex)!; | |
| final Reference libraryReference = canonicalName.reference; | |
| name = new Name.byReference(text, libraryReference); | |
| } else { | |
| name = new Name(text); | |
| } | |
| _nameCache[nameCacheIndex] = name; | |
| return name; | |
| } | |
| 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(() { | |
| 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); | |
| _readExtensionTypeDeclarationList(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, | |
| ); | |
| } | |
| } | |
| void _readExtensionTypeDeclarationList(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.extensionTypeDeclarationsInternal = | |
| emptyListOfExtensionTypeDeclaration; | |
| } else { | |
| library.extensionTypeDeclarationsInternal = | |
| new List<ExtensionTypeDeclaration>.generate( | |
| length, | |
| (int index) => readExtensionTypeDeclaration()..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; | |
| } | |
| List<Procedure> _readProcedureListWithoutOffsets(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 emptyListOfProcedure; | |
| } | |
| List<Procedure> list = new List<Procedure>.generate(length, (int index) { | |
| return readProcedure(/* no end offset = */ -1)..parent = parent; | |
| }, growable: useGrowableLists); | |
| 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 = readAnnotationList(); | |
| 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 = readAnnotationList(); | |
| String partUri = readStringReference(); | |
| Uri fileUri = readUriReference(); | |
| return new LibraryPart(annotations, partUri, fileUri); | |
| } | |
| 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); | |
| node.typeParameters = readAndPushTypeParameterList( | |
| node.typeParameters, | |
| node, | |
| ); | |
| DartType type = readDartType(); | |
| 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 = readUInt30(); | |
| 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); | |
| node.typeParameters = 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; | |
| assert(() { | |
| debugPath.removeLast(); | |
| return true; | |
| }()); | |
| 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(); | |
| node.typeParameters = readAndPushTypeParameterList( | |
| node.typeParameters, | |
| node, | |
| ); | |
| DartType onType = readDartType(); | |
| typeParameterStack.length = 0; | |
| node.name = name; | |
| node.fileUri = fileUri; | |
| node.onType = onType; | |
| node.memberDescriptors = _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? memberName = readNullableCanonicalNameReference(); | |
| CanonicalName? tearOffName = readNullableCanonicalNameReference(); | |
| return new ExtensionMemberDescriptor( | |
| name: name, | |
| kind: ExtensionMemberKind.values[kind], | |
| memberReference: memberName?.reference, | |
| tearOffReference: tearOffName?.reference, | |
| )..flags = flags; | |
| } | |
| ExtensionTypeDeclaration readExtensionTypeDeclaration() { | |
| int tag = readByte(); | |
| assert(tag == Tag.ExtensionTypeDeclaration, "Unexpected tag $tag"); | |
| CanonicalName canonicalName = readNonNullCanonicalNameReference(); | |
| Reference reference = canonicalName.reference; | |
| ExtensionTypeDeclaration? node = | |
| reference.node as ExtensionTypeDeclaration?; | |
| 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 ExtensionTypeDeclaration( | |
| name: name, | |
| reference: reference, | |
| fileUri: fileUri, | |
| ); | |
| } | |
| node.annotations = annotations; | |
| setParents(annotations, node); | |
| node.fileOffset = readOffset(); | |
| node.flags = readByte(); | |
| node.typeParameters = readAndPushTypeParameterList( | |
| node.typeParameters, | |
| node, | |
| ); | |
| DartType representationType = readDartType(); | |
| String representationName = readStringReference(); | |
| List<TypeDeclarationType> implements = | |
| _readExtensionTypeDeclarationImplementsList(); | |
| node.proceduresInternal = _readProcedureListWithoutOffsets(node); | |
| typeParameterStack.length = 0; | |
| node.name = name; | |
| node.fileUri = fileUri; | |
| node.declaredRepresentationType = representationType; | |
| node.representationName = representationName; | |
| node.implements = implements; | |
| node.memberDescriptors = _readExtensionTypeMemberDescriptorList(); | |
| return node; | |
| } | |
| List<TypeDeclarationType> _readExtensionTypeDeclarationImplementsList() { | |
| 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 emptyListOfTypeDeclarationType; | |
| } | |
| return new List<TypeDeclarationType>.generate( | |
| length, | |
| (_) => readDartType() as TypeDeclarationType, | |
| growable: useGrowableLists, | |
| ); | |
| } | |
| List<ExtensionTypeMemberDescriptor> _readExtensionTypeMemberDescriptorList() { | |
| 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 emptyListOfExtensionTypeMemberDescriptor; | |
| } | |
| return new List<ExtensionTypeMemberDescriptor>.generate( | |
| length, | |
| (_) => _readExtensionTypeMemberDescriptor(), | |
| growable: useGrowableLists, | |
| ); | |
| } | |
| ExtensionTypeMemberDescriptor _readExtensionTypeMemberDescriptor() { | |
| Name name = readName(); | |
| int kind = readByte(); | |
| int flags = readByte(); | |
| CanonicalName? memberName = readNullableCanonicalNameReference(); | |
| CanonicalName? tearOffName = readNullableCanonicalNameReference(); | |
| return new ExtensionTypeMemberDescriptor( | |
| name: name, | |
| kind: ExtensionTypeMemberKind.values[kind], | |
| memberReference: memberName?.reference, | |
| tearOffReference: tearOffName?.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); | |
| } | |
| 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, | |
| ); | |
| } | |
| } | |
| /// 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; | |
| }; | |
| } | |
| 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, | |
| ); | |
| } | |
| } | |
| int scopeSize = readScopeSizeAndAllocateContexts(); | |
| List<Expression> annotations = readAnnotationList(node); | |
| assert(() { | |
| debugPath.add(name.text); | |
| return true; | |
| }()); | |
| DartType type = readDartType(); | |
| // TODO(63493): Remove the next line when the scopes are serialized before | |
| // function bodies. | |
| ThisVariable? thisVariable = readAndPushVariableOption() as ThisVariable?; | |
| Expression? initializer = readExpressionOption(); | |
| Scope? scope = readOptionalScope(scopeSize); | |
| 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.thisVariable = thisVariable; | |
| node.thisVariable?.parent = node; | |
| node.initializer = initializer; | |
| node.initializer?.parent = node; | |
| node.scope = scope; | |
| 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; | |
| }()); | |
| FunctionNodeAndContexts functionNodeAndContexts = readFunctionNode(); | |
| FunctionNode function = functionNodeAndContexts.functionNode; | |
| if (node == null) { | |
| node = new Constructor( | |
| function, | |
| reference: reference, | |
| name: name, | |
| fileUri: fileUri, | |
| ); | |
| } | |
| if (function.thisVariable != null) { | |
| pushVariableDeclaration(function.thisVariable!); | |
| } | |
| pushVariableDeclarations(function.positionalParameters); | |
| pushVariableDeclarations(function.namedParameters); | |
| int savedVariableContextStackSize = variableContextStack.length; | |
| if (functionNodeAndContexts.preAllocatedContexts case var contexts?) { | |
| variableContextStack.addAll(contexts); | |
| } | |
| _readInitializers(node); | |
| variableContextStack.length = savedVariableContextStackSize; | |
| variableStack.length = 0; | |
| 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; | |
| return node; | |
| } | |
| Procedure readProcedure(int endOffset) { | |
| int tag = readByte(); | |
| assert(tag == Tag.Procedure); | |
| CanonicalName canonicalName = readNonNullCanonicalNameReference(); | |
| Reference reference = canonicalName.reference; | |
| Procedure? node; | |
| if (!alwaysCreateNewNamedNodes) { | |
| node = reference.node as Procedure?; | |
| } | |
| 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 and extension type declaration procedures | |
| // (where `endOffset == -1`) up front. Postpone everything else. | |
| bool readFunctionNodeNow = | |
| endOffset == -1 || | |
| (kind == ProcedureKind.Factory && functionNodeSize <= 50) || | |
| _disableLazyReading; | |
| Reference? stubTargetReference = readNullableMemberReference(); | |
| FunctionType? signatureType = readDartTypeOption() as FunctionType?; | |
| FunctionNode function = readFunctionNode( | |
| lazyLoadBody: !readFunctionNodeNow, | |
| outerEndOffset: endOffset, | |
| ).functionNode; | |
| if (node == null) { | |
| node = new Procedure( | |
| name, | |
| kind, | |
| function, | |
| reference: reference, | |
| fileUri: fileUri, | |
| ); | |
| } else { | |
| assert(node.kind == kind); | |
| } | |
| assert(((_) => true)(debugPath.removeLast())); | |
| node.fileStartOffset = 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.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; | |
| } | |
| 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(); | |
| switch (tag) { | |
| // 52.71% (43.80% - 59.02%). | |
| case Tag.FieldInitializer: | |
| return _readFieldInitializer(); | |
| // 42.01% (28.38% - 55.93%) | |
| case Tag.SuperInitializer: | |
| return _readSuperInitializer(); | |
| // 4.69% (0.00% - 16.00%). | |
| case Tag.AssertInitializer: | |
| return _readAssertInitializer(); | |
| // The rest is < 2% on average in sampled dills. | |
| case Tag.InvalidInitializer: | |
| return _readInvalidInitializer(); | |
| case Tag.RedirectingInitializer: | |
| return _readRedirectingInitializer(); | |
| case Tag.LocalInitializer: | |
| return _readLocalInitializer(); | |
| default: | |
| throw fail('unexpected initializer tag: $tag'); | |
| } | |
| } | |
| Initializer _readInvalidInitializer() { | |
| int offset = readOffset(); | |
| String message = readStringReference(); | |
| int flags = readByte(); | |
| return new InvalidInitializer(message) | |
| ..flags = flags | |
| ..fileOffset = offset; | |
| } | |
| Initializer _readFieldInitializer() { | |
| int offset = readOffset(); | |
| bool isSynthetic = readByte() == 1; | |
| Reference reference = readNonNullMemberReference(); | |
| Expression value = readExpression(); | |
| return new FieldInitializer.byReference(reference, value) | |
| ..isSynthetic = isSynthetic | |
| ..fileOffset = offset; | |
| } | |
| Initializer _readSuperInitializer() { | |
| int offset = readOffset(); | |
| bool isSynthetic = readByte() == 1; | |
| 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() { | |
| int offset = readOffset(); | |
| SyntheticVariable variable = readAndPushVariable() as SyntheticVariable; | |
| // ignore: deprecated_member_use_from_same_package | |
| return new LocalInitializer(variable, variable.initializer!) | |
| ..fileOffset = offset; | |
| } | |
| Initializer _readAssertInitializer() { | |
| int offset = readOffset(); | |
| return new AssertInitializer(readStatement() as AssertStatement) | |
| ..fileOffset = offset; | |
| } | |
| FunctionNodeAndContexts 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; | |
| int scopeSize = readScopeSizeAndAllocateContexts(); | |
| List<VariableContext>? preAllocatedContexts; | |
| if (scopeSize > 0) { | |
| preAllocatedContexts = variableContextStack | |
| .getRange( | |
| variableContextStack.length - scopeSize, | |
| variableContextStack.length, | |
| ) | |
| .toList(); | |
| } | |
| TypeParameterList typeParameters = readAndPushFunctionTypeParameterList(); | |
| int variableStackHeight = variableStack.length; | |
| // TODO(63493): Remove the next line when the scopes are serialized before | |
| // function bodies. | |
| ThisVariable? thisVariable = readAndPushVariableOption() as ThisVariable?; | |
| readUInt30(); // total parameter count. | |
| int requiredParameterCount = readUInt30(); | |
| PositionalParameterList positional = readAndPushPositionalParameterList(); | |
| NamedParameterList named = readAndPushNamedParameterList(); | |
| DartType returnType = readDartType(); | |
| DartType? futureValueType = readDartTypeOption(); | |
| RedirectingFactoryTarget? redirectingFactoryTarget; | |
| if (readAndCheckOptionTag()) { | |
| Reference? targetReference = readNullableMemberReference(); | |
| DartTypeList? typeArguments; | |
| if (readAndCheckOptionTag()) { | |
| typeArguments = readDartTypeList(); | |
| } | |
| if (readAndCheckOptionTag()) { | |
| assert(targetReference == null && typeArguments == null); | |
| String errorMessage = readStringReference(); | |
| redirectingFactoryTarget = new RedirectingFactoryTarget.error( | |
| errorMessage, | |
| ); | |
| } else { | |
| assert(targetReference != null && typeArguments != null); | |
| redirectingFactoryTarget = new RedirectingFactoryTarget.byReference( | |
| targetReference!, | |
| typeArguments!, | |
| ); | |
| } | |
| } | |
| 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; | |
| Scope? scope; | |
| List<VariableContext>? capturedContexts; | |
| if (!lazyLoadBody) { | |
| labelStackBase = labelStack.length; | |
| switchCaseStackBase = switchCaseStack.length; | |
| body = readStatementOption(); | |
| scope = readOptionalScope(scopeSize); | |
| capturedContexts = readOptionalCapturedContexts(); | |
| } | |
| FunctionNode result = | |
| new FunctionNode( | |
| body, | |
| typeParameters: typeParameters, | |
| requiredParameterCount: requiredParameterCount, | |
| positionalParameters: positional, | |
| namedParameters: named, | |
| returnType: returnType, | |
| asyncMarker: asyncMarker, | |
| dartAsyncMarker: dartAsyncMarker, | |
| emittedValueType: futureValueType, | |
| ) | |
| ..fileOffset = offset | |
| ..fileEndOffset = endOffset | |
| ..redirectingFactoryTarget = redirectingFactoryTarget | |
| ..thisVariable = thisVariable | |
| ..scope = scope | |
| ..capturedContexts = capturedContexts; | |
| thisVariable?.parent = result; | |
| if (lazyLoadBody) { | |
| _setLazyLoadFunction( | |
| result, | |
| oldLabelStackBase, | |
| oldSwitchCaseStackBase, | |
| variableStackHeight, | |
| scopeSize: scopeSize, | |
| ); | |
| } | |
| labelStackBase = oldLabelStackBase; | |
| switchCaseStackBase = oldSwitchCaseStackBase; | |
| variableStack.length = variableStackHeight; | |
| typeParameterStack.length = typeParameterStackHeight; | |
| return new FunctionNodeAndContexts(result, preAllocatedContexts); | |
| } | |
| void _setLazyLoadFunction( | |
| FunctionNode result, | |
| int oldLabelStackBase, | |
| int oldSwitchCaseStackBase, | |
| int variableStackHeight, { | |
| required int scopeSize, | |
| }) { | |
| final int savedByteOffset = _byteOffset; | |
| final int componentStartOffset = _componentStartOffset; | |
| final List<TypeParameter> typeParameters = typeParameterStack | |
| .cast<TypeParameter>() | |
| .toList(); | |
| final List<Variable> variables = variableStack.toList(); | |
| final List<VariableContext> variableContexts = variableContextStack | |
| .toList(); | |
| variableContextStack.length -= scopeSize; | |
| final Library currentLibrary = _currentLibrary!; | |
| result.lazyBuilder = () { | |
| _byteOffset = savedByteOffset; | |
| _currentLibrary = currentLibrary; | |
| typeParameterStack.clear(); | |
| typeParameterStack.addAll(typeParameters); | |
| variableStack.clear(); | |
| variableStack.addAll(variables); | |
| variableContextStack.clear(); | |
| variableContextStack.addAll(variableContexts); | |
| _componentStartOffset = componentStartOffset; | |
| result.body = readStatementOption(); | |
| result.body?.parent = result; | |
| result.scope = readOptionalScope(scopeSize); | |
| result.capturedContexts = readOptionalCapturedContexts(); | |
| labelStackBase = oldLabelStackBase; | |
| switchCaseStackBase = oldSwitchCaseStackBase; | |
| variableStack.length = variableStackHeight; | |
| typeParameterStack.clear(); | |
| }; | |
| } | |
| void pushVariableDeclaration(Variable variable) { | |
| variableStack.add(variable); | |
| } | |
| void pushVariableDeclarations(List<Variable> variables) { | |
| variableStack.addAll(variables); | |
| } | |
| Variable? readVariableReferenceOption() { | |
| return readAndCheckOptionTag() ? readVariableReference() : null; | |
| } | |
| DeclaredVariable readDeclaredVariableReference() { | |
| return readVariableReference() as DeclaredVariable; | |
| } | |
| LocalFunctionVariable readLocalFunctionVariableReference() { | |
| return readVariableReference() as LocalFunctionVariable; | |
| } | |
| Variable readVariableReference() { | |
| readUInt30(); // offset of the variable declaration in the binary. | |
| return _readVariableReferenceInternal(); | |
| } | |
| Variable _readVariableReferenceInternal() { | |
| 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'); | |
| } | |
| } | |
| ExpressionList readExpressionList() { | |
| int length = readUInt30(); | |
| return new ExpressionList.generate(length, (_) => readExpression()); | |
| } | |
| Expression? readExpressionOption() { | |
| return readAndCheckOptionTag() ? readExpression() : null; | |
| } | |
| Expression readExpression() { | |
| int tagByte = readByte(); | |
| int tag = tagByte & Tag.SpecializedTagHighBits == Tag.SpecializedTagHighBits | |
| ? (tagByte & Tag.SpecializedTagMask) | |
| : tagByte; | |
| switch (tag) { | |
| // 18.57% (13.56% - 23.28%). | |
| case Tag.SpecializedVariableGet: | |
| return _readSpecializedVariableGet(tagByte); | |
| // 12.02% (9.14% - 14.14%). | |
| case Tag.InstanceGet: | |
| return _readInstanceGet(); | |
| // 10.61% (6.82% - 14.13%). | |
| case Tag.ThisExpression: | |
| return _readThisLiteral(); | |
| // 10.19% (6.51% - 15.46%). | |
| case Tag.ConstantExpression: | |
| return _readConstantExpression(); | |
| // 9.95% (5.96% - 13.10%). | |
| case Tag.InstanceInvocation: | |
| return _readInstanceInvocation(); | |
| // 6.20% (2.67% - 12.88%) | |
| case Tag.StringLiteral: | |
| return _readStringLiteral(); | |
| // 4.30% (1.89% - 5.58%). | |
| case Tag.VariableGet: | |
| return _readVariableGet(); | |
| // 3.94% (2.48% - 6.29%). | |
| case Tag.StaticInvocation: | |
| return _readStaticInvocation(); | |
| // 2.95% (1.58% - 5.31%). | |
| case Tag.SpecializedIntLiteral: | |
| return _readSpecializedIntLiteral(tagByte); | |
| // 2.92% (1.76% - 5.21%). | |
| case Tag.ConstructorInvocation: | |
| return _readConstructorInvocation(); | |
| // The rest is < 2% on average in sampled dills. | |
| case Tag.LoadLibrary: | |
| return _readLoadLibrary(); | |
| case Tag.CheckLibraryIsLoaded: | |
| return _readCheckLibraryIsLoaded(); | |
| case Tag.InvalidExpression: | |
| return _readInvalidExpression(); | |
| case Tag.VariableSet: | |
| return _readVariableSet(); | |
| case Tag.SpecializedVariableSet: | |
| return _readSpecializedVariableSet(tagByte); | |
| case Tag.InstanceTearOff: | |
| return _readInstanceTearOff(); | |
| case Tag.DynamicGet: | |
| return _readDynamicGet(); | |
| case Tag.RecordIndexGet: | |
| return _readRecordIndexGet(); | |
| case Tag.RecordNameGet: | |
| return _readRecordNameGet(); | |
| case Tag.InstanceSet: | |
| return _readInstanceSet(); | |
| case Tag.DynamicSet: | |
| return _readDynamicSet(); | |
| case Tag.AbstractSuperPropertyGet: | |
| return _readAbstractSuperPropertyGet(); | |
| case Tag.AbstractSuperPropertySet: | |
| return _readAbstractSuperPropertySet(); | |
| 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.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.AbstractSuperMethodInvocation: | |
| return _readAbstractSuperMethodInvocation(); | |
| case Tag.SuperMethodInvocation: | |
| return _readSuperMethodInvocation(); | |
| case Tag.ConstStaticInvocation: | |
| return _readConstStaticInvocation(); | |
| 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.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.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.RecordLiteral: | |
| return _readRecordLiteral(); | |
| case Tag.ConstRecordLiteral: | |
| return _readConstRecordLiteral(); | |
| 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.SwitchExpression: | |
| return _readSwitchExpression(); | |
| case Tag.PatternAssignment: | |
| return _readPatternAssignment(); | |
| case Tag.FileUriConstantExpression: | |
| return _readFileUriConstantExpression(); | |
| case Tag.RedirectingFactoryInvocation: | |
| return _readRedirectingFactoryInvocation(); | |
| default: | |
| throw fail('unexpected expression tag: $tag'); | |
| } | |
| } | |
| Expression _readLoadLibrary() { | |
| int offset = readOffset(); | |
| return new LoadLibrary(readLibraryDependencyReference()) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readCheckLibraryIsLoaded() { | |
| int offset = readOffset(); | |
| return new CheckLibraryIsLoaded(readLibraryDependencyReference()) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readInvalidExpression() { | |
| int offset = readOffset(); | |
| return new InvalidExpression(readStringReference(), readExpressionOption()) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readVariableGet() { | |
| int offset = readOffset(); | |
| 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(); | |
| 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 _readRecordIndexGet() { | |
| int offset = readOffset(); | |
| Expression receiver = readExpression(); | |
| RecordType receiverType = readDartType() as RecordType; | |
| int index = readUInt30(); | |
| return RecordIndexGet(receiver, receiverType, index)..fileOffset = offset; | |
| } | |
| Expression _readRecordNameGet() { | |
| int offset = readOffset(); | |
| Expression receiver = readExpression(); | |
| RecordType receiverType = readDartType() as RecordType; | |
| String name = readStringReference(); | |
| return RecordNameGet(receiver, receiverType, name)..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 _readAbstractSuperPropertyGet() { | |
| int offset = readOffset(); | |
| return new AbstractSuperPropertyGet.byReference( | |
| readExpression(), | |
| readName(), | |
| readNonNullInstanceMemberReference(), | |
| )..fileOffset = offset; | |
| } | |
| Expression _readAbstractSuperPropertySet() { | |
| int offset = readOffset(); | |
| return new AbstractSuperPropertySet.byReference( | |
| readExpression(), | |
| readName(), | |
| readExpression(), | |
| readNonNullInstanceMemberReference(), | |
| )..fileOffset = offset; | |
| } | |
| Expression _readSuperPropertyGet() { | |
| int offset = readOffset(); | |
| return new SuperPropertyGet.byReference( | |
| readExpression(), | |
| readName(), | |
| readNonNullInstanceMemberReference(), | |
| )..fileOffset = offset; | |
| } | |
| Expression _readSuperPropertySet() { | |
| int offset = readOffset(); | |
| return new SuperPropertySet.byReference( | |
| readExpression(), | |
| readName(), | |
| readExpression(), | |
| readNonNullInstanceMemberReference(), | |
| )..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() { | |
| int offset = readOffset(); | |
| StructuralParameterList structuralParameters = | |
| readAndPushStructuralParameterList(); | |
| Expression expression = readExpression(); | |
| DartTypeList typeArguments = readDartTypeList(); | |
| typeParameterStack.length -= structuralParameters.length; | |
| return new TypedefTearOff(structuralParameters, expression, typeArguments) | |
| ..fileOffset = offset; | |
| } | |
| 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(); | |
| Expression receiver = readExpression(); | |
| Name name = readName(); | |
| Arguments arguments = readArguments(); | |
| FunctionType functionType = readDartType() as FunctionType; | |
| DartType resultType = readDartType(); | |
| Reference interfaceTargetReference = readNonNullInstanceMemberReference(); | |
| return new InstanceInvocation.byReference( | |
| kind, | |
| receiver, | |
| name, | |
| arguments, | |
| functionType: functionType, | |
| interfaceTargetReference: interfaceTargetReference, | |
| ) | |
| ..resultType = resultType | |
| ..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 flags = readByte(); | |
| int offset = readOffset(); | |
| return new DynamicInvocation( | |
| kind, | |
| readExpression(), | |
| readName(), | |
| readArguments(), | |
| ) | |
| ..fileOffset = offset | |
| ..flags = flags; | |
| } | |
| 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(); | |
| LocalFunctionVariable variable = readLocalFunctionVariableReference(); | |
| 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 _readAbstractSuperMethodInvocation() { | |
| int offset = readOffset(); | |
| return new AbstractSuperMethodInvocation.byReference( | |
| readExpression(), | |
| readName(), | |
| readArguments(), | |
| readNonNullInstanceMemberReference(), | |
| )..fileOffset = offset; | |
| } | |
| Expression _readSuperMethodInvocation() { | |
| int offset = readOffset(); | |
| return new SuperMethodInvocation.byReference( | |
| readExpression(), | |
| readName(), | |
| readArguments(), | |
| readNonNullInstanceMemberReference(), | |
| )..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 _readRedirectingFactoryInvocation() { | |
| int offset = readOffset(); | |
| return new RedirectingFactoryInvocation.byReference( | |
| readNonNullMemberReference(), | |
| readExpression() as InvocationExpression, | |
| )..fileOffset = offset; | |
| } | |
| Expression _readNot() { | |
| int offset = readOffset(); | |
| return new Not(readExpression())..fileOffset = offset; | |
| } | |
| Expression _readNullCheck() { | |
| int offset = readOffset(); | |
| return new NullCheck(readExpression())..fileOffset = offset; | |
| } | |
| Expression _readLogicalExpression() { | |
| int offset = readOffset(); | |
| return new LogicalExpression( | |
| readExpression(), | |
| logicalOperatorToEnum(readByte()), | |
| readExpression(), | |
| )..fileOffset = offset; | |
| } | |
| Expression _readConditionalExpression() { | |
| int offset = readOffset(); | |
| return new ConditionalExpression( | |
| readExpression(), | |
| readExpression(), | |
| readExpression(), | |
| // TODO(johnniwinther): Change this to use `readDartType`. | |
| readDartTypeOption()!, | |
| )..fileOffset = offset; | |
| } | |
| 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(); | |
| DartTypeList 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, | |
| ); | |
| } | |
| ExpressionList 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(); | |
| return new IsExpression(readExpression(), readDartType()) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readAsExpression() { | |
| int offset = readOffset(); | |
| int flags = readByte(); | |
| return new AsExpression(readExpression(), readDartType()) | |
| ..fileOffset = offset | |
| ..flags = flags; | |
| } | |
| Expression _readStringLiteral() { | |
| int offset = readOffset(); | |
| return new StringLiteral(readStringReference())..fileOffset = offset; | |
| } | |
| Expression _readSpecializedIntLiteral(int tagByte) { | |
| int biasedValue = tagByte & Tag.SpecializedPayloadMask; | |
| return new IntLiteral(biasedValue - Tag.SpecializedIntLiteralBias) | |
| ..fileOffset = readOffset(); | |
| } | |
| Expression _readPositiveIntLiteral() { | |
| int offset = readOffset(); | |
| int value = readUInt30(); | |
| return new IntLiteral(value)..fileOffset = offset; | |
| } | |
| Expression _readNegativeIntLiteral() { | |
| int offset = readOffset(); | |
| int value = -readUInt30(); | |
| return new IntLiteral(value)..fileOffset = offset; | |
| } | |
| Expression _readBigIntLiteral() { | |
| int offset = readOffset(); | |
| int value = int.parse(readStringReference()); | |
| return new IntLiteral(value)..fileOffset = offset; | |
| } | |
| Expression _readDoubleLiteral() { | |
| int offset = readOffset(); | |
| double value = readDouble(); | |
| return new DoubleLiteral(value)..fileOffset = offset; | |
| } | |
| Expression _readTrueLiteral() { | |
| return new BoolLiteral(true)..fileOffset = readOffset(); | |
| } | |
| Expression _readFalseLiteral() { | |
| return new BoolLiteral(false)..fileOffset = readOffset(); | |
| } | |
| Expression _readNullLiteral() { | |
| return new NullLiteral()..fileOffset = readOffset(); | |
| } | |
| Expression _readSymbolLiteral() { | |
| int offset = readOffset(); | |
| String value = readStringReference(); | |
| return new SymbolLiteral(value)..fileOffset = offset; | |
| } | |
| Expression _readTypeLiteral() { | |
| int offset = readOffset(); | |
| return new TypeLiteral(readDartType())..fileOffset = offset; | |
| } | |
| Expression _readThisLiteral() { | |
| return new ThisExpression()..fileOffset = readOffset(); | |
| } | |
| Expression _readRethrow() { | |
| int offset = readOffset(); | |
| return new Rethrow()..fileOffset = offset; | |
| } | |
| Expression _readThrow() { | |
| int offset = readOffset(); | |
| int flags = readByte(); | |
| return new Throw(readExpression()) | |
| ..fileOffset = offset | |
| ..flags = flags; | |
| } | |
| 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 _readRecordLiteral() { | |
| int offset = readOffset(); | |
| ExpressionList positional = readExpressionList(); | |
| NamedExpressionList named = readNamedExpressionList(); | |
| RecordType recordType = readDartType() as RecordType; | |
| return new RecordLiteral(positional, named, recordType, isConst: false) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readConstRecordLiteral() { | |
| int offset = readOffset(); | |
| ExpressionList positional = readExpressionList(); | |
| NamedExpressionList named = readNamedExpressionList(); | |
| RecordType recordType = readDartType() as RecordType; | |
| return new RecordLiteral(positional, named, recordType, isConst: true) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readAwaitExpression() { | |
| int offset = readOffset(); | |
| return new AwaitExpression(readExpression()) | |
| ..fileOffset = offset | |
| ..runtimeCheckType = readDartTypeOption(); | |
| } | |
| Expression _readFunctionExpression() { | |
| int offset = readOffset(); | |
| final LocalFunctionId id = LocalFunctionId(readUInt30()); | |
| return new FunctionExpression(readFunctionNode().functionNode) | |
| ..fileOffset = offset | |
| ..id = id; | |
| } | |
| Expression _readLet() { | |
| int offset = readOffset(); | |
| SyntheticVariable variable = readVariable() as SyntheticVariable; | |
| int stackHeight = variableStack.length; | |
| pushVariableDeclaration(variable); | |
| Expression body = readExpression(); | |
| variableStack.length = stackHeight; | |
| // ignore: deprecated_member_use_from_same_package | |
| return new Let(variable: variable, value: variable.initializer!, body: body) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readBlockExpression() { | |
| int offset = readOffset(); | |
| int stackHeight = variableStack.length; | |
| int scopeSize = readScopeSizeAndAllocateContexts(); | |
| List<Statement> statements = readStatementListAlwaysGrowable(); | |
| Expression value = readExpression(); | |
| Scope? scope = readOptionalScope(scopeSize); | |
| variableStack.length = stackHeight; | |
| return new BlockExpression(new Block(statements), value) | |
| ..fileOffset = offset | |
| ..scope = scope; | |
| } | |
| Expression _readInstantiation() { | |
| int offset = readOffset(); | |
| Expression expression = readExpression(); | |
| DartTypeList typeArguments = readDartTypeList(); | |
| return new Instantiation(expression, typeArguments)..fileOffset = offset; | |
| } | |
| Expression _readConstantExpression() { | |
| int offset = readOffset(); | |
| DartType type = readDartType(); | |
| Constant constant = readConstantReference(); | |
| return new ConstantExpression(constant, type)..fileOffset = offset; | |
| } | |
| Expression _readFileUriConstantExpression() { | |
| int offset = readOffset(); | |
| Uri fileUri = readUriReference(); | |
| DartType type = readDartType(); | |
| Constant constant = readConstantReference(); | |
| return new FileUriConstantExpression(constant, type: type, fileUri: fileUri) | |
| ..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()); | |
| } | |
| Pattern _readPattern() { | |
| int tag = readByte(); | |
| switch (tag) { | |
| case Tag.AndPattern: | |
| return _readAndPattern(); | |
| case Tag.AssignedVariablePattern: | |
| return _readAssignedVariablePattern(); | |
| case Tag.CastPattern: | |
| return _readCastPattern(); | |
| case Tag.ConstantPattern: | |
| return _readConstantPattern(); | |
| case Tag.InvalidPattern: | |
| return _readInvalidPattern(); | |
| case Tag.ListPattern: | |
| return _readListPattern(); | |
| case Tag.MapPattern: | |
| return _readMapPattern(); | |
| case Tag.NamedPattern: | |
| return _readNamedPattern(); | |
| case Tag.NullAssertPattern: | |
| return _readNullAssertPattern(); | |
| case Tag.NullCheckPattern: | |
| return _readNullCheckPattern(); | |
| case Tag.ObjectPattern: | |
| return _readObjectPattern(); | |
| case Tag.OrPattern: | |
| return _readOrPattern(); | |
| case Tag.RecordPattern: | |
| return _readRecordPattern(); | |
| case Tag.RelationalPattern: | |
| return _readRelationalPattern(); | |
| case Tag.RestPattern: | |
| return _readRestPattern(); | |
| case Tag.VariablePattern: | |
| return _readVariablePattern(); | |
| case Tag.WildcardPattern: | |
| return _readWildcardPattern(); | |
| default: | |
| throw fail('unexpected pattern tag: $tag'); | |
| } | |
| } | |
| Pattern? _readOptionalPattern() { | |
| return readAndCheckOptionTag() ? _readPattern() : null; | |
| } | |
| List<Pattern> _readPatternList() { | |
| 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 emptyListOfPattern; | |
| } | |
| return new List<Pattern>.generate( | |
| length, | |
| (_) => _readPattern(), | |
| growable: useGrowableLists, | |
| ); | |
| } | |
| List<NamedPattern> _readNamedPatternList() { | |
| 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 emptyListOfNamedPattern; | |
| } | |
| return new List<NamedPattern>.generate( | |
| length, | |
| (_) => _readPattern() as NamedPattern, | |
| growable: useGrowableLists, | |
| ); | |
| } | |
| List<DeclaredVariable> _readDeclaredVariableReferenceList() { | |
| 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 emptyListOfDeclaredVariable; | |
| } | |
| return new List<DeclaredVariable>.generate( | |
| length, | |
| (_) => readDeclaredVariableReference(), | |
| growable: useGrowableLists, | |
| ); | |
| } | |
| List<MapPatternEntry> _readMapPatternEntryList() { | |
| 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 emptyListOfMapPatternEntry; | |
| } | |
| return new List<MapPatternEntry>.generate( | |
| length, | |
| (_) => _readMapPatternEntry(), | |
| growable: useGrowableLists, | |
| ); | |
| } | |
| AndPattern _readAndPattern() { | |
| int fileOffset = readOffset(); | |
| return new AndPattern(_readPattern(), _readPattern()) | |
| ..fileOffset = fileOffset; | |
| } | |
| AssignedVariablePattern _readAssignedVariablePattern() { | |
| int fileOffset = readOffset(); | |
| String variableName = readStringReference(); | |
| DartType variableType = readDartType(); | |
| Variable writeVariable = readVariableReference(); | |
| DartType matchedValueType = readDartType(); | |
| int flags = readByte(); | |
| return AssignedVariablePattern( | |
| variableName: variableName, | |
| variableType: variableType, | |
| writeVariable: writeVariable, | |
| matchedValueType: matchedValueType, | |
| needsCast: flags & AssignedVariablePattern.FlagNeedsCast != 0, | |
| hasObservableEffect: | |
| flags & AssignedVariablePattern.FlagHasObservableEffect != 0, | |
| )..fileOffset = fileOffset; | |
| } | |
| CastPattern _readCastPattern() { | |
| int fileOffset = readOffset(); | |
| return new CastPattern(_readPattern(), readDartType()) | |
| ..fileOffset = fileOffset; | |
| } | |
| ConstantPattern _readConstantPattern() { | |
| int fileOffset = readOffset(); | |
| Expression expression = readExpression(); | |
| DartType expressionType = readDartType(); | |
| Reference equalsTargetReference = readNonNullMemberReference(); | |
| FunctionType equalsType = readDartType() as FunctionType; | |
| return new ConstantPattern.byReference( | |
| expression: expression, | |
| expressionType: expressionType, | |
| equalsTargetReference: equalsTargetReference, | |
| equalsType: equalsType, | |
| )..fileOffset = fileOffset; | |
| } | |
| InvalidPattern _readInvalidPattern() { | |
| int fileOffset = readOffset(); | |
| Expression invalidExpression = readExpression(); | |
| List<DeclaredVariable> declaredVariables = | |
| readAndPushDeclaredVariableList(); | |
| return InvalidPattern( | |
| invalidExpression, | |
| declaredVariables: declaredVariables, | |
| )..fileOffset = fileOffset; | |
| } | |
| ListPattern _readListPattern() { | |
| int fileOffset = readOffset(); | |
| DartType? typeArgument = readDartTypeOption(); | |
| List<Pattern> patterns = _readPatternList(); | |
| DartType requiredType = readDartType(); | |
| DartType matchedValueType = readDartType(); | |
| int flags = readByte(); | |
| DartType lookupType = readDartType(); | |
| Reference lengthTargetReference = readNonNullMemberReference(); | |
| DartType lengthType = readDartType(); | |
| Reference lengthCheckTargetReference = readNonNullMemberReference(); | |
| FunctionType lengthCheckType = readDartType() as FunctionType; | |
| Reference sublistTargetReference = readNonNullMemberReference(); | |
| FunctionType sublistType = readDartType() as FunctionType; | |
| Reference minusTargetReference = readNonNullMemberReference(); | |
| FunctionType minusType = readDartType() as FunctionType; | |
| Reference indexGetTargetReference = readNonNullMemberReference(); | |
| FunctionType indexGetType = readDartType() as FunctionType; | |
| return new ListPattern.byReference( | |
| typeArgument: typeArgument, | |
| patterns: patterns, | |
| requiredType: requiredType, | |
| matchedValueType: matchedValueType, | |
| lookupType: lookupType, | |
| lengthTargetReference: lengthTargetReference, | |
| lengthType: lengthType, | |
| lengthCheckTargetReference: lengthCheckTargetReference, | |
| lengthCheckType: lengthCheckType, | |
| sublistTargetReference: sublistTargetReference, | |
| sublistType: sublistType, | |
| minusTargetReference: minusTargetReference, | |
| minusType: minusType, | |
| indexGetTargetReference: indexGetTargetReference, | |
| indexGetType: indexGetType, | |
| flags: flags, | |
| )..fileOffset = fileOffset; | |
| } | |
| MapPattern _readMapPattern() { | |
| int fileOffset = readOffset(); | |
| DartType? keyType = readDartTypeOption(); | |
| DartType? valueType = readDartTypeOption(); | |
| List<MapPatternEntry> entries = _readMapPatternEntryList(); | |
| DartType requiredType = readDartType(); | |
| DartType matchedValueType = readDartType(); | |
| int flags = readByte(); | |
| DartType lookupType = readDartType(); | |
| Reference containsKeyTargetReference = readNonNullMemberReference(); | |
| FunctionType containsKeyType = readDartType() as FunctionType; | |
| Reference indexGetTargetReference = readNonNullMemberReference(); | |
| FunctionType indexGetType = readDartType() as FunctionType; | |
| return new MapPattern.byReference( | |
| keyType: keyType, | |
| valueType: valueType, | |
| entries: entries, | |
| requiredType: requiredType, | |
| matchedValueType: matchedValueType, | |
| lookupType: lookupType, | |
| containsKeyTargetReference: containsKeyTargetReference, | |
| containsKeyType: containsKeyType, | |
| indexGetTargetReference: indexGetTargetReference, | |
| indexGetType: indexGetType, | |
| flags: flags, | |
| )..fileOffset = fileOffset; | |
| } | |
| NamedPattern _readNamedPattern() { | |
| int fileOffset = readOffset(); | |
| String name = readStringReference(); | |
| Pattern pattern = _readPattern(); | |
| Name fieldName = readName(); | |
| ObjectAccessKind accessKind = ObjectAccessKind.values[readByte()]; | |
| Reference? targetReference = readNullableMemberReference(); | |
| DartType? resultType = readDartTypeOption(); | |
| RecordType? recordType = readDartTypeOption() as RecordType?; | |
| int recordFieldIndex = readUInt30(); | |
| DartTypeList? typeArguments; | |
| if (readAndCheckOptionTag()) { | |
| typeArguments = readDartTypeList(); | |
| } | |
| return new NamedPattern(name, pattern) | |
| ..fieldName = fieldName | |
| ..accessKind = accessKind | |
| ..targetReference = targetReference | |
| ..resultType = resultType | |
| ..recordType = recordType | |
| ..recordFieldIndex = recordFieldIndex | |
| ..typeArguments = typeArguments | |
| ..fileOffset = fileOffset; | |
| } | |
| NullAssertPattern _readNullAssertPattern() { | |
| int fileOffset = readOffset(); | |
| Pattern pattern = _readPattern(); | |
| return new NullAssertPattern(pattern)..fileOffset = fileOffset; | |
| } | |
| NullCheckPattern _readNullCheckPattern() { | |
| int fileOffset = readOffset(); | |
| Pattern pattern = _readPattern(); | |
| return new NullCheckPattern(pattern)..fileOffset = fileOffset; | |
| } | |
| ObjectPattern _readObjectPattern() { | |
| int fileOffset = readOffset(); | |
| DartType requiredType = readDartType(); | |
| List<NamedPattern> fields = _readNamedPatternList(); | |
| DartType matchedValueType = readDartType(); | |
| bool needsCheck = readByte() == 1; | |
| return new ObjectPattern( | |
| requiredType: requiredType, | |
| fields: fields, | |
| matchedValueType: matchedValueType, | |
| needsCheck: needsCheck, | |
| )..fileOffset = fileOffset; | |
| } | |
| OrPattern _readOrPattern() { | |
| int fileOffset = readOffset(); | |
| Pattern left = _readPattern(); | |
| Pattern right = _readPattern(); | |
| List<DeclaredVariable> orPatternJointVariables = | |
| _readDeclaredVariableReferenceList(); | |
| return new OrPattern( | |
| left, | |
| right, | |
| orPatternJointVariables: orPatternJointVariables, | |
| )..fileOffset = fileOffset; | |
| } | |
| RecordPattern _readRecordPattern() { | |
| int fileOffset = readOffset(); | |
| List<Pattern> patterns = _readPatternList(); | |
| RecordType requiredType = readDartType() as RecordType; | |
| DartType matchedValueType = readDartType(); | |
| bool needsCheck = readByte() == 1; | |
| RecordType lookupType = readDartType() as RecordType; | |
| return new RecordPattern( | |
| patterns: patterns, | |
| requiredType: requiredType, | |
| matchedValueType: matchedValueType, | |
| needsCheck: needsCheck, | |
| lookupType: lookupType, | |
| )..fileOffset = fileOffset; | |
| } | |
| RelationalPattern _readRelationalPattern() { | |
| int fileOffset = readOffset(); | |
| RelationalPatternKind kind = RelationalPatternKind.values[readByte()]; | |
| Expression expression = readExpression(); | |
| DartType expressionType = readDartType(); | |
| DartType matchedValueType = readDartType(); | |
| RelationalAccessKind accessKind = RelationalAccessKind.values[readByte()]; | |
| Name name = readName(); | |
| Reference? targetReference = readNullableMemberReference(); | |
| DartTypeList? typeArguments; | |
| if (readAndCheckOptionTag()) { | |
| typeArguments = readDartTypeList(); | |
| } | |
| FunctionType? functionType = readDartTypeOption() as FunctionType?; | |
| return new RelationalPattern.byReference( | |
| kind: kind, | |
| expression: expression, | |
| expressionType: expressionType, | |
| matchedValueType: matchedValueType, | |
| accessKind: accessKind, | |
| name: name, | |
| targetReference: targetReference, | |
| typeArguments: typeArguments, | |
| functionType: functionType, | |
| )..fileOffset = fileOffset; | |
| } | |
| RestPattern _readRestPattern() { | |
| int fileOffset = readOffset(); | |
| Pattern? subPattern = _readOptionalPattern(); | |
| return new RestPattern(subPattern)..fileOffset = fileOffset; | |
| } | |
| VariablePattern _readVariablePattern() { | |
| int fileOffset = readOffset(); | |
| DartType? type = readDartTypeOption(); | |
| DeclaredVariable variable = readDeclaredVariable(); | |
| DartType matchedValueType = readDartType(); | |
| return new VariablePattern( | |
| type: type, | |
| variable: variable, | |
| matchedValueType: matchedValueType, | |
| )..fileOffset = fileOffset; | |
| } | |
| WildcardPattern _readWildcardPattern() { | |
| int fileOffset = readOffset(); | |
| DartType? type = readDartTypeOption(); | |
| return new WildcardPattern(type)..fileOffset = fileOffset; | |
| } | |
| MapPatternEntry _readMapPatternEntry() { | |
| int tag = readByte(); | |
| switch (tag) { | |
| case Tag.MapPatternEntry: | |
| int fileOffset = readOffset(); | |
| Expression key = readExpression(); | |
| Pattern value = _readPattern(); | |
| DartType? keyType = readDartTypeOption(); | |
| return new MapPatternEntry(key, value) | |
| ..keyType = keyType | |
| ..fileOffset = fileOffset; | |
| case Tag.MapPatternRestEntry: | |
| int fileOffset = readOffset(); | |
| return new MapPatternRestEntry()..fileOffset = fileOffset; | |
| default: | |
| throw fail('unexpected pattern tag: $tag'); | |
| } | |
| } | |
| SwitchExpression _readSwitchExpression() { | |
| int fileOffset = readOffset(); | |
| Expression expression = readExpression(); | |
| DartType? expressionType = readDartTypeOption(); | |
| List<SwitchExpressionCase> cases = _readSwitchExpressionCaseList(); | |
| DartType? staticType = readDartTypeOption(); | |
| return new SwitchExpression(expression, cases) | |
| ..expressionType = expressionType | |
| ..staticType = staticType | |
| ..fileOffset = fileOffset; | |
| } | |
| List<SwitchExpressionCase> _readSwitchExpressionCaseList() { | |
| 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 emptyListOfSwitchExpressionCase; | |
| } | |
| return new List<SwitchExpressionCase>.generate( | |
| length, | |
| (_) => _readSwitchExpressionCase(), | |
| growable: useGrowableLists, | |
| ); | |
| } | |
| SwitchExpressionCase _readSwitchExpressionCase() { | |
| int fileOffset = readOffset(); | |
| PatternGuard patternGuard = _readPatternGuard(); | |
| Expression expression = readExpression(); | |
| return new SwitchExpressionCase(patternGuard, expression) | |
| ..fileOffset = fileOffset; | |
| } | |
| PatternGuard _readPatternGuard() { | |
| int fileOffset = readOffset(); | |
| Pattern pattern = _readPattern(); | |
| Expression? guard = readExpressionOption(); | |
| return new PatternGuard(pattern, guard)..fileOffset = fileOffset; | |
| } | |
| IfCaseStatement _readIfCaseStatement() { | |
| int fileOffset = readOffset(); | |
| Expression expression = readExpression(); | |
| PatternGuard patternGuard = _readPatternGuard(); | |
| Statement then = readStatement(); | |
| Statement? otherwise = readStatementOption(); | |
| DartType matchedValueType = readDartType(); | |
| return new IfCaseStatement( | |
| expression: expression, | |
| patternGuard: patternGuard, | |
| then: then, | |
| otherwise: otherwise, | |
| matchedValueType: matchedValueType, | |
| )..fileOffset = fileOffset; | |
| } | |
| PatternAssignment _readPatternAssignment() { | |
| int fileOffset = readOffset(); | |
| Pattern pattern = _readPattern(); | |
| Expression expression = readExpression(); | |
| DartType matchedValueType = readDartType(); | |
| return new PatternAssignment( | |
| pattern: pattern, | |
| expression: expression, | |
| matchedValueType: matchedValueType, | |
| )..fileOffset = fileOffset; | |
| } | |
| PatternVariableDeclaration _readPatternVariableDeclaration() { | |
| int fileOffset = readOffset(); | |
| Pattern pattern = _readPattern(); | |
| Expression expression = readExpression(); | |
| bool isFinal = readByte() == 1; | |
| DartType matchedValueType = readDartType(); | |
| return new PatternVariableDeclaration( | |
| pattern, | |
| expression, | |
| isFinal: isFinal, | |
| matchedValueType: matchedValueType, | |
| )..fileOffset = fileOffset; | |
| } | |
| PatternSwitchStatement _readPatternSwitchStatement() { | |
| int fileOffset = readOffset(); | |
| Expression expression = readExpression(); | |
| DartType? expressionType = readDartTypeOption(); | |
| int count = readUInt30(); | |
| List<PatternSwitchCase> 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 = emptyListOfPatternSwitchCase; | |
| } else { | |
| cases = new List<PatternSwitchCase>.generate( | |
| count, | |
| (_) => new PatternSwitchCase( | |
| [], | |
| [], | |
| dummyStatement, | |
| isDefault: false, | |
| hasLabel: false, | |
| jointVariableDeclarations: [], | |
| jointVariableFirstUseOffsets: null, | |
| jointVariableScope: null, | |
| ), | |
| growable: useGrowableLists, | |
| ); | |
| } | |
| switchCaseStack.addAll(cases); | |
| for (int i = 0; i < cases.length; ++i) { | |
| _readPatternSwitchCaseInto(cases[i]); | |
| } | |
| switchCaseStack.length -= count; | |
| return new PatternSwitchStatement(expression, cases) | |
| ..expressionTypeInternal = expressionType | |
| ..fileOffset = fileOffset; | |
| } | |
| void _readPatternSwitchCaseInto(PatternSwitchCase caseNode) { | |
| int variableCount = readUInt30(); | |
| for (int i = 0; i < variableCount; ++i) { | |
| caseNode.jointVariableDeclarations.add( | |
| readVariableDeclaration()..parent = caseNode, | |
| ); | |
| } | |
| int caseCount = readUInt30(); | |
| for (int i = 0; i < caseCount; ++i) { | |
| caseNode.caseOffsets.add(readOffset()); | |
| caseNode.patternGuards.add(_readPatternGuard()..parent = caseNode); | |
| } | |
| int flags = readByte(); | |
| caseNode.isDefault = (flags & 0x1) != 0; | |
| caseNode.hasLabel = (flags & 0x2) != 0; | |
| caseNode.body = readStatement()..parent = caseNode; | |
| } | |
| 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) { | |
| // 23.90% (14.98% - 41.04%). | |
| case Tag.ReturnStatement: | |
| return _readReturnStatement(); | |
| // 22.74% (15.27% - 32.36%). | |
| case Tag.ExpressionStatement: | |
| return _readExpressionStatement(); | |
| // 21.29% (17.70% - 25.00%). | |
| case Tag.Block: | |
| return _readBlock(); | |
| // 9.62% (6.92% - 12.64%). | |
| case Tag.VariableStatement: | |
| return _readVariableStatement(); | |
| // 9.28% (6.69% - 11.18%). | |
| case Tag.EmptyStatement: | |
| return _readEmptyStatement(); | |
| // 9.06% (6.03% - 11.58%). | |
| case Tag.IfStatement: | |
| return _readIfStatement(); | |
| // The rest is < 2% on average in sampled dills. | |
| case Tag.AssertBlock: | |
| return _readAssertBlock(); | |
| 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.TryCatch: | |
| return _readTryCatch(); | |
| case Tag.TryFinally: | |
| return _readTryFinally(); | |
| case Tag.YieldStatement: | |
| return _readYieldStatement(); | |
| case Tag.FunctionDeclaration: | |
| return _readFunctionDeclaration(); | |
| case Tag.IfCaseStatement: | |
| return _readIfCaseStatement(); | |
| case Tag.PatternVariableDeclaration: | |
| return _readPatternVariableDeclaration(); | |
| case Tag.PatternSwitchStatement: | |
| return _readPatternSwitchStatement(); | |
| 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); | |
| int offset = readOffset(); | |
| label.fileOffset = offset; | |
| 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(); | |
| int scopeSize = readScopeSizeAndAllocateContexts(); | |
| Expression condition = readExpression(); | |
| Statement body = readStatement(); | |
| Scope? scope = readOptionalScope(scopeSize); | |
| return new WhileStatement(condition, body) | |
| ..fileOffset = offset | |
| ..scope = scope; | |
| } | |
| Statement _readDoStatement() { | |
| int offset = readOffset(); | |
| return new DoStatement(readStatement(), readExpression()) | |
| ..fileOffset = offset; | |
| } | |
| Statement _readForStatement() { | |
| int variableStackHeight = variableStack.length; | |
| int offset = readOffset(); | |
| int scopeSize = readScopeSizeAndAllocateContexts(); | |
| List<VariableDeclaration> variables = readAndPushVariableDeclarationList(); | |
| Expression? condition = readExpressionOption(); | |
| ExpressionList updates = readExpressionList(); | |
| Statement body = readStatement(); | |
| Scope? scope = readOptionalScope(scopeSize); | |
| variableStack.length = variableStackHeight; | |
| return new ForStatement(variables, condition, updates, body) | |
| ..fileOffset = offset | |
| ..scope = scope; | |
| } | |
| Statement _readForInStatement(int tag) { | |
| bool isAsync = tag == Tag.AsyncForInStatement; | |
| int variableStackHeight = variableStack.length; | |
| int offset = readOffset(); | |
| int bodyOffset = readOffset(); | |
| int scopeSize = readScopeSizeAndAllocateContexts(); | |
| DeclaredVariable variable = readAndPushDeclaredVariable(); | |
| Expression iterable = readExpression(); | |
| Statement body = readStatement(); | |
| Scope? scope = readOptionalScope(scopeSize); | |
| variableStack.length = variableStackHeight; | |
| return new ForInStatement(variable, iterable, body, isAsync: isAsync) | |
| ..fileOffset = offset | |
| ..bodyOffset = bodyOffset | |
| ..scope = scope; | |
| } | |
| Statement _readSwitchStatement() { | |
| int offset = readOffset(); | |
| bool isExplicitlyExhaustive = readByte() == 1; | |
| Expression expression = readExpression(); | |
| DartType? expressionType = readDartTypeOption(); | |
| 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( | |
| ExpressionList.empty, | |
| <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, | |
| isExplicitlyExhaustive: isExplicitlyExhaustive, | |
| ) | |
| ..expressionTypeInternal = expressionType | |
| ..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() { | |
| int offset = readOffset(); | |
| Statement body = readStatement(); | |
| int flags = readByte(); | |
| return new TryCatch(body, readCatchList(), isSynthetic: flags & 2 == 2) | |
| ..fileOffset = offset; | |
| } | |
| Statement _readTryFinally() { | |
| int offset = readOffset(); | |
| return new TryFinally(readStatement(), readStatement()) | |
| ..fileOffset = offset; | |
| } | |
| Statement _readYieldStatement() { | |
| int offset = readOffset(); | |
| int flags = readByte(); | |
| return new YieldStatement( | |
| readExpression(), | |
| isYieldStar: flags & YieldStatement.FlagYieldStar != 0, | |
| )..fileOffset = offset; | |
| } | |
| VariableStatement _readVariableStatement() { | |
| int offset = readOffset(); | |
| VariableDeclaration declaration = readVariableDeclaration(); | |
| return new VariableStatement(declaration)..fileOffset = offset; | |
| } | |
| VariableDeclaration readVariableDeclaration() { | |
| int tag = readByte(); | |
| assert(tag == Tag.VariableDeclaration); | |
| int offset = readOffset(); | |
| List<VariableContext>? capturedContexts = readOptionalCapturedContexts(); | |
| DeclaredVariable variable = readDeclaredVariable(); | |
| variableStack.add(variable); // Will be popped by the enclosing scope. | |
| return new VariableDeclaration(variable) | |
| ..fileOffset = offset | |
| ..capturedContexts = capturedContexts; | |
| } | |
| Statement _readFunctionDeclaration() { | |
| int offset = readOffset(); | |
| LocalFunctionVariable variable = readLocalFunctionVariable(); | |
| variableStack.add(variable); // Will be popped by the enclosing scope. | |
| final LocalFunctionId id = LocalFunctionId(readUInt30()); | |
| return new FunctionDeclaration(variable, readFunctionNode().functionNode) | |
| ..fileOffset = offset | |
| ..id = id; | |
| } | |
| void _readSwitchCaseInto(SwitchCase caseNode) { | |
| int offset = readOffset(); | |
| caseNode.fileOffset = offset; | |
| int length = readUInt30(); | |
| caseNode.expressions = new ExpressionList.generate(length, (_) { | |
| caseNode.expressionOffsets.add(readOffset()); | |
| return 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(); | |
| int scopeSize = readScopeSizeAndAllocateContexts(); | |
| DartType guard = readDartType(); | |
| CatchVariable? exception = readAndPushVariableOption() as CatchVariable?; | |
| CatchVariable? stackTrace = readAndPushVariableOption() as CatchVariable?; | |
| Statement body = readStatement(); | |
| Scope? scope = readOptionalScope(scopeSize); | |
| variableStack.length = variableStackHeight; | |
| return new Catch(exception, body, guard: guard, stackTrace: stackTrace) | |
| ..fileOffset = offset | |
| ..scope = scope; | |
| } | |
| Block _readBlock() { | |
| int stackHeight = variableStack.length; | |
| int offset = readOffset(); | |
| int endOffset = readOffset(); | |
| int scopeSize = readScopeSizeAndAllocateContexts(); | |
| List<Statement> body = readStatementListAlwaysGrowable(); | |
| Scope? scope = readOptionalScope(scopeSize); | |
| variableStack.length = stackHeight; | |
| return new Block(body) | |
| ..fileOffset = offset | |
| ..fileEndOffset = endOffset | |
| ..scope = scope; | |
| } | |
| 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.classReference, 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, | |
| ); | |
| } | |
| } | |
| DartTypeList readDartTypeList() { | |
| int length = readUInt30(); | |
| return DartTypeList.generate(length, (_) => readDartType()); | |
| } | |
| NamedDartTypeList readNamedTypeList() { | |
| int length = readUInt30(); | |
| return NamedDartTypeList.generate(length, (_) => readNamedType()); | |
| } | |
| 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) { | |
| // 67.66% (59.53% - 77.94%). | |
| case Tag.SimpleInterfaceType: | |
| return _readSimpleInterfaceType(forSupertype); | |
| // 11.64% (9.11% - 15.49%). | |
| case Tag.SimpleFunctionType: | |
| return _readSimpleFunctionType(); | |
| // 7.33% (5.11% - 8.76%). | |
| case Tag.InterfaceType: | |
| return _readInterfaceType(); | |
| // 5.84% (4.13% - 8.86%). | |
| case Tag.VoidType: | |
| return _readVoidType(); | |
| // 3.64% (1.20% - 7.55%). | |
| case Tag.TypeParameterType: | |
| return _readTypeParameterType(); | |
| // 2.75% (1.03% - 4.13%). | |
| case Tag.DynamicType: | |
| return _readDynamicType(); | |
| // The rest is < 2% on average in sampled dills. | |
| case Tag.TypedefType: | |
| return _readTypedefType(); | |
| case Tag.InvalidType: | |
| return _readInvalidType(); | |
| case Tag.NeverType: | |
| return _readNeverType(); | |
| case Tag.NullType: | |
| return _readNullType(); | |
| case Tag.ExtensionType: | |
| return _readExtensionType(); | |
| case Tag.FunctionType: | |
| return _readFunctionType(); | |
| case Tag.IntersectionType: | |
| return _readIntersectionType(); | |
| case Tag.RecordType: | |
| return _readRecordType(); | |
| case Tag.FutureOrType: | |
| return _readFutureOrType(); | |
| 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 _readNullType() { | |
| return const NullType(); | |
| } | |
| DartType _readInterfaceType() { | |
| int nullabilityIndex = readByte(); | |
| Reference reference = readNonNullClassReference(); | |
| DartTypeList typeArguments = readDartTypeList(); | |
| return new InterfaceType.byReference( | |
| reference, | |
| Nullability.values[nullabilityIndex], | |
| typeArguments, | |
| ); | |
| } | |
| DartType _readSimpleInterfaceType(bool forSupertype) { | |
| final int nullabilityIndex = readByte(); | |
| final int classReferenceIndex = readUInt30(); | |
| final CanonicalName? canonicalName = | |
| getNullableCanonicalNameReferenceFromInt(classReferenceIndex); | |
| if (canonicalName == null) { | |
| throw 'Expected a class reference to be valid but was `null`.'; | |
| } | |
| // Check cache. | |
| final int cacheIndex = | |
| (classReferenceIndex - 1) * Nullability.values.length + | |
| nullabilityIndex; | |
| final DartType? cached = _cachedSimpleInterfaceTypes[cacheIndex]; | |
| if (cached != null) { | |
| return cached; | |
| } | |
| // Not in cache. | |
| final Reference classReference = canonicalName.reference; | |
| final DartType result = new InterfaceType.byReference( | |
| classReference, | |
| Nullability.values[nullabilityIndex], | |
| DartTypeList.empty, | |
| ); | |
| _cachedSimpleInterfaceTypes[cacheIndex] = result; | |
| return result; | |
| } | |
| DartType _readFutureOrType() { | |
| int nullabilityIndex = readByte(); | |
| DartType typeArgument = readDartType(); | |
| return new FutureOrType(typeArgument, Nullability.values[nullabilityIndex]); | |
| } | |
| DartType _readExtensionType() { | |
| int nullabilityIndex = readByte(); | |
| Reference reference = readNonNullExtensionTypeDeclarationReference(); | |
| DartTypeList typeArguments = readDartTypeList(); | |
| readDartType(); // Read type erasure. | |
| return new ExtensionType.byReference( | |
| reference, | |
| Nullability.values[nullabilityIndex], | |
| typeArguments, | |
| ); | |
| } | |
| DartType _readFunctionType() { | |
| int typeParameterStackHeight = typeParameterStack.length; | |
| int nullabilityIndex = readByte(); | |
| StructuralParameterList typeParameters = | |
| readAndPushStructuralParameterList(); | |
| int requiredParameterCount = readUInt30(); | |
| int totalParameterCount = readUInt30(); | |
| DartTypeList positional = readDartTypeList(); | |
| NamedDartTypeList named = readNamedTypeList(); | |
| 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, | |
| ); | |
| } | |
| DartType _readSimpleFunctionType() { | |
| int nullabilityIndex = readByte(); | |
| DartTypeList positional = readDartTypeList(); | |
| DartType returnType = readDartType(); | |
| if (positional.isEmpty && returnType is VoidType) { | |
| // "FunctionType(void Function())" with different nullabilities. | |
| assert( | |
| _voidFunctionFunctionTypesCache.length == Nullability.values.length, | |
| ); | |
| FunctionType? cached = _voidFunctionFunctionTypesCache[nullabilityIndex]; | |
| if (cached != null) { | |
| return cached; | |
| } | |
| FunctionType result = new FunctionType( | |
| DartTypeList.empty, | |
| const VoidType(), | |
| Nullability.values[nullabilityIndex], | |
| ); | |
| _voidFunctionFunctionTypesCache[nullabilityIndex] = result; | |
| return result; | |
| } | |
| return new FunctionType( | |
| positional, | |
| returnType, | |
| Nullability.values[nullabilityIndex], | |
| ); | |
| } | |
| DartType _readTypeParameterType() { | |
| int declaredNullabilityIndex = readByte(); | |
| int index = readUInt30(); | |
| Object typeParameter = typeParameterStack[index]; | |
| if (typeParameter is TypeParameter) { | |
| return new TypeParameterType( | |
| typeParameter, | |
| Nullability.values[declaredNullabilityIndex], | |
| ); | |
| } else { | |
| typeParameter as StructuralParameter; | |
| return new StructuralParameterType( | |
| typeParameter, | |
| Nullability.values[declaredNullabilityIndex], | |
| ); | |
| } | |
| } | |
| DartType _readIntersectionType() { | |
| TypeParameterType left = readDartType() as TypeParameterType; | |
| DartType right = readDartType(); | |
| return new IntersectionType(left, right); | |
| } | |
| DartType _readRecordType() { | |
| int nullabilityIndex = readByte(); | |
| DartTypeList positional = readDartTypeList(); | |
| NamedDartTypeList named = readNamedTypeList(); | |
| return new RecordType( | |
| positional, | |
| named, | |
| Nullability.values[nullabilityIndex], | |
| ); | |
| } | |
| TypeParameterList readAndPushTypeParameterList( | |
| TypeParameterList list, | |
| GenericDeclaration declaration, | |
| ) { | |
| int length = readUInt30(); | |
| if (length == 0) return TypeParameterList.empty; | |
| if (list.length != length) { | |
| list = new TypeParameterList.generate( | |
| length, | |
| (_) => new TypeParameter(null, null)..declaration = declaration, | |
| ); | |
| } | |
| typeParameterStack.addAll(list); | |
| for (int i = 0; i < list.length; ++i) { | |
| readTypeParameter(list[i]); | |
| } | |
| return list; | |
| } | |
| TypeParameterList readAndPushFunctionTypeParameterList() { | |
| int length = readUInt30(); | |
| if (length == 0) { | |
| return TypeParameterList.empty; | |
| } | |
| TypeParameterList list = new TypeParameterList.generate( | |
| length, | |
| (_) => new TypeParameter(null, null), | |
| ); | |
| typeParameterStack.addAll(list); | |
| for (int i = 0; i < list.length; ++i) { | |
| readTypeParameter(list[i]); | |
| } | |
| return list; | |
| } | |
| StructuralParameterList readAndPushStructuralParameterList() { | |
| int length = readUInt30(); | |
| if (length == 0) { | |
| return StructuralParameterList.empty; | |
| } | |
| StructuralParameterList list = StructuralParameterList.generate( | |
| length, | |
| (_) => StructuralParameter(null, null), | |
| ); | |
| typeParameterStack.addAll(list); | |
| for (int i = 0; i < list.length; ++i) { | |
| readStructuralParameter(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 = new Variance.fromEncoding(variance); | |
| } | |
| node.name = readStringOrNullIfEmpty(); | |
| node.bound = readDartType(); | |
| node.defaultType = readDartType(); | |
| } | |
| void readStructuralParameter(StructuralParameter node) { | |
| node.flags = readByte(); | |
| // For now, [StructuralParameter] objects are encoded as | |
| // [TypeParameter] objects, to preserve compatibility with the binary format | |
| // consumers. | |
| // TODO(cstefantsova): Eventually remove the annotations from the binary | |
| // encoding of [StructuralParameter] objects. | |
| readAnnotationList(); | |
| int variance = readByte(); | |
| if (variance == TypeParameter.legacyCovariantSerializationMarker) { | |
| node.variance = null; | |
| } else { | |
| node.variance = new Variance.fromEncoding(variance); | |
| } | |
| node.name = readStringOrNullIfEmpty(); | |
| node.bound = readDartType(); | |
| node.defaultType = readDartType(); | |
| } | |
| Arguments readArguments() { | |
| int numArguments = readUInt30(); | |
| DartTypeList typeArguments = readDartTypeList(); | |
| ExpressionList positional = readExpressionList(); | |
| NamedExpressionList named = readNamedExpressionList(); | |
| assert(numArguments == positional.length + named.length); | |
| return new Arguments(positional, types: typeArguments, named: named); | |
| } | |
| NamedExpressionList readNamedExpressionList() { | |
| int length = readUInt30(); | |
| return new NamedExpressionList.generate( | |
| length, | |
| (_) => readNamedExpression(), | |
| ); | |
| } | |
| 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.generate( | |
| length, | |
| (int index) => readVariableDeclaration(), | |
| growable: useGrowableLists, | |
| ); | |
| } | |
| List<DeclaredVariable> readAndPushDeclaredVariableList() { | |
| 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 emptyListOfDeclaredVariable; | |
| } | |
| return new List<DeclaredVariable>.generate( | |
| length, | |
| (_) => readAndPushDeclaredVariable(), | |
| growable: useGrowableLists, | |
| ); | |
| } | |
| PositionalParameterList readAndPushPositionalParameterList() { | |
| int length = readUInt30(); | |
| return new PositionalParameterList.generate( | |
| length, | |
| (_) => readAndPushPositionalParameter(), | |
| ); | |
| } | |
| NamedParameterList readAndPushNamedParameterList() { | |
| int length = readUInt30(); | |
| return new NamedParameterList.generate( | |
| length, | |
| (_) => readAndPushNamedParameter(), | |
| ); | |
| } | |
| Variable? readAndPushVariableOption() { | |
| return readAndCheckOptionTag() ? readAndPushVariable() : null; | |
| } | |
| Variable readAndPushVariable() { | |
| Variable variable = readVariable(); | |
| variableStack.add(variable); | |
| return variable; | |
| } | |
| DeclaredVariable readAndPushDeclaredVariable() { | |
| DeclaredVariable variable = readDeclaredVariable(); | |
| variableStack.add(variable); | |
| return variable; | |
| } | |
| PositionalParameter readAndPushPositionalParameter() { | |
| PositionalParameter variable = readPositionalParameter(); | |
| variableStack.add(variable); | |
| return variable; | |
| } | |
| NamedParameter readAndPushNamedParameter() { | |
| NamedParameter variable = readNamedParameter(); | |
| variableStack.add(variable); | |
| return variable; | |
| } | |
| DeclaredVariable readDeclaredVariable() { | |
| return readVariable() as DeclaredVariable; | |
| } | |
| PositionalParameter readPositionalParameter() { | |
| return readVariable() as PositionalParameter; | |
| } | |
| NamedParameter readNamedParameter() { | |
| return readVariable() as NamedParameter; | |
| } | |
| LocalFunctionVariable readLocalFunctionVariable() { | |
| return readVariable() as LocalFunctionVariable; | |
| } | |
| Variable readVariable() { | |
| int tag = readByte(); | |
| 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 = readUInt30(); | |
| String? name = readStringOrNullIfEmpty(); | |
| DartType type = readDartType(); | |
| Expression? initializer = readExpressionOption(); | |
| Variable node; | |
| switch (tag) { | |
| case Tag.LocalVariable: | |
| node = | |
| new LocalVariable(name: name!, type: type, initializer: initializer) | |
| ..flags = flags | |
| ..fileOffset = offset | |
| ..fileEqualsOffset = fileEqualsOffset; | |
| case Tag.LateVariable: | |
| node = | |
| new LateVariable(name: name!, type: type, initialValue: initializer) | |
| ..flags = flags | |
| ..fileOffset = offset | |
| ..fileEqualsOffset = fileEqualsOffset; | |
| case Tag.LocalFunctionVariable: | |
| assert( | |
| initializer == null, | |
| "Unexpected initializer on LocalFunctionVariable", | |
| ); | |
| node = new LocalFunctionVariable(name: name!, type: type) | |
| ..flags = flags | |
| ..fileOffset = offset | |
| ..fileEqualsOffset = fileEqualsOffset; | |
| case Tag.ConstVariable: | |
| node = new ConstVariable(name: name!, type: type, value: initializer) | |
| ..flags = flags | |
| ..fileOffset = offset | |
| ..fileEqualsOffset = fileEqualsOffset; | |
| case Tag.SyntheticVariable: | |
| node = | |
| new SyntheticVariable( | |
| cosmeticName: name, | |
| type: type, | |
| initializer: initializer, | |
| ) | |
| ..flags = flags | |
| ..fileOffset = offset | |
| ..fileEqualsOffset = fileEqualsOffset; | |
| case Tag.CatchVariable: | |
| assert(initializer == null, "Unexpected initializer on CatchVariable"); | |
| node = new CatchVariable(name: name!, type: type) | |
| ..flags = flags | |
| ..fileOffset = offset | |
| ..fileEqualsOffset = fileEqualsOffset; | |
| case Tag.PositionalParameter: | |
| node = | |
| new PositionalParameter( | |
| parameterName: name!, | |
| type: type, | |
| defaultValue: initializer, | |
| ) | |
| ..flags = flags | |
| ..fileOffset = offset | |
| ..fileEqualsOffset = fileEqualsOffset; | |
| case Tag.NamedParameter: | |
| node = | |
| new NamedParameter( | |
| parameterName: name!, | |
| type: type, | |
| defaultValue: initializer, | |
| ) | |
| ..flags = flags | |
| ..fileOffset = offset | |
| ..fileEqualsOffset = fileEqualsOffset; | |
| case Tag.ThisVariable: | |
| assert(name == null, "Unexpected name on ThisVariable."); | |
| assert(initializer == null, "Unexpected initializer on ThisVariable."); | |
| node = new ThisVariable(type: type) | |
| ..flags = flags | |
| ..fileOffset = offset | |
| ..fileEqualsOffset = fileEqualsOffset; | |
| default: | |
| throw new UnsupportedError("Unexpected variable tag $tag"); | |
| } | |
| if (annotations.isNotEmpty) { | |
| for (int i = 0; i < annotations.length; ++i) { | |
| Expression annotation = annotations[i]; | |
| annotation.parent = node; | |
| } | |
| node.annotations = annotations; | |
| } | |
| return node; | |
| } | |
| List<VariableContext>? readOptionalCapturedContexts() { | |
| return readAndCheckOptionTag() ? readCapturedContexts() : null; | |
| } | |
| List<VariableContext> readCapturedContexts() { | |
| int length = readUInt30(); | |
| return new List<VariableContext>.generate( | |
| length, | |
| (_) => readVariableContextReference(), | |
| growable: useGrowableLists, | |
| ); | |
| } | |
| VariableContext readVariableContextReference() { | |
| int index = readUInt30(); | |
| if (index >= variableContextStack.length) { | |
| throw fail( | |
| 'Unexpected variable context index: $index. ' | |
| 'Current variable context count: ${variableContextStack.length}.', | |
| ); | |
| } | |
| return variableContextStack[index]; | |
| } | |
| 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; | |
| List<int>? _allKnownMetadataKeys; | |
| int? _previousMetadataLookupKey; | |
| new( | |
| Uint8List 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, | |
| ) { | |
| // If reading a component with several sub-components there's no reason to | |
| // lookup in old ones. | |
| _subsections = null; | |
| _allKnownMetadataKeys = null; | |
| // 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; | |
| List<int> allKnownMetadataKeys = _allKnownMetadataKeys ??= []; | |
| for (int j = 0; j < mappingLength; j++) { | |
| final int nodeOffset = _componentStartOffset + readUint32(); | |
| final int metadataOffset = | |
| binaryOffsetForMetadataPayloads + readUint32(); | |
| mapping[nodeOffset] = metadataOffset; | |
| allKnownMetadataKeys.add(nodeOffset); | |
| } | |
| (_subsections ??= <_MetadataSubsection>[]).add( | |
| new _MetadataSubsection(repository, mapping), | |
| ); | |
| } | |
| // Start of the subsection and the end of the previous one. | |
| endOffset = mappingStart - 4; | |
| } | |
| _allKnownMetadataKeys?.sort(); | |
| } | |
| 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; | |
| } | |
| bool _hasMetadata(int nodeOffset) { | |
| List<int>? allKnownMetadataKeys = _allKnownMetadataKeys; | |
| if (allKnownMetadataKeys == null) return false; | |
| if (allKnownMetadataKeys.isEmpty) return false; | |
| int? prevIndex = _previousMetadataLookupKey; | |
| if (prevIndex != null) { | |
| if (prevIndex >= 0 && prevIndex < allKnownMetadataKeys.length - 1) { | |
| int prevOffset = allKnownMetadataKeys[prevIndex]; | |
| int nextOffset = allKnownMetadataKeys[prevIndex + 1]; | |
| if (prevOffset < nodeOffset && nextOffset > nodeOffset) { | |
| // Common case: This is between the previously found metadata | |
| // and the next metadata (and doesn't itself have metadata). | |
| return false; | |
| } | |
| } | |
| } | |
| _previousMetadataLookupKey = null; | |
| int low = 0, high = allKnownMetadataKeys.length - 1; | |
| while (low < high) { | |
| int mid = high - ((high - low) >> 1); // Get middle, rounding up. | |
| int pivot = allKnownMetadataKeys[mid]; | |
| if (pivot <= nodeOffset) { | |
| low = mid; | |
| } else { | |
| high = mid - 1; | |
| } | |
| } | |
| this._previousMetadataLookupKey = low; | |
| if (allKnownMetadataKeys[low] == nodeOffset) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| @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 bool hasMetadata = _hasMetadata(_byteOffset); | |
| final DartType result = super.readDartType(forSupertype: forSupertype); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Library readLibrary(Component component, int endOffset) { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Library result = super.readLibrary(component, endOffset); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Typedef readTypedef() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Typedef result = super.readTypedef(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Class readClass(int endOffset) { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Class result = super.readClass(endOffset); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Extension readExtension() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Extension result = super.readExtension(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| ExtensionTypeDeclaration readExtensionTypeDeclaration() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final ExtensionTypeDeclaration result = super | |
| .readExtensionTypeDeclaration(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Field readField() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Field result = super.readField(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Constructor readConstructor() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Constructor result = super.readConstructor(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Procedure readProcedure(int endOffset) { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Procedure result = super.readProcedure(endOffset); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Initializer readInitializer() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Initializer result = super.readInitializer(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| FunctionNodeAndContexts readFunctionNode({ | |
| bool lazyLoadBody = false, | |
| int outerEndOffset = -1, | |
| }) { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final FunctionNodeAndContexts result = super.readFunctionNode( | |
| lazyLoadBody: lazyLoadBody, | |
| outerEndOffset: outerEndOffset, | |
| ); | |
| return hasMetadata | |
| ? new FunctionNodeAndContexts( | |
| _associateMetadata(result.functionNode, nodeOffset), | |
| result.preAllocatedContexts, | |
| ) | |
| : result; | |
| } | |
| @override | |
| Expression readExpression() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Expression result = super.readExpression(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Arguments readArguments() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Arguments result = super.readArguments(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| NamedExpression readNamedExpression() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final NamedExpression result = super.readNamedExpression(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Variable readVariable() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Variable result = super.readVariable(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Statement readStatement() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Statement result = super.readStatement(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| VariableDeclaration readVariableDeclaration() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final VariableDeclaration result = super.readVariableDeclaration(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Combinator readCombinator() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Combinator result = super.readCombinator(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| LibraryDependency readLibraryDependency() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final LibraryDependency result = super.readLibraryDependency(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| LibraryPart readLibraryPart() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final LibraryPart result = super.readLibraryPart(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @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; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| InterfaceType type = | |
| super.readDartType(forSupertype: true) as InterfaceType; | |
| Supertype result = new Supertype.byReference( | |
| type.classReference, | |
| type.typeArguments, | |
| ); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| @override | |
| Name readName() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final Name result = super.readName(); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : result; | |
| } | |
| } | |
| /// Deserialized MetadataMapping corresponding to the given metadata repository. | |
| class _MetadataSubsection(this.repository, this.mapping) { | |
| /// [MetadataRepository] that can read this subsection. | |
| final MetadataRepository repository; | |
| /// Deserialized mapping from node offsets to metadata offsets. | |
| final Map<int, int> mapping; | |
| } | |
| /// Result of reading a function node and allocating its scope. | |
| /// | |
| /// Function nodes with lazily loaded bodies have empty scopes immediately after | |
| /// the moment of reading, even though the scope may be not empty after loading | |
| /// the body. This helper class holds both the read function (potentially, | |
| /// without the body and the scope) and the pre-allocated variable contexts from | |
| /// the scope. | |
| class FunctionNodeAndContexts(this.functionNode, this.preAllocatedContexts) { | |
| final FunctionNode functionNode; | |
| final List<VariableContext>? preAllocatedContexts; | |
| } |