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