| Index: sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
|
| ===================================================================
|
| --- sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart (revision 20148)
|
| +++ sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart (working copy)
|
| @@ -144,6 +144,24 @@
|
| new Map<Element, Map<Node, TypeMask>>();
|
|
|
| /**
|
| + * Maps an element to the type of its parameters at call sites.
|
| + */
|
| + final Map<Element, Map<Node, ArgumentsTypes>> typeOfArguments =
|
| + new Map<Element, Map<Node, ArgumentsTypes>>();
|
| +
|
| + /**
|
| + * Maps an optional parameter to its default type.
|
| + */
|
| + final Map<Element, TypeMask> defaultTypeOfParameter =
|
| + new Map<Element, TypeMask>();
|
| +
|
| + /**
|
| + * Set of methods that the inferrer found could be closurized. We
|
| + * don't compute parameter types for such methods.
|
| + */
|
| + final Set<Element> methodsThatCanBeClosurized = new Set<Element>();
|
| +
|
| + /**
|
| * Maps an element to the number of times this type inferrer
|
| * analyzed it.
|
| */
|
| @@ -285,13 +303,12 @@
|
| return getTypeIfValuable(typeOfSelector(selector));
|
| }
|
|
|
| + bool isTypeValuable(TypeMask returnType) {
|
| + return !isDynamicType(returnType) && !isGiveUpType(returnType);
|
| + }
|
| +
|
| TypeMask getTypeIfValuable(TypeMask returnType) {
|
| - if (isDynamicType(returnType)
|
| - || isGiveUpType(returnType)
|
| - || returnType == nullType) {
|
| - return null;
|
| - }
|
| - return returnType;
|
| + return isTypeValuable(returnType) ? returnType : null;
|
| }
|
|
|
| /**
|
| @@ -319,6 +336,8 @@
|
| // If we have analyzed all the world, we know all assigments to
|
| // fields and can therefore infer a type for them.
|
| typeOfFields.keys.forEach(updateNonFinalFieldType);
|
| + // We also know all calls to methods.
|
| + typeOfArguments.keys.forEach(updateArgumentsType);
|
| }
|
|
|
| /**
|
| @@ -423,7 +442,6 @@
|
| rawTypeOf(backend.mapImplementation));
|
| constMapType = new TypeMask.nonNullSubtype(
|
| rawTypeOf(backend.constMapImplementation));
|
| -
|
| functionType = new TypeMask.nonNullSubtype(
|
| rawTypeOf(backend.functionImplementation));
|
| typeType = new TypeMask.nonNullExact(
|
| @@ -604,23 +622,9 @@
|
| TypeMask result;
|
| iterateOverElements(selector, (Element element) {
|
| assert(element.isImplementation);
|
| - TypeMask type;
|
| - if (selector.isGetter()) {
|
| - if (element.isFunction()) {
|
| - // [functionType] is null if the inferrer did not run.
|
| - type = functionType == null ? dynamicType : functionType;
|
| - } else if (element.isField()) {
|
| - type = typeOfElement(element);
|
| - } else {
|
| - assert(element.isGetter());
|
| - type = returnTypeOfElement(element);
|
| - }
|
| - } else {
|
| - type = returnTypeOfElement(element);
|
| - }
|
| + TypeMask type = typeOfElementWithSelector(element, selector);
|
| result = computeLUB(result, type);
|
| - // Continue if the type is valuable.
|
| - return !isDynamicType(result) && !isGiveUpType(result);
|
| + return isTypeValuable(result);
|
| });
|
| if (result == null || isGiveUpType(result)) {
|
| result = dynamicType;
|
| @@ -628,6 +632,22 @@
|
| return result;
|
| }
|
|
|
| + TypeMask typeOfElementWithSelector(Element element, Selector selector) {
|
| + if (selector.isGetter()) {
|
| + if (element.isFunction()) {
|
| + // [functionType] is null if the inferrer did not run.
|
| + return functionType == null ? dynamicType : functionType;
|
| + } else if (element.isField()) {
|
| + return typeOfElement(element);
|
| + } else {
|
| + assert(element.isGetter());
|
| + return returnTypeOfElement(element);
|
| + }
|
| + } else {
|
| + return returnTypeOfElement(element);
|
| + }
|
| + }
|
| +
|
| bool isNotClosure(Element element) {
|
| // If the outermost enclosing element of [element] is [element]
|
| // itself, we know it cannot be a closure.
|
| @@ -635,51 +655,172 @@
|
| return outermost.declaration == element.declaration;
|
| }
|
|
|
| + void addCaller(Element caller, Element callee) {
|
| + assert(caller.isImplementation);
|
| + assert(callee.isImplementation);
|
| + assert(isNotClosure(caller));
|
| + Set<Element> callers = callersOf.putIfAbsent(
|
| + callee, () => new Set<Element>());
|
| + callers.add(caller);
|
| + }
|
| +
|
| + bool addArguments(Node node, Element element, ArgumentsTypes arguments) {
|
| + Map<Node, ArgumentsTypes> types = typeOfArguments.putIfAbsent(
|
| + element, () => new Map<Node, ArgumentsTypes>());
|
| + ArgumentsTypes existing = types[node];
|
| + types[node] = arguments;
|
| + return existing != arguments;
|
| + }
|
| +
|
| /**
|
| * Registers that [caller] calls [callee] with the given
|
| * [arguments].
|
| */
|
| - void registerCalledElement(Element caller,
|
| + void registerCalledElement(Send send,
|
| + Selector selector,
|
| + Element caller,
|
| Element callee,
|
| ArgumentsTypes arguments) {
|
| assert(isNotClosure(caller));
|
| - if (analyzeCount.containsKey(caller)) return;
|
| callee = callee.implementation;
|
| - addCaller(caller, callee);
|
| + if (!analyzeCount.containsKey(caller)) {
|
| + addCaller(caller, callee);
|
| + }
|
| +
|
| + if (selector.isSetter() && callee.isField()) {
|
| + recordNonFinalFieldElementType(send, callee, arguments.positional[0]);
|
| + return;
|
| + } else if (selector.isGetter()) {
|
| + assert(arguments == null);
|
| + if (callee.isFunction()) {
|
| + methodsThatCanBeClosurized.add(callee);
|
| + }
|
| + return;
|
| + } else if (callee.isField()) {
|
| + // We're not tracking closure calls.
|
| + return;
|
| + } else if (callee.isGetter()) {
|
| + // Getters don't have arguments.
|
| + return;
|
| + }
|
| + FunctionElement function = callee;
|
| + if (function.computeSignature(compiler).parameterCount == 0) return;
|
| +
|
| + assert(arguments != null);
|
| + bool isUseful = addArguments(send, callee, arguments);
|
| + if (hasAnalyzedAll && isUseful) {
|
| + updateArgumentsType(callee);
|
| + }
|
| }
|
|
|
| - void addCaller(Element caller, Element callee) {
|
| - Set<Element> callers = callersOf.putIfAbsent(
|
| - callee, () => new Set<Element>());
|
| - callers.add(caller);
|
| + void unregisterCalledElement(Send send,
|
| + Selector selector,
|
| + Element caller,
|
| + Element callee) {
|
| + if (callee.isField()) {
|
| + if (selector.isSetter()) {
|
| + Map<Node, TypeMask> types = typeOfFields[callee];
|
| + if (types == null || !types.containsKey(send)) return;
|
| + types.remove(send);
|
| + if (hasAnalyzedAll) updateNonFinalFieldType(callee);
|
| + }
|
| + } if (callee.isGetter()) {
|
| + return;
|
| + } else {
|
| + Map<Node, ArgumentsTypes> types = typeOfArguments[callee];
|
| + if (types == null || !types.containsKey(send)) return;
|
| + types.remove(send);
|
| + if (hasAnalyzedAll) updateArgumentsType(callee);
|
| + }
|
| }
|
|
|
| /**
|
| + * Computes the parameter types of [element], based on all call sites we
|
| + * have collected on that [element]. This method can only be called after
|
| + * we have analyzed all elements in the world.
|
| + */
|
| + void updateArgumentsType(FunctionElement element) {
|
| + assert(hasAnalyzedAll);
|
| + if (methodsThatCanBeClosurized.contains(element)) return;
|
| + FunctionSignature signature = element.computeSignature(compiler);
|
| +
|
| + if (typeOfArguments[element].isEmpty) {
|
| + signature.forEachParameter((Element parameter) {
|
| + typeOf.remove(parameter);
|
| + });
|
| + return;
|
| + }
|
| +
|
| + int parameterIndex = 0;
|
| + bool changed = false;
|
| + bool visitingOptionalParameter = false;
|
| + signature.forEachParameter((Element parameter) {
|
| + if (parameter == signature.firstOptionalParameter) {
|
| + visitingOptionalParameter = true;
|
| + }
|
| + TypeMask type;
|
| + typeOfArguments[element].forEach((_, ArgumentsTypes arguments) {
|
| + if (!visitingOptionalParameter) {
|
| + type = computeLUB(type, arguments.positional[parameterIndex]);
|
| + } else {
|
| + TypeMask argumentType = signature.optionalParametersAreNamed
|
| + ? arguments.named[parameter.name]
|
| + : parameterIndex < arguments.positional.length
|
| + ? arguments.positional[parameterIndex]
|
| + : null;
|
| + if (argumentType == null) {
|
| + argumentType = defaultTypeOfParameter[parameter];
|
| + }
|
| + assert(argumentType != null);
|
| + type = computeLUB(type, argumentType);
|
| + }
|
| + });
|
| + if (recordType(parameter, type)) {
|
| + changed = true;
|
| + }
|
| + parameterIndex++;
|
| + });
|
| +
|
| + if (changed) enqueueAgain(element);
|
| + }
|
| +
|
| + /**
|
| * Registers that [caller] calls an element matching [selector]
|
| * with the given [arguments].
|
| */
|
| - void registerCalledSelector(Element caller,
|
| - Selector selector,
|
| - ArgumentsTypes arguments) {
|
| + TypeMask registerCalledSelector(Send send,
|
| + Selector selector,
|
| + TypeMask receiverType,
|
| + Element caller,
|
| + ArgumentsTypes arguments) {
|
| + assert(!isGiveUpType(receiverType));
|
| assert(isNotClosure(caller));
|
| - if (analyzeCount.containsKey(caller)) return;
|
| - iterateOverElements(selector, (Element element) {
|
| + Selector typedSelector = isDynamicType(receiverType)
|
| + ? selector
|
| + : new TypedSelector(receiverType, selector);
|
| +
|
| + TypeMask result;
|
| + iterateOverElements(typedSelector, (Element element) {
|
| assert(element.isImplementation);
|
| - registerCalledElement(caller, element, arguments);
|
| + // TODO(ngeoffray): Enable unregistering by having a
|
| + // [: TypeMask.appliesTo(element) :] method, that will return
|
| + // whether [: element :] is a potential target for the type.
|
| + if (true) {
|
| + registerCalledElement(send, selector, caller, element, arguments);
|
| + } else {
|
| + unregisterCalledElement(send, selector, caller, element);
|
| + }
|
| + if (!selector.isSetter()) {
|
| + TypeMask type = typeOfElementWithSelector(element, selector);
|
| + result = computeLUB(result, type);
|
| + }
|
| return true;
|
| });
|
| - }
|
|
|
| - /**
|
| - * Registers that [caller] closurizes [function].
|
| - */
|
| - void registerGetFunction(Element caller, Element function) {
|
| - assert(isNotClosure(caller));
|
| - assert(caller.isImplementation);
|
| - if (analyzeCount.containsKey(caller)) return;
|
| - // We don't register that [caller] calls [function] because we
|
| - // don't know if the code is going to call it, and if it is, then
|
| - // the inferrer has lost track of its identity anyway.
|
| + if (result == null || isGiveUpType(result)) {
|
| + result = dynamicType;
|
| + }
|
| + return result;
|
| }
|
|
|
| /**
|
| @@ -694,22 +835,6 @@
|
| }
|
|
|
| /**
|
| - * Records an assignment to [selector] with the given
|
| - * [argumentType]. This method iterates over fields that match
|
| - * [selector] and update their type.
|
| - */
|
| - void recordNonFinalFieldSelectorType(Node node,
|
| - Selector selector,
|
| - TypeMask argumentType) {
|
| - iterateOverElements(selector, (Element element) {
|
| - if (element.isField()) {
|
| - recordNonFinalFieldElementType(node, element, argumentType);
|
| - }
|
| - return true;
|
| - });
|
| - }
|
| -
|
| - /**
|
| * Records an assignment to [element] with the given
|
| * [argumentType].
|
| */
|
| @@ -828,6 +953,7 @@
|
| info.typesOfFinalFields.forEach((Element field,
|
| Map<Node, TypeMask> types) {
|
| if (isNativeElement(field)) return;
|
| + if (isNativeElement(field)) return;
|
| assert(field.modifiers.isFinal());
|
| TypeMask fieldType = computeFieldTypeWithConstraints(field, types);
|
| if (recordType(field, fieldType)) {
|
| @@ -870,10 +996,22 @@
|
| */
|
| class ArgumentsTypes {
|
| final List<TypeMask> positional;
|
| - final Map<Identifier, TypeMask> named;
|
| - ArgumentsTypes(this.positional, this.named);
|
| + final Map<SourceString, TypeMask> named;
|
| + ArgumentsTypes(this.positional, named)
|
| + : this.named = (named == null) ? new Map<SourceString, TypeMask>() : named;
|
| int get length => positional.length + named.length;
|
| - toString() => "{ positional = $positional, named = $named }";
|
| + String toString() => "{ positional = $positional, named = $named }";
|
| + bool operator==(other) {
|
| + if (positional.length != other.positional.length) return false;
|
| + if (named.length != other.named.length) return false;
|
| + for (int i = 0; i < positional.length; i++) {
|
| + if (positional[i] != other.positional[i]) return false;
|
| + }
|
| + named.forEach((name, type) {
|
| + if (other.named[name] != type) return false;
|
| + });
|
| + return true;
|
| + }
|
| }
|
|
|
| /**
|
| @@ -1063,13 +1201,30 @@
|
| });
|
| }
|
| if (analyzedElement.isField()) {
|
| - returnType = visit(node.asSendSet().arguments.head);
|
| - } else if (analyzedElement.isGenerativeConstructor()) {
|
| - FunctionElement function = analyzedElement;
|
| - FunctionSignature signature = function.computeSignature(compiler);
|
| + return visit(node.asSendSet().arguments.head);
|
| + }
|
| +
|
| + FunctionElement function = analyzedElement;
|
| + FunctionSignature signature = function.computeSignature(compiler);
|
| + signature.forEachOptionalParameter((element) {
|
| + Node node = element.parseNode(compiler);
|
| + Send send = node.asSendSet();
|
| + inferrer.defaultTypeOfParameter[element] = (send == null)
|
| + ? inferrer.nullType
|
| + : visit(node.arguments.head);
|
| + assert(inferrer.defaultTypeOfParameter[element] != null);
|
| + });
|
| +
|
| + if (analyzedElement.isNative()) {
|
| + // Native methods do not have a body, and we currently just say
|
| + // they return dynamic.
|
| + return inferrer.dynamicType;
|
| + }
|
| +
|
| + if (analyzedElement.isGenerativeConstructor()) {
|
| + isThisExposed = false;
|
| signature.forEachParameter((element) {
|
| - // We don't track argument types yet, so just set the fields
|
| - // and parameters as dynamic.
|
| + TypeMask parameterType = inferrer.typeOfElement(element);
|
| if (element.kind == ElementKind.FIELD_PARAMETER) {
|
| if (element.fieldElement.modifiers.isFinal()) {
|
| inferrer.recordFinalFieldType(
|
| @@ -1078,17 +1233,17 @@
|
| element.fieldElement,
|
| inferrer.dynamicType);
|
| } else {
|
| + locals.updateField(element.fieldElement, parameterType);
|
| inferrer.recordNonFinalFieldElementType(
|
| element.parseNode(compiler),
|
| element.fieldElement,
|
| - inferrer.dynamicType);
|
| + parameterType);
|
| }
|
| } else {
|
| - locals.update(element, inferrer.dynamicType);
|
| + locals.update(element, parameterType);
|
| }
|
| });
|
| visitingInitializers = true;
|
| - isThisExposed = false;
|
| visit(node.initializers);
|
| visitingInitializers = false;
|
| visit(node.body);
|
| @@ -1107,17 +1262,9 @@
|
| }
|
| inferrer.doneAnalyzingGenerativeConstructor(analyzedElement);
|
| returnType = new TypeMask.nonNullExact(inferrer.rawTypeOf(cls));
|
| - } else if (analyzedElement.isNative()) {
|
| - // Native methods do not have a body, and we currently just say
|
| - // they return dynamic.
|
| - returnType = inferrer.dynamicType;
|
| } else {
|
| - FunctionElement function = analyzedElement;
|
| - FunctionSignature signature = function.computeSignature(compiler);
|
| signature.forEachParameter((element) {
|
| - // We don't track argument types yet, so just set the
|
| - // parameters as dynamic.
|
| - locals.update(element, inferrer.dynamicType);
|
| + locals.update(element, inferrer.typeOfElement(element));
|
| });
|
| visit(node.body);
|
| if (returnType == null) {
|
| @@ -1395,7 +1542,8 @@
|
| ArgumentsTypes arguments = new ArgumentsTypes([rhsType], null);
|
| if (Elements.isStaticOrTopLevelField(element)) {
|
| if (element.isSetter()) {
|
| - inferrer.registerCalledElement(outermostElement, element, arguments);
|
| + inferrer.registerCalledElement(
|
| + node, setterSelector, outermostElement, element, arguments);
|
| } else {
|
| assert(element.isField());
|
| inferrer.recordNonFinalFieldElementType(node, element, rhsType);
|
| @@ -1424,6 +1572,7 @@
|
| var rhs = node.arguments.head;
|
| if (rhs.asSend() != null
|
| && rhs.isPropertyAccess
|
| + && !Elements.isLocal(elements[rhs])
|
| && rhs.selector.source == node.selector.asIdentifier().source) {
|
| // TODO(ngeoffray): We should update selectors in the
|
| // element tree and find out if the typed selector still
|
| @@ -1444,7 +1593,8 @@
|
| ? inferrer.typeOfElement(element)
|
| : inferrer.returnTypeOfElement(element);
|
| if (getterElement.isGetter()) {
|
| - inferrer.registerCalledElement(outermostElement, getterElement, null);
|
| + inferrer.registerCalledElement(
|
| + node, getterSelector, outermostElement, getterElement, null);
|
| }
|
| newType = handleDynamicSend(
|
| node, operatorSelector, getterType, operatorArguments);
|
| @@ -1453,7 +1603,8 @@
|
| } else {
|
| assert(element.isSetter());
|
| inferrer.registerCalledElement(
|
| - outermostElement, element, new ArgumentsTypes([newType], null));
|
| + node, setterSelector, outermostElement, element,
|
| + new ArgumentsTypes([newType], null));
|
| }
|
| } else if (Elements.isUnresolved(element) || element.isSetter()) {
|
| getterType = handleDynamicSend(
|
| @@ -1513,11 +1664,14 @@
|
| // are calling does not expose this.
|
| isThisExposed = true;
|
| if (node.isPropertyAccess) {
|
| - inferrer.registerCalledElement(outermostElement, element, null);
|
| + inferrer.registerCalledElement(
|
| + node, selector, outermostElement, element, null);
|
| return inferrer.typeOfElement(element);
|
| } else if (element.isFunction()) {
|
| + if (!selector.applies(element, compiler)) return inferrer.dynamicType;
|
| ArgumentsTypes arguments = analyzeArguments(node.arguments);
|
| - inferrer.registerCalledElement(outermostElement, element, arguments);
|
| + inferrer.registerCalledElement(
|
| + node, selector, outermostElement, element, arguments);
|
| return inferrer.returnTypeOfElement(element);
|
| } else {
|
| analyzeArguments(node.arguments);
|
| @@ -1535,18 +1689,22 @@
|
| if (element.isForeign(compiler)) {
|
| return handleForeignSend(node);
|
| }
|
| + Selector selector = elements.getSelector(node);
|
| ArgumentsTypes arguments = analyzeArguments(node.arguments);
|
| if (Elements.isUnresolved(element)
|
| || element.isGetter()
|
| || element.isField()) {
|
| if (element.isGetter()) {
|
| - inferrer.registerCalledElement(outermostElement, element, null);
|
| + inferrer.registerCalledElement(node, new Selector.getterFrom(selector),
|
| + outermostElement, element, null);
|
| }
|
| return inferrer.dynamicType;
|
| }
|
|
|
| - Selector selector = elements.getSelector(node);
|
| - inferrer.registerCalledElement(outermostElement, element, arguments);
|
| + if (!selector.applies(element, compiler)) return inferrer.dynamicType;
|
| +
|
| + inferrer.registerCalledElement(
|
| + node, selector, outermostElement, element, arguments);
|
| if (Elements.isGrowableListConstructorCall(element, node, compiler)) {
|
| return inferrer.growableListType;
|
| } else if (Elements.isFixedListConstructorCall(element, node, compiler)) {
|
| @@ -1597,6 +1755,10 @@
|
| inferrer.rawTypeOf(type.element));
|
| }
|
| returnType = inferrer.computeLUB(returnType, mappedType);
|
| + if (!inferrer.isTypeValuable(returnType)) {
|
| + returnType = inferrer.dynamicType;
|
| + break;
|
| + }
|
| }
|
| return returnType;
|
| } else if (name == const SourceString('JS_OPERATOR_IS_PREFIX')
|
| @@ -1609,12 +1771,12 @@
|
|
|
| ArgumentsTypes analyzeArguments(Link<Node> arguments) {
|
| List<TypeMask> positional = [];
|
| - Map<Identifier, TypeMask> named = new Map<Identifier, TypeMask>();
|
| + Map<SourceString, TypeMask> named = new Map<SourceString, TypeMask>();
|
| for (var argument in arguments) {
|
| NamedArgument namedArgument = argument.asNamedArgument();
|
| if (namedArgument != null) {
|
| argument = namedArgument.expression;
|
| - named[namedArgument.name] = argument.accept(this);
|
| + named[namedArgument.name.source] = argument.accept(this);
|
| } else {
|
| positional.add(argument.accept(this));
|
| }
|
| @@ -1670,13 +1832,16 @@
|
|
|
| TypeMask visitGetterSend(Send node) {
|
| Element element = elements[node];
|
| + Selector selector = elements.getSelector(node);
|
| if (Elements.isStaticOrTopLevelField(element)) {
|
| - inferrer.registerCalledElement(outermostElement, element, null);
|
| + inferrer.registerCalledElement(
|
| + node, selector, outermostElement, element, null);
|
| return inferrer.typeOfElement(element);
|
| } else if (Elements.isInstanceSend(node, elements)) {
|
| return visitDynamicSend(node);
|
| } else if (Elements.isStaticOrTopLevelFunction(element)) {
|
| - inferrer.registerGetFunction(outermostElement, element);
|
| + inferrer.registerCalledElement(
|
| + node, selector, outermostElement, element, null);
|
| return inferrer.functionType;
|
| } else if (Elements.isErroneousElement(element)) {
|
| return inferrer.dynamicType;
|
| @@ -1706,18 +1871,8 @@
|
| Selector selector,
|
| TypeMask receiver,
|
| ArgumentsTypes arguments) {
|
| - if (!inferrer.isDynamicType(receiver)) {
|
| - selector = new TypedSelector(receiver, selector);
|
| - }
|
| - inferrer.registerCalledSelector(outermostElement, selector, arguments);
|
| - if (selector.isSetter()) {
|
| - inferrer.recordNonFinalFieldSelectorType(
|
| - node, selector, arguments.positional[0]);
|
| - // We return null to prevent using a type for a called setter.
|
| - // The return type is the right hand side of the setter.
|
| - return null;
|
| - }
|
| - return inferrer.typeOfSelector(selector);
|
| + return inferrer.registerCalledSelector(
|
| + node, selector, receiver, outermostElement, arguments);
|
| }
|
|
|
| TypeMask visitDynamicSend(Send node) {
|
| @@ -1751,10 +1906,27 @@
|
| recordReturnType(inferrer.dynamicType);
|
| } else {
|
| element = element.implementation;
|
| - // Call [:addCaller:] directly and not
|
| - // [:registerCalledElement:] because we should actually pass
|
| - // the parameters.
|
| + // We don't create a selector for redirecting factories, and
|
| + // the send is just a property access. Therefore we must
|
| + // manually create the [ArgumentsTypes] of the call, and
|
| + // manually register [analyzedElement] as a caller of [element].
|
| + FunctionElement function = analyzedElement;
|
| + FunctionSignature signature = function.computeSignature(compiler);
|
| + List<TypeMask> unnamed = <TypeMask>[];
|
| + Map<SourceString, TypeMask> named = new Map<SourceString, TypeMask>();
|
| + signature.forEachRequiredParameter((Element element) {
|
| + unnamed.add(locals.use(element));
|
| + });
|
| + signature.forEachOptionalParameter((Element element) {
|
| + if (signature.optionalParametersAreNamed) {
|
| + named[element.name] = locals.use(element);
|
| + } else {
|
| + unnamed.add(locals.use(element));
|
| + }
|
| + });
|
| + ArgumentsTypes arguments = new ArgumentsTypes(unnamed, named);
|
| inferrer.addCaller(analyzedElement, element);
|
| + inferrer.addArguments(node.expression, element, arguments);
|
| recordReturnType(inferrer.returnTypeOfElement(element));
|
| }
|
| } else {
|
|
|