Chromium Code Reviews| 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..ab37d86fca6f97141176605b5427ee02b81a0e88 |
| --- /dev/null |
| +++ b/pkg/kernel/lib/canonical_name.dart |
| @@ -0,0 +1,179 @@ |
| +// 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: |
| +/// URI 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. |
| + LinkedNode definition; |
| + |
| + /// Temporary index used during serialization. |
| + int index = -1; |
| + |
| + CanonicalName._(this.parent, this.name) { |
| + assert(name != null); |
| + } |
| + |
| + CanonicalName.root() |
| + : parent = null, |
| + name = ''; |
| + |
| + /// Used as temporary measure to get the dart2js-kernel adaptor working. |
|
ahe
2017/02/02 16:24:01
Add TODO to remove?
asgerf
2017/02/03 10:31:16
Same as previous answer.
|
| + /// |
| + /// This canonical name cannot be used in serialization but can hold in-memory |
| + /// references. |
| + CanonicalName.dummy() |
| + : parent = null, |
| + name = 'dummy'; |
| + |
| + bool get isRoot => parent == null; |
| + |
| + /// True if this is a fake canonical name object used by the dart2js/kernel |
|
ahe
2017/02/02 16:24:01
Ditto?
asgerf
2017/02/03 10:31:16
Same as previous answer.
|
| + /// adaptor. |
| + bool get isDummy => parent == null && name == 'dummy'; |
| + |
| + 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 |
| + ? getChild(name.libraryName.name).getChild(name.name) |
| + : getChild(name.name); |
| + } |
| + |
| + CanonicalName getChildFromMember(Member member) { |
| + return getChild(getMemberQualifier(member)) |
| + .getChildFromQualifiedName(member.name); |
| + } |
| + |
| + void bindTo(LinkedNode target) { |
| + if (definition == target) return; |
| + if (definition != null) { |
| + throw '$this is already bound'; |
| + } |
| + if (target.canonicalName != null) { |
| + throw 'Cannot bind $this to $target, target is already bound'; |
| + } |
| + target.canonicalName = this; |
| + this.definition = target; |
| + } |
| + |
| + void unbind() { |
| + if (definition == null) return; |
| + assert(definition.canonicalName == this); |
| + definition.canonicalName = null; |
| + definition = null; |
| + } |
| + |
| + String toString() => parent == null ? 'root' : '$parent::$name'; |
| + |
| + Library get asLibrary { |
|
ahe
2017/02/02 16:24:01
Nice!
asgerf
2017/02/03 10:31:16
Acknowledged.
|
| + if (definition == null) throw '$this is not bound'; |
| + return definition as Library; |
| + } |
| + |
| + Class get asClass { |
| + if (definition == null) throw '$this is not bound'; |
| + return definition as Class; |
| + } |
| + |
| + Member get asMember { |
| + if (definition == null) throw '$this is not bound'; |
| + return definition as Member; |
| + } |
| + |
| + Constructor get asConstructor { |
| + if (definition == null) throw '$this is not bound'; |
| + return definition as Constructor; |
| + } |
| + |
| + Procedure get asProcedure { |
| + if (definition == null) throw '$this is not bound'; |
| + return definition as Procedure; |
| + } |
| + |
| + Field get asField { |
| + if (definition == null) throw '$this is not bound'; |
| + return definition as Field; |
| + } |
| + |
| + 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'; |
| + } |
| +} |