blob: ecace4eb1c724729907a3a255d3b7a9de787c3ea [file] [log] [blame]
// 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;
// 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,
);
/// 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) {
return DynamicTypeImpl.instance;
} else if (tag == Tag.FunctionType) {
return _readFunctionType();
} else if (tag == Tag.InterfaceType) {
var element = nextElement() as ClassElement;
var typeArguments = _readTypeList();
var nullability = _readNullability();
return InterfaceTypeImpl(
element: element,
typeArguments: typeArguments,
nullabilitySuffix: nullability,
);
} else if (tag == Tag.InterfaceType_noTypeArguments_none) {
var element = nextElement() as ClassElement;
return InterfaceTypeImpl(
element: element,
typeArguments: const <DartType>[],
nullabilitySuffix: NullabilitySuffix.none,
);
} else if (tag == Tag.InterfaceType_noTypeArguments_question) {
var element = nextElement() as ClassElement;
return InterfaceTypeImpl(
element: element,
typeArguments: const <DartType>[],
nullabilitySuffix: NullabilitySuffix.question,
);
} else if (tag == Tag.InterfaceType_noTypeArguments_star) {
var element = nextElement() as ClassElement;
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() as TypeParameterElement;
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 aliasElement = nextElement() as TypeAliasElement?;
var aliasArguments = aliasElement != null ? _readTypeList() : null;
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,
aliasElement: aliasElement,
aliasArguments: aliasArguments,
);
}
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() {}
}