| // Copyright (c) 2021, 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 'package:kernel/ast.dart'; | |
| import '../kernel/constructor_tearoff_lowering.dart'; | |
| import '../kernel/late_lowering.dart' as late_lowering; | |
| enum FieldNameType { Field, Getter, Setter, IsSetField, RepresentationField } | |
| enum ContainerType { Library, Class, ExtensionType, Extension } | |
| class NameScheme { | |
| final bool isInstanceMember; | |
| final ContainerName? containerName; | |
| final ContainerType containerType; | |
| final LibraryName libraryName; | |
| new({ | |
| required this.isInstanceMember, | |
| required this.containerName, | |
| required this.containerType, | |
| required this.libraryName, | |
| }) : assert( | |
| (containerName == null) == (containerType == ContainerType.Library), | |
| ); | |
| bool get isStatic => !isInstanceMember; | |
| bool get isExtensionMember => containerType == ContainerType.Extension; | |
| bool get isExtensionTypeMember => | |
| containerType == ContainerType.ExtensionType; | |
| // TODO(johnniwinther): Why do we need [isSynthesized] ? | |
| MemberName getFieldMemberName( | |
| FieldNameType fieldNameType, | |
| String name, { | |
| required bool isSynthesized, | |
| }) { | |
| bool hasSynthesizedName; | |
| switch (containerType) { | |
| case ContainerType.Library: | |
| case ContainerType.Class: | |
| hasSynthesizedName = isSynthesized; | |
| break; | |
| case ContainerType.ExtensionType: | |
| case ContainerType.Extension: | |
| hasSynthesizedName = true; | |
| break; | |
| } | |
| if (hasSynthesizedName) { | |
| return new SynthesizedFieldName( | |
| libraryName, | |
| containerName, | |
| containerType, | |
| fieldNameType, | |
| name, | |
| isInstanceMember: isInstanceMember, | |
| isSynthesized: isSynthesized, | |
| ); | |
| } else { | |
| return name.startsWith('_') | |
| ? new PrivateMemberName(libraryName, name) | |
| : new PublicMemberName(name); | |
| } | |
| } | |
| static String createFieldName( | |
| FieldNameType fieldNameType, | |
| String name, { | |
| required bool isInstanceMember, | |
| required ContainerName? containerName, | |
| required ContainerType containerType, | |
| bool isSynthesized = false, | |
| }) { | |
| assert( | |
| isSynthesized || fieldNameType == FieldNameType.Field, | |
| "Unexpected field name type for non-synthesized field: $fieldNameType", | |
| ); | |
| assert( | |
| (containerName == null) == (containerType == ContainerType.Library), | |
| "Missing container name for ${containerType}", | |
| ); | |
| String baseName; | |
| switch (containerType) { | |
| case ContainerType.Library: | |
| case ContainerType.Class: | |
| baseName = name; | |
| break; | |
| case ContainerType.ExtensionType: | |
| case ContainerType.Extension: | |
| baseName = "${containerName!.name}|${name}"; | |
| break; | |
| } | |
| if (!isSynthesized) { | |
| return baseName; | |
| } else { | |
| String namePrefix = late_lowering.lateFieldPrefix; | |
| if (isInstanceMember) { | |
| namePrefix = '$namePrefix${containerName!.name}#'; | |
| } | |
| switch (fieldNameType) { | |
| case FieldNameType.Field: | |
| return "$namePrefix$baseName"; | |
| case FieldNameType.Getter: | |
| return baseName; | |
| case FieldNameType.Setter: | |
| return baseName; | |
| case FieldNameType.IsSetField: | |
| return "$namePrefix$baseName${late_lowering.lateIsSetSuffix}"; | |
| case FieldNameType.RepresentationField: | |
| return name; | |
| } | |
| } | |
| } | |
| MemberName getProcedureMemberName(ProcedureKind kind, String name) { | |
| switch (containerType) { | |
| case ContainerType.Library: | |
| case ContainerType.Class: | |
| return name.startsWith('_') | |
| ? new PrivateMemberName(libraryName, name) | |
| : new PublicMemberName(name); | |
| case ContainerType.ExtensionType: | |
| case ContainerType.Extension: | |
| switch (kind) { | |
| case ProcedureKind.Method: | |
| case ProcedureKind.Getter: | |
| case ProcedureKind.Setter: | |
| case ProcedureKind.Operator: | |
| return new ExtensionProcedureName( | |
| libraryName, | |
| containerName!, | |
| containerType, | |
| kind, | |
| name, | |
| isStatic: isStatic, | |
| ); | |
| // Coverage-ignore(suite): Not run. | |
| case ProcedureKind.Factory: | |
| throw new UnsupportedError('Unexpected procedure kind ${kind}'); | |
| } | |
| } | |
| } | |
| static const String extensionTypeConstructorPrefix = 'constructor#'; | |
| static const String extensionGetterPrefix = 'get#'; | |
| static const String extensionSetterPrefix = 'set#'; | |
| static const String extensionNameDelimiter = '|'; | |
| static const String unnamedExtensionNamePrefix = '_extension#'; | |
| static String _createProcedureName({ | |
| required ContainerName? containerName, | |
| required ContainerType containerType, | |
| required bool isStatic, | |
| required ProcedureKind kind, | |
| required String name, | |
| }) { | |
| switch (containerType) { | |
| case ContainerType.Extension: | |
| case ContainerType.ExtensionType: | |
| String extensionName = containerName!.name; | |
| String kindInfix = ''; | |
| if (!isStatic) { | |
| // Instance getter and setter are converted to methods so we use an | |
| // infix to make their names unique. | |
| switch (kind) { | |
| case ProcedureKind.Getter: | |
| kindInfix = extensionGetterPrefix; | |
| break; | |
| case ProcedureKind.Setter: | |
| kindInfix = extensionSetterPrefix; | |
| break; | |
| case ProcedureKind.Method: | |
| case ProcedureKind.Operator: | |
| kindInfix = ''; | |
| break; | |
| // Coverage-ignore(suite): Not run. | |
| case ProcedureKind.Factory: | |
| throw new UnsupportedError( | |
| 'Unexpected extension method kind ${kind}', | |
| ); | |
| } | |
| } | |
| return '${extensionName}${extensionNameDelimiter}${kindInfix}${name}'; | |
| // Coverage-ignore(suite): Not run. | |
| case ContainerType.Library: | |
| case ContainerType.Class: | |
| return name; | |
| } | |
| } | |
| MemberName getConstructorMemberName(String name, {required bool isTearOff}) { | |
| switch (containerType) { | |
| case ContainerType.Library: | |
| case ContainerType.Class: | |
| if (isTearOff) { | |
| name = constructorTearOffName(name); | |
| } | |
| return name.startsWith('_') | |
| ? new PrivateMemberName(libraryName, name) | |
| : new PublicMemberName(name); | |
| case ContainerType.ExtensionType: | |
| case ContainerType.Extension: | |
| // Extension is handled here for the error case only. | |
| return new ExtensionTypeConstructorName( | |
| libraryName, | |
| containerName!, | |
| name, | |
| isTearOff: isTearOff, | |
| ); | |
| } | |
| } | |
| /// Returns the [MemberName] corresponding to the declared member, i.e. the | |
| /// name of the member without any applied lowering. | |
| MemberName getDeclaredName(String name) { | |
| // TODO(johnniwinther): Add a helper method for `isPrivate`. | |
| return name.startsWith('_') | |
| ? new PrivateMemberName(libraryName, name) | |
| : new PublicMemberName(name); | |
| } | |
| } | |
| /// The part of a member name defined by a library. | |
| /// | |
| /// This is used for creating member names together with the member builders | |
| /// while still supporting that the resulting library is computed late. | |
| class LibraryName { | |
| /// The reference to the [Library] that defines the library name scope. | |
| Reference _reference; | |
| /// [MemberName]s dependent on this library name. | |
| List<MemberName> _memberNames = []; | |
| new(this._reference); | |
| /// Registers [name] as dependent on this library name. | |
| void attachMemberName(MemberName name) { | |
| _memberNames.add(name); | |
| } | |
| /// Returns the current [Reference] used for creating [Name]s in this library | |
| /// name scope. | |
| Reference get reference => _reference; | |
| // Coverage-ignore(suite): Not run. | |
| /// Updates the [Reference] that defines the library name scope. | |
| /// | |
| /// If changed, the dependent [_memberNames] are updated accordingly. | |
| void set reference(Reference value) { | |
| if (_reference != value) { | |
| _reference = value; | |
| for (MemberName name in _memberNames) { | |
| name.updateMemberName(); | |
| } | |
| } | |
| } | |
| } | |
| /// The name of a class, extension type declaration or extension. | |
| abstract class ContainerName { | |
| /// The current name of the container. | |
| /// | |
| /// For an unnamed extension, this is initially a sentinel value, which is | |
| /// updated within the containing [Library] is composed. | |
| String get name; | |
| /// Registers [name] as dependent on this container name. | |
| void attachMemberName(MemberName name); | |
| } | |
| /// The name of a class or an extension type declaration. | |
| class ClassName extends ContainerName { | |
| final String _name; | |
| new(this._name); | |
| @override | |
| String get name => _name; | |
| @override | |
| void attachMemberName(MemberName name) {} | |
| } | |
| /// The name of an extension, either named or unnamed. | |
| /// | |
| /// This is used for creating extension member names together with the member | |
| /// builders while still supporting that the synthesized extension names for | |
| /// unnamed extensions are computed late. | |
| abstract class ExtensionName extends ContainerName { | |
| /// Updates the name for this extension. | |
| /// | |
| /// If changed, the dependent [Extension] names and [MemberName]s will be | |
| /// updated accordingly. | |
| void set name(String value); | |
| /// Returns `true` if this is the name of an unnamed extension. | |
| bool get isUnnamedExtension; | |
| /// Associates the [extension] with this extension name. | |
| /// | |
| /// When the [name] is updated, the name of the [extension] will be updated | |
| /// accordingly. | |
| void attachExtension(Extension extension); | |
| } | |
| /// The name of a named extension. | |
| class FixedExtensionName implements ExtensionName { | |
| @override | |
| final String name; | |
| new(this.name); | |
| @override | |
| bool get isUnnamedExtension => false; | |
| @override | |
| void set name(String value) { | |
| throw new UnsupportedError("Cannot change name of a fixed extension name."); | |
| } | |
| @override | |
| void attachExtension(Extension extension) { | |
| extension.name = name; | |
| } | |
| @override | |
| void attachMemberName(MemberName name) {} | |
| } | |
| /// The name of an unnamed extension. | |
| class UnnamedExtensionName implements ExtensionName { | |
| static const String unnamedExtensionSentinel = '_unnamed-extension_'; | |
| String? _name; | |
| Extension? _extension; | |
| List<MemberName> _memberNames = []; | |
| @override | |
| bool get isUnnamedExtension => true; | |
| @override | |
| String get name => _name ?? unnamedExtensionSentinel; | |
| @override | |
| void attachExtension(Extension extension) { | |
| _extension = extension; | |
| extension.name = name; | |
| } | |
| @override | |
| void set name(String name) { | |
| if (_name != name) { | |
| _name = name; | |
| _extension?.name = name; | |
| for (MemberName memberName in _memberNames) { | |
| memberName.updateMemberName(); | |
| } | |
| } | |
| } | |
| @override | |
| void attachMemberName(MemberName name) { | |
| _memberNames.add(name); | |
| } | |
| } | |
| /// The name of a [Member] which support late computation of member names needed | |
| /// for private names and names of members of unnamed extensions. | |
| /// | |
| /// Since members are built early when library vs parts haven't been resolved | |
| /// the private names of parts needed to be updated once the resulting [Library] | |
| /// node has been determined, and for members of unnamed extensions, when the | |
| /// synthesized name of the unnamed extension has been determined. | |
| abstract class MemberName { | |
| factory(LibraryName libraryName, String text) => text.startsWith('_') | |
| ? new PrivateMemberName(libraryName, text) | |
| : | |
| // Coverage-ignore(suite): Not run. | |
| new PublicMemberName(text); | |
| /// Returns the current [Name] for this member name. | |
| Name get name; | |
| /// Recomputes the [name] property and, if changed, updates dependent members | |
| /// accordingly. | |
| /// | |
| /// This is called by [LibraryName] and [UnnamedExtensionName] when these | |
| /// are updated. | |
| void updateMemberName(); | |
| /// Registers [member] has dependent of this member name and set the name | |
| /// of [member] to the current [name]. | |
| void attachMember(Member member); | |
| } | |
| /// A public member name. | |
| /// | |
| /// This never changes once created. | |
| class PublicMemberName implements MemberName { | |
| @override | |
| final Name name; | |
| new(String text) : assert(!text.startsWith('_')), name = new Name(text); | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| void updateMemberName() {} | |
| @override | |
| void attachMember(Member member) { | |
| member.name = name; | |
| } | |
| } | |
| /// A member name that can be updated. | |
| abstract class UpdatableMemberName implements MemberName { | |
| Name? _name; | |
| List<Member> _members = []; | |
| Name _createName(); | |
| @override | |
| Name get name => _name ??= _createName(); | |
| @override | |
| void attachMember(Member member) { | |
| member.name = name; | |
| _members.add(member); | |
| } | |
| @override | |
| void updateMemberName() { | |
| Name name = _createName(); | |
| if (_name != name) { | |
| for (Member member in _members) { | |
| member.name = name; | |
| } | |
| } | |
| } | |
| } | |
| /// A private member name. | |
| /// | |
| /// This depends on a [LibraryName] and is updated when the reference of the | |
| /// [LibraryName] is changed. | |
| class PrivateMemberName extends UpdatableMemberName { | |
| final LibraryName _libraryName; | |
| final String _text; | |
| new(this._libraryName, this._text) : assert(_text.startsWith('_')) { | |
| _libraryName.attachMemberName(this); | |
| } | |
| @override | |
| Name _createName() { | |
| return new Name.byReference(_text, _libraryName.reference); | |
| } | |
| } | |
| /// A name for an extension procedure. | |
| /// | |
| /// This depends on a [LibraryName] and an [ContainerName] and is updated the | |
| /// reference of the [LibraryName] or the name of the [ContainerName] is | |
| /// changed. | |
| class ExtensionProcedureName extends UpdatableMemberName { | |
| final LibraryName _libraryName; | |
| final ContainerName _containerName; | |
| final ContainerType _containerType; | |
| final ProcedureKind _kind; | |
| final bool isStatic; | |
| final String _text; | |
| new( | |
| this._libraryName, | |
| this._containerName, | |
| this._containerType, | |
| this._kind, | |
| this._text, { | |
| required this.isStatic, | |
| }) { | |
| _libraryName.attachMemberName(this); | |
| _containerName.attachMemberName(this); | |
| } | |
| @override | |
| Name _createName() { | |
| return new Name.byReference( | |
| NameScheme._createProcedureName( | |
| containerName: _containerName, | |
| containerType: _containerType, | |
| isStatic: isStatic, | |
| kind: _kind, | |
| name: _text, | |
| ), | |
| _libraryName.reference, | |
| ); | |
| } | |
| } | |
| /// A name for an extension type constructor. | |
| /// | |
| /// This depends on a [LibraryName] and an [ContainerName] and is updated the | |
| /// reference of the [LibraryName] or the name of the [ContainerName] is | |
| /// changed. | |
| class ExtensionTypeConstructorName extends UpdatableMemberName { | |
| final LibraryName _libraryName; | |
| final ContainerName _containerName; | |
| final bool isTearOff; | |
| final String _text; | |
| new( | |
| this._libraryName, | |
| this._containerName, | |
| this._text, { | |
| required this.isTearOff, | |
| }) { | |
| _libraryName.attachMemberName(this); | |
| _containerName.attachMemberName(this); | |
| } | |
| @override | |
| Name _createName() { | |
| String className = _containerName.name; | |
| // Constructors and tear-offs are converted to methods so we use an | |
| // infix to make their names unique. | |
| String name; | |
| if (isTearOff) { | |
| name = constructorTearOffName(_text); | |
| } else { | |
| name = _text; | |
| } | |
| return new Name.byReference( | |
| '${className}' | |
| '${NameScheme.extensionNameDelimiter}' | |
| '${NameScheme.extensionTypeConstructorPrefix}' | |
| '${name}', | |
| _libraryName.reference, | |
| ); | |
| } | |
| } | |
| /// A name of a synthesized field. | |
| /// | |
| /// This depends on a [LibraryName] and an [ExtensionName] and is updated the | |
| /// reference of the [LibraryName] or the name of the [ExtensionName] is | |
| /// changed. | |
| class SynthesizedFieldName extends UpdatableMemberName { | |
| final LibraryName _libraryName; | |
| final ContainerName? _containerName; | |
| final ContainerType _containerType; | |
| final FieldNameType _type; | |
| final bool isInstanceMember; | |
| final bool isSynthesized; | |
| final String _text; | |
| new( | |
| this._libraryName, | |
| this._containerName, | |
| this._containerType, | |
| this._type, | |
| this._text, { | |
| required this.isInstanceMember, | |
| required this.isSynthesized, | |
| }) { | |
| _libraryName.attachMemberName(this); | |
| _containerName?.attachMemberName(this); | |
| } | |
| @override | |
| Name _createName() { | |
| return new Name.byReference( | |
| NameScheme.createFieldName( | |
| _type, | |
| _text, | |
| isInstanceMember: isInstanceMember, | |
| containerName: _containerName, | |
| containerType: _containerType, | |
| isSynthesized: isSynthesized, | |
| ), | |
| _libraryName.reference, | |
| ); | |
| } | |
| } |