Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dart2js.call_structure; | 5 library dart2js.call_structure; |
| 6 | 6 |
| 7 import '../common.dart'; | 7 import '../common.dart'; |
| 8 import '../common/names.dart' show Names; | 8 import '../common/names.dart' show Names; |
| 9 import '../elements/elements.dart'; | 9 import '../elements/types.dart' show FunctionType; |
| 10 import '../tree/tree.dart'; | |
| 11 import '../util/util.dart'; | 10 import '../util/util.dart'; |
| 12 import 'selector.dart' show Selector; | 11 import 'selector.dart' show Selector; |
| 13 | 12 |
| 14 /// The structure of the arguments at a call-site. | 13 /// The structure of the arguments at a call-site. |
| 15 // TODO(johnniwinther): Should these be cached? | 14 // TODO(johnniwinther): Should these be cached? |
| 16 // TODO(johnniwinther): Should isGetter/isSetter be part of the call structure | 15 // TODO(johnniwinther): Should isGetter/isSetter be part of the call structure |
| 17 // instead of the selector? | 16 // instead of the selector? |
| 18 class CallStructure { | 17 class CallStructure { |
| 19 static const CallStructure NO_ARGS = const CallStructure.unnamed(0); | 18 static const CallStructure NO_ARGS = const CallStructure.unnamed(0); |
| 20 static const CallStructure ONE_ARG = const CallStructure.unnamed(1); | 19 static const CallStructure ONE_ARG = const CallStructure.unnamed(1); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 32 | 31 |
| 33 const CallStructure.unnamed(this.argumentCount); | 32 const CallStructure.unnamed(this.argumentCount); |
| 34 | 33 |
| 35 factory CallStructure(int argumentCount, [List<String> namedArguments]) { | 34 factory CallStructure(int argumentCount, [List<String> namedArguments]) { |
| 36 if (namedArguments == null || namedArguments.isEmpty) { | 35 if (namedArguments == null || namedArguments.isEmpty) { |
| 37 return new CallStructure.unnamed(argumentCount); | 36 return new CallStructure.unnamed(argumentCount); |
| 38 } | 37 } |
| 39 return new NamedCallStructure(argumentCount, namedArguments); | 38 return new NamedCallStructure(argumentCount, namedArguments); |
| 40 } | 39 } |
| 41 | 40 |
| 42 /// Creates the [CallStructure] corresponding to calling [signature] as | |
| 43 /// declared, that is, all named arguments are in the order of declaration. | |
| 44 factory CallStructure.fromSignature(FunctionSignature signature) { | |
| 45 List<String> namedParameters; | |
| 46 if (signature.optionalParametersAreNamed) { | |
| 47 namedParameters = | |
| 48 signature.optionalParameters.map((e) => e.name).toList(); | |
| 49 } | |
| 50 return new CallStructure(signature.parameterCount, namedParameters); | |
| 51 } | |
| 52 | |
| 53 /// `true` if this call has named arguments. | 41 /// `true` if this call has named arguments. |
| 54 bool get isNamed => false; | 42 bool get isNamed => false; |
| 55 | 43 |
| 56 /// `true` if this call has no named arguments. | 44 /// `true` if this call has no named arguments. |
| 57 bool get isUnnamed => true; | 45 bool get isUnnamed => true; |
| 58 | 46 |
| 59 /// The names of the named arguments in call-site order. | 47 /// The names of the named arguments in call-site order. |
| 60 List<String> get namedArguments => const <String>[]; | 48 List<String> get namedArguments => const <String>[]; |
| 61 | 49 |
| 62 /// The names of the named arguments in canonicalized order. | 50 /// The names of the named arguments in canonicalized order. |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 80 int get hashCode { | 68 int get hashCode { |
| 81 return Hashing.listHash(namedArguments, | 69 return Hashing.listHash(namedArguments, |
| 82 Hashing.objectHash(argumentCount, namedArguments.length)); | 70 Hashing.objectHash(argumentCount, namedArguments.length)); |
| 83 } | 71 } |
| 84 | 72 |
| 85 bool operator ==(other) { | 73 bool operator ==(other) { |
| 86 if (other is! CallStructure) return false; | 74 if (other is! CallStructure) return false; |
| 87 return match(other); | 75 return match(other); |
| 88 } | 76 } |
| 89 | 77 |
| 90 bool signatureApplies(FunctionSignature parameters) { | 78 bool signatureApplies(FunctionType type) { |
| 91 if (argumentCount > parameters.parameterCount) return false; | 79 int requiredParameterCount = type.parameterTypes.length; |
| 92 int requiredParameterCount = parameters.requiredParameterCount; | 80 int optionalParameterCount = |
| 93 int optionalParameterCount = parameters.optionalParameterCount; | 81 type.optionalParameterTypes.length + type.namedParameters.length; |
| 82 int parameterCount = requiredParameterCount + optionalParameterCount; | |
| 83 if (argumentCount > parameterCount) return false; | |
| 94 if (positionalArgumentCount < requiredParameterCount) return false; | 84 if (positionalArgumentCount < requiredParameterCount) return false; |
| 95 | 85 |
| 96 if (!parameters.optionalParametersAreNamed) { | 86 if (type.namedParameters.isEmpty) { |
| 97 // We have already checked that the number of arguments are | 87 // We have already checked that the number of arguments are |
| 98 // not greater than the number of parameters. Therefore the | 88 // not greater than the number of parameters. Therefore the |
| 99 // number of positional arguments are not greater than the | 89 // number of positional arguments are not greater than the |
| 100 // number of parameters. | 90 // number of parameters. |
| 101 assert(positionalArgumentCount <= parameters.parameterCount); | 91 assert(positionalArgumentCount <= parameterCount); |
| 102 return namedArguments.isEmpty; | 92 return namedArguments.isEmpty; |
| 103 } else { | 93 } else { |
| 104 if (positionalArgumentCount > requiredParameterCount) return false; | 94 if (positionalArgumentCount > requiredParameterCount) return false; |
| 105 assert(positionalArgumentCount == requiredParameterCount); | 95 assert(positionalArgumentCount == requiredParameterCount); |
| 106 if (namedArgumentCount > optionalParameterCount) return false; | 96 if (namedArgumentCount > optionalParameterCount) return false; |
| 107 Set<String> nameSet = new Set<String>(); | 97 |
| 108 parameters.optionalParameters.forEach((Element element) { | 98 int nameIndex = 0; |
| 109 nameSet.add(element.name); | 99 List<String> namedParameters = type.namedParameters; |
| 110 }); | 100 OUTER: |
| 111 for (String name in namedArguments) { | 101 for (String name in getOrderedNamedArguments()) { |
| 112 if (!nameSet.contains(name)) return false; | 102 while (nameIndex < namedParameters.length) { |
| 113 // TODO(5213): By removing from the set we are checking | 103 if (name == namedParameters[nameIndex]) { |
|
Siggi Cherem (dart-lang)
2017/01/13 17:11:07
Can we get here anymore with the duplicated names,
Johnni Winther
2017/01/18 11:02:51
We still create both function signatures/types and
| |
| 114 // that we are not passing the name twice. We should have this | 104 continue OUTER; |
|
Siggi Cherem (dart-lang)
2017/01/13 17:11:07
I find the logic with `continue` a bit hard to fol
Johnni Winther
2017/01/18 11:02:51
Done.
| |
| 115 // check in the resolver also. | 105 } |
| 116 nameSet.remove(name); | 106 nameIndex++; |
| 107 } | |
| 108 return false; | |
| 117 } | 109 } |
| 118 return true; | 110 return true; |
| 119 } | 111 } |
| 120 } | 112 } |
| 121 | 113 |
| 122 /** | |
| 123 * Returns a `List` with the evaluated arguments in the normalized order. | |
| 124 * | |
| 125 * [compileDefaultValue] is a function that returns a compiled constant | |
| 126 * of an optional argument that is not in [compiledArguments]. | |
| 127 * | |
| 128 * Precondition: `this.applies(element, world)`. | |
| 129 * | |
| 130 * Invariant: [element] must be the implementation element. | |
| 131 */ | |
| 132 /*<T>*/ List/*<T>*/ makeArgumentsList( | |
| 133 Link<Node> arguments, | |
| 134 FunctionElement element, | |
| 135 /*T*/ compileArgument(Node argument), | |
| 136 /*T*/ compileDefaultValue(ParameterElement element)) { | |
| 137 assert(invariant(element, element.isImplementation)); | |
| 138 List/*<T>*/ result = new List(); | |
| 139 | |
| 140 FunctionSignature parameters = element.functionSignature; | |
| 141 parameters.forEachRequiredParameter((ParameterElement element) { | |
| 142 result.add(compileArgument(arguments.head)); | |
| 143 arguments = arguments.tail; | |
| 144 }); | |
| 145 | |
| 146 if (!parameters.optionalParametersAreNamed) { | |
| 147 parameters.forEachOptionalParameter((ParameterElement element) { | |
| 148 if (!arguments.isEmpty) { | |
| 149 result.add(compileArgument(arguments.head)); | |
| 150 arguments = arguments.tail; | |
| 151 } else { | |
| 152 result.add(compileDefaultValue(element)); | |
| 153 } | |
| 154 }); | |
| 155 } else { | |
| 156 // Visit named arguments and add them into a temporary list. | |
| 157 List compiledNamedArguments = []; | |
| 158 for (; !arguments.isEmpty; arguments = arguments.tail) { | |
| 159 NamedArgument namedArgument = arguments.head; | |
| 160 compiledNamedArguments.add(compileArgument(namedArgument.expression)); | |
| 161 } | |
| 162 // Iterate over the optional parameters of the signature, and try to | |
| 163 // find them in [compiledNamedArguments]. If found, we use the | |
| 164 // value in the temporary list, otherwise the default value. | |
| 165 parameters.orderedOptionalParameters.forEach((ParameterElement element) { | |
| 166 int foundIndex = namedArguments.indexOf(element.name); | |
| 167 if (foundIndex != -1) { | |
| 168 result.add(compiledNamedArguments[foundIndex]); | |
| 169 } else { | |
| 170 result.add(compileDefaultValue(element)); | |
| 171 } | |
| 172 }); | |
| 173 } | |
| 174 return result; | |
| 175 } | |
| 176 | |
| 177 /** | |
| 178 * Fills [list] with the arguments in the order expected by | |
| 179 * [callee], and where [caller] is a synthesized element | |
| 180 * | |
| 181 * [compileArgument] is a function that returns a compiled version | |
| 182 * of a parameter of [callee]. | |
| 183 * | |
| 184 * [compileConstant] is a function that returns a compiled constant | |
| 185 * of an optional argument that is not in the parameters of [callee]. | |
| 186 * | |
| 187 * Returns [:true:] if the signature of the [caller] matches the | |
| 188 * signature of the [callee], [:false:] otherwise. | |
| 189 */ | |
| 190 static/*<T>*/ bool addForwardingElementArgumentsToList( | |
| 191 ConstructorElement caller, | |
| 192 List/*<T>*/ list, | |
| 193 ConstructorElement callee, | |
| 194 /*T*/ compileArgument(ParameterElement element), | |
| 195 /*T*/ compileConstant(ParameterElement element)) { | |
| 196 assert(invariant(caller, !callee.isMalformed, | |
| 197 message: "Cannot compute arguments to malformed constructor: " | |
| 198 "$caller calling $callee.")); | |
| 199 | |
| 200 FunctionSignature signature = caller.functionSignature; | |
| 201 Map<Node, ParameterElement> mapping = <Node, ParameterElement>{}; | |
| 202 | |
| 203 // TODO(ngeoffray): This is a hack that fakes up AST nodes, so | |
| 204 // that we can call [addArgumentsToList]. | |
| 205 Link<Node> computeCallNodesFromParameters() { | |
| 206 LinkBuilder<Node> builder = new LinkBuilder<Node>(); | |
| 207 signature.forEachRequiredParameter((ParameterElement element) { | |
| 208 Node node = element.node; | |
| 209 mapping[node] = element; | |
| 210 builder.addLast(node); | |
| 211 }); | |
| 212 if (signature.optionalParametersAreNamed) { | |
| 213 signature.forEachOptionalParameter((ParameterElement element) { | |
| 214 mapping[element.initializer] = element; | |
| 215 builder.addLast(new NamedArgument(null, null, element.initializer)); | |
| 216 }); | |
| 217 } else { | |
| 218 signature.forEachOptionalParameter((ParameterElement element) { | |
| 219 Node node = element.node; | |
| 220 mapping[node] = element; | |
| 221 builder.addLast(node); | |
| 222 }); | |
| 223 } | |
| 224 return builder.toLink(); | |
| 225 } | |
| 226 | |
| 227 /*T*/ internalCompileArgument(Node node) { | |
| 228 return compileArgument(mapping[node]); | |
| 229 } | |
| 230 | |
| 231 Link<Node> nodes = computeCallNodesFromParameters(); | |
| 232 | |
| 233 // Synthesize a structure for the call. | |
| 234 // TODO(ngeoffray): Should the resolver do it instead? | |
| 235 CallStructure callStructure = new CallStructure.fromSignature(signature); | |
| 236 if (!callStructure.signatureApplies(signature)) { | |
| 237 return false; | |
| 238 } | |
| 239 list.addAll(callStructure.makeArgumentsList( | |
| 240 nodes, callee, internalCompileArgument, compileConstant)); | |
| 241 | |
| 242 return true; | |
| 243 } | |
| 244 | |
| 245 static bool sameNames(List<String> first, List<String> second) { | 114 static bool sameNames(List<String> first, List<String> second) { |
| 246 for (int i = 0; i < first.length; i++) { | 115 for (int i = 0; i < first.length; i++) { |
| 247 if (first[i] != second[i]) return false; | 116 if (first[i] != second[i]) return false; |
| 248 } | 117 } |
| 249 return true; | 118 return true; |
| 250 } | 119 } |
| 251 } | 120 } |
| 252 | 121 |
| 253 /// | 122 /// |
| 254 class NamedCallStructure extends CallStructure { | 123 class NamedCallStructure extends CallStructure { |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 281 return first.compareTo(second); | 150 return first.compareTo(second); |
| 282 }); | 151 }); |
| 283 return _orderedNamedArguments; | 152 return _orderedNamedArguments; |
| 284 } | 153 } |
| 285 | 154 |
| 286 @override | 155 @override |
| 287 String structureToString() { | 156 String structureToString() { |
| 288 return 'arity=$argumentCount, named=[${namedArguments.join(', ')}]'; | 157 return 'arity=$argumentCount, named=[${namedArguments.join(', ')}]'; |
| 289 } | 158 } |
| 290 } | 159 } |
| OLD | NEW |