| // 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. |
| |
| library dart2js.selector; |
| |
| import '../common/names.dart' show |
| Names; |
| import '../diagnostics/spannable.dart' show |
| SpannableAssertionFailure; |
| import '../elements/elements.dart' show |
| Element, |
| Elements, |
| FunctionElement, |
| FunctionSignature, |
| Name, |
| LibraryElement, |
| PublicName; |
| import '../util/util.dart' show |
| Hashing; |
| import '../world.dart' show |
| World; |
| |
| import 'call_structure.dart' show |
| CallStructure; |
| |
| 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 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; |
| |
| Selector.internal(this.kind, |
| this.memberName, |
| this.callStructure, |
| this.hashCode) { |
| assert(kind == SelectorKind.INDEX || |
| (memberName != Names.INDEX_NAME && |
| memberName != Names.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 == Names.INDEX_NAME) { |
| return new Selector.index(); |
| } else if (name == Names.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, Names.INDEX_NAME, |
| CallStructure.ONE_ARG); |
| |
| factory Selector.indexSet() |
| => new Selector(SelectorKind.INDEX, Names.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, Names.call, |
| new CallStructure(arity, namedArguments)); |
| |
| factory Selector.callClosureFrom(Selector selector) |
| => new Selector(SelectorKind.CALL, Names.call, 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 == Names.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; |
| |
| /** |
| * 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; |
| } |
| |
| 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); |
| } |