blob: a7ddbb03bd91dbda5eafd2af2d45e2c0c780f12d [file] [log] [blame]
// Copyright (c) 2012, 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 universe;
import '../elements/elements.dart';
import '../dart2jslib.dart';
import '../dart_types.dart';
import '../types/types.dart';
import '../tree/tree.dart';
import '../util/util.dart';
part 'function_set.dart';
part 'side_effects.dart';
class Universe {
/// The set of all directly instantiated classes, that is, classes with a
/// generative constructor that has been called directly and not only through
/// a super-call.
///
/// Invariant: Elements are declaration elements.
// TODO(johnniwinther): [_directlyInstantiatedClasses] and
// [_instantiatedTypes] sets should be merged.
final Set<ClassElement> _directlyInstantiatedClasses =
new Set<ClassElement>();
/// The set of all directly instantiated types, that is, the types of the
/// directly instantiated classes.
///
/// See [_directlyInstantiatedClasses].
final Set<DartType> _instantiatedTypes = new Set<DartType>();
/// The set of all instantiated classes, either directly, as superclasses or
/// as supertypes.
///
/// Invariant: Elements are declaration elements.
final Set<ClassElement> _allInstantiatedClasses = new Set<ClassElement>();
/**
* Documentation wanted -- johnniwinther
*
* Invariant: Elements are declaration elements.
*/
final Set<FunctionElement> staticFunctionsNeedingGetter =
new Set<FunctionElement>();
final Set<FunctionElement> methodsNeedingSuperGetter =
new Set<FunctionElement>();
final Map<String, Set<Selector>> invokedNames =
new Map<String, Set<Selector>>();
final Map<String, Set<Selector>> invokedGetters =
new Map<String, Set<Selector>>();
final Map<String, Set<Selector>> invokedSetters =
new Map<String, Set<Selector>>();
/**
* Fields accessed. Currently only the codegen knows this
* information. The resolver is too conservative when seeing a
* getter and only registers an invoked getter.
*/
final Set<Element> fieldGetters = new Set<Element>();
/**
* Fields set. See comment in [fieldGetters].
*/
final Set<Element> fieldSetters = new Set<Element>();
final Set<DartType> isChecks = new Set<DartType>();
/**
* Set of (live) [:call:] methods whose signatures reference type variables.
*
* A live [:call:] method is one whose enclosing class has been instantiated.
*/
final Set<Element> callMethodsWithFreeTypeVariables = new Set<Element>();
/**
* Set of (live) local functions (closures) whose signatures reference type
* variables.
*
* A live function is one whose enclosing member function has been enqueued.
*/
final Set<Element> closuresWithFreeTypeVariables = new Set<Element>();
/**
* Set of all closures in the program. Used by the mirror tracking system
* to find all live closure instances.
*/
final Set<LocalFunctionElement> allClosures = new Set<LocalFunctionElement>();
/**
* Set of methods in instantiated classes that are potentially
* closurized.
*/
final Set<Element> closurizedMembers = new Set<Element>();
/// All directly instantiated classes, that is, classes with a generative
/// constructor that has been called directly and not only through a
/// super-call.
// TODO(johnniwinther): Improve semantic precision.
Iterable<ClassElement> get directlyInstantiatedClasses {
return _directlyInstantiatedClasses;
}
/// All instantiated classes, either directly, as superclasses or as
/// supertypes.
// TODO(johnniwinther): Improve semantic precision.
Iterable<ClassElement> get allInstantiatedClasses {
return _allInstantiatedClasses;
}
/// All directly instantiated types, that is, the types of the directly
/// instantiated classes.
///
/// See [directlyInstantiatedClasses].
// TODO(johnniwinther): Improve semantic precision.
Iterable<DartType> get instantiatedTypes => _instantiatedTypes;
/// Returns `true` if [cls] is considered to be instantiated, either directly,
/// through subclasses or through subtypes. The latter case only contains
/// spurious information from instatiations through factory constructors and
/// mixins.
// TODO(johnniwinther): Improve semantic precision.
bool isInstantiated(ClassElement cls) {
return _allInstantiatedClasses.contains(cls);
}
/// Register [type] as (directly) instantiated.
///
/// If [byMirrors] is `true`, the instantiation is through mirrors.
// TODO(johnniwinther): Fully enforce the separation between exact, through
// subclass and through subtype instantiated types/classes.
// TODO(johnniwinther): Support unknown type arguments for generic types.
void registerTypeInstantiation(InterfaceType type,
{bool byMirrors: false}) {
_instantiatedTypes.add(type);
ClassElement cls = type.element;
if (!cls.isAbstract
// We can't use the closed-world assumption with native abstract
// classes; a native abstract class may have non-abstract subclasses
// not declared to the program. Instances of these classes are
// indistinguishable from the abstract class.
|| cls.isNative
// Likewise, if this registration comes from the mirror system,
// all bets are off.
// TODO(herhut): Track classes required by mirrors seperately.
|| byMirrors) {
_directlyInstantiatedClasses.add(cls);
}
// TODO(johnniwinther): Replace this by separate more specific mappings.
if (!_allInstantiatedClasses.add(cls)) return;
cls.allSupertypes.forEach((InterfaceType supertype) {
_allInstantiatedClasses.add(supertype.element);
});
}
bool hasMatchingSelector(Set<Selector> selectors,
Element member,
World world) {
if (selectors == null) return false;
for (Selector selector in selectors) {
if (selector.appliesUnnamed(member, world)) return true;
}
return false;
}
bool hasInvocation(Element member, World world) {
return hasMatchingSelector(invokedNames[member.name], member, world);
}
bool hasInvokedGetter(Element member, World world) {
return hasMatchingSelector(invokedGetters[member.name], member, world);
}
bool hasInvokedSetter(Element member, World world) {
return hasMatchingSelector(invokedSetters[member.name], member, world);
}
DartType registerIsCheck(DartType type, Compiler compiler) {
type = type.unalias(compiler);
// Even in checked mode, type annotations for return type and argument
// types do not imply type checks, so there should never be a check
// against the type variable of a typedef.
isChecks.add(type);
return type;
}
void forgetElement(Element element, Compiler compiler) {
allClosures.remove(element);
slowDirectlyNestedClosures(element).forEach(compiler.forgetElement);
closurizedMembers.remove(element);
fieldSetters.remove(element);
fieldGetters.remove(element);
_directlyInstantiatedClasses.remove(element);
_allInstantiatedClasses.remove(element);
if (element is ClassElement) {
assert(invariant(
element, element.thisType.isRaw,
message: 'Generic classes not supported (${element.thisType}).'));
_instantiatedTypes
..remove(element.rawType)
..remove(element.thisType);
}
}
// TODO(ahe): Replace this method with something that is O(1), for example,
// by using a map.
List<LocalFunctionElement> slowDirectlyNestedClosures(Element element) {
// Return new list to guard against concurrent modifications.
return new List<LocalFunctionElement>.from(
allClosures.where((LocalFunctionElement closure) {
return closure.executableContext == element;
}));
}
}
class SelectorKind {
final String name;
final int hashCode;
const SelectorKind(this.name, this.hashCode);
static const SelectorKind GETTER = const SelectorKind('getter', 0);
static const SelectorKind SETTER = const SelectorKind('setter', 1);
static const SelectorKind CALL = const SelectorKind('call', 2);
static const SelectorKind OPERATOR = const SelectorKind('operator', 3);
static const SelectorKind INDEX = const SelectorKind('index', 4);
String toString() => name;
}
class Selector {
final SelectorKind kind;
final String name;
final LibraryElement library; // Library is null for non-private selectors.
// The numbers of arguments of the selector. Includes named arguments.
final int argumentCount;
final List<String> namedArguments;
final List<String> _orderedNamedArguments;
final int hashCode;
static const String INDEX_NAME ="[]";
static const String INDEX_SET_NAME = "[]=";
static const String CALL_NAME = Compiler.CALL_OPERATOR_NAME;
Selector.internal(this.kind,
this.name,
this.library,
this.argumentCount,
this.namedArguments,
this._orderedNamedArguments,
this.hashCode) {
assert(kind == SelectorKind.INDEX
|| (name != INDEX_NAME && name != INDEX_SET_NAME));
assert(kind == SelectorKind.OPERATOR
|| kind == SelectorKind.INDEX
|| !Elements.isOperatorName(name));
assert(kind == SelectorKind.CALL
|| kind == SelectorKind.GETTER
|| kind == SelectorKind.SETTER
|| Elements.isOperatorName(name));
assert(!isPrivateName(name) || library != null);
}
static Map<int, List<Selector>> canonicalizedValues =
new Map<int, List<Selector>>();
factory Selector(SelectorKind kind,
String name,
LibraryElement library,
int argumentCount,
[List<String> namedArguments]) {
if (!isPrivateName(name)) library = null;
if (namedArguments == null) namedArguments = const <String>[];
int hashCode = computeHashCode(
kind, name, library, argumentCount, namedArguments);
List<Selector> list = canonicalizedValues.putIfAbsent(hashCode,
() => <Selector>[]);
for (int i = 0; i < list.length; i++) {
Selector existing = list[i];
if (existing.match(kind, name, library, argumentCount, namedArguments)) {
assert(existing.hashCode == hashCode);
assert(existing.mask == null);
return existing;
}
}
List<String> orderedNamedArguments = namedArguments.isEmpty
? const <String>[]
: <String>[];
Selector result = new Selector.internal(
kind, name, library, argumentCount,
namedArguments, orderedNamedArguments,
hashCode);
list.add(result);
return result;
}
factory Selector.fromElement(Element element) {
String name = element.name;
if (element.isFunction) {
if (name == '[]') {
return new Selector.index();
} else if (name == '[]=') {
return new Selector.indexSet();
}
FunctionSignature signature =
element.asFunctionElement().functionSignature;
int arity = signature.parameterCount;
List<String> namedArguments = null;
if (signature.optionalParametersAreNamed) {
namedArguments =
signature.orderedOptionalParameters.map((e) => e.name).toList();
}
if (element.isOperator) {
// Operators cannot have named arguments, however, that doesn't prevent
// a user from declaring such an operator.
return new Selector(
SelectorKind.OPERATOR, name, null, arity, namedArguments);
} else {
return new Selector.call(
name, element.library, arity, namedArguments);
}
} else if (element.isSetter) {
return new Selector.setter(name, element.library);
} else if (element.isGetter) {
return new Selector.getter(name, element.library);
} else if (element.isField) {
return new Selector.getter(name, element.library);
} else if (element.isConstructor) {
return new Selector.callConstructor(name, element.library);
} else {
throw new SpannableAssertionFailure(
element, "Can't get selector from $element");
}
}
factory Selector.getter(String name, LibraryElement library)
=> new Selector(SelectorKind.GETTER, name, library, 0);
factory Selector.getterFrom(Selector selector)
=> new Selector(SelectorKind.GETTER, selector.name, selector.library, 0);
factory Selector.setter(String name, LibraryElement library)
=> new Selector(SelectorKind.SETTER, name, library, 1);
factory Selector.unaryOperator(String name)
=> new Selector(SelectorKind.OPERATOR,
Elements.constructOperatorName(name, true),
null, 0);
factory Selector.binaryOperator(String name)
=> new Selector(SelectorKind.OPERATOR,
Elements.constructOperatorName(name, false),
null, 1);
factory Selector.index()
=> new Selector(SelectorKind.INDEX,
Elements.constructOperatorName(INDEX_NAME, false),
null, 1);
factory Selector.indexSet()
=> new Selector(SelectorKind.INDEX,
Elements.constructOperatorName(INDEX_SET_NAME, false),
null, 2);
factory Selector.call(String name,
LibraryElement library,
int arity,
[List<String> namedArguments])
=> new Selector(SelectorKind.CALL, name, library, arity, namedArguments);
factory Selector.callClosure(int arity, [List<String> namedArguments])
=> new Selector(SelectorKind.CALL, CALL_NAME, null,
arity, namedArguments);
factory Selector.callClosureFrom(Selector selector)
=> new Selector(SelectorKind.CALL, CALL_NAME, null,
selector.argumentCount, selector.namedArguments);
factory Selector.callConstructor(String name, LibraryElement library,
[int arity = 0,
List<String> namedArguments])
=> new Selector(SelectorKind.CALL, name, library,
arity, namedArguments);
factory Selector.callDefaultConstructor()
=> new Selector(SelectorKind.CALL, "", null, 0);
bool get isGetter => identical(kind, SelectorKind.GETTER);
bool get isSetter => identical(kind, SelectorKind.SETTER);
bool get isCall => identical(kind, SelectorKind.CALL);
bool get isClosureCall {
String callName = Compiler.CALL_OPERATOR_NAME;
return isCall && name == callName;
}
bool get isIndex => identical(kind, SelectorKind.INDEX) && argumentCount == 1;
bool get isIndexSet => identical(kind, SelectorKind.INDEX) && argumentCount == 2;
bool get isOperator => identical(kind, SelectorKind.OPERATOR);
bool get isUnaryOperator => isOperator && argumentCount == 0;
/** Check whether this is a call to 'assert'. */
bool get isAssert => isCall && identical(name, "assert");
int get namedArgumentCount => namedArguments.length;
int get positionalArgumentCount => argumentCount - namedArgumentCount;
bool get hasExactMask => false;
TypeMask get mask => null;
Selector get asUntyped => this;
/**
* The member name for invocation mirrors created from this selector.
*/
String get invocationMirrorMemberName =>
isSetter ? '$name=' : name;
int get invocationMirrorKind {
const int METHOD = 0;
const int GETTER = 1;
const int SETTER = 2;
int kind = METHOD;
if (isGetter) {
kind = GETTER;
} else if (isSetter) {
kind = SETTER;
}
return kind;
}
bool appliesUnnamed(Element element, World world) {
assert(sameNameHack(element, world));
return appliesUntyped(element, world);
}
bool appliesUntyped(Element element, World world) {
assert(sameNameHack(element, world));
if (Elements.isUnresolved(element)) return false;
if (isPrivateName(name) && library != element.library) return false;
if (world.isForeign(element)) return true;
if (element.isSetter) return isSetter;
if (element.isGetter) return isGetter || isCall;
if (element.isField) {
return isSetter
? !element.isFinal && !element.isConst
: isGetter || isCall;
}
if (isGetter) return true;
if (isSetter) return false;
return signatureApplies(element);
}
bool signatureApplies(FunctionElement function) {
FunctionSignature parameters = function.functionSignature;
if (argumentCount > parameters.parameterCount) return false;
int requiredParameterCount = parameters.requiredParameterCount;
int optionalParameterCount = parameters.optionalParameterCount;
if (positionalArgumentCount < requiredParameterCount) return false;
if (!parameters.optionalParametersAreNamed) {
// We have already checked that the number of arguments are
// not greater than the number of parameters. Therefore the
// number of positional arguments are not greater than the
// number of parameters.
assert(positionalArgumentCount <= parameters.parameterCount);
return namedArguments.isEmpty;
} else {
if (positionalArgumentCount > requiredParameterCount) return false;
assert(positionalArgumentCount == requiredParameterCount);
if (namedArgumentCount > optionalParameterCount) return false;
Set<String> nameSet = new Set<String>();
parameters.optionalParameters.forEach((Element element) {
nameSet.add(element.name);
});
for (String name in namedArguments) {
if (!nameSet.contains(name)) return false;
// TODO(5213): By removing from the set we are checking
// that we are not passing the name twice. We should have this
// check in the resolver also.
nameSet.remove(name);
}
return true;
}
}
bool sameNameHack(Element element, World world) {
// TODO(ngeoffray): Remove workaround checks.
return element.isConstructor ||
name == element.name ||
name == 'assert' && world.isAssertMethod(element);
}
bool applies(Element element, World world) {
if (!sameNameHack(element, world)) return false;
return appliesUnnamed(element, world);
}
/**
* Returns a `List` with the evaluated arguments in the normalized order.
*
* [compileDefaultValue] is a function that returns a compiled constant
* of an optional argument that is not in [compiledArguments].
*
* Precondition: `this.applies(element, world)`.
*
* Invariant: [element] must be the implementation element.
*/
/*<S, T>*/ List/*<T>*/ makeArgumentsList(
FunctionElement element,
List/*<T>*/ compiledArguments,
/*T*/ compileDefaultValue(ParameterElement element)) {
assert(invariant(element, element.isImplementation));
List/*<T>*/ result = new List();
FunctionSignature parameters = element.functionSignature;
int i = 0;
parameters.forEachRequiredParameter((ParameterElement element) {
result.add(compiledArguments[i]);
++i;
});
if (!parameters.optionalParametersAreNamed) {
parameters.forEachOptionalParameter((ParameterElement element) {
if (i < compiledArguments.length) {
result.add(compiledArguments[i]);
++i;
} else {
result.add(compileDefaultValue(element));
}
});
} else {
int offset = i;
// Iterate over the optional parameters of the signature, and try to
// find them in [compiledNamedArguments]. If found, we use the
// value in the temporary list, otherwise the default value.
parameters.orderedOptionalParameters
.forEach((ParameterElement element) {
int foundIndex = namedArguments.indexOf(element.name);
if (foundIndex != -1) {
result.add(compiledArguments[offset + foundIndex]);
} else {
result.add(compileDefaultValue(element));
}
});
}
return result;
}
/// This is a version of [makeArgumentsList] that works for a `Link`
/// representation of arguments.
/*<T>*/ List/*<T>*/ makeArgumentsList2(
Link<Node> arguments,
FunctionElement element,
/*T*/ compileArgument(Node argument),
/*T*/ compileDefaultValue(ParameterElement element)) {
assert(invariant(element, element.isImplementation));
List/*<T>*/ result = new List();
FunctionSignature parameters = element.functionSignature;
parameters.forEachRequiredParameter((ParameterElement element) {
result.add(compileArgument(arguments.head));
arguments = arguments.tail;
});
if (!parameters.optionalParametersAreNamed) {
parameters.forEachOptionalParameter((ParameterElement element) {
if (!arguments.isEmpty) {
result.add(compileArgument(arguments.head));
arguments = arguments.tail;
} else {
result.add(compileDefaultValue(element));
}
});
} else {
// Visit named arguments and add them into a temporary list.
List compiledNamedArguments = [];
for (; !arguments.isEmpty; arguments = arguments.tail) {
NamedArgument namedArgument = arguments.head;
compiledNamedArguments.add(compileArgument(namedArgument.expression));
}
// Iterate over the optional parameters of the signature, and try to
// find them in [compiledNamedArguments]. If found, we use the
// value in the temporary list, otherwise the default value.
parameters.orderedOptionalParameters.forEach((ParameterElement element) {
int foundIndex = namedArguments.indexOf(element.name);
if (foundIndex != -1) {
result.add(compiledNamedArguments[foundIndex]);
} else {
result.add(compileDefaultValue(element));
}
});
}
return result;
}
/**
* Fills [list] with the arguments in the order expected by
* [callee], and where [caller] is a synthesized element
*
* [compileArgument] is a function that returns a compiled version
* of a parameter of [callee].
*
* [compileConstant] is a function that returns a compiled constant
* of an optional argument that is not in the parameters of [callee].
*
* Returns [:true:] if the signature of the [caller] matches the
* signature of the [callee], [:false:] otherwise.
*/
static bool addForwardingElementArgumentsToList(
FunctionElement caller,
List list,
FunctionElement callee,
compileArgument(Element element),
compileConstant(Element element),
World world) {
FunctionSignature signature = caller.functionSignature;
Map mapping = new Map();
// TODO(ngeoffray): This is a hack that fakes up AST nodes, so
// that we can call [addArgumentsToList].
Link computeCallNodesFromParameters() {
LinkBuilder builder = new LinkBuilder();
signature.forEachRequiredParameter((ParameterElement element) {
Node node = element.node;
mapping[node] = element;
builder.addLast(node);
});
if (signature.optionalParametersAreNamed) {
signature.forEachOptionalParameter((ParameterElement element) {
mapping[element.initializer] = element;
builder.addLast(new NamedArgument(null, null, element.initializer));
});
} else {
signature.forEachOptionalParameter((ParameterElement element) {
Node node = element.node;
mapping[node] = element;
builder.addLast(node);
});
}
return builder.toLink();
}
internalCompileArgument(Node node) {
return compileArgument(mapping[node]);
}
Link<Node> nodes = computeCallNodesFromParameters();
// Synthesize a selector for the call.
// TODO(ngeoffray): Should the resolver do it instead?
List<String> namedParameters;
if (signature.optionalParametersAreNamed) {
namedParameters =
signature.optionalParameters.mapToList((e) => e.name);
}
Selector selector = new Selector.call(callee.name,
caller.library,
signature.parameterCount,
namedParameters);
if (!selector.applies(callee, world)) return false;
list.addAll(selector.makeArgumentsList2(nodes,
callee,
internalCompileArgument,
compileConstant));
return true;
}
static bool sameNames(List<String> first, List<String> second) {
for (int i = 0; i < first.length; i++) {
if (first[i] != second[i]) return false;
}
return true;
}
bool match(SelectorKind kind,
String name,
LibraryElement library,
int argumentCount,
List<String> namedArguments) {
return this.kind == kind
&& this.name == name
&& identical(this.library, library)
&& this.argumentCount == argumentCount
&& this.namedArguments.length == namedArguments.length
&& sameNames(this.namedArguments, namedArguments);
}
static int computeHashCode(SelectorKind kind,
String name,
LibraryElement library,
int argumentCount,
List<String> namedArguments) {
// Add bits from name and kind.
int hash = mixHashCodeBits(name.hashCode, kind.hashCode);
// Add bits from the library.
if (library != null) hash = mixHashCodeBits(hash, library.hashCode);
// Add bits from the unnamed arguments.
hash = mixHashCodeBits(hash, argumentCount);
// Add bits from the named arguments.
int named = namedArguments.length;
hash = mixHashCodeBits(hash, named);
for (int i = 0; i < named; i++) {
hash = mixHashCodeBits(hash, namedArguments[i].hashCode);
}
return hash;
}
// TODO(kasperl): Move this out so it becomes useful in other places too?
static int mixHashCodeBits(int existing, int value) {
// Spread the bits of value. Try to stay in the 30-bit range to
// avoid overflowing into a more expensive integer representation.
int h = value & 0x1fffffff;
h += ((h & 0x3fff) << 15) ^ 0x1fffcd7d;
h ^= (h >> 10);
h += ((h & 0x3ffffff) << 3);
h ^= (h >> 6);
h += ((h & 0x7ffffff) << 2) + ((h & 0x7fff) << 14);
h ^= (h >> 16);
// Combine the two hash values.
int high = existing >> 15;
int low = existing & 0x7fff;
return ((high * 13) ^ (low * 997) ^ h) & SMI_MASK;
}
List<String> getOrderedNamedArguments() {
if (namedArguments.isEmpty) return namedArguments;
if (!_orderedNamedArguments.isEmpty) return _orderedNamedArguments;
_orderedNamedArguments.addAll(namedArguments);
_orderedNamedArguments.sort((String first, String second) {
return first.compareTo(second);
});
return _orderedNamedArguments;
}
String namedArgumentsToString() {
if (namedArgumentCount > 0) {
StringBuffer result = new StringBuffer();
for (int i = 0; i < namedArgumentCount; i++) {
if (i != 0) result.write(', ');
result.write(namedArguments[i]);
}
return "[$result]";
}
return '';
}
String toString() {
String named = '';
String type = '';
if (namedArgumentCount > 0) named = ', named=${namedArgumentsToString()}';
if (mask != null) type = ', mask=$mask';
return 'Selector($kind, $name, '
'arity=$argumentCount$named$type)';
}
Selector extendIfReachesAll(Compiler compiler) {
return new TypedSelector(
compiler.typesTask.dynamicType, this, compiler.world);
}
Selector toCallSelector() => new Selector.callClosureFrom(this);
}
class TypedSelector extends Selector {
final Selector asUntyped;
final TypeMask mask;
TypedSelector.internal(this.mask, Selector selector, int hashCode)
: asUntyped = selector,
super.internal(selector.kind,
selector.name,
selector.library,
selector.argumentCount,
selector.namedArguments,
selector._orderedNamedArguments,
hashCode) {
assert(mask != null);
assert(asUntyped.mask == null);
}
factory TypedSelector(TypeMask mask, Selector selector, World world) {
if (!world.hasClosedWorldAssumption) {
// TODO(johnniwinther): Improve use of TypedSelector in an open world.
bool isNullable = mask.isNullable;
mask = world.compiler.typesTask.dynamicType;
if (isNullable) {
mask = mask.nullable();
}
}
// TODO(johnniwinther): Allow more TypeSelector kinds during resoluton.
assert(world.isClosed || mask.isExact);
if (selector.mask == mask) return selector;
Selector untyped = selector.asUntyped;
Map<TypeMask, TypedSelector> map = world.canonicalizedValues
.putIfAbsent(untyped, () => new Map<TypeMask, TypedSelector>());
TypedSelector result = map[mask];
if (result == null) {
int hashCode = Selector.mixHashCodeBits(untyped.hashCode, mask.hashCode);
result = map[mask] = new TypedSelector.internal(mask, untyped, hashCode);
}
return result;
}
factory TypedSelector.exact(
ClassElement base, Selector selector, World world)
=> new TypedSelector(new TypeMask.exact(base, world), selector,
world);
factory TypedSelector.subclass(
ClassElement base, Selector selector, World world)
=> new TypedSelector(new TypeMask.subclass(base, world),
selector, world);
factory TypedSelector.subtype(
ClassElement base, Selector selector, World world)
=> new TypedSelector(new TypeMask.subtype(base, world),
selector, world);
bool appliesUnnamed(Element element, World world) {
assert(sameNameHack(element, world));
// [TypedSelector] are only used after resolution.
if (!element.isClassMember) return false;
// A closure can be called through any typed selector:
// class A {
// get foo => () => 42;
// bar() => foo(); // The call to 'foo' is a typed selector.
// }
if (element.enclosingClass.isClosure) {
return appliesUntyped(element, world);
}
if (!mask.canHit(element, this, world)) return false;
return appliesUntyped(element, world);
}
Selector extendIfReachesAll(Compiler compiler) {
bool canReachAll = compiler.enabledInvokeOn
&& mask.needsNoSuchMethodHandling(this, compiler.world);
return canReachAll
? new TypedSelector(
compiler.typesTask.dynamicType, this, compiler.world)
: this;
}
}