| // Copyright (c) 2020, 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. |
| |
| import 'dart:typed_data'; |
| |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/standard_ast_factory.dart'; |
| import 'package:analyzer/dart/ast/token.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/nullability_suffix.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/source/line_info.dart'; |
| import 'package:analyzer/src/dart/analysis/experiments.dart'; |
| import 'package:analyzer/src/dart/ast/ast.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/member.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/element/type_algebra.dart'; |
| import 'package:analyzer/src/generated/testing/token_factory.dart'; |
| import 'package:analyzer/src/generated/utilities_dart.dart'; |
| import 'package:analyzer/src/summary2/apply_resolution.dart'; |
| import 'package:analyzer/src/summary2/ast_binary_reader.dart'; |
| import 'package:analyzer/src/summary2/ast_binary_tag.dart'; |
| import 'package:analyzer/src/summary2/data_reader.dart'; |
| import 'package:analyzer/src/summary2/linked_element_factory.dart'; |
| import 'package:analyzer/src/summary2/linked_unit_context.dart'; |
| import 'package:analyzer/src/summary2/reference.dart'; |
| import 'package:meta/meta.dart'; |
| import 'package:pub_semver/pub_semver.dart'; |
| |
| class BundleReader { |
| final LinkedElementFactory elementFactory; |
| |
| final SummaryDataReader _astReader; |
| final SummaryDataReader _resolutionReader; |
| |
| bool withInformative = false; |
| |
| final Map<String, LibraryReader> libraryMap = {}; |
| |
| BundleReader({ |
| @required LinkedElementFactory elementFactory, |
| @required Uint8List astBytes, |
| @required Uint8List resolutionBytes, |
| }) : elementFactory = elementFactory, |
| _astReader = SummaryDataReader(astBytes), |
| _resolutionReader = SummaryDataReader(resolutionBytes) { |
| _astReader.offset = 0; |
| withInformative = _astReader.readByte() == 1; |
| |
| _astReader.offset = _astReader.bytes.length - 4 * 2; |
| var astLibrariesOffset = _astReader.readUint32(); |
| var astStringsOffset = _astReader.readUint32(); |
| _astReader.createStringTable(astStringsOffset); |
| |
| _resolutionReader.offset = _resolutionReader.bytes.length - 4 * 3; |
| var resolutionLibrariesOffset = _resolutionReader.readUint32(); |
| var resolutionReferencesOffset = _resolutionReader.readUint32(); |
| var resolutionStringsOffset = _resolutionReader.readUint32(); |
| _resolutionReader.createStringTable(resolutionStringsOffset); |
| |
| var referenceReader = _ReferenceReader( |
| elementFactory, |
| _resolutionReader, |
| resolutionReferencesOffset, |
| ); |
| |
| _astReader.offset = astLibrariesOffset; |
| var astLibraryOffsets = _astReader.readUint30List(); |
| |
| _resolutionReader.offset = resolutionLibrariesOffset; |
| var resolutionLibraryOffsets = _resolutionReader.readUint30List(); |
| |
| assert(astLibraryOffsets.length == resolutionLibraryOffsets.length); |
| |
| for (var i = 0; i < astLibraryOffsets.length; i++) { |
| _astReader.offset = astLibraryOffsets[i]; |
| var name = _astReader.readStringReference(); |
| var nameOffset = _astReader.readUInt30() - 1; |
| var nameLength = _astReader.readUInt30(); |
| var hasPartOfDirective = _astReader.readByte() != 0; |
| var astUnitOffsets = _astReader.readUint30List(); |
| |
| _resolutionReader.offset = resolutionLibraryOffsets[i]; |
| var libraryUriStr = _resolutionReader.readStringReference(); |
| var resolutionUnitOffsets = _resolutionReader.readUint30List(); |
| assert(astUnitOffsets.length == resolutionUnitOffsets.length); |
| var exportsIndexList = _resolutionReader.readUint30List(); |
| |
| var reference = elementFactory.rootReference.getChild(libraryUriStr); |
| var libraryReader = LibraryReader._( |
| elementFactory, |
| withInformative, |
| _astReader, |
| _resolutionReader, |
| referenceReader, |
| reference, |
| name, |
| nameOffset, |
| nameLength, |
| hasPartOfDirective, |
| astUnitOffsets, |
| resolutionUnitOffsets, |
| exportsIndexList, |
| ); |
| libraryMap[libraryUriStr] = libraryReader; |
| } |
| } |
| |
| LibraryReader getLibrary(String uriStr) { |
| return libraryMap[uriStr]; |
| } |
| } |
| |
| class ClassReader { |
| final int membersOffset; |
| |
| ClassReader(this.membersOffset); |
| } |
| |
| class LibraryReader { |
| final LinkedElementFactory elementFactory; |
| final bool withInformative; |
| final SummaryDataReader astReader; |
| final SummaryDataReader resolutionReader; |
| final _ReferenceReader referenceReader; |
| final Reference reference; |
| |
| final String name; |
| final int nameOffset; |
| final int nameLength; |
| |
| /// Is `true` if the defining unit has [PartOfDirective]. |
| final bool hasPartOfDirective; |
| |
| final Uint32List astUnitOffsets; |
| final Uint32List resolutionUnitOffsets; |
| final Uint32List exportsIndexList; |
| List<Reference> _exports; |
| |
| List<UnitReader> _units; |
| |
| LibraryReader._( |
| this.elementFactory, |
| this.withInformative, |
| this.astReader, |
| this.resolutionReader, |
| this.referenceReader, |
| this.reference, |
| this.name, |
| this.nameOffset, |
| this.nameLength, |
| this.hasPartOfDirective, |
| this.astUnitOffsets, |
| this.resolutionUnitOffsets, |
| this.exportsIndexList, |
| ) { |
| assert(astUnitOffsets.length == resolutionUnitOffsets.length); |
| } |
| |
| List<Reference> get exports { |
| if (_exports == null) { |
| var length = exportsIndexList.length; |
| _exports = List.filled(length, null, growable: false); |
| for (var i = 0; i < length; i++) { |
| var index = exportsIndexList[i]; |
| var reference = referenceReader.referenceOfIndex(index); |
| _exports[i] = reference; |
| } |
| } |
| return _exports; |
| } |
| |
| List<UnitReader> get units { |
| if (_units == null) { |
| _units = []; |
| for (var i = 0; i < astUnitOffsets.length; i++) { |
| var astUnitOffset = astUnitOffsets[i]; |
| var resolutionUnitOffset = resolutionUnitOffsets[i]; |
| |
| astReader.offset = astUnitOffset; |
| var headerOffset = astReader.readUInt30(); |
| var indexOffset = astReader.offset; |
| |
| resolutionReader.offset = resolutionUnitOffset; |
| var unitUriStr = resolutionReader.readStringReference(); |
| var isSynthetic = resolutionReader.readByte() != 0; |
| var isPart = resolutionReader.readByte() != 0; |
| var partUriStr = resolutionReader.readStringReference(); |
| if (!isPart) { |
| partUriStr = null; |
| } |
| var resolutionDirectivesOffset = resolutionReader.readUInt30(); |
| var resolutionDeclarationOffsets = resolutionReader.readUint30List(); |
| |
| _units.add( |
| UnitReader._( |
| this, |
| resolutionDirectivesOffset, |
| resolutionDeclarationOffsets, |
| reference.getChild('@unit').getChild(unitUriStr), |
| isSynthetic, |
| partUriStr, |
| headerOffset, |
| indexOffset, |
| ), |
| ); |
| } |
| } |
| return _units; |
| } |
| } |
| |
| class LinkedContext implements AstLinkedContext { |
| final UnitReader _unitReader; |
| final AstNode _node; |
| final int _resolutionIndex; |
| final Uint32List _codeOffsetLengthList; |
| final Uint32List _documentationTokenIndexList; |
| |
| @override |
| final int codeOffset; |
| |
| @override |
| final int codeLength; |
| |
| @override |
| final bool isClassWithConstConstructor; |
| |
| bool _isApplied = false; |
| |
| bool _hasDocumentationComment = false; |
| |
| _UnitMemberReader _reader; |
| |
| LinkedContext( |
| this._unitReader, |
| this._node, { |
| @required this.codeOffset, |
| @required this.codeLength, |
| this.isClassWithConstConstructor = false, |
| Uint32List codeOffsetLengthList, |
| @required int resolutionIndex, |
| @required Uint32List documentationTokenIndexList, |
| }) : _resolutionIndex = resolutionIndex, |
| _codeOffsetLengthList = codeOffsetLengthList, |
| _documentationTokenIndexList = documentationTokenIndexList; |
| |
| @override |
| List<ClassMember> get classMembers { |
| var reader = _reader; |
| if (reader is _ClassReader) { |
| return reader.classMembers; |
| } else if (_node is ClassTypeAlias) { |
| return const <ClassMember>[]; |
| } else { |
| throw UnimplementedError(); |
| } |
| } |
| |
| @override |
| // TODO: implement unitDirectives |
| List<Directive> get unitDirectives => throw UnimplementedError(); |
| |
| @override |
| void applyResolution(LinkedUnitContext unitContext) { |
| if (_isApplied) { |
| return; |
| } |
| _isApplied = true; |
| |
| // EnumConstantDeclaration has no separate resolution. |
| // Its metadata is resolved during EnumDeclaration resolution. |
| if (_resolutionIndex == -1) { |
| return; |
| } |
| |
| var localElements = <Element>[]; |
| var resolutionReader = LinkedResolutionReader( |
| _unitReader, |
| localElements, |
| _unitReader._resolutionDeclarationsOffset[_resolutionIndex], |
| ); |
| _node.accept( |
| ApplyResolutionVisitor( |
| unitContext, |
| localElements, |
| resolutionReader, |
| ), |
| ); |
| } |
| |
| @override |
| int getVariableDeclarationCodeLength(VariableDeclaration node) { |
| var variableList = node.parent as VariableDeclarationList; |
| var variables = variableList.variables; |
| for (var i = 0; i < variables.length; i++) { |
| if (identical(variables[i], node)) { |
| return _codeOffsetLengthList[2 * i + 1]; |
| } |
| } |
| throw StateError('No |$node| in: $variableList'); |
| } |
| |
| @override |
| int getVariableDeclarationCodeOffset(VariableDeclaration node) { |
| var variableList = node.parent as VariableDeclarationList; |
| var variables = variableList.variables; |
| for (var i = 0; i < variables.length; i++) { |
| if (identical(variables[i], node)) { |
| return _codeOffsetLengthList[2 * i + 0]; |
| } |
| } |
| throw StateError('No |$node| in: $variableList'); |
| } |
| |
| @override |
| void readDocumentationComment() { |
| if (_hasDocumentationComment) { |
| return; |
| } |
| _hasDocumentationComment = true; |
| |
| if (_documentationTokenIndexList.isEmpty) { |
| return; |
| } |
| |
| var tokens = <Token>[]; |
| for (var lexemeIndex in _documentationTokenIndexList) { |
| var lexeme = _unitReader.astReader.stringOfIndex(lexemeIndex); |
| var token = TokenFactory.tokenFromString(lexeme); |
| tokens.add(token); |
| } |
| |
| var comment = astFactory.documentationComment(tokens); |
| (_node as AnnotatedNodeImpl).documentationComment = comment; |
| } |
| } |
| |
| /// Helper for reading elements and types from their binary encoding. |
| class LinkedResolutionReader { |
| final UnitReader _unitReader; |
| |
| /// The stack of [TypeParameterElement]s and [ParameterElement] that are |
| /// available in the scope of [nextElement] and [nextType]. |
| /// |
| /// This stack is shared with the client of the reader, and update mostly |
| /// by the client. However it is also updated during [_readFunctionType]. |
| final List<Element> _localElements; |
| |
| /// The offset in [_Reader.bytes] from which we read resolution now. |
| int _byteOffset = 0; |
| |
| LinkedResolutionReader( |
| this._unitReader, |
| this._localElements, |
| this._byteOffset, |
| ); |
| |
| Element nextElement() { |
| var memberFlags = readByte(); |
| var element = _readRawElement(); |
| |
| if (memberFlags == Tag.RawElement) { |
| return element; |
| } |
| |
| if (memberFlags == Tag.MemberLegacyWithTypeArguments || |
| memberFlags == Tag.MemberWithTypeArguments) { |
| var arguments = _readTypeList(); |
| // TODO(scheglov) why to check for empty? If we have this flags. |
| if (arguments.isNotEmpty) { |
| var typeParameters = |
| (element.enclosingElement as TypeParameterizedElement) |
| .typeParameters; |
| var substitution = Substitution.fromPairs(typeParameters, arguments); |
| element = ExecutableMember.from2(element, substitution); |
| } |
| } |
| |
| if (memberFlags == Tag.MemberLegacyWithTypeArguments) { |
| return Member.legacy(element); |
| } |
| |
| if (memberFlags == Tag.MemberWithTypeArguments) { |
| return element; |
| } |
| |
| throw UnimplementedError('memberFlags: $memberFlags'); |
| } |
| |
| String nextString() { |
| var index = _readUInt30(); |
| return _unitReader._resolutionReader.stringOfIndex(index); |
| } |
| |
| DartType nextType() { |
| var tag = readByte(); |
| if (tag == Tag.NullType) { |
| return null; |
| } else if (tag == Tag.DynamicType) { |
| return DynamicTypeImpl.instance; |
| } else if (tag == Tag.FunctionType) { |
| return _readFunctionType(); |
| } else if (tag == Tag.InterfaceType) { |
| var element = nextElement(); |
| var length = _readUInt30(); |
| var typeArguments = List<DartType>.filled(length, null); |
| for (var i = 0; i < length; i++) { |
| typeArguments[i] = nextType(); |
| } |
| var nullability = _readNullability(); |
| return InterfaceTypeImpl( |
| element: element, |
| typeArguments: typeArguments, |
| nullabilitySuffix: nullability, |
| ); |
| } else if (tag == Tag.InterfaceType_noTypeArguments_none) { |
| var element = nextElement(); |
| return InterfaceTypeImpl( |
| element: element, |
| typeArguments: const <DartType>[], |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } else if (tag == Tag.InterfaceType_noTypeArguments_question) { |
| var element = nextElement(); |
| return InterfaceTypeImpl( |
| element: element, |
| typeArguments: const <DartType>[], |
| nullabilitySuffix: NullabilitySuffix.question, |
| ); |
| } else if (tag == Tag.InterfaceType_noTypeArguments_star) { |
| var element = nextElement(); |
| return InterfaceTypeImpl( |
| element: element, |
| typeArguments: const <DartType>[], |
| nullabilitySuffix: NullabilitySuffix.star, |
| ); |
| } else if (tag == Tag.NeverType) { |
| var nullability = _readNullability(); |
| return NeverTypeImpl.instance.withNullability(nullability); |
| } else if (tag == Tag.TypeParameterType) { |
| var element = nextElement(); |
| var nullability = _readNullability(); |
| return TypeParameterTypeImpl( |
| element: element, |
| nullabilitySuffix: nullability, |
| ); |
| } else if (tag == Tag.VoidType) { |
| return VoidTypeImpl.instance; |
| } else { |
| throw UnimplementedError('$tag'); |
| } |
| } |
| |
| int readByte() { |
| return _unitReader._resolutionReader.bytes[_byteOffset++]; |
| } |
| |
| List<String> readStringList() { |
| var values = <String>[]; |
| var length = _readUInt30(); |
| for (var i = 0; i < length; i++) { |
| var value = _readStringReference(); |
| values.add(value); |
| } |
| return values; |
| } |
| |
| int readUInt30() { |
| var 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(); |
| } |
| } |
| |
| /// TODO(scheglov) Optimize for write/read of types without type parameters. |
| FunctionType _readFunctionType() { |
| var typeParameters = <TypeParameterElement>[]; |
| var typeParametersLength = _readUInt30(); |
| for (var i = 0; i < typeParametersLength; i++) { |
| var name = _readStringReference(); |
| var element = TypeParameterElementImpl.synthetic(name); |
| typeParameters.add(element); |
| _localElements.add(element); |
| } |
| for (var i = 0; i < typeParametersLength; i++) { |
| var element = typeParameters[i] as TypeParameterElementImpl; |
| var bound = nextType(); |
| element.bound = bound; |
| } |
| |
| var typedefElement = nextElement(); |
| var typeArguments = _readTypeList(); |
| |
| var returnType = nextType(); |
| |
| var formalParameters = <ParameterElement>[]; |
| var formalParametersLength = _readUInt30(); |
| for (var i = 0; i < formalParametersLength; i++) { |
| var kindIndex = readByte(); |
| var type = nextType(); |
| var name = nextString(); |
| formalParameters.add( |
| ParameterElementImpl.synthetic( |
| name, |
| type, |
| _formalParameterKind(kindIndex), |
| ), |
| ); |
| } |
| |
| var nullability = _readNullability(); |
| |
| _localElements.length -= typeParametersLength; |
| |
| return FunctionTypeImpl( |
| typeFormals: typeParameters, |
| parameters: formalParameters, |
| returnType: returnType, |
| nullabilitySuffix: nullability, |
| element: typedefElement, |
| typeArguments: typeArguments, |
| ); |
| } |
| |
| NullabilitySuffix _readNullability() { |
| var index = readByte(); |
| return NullabilitySuffix.values[index]; |
| } |
| |
| Element _readRawElement() { |
| var index = _readUInt30(); |
| |
| if ((index & 0x1) == 0x1) { |
| return _localElements[index >> 1]; |
| } |
| |
| var referenceIndex = index >> 1; |
| var referenceReader = _unitReader._referenceReader; |
| var reference = referenceReader.referenceOfIndex(referenceIndex); |
| |
| var elementFactory = _unitReader.elementFactory; |
| return elementFactory.elementOfReference(reference); |
| } |
| |
| String _readStringReference() { |
| var index = _readUInt30(); |
| return _unitReader._resolutionReader.stringOfIndex(index); |
| } |
| |
| List<DartType> _readTypeList() { |
| var types = <DartType>[]; |
| var length = _readUInt30(); |
| for (var i = 0; i < length; i++) { |
| var argument = nextType(); |
| types.add(argument); |
| } |
| return types; |
| } |
| |
| int _readUInt30() { |
| var 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(); |
| } |
| } |
| |
| static ParameterKind _formalParameterKind(int encoding) { |
| if (encoding == Tag.ParameterKindRequiredPositional) { |
| return ParameterKind.REQUIRED; |
| } else if (encoding == Tag.ParameterKindOptionalPositional) { |
| return ParameterKind.POSITIONAL; |
| } else if (encoding == Tag.ParameterKindRequiredNamed) { |
| return ParameterKind.NAMED_REQUIRED; |
| } else if (encoding == Tag.ParameterKindOptionalNamed) { |
| return ParameterKind.NAMED; |
| } else { |
| throw StateError('Unexpected parameter kind encoding: $encoding'); |
| } |
| } |
| } |
| |
| class SummaryDataForCompilationUnit { |
| final int codeLength; |
| |
| SummaryDataForCompilationUnit(this.codeLength); |
| } |
| |
| class SummaryDataForFormalParameter { |
| final int codeOffset; |
| final int codeLength; |
| |
| SummaryDataForFormalParameter({ |
| @required this.codeOffset, |
| @required this.codeLength, |
| }); |
| } |
| |
| class SummaryDataForLibraryDirective { |
| final UnitReader _unitReader; |
| final LibraryDirectiveImpl _node; |
| final Uint32List _documentationTokenIndexList; |
| bool _hasDocumentationComment = false; |
| |
| SummaryDataForLibraryDirective( |
| this._unitReader, |
| this._node, { |
| @required Uint32List documentationTokenIndexList, |
| }) : _documentationTokenIndexList = documentationTokenIndexList { |
| _node.summaryData = this; |
| } |
| |
| void readDocumentationComment() { |
| if (_hasDocumentationComment) { |
| return; |
| } |
| _hasDocumentationComment = true; |
| |
| if (_documentationTokenIndexList.isEmpty) { |
| return; |
| } |
| |
| var tokens = <Token>[]; |
| for (var lexemeIndex in _documentationTokenIndexList) { |
| var lexeme = _unitReader.astReader.stringOfIndex(lexemeIndex); |
| var token = TokenFactory.tokenFromString(lexeme); |
| tokens.add(token); |
| } |
| |
| var comment = astFactory.documentationComment(tokens); |
| _node.documentationComment = comment; |
| } |
| } |
| |
| class SummaryDataForTypeParameter { |
| final int codeOffset; |
| final int codeLength; |
| |
| SummaryDataForTypeParameter({ |
| @required this.codeOffset, |
| @required this.codeLength, |
| }); |
| } |
| |
| class UnitReader implements ReferenceNodeAccessor { |
| final LibraryReader libraryReader; |
| final Reference reference; |
| |
| final bool isSynthetic; |
| |
| /// If a part, the URI that is used in the [PartDirective]. |
| /// Or `null` for the defining unit. |
| final String partUriStr; |
| |
| final int _directivesResolutionOffset; |
| bool _isDirectivesResolutionApplied = false; |
| |
| final Uint32List _resolutionDeclarationsOffset; |
| |
| int _directivesOffset; |
| final List<_UnitMemberReader> _memberReaders = []; |
| |
| CompilationUnitImpl _unit; |
| bool _hasDirectives = false; |
| bool _hasDeclarations = false; |
| |
| UnitReader._( |
| this.libraryReader, |
| this._directivesResolutionOffset, |
| this._resolutionDeclarationsOffset, |
| this.reference, |
| this.isSynthetic, |
| this.partUriStr, |
| int headerOffset, |
| int indexOffset, |
| ) { |
| reference.nodeAccessor = this; |
| |
| astReader.offset = headerOffset; |
| var languageVersion = _readLanguageVersion(); |
| var featureSetEncoded = astReader.readUint8List(); |
| var lineInfo = _readLineInfo(); |
| var codeLength = astReader.readUInt30(); |
| var featureSet = ExperimentStatus.fromStorage(featureSetEncoded); |
| _directivesOffset = astReader.offset; |
| |
| _unit = astFactory.compilationUnit( |
| beginToken: null, |
| // TODO(scheglov) |
| // scriptTag: _readNode(data.compilationUnit_scriptTag), |
| directives: [], |
| declarations: [], |
| endToken: null, |
| featureSet: featureSet, |
| ); |
| _unit.languageVersion = languageVersion; |
| _unit.lineInfo = lineInfo; |
| _unit.summaryData = SummaryDataForCompilationUnit(codeLength); |
| |
| astReader.offset = indexOffset; |
| _readIndex2(); |
| } |
| |
| SummaryDataReader get astReader => libraryReader.astReader; |
| |
| LinkedElementFactory get elementFactory => libraryReader.elementFactory; |
| |
| /// TODO(scheglov) |
| /// This methods breaks lazy loading, and loads everything eagerly. |
| /// We use it because of `unitElement.types` for example, when we are |
| /// explicitly asked for all [ClassDeclaration]s and [ClassTypeAlias]s. |
| @Deprecated('review it') |
| @override |
| CompilationUnit get node { |
| readDirectives(); |
| readDeclarations(); |
| return _unit; |
| } |
| |
| CompilationUnit get unit => _unit; |
| |
| String get uriStr => reference.name; |
| |
| bool get withInformative => libraryReader.withInformative; |
| |
| _ReferenceReader get _referenceReader => libraryReader.referenceReader; |
| |
| SummaryDataReader get _resolutionReader => libraryReader.resolutionReader; |
| |
| /// Apply resolution to directives. |
| void applyDirectivesResolution(LinkedUnitContext unitContext) { |
| if (_isDirectivesResolutionApplied) { |
| return; |
| } |
| _isDirectivesResolutionApplied = true; |
| |
| var localElements = <Element>[]; |
| var resolutionReader = LinkedResolutionReader( |
| this, |
| localElements, |
| _directivesResolutionOffset, |
| ); |
| for (var directive in _unit.directives) { |
| directive.accept( |
| ApplyResolutionVisitor( |
| unitContext, |
| localElements, |
| resolutionReader, |
| ), |
| ); |
| } |
| } |
| |
| void readDeclarations() { |
| if (!_hasDeclarations) { |
| _hasDeclarations = true; |
| for (var reader in _memberReaders) { |
| reader.node; |
| } |
| } |
| } |
| |
| /// Ensure that directives are read in this unit. |
| void readDirectives() { |
| if (!_hasDirectives) { |
| _hasDirectives = true; |
| astReader.offset = _directivesOffset; |
| var length = astReader.readUInt30(); |
| for (var i = 0; i < length; i++) { |
| var astReader = AstBinaryReader( |
| reader: this, |
| ); |
| var directive = astReader.readNode() as Directive; |
| _unit.directives.add(directive); |
| } |
| } |
| } |
| |
| @override |
| void readIndex() {} |
| |
| /// Read the index of declarations in this unit, and add `null`s into |
| /// [CompilationUnit.declarations] as placeholders. |
| /// |
| /// TODO(scheglov) we don't need both this method, and [readIndex]. |
| void _readIndex2() { |
| var unitReference = reference; |
| var length = astReader.readUInt30(); |
| for (var i = 0; i < length; i++) { |
| var offset = astReader.readUInt30(); |
| var tag = astReader.readByte(); |
| if (tag == Tag.Class) { |
| var name = astReader.readStringReference(); |
| var indexOffset = astReader.readUInt30(); |
| var reference = unitReference.getChild('@class').getChild(name); |
| _memberReaders.add( |
| _ClassReader( |
| unitReader: this, |
| reference: reference, |
| offset: offset, |
| unit: _unit, |
| indexOffset: indexOffset, |
| ), |
| ); |
| } else if (tag == Tag.ClassTypeAlias) { |
| var name = astReader.readStringReference(); |
| var reader = _UnitMemberReader(this, offset, _unit); |
| _memberReaders.add(reader); |
| unitReference.getChild('@class').getChild(name).nodeAccessor = reader; |
| } else if (tag == Tag.EnumDeclaration) { |
| var name = astReader.readStringReference(); |
| var reader = _UnitMemberReader(this, offset, _unit); |
| _memberReaders.add(reader); |
| unitReference.getChild('@enum').getChild(name).nodeAccessor = reader; |
| } else if (tag == Tag.ExtensionDeclaration) { |
| var name = astReader.readStringReference(); |
| var indexOffset = astReader.readUInt30(); |
| var reference = unitReference.getChild('@extension').getChild(name); |
| _memberReaders.add( |
| _ClassReader( |
| unitReader: this, |
| reference: reference, |
| offset: offset, |
| unit: _unit, |
| indexOffset: indexOffset, |
| ), |
| ); |
| } else if (tag == Tag.FunctionDeclaration) { |
| var name = astReader.readStringReference(); |
| var reader = _UnitMemberReader(this, offset, _unit); |
| _memberReaders.add(reader); |
| var containerRef = unitReference.getChild('@function'); |
| containerRef.getChild(name).nodeAccessor = reader; |
| } else if (tag == Tag.FunctionDeclaration_getter) { |
| var name = astReader.readStringReference(); |
| var reader = _UnitMemberReader(this, offset, _unit); |
| _memberReaders.add(reader); |
| var getterRef = unitReference.getChild('@getter'); |
| getterRef.getChild(name).nodeAccessor = reader; |
| var variableRef = unitReference.getChild('@variable'); |
| variableRef.getChild(name).nodeAccessor ??= reader; |
| } else if (tag == Tag.FunctionDeclaration_setter) { |
| var name = astReader.readStringReference(); |
| var reader = _UnitMemberReader(this, offset, _unit); |
| _memberReaders.add(reader); |
| var setterRef = unitReference.getChild('@setter'); |
| setterRef.getChild(name).nodeAccessor = reader; |
| var variableRef = unitReference.getChild('@variable'); |
| variableRef.getChild(name).nodeAccessor ??= reader; |
| } else if (tag == Tag.GenericTypeAlias) { |
| var name = astReader.readStringReference(); |
| var reader = _UnitMemberReader(this, offset, _unit); |
| _memberReaders.add(reader); |
| unitReference.getChild('@typeAlias').getChild(name).nodeAccessor = |
| reader; |
| } else if (tag == Tag.FunctionTypeAlias) { |
| var name = astReader.readStringReference(); |
| var reader = _UnitMemberReader(this, offset, _unit); |
| _memberReaders.add(reader); |
| unitReference.getChild('@typeAlias').getChild(name).nodeAccessor = |
| reader; |
| } else if (tag == Tag.MixinDeclaration) { |
| var name = astReader.readStringReference(); |
| var indexOffset = astReader.readUInt30(); |
| var reference = unitReference.getChild('@mixin').getChild(name); |
| _memberReaders.add( |
| _ClassReader( |
| unitReader: this, |
| reference: reference, |
| offset: offset, |
| unit: _unit, |
| indexOffset: indexOffset, |
| ), |
| ); |
| } else if (tag == Tag.TopLevelVariableDeclaration) { |
| var reader = _UnitMemberReader(this, offset, _unit); |
| var length = astReader.readUInt30(); |
| for (var i = 0; i < length; i++) { |
| var name = astReader.readStringReference(); |
| _memberReaders.add(reader); |
| unitReference.getChild('@getter').getChild(name).nodeAccessor = |
| reader; |
| // TODO(scheglov) only if not final/const |
| // Crash in language_2/export/local_export_test.dart |
| unitReference.getChild('@setter').getChild(name).nodeAccessor = |
| reader; |
| } |
| } else { |
| // TODO(scheglov) implement |
| } |
| } |
| } |
| |
| LibraryLanguageVersion _readLanguageVersion() { |
| var packageMajor = astReader.readUInt30(); |
| var packageMinor = astReader.readUInt30(); |
| var overrideMajor = astReader.readUInt30(); |
| var overrideMinor = astReader.readUInt30(); |
| return LibraryLanguageVersion( |
| package: Version(packageMajor, packageMinor, 0), |
| override: overrideMajor > 0 |
| ? Version(overrideMajor - 1, overrideMinor - 1, 0) |
| : null, |
| ); |
| } |
| |
| LineInfo _readLineInfo() { |
| var lineStarts = astReader.readUint30List(); |
| return LineInfo(lineStarts); |
| } |
| } |
| |
| class _ClassMemberReader implements ReferenceNodeAccessor { |
| final UnitReader unitReader; |
| final int offset; |
| final NodeListImpl<ClassMember> _members; |
| final int _membersIndex; |
| ClassMemberImpl _node; |
| |
| _ClassMemberReader(this.unitReader, this.offset, this._members) |
| : _membersIndex = _members.length { |
| _members.add(null); |
| } |
| |
| @override |
| AstNode get node { |
| if (_node == null) { |
| var astReader = AstBinaryReader( |
| reader: unitReader, |
| ); |
| unitReader.astReader.offset = offset; |
| _node = astReader.readNode() as ClassMemberImpl; |
| _members[_membersIndex] = _node; |
| } |
| return _node; |
| } |
| |
| @override |
| void readIndex() {} |
| } |
| |
| class _ClassReader extends _UnitMemberReader { |
| final Reference reference; |
| final int indexOffset; |
| |
| bool _hasIndex = false; |
| final List<_ClassMemberReader> _classMemberReaders = []; |
| List<ClassMember> _classMembers; |
| |
| _ClassReader({ |
| @required this.reference, |
| @required UnitReader unitReader, |
| @required int offset, |
| @required CompilationUnit unit, |
| @required this.indexOffset, |
| }) : super(unitReader, offset, unit) { |
| reference.nodeAccessor ??= this; |
| } |
| |
| List<_ClassMemberReader> get classMemberReaders { |
| readIndex(); |
| return _classMemberReaders; |
| } |
| |
| List<ClassMember> get classMembers { |
| return classMemberReaders.map((e) => e.node as ClassMember).toList(); |
| } |
| |
| @override |
| void readIndex() { |
| if (_hasIndex) return; |
| _hasIndex = true; |
| |
| var node = _node; |
| if (node == null) { |
| throw StateError('The class node must be read before reading members.'); |
| } |
| |
| if (node is ClassDeclarationImpl) { |
| _classMembers = node.members; |
| } else if (node is ExtensionDeclarationImpl) { |
| _classMembers = node.members; |
| } else if (node is MixinDeclarationImpl) { |
| _classMembers = node.members; |
| } else { |
| throw StateError('(${node.runtimeType}) $node'); |
| } |
| |
| unitReader.astReader.offset = indexOffset; |
| |
| var length = unitReader.astReader.readUInt30(); |
| for (var i = 0; i < length; i++) { |
| var offset = unitReader.astReader.readUInt30(); |
| var tag = unitReader.astReader.readByte(); |
| if (tag == Tag.ConstructorDeclaration) { |
| var reader = _ClassMemberReader(unitReader, offset, _classMembers); |
| _classMemberReaders.add(reader); |
| var name = unitReader.astReader.readStringReference(); |
| var reference = this.reference.getChild('@constructor').getChild(name); |
| reference.nodeAccessor ??= reader; |
| } else if (tag == Tag.MethodDeclaration) { |
| var reader = _ClassMemberReader(unitReader, offset, _classMembers); |
| _classMemberReaders.add(reader); |
| var name = unitReader.astReader.readStringReference(); |
| var reference = this.reference.getChild('@method').getChild(name); |
| reference.nodeAccessor ??= reader; |
| } else if (tag == Tag.MethodDeclaration_getter) { |
| var reader = _ClassMemberReader(unitReader, offset, _classMembers); |
| _classMemberReaders.add(reader); |
| var name = unitReader.astReader.readStringReference(); |
| var reference = this.reference.getChild('@getter').getChild(name); |
| reference.nodeAccessor ??= reader; |
| } else if (tag == Tag.MethodDeclaration_setter) { |
| var reader = _ClassMemberReader(unitReader, offset, _classMembers); |
| _classMemberReaders.add(reader); |
| var name = unitReader.astReader.readStringReference(); |
| var reference = this.reference.getChild('@setter').getChild(name); |
| reference.nodeAccessor ??= reader; |
| } else if (tag == Tag.FieldDeclaration) { |
| var reader = _ClassMemberReader(unitReader, offset, _classMembers); |
| _classMemberReaders.add(reader); |
| var length = unitReader.astReader.readUInt30(); |
| for (var i = 0; i < length; i++) { |
| var name = unitReader.astReader.readStringReference(); |
| var fieldRef = reference.getChild('@field').getChild(name); |
| fieldRef.nodeAccessor ??= reader; |
| var getterRef = reference.getChild('@getter').getChild(name); |
| getterRef.nodeAccessor ??= reader; |
| var setterRef = reference.getChild('@setter').getChild(name); |
| setterRef.nodeAccessor ??= reader; |
| } |
| } else { |
| throw UnimplementedError('tag: $tag'); |
| } |
| } |
| } |
| } |
| |
| class _ReferenceReader { |
| final LinkedElementFactory elementFactory; |
| final SummaryDataReader _reader; |
| Uint32List _parents; |
| Uint32List _names; |
| List<Reference> _references; |
| |
| _ReferenceReader(this.elementFactory, this._reader, int offset) { |
| _reader.offset = offset; |
| _parents = _reader.readUint30List(); |
| _names = _reader.readUint30List(); |
| assert(_parents.length == _names.length); |
| |
| _references = List.filled(_names.length, null); |
| } |
| |
| Reference referenceOfIndex(int index) { |
| var reference = _references[index]; |
| if (reference != null) { |
| return reference; |
| } |
| |
| if (index == 0) { |
| reference = elementFactory.rootReference; |
| _references[index] = reference; |
| return reference; |
| } |
| |
| var nameIndex = _names[index]; |
| var name = _reader.stringOfIndex(nameIndex); |
| |
| var parentIndex = _parents[index]; |
| var parent = referenceOfIndex(parentIndex); |
| |
| reference = parent.getChild(name); |
| _references[index] = reference; |
| |
| return reference; |
| } |
| } |
| |
| class _UnitMemberReader implements ReferenceNodeAccessor { |
| final UnitReader unitReader; |
| final int offset; |
| final CompilationUnit _unit; |
| final int _index; |
| CompilationUnitMemberImpl _node; |
| |
| _UnitMemberReader(this.unitReader, this.offset, this._unit) |
| : _index = _unit.declarations.length { |
| _unit.declarations.add(null); |
| } |
| |
| @override |
| AstNode get node { |
| if (_node == null) { |
| var astReader = AstBinaryReader( |
| reader: unitReader, |
| ); |
| unitReader.astReader.offset = offset; |
| _node = astReader.readNode() as CompilationUnitMember; |
| _unit.declarations[_index] = _node; |
| |
| var hasLinkedContext = _node as HasAstLinkedContext; |
| var linkedContext = hasLinkedContext.linkedContext as LinkedContext; |
| linkedContext._reader = this; |
| } |
| return _node; |
| } |
| |
| @override |
| void readIndex() {} |
| } |