| // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| library kernel.ast_from_binary; | |
| import 'dart:developer'; | |
| import 'dart:convert'; | |
| import 'dart:typed_data'; | |
| import '../ast.dart'; | |
| import '../transformations/flags.dart'; | |
| import 'tag.dart'; | |
| const int $_ = 95; | |
| class ParseError { | |
| final String? filename; | |
| final int byteIndex; | |
| final String message; | |
| final String path; | |
| ParseError(this.message, | |
| {required this.filename, required this.byteIndex, required this.path}); | |
| @override | |
| String toString() => '$filename:$byteIndex: $message at $path'; | |
| } | |
| class InvalidKernelVersionError { | |
| final String? filename; | |
| final int version; | |
| InvalidKernelVersionError(this.filename, this.version); | |
| @override | |
| String toString() { | |
| StringBuffer sb = new StringBuffer(); | |
| sb.write('Unexpected Kernel Format Version ${version} ' | |
| '(expected ${Tag.BinaryFormatVersion})'); | |
| if (filename != null) { | |
| sb.write(' when reading $filename.'); | |
| } | |
| return '$sb'; | |
| } | |
| } | |
| class InvalidKernelSdkVersionError { | |
| final String version; | |
| InvalidKernelSdkVersionError(this.version); | |
| @override | |
| String toString() { | |
| return 'Unexpected Kernel SDK Version ${version} ' | |
| '(expected ${expectedSdkHash}).'; | |
| } | |
| } | |
| class CompilationModeError { | |
| final String message; | |
| CompilationModeError(this.message); | |
| @override | |
| String toString() => "CompilationModeError[$message]"; | |
| } | |
| class _ComponentIndex { | |
| static const int numberOfFixedFields = 12; | |
| final int binaryOffsetForSourceTable; | |
| final int binaryOffsetForCanonicalNames; | |
| final int binaryOffsetForMetadataPayloads; | |
| final int binaryOffsetForMetadataMappings; | |
| final int binaryOffsetForStringTable; | |
| final int binaryOffsetForConstantTable; | |
| final int binaryOffsetForConstantTableIndex; | |
| final int binaryOffsetForStartOfComponentIndex; | |
| final int mainMethodReference; | |
| final NonNullableByDefaultCompiledMode compiledMode; | |
| final List<int> libraryOffsets; | |
| final int libraryCount; | |
| final int componentFileSizeInBytes; | |
| _ComponentIndex( | |
| {required this.binaryOffsetForSourceTable, | |
| required this.binaryOffsetForCanonicalNames, | |
| required this.binaryOffsetForMetadataPayloads, | |
| required this.binaryOffsetForMetadataMappings, | |
| required this.binaryOffsetForStringTable, | |
| required this.binaryOffsetForConstantTable, | |
| required this.binaryOffsetForConstantTableIndex, | |
| required this.binaryOffsetForStartOfComponentIndex, | |
| required this.mainMethodReference, | |
| required this.compiledMode, | |
| required this.libraryOffsets, | |
| required this.libraryCount, | |
| required this.componentFileSizeInBytes}); | |
| } | |
| class SubComponentView { | |
| final List<Library> libraries; | |
| final int componentStartOffset; | |
| final int componentFileSize; | |
| SubComponentView( | |
| this.libraries, this.componentStartOffset, this.componentFileSize); | |
| } | |
| /// [StringInterner] allows Strings created from the binary to be shared with | |
| /// other components. | |
| /// | |
| /// The [StringInterner] is an optional parameter to [BinaryBuilder], so sharing | |
| /// should not be required for correctness, and an implementation of | |
| /// [StringInterner] method may be partial (sometimes not finding an existing | |
| /// object) or trivial (returning the argument). | |
| abstract class StringInterner { | |
| /// Returns a string with the same contents as [string]. | |
| String internString(String string); | |
| } | |
| /// Helper used to trigger the read of a late variable in asserts. | |
| bool _lateIsInitialized(dynamic value) { | |
| return true; | |
| } | |
| class BinaryBuilder { | |
| final List<VariableDeclaration> variableStack = <VariableDeclaration>[]; | |
| final List<LabeledStatement> labelStack = <LabeledStatement>[]; | |
| int labelStackBase = 0; | |
| int switchCaseStackBase = 0; | |
| final List<SwitchCase> switchCaseStack = <SwitchCase>[]; | |
| final List< /* TypeParameter | StructuralParameter */ Object> | |
| typeParameterStack = <Object>[]; | |
| 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, | |
| null | |
| ]; | |
| int _transformerFlags = 0; | |
| Library? _currentLibrary; | |
| int _componentStartOffset = 0; | |
| NonNullableByDefaultCompiledMode? compilationMode; | |
| // If something goes wrong, this list should indicate what library, | |
| // class, and member was being built. | |
| List<String> debugPath = <String>[]; | |
| final bool alwaysCreateNewNamedNodes; | |
| /// If binary contains metadata section with payloads referencing other nodes | |
| /// such Kernel binary can't be read lazily because metadata cross references | |
| /// will not be resolved correctly. | |
| bool _disableLazyReading = false; | |
| /// If binary contains metadata section with payloads referencing other nodes | |
| /// such Kernel binary can't be read lazily because metadata cross references | |
| /// will not be resolved correctly. | |
| bool _disableLazyClassReading = false; | |
| /// [stringInterner] (optional) may be used to allow components to share | |
| /// instances of [String] that have the same contents. | |
| final StringInterner? stringInterner; | |
| /// When creating lists that *might* be growable, use this boolean as the | |
| /// setting to pass to `growable` so the dill can be loaded in a more compact | |
| /// manner if the caller knows that the growability isn't needed. | |
| final bool useGrowableLists; | |
| /// Note that [disableLazyClassReading] is incompatible | |
| /// with checkCanonicalNames on readComponent. | |
| BinaryBuilder(this._bytes, | |
| {this.filename, | |
| bool disableLazyReading = false, | |
| bool disableLazyClassReading = false, | |
| bool? alwaysCreateNewNamedNodes, | |
| this.stringInterner, | |
| this.useGrowableLists = true}) | |
| : _disableLazyReading = disableLazyReading, | |
| _disableLazyClassReading = disableLazyReading || | |
| disableLazyClassReading || | |
| // Disable lazy class reading when forcing the creation of new named | |
| // nodes as it is a logical "relink" to the new version (overwriting | |
| // the old one) - which doesn't play well with lazy loading class | |
| // content as old loaded references will then potentially still | |
| // point to the old content until the new class has been lazy | |
| // loaded. | |
| (alwaysCreateNewNamedNodes == true), | |
| this.alwaysCreateNewNamedNodes = alwaysCreateNewNamedNodes ?? false; | |
| Never fail(String message) { | |
| throw ParseError(message, | |
| byteIndex: _byteOffset, filename: filename, path: debugPath.join('::')); | |
| } | |
| int get byteOffset => _byteOffset; | |
| int readByte() => _bytes[_byteOffset++]; | |
| int 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 int length = readUInt30(); | |
| final List<ConstantMapEntry> entries = | |
| new List<ConstantMapEntry>.generate(length, (_) { | |
| final Constant key = readConstantReference(); | |
| final Constant value = readConstantReference(); | |
| return new ConstantMapEntry(key, value); | |
| }, growable: useGrowableLists); | |
| return new MapConstant(keyType, valueType, entries); | |
| } | |
| Constant _readListConstant() { | |
| final DartType typeArgument = readDartType(); | |
| List<Constant> entries = _readConstantReferenceList(); | |
| return new ListConstant(typeArgument, entries); | |
| } | |
| Constant _readSetConstant() { | |
| final DartType typeArgument = readDartType(); | |
| List<Constant> entries = _readConstantReferenceList(); | |
| return new SetConstant(typeArgument, entries); | |
| } | |
| Constant _readRecordConstant() { | |
| List<Constant> 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 List<DartType> typeArguments = readDartTypeList(); | |
| final int fieldValueCount = readUInt30(); | |
| final Map<Reference, Constant> fieldValues = <Reference, Constant>{}; | |
| for (int i = 0; i < fieldValueCount; i++) { | |
| final Reference fieldRef = readNonNullCanonicalNameReference().reference; | |
| final Constant constant = readConstantReference(); | |
| fieldValues[fieldRef] = constant; | |
| } | |
| return new InstanceConstant(classReference, typeArguments, fieldValues); | |
| } | |
| Constant _readInstantiationConstant() { | |
| final Constant tearOffConstant = readConstantReference(); | |
| final List<DartType> types = readDartTypeList(); | |
| return new InstantiationConstant(tearOffConstant, types); | |
| } | |
| Constant _readTypedefTearOffConstant() { | |
| final List<StructuralParameter> parameters = | |
| readAndPushStructuralParameterList(); | |
| final TearOffConstant tearOffConstant = | |
| readConstantReference() as TearOffConstant; | |
| final List<DartType> types = readDartTypeList(); | |
| typeParameterStack.length -= parameters.length; | |
| return new TypedefTearOffConstant(parameters, tearOffConstant, types); | |
| } | |
| Constant _readStaticTearOffConstant() { | |
| final Reference reference = readNonNullCanonicalNameReference().reference; | |
| return new StaticTearOffConstant.byReference(reference); | |
| } | |
| Constant _readConstructorTearOffConstant() { | |
| final Reference reference = readNonNullCanonicalNameReference().reference; | |
| return new ConstructorTearOffConstant.byReference(reference); | |
| } | |
| Constant _readRedirectingFactoryTearOffConstant() { | |
| final Reference reference = readNonNullCanonicalNameReference().reference; | |
| return new RedirectingFactoryTearOffConstant.byReference(reference); | |
| } | |
| Constant _readTypeLiteralConstant() { | |
| final DartType type = readDartType(); | |
| return new TypeLiteralConstant(type); | |
| } | |
| Constant _readUnevaluatedConstant() { | |
| final Expression expression = readExpression(); | |
| return new UnevaluatedConstant(expression); | |
| } | |
| Constant readConstantReference() { | |
| final int index = readUInt30(); | |
| Constant constant = _constantTable[index]; | |
| assert(!identical(constant, dummyConstant), | |
| "No constant found at index $index."); | |
| return constant; | |
| } | |
| List<Constant> _readConstantReferenceList() { | |
| final int length = readUInt30(); | |
| if (!useGrowableLists && length == 0) { | |
| // When lists don't have to be growable anyway, we might as well use an | |
| // almost constant one for the empty list. | |
| return emptyListOfConstant; | |
| } | |
| return new List<Constant>.generate(length, (_) => readConstantReference(), | |
| growable: useGrowableLists); | |
| } | |
| Uri readUriReference() { | |
| return _sourceUriTable[readUInt30()]; | |
| } | |
| String readStringReference() { | |
| return _stringTable[readUInt30()]; | |
| } | |
| List<String> readStringReferenceList() { | |
| int length = readUInt30(); | |
| if (!useGrowableLists && length == 0) { | |
| // When lists don't have to be growable anyway, we might as well use an | |
| // almost constant one for the empty list. | |
| return emptyListOfString; | |
| } | |
| return new List<String>.generate(length, (_) => readStringReference(), | |
| growable: useGrowableLists); | |
| } | |
| 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 -= | |
| ((libraryCount + 1) + _ComponentIndex.numberOfFixedFields) * 4; | |
| // Now read the component index. | |
| int binaryOffsetForSourceTable = _componentStartOffset + readUint32(); | |
| int binaryOffsetForConstantTable = _componentStartOffset + readUint32(); | |
| int binaryOffsetForConstantTableIndex = | |
| _componentStartOffset + readUint32(); | |
| int binaryOffsetForCanonicalNames = _componentStartOffset + readUint32(); | |
| int binaryOffsetForMetadataPayloads = _componentStartOffset + readUint32(); | |
| int binaryOffsetForMetadataMappings = _componentStartOffset + readUint32(); | |
| int binaryOffsetForStringTable = _componentStartOffset + readUint32(); | |
| int binaryOffsetForStartOfComponentIndex = | |
| _componentStartOffset + readUint32(); | |
| int mainMethodReference = readUint32(); | |
| NonNullableByDefaultCompiledMode compiledMode = | |
| NonNullableByDefaultCompiledMode.values[readUint32()]; | |
| for (int i = 0; i < libraryCount + 1; ++i) { | |
| libraryOffsets[i] = _componentStartOffset + readUint32(); | |
| } | |
| _byteOffset = savedByteIndex; | |
| return new _ComponentIndex( | |
| libraryCount: libraryCount, | |
| libraryOffsets: libraryOffsets, | |
| componentFileSizeInBytes: componentFileSizeInBytes, | |
| binaryOffsetForSourceTable: binaryOffsetForSourceTable, | |
| binaryOffsetForCanonicalNames: binaryOffsetForCanonicalNames, | |
| binaryOffsetForMetadataPayloads: binaryOffsetForMetadataPayloads, | |
| binaryOffsetForMetadataMappings: binaryOffsetForMetadataMappings, | |
| binaryOffsetForStringTable: binaryOffsetForStringTable, | |
| binaryOffsetForConstantTable: binaryOffsetForConstantTable, | |
| binaryOffsetForConstantTableIndex: binaryOffsetForConstantTableIndex, | |
| binaryOffsetForStartOfComponentIndex: | |
| binaryOffsetForStartOfComponentIndex, | |
| mainMethodReference: mainMethodReference, | |
| compiledMode: compiledMode); | |
| } | |
| void _readOneComponentSource(Component component, int componentFileSize) { | |
| _componentStartOffset = _byteOffset; | |
| _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); | |
| if (compilationMode == null) { | |
| compilationMode = component.modeRaw; | |
| } | |
| compilationMode = | |
| mergeCompilationModeOrThrow(compilationMode, index.compiledMode); | |
| _byteOffset = index.binaryOffsetForStringTable; | |
| readStringTable(); | |
| _byteOffset = index.binaryOffsetForCanonicalNames; | |
| readLinkTable(component.root); | |
| // TODO(alexmarkov): reverse metadata mappings and read forwards | |
| _byteOffset = index.binaryOffsetForStringTable; // Read backwards. | |
| _readMetadataMappings(component, index.binaryOffsetForMetadataPayloads); | |
| _associateMetadata(component, _componentStartOffset); | |
| _byteOffset = index.binaryOffsetForSourceTable; | |
| Map<Uri, Source> uriToSource = readUriToSource(readCoverage: true); | |
| _mergeUriToSource(component.uriToSource, uriToSource); | |
| _byteOffset = index.binaryOffsetForConstantTable; | |
| readConstantTable(); | |
| // We don't need the constant table index on the dart side. | |
| int numberOfLibraries = index.libraryCount; | |
| SubComponentView? result; | |
| if (createView) { | |
| result = new SubComponentView( | |
| new List<Library>.generate(numberOfLibraries, (int i) { | |
| _byteOffset = index.libraryOffsets[i]; | |
| return readLibrary(component, index.libraryOffsets[i + 1]); | |
| }, growable: false), | |
| _componentStartOffset, | |
| componentFileSize); | |
| } else { | |
| for (int i = 0; i < numberOfLibraries; ++i) { | |
| _byteOffset = index.libraryOffsets[i]; | |
| readLibrary(component, index.libraryOffsets[i + 1]); | |
| } | |
| } | |
| Reference? mainMethod = | |
| getNullableMemberReferenceFromInt(index.mainMethodReference); | |
| component.setMainMethodAndMode(mainMethod, false, compilationMode!); | |
| _byteOffset = _componentStartOffset + componentFileSize; | |
| assert(typeParameterStack.isEmpty); | |
| return result; | |
| } | |
| /// Read a list of strings. If the list is empty, [null] is returned. | |
| List<String>? readListOfStrings() { | |
| int length = readUInt30(); | |
| if (length == 0) return null; | |
| return new List<String>.generate(length, (_) => readString(), | |
| growable: useGrowableLists); | |
| } | |
| /// Read the uri-to-source part of the binary. | |
| /// Note that this can include coverage, but that it is only included if | |
| /// [readCoverage] is true, otherwise coverage will be skipped. Note also that | |
| /// if [readCoverage] is true, references are read and that the link table | |
| /// thus has to be read first. | |
| Map<Uri, Source> readUriToSource({required bool readCoverage}) { | |
| 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( | |
| mergeCompilationModeOrThrow( | |
| compilationMode, library.nonNullableByDefaultCompiledMode) == | |
| compilationMode, | |
| "Cannot load ${library.nonNullableByDefaultCompiledMode} " | |
| "into component with mode $compilationMode"); | |
| assert(() { | |
| debugPath.add(library!.name ?? library.importUri.toString()); | |
| return true; | |
| }()); | |
| library.annotations = readAnnotationList(library); | |
| _readLibraryDependencies(library); | |
| _readAdditionalExports(library); | |
| _readLibraryParts(library); | |
| _readTypedefList(library); | |
| _readClassList(library, classOffsets); | |
| _readExtensionList(library); | |
| _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 = readExpressionList(); | |
| Reference targetLibrary = readNonNullLibraryReference(); | |
| String? prefixName = readStringOrNullIfEmpty(); | |
| List<Combinator> names = readCombinatorList(); | |
| return new LibraryDependency.byReference( | |
| flags, annotations, targetLibrary, prefixName, names) | |
| ..fileOffset = fileOffset; | |
| } | |
| void _readAdditionalExports(Library library) { | |
| int numExportedReference = readUInt30(); | |
| if (numExportedReference != 0) { | |
| library.additionalExports.clear(); | |
| for (int i = 0; i < numExportedReference; i++) { | |
| CanonicalName exportedName = readNonNullCanonicalNameReference(); | |
| Reference reference = exportedName.reference; | |
| library.additionalExports.add(reference); | |
| } | |
| } | |
| } | |
| Combinator readCombinator() { | |
| bool isShow = readByte() == 1; | |
| List<String> names = readStringReferenceList(); | |
| return new Combinator(isShow, names); | |
| } | |
| List<Combinator> readCombinatorList() { | |
| int length = readUInt30(); | |
| if (!useGrowableLists && length == 0) { | |
| // When lists don't have to be growable anyway, we might as well use an | |
| // almost constant one for the empty list. | |
| return emptyListOfCombinator; | |
| } | |
| return new List<Combinator>.generate(length, (_) => readCombinator(), | |
| growable: useGrowableLists); | |
| } | |
| void _readLibraryParts(Library library) { | |
| int length = readUInt30(); | |
| if (!useGrowableLists && length == 0) { | |
| // When lists don't have to be growable anyway, we might as well use an | |
| // almost constant one for the empty list. | |
| library.parts = emptyListOfLibraryPart; | |
| } else { | |
| library.parts = new List<LibraryPart>.generate( | |
| length, (int index) => readLibraryPart()..parent = library, | |
| growable: useGrowableLists); | |
| } | |
| } | |
| LibraryPart readLibraryPart() { | |
| List<Expression> annotations = readExpressionList(); | |
| String partUri = readStringReference(); | |
| return new LibraryPart(annotations, partUri); | |
| } | |
| Typedef readTypedef() { | |
| CanonicalName canonicalName = readNonNullCanonicalNameReference(); | |
| Reference reference = canonicalName.reference; | |
| Typedef? node = reference.node as Typedef?; | |
| if (alwaysCreateNewNamedNodes) { | |
| node = null; | |
| } | |
| Uri fileUri = readUriReference(); | |
| int fileOffset = readOffset(); | |
| String name = readStringReference(); | |
| if (node == null) { | |
| node = new Typedef(name, null, reference: reference, fileUri: fileUri); | |
| } | |
| node.annotations = readAnnotationList(node); | |
| readAndPushTypeParameterList(node.typeParameters, node); | |
| DartType type = readDartType(); | |
| 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); | |
| 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(); | |
| 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(); | |
| 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; | |
| }; | |
| } | |
| int getAndResetTransformerFlags() { | |
| int flags = _transformerFlags; | |
| _transformerFlags = 0; | |
| return flags; | |
| } | |
| /// Adds the given flag to the current [Member.transformerFlags]. | |
| void addTransformerFlag(int flags) { | |
| _transformerFlags |= flags; | |
| } | |
| Field readField() { | |
| int tag = readByte(); | |
| assert(tag == Tag.Field); | |
| CanonicalName fieldCanonicalName = readNonNullCanonicalNameReference(); | |
| Reference fieldReference = fieldCanonicalName.reference; | |
| CanonicalName getterCanonicalName = readNonNullCanonicalNameReference(); | |
| Reference getterReference = getterCanonicalName.reference; | |
| CanonicalName? setterCanonicalName = readNullableCanonicalNameReference(); | |
| Reference? setterReference = setterCanonicalName?.reference; | |
| Field? node = fieldReference.node as Field?; | |
| if (alwaysCreateNewNamedNodes) { | |
| node = null; | |
| } | |
| Uri fileUri = readUriReference(); | |
| int fileOffset = readOffset(); | |
| int fileEndOffset = readOffset(); | |
| int flags = readUInt30(); | |
| Name name = readName(); | |
| if (node == null) { | |
| if (setterReference != null) { | |
| node = new Field.mutable(name, | |
| fieldReference: fieldReference, | |
| getterReference: getterReference, | |
| setterReference: setterReference, | |
| fileUri: fileUri); | |
| } else { | |
| node = new Field.immutable(name, | |
| fieldReference: fieldReference, | |
| getterReference: getterReference, | |
| fileUri: fileUri); | |
| } | |
| } | |
| List<Expression> annotations = readAnnotationList(node); | |
| assert(() { | |
| debugPath.add(name.text); | |
| return true; | |
| }()); | |
| DartType type = readDartType(); | |
| Expression? initializer = readExpressionOption(); | |
| int transformerFlags = getAndResetTransformerFlags(); | |
| assert(((_) => true)(debugPath.removeLast())); | |
| node.fileOffset = fileOffset; | |
| node.fileEndOffset = fileEndOffset; | |
| node.flags = flags; | |
| node.name = name; | |
| node.fileUri = fileUri; | |
| node.annotations = annotations; | |
| node.type = type; | |
| node.initializer = initializer; | |
| node.initializer?.parent = node; | |
| node.transformerFlags = transformerFlags; | |
| return node; | |
| } | |
| Constructor readConstructor() { | |
| int tag = readByte(); | |
| assert(tag == Tag.Constructor); | |
| CanonicalName canonicalName = readNonNullCanonicalNameReference(); | |
| Reference reference = canonicalName.reference; | |
| Constructor? node = reference.node as Constructor?; | |
| if (alwaysCreateNewNamedNodes) { | |
| node = null; | |
| } | |
| Uri fileUri = readUriReference(); | |
| int startFileOffset = readOffset(); | |
| int fileOffset = readOffset(); | |
| int fileEndOffset = readOffset(); | |
| int flags = readByte(); | |
| Name name = readName(); | |
| List<Expression> annotations = readAnnotationList(); | |
| assert(() { | |
| debugPath.add(name.text); | |
| return true; | |
| }()); | |
| FunctionNode function = readFunctionNode(); | |
| if (node == null) { | |
| node = new Constructor(function, | |
| reference: reference, name: name, fileUri: fileUri); | |
| } | |
| pushVariableDeclarations(function.positionalParameters); | |
| pushVariableDeclarations(function.namedParameters); | |
| _readInitializers(node); | |
| variableStack.length = 0; | |
| int transformerFlags = getAndResetTransformerFlags(); | |
| assert(((_) => true)(debugPath.removeLast())); | |
| node.startFileOffset = startFileOffset; | |
| node.fileOffset = fileOffset; | |
| node.fileEndOffset = fileEndOffset; | |
| node.flags = flags; | |
| node.name = name; | |
| node.fileUri = fileUri; | |
| node.annotations = annotations; | |
| setParents(annotations, node); | |
| node.function = function..parent = node; | |
| node.transformerFlags = transformerFlags; | |
| return node; | |
| } | |
| Procedure readProcedure(int endOffset) { | |
| int tag = readByte(); | |
| assert(tag == Tag.Procedure); | |
| CanonicalName canonicalName = readNonNullCanonicalNameReference(); | |
| Reference reference = canonicalName.reference; | |
| Procedure? node; | |
| 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); | |
| if (node == null) { | |
| node = new Procedure(name, kind, function, | |
| reference: reference, fileUri: fileUri); | |
| } else { | |
| assert(node.kind == kind); | |
| } | |
| int transformerFlags = getAndResetTransformerFlags(); | |
| assert(((_) => true)(debugPath.removeLast())); | |
| node.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.setTransformerFlagsWithoutLazyLoading(transformerFlags); | |
| node.stubKind = stubKind; | |
| node.stubTargetReference = stubTargetReference; | |
| node.signatureType = signatureType; | |
| assert((node.stubKind == ProcedureStubKind.ConcreteForwardingStub && | |
| node.stubTargetReference != null) || | |
| !(node.isForwardingStub && node.function.body != null)); | |
| assert(!(node.isMemberSignature && node.stubTargetReference == null), | |
| "No member signature origin for member signature $node."); | |
| return node; | |
| } | |
| void _readInitializers(Constructor constructor) { | |
| int length = readUInt30(); | |
| if (!useGrowableLists && length == 0) { | |
| // When lists don't have to be growable anyway, we might as well use a | |
| // constant one for the empty list. | |
| constructor.initializers = emptyListOfInitializer; | |
| } else { | |
| constructor.initializers = new List<Initializer>.generate( | |
| length, (int index) => readInitializer()..parent = constructor, | |
| growable: useGrowableLists); | |
| } | |
| } | |
| Initializer readInitializer() { | |
| int tag = readByte(); | |
| bool isSynthetic = readByte() == 1; | |
| switch (tag) { | |
| // 52.71% (43.80% - 59.02%). | |
| case Tag.FieldInitializer: | |
| return _readFieldInitializer(isSynthetic); | |
| // 42.01% (28.38% - 55.93%) | |
| case Tag.SuperInitializer: | |
| return _readSuperInitializer(isSynthetic); | |
| // 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() { | |
| return new InvalidInitializer(); | |
| } | |
| Initializer _readFieldInitializer(bool isSynthetic) { | |
| int offset = readOffset(); | |
| Reference reference = readNonNullMemberReference(); | |
| Expression value = readExpression(); | |
| return new FieldInitializer.byReference(reference, value) | |
| ..isSynthetic = isSynthetic | |
| ..fileOffset = offset; | |
| } | |
| Initializer _readSuperInitializer(bool isSynthetic) { | |
| int offset = readOffset(); | |
| Reference reference = readNonNullMemberReference(); | |
| Arguments arguments = readArguments(); | |
| return new SuperInitializer.byReference(reference, arguments) | |
| ..isSynthetic = isSynthetic | |
| ..fileOffset = offset; | |
| } | |
| Initializer _readRedirectingInitializer() { | |
| int offset = readOffset(); | |
| return new RedirectingInitializer.byReference( | |
| readNonNullMemberReference(), readArguments()) | |
| ..fileOffset = offset; | |
| } | |
| Initializer _readLocalInitializer() { | |
| return new LocalInitializer(readAndPushVariableDeclaration()); | |
| } | |
| Initializer _readAssertInitializer() { | |
| return new AssertInitializer(readStatement() as AssertStatement); | |
| } | |
| FunctionNode readFunctionNode( | |
| {bool lazyLoadBody = false, int outerEndOffset = -1}) { | |
| int tag = readByte(); | |
| assert(tag == Tag.FunctionNode); | |
| int offset = readOffset(); | |
| int endOffset = readOffset(); | |
| AsyncMarker asyncMarker = AsyncMarker.values[readByte()]; | |
| AsyncMarker dartAsyncMarker = AsyncMarker.values[readByte()]; | |
| int typeParameterStackHeight = typeParameterStack.length; | |
| List<TypeParameter> typeParameters = readAndPushTypeParameterList(); | |
| readUInt30(); // total parameter count. | |
| int requiredParameterCount = readUInt30(); | |
| int variableStackHeight = variableStack.length; | |
| List<VariableDeclaration> positional = readAndPushVariableDeclarationList(); | |
| List<VariableDeclaration> named = readAndPushVariableDeclarationList(); | |
| DartType returnType = readDartType(); | |
| DartType? futureValueType = readDartTypeOption(); | |
| RedirectingFactoryTarget? redirectingFactoryTarget; | |
| if (readAndCheckOptionTag()) { | |
| Reference? targetReference = readNullableMemberReference(); | |
| List<DartType>? 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; | |
| if (!lazyLoadBody) { | |
| labelStackBase = labelStack.length; | |
| switchCaseStackBase = switchCaseStack.length; | |
| body = readStatementOption(); | |
| } | |
| FunctionNode result = new FunctionNode(body, | |
| typeParameters: typeParameters, | |
| requiredParameterCount: requiredParameterCount, | |
| positionalParameters: positional, | |
| namedParameters: named, | |
| returnType: returnType, | |
| asyncMarker: asyncMarker, | |
| dartAsyncMarker: dartAsyncMarker, | |
| emittedValueType: futureValueType) | |
| ..fileOffset = offset | |
| ..fileEndOffset = endOffset | |
| ..redirectingFactoryTarget = redirectingFactoryTarget; | |
| if (lazyLoadBody) { | |
| _setLazyLoadFunction(result, oldLabelStackBase, oldSwitchCaseStackBase, | |
| variableStackHeight); | |
| } | |
| labelStackBase = oldLabelStackBase; | |
| switchCaseStackBase = oldSwitchCaseStackBase; | |
| variableStack.length = variableStackHeight; | |
| typeParameterStack.length = typeParameterStackHeight; | |
| return result; | |
| } | |
| void _setLazyLoadFunction(FunctionNode result, int oldLabelStackBase, | |
| int oldSwitchCaseStackBase, int variableStackHeight) { | |
| final int savedByteOffset = _byteOffset; | |
| final int componentStartOffset = _componentStartOffset; | |
| final List<TypeParameter> typeParameters = | |
| typeParameterStack.cast<TypeParameter>().toList(); | |
| final List<VariableDeclaration> variables = variableStack.toList(); | |
| final Library currentLibrary = _currentLibrary!; | |
| result.lazyBuilder = () { | |
| _byteOffset = savedByteOffset; | |
| _currentLibrary = currentLibrary; | |
| typeParameterStack.clear(); | |
| typeParameterStack.addAll(typeParameters); | |
| variableStack.clear(); | |
| variableStack.addAll(variables); | |
| _componentStartOffset = componentStartOffset; | |
| result.body = readStatementOption(); | |
| result.body?.parent = result; | |
| labelStackBase = oldLabelStackBase; | |
| switchCaseStackBase = oldSwitchCaseStackBase; | |
| variableStack.length = variableStackHeight; | |
| typeParameterStack.clear(); | |
| TreeNode? parent = result.parent; | |
| if (parent is Procedure) { | |
| parent.transformerFlags |= getAndResetTransformerFlags(); | |
| } | |
| }; | |
| } | |
| void pushVariableDeclaration(VariableDeclaration variable) { | |
| variableStack.add(variable); | |
| } | |
| void pushVariableDeclarations(List<VariableDeclaration> variables) { | |
| variableStack.addAll(variables); | |
| } | |
| VariableDeclaration readVariableReference() { | |
| readUInt30(); // offset of the variable declaration in the binary. | |
| return _readVariableReferenceInternal(); | |
| } | |
| VariableDeclaration _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'); | |
| } | |
| } | |
| List<Expression> readExpressionList() { | |
| int length = readUInt30(); | |
| if (!useGrowableLists && length == 0) { | |
| // When lists don't have to be growable anyway, we might as well use a | |
| // constant one for the empty list. | |
| return emptyListOfExpression; | |
| } | |
| return new List<Expression>.generate(length, (_) => readExpression(), | |
| growable: useGrowableLists); | |
| } | |
| Expression? readExpressionOption() { | |
| return readAndCheckOptionTag() ? readExpression() : null; | |
| } | |
| Expression readExpression() { | |
| int tagByte = readByte(); | |
| int tag = tagByte & Tag.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(); | |
| 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( | |
| readStringOrNullIfEmpty(), 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(); | |
| addTransformerFlag(TransformerFlag.superCalls); | |
| return new AbstractSuperPropertyGet.byReference( | |
| readName(), readNonNullInstanceMemberReference()) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readAbstractSuperPropertySet() { | |
| int offset = readOffset(); | |
| addTransformerFlag(TransformerFlag.superCalls); | |
| return new AbstractSuperPropertySet.byReference( | |
| readName(), readExpression(), readNonNullInstanceMemberReference()) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readSuperPropertyGet() { | |
| int offset = readOffset(); | |
| addTransformerFlag(TransformerFlag.superCalls); | |
| return new SuperPropertyGet.byReference( | |
| readName(), readNonNullInstanceMemberReference()) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readSuperPropertySet() { | |
| int offset = readOffset(); | |
| addTransformerFlag(TransformerFlag.superCalls); | |
| return new SuperPropertySet.byReference( | |
| 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(); | |
| List<StructuralParameter> structuralParameters = | |
| readAndPushStructuralParameterList(); | |
| Expression expression = readExpression(); | |
| List<DartType> 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(); | |
| return new InstanceInvocation.byReference( | |
| kind, readExpression(), readName(), readArguments(), | |
| functionType: readDartType() as FunctionType, | |
| interfaceTargetReference: readNonNullInstanceMemberReference()) | |
| ..fileOffset = offset | |
| ..flags = flags; | |
| } | |
| Expression _readInstanceGetterInvocation() { | |
| InstanceAccessKind kind = InstanceAccessKind.values[readByte()]; | |
| int flags = readByte(); | |
| int offset = readOffset(); | |
| Expression receiver = readExpression(); | |
| Name name = readName(); | |
| Arguments arguments = readArguments(); | |
| DartType functionType = readDartType(); | |
| // `const DynamicType()` is used to encode a missing function type. | |
| assert(functionType is FunctionType || functionType is DynamicType, | |
| "Unexpected function type $functionType for InstanceGetterInvocation"); | |
| Reference interfaceTargetReference = readNonNullInstanceMemberReference(); | |
| return new InstanceGetterInvocation.byReference( | |
| kind, receiver, name, arguments, | |
| functionType: functionType is FunctionType ? functionType : null, | |
| interfaceTargetReference: interfaceTargetReference) | |
| ..fileOffset = offset | |
| ..flags = flags; | |
| } | |
| Expression _readDynamicInvocation() { | |
| DynamicAccessKind kind = DynamicAccessKind.values[readByte()]; | |
| int 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(); | |
| VariableDeclaration variable = readVariableReference(); | |
| return new LocalFunctionInvocation(variable, readArguments(), | |
| functionType: readDartType() as FunctionType) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readEqualsNull() { | |
| int offset = readOffset(); | |
| return new EqualsNull(readExpression())..fileOffset = offset; | |
| } | |
| Expression _readEqualsCall() { | |
| int offset = readOffset(); | |
| return new EqualsCall.byReference(readExpression(), readExpression(), | |
| functionType: readDartType() as FunctionType, | |
| interfaceTargetReference: readNonNullInstanceMemberReference()) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readAbstractSuperMethodInvocation() { | |
| int offset = readOffset(); | |
| addTransformerFlag(TransformerFlag.superCalls); | |
| return new AbstractSuperMethodInvocation.byReference( | |
| readName(), readArguments(), readNonNullInstanceMemberReference()) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readSuperMethodInvocation() { | |
| int offset = readOffset(); | |
| addTransformerFlag(TransformerFlag.superCalls); | |
| return new SuperMethodInvocation.byReference( | |
| 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 _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(); | |
| List<DartType> typeArguments = readDartTypeList(); | |
| int fieldValueCount = readUInt30(); | |
| Map<Reference, Expression> fieldValues = <Reference, Expression>{}; | |
| for (int i = 0; i < fieldValueCount; i++) { | |
| final Reference fieldRef = readNonNullCanonicalNameReference().reference; | |
| final Expression value = readExpression(); | |
| fieldValues[fieldRef] = value; | |
| } | |
| int assertCount = readUInt30(); | |
| List<AssertStatement> asserts; | |
| if (!useGrowableLists && assertCount == 0) { | |
| // When lists don't have to be growable anyway, we might as well use an | |
| // almost constant one for the empty list. | |
| asserts = emptyListOfAssertStatement; | |
| } else { | |
| asserts = new List<AssertStatement>.generate( | |
| assertCount, (_) => readStatement() as AssertStatement, | |
| growable: false); | |
| } | |
| List<Expression> unusedArguments = readExpressionList(); | |
| return new InstanceCreation( | |
| classReference, typeArguments, fieldValues, asserts, unusedArguments) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readFileUriExpression() { | |
| Uri fileUri = readUriReference(); | |
| int offset = readOffset(); | |
| return new FileUriExpression(readExpression(), fileUri) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readIsExpression() { | |
| int offset = readOffset(); | |
| 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(); | |
| List<Expression> positional = readExpressionList(); | |
| List<NamedExpression> named = readNamedExpressionList(); | |
| RecordType recordType = readDartType() as RecordType; | |
| return new RecordLiteral(positional, named, recordType, isConst: false) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readConstRecordLiteral() { | |
| int offset = readOffset(); | |
| List<Expression> positional = readExpressionList(); | |
| List<NamedExpression> 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(); | |
| return new FunctionExpression(readFunctionNode())..fileOffset = offset; | |
| } | |
| Expression _readLet() { | |
| int offset = readOffset(); | |
| VariableDeclaration variable = readVariableDeclaration(); | |
| int stackHeight = variableStack.length; | |
| pushVariableDeclaration(variable); | |
| Expression body = readExpression(); | |
| variableStack.length = stackHeight; | |
| return new Let(variable, body)..fileOffset = offset; | |
| } | |
| Expression _readBlockExpression() { | |
| int offset = readOffset(); | |
| int stackHeight = variableStack.length; | |
| List<Statement> statements = readStatementListAlwaysGrowable(); | |
| Expression value = readExpression(); | |
| variableStack.length = stackHeight; | |
| return new BlockExpression(new Block(statements), value) | |
| ..fileOffset = offset; | |
| } | |
| Expression _readInstantiation() { | |
| int offset = readOffset(); | |
| Expression expression = readExpression(); | |
| List<DartType> 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<VariableDeclaration> _readVariableReferenceList() { | |
| int length = readUInt30(); | |
| if (!useGrowableLists && length == 0) { | |
| // When lists don't have to be growable anyway, we might as well use an | |
| // almost constant one for the empty list. | |
| return emptyListOfVariableDeclaration; | |
| } | |
| return new List<VariableDeclaration>.generate( | |
| length, (_) => readVariableReference(), | |
| 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(); | |
| VariableDeclaration variable = readVariableReference(); | |
| DartType? matchedType = readDartTypeOption(); | |
| bool needsCheck = readByte() == 1; | |
| return AssignedVariablePattern(variable) | |
| ..fileOffset = fileOffset | |
| ..matchedValueType = matchedType | |
| ..needsCast = needsCheck; | |
| } | |
| CastPattern _readCastPattern() { | |
| int fileOffset = readOffset(); | |
| return new CastPattern(_readPattern(), readDartType()) | |
| ..fileOffset = fileOffset; | |
| } | |
| ConstantPattern _readConstantPattern() { | |
| int fileOffset = readOffset(); | |
| Expression expression = readExpression(); | |
| DartType? expressionType = readDartTypeOption(); | |
| Reference? equalsTargetReference = readNullableMemberReference(); | |
| FunctionType? equalsType = readDartTypeOption() as FunctionType?; | |
| return new ConstantPattern(expression) | |
| ..expressionType = expressionType | |
| ..equalsTargetReference = equalsTargetReference | |
| ..equalsType = equalsType | |
| ..fileOffset = fileOffset; | |
| } | |
| InvalidPattern _readInvalidPattern() { | |
| int fileOffset = readOffset(); | |
| Expression invalidExpression = readExpression(); | |
| List<VariableDeclaration> declaredVariables = | |
| readAndPushVariableDeclarationList(); | |
| return InvalidPattern(invalidExpression, | |
| declaredVariables: declaredVariables) | |
| ..fileOffset = fileOffset; | |
| } | |
| ListPattern _readListPattern() { | |
| int fileOffset = readOffset(); | |
| DartType? typeArgument = readDartTypeOption(); | |
| List<Pattern> patterns = _readPatternList(); | |
| DartType? requiredType = readDartTypeOption(); | |
| DartType? matchedValueType = readDartTypeOption(); | |
| int flags = readByte(); | |
| DartType? lookupType = readDartTypeOption(); | |
| Reference? lengthTargetReference = readNullableMemberReference(); | |
| DartType? lengthType = readDartTypeOption(); | |
| Reference? lengthCheckTargetReference = readNullableMemberReference(); | |
| FunctionType? lengthCheckType = readDartTypeOption() as FunctionType?; | |
| Reference? sublistTargetReference = readNullableMemberReference(); | |
| FunctionType? sublistType = readDartTypeOption() as FunctionType?; | |
| Reference? minusTargetReference = readNullableMemberReference(); | |
| FunctionType? minusType = readDartTypeOption() as FunctionType?; | |
| Reference? indexGetTargetReference = readNullableMemberReference(); | |
| FunctionType? indexGetType = readDartTypeOption() as FunctionType?; | |
| return new ListPattern(typeArgument, patterns) | |
| ..requiredType = requiredType | |
| ..matchedValueType = matchedValueType | |
| ..flags = flags | |
| ..lookupType = lookupType | |
| ..lengthTargetReference = lengthTargetReference | |
| ..lengthType = lengthType | |
| ..lengthCheckTargetReference = lengthCheckTargetReference | |
| ..lengthCheckType = lengthCheckType | |
| ..sublistTargetReference = sublistTargetReference | |
| ..sublistType = sublistType | |
| ..minusTargetReference = minusTargetReference | |
| ..minusType = minusType | |
| ..indexGetTargetReference = indexGetTargetReference | |
| ..indexGetType = indexGetType | |
| ..fileOffset = fileOffset; | |
| } | |
| MapPattern _readMapPattern() { | |
| int fileOffset = readOffset(); | |
| DartType? keyType = readDartTypeOption(); | |
| DartType? valueType = readDartTypeOption(); | |
| List<MapPatternEntry> entries = _readMapPatternEntryList(); | |
| DartType? requiredType = readDartTypeOption(); | |
| DartType? matchedValueType = readDartTypeOption(); | |
| int flags = readByte(); | |
| DartType? lookupType = readDartTypeOption(); | |
| Reference? containsKeyTargetReference = readNullableMemberReference(); | |
| FunctionType? containsKeyType = readDartTypeOption() as FunctionType?; | |
| Reference? indexGetTargetReference = readNullableMemberReference(); | |
| FunctionType? indexGetType = readDartTypeOption() as FunctionType?; | |
| return new MapPattern(keyType, valueType, entries) | |
| ..requiredType = requiredType | |
| ..matchedValueType = matchedValueType | |
| ..flags = flags | |
| ..lookupType = lookupType | |
| ..containsKeyTargetReference = containsKeyTargetReference | |
| ..containsKeyType = containsKeyType | |
| ..indexGetTargetReference = indexGetTargetReference | |
| ..indexGetType = indexGetType | |
| ..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(); | |
| FunctionType? functionType = readDartTypeOption() as FunctionType?; | |
| List<DartType>? typeArguments; | |
| if (readAndCheckOptionTag()) { | |
| typeArguments = readDartTypeList(); | |
| } | |
| return new NamedPattern(name, pattern) | |
| ..fieldName = fieldName | |
| ..accessKind = accessKind | |
| ..targetReference = targetReference | |
| ..resultType = resultType | |
| ..recordType = recordType | |
| ..recordFieldIndex = recordFieldIndex | |
| ..functionType = functionType | |
| ..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 type = readDartType(); | |
| List<NamedPattern> fields = _readNamedPatternList(); | |
| DartType? matchedType = readDartTypeOption(); | |
| bool needsCheck = readByte() == 1; | |
| DartType? objectType = readDartTypeOption(); | |
| return new ObjectPattern(type, fields) | |
| ..matchedValueType = matchedType | |
| ..needsCheck = needsCheck | |
| ..lookupType = objectType | |
| ..fileOffset = fileOffset; | |
| } | |
| OrPattern _readOrPattern() { | |
| int fileOffset = readOffset(); | |
| Pattern left = _readPattern(); | |
| Pattern right = _readPattern(); | |
| List<VariableDeclaration> orPatternJointVariables = | |
| _readVariableReferenceList(); | |
| return new OrPattern(left, right, | |
| orPatternJointVariables: orPatternJointVariables) | |
| ..fileOffset = fileOffset; | |
| } | |
| RecordPattern _readRecordPattern() { | |
| int fileOffset = readOffset(); | |
| List<Pattern> patterns = _readPatternList(); | |
| RecordType? type = readDartTypeOption() as RecordType?; | |
| DartType? matchedType = readDartTypeOption(); | |
| bool needsCheck = readByte() == 1; | |
| RecordType? recordType = readDartTypeOption() as RecordType?; | |
| return new RecordPattern(patterns) | |
| ..requiredType = type | |
| ..matchedValueType = matchedType | |
| ..needsCheck = needsCheck | |
| ..lookupType = recordType | |
| ..fileOffset = fileOffset; | |
| } | |
| RelationalPattern _readRelationalPattern() { | |
| int fileOffset = readOffset(); | |
| RelationalPatternKind kind = RelationalPatternKind.values[readByte()]; | |
| Expression expression = readExpression(); | |
| DartType? expressionType = readDartTypeOption(); | |
| DartType? matchedType = readDartTypeOption(); | |
| RelationalAccessKind accessKind = RelationalAccessKind.values[readByte()]; | |
| Name name = readName(); | |
| Reference? targetReference = readNullableMemberReference(); | |
| List<DartType>? typeArguments; | |
| if (readAndCheckOptionTag()) { | |
| typeArguments = readDartTypeList(); | |
| } | |
| FunctionType? functionType = readDartTypeOption() as FunctionType?; | |
| return new RelationalPattern(kind, expression) | |
| ..expressionType = expressionType | |
| ..matchedValueType = matchedType | |
| ..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(); | |
| VariableDeclaration variable = readVariableDeclaration(); | |
| DartType? matchedType = readDartTypeOption(); | |
| return new VariablePattern(type, variable) | |
| ..matchedValueType = matchedType | |
| ..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 = readDartTypeOption(); | |
| return new IfCaseStatement(expression, patternGuard, then, otherwise) | |
| ..matchedValueType = matchedValueType | |
| ..fileOffset = fileOffset; | |
| } | |
| PatternAssignment _readPatternAssignment() { | |
| int fileOffset = readOffset(); | |
| Pattern pattern = _readPattern(); | |
| Expression expression = readExpression(); | |
| DartType? matchedValueType = readDartTypeOption(); | |
| return new PatternAssignment(pattern, expression) | |
| ..matchedValueType = matchedValueType | |
| ..fileOffset = fileOffset; | |
| } | |
| PatternVariableDeclaration _readPatternVariableDeclaration() { | |
| int fileOffset = readOffset(); | |
| Pattern pattern = _readPattern(); | |
| Expression expression = readExpression(); | |
| bool isFinal = readByte() == 1; | |
| DartType? matchedValueType = readDartTypeOption(); | |
| 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, | |
| jointVariables: [], | |
| jointVariableFirstUseOffsets: 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.jointVariables.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.VariableDeclaration: | |
| return _readVariableDeclaration(); | |
| // 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(); | |
| return new WhileStatement(readExpression(), readStatement()) | |
| ..fileOffset = offset; | |
| } | |
| Statement _readDoStatement() { | |
| int offset = readOffset(); | |
| return new DoStatement(readStatement(), readExpression()) | |
| ..fileOffset = offset; | |
| } | |
| Statement _readForStatement() { | |
| int variableStackHeight = variableStack.length; | |
| int offset = readOffset(); | |
| List<VariableDeclaration> variables = readAndPushVariableDeclarationList(); | |
| Expression? condition = readExpressionOption(); | |
| List<Expression> updates = readExpressionList(); | |
| Statement body = readStatement(); | |
| variableStack.length = variableStackHeight; | |
| return new ForStatement(variables, condition, updates, body) | |
| ..fileOffset = offset; | |
| } | |
| Statement _readForInStatement(int tag) { | |
| bool isAsync = tag == Tag.AsyncForInStatement; | |
| int variableStackHeight = variableStack.length; | |
| int offset = readOffset(); | |
| int bodyOffset = readOffset(); | |
| VariableDeclaration variable = readAndPushVariableDeclaration(); | |
| Expression iterable = readExpression(); | |
| Statement body = readStatement(); | |
| variableStack.length = variableStackHeight; | |
| return new ForInStatement(variable, iterable, body, isAsync: isAsync) | |
| ..fileOffset = offset | |
| ..bodyOffset = bodyOffset; | |
| } | |
| Statement _readSwitchStatement() { | |
| int offset = readOffset(); | |
| 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(<Expression>[], <int>[], dummyStatement, | |
| isDefault: false), | |
| growable: useGrowableLists); | |
| } | |
| switchCaseStack.addAll(cases); | |
| for (int i = 0; i < cases.length; ++i) { | |
| _readSwitchCaseInto(cases[i]); | |
| } | |
| switchCaseStack.length -= count; | |
| return new SwitchStatement(expression, cases, | |
| 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; | |
| } | |
| Statement _readVariableDeclaration() { | |
| VariableDeclaration variable = readVariableDeclaration(); | |
| variableStack.add(variable); // Will be popped by the enclosing scope. | |
| return variable; | |
| } | |
| Statement _readFunctionDeclaration() { | |
| int offset = readOffset(); | |
| VariableDeclaration variable = readVariableDeclaration(); | |
| variableStack.add(variable); // Will be popped by the enclosing scope. | |
| return new FunctionDeclaration(variable, readFunctionNode()) | |
| ..fileOffset = offset; | |
| } | |
| void _readSwitchCaseInto(SwitchCase caseNode) { | |
| int offset = readOffset(); | |
| caseNode.fileOffset = offset; | |
| int length = readUInt30(); | |
| for (int i = 0; i < length; ++i) { | |
| caseNode.expressionOffsets.add(readOffset()); | |
| caseNode.expressions.add(readExpression()..parent = caseNode); | |
| } | |
| caseNode.isDefault = readByte() == 1; | |
| caseNode.body = readStatement()..parent = caseNode; | |
| } | |
| List<Catch> readCatchList() { | |
| int length = readUInt30(); | |
| if (!useGrowableLists && length == 0) { | |
| // When lists don't have to be growable anyway, we might as well use an | |
| // almost constant one for the empty list. | |
| return emptyListOfCatch; | |
| } | |
| return new List<Catch>.generate(length, (_) => readCatch(), | |
| growable: useGrowableLists); | |
| } | |
| Catch readCatch() { | |
| int variableStackHeight = variableStack.length; | |
| int offset = readOffset(); | |
| DartType guard = readDartType(); | |
| VariableDeclaration? exception = readAndPushVariableDeclarationOption(); | |
| VariableDeclaration? stackTrace = readAndPushVariableDeclarationOption(); | |
| Statement body = readStatement(); | |
| variableStack.length = variableStackHeight; | |
| return new Catch(exception, body, guard: guard, stackTrace: stackTrace) | |
| ..fileOffset = offset; | |
| } | |
| Block _readBlock() { | |
| int stackHeight = variableStack.length; | |
| int offset = readOffset(); | |
| int endOffset = readOffset(); | |
| List<Statement> body = readStatementListAlwaysGrowable(); | |
| variableStack.length = stackHeight; | |
| return new Block(body) | |
| ..fileOffset = offset | |
| ..fileEndOffset = endOffset; | |
| } | |
| AssertBlock _readAssertBlock() { | |
| int stackHeight = variableStack.length; | |
| List<Statement> body = readStatementListAlwaysGrowable(); | |
| variableStack.length = stackHeight; | |
| return new AssertBlock(body); | |
| } | |
| Supertype readSupertype() { | |
| InterfaceType type = readDartType(forSupertype: true) as InterfaceType; | |
| assert( | |
| type.nullability == _currentLibrary!.nonNullable, | |
| "In serialized form supertypes should have Nullability.legacy if they " | |
| "are in a library that is opted out of the NNBD feature. If they are " | |
| "in an opted-in library, they should have Nullability.nonNullable."); | |
| return new Supertype.byReference(type.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); | |
| } | |
| } | |
| List<DartType> readDartTypeList() { | |
| int length = readUInt30(); | |
| if (!useGrowableLists && length == 0) { | |
| // When lists don't have to be growable anyway, we might as well use an | |
| // almost constant one for the empty list. | |
| return emptyListOfDartType; | |
| } | |
| return new List<DartType>.generate(length, (_) => readDartType(), | |
| growable: useGrowableLists); | |
| } | |
| List<NamedType> readNamedTypeList() { | |
| int length = readUInt30(); | |
| if (!useGrowableLists && length == 0) { | |
| // When lists don't have to be growable anyway, we might as well use a | |
| // constant one for the empty list. | |
| return emptyListOfNamedType; | |
| } | |
| return new List<NamedType>.generate(length, (_) => readNamedType(), | |
| growable: useGrowableLists); | |
| } | |
| NamedType readNamedType() { | |
| String name = readStringReference(); | |
| DartType type = readDartType(); | |
| int flags = readByte(); | |
| return new NamedType(name, type, | |
| isRequired: (flags & NamedType.FlagRequiredNamedType) != 0); | |
| } | |
| DartType? readDartTypeOption() { | |
| return readAndCheckOptionTag() ? readDartType() : null; | |
| } | |
| DartType readDartType({bool forSupertype = false}) { | |
| int tag = readByte(); | |
| switch (tag) { | |
| // 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(); | |
| List<DartType> 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], const <DartType>[]); | |
| _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(); | |
| List<DartType> typeArguments = readDartTypeList(); | |
| readDartType(); // Read type erasure. | |
| return new ExtensionType.byReference( | |
| reference, Nullability.values[nullabilityIndex], typeArguments); | |
| } | |
| DartType _readFunctionType() { | |
| int typeParameterStackHeight = typeParameterStack.length; | |
| int nullabilityIndex = readByte(); | |
| List<StructuralParameter> typeParameters = | |
| readAndPushStructuralParameterList(); | |
| int requiredParameterCount = readUInt30(); | |
| int totalParameterCount = readUInt30(); | |
| List<DartType> positional = readDartTypeList(); | |
| List<NamedType> 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(); | |
| List<DartType> 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( | |
| const [], 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(); | |
| List<DartType> positional = readDartTypeList(); | |
| List<NamedType> named = readNamedTypeList(); | |
| return new RecordType( | |
| positional, named, Nullability.values[nullabilityIndex]); | |
| } | |
| List<TypeParameter> readAndPushTypeParameterList( | |
| [List<TypeParameter>? list, GenericDeclaration? declaration]) { | |
| int length = readUInt30(); | |
| if (length == 0) { | |
| if (list != null) return list; | |
| if (useGrowableLists) { | |
| return <TypeParameter>[]; | |
| } else { | |
| return emptyListOfTypeParameter; | |
| } | |
| } | |
| if (list == null) { | |
| list = new List<TypeParameter>.generate(length, | |
| (_) => new TypeParameter(null, null)..declaration = declaration, | |
| growable: useGrowableLists); | |
| } else if (list.length != length) { | |
| for (int i = 0; i < length; ++i) { | |
| list.add(new TypeParameter(null, null)..declaration = declaration); | |
| } | |
| } | |
| typeParameterStack.addAll(list); | |
| for (int i = 0; i < list.length; ++i) { | |
| readTypeParameter(list[i]); | |
| } | |
| return list; | |
| } | |
| List<StructuralParameter> readAndPushStructuralParameterList( | |
| [List<StructuralParameter>? list]) { | |
| int length = readUInt30(); | |
| if (length == 0) { | |
| if (list != null) return list; | |
| if (useGrowableLists) { | |
| return <StructuralParameter>[]; | |
| } else { | |
| return emptyListOfStructuralParameter; | |
| } | |
| } | |
| if (list == null) { | |
| list = new List<StructuralParameter>.generate( | |
| length, (_) => new StructuralParameter(null, null), | |
| growable: useGrowableLists); | |
| } else if (list.length != length) { | |
| for (int i = 0; i < length; ++i) { | |
| list.add(new 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(); | |
| List<DartType> typeArguments = readDartTypeList(); | |
| List<Expression> positional = readExpressionList(); | |
| List<NamedExpression> named = readNamedExpressionList(); | |
| assert(numArguments == positional.length + named.length); | |
| return new Arguments(positional, types: typeArguments, named: named); | |
| } | |
| List<NamedExpression> readNamedExpressionList() { | |
| int length = readUInt30(); | |
| if (!useGrowableLists && length == 0) { | |
| // When lists don't have to be growable anyway, we might as well use an | |
| // almost-constant one for the empty list. | |
| return emptyListOfNamedExpression; | |
| } | |
| return new List<NamedExpression>.generate( | |
| length, (_) => readNamedExpression(), | |
| growable: useGrowableLists); | |
| } | |
| NamedExpression readNamedExpression() { | |
| return new NamedExpression(readStringReference(), readExpression()); | |
| } | |
| List<VariableDeclaration> readAndPushVariableDeclarationList() { | |
| int length = readUInt30(); | |
| if (!useGrowableLists && length == 0) { | |
| // When lists don't have to be growable anyway, we might as well use an | |
| // almost constant one for the empty list. | |
| return emptyListOfVariableDeclaration; | |
| } | |
| return new List<VariableDeclaration>.generate( | |
| length, (_) => readAndPushVariableDeclaration(), | |
| growable: useGrowableLists); | |
| } | |
| VariableDeclaration? readAndPushVariableDeclarationOption() { | |
| return readAndCheckOptionTag() ? readAndPushVariableDeclaration() : null; | |
| } | |
| VariableDeclaration readAndPushVariableDeclaration() { | |
| VariableDeclaration variable = readVariableDeclaration(); | |
| variableStack.add(variable); | |
| return variable; | |
| } | |
| VariableDeclaration readVariableDeclaration() { | |
| int offset = readOffset(); | |
| int fileEqualsOffset = readOffset(); | |
| // The [VariableDeclaration] instance is not created at this point yet, | |
| // so `null` is temporarily set as the parent of the annotation nodes. | |
| List<Expression> annotations = readAnnotationList(null); | |
| int flags = readUInt30(); | |
| VariableDeclaration node = new VariableDeclaration( | |
| readStringOrNullIfEmpty(), | |
| type: readDartType(), | |
| initializer: readExpressionOption(), | |
| flags: flags) | |
| ..fileOffset = offset | |
| ..fileEqualsOffset = fileEqualsOffset; | |
| if (annotations.isNotEmpty) { | |
| for (int i = 0; i < annotations.length; ++i) { | |
| Expression annotation = annotations[i]; | |
| annotation.parent = node; | |
| } | |
| node.annotations = annotations; | |
| } | |
| return node; | |
| } | |
| int readOffset() { | |
| // Offset is saved as unsigned, | |
| // but actually ranges from -1 and up (thus the -1) | |
| return readUInt30() - 1; | |
| } | |
| } | |
| class BinaryBuilderWithMetadata extends BinaryBuilder implements BinarySource { | |
| /// List of metadata subsections that have corresponding [MetadataRepository] | |
| /// and are awaiting to be parsed and attached to nodes. | |
| List<_MetadataSubsection>? _subsections; | |
| List<int>? _allKnownMetadataKeys; | |
| int? _previousMetadataLookupKey; | |
| BinaryBuilderWithMetadata(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 | |
| FunctionNode readFunctionNode( | |
| {bool lazyLoadBody = false, int outerEndOffset = -1}) { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final FunctionNode result = super.readFunctionNode( | |
| lazyLoadBody: lazyLoadBody, outerEndOffset: outerEndOffset); | |
| return hasMetadata ? _associateMetadata(result, nodeOffset) : 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 | |
| VariableDeclaration readVariableDeclaration() { | |
| final int nodeOffset = _byteOffset; | |
| final bool hasMetadata = _hasMetadata(_byteOffset); | |
| final VariableDeclaration result = super.readVariableDeclaration(); | |
| 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 | |
| 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 { | |
| /// [MetadataRepository] that can read this subsection. | |
| final MetadataRepository repository; | |
| /// Deserialized mapping from node offsets to metadata offsets. | |
| final Map<int, int> mapping; | |
| _MetadataSubsection(this.repository, this.mapping); | |
| } | |
| /// Merges two compilation modes or throws if they are not compatible. | |
| NonNullableByDefaultCompiledMode mergeCompilationModeOrThrow( | |
| NonNullableByDefaultCompiledMode? a, NonNullableByDefaultCompiledMode b) { | |
| if (a == null || a == b) { | |
| return b; | |
| } | |
| // If something is invalid, it should always merge as invalid. | |
| if (a == NonNullableByDefaultCompiledMode.Invalid) { | |
| return a; | |
| } | |
| if (b == NonNullableByDefaultCompiledMode.Invalid) { | |
| return b; | |
| } | |
| throw new CompilationModeError("Mixed compilation mode found: $a and $b"); | |
| } |