| // 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/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/ast/ast_factory.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/ast_binary_tokens.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:pub_semver/pub_semver.dart'; |
| |
| Map<String, LibraryReader> createLibraryReadersWithAstBytes({ |
| required LinkedElementFactory elementFactory, |
| required Uint8List resolutionBytes, |
| required Map<String, Map<String, Uint8List>> uriToLibrary_uriToUnitAstBytes, |
| }) { |
| var _resolutionReader = SummaryDataReader(resolutionBytes); |
| |
| _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, |
| ); |
| |
| _resolutionReader.offset = resolutionLibrariesOffset; |
| var resolutionLibraryOffsets = _resolutionReader.readUint30List(); |
| |
| assert( |
| uriToLibrary_uriToUnitAstBytes.length == resolutionLibraryOffsets.length, |
| ); |
| |
| // TODO(scheglov) Don't read anything, we know URIs. |
| var libraryMap = <String, LibraryReader>{}; |
| for (var i = 0; i < resolutionLibraryOffsets.length; i++) { |
| _resolutionReader.offset = resolutionLibraryOffsets[i]; |
| var libraryUriStr = _resolutionReader.readStringReference(); |
| var resolutionUnitOffsets = _resolutionReader.readUint30List(); |
| var exportsIndexList = _resolutionReader.readUint30List(); |
| |
| var uriToUnitAstBytes = uriToLibrary_uriToUnitAstBytes[libraryUriStr]!; |
| |
| var reference = elementFactory.rootReference.getChild(libraryUriStr); |
| var libraryReader = LibraryReaderForAstBytes._( |
| elementFactory, |
| uriToUnitAstBytes, |
| _resolutionReader, |
| referenceReader, |
| reference, |
| resolutionUnitOffsets, |
| exportsIndexList, |
| ); |
| libraryMap[libraryUriStr] = libraryReader; |
| } |
| |
| return libraryMap; |
| } |
| |
| class BundleReader { |
| final SummaryDataReader _astReader; |
| final SummaryDataReader _resolutionReader; |
| |
| bool _withInformative = false; |
| |
| final Map<String, LibraryReader> libraryMap = {}; |
| |
| BundleReader({ |
| required LinkedElementFactory elementFactory, |
| required Uint8List astBytes, |
| required Uint8List resolutionBytes, |
| }) : _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 = LibraryReaderFromBundle._( |
| 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); |
| } |
| |
| abstract class LibraryReader { |
| final LinkedElementFactory _elementFactory; |
| final SummaryDataReader _resolutionReader; |
| final _ReferenceReader _referenceReader; |
| final Reference reference; |
| |
| final Uint32List _resolutionUnitOffsets; |
| final Uint32List _exportsIndexList; |
| List<Reference>? _exports; |
| |
| List<UnitReader>? _units; |
| |
| LibraryReader._( |
| this._elementFactory, |
| this._resolutionReader, |
| this._referenceReader, |
| this.reference, |
| this._resolutionUnitOffsets, |
| this._exportsIndexList, |
| ); |
| |
| List<Reference> get exports { |
| return _exports ??= _exportsIndexList |
| .map((index) => _referenceReader.referenceOfIndex(index)) |
| .toList(); |
| } |
| |
| /// Is `true` if the defining unit has [PartOfDirective]. |
| bool get hasPartOfDirective; |
| |
| String get name; |
| |
| int get nameLength; |
| |
| int get nameOffset; |
| |
| List<UnitReader> get units; |
| |
| bool get withInformative; |
| } |
| |
| /// Implementation of [LibraryReader] that reads ASTs for units from separate |
| /// byte buffers. |
| class LibraryReaderForAstBytes extends LibraryReader { |
| final Map<String, Uint8List> _uriToUnitAstBytes; |
| |
| bool _hasNameRead = false; |
| late final bool _withInformative; |
| late final String _name; |
| late final int _nameOffset; |
| late final int _nameLength; |
| late final bool _hasPartOfDirective; |
| |
| LibraryReaderForAstBytes._( |
| LinkedElementFactory elementFactory, |
| Map<String, Uint8List> uriToUnitAstBytes, |
| SummaryDataReader resolutionReader, |
| _ReferenceReader referenceReader, |
| Reference reference, |
| Uint32List resolutionUnitOffsets, |
| Uint32List exportsIndexList, |
| ) : _uriToUnitAstBytes = uriToUnitAstBytes, |
| super._( |
| elementFactory, |
| resolutionReader, |
| referenceReader, |
| reference, |
| resolutionUnitOffsets, |
| exportsIndexList, |
| ) { |
| // TODO(scheglov) This fails when there are invalid URIs. |
| // assert(_uriToUnitAstBytes.length == _resolutionUnitOffsets.length); |
| } |
| |
| @override |
| bool get hasPartOfDirective { |
| _readName(); |
| return _hasPartOfDirective; |
| } |
| |
| @override |
| String get name { |
| _readName(); |
| return _name; |
| } |
| |
| @override |
| int get nameLength { |
| _readName(); |
| return _nameLength; |
| } |
| |
| @override |
| int get nameOffset { |
| _readName(); |
| return _nameOffset; |
| } |
| |
| @override |
| List<UnitReader> get units { |
| if (_units != null) return _units!; |
| _units = []; |
| |
| for (var i = 0; i < _resolutionUnitOffsets.length; i++) { |
| _resolutionReader.offset = _resolutionUnitOffsets[i]; |
| var unitUriStr = _resolutionReader.readStringReference(); |
| var isSynthetic = _resolutionReader.readByte() != 0; |
| var isPart = _resolutionReader.readByte() != 0; |
| String? partUriStr = _resolutionReader.readStringReference(); |
| if (!isPart) { |
| partUriStr = null; |
| } |
| var resolutionDirectivesOffset = _resolutionReader.readUInt30(); |
| var resolutionDeclarationOffsets = _resolutionReader.readUint30List(); |
| |
| // TODO(scheglov) Is this right? |
| if (unitUriStr.isEmpty) { |
| unitUriStr = 'null'; |
| } |
| |
| var astBytes = _uriToUnitAstBytes[unitUriStr]!; |
| var astReader = SummaryDataReader(astBytes); |
| astReader.offset = astBytes.length - 4 * 4; |
| var headerOffset = astReader.readUint32(); |
| var indexOffset = astReader.readUint32(); |
| astReader.readUint32(); // library data |
| var astStringsOffset = astReader.readUint32(); |
| astReader.createStringTable(astStringsOffset); |
| |
| _units!.add( |
| UnitReader._( |
| this, |
| resolutionDirectivesOffset, |
| resolutionDeclarationOffsets, |
| reference.getChild('@unit').getChild(unitUriStr), |
| isSynthetic, |
| partUriStr, |
| astReader, |
| headerOffset, |
| indexOffset, |
| ), |
| ); |
| } |
| |
| return _units!; |
| } |
| |
| @override |
| bool get withInformative { |
| _readName(); |
| return _withInformative; |
| } |
| |
| void _readName() { |
| if (_hasNameRead) return; |
| _hasNameRead = true; |
| |
| var uriStr = reference.name; |
| var definingUnitBytes = _uriToUnitAstBytes[uriStr]!; |
| var reader = SummaryDataReader(definingUnitBytes); |
| reader.offset = definingUnitBytes.length - 4 * 2; |
| var libraryDataOffset = reader.readUint32(); |
| var astStringsOffset = reader.readUint32(); |
| reader.createStringTable(astStringsOffset); |
| |
| reader.offset = libraryDataOffset; |
| _name = reader.readStringReference(); |
| _nameOffset = reader.readUInt30() - 1; |
| _nameLength = reader.readUInt30(); |
| _hasPartOfDirective = reader.readByte() != 0; |
| _withInformative = reader.readByte() != 0; |
| } |
| } |
| |
| class LibraryReaderFromBundle extends LibraryReader { |
| final SummaryDataReader _astReader; |
| final Uint32List _astUnitOffsets; |
| |
| @override |
| final String name; |
| |
| @override |
| final int nameOffset; |
| |
| @override |
| final int nameLength; |
| |
| @override |
| final bool hasPartOfDirective; |
| |
| @override |
| final bool withInformative; |
| |
| LibraryReaderFromBundle._( |
| LinkedElementFactory elementFactory, |
| this.withInformative, |
| SummaryDataReader astReader, |
| SummaryDataReader resolutionReader, |
| _ReferenceReader referenceReader, |
| Reference reference, |
| this.name, |
| this.nameOffset, |
| this.nameLength, |
| this.hasPartOfDirective, |
| Uint32List astUnitOffsets, |
| Uint32List resolutionUnitOffsets, |
| Uint32List exportsIndexList, |
| ) : _astReader = astReader, |
| _astUnitOffsets = astUnitOffsets, |
| super._( |
| elementFactory, |
| resolutionReader, |
| referenceReader, |
| reference, |
| resolutionUnitOffsets, |
| exportsIndexList, |
| ) { |
| assert(_astUnitOffsets.length == _resolutionUnitOffsets.length); |
| } |
| |
| @override |
| List<UnitReader> get units { |
| if (_units != null) return _units!; |
| _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; |
| String? 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, |
| _astReader, |
| 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; |
| |
| late final _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; |
| |
| 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, |
| ); |
| |
| /// TODO(scheglov) Remove after fixing http://dartbug.com/44449 |
| int get byteOffset => _byteOffset; |
| |
| /// TODO(scheglov) Remove after fixing http://dartbug.com/44449 |
| Uint8List get bytes => _unitReader._resolutionReader.bytes; |
| |
| Element? nextElement() { |
| var memberFlags = readByte(); |
| var element = _readRawElement(); |
| |
| if (element == null) { |
| return null; |
| } |
| |
| 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 as ExecutableElement, 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) { |
| var type = DynamicTypeImpl.instance; |
| return _readAliasElementArguments(type); |
| } else if (tag == Tag.FunctionType) { |
| var type = _readFunctionType(); |
| return _readAliasElementArguments(type); |
| } else if (tag == Tag.InterfaceType) { |
| var element = nextElement() as ClassElement; |
| var typeArguments = _readTypeList(); |
| var nullability = _readNullability(); |
| var type = InterfaceTypeImpl( |
| element: element, |
| typeArguments: typeArguments, |
| nullabilitySuffix: nullability, |
| ); |
| return _readAliasElementArguments(type); |
| } else if (tag == Tag.InterfaceType_noTypeArguments_none) { |
| var element = nextElement() as ClassElement; |
| var type = InterfaceTypeImpl( |
| element: element, |
| typeArguments: const <DartType>[], |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| return _readAliasElementArguments(type); |
| } else if (tag == Tag.InterfaceType_noTypeArguments_question) { |
| var element = nextElement() as ClassElement; |
| var type = InterfaceTypeImpl( |
| element: element, |
| typeArguments: const <DartType>[], |
| nullabilitySuffix: NullabilitySuffix.question, |
| ); |
| return _readAliasElementArguments(type); |
| } else if (tag == Tag.InterfaceType_noTypeArguments_star) { |
| var element = nextElement() as ClassElement; |
| var type = InterfaceTypeImpl( |
| element: element, |
| typeArguments: const <DartType>[], |
| nullabilitySuffix: NullabilitySuffix.star, |
| ); |
| return _readAliasElementArguments(type); |
| } else if (tag == Tag.NeverType) { |
| var nullability = _readNullability(); |
| var type = NeverTypeImpl.instance.withNullability(nullability); |
| return _readAliasElementArguments(type); |
| } else if (tag == Tag.TypeParameterType) { |
| var element = nextElement() as TypeParameterElement; |
| var nullability = _readNullability(); |
| var type = TypeParameterTypeImpl( |
| element: element, |
| nullabilitySuffix: nullability, |
| ); |
| return _readAliasElementArguments(type); |
| } else if (tag == Tag.VoidType) { |
| var type = VoidTypeImpl.instance; |
| return _readAliasElementArguments(type); |
| } 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(); |
| } |
| } |
| |
| DartType _readAliasElementArguments(DartType type) { |
| var aliasElement = _readRawElement(); |
| if (aliasElement is TypeAliasElement) { |
| var aliasArguments = _readTypeList(); |
| if (type is DynamicType) { |
| // TODO(scheglov) add support for `dynamic` aliasing |
| return type; |
| } else if (type is FunctionType) { |
| return FunctionTypeImpl( |
| typeFormals: type.typeFormals, |
| parameters: type.parameters, |
| returnType: type.returnType, |
| nullabilitySuffix: type.nullabilitySuffix, |
| aliasElement: aliasElement, |
| aliasArguments: aliasArguments, |
| ); |
| } else if (type is InterfaceType) { |
| return InterfaceTypeImpl( |
| element: type.element, |
| typeArguments: type.typeArguments, |
| nullabilitySuffix: type.nullabilitySuffix, |
| aliasElement: aliasElement, |
| aliasArguments: aliasArguments, |
| ); |
| } else if (type is TypeParameterType) { |
| return TypeParameterTypeImpl( |
| element: type.element, |
| nullabilitySuffix: type.nullabilitySuffix, |
| aliasElement: aliasElement, |
| aliasArguments: aliasArguments, |
| ); |
| } else if (type is VoidType) { |
| // TODO(scheglov) add support for `void` aliasing |
| return type; |
| } else { |
| throw UnimplementedError('${type.runtimeType}'); |
| } |
| } |
| return type; |
| } |
| |
| /// 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 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, |
| ); |
| } |
| |
| 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; |
| |
| final SummaryDataReader astReader; |
| |
| late final int _directivesOffset; |
| final List<_UnitMemberReader> _memberReaders = []; |
| |
| late final CompilationUnitImpl _unit; |
| bool _hasDirectives = false; |
| bool _hasDeclarations = false; |
| |
| UnitReader._( |
| this.libraryReader, |
| this._directivesResolutionOffset, |
| this._resolutionDeclarationsOffset, |
| this.reference, |
| this.isSynthetic, |
| this.partUriStr, |
| this.astReader, |
| 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: Tokens.BANG, |
| // TODO(scheglov) |
| // scriptTag: _readNode(data.compilationUnit_scriptTag), |
| directives: [], |
| declarations: [], |
| endToken: Tokens.BANG, |
| featureSet: featureSet, |
| ); |
| _unit.languageVersion = languageVersion; |
| _unit.lineInfo = lineInfo; |
| _unit.summaryData = SummaryDataForCompilationUnit(codeLength); |
| |
| astReader.offset = indexOffset; |
| _readIndex2(); |
| } |
| |
| 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 _ClassMemberMock extends AstNodeImpl implements ClassMemberImpl { |
| static final instance = _ClassMemberMock(); |
| |
| @override |
| AstNode? parent; |
| |
| @override |
| noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); |
| } |
| |
| class _ClassMemberReader implements ReferenceNodeAccessor { |
| final UnitReader unitReader; |
| final int offset; |
| final List<ClassMember> _members; |
| final int _membersIndex; |
| ClassMemberImpl? _node; |
| |
| _ClassMemberReader(this.unitReader, this.offset, this._members) |
| : _membersIndex = _members.length { |
| _members.add(_ClassMemberMock.instance); |
| } |
| |
| @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 = []; |
| late final List<ClassMember> _classMembers; |
| |
| _ClassReader({ |
| required this.reference, |
| required UnitReader unitReader, |
| required int offset, |
| required CompilationUnitImpl 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 _CompilationUnitMemberMock extends AstNodeImpl |
| implements CompilationUnitMemberImpl { |
| static final instance = _CompilationUnitMemberMock(); |
| |
| @override |
| noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); |
| } |
| |
| class _ReferenceReader { |
| final LinkedElementFactory elementFactory; |
| final SummaryDataReader _reader; |
| late final Uint32List _parents; |
| late final Uint32List _names; |
| late final 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 CompilationUnitImpl _unit; |
| final int _index; |
| CompilationUnitMemberImpl? _node; |
| |
| _UnitMemberReader(this.unitReader, this.offset, this._unit) |
| : _index = _unit.declarations.length { |
| _unit.declarations.add(_CompilationUnitMemberMock.instance); |
| } |
| |
| @override |
| AstNode get node { |
| if (_node == null) { |
| var astReader = AstBinaryReader( |
| reader: unitReader, |
| ); |
| unitReader.astReader.offset = offset; |
| _node = astReader.readNode() as CompilationUnitMemberImpl; |
| _unit.declarations[_index] = _node!; |
| |
| var hasLinkedContext = _node as HasAstLinkedContext; |
| var linkedContext = hasLinkedContext.linkedContext as LinkedContext; |
| linkedContext._reader = this; |
| } |
| return _node!; |
| } |
| |
| @override |
| void readIndex() {} |
| } |