blob: b5490985d4976757387a71ef623c199f30bb9801 [file] [log] [blame]
// Copyright (c) 2015, 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.
part of js_backend.namer;
abstract class _NamerName extends jsAst.Name {
int get _kind;
@override
_NamerName withSourceInformation(
jsAst.JavaScriptNodeSourceInformation newSourceInformation) {
if (sourceInformation == newSourceInformation) return this;
final name = this; // Variable needed for type promotion in next line.
final underlying = name is _NameReference ? name._target : this;
return _NameReference(underlying, newSourceInformation);
}
int _compareSameKind(covariant _NamerName);
@override
String toString() {
if (DEBUG_MODE) {
return 'Name($key)';
}
throw UnsupportedError("Cannot convert a name to a string");
}
}
enum _NamerNameKinds { StringBacked, Getter, Setter, Async, Compound, Token }
/// An arbitrary but stable sorting comparison method.
int compareNames(jsAst.Name aName, jsAst.Name bName) {
_NamerName dereference(jsAst.Name name) {
if (name is ModularName) return dereference(name.value);
if (name is _NameReference) return dereference(name._target);
if (name is _NamerName) return name;
throw UnsupportedError('Cannot find underlying _NamerName: $name');
}
_NamerName a = dereference(aName);
_NamerName b = dereference(bName);
int aKind = a._kind;
int bKind = b._kind;
if (bKind != aKind) return bKind - aKind;
return a._compareSameKind(b);
}
class StringBackedName extends _NamerName {
@override
final String name;
@override
int get _kind => _NamerNameKinds.StringBacked.index;
StringBackedName(this.name);
@override
String get key => name;
@override
operator ==(Object other) {
if (other is _NameReference) return this == other._target;
if (identical(this, other)) return true;
return other is StringBackedName && name == other.name;
}
@override
int get hashCode => name.hashCode;
@override
int _compareSameKind(StringBackedName other) {
return name.compareTo(other.name);
}
}
abstract class _PrefixedName extends _NamerName implements jsAst.AstContainer {
final jsAst.Name prefix;
final jsAst.Name base;
@override
int get _kind;
@override
Iterable<jsAst.Node> get containedNodes => [prefix, base];
_PrefixedName(this.prefix, this.base);
@override
String get name => prefix.name + base.name;
@override
String get key => prefix.key + base.key;
@override
bool operator ==(Object other) {
if (other is _NameReference) return this == other._target;
if (identical(this, other)) return true;
return other is _PrefixedName &&
base == other.base &&
prefix == other.prefix;
}
@override
int get hashCode => base.hashCode * 13 + prefix.hashCode;
@override
bool get isFinalized => prefix.isFinalized && base.isFinalized;
@override
int _compareSameKind(_PrefixedName other) {
int result = compareNames(prefix, other.prefix);
if (result == 0) {
result = compareNames(base, other.base);
}
return result;
}
}
class GetterName extends _PrefixedName {
@override
int get _kind => _NamerNameKinds.Getter.index;
GetterName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
}
class SetterName extends _PrefixedName {
@override
int get _kind => _NamerNameKinds.Setter.index;
SetterName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
}
class AsyncName extends _PrefixedName {
@override
int get _kind => _NamerNameKinds.Async.index;
AsyncName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
@override
bool get allowRename => true;
}
class CompoundName extends _NamerName implements jsAst.AstContainer {
final List<_NamerName> _parts;
@override
int get _kind => _NamerNameKinds.Compound.index;
String _cachedName;
int _cachedHashCode = -1;
@override
Iterable<jsAst.Node> get containedNodes => _parts;
CompoundName(this._parts);
CompoundName.from(List<jsAst.Name> parts) : this([...parts]);
@override
bool get isFinalized => _parts.every((name) => name.isFinalized);
@override
String get name {
return _cachedName ??= _parts.map((jsAst.Name name) => name.name).join();
}
@override
String get key => _parts.map((_NamerName name) => name.key).join();
@override
bool operator ==(Object other) {
if (other is _NameReference) return this == other._target;
if (identical(this, other)) return true;
if (other is CompoundName) {
if (other._parts.length != _parts.length) return false;
for (int i = 0; i < _parts.length; ++i) {
if (_parts[i] != other._parts[i]) return false;
}
return true;
}
return false;
}
@override
int get hashCode {
if (_cachedHashCode < 0) {
_cachedHashCode = 0;
for (jsAst.Name name in _parts) {
_cachedHashCode = (_cachedHashCode * 17 + name.hashCode) & 0x7fffffff;
}
}
return _cachedHashCode;
}
@override
int _compareSameKind(CompoundName other) {
int result = _parts.length.compareTo(other._parts.length);
for (int pos = 0; result == 0 && pos < _parts.length; pos++) {
result = compareNames(_parts[pos], other._parts[pos]);
}
return result;
}
}
class TokenName extends _NamerName implements jsAst.ReferenceCountedAstNode {
@override
int get _kind => _NamerNameKinds.Token.index;
String _name;
@override
final String key;
final TokenScope _scope;
int _rc = 0;
TokenName(this._scope, this.key);
@override
bool get isFinalized => _name != null;
@override
String get name {
assert(isFinalized, "TokenName($key) has not been finalized.");
return _name;
}
@override
int _compareSameKind(TokenName other) {
return key.compareTo(other.key);
}
@override
void markSeen(jsAst.TokenCounter counter) => _rc++;
@override
bool operator ==(Object other) {
if (other is _NameReference) return this == other._target;
if (identical(this, other)) return true;
return false;
}
@override
int get hashCode => super.hashCode;
void finalize() {
assert(
!isFinalized,
failedAt(NO_LOCATION_SPANNABLE,
"TokenName($key)=$_name has already been finalized."));
_name = _scope.getNextName();
}
}
/// A [_NameReference] behaves like the underlying (target) [_NamerName] but
/// carries its own [JavaScriptNodeSourceInformation].
// TODO(sra): See if this functionality can be moved into js_ast.
class _NameReference extends _NamerName implements jsAst.AstContainer {
final _NamerName _target;
final jsAst.JavaScriptNodeSourceInformation _sourceInformation;
_NameReference(this._target, this._sourceInformation);
@override
jsAst.JavaScriptNodeSourceInformation get sourceInformation =>
_sourceInformation;
@override
int get _kind => _target._kind;
@override
String get key => _target.key;
@override
Iterable<jsAst.Node> get containedNodes => [_target];
@override
String get name => _target.name;
@override
int _compareSameKind(_NameReference other) {
throw StateError('Should have been dereferenced: $this');
}
@override
bool get isFinalized => _target.isFinalized;
@override
bool operator ==(Object other) => _target == other;
@override
int get hashCode => _target.hashCode;
}