| 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.
|
| + ///
|
| + /// 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
|
| + /// 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 {
|
| + 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';
|
| + }
|
| +}
|
|
|