| Index: pkg/compiler/lib/src/universe/universe.dart
|
| diff --git a/pkg/compiler/lib/src/universe/universe.dart b/pkg/compiler/lib/src/universe/universe.dart
|
| index 4530400e1cbaffc794effa09c6a3e22e636bd033..5268be3de5802c9ac6661b5efecaf035f545d677 100644
|
| --- a/pkg/compiler/lib/src/universe/universe.dart
|
| +++ b/pkg/compiler/lib/src/universe/universe.dart
|
| @@ -25,7 +25,9 @@ import '../world.dart' show
|
| ClassWorld,
|
| World;
|
|
|
| +part 'call_structure.dart';
|
| part 'function_set.dart';
|
| +part 'selector.dart';
|
| part 'side_effects.dart';
|
|
|
| class UniverseSelector {
|
| @@ -371,546 +373,3 @@ class Universe {
|
| }));
|
| }
|
| }
|
| -
|
| -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;
|
| -}
|
| -
|
| -/// The structure of the arguments at a call-site.
|
| -// TODO(johnniwinther): Should these be cached?
|
| -// TODO(johnniwinther): Should isGetter/isSetter be part of the call structure
|
| -// instead of the selector?
|
| -class CallStructure {
|
| - static const CallStructure NO_ARGS = const CallStructure.unnamed(0);
|
| - static const CallStructure ONE_ARG = const CallStructure.unnamed(1);
|
| - static const CallStructure TWO_ARGS = const CallStructure.unnamed(2);
|
| -
|
| - /// The numbers of arguments of the call. Includes named arguments.
|
| - final int argumentCount;
|
| -
|
| - /// The number of named arguments of the call.
|
| - int get namedArgumentCount => 0;
|
| -
|
| - /// The number of positional argument of the call.
|
| - int get positionalArgumentCount => argumentCount;
|
| -
|
| - const CallStructure.unnamed(this.argumentCount);
|
| -
|
| - factory CallStructure(int argumentCount, [List<String> namedArguments]) {
|
| - if (namedArguments == null || namedArguments.isEmpty) {
|
| - return new CallStructure.unnamed(argumentCount);
|
| - }
|
| - return new NamedCallStructure(argumentCount, namedArguments);
|
| - }
|
| -
|
| - /// `true` if this call has named arguments.
|
| - bool get isNamed => false;
|
| -
|
| - /// `true` if this call has no named arguments.
|
| - bool get isUnnamed => true;
|
| -
|
| - /// The names of the named arguments in call-site order.
|
| - List<String> get namedArguments => const <String>[];
|
| -
|
| - /// The names of the named arguments in canonicalized order.
|
| - List<String> getOrderedNamedArguments() => const <String>[];
|
| -
|
| - /// A description of the argument structure.
|
| - String structureToString() => 'arity=$argumentCount';
|
| -
|
| - String toString() => 'CallStructure(${structureToString()})';
|
| -
|
| - Selector get callSelector {
|
| - return new Selector(SelectorKind.CALL, Selector.CALL_NAME, this);
|
| - }
|
| -
|
| - bool match(CallStructure other) {
|
| - if (identical(this, other)) return true;
|
| - return this.argumentCount == other.argumentCount
|
| - && this.namedArgumentCount == other.namedArgumentCount
|
| - && sameNames(this.namedArguments, other.namedArguments);
|
| - }
|
| -
|
| - // TODO(johnniwinther): Cache hash code?
|
| - int get hashCode {
|
| - return Hashing.listHash(namedArguments,
|
| - Hashing.objectHash(argumentCount, namedArguments.length));
|
| - }
|
| -
|
| - bool operator ==(other) {
|
| - if (other is! CallStructure) return false;
|
| - return match(other);
|
| - }
|
| -
|
| - bool signatureApplies(FunctionSignature parameters) {
|
| - 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;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * 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.
|
| - */
|
| - /*<T>*/ List/*<T>*/ makeArgumentsList(
|
| - 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 /*<T>*/ bool addForwardingElementArgumentsToList(
|
| - ConstructorElement caller,
|
| - List/*<T>*/ list,
|
| - ConstructorElement callee,
|
| - /*T*/ compileArgument(ParameterElement element),
|
| - /*T*/ compileConstant(ParameterElement element)) {
|
| - assert(invariant(caller, !callee.isErroneous,
|
| - message: "Cannot compute arguments to erroneous constructor: "
|
| - "$caller calling $callee."));
|
| -
|
| - FunctionSignature signature = caller.functionSignature;
|
| - Map<Node, ParameterElement> mapping = <Node, ParameterElement>{};
|
| -
|
| - // TODO(ngeoffray): This is a hack that fakes up AST nodes, so
|
| - // that we can call [addArgumentsToList].
|
| - Link<Node> computeCallNodesFromParameters() {
|
| - LinkBuilder<Node> builder = new LinkBuilder<Node>();
|
| - 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();
|
| - }
|
| -
|
| - /*T*/ internalCompileArgument(Node node) {
|
| - return compileArgument(mapping[node]);
|
| - }
|
| -
|
| - Link<Node> nodes = computeCallNodesFromParameters();
|
| -
|
| - // Synthesize a structure for the call.
|
| - // TODO(ngeoffray): Should the resolver do it instead?
|
| - List<String> namedParameters;
|
| - if (signature.optionalParametersAreNamed) {
|
| - namedParameters =
|
| - signature.optionalParameters.map((e) => e.name).toList();
|
| - }
|
| - CallStructure callStructure =
|
| - new CallStructure(signature.parameterCount, namedParameters);
|
| - if (!callStructure.signatureApplies(signature)) {
|
| - return false;
|
| - }
|
| - list.addAll(callStructure.makeArgumentsList(
|
| - 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;
|
| - }
|
| -}
|
| -
|
| -///
|
| -class NamedCallStructure extends CallStructure {
|
| - final List<String> namedArguments;
|
| - final List<String> _orderedNamedArguments = <String>[];
|
| -
|
| - NamedCallStructure(int argumentCount, this.namedArguments)
|
| - : super.unnamed(argumentCount) {
|
| - assert(namedArguments.isNotEmpty);
|
| - }
|
| -
|
| - @override
|
| - bool get isNamed => true;
|
| -
|
| - @override
|
| - bool get isUnnamed => false;
|
| -
|
| - @override
|
| - int get namedArgumentCount => namedArguments.length;
|
| -
|
| - @override
|
| - int get positionalArgumentCount => argumentCount - namedArgumentCount;
|
| -
|
| - @override
|
| - List<String> getOrderedNamedArguments() {
|
| - if (!_orderedNamedArguments.isEmpty) return _orderedNamedArguments;
|
| -
|
| - _orderedNamedArguments.addAll(namedArguments);
|
| - _orderedNamedArguments.sort((String first, String second) {
|
| - return first.compareTo(second);
|
| - });
|
| - return _orderedNamedArguments;
|
| - }
|
| -
|
| - @override
|
| - String structureToString() {
|
| - return 'arity=$argumentCount, named=[${namedArguments.join(', ')}]';
|
| - }
|
| -}
|
| -
|
| -class Selector {
|
| - final SelectorKind kind;
|
| - final Name memberName;
|
| - final CallStructure callStructure;
|
| -
|
| - final int hashCode;
|
| -
|
| - int get argumentCount => callStructure.argumentCount;
|
| - int get namedArgumentCount => callStructure.namedArgumentCount;
|
| - int get positionalArgumentCount => callStructure.positionalArgumentCount;
|
| - List<String> get namedArguments => callStructure.namedArguments;
|
| -
|
| - String get name => memberName.text;
|
| -
|
| - LibraryElement get library => memberName.library;
|
| -
|
| - static const Name INDEX_NAME = const PublicName("[]");
|
| - static const Name INDEX_SET_NAME = const PublicName("[]=");
|
| - static const Name CALL_NAME = Names.call;
|
| -
|
| - Selector.internal(this.kind,
|
| - this.memberName,
|
| - this.callStructure,
|
| - this.hashCode) {
|
| - assert(kind == SelectorKind.INDEX ||
|
| - (memberName != INDEX_NAME && memberName != INDEX_SET_NAME));
|
| - assert(kind == SelectorKind.OPERATOR ||
|
| - kind == SelectorKind.INDEX ||
|
| - !Elements.isOperatorName(memberName.text) ||
|
| - identical(memberName.text, '??'));
|
| - assert(kind == SelectorKind.CALL ||
|
| - kind == SelectorKind.GETTER ||
|
| - kind == SelectorKind.SETTER ||
|
| - Elements.isOperatorName(memberName.text) ||
|
| - identical(memberName.text, '??'));
|
| - }
|
| -
|
| - // TODO(johnniwinther): Extract caching.
|
| - static Map<int, List<Selector>> canonicalizedValues =
|
| - new Map<int, List<Selector>>();
|
| -
|
| - factory Selector(SelectorKind kind,
|
| - Name name,
|
| - CallStructure callStructure) {
|
| - // TODO(johnniwinther): Maybe use equality instead of implicit hashing.
|
| - int hashCode = computeHashCode(kind, name, callStructure);
|
| - List<Selector> list = canonicalizedValues.putIfAbsent(hashCode,
|
| - () => <Selector>[]);
|
| - for (int i = 0; i < list.length; i++) {
|
| - Selector existing = list[i];
|
| - if (existing.match(kind, name, callStructure)) {
|
| - assert(existing.hashCode == hashCode);
|
| - return existing;
|
| - }
|
| - }
|
| - Selector result = new Selector.internal(
|
| - kind, name, callStructure, hashCode);
|
| - list.add(result);
|
| - return result;
|
| - }
|
| -
|
| - factory Selector.fromElement(Element element) {
|
| - Name name = new Name(element.name, element.library);
|
| - if (element.isFunction) {
|
| - if (name == INDEX_NAME) {
|
| - return new Selector.index();
|
| - } else if (name == INDEX_SET_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,
|
| - new CallStructure(arity, namedArguments));
|
| - } else {
|
| - return new Selector.call(
|
| - name, new CallStructure(arity, namedArguments));
|
| - }
|
| - } else if (element.isSetter) {
|
| - return new Selector.setter(name);
|
| - } else if (element.isGetter) {
|
| - return new Selector.getter(name);
|
| - } else if (element.isField) {
|
| - return new Selector.getter(name);
|
| - } else if (element.isConstructor) {
|
| - return new Selector.callConstructor(name);
|
| - } else {
|
| - throw new SpannableAssertionFailure(
|
| - element, "Can't get selector from $element");
|
| - }
|
| - }
|
| -
|
| - factory Selector.getter(Name name)
|
| - => new Selector(SelectorKind.GETTER,
|
| - name.getter,
|
| - CallStructure.NO_ARGS);
|
| -
|
| - factory Selector.setter(Name name)
|
| - => new Selector(SelectorKind.SETTER,
|
| - name.setter,
|
| - CallStructure.ONE_ARG);
|
| -
|
| - factory Selector.unaryOperator(String name) => new Selector(
|
| - SelectorKind.OPERATOR,
|
| - new PublicName(Elements.constructOperatorName(name, true)),
|
| - CallStructure.NO_ARGS);
|
| -
|
| - factory Selector.binaryOperator(String name) => new Selector(
|
| - SelectorKind.OPERATOR,
|
| - new PublicName(Elements.constructOperatorName(name, false)),
|
| - CallStructure.ONE_ARG);
|
| -
|
| - factory Selector.index()
|
| - => new Selector(SelectorKind.INDEX, INDEX_NAME,
|
| - CallStructure.ONE_ARG);
|
| -
|
| - factory Selector.indexSet()
|
| - => new Selector(SelectorKind.INDEX, INDEX_SET_NAME,
|
| - CallStructure.TWO_ARGS);
|
| -
|
| - factory Selector.call(Name name, CallStructure callStructure)
|
| - => new Selector(SelectorKind.CALL, name, callStructure);
|
| -
|
| - factory Selector.callClosure(int arity, [List<String> namedArguments])
|
| - => new Selector(SelectorKind.CALL, CALL_NAME,
|
| - new CallStructure(arity, namedArguments));
|
| -
|
| - factory Selector.callClosureFrom(Selector selector)
|
| - => new Selector(SelectorKind.CALL, CALL_NAME, selector.callStructure);
|
| -
|
| - factory Selector.callConstructor(Name name,
|
| - [int arity = 0,
|
| - List<String> namedArguments])
|
| - => new Selector(SelectorKind.CALL, name,
|
| - new CallStructure(arity, namedArguments));
|
| -
|
| - factory Selector.callDefaultConstructor()
|
| - => new Selector(
|
| - SelectorKind.CALL,
|
| - const PublicName(''),
|
| - CallStructure.NO_ARGS);
|
| -
|
| - bool get isGetter => kind == SelectorKind.GETTER;
|
| - bool get isSetter => kind == SelectorKind.SETTER;
|
| - bool get isCall => kind == SelectorKind.CALL;
|
| - bool get isClosureCall => isCall && memberName == CALL_NAME;
|
| -
|
| - bool get isIndex => kind == SelectorKind.INDEX && argumentCount == 1;
|
| - bool get isIndexSet => kind == SelectorKind.INDEX && argumentCount == 2;
|
| -
|
| - bool get isOperator => kind == SelectorKind.OPERATOR;
|
| - bool get isUnaryOperator => isOperator && argumentCount == 0;
|
| -
|
| - /** Check whether this is a call to 'assert'. */
|
| - bool get isAssert => isCall && identical(name, "assert");
|
| -
|
| - /**
|
| - * 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 (memberName.isPrivate && memberName.library != element.library) {
|
| - // TODO(johnniwinther): Maybe this should be
|
| - // `memberName != element.memberName`.
|
| - 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) {
|
| - if (Elements.isUnresolved(function)) return false;
|
| - return callStructure.signatureApplies(function.functionSignature);
|
| - }
|
| -
|
| - 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);
|
| - }
|
| -
|
| - bool match(SelectorKind kind,
|
| - Name memberName,
|
| - CallStructure callStructure) {
|
| - return this.kind == kind
|
| - && this.memberName == memberName
|
| - && this.callStructure.match(callStructure);
|
| - }
|
| -
|
| - static int computeHashCode(SelectorKind kind,
|
| - Name name,
|
| - CallStructure callStructure) {
|
| - // Add bits from name and kind.
|
| - int hash = Hashing.mixHashCodeBits(name.hashCode, kind.hashCode);
|
| - // Add bits from the call structure.
|
| - return Hashing.mixHashCodeBits(hash, callStructure.hashCode);
|
| - }
|
| -
|
| - String toString() {
|
| - return 'Selector($kind, $name, ${callStructure.structureToString()})';
|
| - }
|
| -
|
| - Selector toCallSelector() => new Selector.callClosureFrom(this);
|
| -}
|
|
|