| // 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 { | 
 |   final int binaryOffsetForSourceTable; | 
 |   final int binaryOffsetForCanonicalNames; | 
 |   final int binaryOffsetForMetadataPayloads; | 
 |   final int binaryOffsetForMetadataMappings; | 
 |   final int binaryOffsetForStringTable; | 
 |   final int binaryOffsetForConstantTable; | 
 |   final int binaryOffsetForConstantTableIndex; | 
 |   final int binaryOffsetForStartOfComponentIndex; | 
 |   final int mainMethodReference; | 
 |   final List<int> libraryOffsets; | 
 |   final int libraryCount; | 
 |   final int componentFileSizeInBytes; | 
 |  | 
 |   _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.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; | 
 |  | 
 |   // 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 -= numberOfFixedFields(libraryCount) * 4; | 
 |  | 
 |     // Now read the component index. | 
 |     int binaryOffsetForSourceTable = _componentStartOffset + readUint32(); | 
 |     int binaryOffsetForConstantTable = _componentStartOffset + readUint32(); | 
 |     int binaryOffsetForConstantTableIndex = | 
 |         _componentStartOffset + readUint32(); | 
 |     int binaryOffsetForCanonicalNames = _componentStartOffset + readUint32(); | 
 |     int binaryOffsetForMetadataPayloads = _componentStartOffset + readUint32(); | 
 |     int binaryOffsetForMetadataMappings = _componentStartOffset + readUint32(); | 
 |     int binaryOffsetForStringTable = _componentStartOffset + readUint32(); | 
 |     int binaryOffsetForStartOfComponentIndex = | 
 |         _componentStartOffset + readUint32(); | 
 |     int mainMethodReference = readUint32(); | 
 |     for (int i = 0; i < libraryCount + 1; ++i) { | 
 |       libraryOffsets[i] = _componentStartOffset + readUint32(); | 
 |     } | 
 |  | 
 |     _byteOffset = savedByteIndex; | 
 |  | 
 |     return new _ComponentIndex( | 
 |         libraryCount: libraryCount, | 
 |         libraryOffsets: libraryOffsets, | 
 |         componentFileSizeInBytes: componentFileSizeInBytes, | 
 |         binaryOffsetForSourceTable: binaryOffsetForSourceTable, | 
 |         binaryOffsetForCanonicalNames: binaryOffsetForCanonicalNames, | 
 |         binaryOffsetForMetadataPayloads: binaryOffsetForMetadataPayloads, | 
 |         binaryOffsetForMetadataMappings: binaryOffsetForMetadataMappings, | 
 |         binaryOffsetForStringTable: binaryOffsetForStringTable, | 
 |         binaryOffsetForConstantTable: binaryOffsetForConstantTable, | 
 |         binaryOffsetForConstantTableIndex: binaryOffsetForConstantTableIndex, | 
 |         binaryOffsetForStartOfComponentIndex: | 
 |             binaryOffsetForStartOfComponentIndex, | 
 |         mainMethodReference: mainMethodReference); | 
 |   } | 
 |  | 
 |   void _readOneComponentSource(Component component, int componentFileSize) { | 
 |     _componentStartOffset = _byteOffset; | 
 |     _verifyComponentInitialBytes(resetOffset: false); | 
 |  | 
 |     // Read component index from the end of this ComponentFiles serialized data. | 
 |     _ComponentIndex index = _readComponentIndex(componentFileSize); | 
 |  | 
 |     _byteOffset = index.binaryOffsetForSourceTable; | 
 |     Map<Uri, Source> uriToSource = readUriToSource(readCoverage: false); | 
 |     _mergeUriToSource(component.uriToSource, uriToSource); | 
 |  | 
 |     _byteOffset = _componentStartOffset + componentFileSize; | 
 |   } | 
 |  | 
 |   /// Verify the initial bytes could correspond to a valid component. | 
 |   /// | 
 |   /// * Checks we have non-empty input. | 
 |   /// * Verifies that the magic number is correct. | 
 |   /// * Verifies the binary format version. | 
 |   /// * Verifies the sdk hash. | 
 |   /// | 
 |   /// If [resetOffset] is true the [_byteOffset] will be reset to match what it | 
 |   /// was before this method was called. If false it will be so we read passed | 
 |   /// the sdk hash. | 
 |   void _verifyComponentInitialBytes({required bool resetOffset}) { | 
 |     // Check that we have a .dill file and it has the correct version before | 
 |     // we start decoding it.  Otherwise we will fail for cryptic reasons. | 
 |     _checkEmptyInput(); | 
 |     int offset = _byteOffset; | 
 |     int magic = readUint32(); | 
 |     if (magic != Tag.ComponentFile) { | 
 |       throw ArgumentError('Not a .dill file (wrong magic number).'); | 
 |     } | 
 |     int version = readUint32(); | 
 |     if (version != Tag.BinaryFormatVersion) { | 
 |       throw InvalidKernelVersionError(filename, version); | 
 |     } | 
 |  | 
 |     _readAndVerifySdkHash(); | 
 |  | 
 |     if (resetOffset) { | 
 |       _byteOffset = offset; | 
 |     } | 
 |   } | 
 |  | 
 |   SubComponentView? _readOneComponent( | 
 |       Component component, int componentFileSize, | 
 |       {bool createView = false}) { | 
 |     _componentStartOffset = _byteOffset; | 
 |     _verifyComponentInitialBytes(resetOffset: false); | 
 |  | 
 |     List<String>? problemsAsJson = readListOfStrings(); | 
 |     if (problemsAsJson != null) { | 
 |       component.problemsAsJson ??= <String>[]; | 
 |       component.problemsAsJson!.addAll(problemsAsJson); | 
 |     } | 
 |  | 
 |     // Read component index from the end of this ComponentFiles serialized data. | 
 |     _ComponentIndex index = _readComponentIndex(componentFileSize); | 
 |  | 
 |     _byteOffset = index.binaryOffsetForStringTable; | 
 |     readStringTable(); | 
 |  | 
 |     _byteOffset = index.binaryOffsetForCanonicalNames; | 
 |     readLinkTable(component.root); | 
 |  | 
 |     _byteOffset = index.binaryOffsetForSourceTable; | 
 |     Map<Uri, Source> uriToSource = readUriToSource(readCoverage: true); | 
 |     _mergeUriToSource(component.uriToSource, uriToSource); | 
 |  | 
 |     _byteOffset = index.binaryOffsetForConstantTable; | 
 |     readConstantTable(); | 
 |     // We don't need the constant table index on the dart side. | 
 |  | 
 |     // TODO(alexmarkov): reverse metadata mappings and read forwards | 
 |     // Ensure constant table is loaded before metadata is read as it may contain | 
 |     // references to the constant table. | 
 |     _byteOffset = index.binaryOffsetForStringTable; // Read backwards. | 
 |     _readMetadataMappings(component, index.binaryOffsetForMetadataPayloads); | 
 |  | 
 |     _associateMetadata(component, _componentStartOffset); | 
 |  | 
 |     int numberOfLibraries = index.libraryCount; | 
 |  | 
 |     SubComponentView? result; | 
 |     if (createView) { | 
 |       result = new SubComponentView( | 
 |           new List<Library>.generate(numberOfLibraries, (int i) { | 
 |             _byteOffset = index.libraryOffsets[i]; | 
 |             return readLibrary(component, index.libraryOffsets[i + 1]); | 
 |           }, growable: false), | 
 |           _componentStartOffset, | 
 |           componentFileSize); | 
 |     } else { | 
 |       for (int i = 0; i < numberOfLibraries; ++i) { | 
 |         _byteOffset = index.libraryOffsets[i]; | 
 |         readLibrary(component, index.libraryOffsets[i + 1]); | 
 |       } | 
 |     } | 
 |  | 
 |     Reference? mainMethod = | 
 |         getNullableMemberReferenceFromInt(index.mainMethodReference); | 
 |     component.setMainMethodAndMode(mainMethod, false); | 
 |  | 
 |     _byteOffset = _componentStartOffset + componentFileSize; | 
 |  | 
 |     assert(typeParameterStack.isEmpty); | 
 |  | 
 |     return result; | 
 |   } | 
 |  | 
 |   /// Read a list of strings. If the list is empty, [null] is returned. | 
 |   List<String>? readListOfStrings() { | 
 |     int length = readUInt30(); | 
 |     if (length == 0) return null; | 
 |     return new List<String>.generate(length, (_) => readString(), | 
 |         growable: useGrowableLists); | 
 |   } | 
 |  | 
 |   /// Read the uri-to-source part of the binary. | 
 |   /// Note that this can include coverage, but that it is only included if | 
 |   /// [readCoverage] is true, otherwise coverage will be skipped. Note also that | 
 |   /// if [readCoverage] is true, references are read and that the link table | 
 |   /// thus has to be read first. | 
 |   Map<Uri, Source> readUriToSource({required bool readCoverage}) { | 
 |     assert(!readCoverage || (readCoverage && _lateIsInitialized(_linkTable))); | 
 |  | 
 |     int length = readUint32(); | 
 |  | 
 |     // Read data. | 
 |     _sourceUriTable = new List<Uri>.filled(length, dummyUri, growable: false); | 
 |     Map<Uri, Source> uriToSource = <Uri, Source>{}; | 
 |     for (int i = 0; i < length; ++i) { | 
 |       String uriString = readString(); | 
 |       Uri uri = Uri.parse(uriString); | 
 |       _sourceUriTable[i] = uri; | 
 |       Uint8List sourceCode = readOrViewByteList(); | 
 |       int lineCount = readUInt30(); | 
 |       List<int> lineStarts = new List<int>.filled( | 
 |           lineCount, | 
 |           // Use `-1` as a dummy default value. | 
 |           -1, | 
 |           growable: false); | 
 |       int previousLineStart = 0; | 
 |       for (int j = 0; j < lineCount; ++j) { | 
 |         int lineStart = readUInt30() + previousLineStart; | 
 |         lineStarts[j] = lineStart; | 
 |         previousLineStart = lineStart; | 
 |       } | 
 |       String importUriString = readString(); | 
 |       Uri importUri = Uri.parse(importUriString); | 
 |  | 
 |       Set<Reference>? coverageConstructors; | 
 |       { | 
 |         int constructorCoverageCount = readUInt30(); | 
 |         if (constructorCoverageCount > 0) { | 
 |           if (readCoverage) { | 
 |             coverageConstructors = new Set<Reference>(); | 
 |             for (int j = 0; j < constructorCoverageCount; ++j) { | 
 |               coverageConstructors.add(readNonNullMemberReference()); | 
 |             } | 
 |           } else { | 
 |             for (int j = 0; j < constructorCoverageCount; ++j) { | 
 |               skipMemberReference(); | 
 |             } | 
 |           } | 
 |         } | 
 |       } | 
 |  | 
 |       uriToSource[uri] = new Source(lineStarts, sourceCode, importUri, uri) | 
 |         ..constantCoverageConstructors = coverageConstructors; | 
 |     } | 
 |  | 
 |     // Read index. | 
 |     for (int i = 0; i < length; ++i) { | 
 |       readUint32(); | 
 |     } | 
 |     return uriToSource; | 
 |   } | 
 |  | 
 |   // Add everything from [src] into [dst], but don't overwrite a non-empty | 
 |   // source with an empty source. Empty sources may be introduced by | 
 |   // synthetic, copy-down implementations such as mixin applications or | 
 |   // noSuchMethod forwarders. | 
 |   void _mergeUriToSource(Map<Uri, Source> dst, Map<Uri, Source> src) { | 
 |     if (dst.isEmpty) { | 
 |       // Fast path for the common case of one component per binary. | 
 |       dst.addAll(src); | 
 |     } else { | 
 |       src.forEach((Uri key, Source value) { | 
 |         Source? originalDestinationSource = dst[key]; | 
 |         Source? mergeFrom; | 
 |         Source mergeTo; | 
 |         if (value.source.isNotEmpty || originalDestinationSource == null) { | 
 |           dst[key] = value; | 
 |           mergeFrom = originalDestinationSource; | 
 |           mergeTo = value; | 
 |         } else { | 
 |           mergeFrom = value; | 
 |           mergeTo = originalDestinationSource; | 
 |         } | 
 |  | 
 |         // TODO(jensj): Find out what the right thing to do is --- it probably | 
 |         // depends on what we do if read the same library twice - do we merge or | 
 |         // do we overwrite, and should we even support such a thing? | 
 |  | 
 |         // Merge coverage. Note that mergeFrom might be null. | 
 |         if (mergeTo.constantCoverageConstructors == null) { | 
 |           mergeTo.constantCoverageConstructors = | 
 |               mergeFrom?.constantCoverageConstructors; | 
 |         } else if (mergeFrom?.constantCoverageConstructors == null) { | 
 |           // Nothing to do. | 
 |         } else { | 
 |           // Both are non-null: Merge. | 
 |           mergeTo.constantCoverageConstructors! | 
 |               .addAll(mergeFrom!.constantCoverageConstructors!); | 
 |         } | 
 |       }); | 
 |     } | 
 |   } | 
 |  | 
 |   void skipCanonicalNameReference() { | 
 |     readUInt30(); | 
 |   } | 
 |  | 
 |   CanonicalName? readNullableCanonicalNameReference() { | 
 |     int index = readUInt30(); | 
 |     if (index == 0) return null; | 
 |     return _linkTable[index - 1]; | 
 |   } | 
 |  | 
 |   CanonicalName readNonNullCanonicalNameReference() { | 
 |     CanonicalName? canonicalName = readNullableCanonicalNameReference(); | 
 |     if (canonicalName == null) { | 
 |       throw new StateError('No canonical name found.'); | 
 |     } | 
 |     return canonicalName; | 
 |   } | 
 |  | 
 |   CanonicalName? getNullableCanonicalNameReferenceFromInt(int index) { | 
 |     if (index == 0) return null; | 
 |     return _linkTable[index - 1]; | 
 |   } | 
 |  | 
 |   Reference? readNullableLibraryReference() { | 
 |     CanonicalName? canonicalName = readNullableCanonicalNameReference(); | 
 |     return canonicalName?.reference; | 
 |   } | 
 |  | 
 |   Reference readNonNullLibraryReference() { | 
 |     CanonicalName? canonicalName = readNullableCanonicalNameReference(); | 
 |     if (canonicalName != null) return canonicalName.reference; | 
 |     throw 'Expected a library reference to be valid but was `null`.'; | 
 |   } | 
 |  | 
 |   LibraryDependency readLibraryDependencyReference() { | 
 |     int index = readUInt30(); | 
 |     return _currentLibrary!.dependencies[index]; | 
 |   } | 
 |  | 
 |   Reference readNonNullClassReference() { | 
 |     CanonicalName? name = readNullableCanonicalNameReference(); | 
 |     if (name == null) { | 
 |       throw 'Expected a class reference to be valid but was `null`.'; | 
 |     } | 
 |     return name.reference; | 
 |   } | 
 |  | 
 |   Reference readNonNullExtensionTypeDeclarationReference() { | 
 |     CanonicalName? name = readNullableCanonicalNameReference(); | 
 |     if (name == null) { | 
 |       throw 'Expected an extension type declaration reference to be valid but ' | 
 |           'was `null`.'; | 
 |     } | 
 |     return name.reference; | 
 |   } | 
 |  | 
 |   void skipMemberReference() { | 
 |     skipCanonicalNameReference(); | 
 |   } | 
 |  | 
 |   Reference? readNullableMemberReference() { | 
 |     CanonicalName? name = readNullableCanonicalNameReference(); | 
 |     return name?.reference; | 
 |   } | 
 |  | 
 |   Reference readNonNullMemberReference() { | 
 |     CanonicalName? name = readNullableCanonicalNameReference(); | 
 |     if (name == null) { | 
 |       throw 'Expected a member reference to be valid but was `null`.'; | 
 |     } | 
 |     return name.reference; | 
 |   } | 
 |  | 
 |   Reference readNonNullInstanceMemberReference() { | 
 |     Reference reference = readNonNullMemberReference(); | 
 |     readNullableMemberReference(); // Skip origin | 
 |     return reference; | 
 |   } | 
 |  | 
 |   Reference? getNullableMemberReferenceFromInt(int index) { | 
 |     return getNullableCanonicalNameReferenceFromInt(index)?.reference; | 
 |   } | 
 |  | 
 |   Reference readNonNullTypedefReference() { | 
 |     return readNonNullCanonicalNameReference().reference; | 
 |   } | 
 |  | 
 |   Name readName() { | 
 |     final int stringReference = readUInt30(); | 
 |     assert(stringReference < (1 << 30)); | 
 |     final String text = _stringTable[stringReference]; | 
 |     final bool isPrivate = text.isNotEmpty && text.codeUnitAt(0) == $_; | 
 |     final int libraryReferenceIndex; | 
 |     final int nameCacheIndex; | 
 |  | 
 |     if (isPrivate) { | 
 |       // "Raw" reference index of 0 means null which we don't allow. | 
 |       libraryReferenceIndex = readUInt30(); | 
 |       if (libraryReferenceIndex == 0) { | 
 |         throw 'Expected a library reference to be valid but was `null`.'; | 
 |       } | 
 |  | 
 |       // Check cache using the upper bits for the library reference. | 
 |       nameCacheIndex = stringReference | ((libraryReferenceIndex) << 30); | 
 |     } else { | 
 |       // the 0 will be unused but we need to assign it. | 
 |       libraryReferenceIndex = 0; | 
 |       nameCacheIndex = stringReference; | 
 |     } | 
 |  | 
 |     final Name? cached = _nameCache[nameCacheIndex]; | 
 |     if (cached != null) { | 
 |       return cached; | 
 |     } | 
 |  | 
 |     // Not in cache. Create it and cache it. | 
 |     final Name name; | 
 |     if (isPrivate) { | 
 |       // libraryReferenceIndex was checked to be > 0 so we get a canonical name. | 
 |       final CanonicalName canonicalName = | 
 |           getNullableCanonicalNameReferenceFromInt(libraryReferenceIndex)!; | 
 |       final Reference libraryReference = canonicalName.reference; | 
 |       name = new Name.byReference(text, libraryReference); | 
 |     } else { | 
 |       name = new Name(text); | 
 |     } | 
 |     _nameCache[nameCacheIndex] = name; | 
 |     return name; | 
 |   } | 
 |  | 
 |   Library readLibrary(Component component, int endOffset) { | 
 |     // Read index. | 
 |     int savedByteOffset = _byteOffset; | 
 |  | 
 |     // There is a field for the procedure count. | 
 |     _byteOffset = endOffset - (1) * 4; | 
 |     int procedureCount = readUint32(); | 
 |  | 
 |     // There is a field for the procedure count, that number + 1 (for the end) | 
 |     // offsets, and then the class count (i.e. procedure count + 3 fields). | 
 |     _byteOffset = endOffset - (procedureCount + 3) * 4; | 
 |     int classCount = readUint32(); | 
 |     List<int> procedureOffsets = new List<int>.generate( | 
 |         procedureCount + 1, (int index) => _componentStartOffset + readUint32(), | 
 |         growable: false); | 
 |  | 
 |     // There is a field for the procedure count, that number + 1 (for the end) | 
 |     // offsets, then the class count and that number + 1 (for the end) offsets. | 
 |     // (i.e. procedure count + class count + 4 fields). | 
 |     _byteOffset = endOffset - (procedureCount + classCount + 4) * 4; | 
 |     List<int> classOffsets = new List<int>.generate( | 
 |         classCount + 1, (int index) => _componentStartOffset + readUint32(), | 
 |         growable: false); | 
 |     _byteOffset = savedByteOffset; | 
 |  | 
 |     int flags = readByte(); | 
 |  | 
 |     int languageVersionMajor = readUInt30(); | 
 |     int languageVersionMinor = readUInt30(); | 
 |  | 
 |     CanonicalName canonicalName = readNonNullCanonicalNameReference(); | 
 |     Reference reference = canonicalName.reference; | 
 |     Library? library = reference.node as Library?; | 
 |     String? name = readStringOrNullIfEmpty(); | 
 |  | 
 |     // TODO(jensj): We currently save (almost the same) uri twice. | 
 |     Uri fileUri = readUriReference(); | 
 |  | 
 |     if (alwaysCreateNewNamedNodes) { | 
 |       library = null; | 
 |     } | 
 |     if (library == null) { | 
 |       library = new Library(Uri.parse(canonicalName.name), | 
 |           reference: reference, fileUri: fileUri); | 
 |       component.libraries.add(library..parent = component); | 
 |     } | 
 |     _currentLibrary = library; | 
 |  | 
 |     List<String>? problemsAsJson = readListOfStrings(); | 
 |  | 
 |     library.flags = flags; | 
 |     library.setLanguageVersion( | 
 |         new Version(languageVersionMajor, languageVersionMinor)); | 
 |     library.name = name; | 
 |     library.fileUri = fileUri; | 
 |     library.problemsAsJson = problemsAsJson; | 
 |  | 
 |     assert(() { | 
 |       debugPath.add(library!.name ?? library.importUri.toString()); | 
 |       return true; | 
 |     }()); | 
 |  | 
 |     library.annotations = readAnnotationList(library); | 
 |     _readLibraryDependencies(library); | 
 |     _readAdditionalExports(library); | 
 |     _readLibraryParts(library); | 
 |     _readTypedefList(library); | 
 |     _readClassList(library, classOffsets); | 
 |     _readExtensionList(library); | 
 |     _readExtensionTypeDeclarationList(library); | 
 |     library.fieldsInternal = _readFieldList(library); | 
 |     library.proceduresInternal = _readProcedureList(library, procedureOffsets); | 
 |  | 
 |     assert(((_) => true)(debugPath.removeLast())); | 
 |     _currentLibrary = null; | 
 |     return library; | 
 |   } | 
 |  | 
 |   void _readTypedefList(Library library) { | 
 |     int length = readUInt30(); | 
 |     if (!useGrowableLists && length == 0) { | 
 |       // When lists don't have to be growable anyway, we might as well use an | 
 |       // almost constant one for the empty list. | 
 |       library.typedefsInternal = emptyListOfTypedef; | 
 |     } else { | 
 |       library.typedefsInternal = new List<Typedef>.generate( | 
 |           length, (int index) => readTypedef()..parent = library, | 
 |           growable: useGrowableLists); | 
 |     } | 
 |   } | 
 |  | 
 |   void _readClassList(Library library, List<int> classOffsets) { | 
 |     int length = readUInt30(); | 
 |     if (!useGrowableLists && length == 0) { | 
 |       // When lists don't have to be growable anyway, we might as well use an | 
 |       // almost constant one for the empty list. | 
 |       library.classesInternal = emptyListOfClass; | 
 |     } else { | 
 |       library.classesInternal = new List<Class>.generate(length, (int index) { | 
 |         _byteOffset = classOffsets[index]; | 
 |         return readClass(classOffsets[index + 1])..parent = library; | 
 |       }, growable: useGrowableLists); | 
 |       _byteOffset = classOffsets.last; | 
 |     } | 
 |   } | 
 |  | 
 |   void _readExtensionList(Library library) { | 
 |     int length = readUInt30(); | 
 |     if (!useGrowableLists && length == 0) { | 
 |       // When lists don't have to be growable anyway, we might as well use an | 
 |       // almost constant one for the empty list. | 
 |       library.extensionsInternal = emptyListOfExtension; | 
 |     } else { | 
 |       library.extensionsInternal = new List<Extension>.generate( | 
 |           length, (int index) => readExtension()..parent = library, | 
 |           growable: useGrowableLists); | 
 |     } | 
 |   } | 
 |  | 
 |   void _readExtensionTypeDeclarationList(Library library) { | 
 |     int length = readUInt30(); | 
 |     if (!useGrowableLists && length == 0) { | 
 |       // When lists don't have to be growable anyway, we might as well use an | 
 |       // almost constant one for the empty list. | 
 |       library.extensionTypeDeclarationsInternal = | 
 |           emptyListOfExtensionTypeDeclaration; | 
 |     } else { | 
 |       library.extensionTypeDeclarationsInternal = | 
 |           new List<ExtensionTypeDeclaration>.generate(length, | 
 |               (int index) => readExtensionTypeDeclaration()..parent = library, | 
 |               growable: useGrowableLists); | 
 |     } | 
 |   } | 
 |  | 
 |   List<Field> _readFieldList(TreeNode parent) { | 
 |     int length = readUInt30(); | 
 |     if (!useGrowableLists && length == 0) { | 
 |       // When lists don't have to be growable anyway, we might as well use an | 
 |       // almost constant one for the empty list. | 
 |       return emptyListOfField; | 
 |     } | 
 |     return new List<Field>.generate( | 
 |         length, (int index) => readField()..parent = parent, | 
 |         growable: useGrowableLists); | 
 |   } | 
 |  | 
 |   List<Procedure> _readProcedureList( | 
 |       TreeNode parent, List<int> procedureOffsets) { | 
 |     int length = readUInt30(); | 
 |     if (!useGrowableLists && length == 0) { | 
 |       // When lists don't have to be growable anyway, we might as well use an | 
 |       // almost constant one for the empty list. | 
 |       return emptyListOfProcedure; | 
 |     } | 
 |     List<Procedure> list = new List<Procedure>.generate(length, (int index) { | 
 |       _byteOffset = procedureOffsets[index]; | 
 |       return readProcedure(procedureOffsets[index + 1])..parent = parent; | 
 |     }, growable: useGrowableLists); | 
 |     _byteOffset = procedureOffsets.last; | 
 |     return list; | 
 |   } | 
 |  | 
 |   List<Procedure> _readProcedureListWithoutOffsets(TreeNode parent) { | 
 |     int length = readUInt30(); | 
 |     if (!useGrowableLists && length == 0) { | 
 |       // When lists don't have to be growable anyway, we might as well use an | 
 |       // almost constant one for the empty list. | 
 |       return emptyListOfProcedure; | 
 |     } | 
 |     List<Procedure> list = new List<Procedure>.generate(length, (int index) { | 
 |       return readProcedure(/* no end offset = */ -1)..parent = parent; | 
 |     }, growable: useGrowableLists); | 
 |     return list; | 
 |   } | 
 |  | 
 |   void _readLibraryDependencies(Library library) { | 
 |     int length = readUInt30(); | 
 |     if (!useGrowableLists && length == 0) { | 
 |       // When lists don't have to be growable anyway, we might as well use an | 
 |       // almost constant one for the empty list. | 
 |       library.dependencies = emptyListOfLibraryDependency; | 
 |     } else { | 
 |       library.dependencies = new List<LibraryDependency>.generate( | 
 |           length, (int index) => readLibraryDependency()..parent = library, | 
 |           growable: useGrowableLists); | 
 |     } | 
 |   } | 
 |  | 
 |   LibraryDependency readLibraryDependency() { | 
 |     int fileOffset = readOffset(); | 
 |     int flags = readByte(); | 
 |     List<Expression> annotations = 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); | 
 | } |