Index: pkg/kernel/lib/canonical_name.dart |
diff --git a/pkg/kernel/lib/canonical_name.dart b/pkg/kernel/lib/canonical_name.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7bf05dc4507ccf12ad724daf9d10de4e9a5a5f29 |
--- /dev/null |
+++ b/pkg/kernel/lib/canonical_name.dart |
@@ -0,0 +1,149 @@ |
+// 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: |
+/// 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 |
+/// |
+/// 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 { |
+ final CanonicalName parent; |
+ final String name; |
+ |
+ 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); |
+ } |
+ |
+ CanonicalName.root() |
+ : parent = null, |
+ name = ''; |
+ |
+ bool get isRoot => parent == null; |
+ |
+ Iterable<CanonicalName> get children => |
+ _children?.values ?? const <CanonicalName>[]; |
+ |
+ 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); |
+ } |
+ |
+ 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'; |
+ return '@methods'; |
+ } |
+ if (member is Field) { |
+ return '@fields'; |
+ } |
+ if (member is Constructor) { |
+ return '@constructors'; |
+ } |
+ throw 'Unexpected member: $member'; |
+ } |
+} |