blob: c5ede2c9f17e9693f4aabe5dae9c4a54ff7c10d1 [file]
// Copyright (c) 2019, 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/src/binary/binary_reader.dart';
import 'package:analyzer/src/binary/binary_writer.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/utilities/uri_cache.dart';
final class BuiltInReference extends Reference implements ExportableReference {
final LibraryReference library;
final _BuiltInReferenceKind _kind;
BuiltInReference._({
required this.library,
required _BuiltInReferenceKind kind,
}) : _kind = kind,
super._();
}
sealed class DeclarationReference extends Reference {
/// The stable identity component for this declaration under its enclosing
/// reference.
///
/// This key is internal to reference identity. It is not necessarily the
/// user-visible name written in source, because unnamed and duplicate
/// declarations receive generated keys.
final String _key;
DeclarationReference._({required String key}) : _key = key, super._();
}
sealed class ExportableReference implements Reference {}
final class LeafTopLevelReference extends TopLevelReference {
LeafTopLevelReference._({
required super.library,
required super.key,
required super.kind,
}) : super._();
}
final class LibraryReference extends Reference {
final String uriString;
final List<Map<String, TopLevelReference>?> _topLevelsByKind = List.filled(
_TopLevelReferenceKind.values.length,
null,
);
List<BuiltInReference?>? _builtInByKind;
LibraryReference._({required this.uriString}) : super._();
@override
List<Reference> get children => [
for (var byKey in _topLevelsByKind) ...?byKey?.values,
if (_builtInByKind case var builtInByKind?)
for (var builtIn in builtInByKind) ?builtIn,
];
BuiltInReference get dynamicRef {
assert(uriString == 'dart:core');
return _builtIn(_BuiltInReferenceKind.dynamic_);
}
BuiltInReference get neverRef {
assert(uriString == 'dart:core');
return _builtIn(_BuiltInReferenceKind.never_);
}
Uri get uri => uriCache.parse(uriString);
MemberContainerReference declareClass(String key) {
return _declareMemberContainer(
kind: _TopLevelReferenceKind.class_,
key: key,
);
}
MemberContainerReference declareEnum(String key) {
return _declareMemberContainer(
kind: _TopLevelReferenceKind.enum_,
key: key,
);
}
MemberContainerReference declareExtension(String key) {
return _declareMemberContainer(
kind: _TopLevelReferenceKind.extension_,
key: key,
);
}
MemberContainerReference declareExtensionType(String key) {
return _declareMemberContainer(
kind: _TopLevelReferenceKind.extensionType,
key: key,
);
}
TopLevelReference declareGetter(String key) {
return _declareTopLevel(kind: _TopLevelReferenceKind.getter, key: key);
}
MemberContainerReference declareMixin(String key) {
return _declareMemberContainer(
kind: _TopLevelReferenceKind.mixin_,
key: key,
);
}
TopLevelReference declareSetter(String key) {
return _declareTopLevel(kind: _TopLevelReferenceKind.setter, key: key);
}
TopLevelReference declareTopLevelFunction(String key) {
return _declareTopLevel(kind: _TopLevelReferenceKind.function, key: key);
}
TopLevelReference declareTopLevelVariable(String key) {
return _declareTopLevel(
kind: _TopLevelReferenceKind.topLevelVariable,
key: key,
);
}
TopLevelReference declareTypeAlias(String key) {
return _declareTopLevel(kind: _TopLevelReferenceKind.typeAlias, key: key);
}
MemberContainerReference getOrCreateClass(String key) {
return _getOrCreateMemberContainer(
kind: _TopLevelReferenceKind.class_,
key: key,
);
}
MemberContainerReference getOrCreateEnum(String key) {
return _getOrCreateMemberContainer(
kind: _TopLevelReferenceKind.enum_,
key: key,
);
}
MemberContainerReference getOrCreateExtension(String key) {
return _getOrCreateMemberContainer(
kind: _TopLevelReferenceKind.extension_,
key: key,
);
}
MemberContainerReference getOrCreateExtensionType(String key) {
return _getOrCreateMemberContainer(
kind: _TopLevelReferenceKind.extensionType,
key: key,
);
}
TopLevelReference getOrCreateGetter(String key) {
return _getOrCreateTopLevel(kind: _TopLevelReferenceKind.getter, key: key);
}
MemberContainerReference getOrCreateMixin(String key) {
return _getOrCreateMemberContainer(
kind: _TopLevelReferenceKind.mixin_,
key: key,
);
}
TopLevelReference getOrCreateSetter(String key) {
return _getOrCreateTopLevel(kind: _TopLevelReferenceKind.setter, key: key);
}
TopLevelReference getOrCreateTopLevelFunction(String key) {
return _getOrCreateTopLevel(
kind: _TopLevelReferenceKind.function,
key: key,
);
}
TopLevelReference getOrCreateTopLevelVariable(String key) {
return _getOrCreateTopLevel(
kind: _TopLevelReferenceKind.topLevelVariable,
key: key,
);
}
TopLevelReference getOrCreateTypeAlias(String key) {
return _getOrCreateTopLevel(
kind: _TopLevelReferenceKind.typeAlias,
key: key,
);
}
BuiltInReference _builtIn(_BuiltInReferenceKind kind) {
var builtInByKind = _builtInByKind ??= List.filled(
_BuiltInReferenceKind.values.length,
null,
);
return builtInByKind[kind.index] ??= BuiltInReference._(
library: this,
kind: kind,
);
}
TopLevelReference _createTopLevelReference(
_TopLevelReferenceKind kind,
String key,
) {
if (kind.hasMembers) {
return MemberContainerReference._(library: this, key: key, kind: kind);
}
return LeafTopLevelReference._(library: this, key: key, kind: kind);
}
MemberContainerReference _declareMemberContainer({
required _TopLevelReferenceKind kind,
required String key,
}) {
var byKey = _topLevelByKey(kind);
if (byKey[key] != null) {
throw StateError('Duplicate reference key: $key');
}
return byKey[key] = MemberContainerReference._(
library: this,
key: key,
kind: kind,
);
}
TopLevelReference _declareTopLevel({
required _TopLevelReferenceKind kind,
required String key,
}) {
return Reference._declare(
byKey: _topLevelByKey(kind),
key: key,
create: (key) => _createTopLevelReference(kind, key),
);
}
MemberContainerReference _getOrCreateMemberContainer({
required _TopLevelReferenceKind kind,
required String key,
}) {
var byKey = _topLevelByKey(kind);
if (byKey[key] case MemberContainerReference existing) {
return existing;
}
var result = MemberContainerReference._(
library: this,
key: key,
kind: kind,
);
byKey[key] = result;
return result;
}
TopLevelReference _getOrCreateTopLevel({
required _TopLevelReferenceKind kind,
required String key,
}) {
return _topLevelByKey(kind)[key] ??= _createTopLevelReference(kind, key);
}
Map<String, TopLevelReference> _topLevelByKey(_TopLevelReferenceKind kind) {
return _topLevelsByKind[kind.index] ??= {};
}
}
final class LibraryReferenceBuilder {
final LibraryReference reference;
final Map<Reference, _KeyAllocator> _keyAllocatorByReference = Map.identity();
LibraryReferenceBuilder(this.reference);
MemberContainerReference declareClass(String? name) {
return reference.declareClass(
_allocateTopLevelKey(kind: _TopLevelReferenceKind.class_, name: name),
);
}
MemberContainerReference declareEnum(String? name) {
return reference.declareEnum(
_allocateTopLevelKey(kind: _TopLevelReferenceKind.enum_, name: name),
);
}
MemberContainerReference declareExtension(String? name) {
return reference.declareExtension(
_allocateTopLevelKey(kind: _TopLevelReferenceKind.extension_, name: name),
);
}
MemberContainerReference declareExtensionType(String? name) {
return reference.declareExtensionType(
_allocateTopLevelKey(
kind: _TopLevelReferenceKind.extensionType,
name: name,
),
);
}
TopLevelReference declareGetter(String? name) {
return reference.declareGetter(
_allocateTopLevelKey(kind: _TopLevelReferenceKind.getter, name: name),
);
}
MemberReference declareMemberConstructor({
required MemberContainerReference container,
required String? name,
}) {
return container.declareConstructor(
_allocateMemberKey(
container: container,
kind: _MemberReferenceKind.constructor,
name: name,
),
);
}
MemberReference declareMemberField({
required MemberContainerReference container,
required String? name,
}) {
return container.declareField(
_allocateMemberKey(
container: container,
kind: _MemberReferenceKind.field,
name: name,
),
);
}
MemberReference declareMemberGetter({
required MemberContainerReference container,
required String? name,
}) {
return container.declareGetter(
_allocateMemberKey(
container: container,
kind: _MemberReferenceKind.getter,
name: name,
),
);
}
MemberReference declareMemberMethod({
required MemberContainerReference container,
required String? name,
}) {
return container.declareMethod(
_allocateMemberKey(
container: container,
kind: _MemberReferenceKind.method,
name: name,
),
);
}
MemberReference declareMemberSetter({
required MemberContainerReference container,
required String? name,
}) {
return container.declareSetter(
_allocateMemberKey(
container: container,
kind: _MemberReferenceKind.setter,
name: name,
),
);
}
MemberContainerReference declareMixin(String? name) {
return reference.declareMixin(
_allocateTopLevelKey(kind: _TopLevelReferenceKind.mixin_, name: name),
);
}
TopLevelReference declareSetter(String? name) {
return reference.declareSetter(
_allocateTopLevelKey(kind: _TopLevelReferenceKind.setter, name: name),
);
}
TopLevelReference declareTopLevelFunction(String? name) {
return reference.declareTopLevelFunction(
_allocateTopLevelKey(kind: _TopLevelReferenceKind.function, name: name),
);
}
TopLevelReference declareTopLevelVariable(String? name) {
return reference.declareTopLevelVariable(
_allocateTopLevelKey(
kind: _TopLevelReferenceKind.topLevelVariable,
name: name,
),
);
}
TopLevelReference declareTypeAlias(String? name) {
return reference.declareTypeAlias(
_allocateTopLevelKey(kind: _TopLevelReferenceKind.typeAlias, name: name),
);
}
String _allocateMemberKey({
required MemberContainerReference container,
required _MemberReferenceKind kind,
required String? name,
}) {
return (_keyAllocatorByReference[container] ??= _KeyAllocator(
kindCount: _MemberReferenceKind.values.length,
)).allocate(kindIndex: kind.index, name: name);
}
String _allocateTopLevelKey({
required _TopLevelReferenceKind kind,
required String? name,
}) {
return (_keyAllocatorByReference[reference] ??= _KeyAllocator(
kindCount: _TopLevelReferenceKind.values.length,
)).allocate(kindIndex: kind.index, name: name);
}
}
final class MemberContainerReference extends TopLevelReference {
final List<Map<String, MemberReference>?> _membersByKind = List.filled(
_MemberReferenceKind.values.length,
null,
);
MemberContainerReference._({
required super.library,
required super.key,
required super.kind,
}) : super._();
@override
List<Reference> get children => [
for (var byKey in _membersByKind) ...?byKey?.values,
];
MemberReference declareConstructor(String key) {
return _declareMember(kind: _MemberReferenceKind.constructor, key: key);
}
MemberReference declareField(String key) {
return _declareMember(kind: _MemberReferenceKind.field, key: key);
}
MemberReference declareGetter(String key) {
return _declareMember(kind: _MemberReferenceKind.getter, key: key);
}
MemberReference declareMethod(String key) {
return _declareMember(kind: _MemberReferenceKind.method, key: key);
}
MemberReference declareSetter(String key) {
return _declareMember(kind: _MemberReferenceKind.setter, key: key);
}
MemberReference getOrCreateConstructor(String key) {
return _getOrCreateMember(kind: _MemberReferenceKind.constructor, key: key);
}
MemberReference getOrCreateField(String key) {
return _getOrCreateMember(kind: _MemberReferenceKind.field, key: key);
}
MemberReference getOrCreateGetter(String key) {
return _getOrCreateMember(kind: _MemberReferenceKind.getter, key: key);
}
MemberReference getOrCreateMethod(String key) {
return _getOrCreateMember(kind: _MemberReferenceKind.method, key: key);
}
MemberReference getOrCreateSetter(String key) {
return _getOrCreateMember(kind: _MemberReferenceKind.setter, key: key);
}
MemberReference _declareMember({
required _MemberReferenceKind kind,
required String key,
}) {
return Reference._declare(
byKey: _memberByKey(kind),
key: key,
create: (key) => MemberReference._(container: this, key: key, kind: kind),
);
}
MemberReference _getOrCreateMember({
required _MemberReferenceKind kind,
required String key,
}) {
return _memberByKey(kind)[key] ??= MemberReference._(
container: this,
key: key,
kind: kind,
);
}
Map<String, MemberReference> _memberByKey(_MemberReferenceKind kind) {
return _membersByKind[kind.index] ??= {};
}
}
final class MemberReference extends DeclarationReference {
final MemberContainerReference container;
final _MemberReferenceKind _kind;
MemberReference._({
required this.container,
required super.key,
required _MemberReferenceKind kind,
}) : _kind = kind,
super._();
}
/// A canonical identity node for an analyzer element.
///
/// References are the lightweight tree that summary reading and writing use to
/// name elements without requiring the corresponding [ElementImpl] to be built
/// yet. When an element is materialized, [element] is filled in with that
/// implementation object.
///
/// The tree shape is intentionally small:
///
/// - [RootReference] owns libraries.
/// - [LibraryReference] owns top-level declarations and `dart:core` built-ins.
/// - [MemberContainerReference] owns members of classes, enums, mixins,
/// extensions, and extension types.
/// - [LeafTopLevelReference], [MemberReference], and [BuiltInReference] have no
/// children.
///
/// Declaration references are identified under their enclosing reference by
/// kind and by an internal key. The key is not always the source name;
/// generated keys are used for unnamed declarations and duplicate source names.
sealed class Reference {
/// The corresponding [ElementImpl], or `null` if not created yet.
ElementImpl? element;
Reference._();
List<Reference> get children => const [];
/// The reference that must be materialized before this reference, or `null`
/// if this reference can be materialized directly.
Reference? get enclosingReference {
return switch (this) {
TopLevelReference(:var library) => library,
MemberReference(:var container) => container,
BuiltInReference(:var library) => library,
_ => null,
};
}
String debugString({String Function(String uriString)? formatLibraryUri}) {
String recurse(Reference reference) {
return reference.debugString(formatLibraryUri: formatLibraryUri);
}
switch (this) {
case RootReference():
return 'root';
case LibraryReference ref:
return formatLibraryUri?.call(ref.uriString) ?? ref.uriString;
case TopLevelReference ref:
var libraryStr = recurse(ref.library);
var kindStr = ref._kind.debugName;
return '$libraryStr::$kindStr::${ref._key}';
case MemberReference ref:
var containerStr = recurse(ref.container);
var kindStr = ref._kind.debugName;
return '$containerStr::$kindStr::${ref._key}';
case BuiltInReference ref:
var libraryStr = recurse(ref.library);
return '$libraryStr::${ref._kind.identifier}';
}
}
@override
String toString() => debugString();
static T _declare<T extends Reference>({
required Map<String, T> byKey,
required String key,
required T Function(String key) create,
}) {
if (byKey[key] != null) {
throw StateError('Duplicate reference key: $key');
}
return byKey[key] = create(key);
}
}
final class ReferenceTableReader {
final String Function(int index) _stringOfIndex;
final RootReference _rootReference;
late final Uint8List _tagIndexes;
late final Uint32List _parentIndexes;
late final Uint8List _kindIndexes;
late final Uint32List _keyStringIndexes;
late final Uint32List _uriStringIndexes;
late final List<Reference?> _referenceByIndex;
ReferenceTableReader({
required BinaryReader reader,
required RootReference rootReference,
}) : _stringOfIndex = reader.stringOfIndex,
_rootReference = rootReference {
_tagIndexes = reader.readUint8List();
_parentIndexes = reader.readUint30List();
_kindIndexes = reader.readUint8List();
_keyStringIndexes = reader.readUint30List();
_uriStringIndexes = reader.readUint30List();
assert(_tagIndexes.length == _parentIndexes.length);
assert(_tagIndexes.length == _kindIndexes.length);
assert(_tagIndexes.length == _keyStringIndexes.length);
assert(_tagIndexes.length == _uriStringIndexes.length);
_referenceByIndex = List.filled(_tagIndexes.length, null);
_referenceByIndex[0] = _rootReference;
}
DeclarationReference readDeclarationReference(BinaryReader reader) {
var index = reader.readUint30();
return _referenceOfIndex(index) as DeclarationReference;
}
ExportableReference readExportableReference(BinaryReader reader) {
var index = reader.readUint30();
return _referenceOfIndex(index) as ExportableReference;
}
LibraryReference readLibraryReference(BinaryReader reader) {
var index = reader.readUint30();
return _referenceOfIndex(index) as LibraryReference;
}
MemberContainerReference readMemberContainerReference(BinaryReader reader) {
var index = reader.readUint30();
return _referenceOfIndex(index) as MemberContainerReference;
}
MemberReference readMemberReference(BinaryReader reader) {
var index = reader.readUint30();
return _referenceOfIndex(index) as MemberReference;
}
Reference readReference(BinaryReader reader) {
var index = reader.readUint30();
return _referenceOfIndex(index);
}
TopLevelReference readTopLevelReference(BinaryReader reader) {
var index = reader.readUint30();
return _referenceOfIndex(index) as TopLevelReference;
}
LibraryReference _libraryReferenceOfIndex(int index) {
return _referenceOfIndex(index) as LibraryReference;
}
MemberContainerReference _memberContainerReferenceOfIndex(int index) {
return _referenceOfIndex(index) as MemberContainerReference;
}
Reference _referenceOfIndex(int index) {
var reference = _referenceByIndex[index];
if (reference != null) {
return reference;
}
var tag = _SerializedReferenceTag.values[_tagIndexes[index]];
switch (tag) {
case _SerializedReferenceTag.root:
reference = _rootReference;
case _SerializedReferenceTag.library:
var uriString = _stringOfIndex(_uriStringIndexes[index]);
reference = _rootReference.getOrCreateLibrary(
uriCache.parse(uriString),
);
case _SerializedReferenceTag.topLevel:
var library = _libraryReferenceOfIndex(_parentIndexes[index]);
var kind = _TopLevelReferenceKind.values[_kindIndexes[index]];
var key = _stringOfIndex(_keyStringIndexes[index]);
reference = library._getOrCreateTopLevel(kind: kind, key: key);
case _SerializedReferenceTag.member:
var container = _memberContainerReferenceOfIndex(_parentIndexes[index]);
reference = container._getOrCreateMember(
kind: _MemberReferenceKind.values[_kindIndexes[index]],
key: _stringOfIndex(_keyStringIndexes[index]),
);
case _SerializedReferenceTag.builtIn:
var library = _libraryReferenceOfIndex(_parentIndexes[index]);
var kind = _BuiltInReferenceKind.values[_kindIndexes[index]];
reference = library._builtIn(kind);
}
_referenceByIndex[index] = reference;
return reference;
}
}
final class ReferenceTableWriter {
static const int _rootIndex = 0;
final Map<Reference, int> _indexByReference = Map.identity();
final List<int> _tagIndexes = [];
final List<int> _parentIndexes = [];
final List<int> _kindIndexes = [];
final List<String> _keyStrings = [];
final List<String> _uriStrings = [];
ReferenceTableWriter() {
_appendRow(
reference: null,
tag: _SerializedReferenceTag.root,
parentIndex: _rootIndex,
kindIndex: 0,
key: '',
uriString: '',
);
}
void write(BinaryWriter sink) {
sink.writeUint8List(Uint8List.fromList(_tagIndexes));
sink.writeUint30List(_parentIndexes);
sink.writeUint8List(Uint8List.fromList(_kindIndexes));
sink.writeStringList(_keyStrings);
sink.writeStringList(_uriStrings);
}
void writeReference(BinaryWriter sink, Reference reference) {
var index = _indexOfReference(reference);
sink.writeUint30(index);
}
int _appendRow({
required Reference? reference,
required _SerializedReferenceTag tag,
required int parentIndex,
required int kindIndex,
required String key,
required String uriString,
}) {
assert(tag.index <= 0xFF);
assert(kindIndex <= 0xFF);
var index = _tagIndexes.length;
_tagIndexes.add(tag.index);
_parentIndexes.add(parentIndex);
_kindIndexes.add(kindIndex);
_keyStrings.add(key);
_uriStrings.add(uriString);
if (reference != null) {
_indexByReference[reference] = index;
}
return index;
}
int _indexOfReference(Reference reference) {
var index = _indexByReference[reference];
if (index != null) return index;
switch (reference) {
case RootReference():
return _rootIndex;
case LibraryReference():
return _appendRow(
reference: reference,
tag: _SerializedReferenceTag.library,
parentIndex: _rootIndex,
kindIndex: 0,
key: '',
uriString: reference.uriString,
);
case TopLevelReference():
return _appendRow(
reference: reference,
tag: _SerializedReferenceTag.topLevel,
parentIndex: _indexOfReference(reference.library),
kindIndex: reference._kind.index,
key: reference._key,
uriString: '',
);
case MemberReference():
return _appendRow(
reference: reference,
tag: _SerializedReferenceTag.member,
parentIndex: _indexOfReference(reference.container),
kindIndex: reference._kind.index,
key: reference._key,
uriString: '',
);
case BuiltInReference():
return _appendRow(
reference: reference,
tag: _SerializedReferenceTag.builtIn,
parentIndex: _indexOfReference(reference.library),
kindIndex: reference._kind.index,
key: '',
uriString: '',
);
}
}
}
final class RootReference extends Reference {
final Map<String, LibraryReference> _libraries = {};
RootReference() : super._();
@override
List<LibraryReference> get children => [..._libraries.values];
LibraryReference getOrCreateLibrary(Uri uri) {
var uriString = '$uri';
return _libraries[uriString] ??= LibraryReference._(uriString: uriString);
}
LibraryReference? libraryIfExists(Uri uri) {
var uriString = '$uri';
return _libraries[uriString];
}
LibraryReference? removeLibrary(Uri uri) {
var uriString = '$uri';
return _libraries.remove(uriString);
}
}
sealed class TopLevelReference extends DeclarationReference
implements ExportableReference {
final LibraryReference library;
final _TopLevelReferenceKind _kind;
TopLevelReference._({
required this.library,
required super.key,
required _TopLevelReferenceKind kind,
}) : _kind = kind,
super._();
}
enum _BuiltInReferenceKind {
dynamic_('dynamic'),
never_('Never');
final String identifier;
const _BuiltInReferenceKind(this.identifier);
}
final class _KeyAllocator {
final List<Map<String, int>?> _nextDuplicateIndexByKind;
int _nextUnnamedIndex = 0;
_KeyAllocator({required int kindCount})
: _nextDuplicateIndexByKind = List.filled(kindCount, null);
String allocate({required int kindIndex, required String? name}) {
var baseName = name ?? _unnamedName(_nextUnnamedIndex++);
var byBaseName = _nextDuplicateIndexByKind[kindIndex] ??= {};
var duplicateIndex = byBaseName[baseName] ?? 0;
byBaseName[baseName] = duplicateIndex + 1;
if (duplicateIndex == 0) {
return baseName;
}
return _duplicateName(baseName, duplicateIndex);
}
String _duplicateName(String baseName, int index) => '$baseName#$index';
String _unnamedName(int index) => '#$index';
}
enum _MemberReferenceKind {
constructor('@constructor'),
field('@field'),
getter('@getter'),
setter('@setter'),
method('@method');
final String debugName;
const _MemberReferenceKind(this.debugName);
}
enum _SerializedReferenceTag { root, library, topLevel, member, builtIn }
enum _TopLevelReferenceKind {
class_('@class', hasMembers: true),
enum_('@enum', hasMembers: true),
extension_('@extension', hasMembers: true),
extensionType('@extensionType', hasMembers: true),
mixin_('@mixin', hasMembers: true),
typeAlias('@typeAlias'),
function('@function'),
getter('@getter'),
setter('@setter'),
topLevelVariable('@topLevelVariable');
final String debugName;
final bool hasMembers;
const _TopLevelReferenceKind(this.debugName, {this.hasMembers = false});
}