blob: 43102e1b1bb08c3f83e0cafca7a5bb9d0285ee36 [file] [log] [blame]
// 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;
NameScheme(
{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}');
}
}
}
// Coverage-ignore(suite): Not run.
static String createProcedureNameForTesting(
{required ContainerName? containerName,
required ContainerType containerType,
required bool isStatic,
required ProcedureKind kind,
required String name}) {
return _createProcedureName(
containerName: containerName,
containerType: containerType,
isStatic: isStatic,
kind: kind,
name: name);
}
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 = 'get#';
break;
case ProcedureKind.Setter:
kindInfix = 'set#';
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}|${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 = [];
LibraryName(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;
ClassName(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;
FixedExtensionName(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 MemberName(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;
PublicMemberName(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;
PrivateMemberName(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;
ExtensionProcedureName(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;
ExtensionTypeConstructorName(
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}|constructor#${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;
SynthesizedFieldName(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);
}
}