Files
sdk/pkg/kernel/lib/canonical_name.dart
T
Dmitry Stefantsov ccbeebe2a6 [kernel] Add new member kind for redirecting factory constructors
New type of AST nodes RedirectingFactoryConstructor is added to Kernel.
The goal is to keep the information about redirecting factory
constructors in the class for the linking phase.

Change-Id: I7477c617fa7fd4935ceb23098ec7c056d1f286ca
Reviewed-on: https://dart-review.googlesource.com/14740
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
2017-11-28 17:36:44 +00:00

203 lines
5.8 KiB
Dart

// Copyright (c) 2016, 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.
library kernel.canonical_name;
import 'ast.dart';
/// A string sequence that identifies a library, class, or member.
///
/// Canonical names are organized in a prefix tree. Each node knows its
/// parent, children, and the AST node it is currently bound to.
///
/// The following schema specifies how the canonical name of a given object
/// is defined:
///
/// Library:
/// URI of library
///
/// Class:
/// Canonical name of enclosing library
/// Name of class
///
/// Constructor:
/// Canonical name of enclosing class or library
/// "@constructors"
/// Qualified name
///
/// Field:
/// Canonical name of enclosing class or library
/// "@fields"
/// Qualified name
///
/// Procedure that is not an accessor or factory:
/// Canonical name of enclosing class or library
/// "@methods"
/// Qualified name
///
/// Procedure that is a getter:
/// Canonical name of enclosing class or library
/// "@getters"
/// Qualified name
///
/// Procedure that is a setter:
/// Canonical name of enclosing class or library
/// "@setters"
/// Qualified name
///
/// Procedure that is a factory:
/// Canonical name of enclosing class
/// "@factories"
/// Qualified name
///
/// Qualified name:
/// if private: URI of library
/// Name text
///
/// The "qualified name" allows a member to have a name that is private to
/// a library other than the one containing that member.
class CanonicalName {
CanonicalName _parent;
CanonicalName get parent => _parent;
final String name;
CanonicalName _nonRootTop;
Map<String, CanonicalName> _children;
/// The library, class, or member bound to this name.
Reference reference;
/// Temporary index used during serialization.
int index = -1;
CanonicalName._(this._parent, this.name) {
assert(name != null);
assert(parent != null);
_nonRootTop = _parent.isRoot ? this : _parent._nonRootTop;
}
CanonicalName.root()
: _parent = null,
_nonRootTop = null,
name = '';
bool get isRoot => _parent == null;
CanonicalName get nonRootTop => _nonRootTop;
Iterable<CanonicalName> get children =>
_children?.values ?? const <CanonicalName>[];
bool hasChild(String name) {
return _children != null && _children.containsKey(name);
}
CanonicalName getChild(String name) {
var map = _children ??= <String, CanonicalName>{};
return map[name] ??= new CanonicalName._(this, name);
}
CanonicalName getChildFromUri(Uri uri) {
// Note that the Uri class caches its string representation, and all library
// URIs will be stringified for serialization anyway, so there is no
// significant cost for converting the Uri to a string here.
return getChild('$uri');
}
CanonicalName getChildFromQualifiedName(Name name) {
return name.isPrivate
? getChildFromUri(name.library.importUri).getChild(name.name)
: getChild(name.name);
}
CanonicalName getChildFromMember(Member member) {
return getChild(getMemberQualifier(member))
.getChildFromQualifiedName(member.name);
}
CanonicalName getChildFromTypedef(Typedef typedef_) {
return getChild('@typedefs').getChild(typedef_.name);
}
/// Take ownership of a child canonical name and its subtree.
///
/// The child name is removed as a child of its current parent and this name
/// becomes the new parent. Note that this moves the entire subtree rooted at
/// the child.
///
/// This method can be used to move subtrees within a canonical name tree or
/// else move them between trees. It is safe to call this method if the child
/// name is already a child of this name.
///
/// The precondition is that this name cannot have a (different) child with
/// the same name.
void adoptChild(CanonicalName child) {
if (child._parent == this) return;
if (_children != null && _children.containsKey(child.name)) {
throw 'Cannot add a child to $this because this name already has a '
'child named ${child.name}';
}
child._parent.removeChild(child.name);
child._parent = this;
if (_children == null) _children = <String, CanonicalName>{};
_children[child.name] = child;
}
void removeChild(String name) {
_children?.remove(name);
}
void bindTo(Reference target) {
if (reference == target) return;
if (reference != null) {
throw '$this is already bound';
}
if (target.canonicalName != null) {
throw 'Cannot bind $this to ${target.node}, target is already bound to '
'${target.canonicalName}';
}
target.canonicalName = this;
this.reference = target;
}
void unbind() {
if (reference == null) return;
assert(reference.canonicalName == this);
reference.canonicalName = null;
reference = null;
}
void unbindAll() {
unbind();
for (var child in children) {
child.unbindAll();
}
}
String toString() => _parent == null ? 'root' : '$parent::$name';
Reference getReference() {
return reference ??= (new Reference()..canonicalName = this);
}
static String getMemberQualifier(Member member) {
if (member is Procedure) {
if (member.isGetter) return '@getters';
if (member.isSetter) return '@setters';
if (member.isFactory) return '@factories';
return '@methods';
}
if (member is Field) {
return '@fields';
}
if (member is Constructor) {
return '@constructors';
}
if (member is RedirectingFactoryConstructor) {
return '@factories';
}
throw 'Unexpected member: $member';
}
}