OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 universe; | 5 library universe; |
6 | 6 |
7 import '../elements/elements.dart'; | 7 import '../elements/elements.dart'; |
8 import '../dart2jslib.dart'; | 8 import '../dart2jslib.dart'; |
9 import '../dart_types.dart'; | 9 import '../dart_types.dart'; |
10 import '../types/types.dart'; | 10 import '../types/types.dart'; |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
221 | 221 |
222 static const SelectorKind GETTER = const SelectorKind('getter', 0); | 222 static const SelectorKind GETTER = const SelectorKind('getter', 0); |
223 static const SelectorKind SETTER = const SelectorKind('setter', 1); | 223 static const SelectorKind SETTER = const SelectorKind('setter', 1); |
224 static const SelectorKind CALL = const SelectorKind('call', 2); | 224 static const SelectorKind CALL = const SelectorKind('call', 2); |
225 static const SelectorKind OPERATOR = const SelectorKind('operator', 3); | 225 static const SelectorKind OPERATOR = const SelectorKind('operator', 3); |
226 static const SelectorKind INDEX = const SelectorKind('index', 4); | 226 static const SelectorKind INDEX = const SelectorKind('index', 4); |
227 | 227 |
228 String toString() => name; | 228 String toString() => name; |
229 } | 229 } |
230 | 230 |
231 class Selector { | 231 /// The structure of the arguments at a call-site. |
232 final SelectorKind kind; | 232 // TODO(johnniwinther): Should these be cached? |
233 final String name; | 233 // TODO(johnniwinther): Should isGetter/isSetter be part of the call structure |
234 final LibraryElement library; // Library is null for non-private selectors. | 234 // instead of the selector? |
235 class CallStructure { | |
236 static const CallStructure NO_ARGS = const CallStructure.unnamed(0); | |
237 static const CallStructure ONE_ARG = const CallStructure.unnamed(1); | |
238 static const CallStructure TWO_ARGS = const CallStructure.unnamed(2); | |
235 | 239 |
236 // The numbers of arguments of the selector. Includes named arguments. | 240 /// The numbers of arguments of the call. Includes named arguments. |
237 final int argumentCount; | 241 final int argumentCount; |
238 final List<String> namedArguments; | |
239 final List<String> _orderedNamedArguments; | |
240 final int hashCode; | |
241 | 242 |
242 static const String INDEX_NAME ="[]"; | 243 /// The number of named arguments of the call. |
243 static const String INDEX_SET_NAME = "[]="; | 244 int get namedArgumentCount => 0; |
244 static const String CALL_NAME = Compiler.CALL_OPERATOR_NAME; | |
245 | 245 |
246 Selector.internal(this.kind, | 246 /// The number of positional argument of the call. |
247 this.name, | 247 int get positionalArgumentCount => argumentCount; |
248 this.library, | 248 |
249 this.argumentCount, | 249 const CallStructure.unnamed(this.argumentCount); |
250 this.namedArguments, | 250 |
251 this._orderedNamedArguments, | 251 factory CallStructure(int argumentCount, [List<String> namedArguments]) { |
252 this.hashCode) { | 252 if (namedArguments == null || namedArguments.isEmpty) { |
253 assert(kind == SelectorKind.INDEX | 253 return new CallStructure.unnamed(argumentCount); |
254 || (name != INDEX_NAME && name != INDEX_SET_NAME)); | 254 } |
255 assert(kind == SelectorKind.OPERATOR | 255 return new NamedCallStructure(argumentCount, namedArguments); |
256 || kind == SelectorKind.INDEX | |
257 || !Elements.isOperatorName(name)); | |
258 assert(kind == SelectorKind.CALL | |
259 || kind == SelectorKind.GETTER | |
260 || kind == SelectorKind.SETTER | |
261 || Elements.isOperatorName(name)); | |
262 assert(!isPrivateName(name) || library != null); | |
263 } | 256 } |
264 | 257 |
265 static Map<int, List<Selector>> canonicalizedValues = | 258 /// `true` if this call has named arguments. |
266 new Map<int, List<Selector>>(); | 259 bool get isNamed => false; |
267 | 260 |
268 factory Selector(SelectorKind kind, | 261 /// `true` if this call has no named arguments. |
269 String name, | 262 bool get isUnnamed => true; |
270 LibraryElement library, | 263 |
271 int argumentCount, | 264 /// The names of the named arguments in call-site order. |
272 [List<String> namedArguments]) { | 265 List<String> get namedArguments => const <String>[]; |
273 if (!isPrivateName(name)) library = null; | 266 |
274 if (namedArguments == null) namedArguments = const <String>[]; | 267 /// The names of the named arguments in canonicalized order. |
275 int hashCode = computeHashCode( | 268 List<String> getOrderedNamedArguments() => const <String>[]; |
276 kind, name, library, argumentCount, namedArguments); | 269 |
277 List<Selector> list = canonicalizedValues.putIfAbsent(hashCode, | 270 /// A description of the argument structure. |
278 () => <Selector>[]); | 271 String structureToString() => 'arity=$argumentCount'; |
279 for (int i = 0; i < list.length; i++) { | 272 |
280 Selector existing = list[i]; | 273 String toString() => 'CallStructure(${structureToString()})'; |
281 if (existing.match(kind, name, library, argumentCount, namedArguments)) { | 274 |
282 assert(existing.hashCode == hashCode); | 275 Selector get callSelector { |
283 assert(existing.mask == null); | 276 return new Selector(SelectorKind.CALL, Selector.CALL_NAME, this); |
284 return existing; | |
285 } | |
286 } | |
287 List<String> orderedNamedArguments = namedArguments.isEmpty | |
288 ? const <String>[] | |
289 : <String>[]; | |
290 Selector result = new Selector.internal( | |
291 kind, name, library, argumentCount, | |
292 namedArguments, orderedNamedArguments, | |
293 hashCode); | |
294 list.add(result); | |
295 return result; | |
296 } | 277 } |
297 | 278 |
298 factory Selector.fromElement(Element element) { | 279 bool match(CallStructure other) { |
299 String name = element.name; | 280 if (identical(this, other)) return true; |
300 if (element.isFunction) { | 281 return this.argumentCount == other.argumentCount |
301 if (name == '[]') { | 282 && this.namedArgumentCount == other.namedArgumentCount |
302 return new Selector.index(); | 283 && sameNames(this.namedArguments, other.namedArguments); |
303 } else if (name == '[]=') { | |
304 return new Selector.indexSet(); | |
305 } | |
306 FunctionSignature signature = | |
307 element.asFunctionElement().functionSignature; | |
308 int arity = signature.parameterCount; | |
309 List<String> namedArguments = null; | |
310 if (signature.optionalParametersAreNamed) { | |
311 namedArguments = | |
312 signature.orderedOptionalParameters.map((e) => e.name).toList(); | |
313 } | |
314 if (element.isOperator) { | |
315 // Operators cannot have named arguments, however, that doesn't prevent | |
316 // a user from declaring such an operator. | |
317 return new Selector( | |
318 SelectorKind.OPERATOR, name, null, arity, namedArguments); | |
319 } else { | |
320 return new Selector.call( | |
321 name, element.library, arity, namedArguments); | |
322 } | |
323 } else if (element.isSetter) { | |
324 return new Selector.setter(name, element.library); | |
325 } else if (element.isGetter) { | |
326 return new Selector.getter(name, element.library); | |
327 } else if (element.isField) { | |
328 return new Selector.getter(name, element.library); | |
329 } else if (element.isConstructor) { | |
330 return new Selector.callConstructor(name, element.library); | |
331 } else { | |
332 throw new SpannableAssertionFailure( | |
333 element, "Can't get selector from $element"); | |
334 } | |
335 } | 284 } |
336 | 285 |
337 factory Selector.getter(String name, LibraryElement library) | 286 // TODO(johnniwinther): Cache hash code? |
338 => new Selector(SelectorKind.GETTER, name, library, 0); | 287 int get hashCode { |
339 | 288 int named = namedArguments.length; |
340 factory Selector.getterFrom(Selector selector) | 289 int hash = mixHashCodeBits(argumentCount, named); |
341 => new Selector(SelectorKind.GETTER, selector.name, selector.library, 0); | 290 for (int i = 0; i < named; i++) { |
342 | 291 hash = mixHashCodeBits(hash, namedArguments[i].hashCode); |
343 factory Selector.setter(String name, LibraryElement library) | 292 } |
344 => new Selector(SelectorKind.SETTER, name, library, 1); | 293 return hash; |
345 | |
346 factory Selector.unaryOperator(String name) | |
347 => new Selector(SelectorKind.OPERATOR, | |
348 Elements.constructOperatorName(name, true), | |
349 null, 0); | |
350 | |
351 factory Selector.binaryOperator(String name) | |
352 => new Selector(SelectorKind.OPERATOR, | |
353 Elements.constructOperatorName(name, false), | |
354 null, 1); | |
355 | |
356 factory Selector.index() | |
357 => new Selector(SelectorKind.INDEX, | |
358 Elements.constructOperatorName(INDEX_NAME, false), | |
359 null, 1); | |
360 | |
361 factory Selector.indexSet() | |
362 => new Selector(SelectorKind.INDEX, | |
363 Elements.constructOperatorName(INDEX_SET_NAME, false), | |
364 null, 2); | |
365 | |
366 factory Selector.call(String name, | |
367 LibraryElement library, | |
368 int arity, | |
369 [List<String> namedArguments]) | |
370 => new Selector(SelectorKind.CALL, name, library, arity, namedArguments); | |
371 | |
372 factory Selector.callClosure(int arity, [List<String> namedArguments]) | |
373 => new Selector(SelectorKind.CALL, CALL_NAME, null, | |
374 arity, namedArguments); | |
375 | |
376 factory Selector.callClosureFrom(Selector selector) | |
377 => new Selector(SelectorKind.CALL, CALL_NAME, null, | |
378 selector.argumentCount, selector.namedArguments); | |
379 | |
380 factory Selector.callConstructor(String name, LibraryElement library, | |
381 [int arity = 0, | |
382 List<String> namedArguments]) | |
383 => new Selector(SelectorKind.CALL, name, library, | |
384 arity, namedArguments); | |
385 | |
386 factory Selector.callDefaultConstructor() | |
387 => new Selector(SelectorKind.CALL, "", null, 0); | |
388 | |
389 bool get isGetter => identical(kind, SelectorKind.GETTER); | |
390 bool get isSetter => identical(kind, SelectorKind.SETTER); | |
391 bool get isCall => identical(kind, SelectorKind.CALL); | |
392 bool get isClosureCall { | |
393 String callName = Compiler.CALL_OPERATOR_NAME; | |
394 return isCall && name == callName; | |
395 } | 294 } |
396 | 295 |
397 bool get isIndex => identical(kind, SelectorKind.INDEX) && argumentCount == 1; | 296 bool operator ==(other) { |
398 bool get isIndexSet => identical(kind, SelectorKind.INDEX) && argumentCount == 2; | 297 if (other is! CallStructure) return false; |
399 | 298 return match(other); |
400 bool get isOperator => identical(kind, SelectorKind.OPERATOR); | |
401 bool get isUnaryOperator => isOperator && argumentCount == 0; | |
402 | |
403 /** Check whether this is a call to 'assert'. */ | |
404 bool get isAssert => isCall && identical(name, "assert"); | |
405 | |
406 int get namedArgumentCount => namedArguments.length; | |
407 int get positionalArgumentCount => argumentCount - namedArgumentCount; | |
408 | |
409 bool get hasExactMask => false; | |
410 TypeMask get mask => null; | |
411 Selector get asUntyped => this; | |
412 | |
413 /** | |
414 * The member name for invocation mirrors created from this selector. | |
415 */ | |
416 String get invocationMirrorMemberName => | |
417 isSetter ? '$name=' : name; | |
418 | |
419 int get invocationMirrorKind { | |
420 const int METHOD = 0; | |
421 const int GETTER = 1; | |
422 const int SETTER = 2; | |
423 int kind = METHOD; | |
424 if (isGetter) { | |
425 kind = GETTER; | |
426 } else if (isSetter) { | |
427 kind = SETTER; | |
428 } | |
429 return kind; | |
430 } | |
431 | |
432 bool appliesUnnamed(Element element, World world) { | |
433 assert(sameNameHack(element, world)); | |
434 return appliesUntyped(element, world); | |
435 } | |
436 | |
437 bool appliesUntyped(Element element, World world) { | |
438 assert(sameNameHack(element, world)); | |
439 if (Elements.isUnresolved(element)) return false; | |
440 if (isPrivateName(name) && library != element.library) return false; | |
441 if (world.isForeign(element)) return true; | |
442 if (element.isSetter) return isSetter; | |
443 if (element.isGetter) return isGetter || isCall; | |
444 if (element.isField) { | |
445 return isSetter | |
446 ? !element.isFinal && !element.isConst | |
447 : isGetter || isCall; | |
448 } | |
449 if (isGetter) return true; | |
450 if (isSetter) return false; | |
451 return signatureApplies(element); | |
452 } | 299 } |
453 | 300 |
454 bool signatureApplies(FunctionElement function) { | 301 bool signatureApplies(FunctionElement function) { |
302 if (Elements.isUnresolved(function)) return false; | |
455 FunctionSignature parameters = function.functionSignature; | 303 FunctionSignature parameters = function.functionSignature; |
456 if (argumentCount > parameters.parameterCount) return false; | 304 if (argumentCount > parameters.parameterCount) return false; |
457 int requiredParameterCount = parameters.requiredParameterCount; | 305 int requiredParameterCount = parameters.requiredParameterCount; |
458 int optionalParameterCount = parameters.optionalParameterCount; | 306 int optionalParameterCount = parameters.optionalParameterCount; |
459 if (positionalArgumentCount < requiredParameterCount) return false; | 307 if (positionalArgumentCount < requiredParameterCount) return false; |
460 | 308 |
461 if (!parameters.optionalParametersAreNamed) { | 309 if (!parameters.optionalParametersAreNamed) { |
462 // We have already checked that the number of arguments are | 310 // We have already checked that the number of arguments are |
463 // not greater than the number of parameters. Therefore the | 311 // not greater than the number of parameters. Therefore the |
464 // number of positional arguments are not greater than the | 312 // number of positional arguments are not greater than the |
(...skipping 12 matching lines...) Expand all Loading... | |
477 if (!nameSet.contains(name)) return false; | 325 if (!nameSet.contains(name)) return false; |
478 // TODO(5213): By removing from the set we are checking | 326 // TODO(5213): By removing from the set we are checking |
479 // that we are not passing the name twice. We should have this | 327 // that we are not passing the name twice. We should have this |
480 // check in the resolver also. | 328 // check in the resolver also. |
481 nameSet.remove(name); | 329 nameSet.remove(name); |
482 } | 330 } |
483 return true; | 331 return true; |
484 } | 332 } |
485 } | 333 } |
486 | 334 |
487 bool sameNameHack(Element element, World world) { | |
488 // TODO(ngeoffray): Remove workaround checks. | |
489 return element.isConstructor || | |
490 name == element.name || | |
491 name == 'assert' && world.isAssertMethod(element); | |
492 } | |
493 | |
494 bool applies(Element element, World world) { | |
495 if (!sameNameHack(element, world)) return false; | |
496 return appliesUnnamed(element, world); | |
497 } | |
498 | |
499 /** | 335 /** |
500 * Returns a `List` with the evaluated arguments in the normalized order. | 336 * Returns a `List` with the evaluated arguments in the normalized order. |
501 * | 337 * |
502 * [compileDefaultValue] is a function that returns a compiled constant | 338 * [compileDefaultValue] is a function that returns a compiled constant |
503 * of an optional argument that is not in [compiledArguments]. | 339 * of an optional argument that is not in [compiledArguments]. |
504 * | 340 * |
505 * Precondition: `this.applies(element, world)`. | 341 * Precondition: `this.applies(element, world)`. |
506 * | 342 * |
507 * Invariant: [element] must be the implementation element. | 343 * Invariant: [element] must be the implementation element. |
508 */ | 344 */ |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
557 * | 393 * |
558 * [compileArgument] is a function that returns a compiled version | 394 * [compileArgument] is a function that returns a compiled version |
559 * of a parameter of [callee]. | 395 * of a parameter of [callee]. |
560 * | 396 * |
561 * [compileConstant] is a function that returns a compiled constant | 397 * [compileConstant] is a function that returns a compiled constant |
562 * of an optional argument that is not in the parameters of [callee]. | 398 * of an optional argument that is not in the parameters of [callee]. |
563 * | 399 * |
564 * Returns [:true:] if the signature of the [caller] matches the | 400 * Returns [:true:] if the signature of the [caller] matches the |
565 * signature of the [callee], [:false:] otherwise. | 401 * signature of the [callee], [:false:] otherwise. |
566 */ | 402 */ |
567 static bool addForwardingElementArgumentsToList( | 403 static /*<T>*/ bool addForwardingElementArgumentsToList( |
568 FunctionElement caller, | 404 ConstructorElement caller, |
569 List list, | 405 List/*<T>*/ list, |
570 FunctionElement callee, | 406 ConstructorElement callee, |
571 compileArgument(Element element), | 407 /*T*/ compileArgument(ParameterElement element), |
572 compileConstant(Element element), | 408 /*T*/ compileConstant(ParameterElement element)) { |
573 World world) { | |
574 | 409 |
575 FunctionSignature signature = caller.functionSignature; | 410 FunctionSignature signature = caller.functionSignature; |
576 Map mapping = new Map(); | 411 Map<Node, ParameterElement> mapping = <Node, ParameterElement>{}; |
577 | 412 |
578 // TODO(ngeoffray): This is a hack that fakes up AST nodes, so | 413 // TODO(ngeoffray): This is a hack that fakes up AST nodes, so |
579 // that we can call [addArgumentsToList]. | 414 // that we can call [addArgumentsToList]. |
580 Link computeCallNodesFromParameters() { | 415 Link<Node> computeCallNodesFromParameters() { |
581 LinkBuilder builder = new LinkBuilder(); | 416 LinkBuilder<Node> builder = new LinkBuilder<Node>(); |
582 signature.forEachRequiredParameter((ParameterElement element) { | 417 signature.forEachRequiredParameter((ParameterElement element) { |
583 Node node = element.node; | 418 Node node = element.node; |
584 mapping[node] = element; | 419 mapping[node] = element; |
585 builder.addLast(node); | 420 builder.addLast(node); |
586 }); | 421 }); |
587 if (signature.optionalParametersAreNamed) { | 422 if (signature.optionalParametersAreNamed) { |
588 signature.forEachOptionalParameter((ParameterElement element) { | 423 signature.forEachOptionalParameter((ParameterElement element) { |
589 mapping[element.initializer] = element; | 424 mapping[element.initializer] = element; |
590 builder.addLast(new NamedArgument(null, null, element.initializer)); | 425 builder.addLast(new NamedArgument(null, null, element.initializer)); |
591 }); | 426 }); |
592 } else { | 427 } else { |
593 signature.forEachOptionalParameter((ParameterElement element) { | 428 signature.forEachOptionalParameter((ParameterElement element) { |
594 Node node = element.node; | 429 Node node = element.node; |
595 mapping[node] = element; | 430 mapping[node] = element; |
596 builder.addLast(node); | 431 builder.addLast(node); |
597 }); | 432 }); |
598 } | 433 } |
599 return builder.toLink(); | 434 return builder.toLink(); |
600 } | 435 } |
601 | 436 |
602 internalCompileArgument(Node node) { | 437 /*T*/ internalCompileArgument(Node node) { |
603 return compileArgument(mapping[node]); | 438 return compileArgument(mapping[node]); |
604 } | 439 } |
605 | 440 |
606 Link<Node> nodes = computeCallNodesFromParameters(); | 441 Link<Node> nodes = computeCallNodesFromParameters(); |
607 | 442 |
608 // Synthesize a selector for the call. | 443 // Synthesize a structure for the call. |
609 // TODO(ngeoffray): Should the resolver do it instead? | 444 // TODO(ngeoffray): Should the resolver do it instead? |
610 List<String> namedParameters; | 445 List<String> namedParameters; |
611 if (signature.optionalParametersAreNamed) { | 446 if (signature.optionalParametersAreNamed) { |
612 namedParameters = | 447 namedParameters = signature.optionalParameters.mapToList((e) => e.name); |
613 signature.optionalParameters.mapToList((e) => e.name); | |
614 } | 448 } |
615 Selector selector = new Selector.call(callee.name, | 449 CallStructure callStructure = |
616 caller.library, | 450 new CallStructure(signature.parameterCount, namedParameters); |
617 signature.parameterCount, | 451 if (!callStructure.signatureApplies(callee)) { |
618 namedParameters); | 452 return false; |
619 | 453 } |
620 if (!selector.applies(callee, world)) return false; | 454 list.addAll(callStructure.makeArgumentsList( |
621 list.addAll(selector.makeArgumentsList(nodes, | 455 nodes, |
622 callee, | 456 callee, |
623 internalCompileArgument, | 457 internalCompileArgument, |
624 compileConstant)); | 458 compileConstant)); |
625 | 459 |
626 return true; | 460 return true; |
627 } | 461 } |
628 | 462 |
629 static bool sameNames(List<String> first, List<String> second) { | 463 static bool sameNames(List<String> first, List<String> second) { |
630 for (int i = 0; i < first.length; i++) { | 464 for (int i = 0; i < first.length; i++) { |
631 if (first[i] != second[i]) return false; | 465 if (first[i] != second[i]) return false; |
632 } | 466 } |
633 return true; | 467 return true; |
634 } | 468 } |
469 } | |
635 | 470 |
636 bool match(SelectorKind kind, | 471 /// |
637 String name, | 472 class NamedCallStructure extends CallStructure { |
638 LibraryElement library, | 473 final List<String> namedArguments; |
639 int argumentCount, | 474 final List<String> _orderedNamedArguments = <String>[]; |
640 List<String> namedArguments) { | 475 |
641 return this.kind == kind | 476 NamedCallStructure(int argumentCount, this.namedArguments) |
642 && this.name == name | 477 : super.unnamed(argumentCount) { |
643 && identical(this.library, library) | 478 assert(namedArguments.isNotEmpty); |
644 && this.argumentCount == argumentCount | |
645 && this.namedArguments.length == namedArguments.length | |
646 && sameNames(this.namedArguments, namedArguments); | |
647 } | 479 } |
648 | 480 |
649 static int computeHashCode(SelectorKind kind, | 481 @override |
650 String name, | 482 bool get isNamed => true; |
651 LibraryElement library, | |
652 int argumentCount, | |
653 List<String> namedArguments) { | |
654 // Add bits from name and kind. | |
655 int hash = mixHashCodeBits(name.hashCode, kind.hashCode); | |
656 // Add bits from the library. | |
657 if (library != null) hash = mixHashCodeBits(hash, library.hashCode); | |
658 // Add bits from the unnamed arguments. | |
659 hash = mixHashCodeBits(hash, argumentCount); | |
660 // Add bits from the named arguments. | |
661 int named = namedArguments.length; | |
662 hash = mixHashCodeBits(hash, named); | |
663 for (int i = 0; i < named; i++) { | |
664 hash = mixHashCodeBits(hash, namedArguments[i].hashCode); | |
665 } | |
666 return hash; | |
667 } | |
668 | 483 |
669 // TODO(kasperl): Move this out so it becomes useful in other places too? | 484 @override |
670 static int mixHashCodeBits(int existing, int value) { | 485 bool get isUnnamed => false; |
671 // Spread the bits of value. Try to stay in the 30-bit range to | |
672 // avoid overflowing into a more expensive integer representation. | |
673 int h = value & 0x1fffffff; | |
674 h += ((h & 0x3fff) << 15) ^ 0x1fffcd7d; | |
675 h ^= (h >> 10); | |
676 h += ((h & 0x3ffffff) << 3); | |
677 h ^= (h >> 6); | |
678 h += ((h & 0x7ffffff) << 2) + ((h & 0x7fff) << 14); | |
679 h ^= (h >> 16); | |
680 // Combine the two hash values. | |
681 int high = existing >> 15; | |
682 int low = existing & 0x7fff; | |
683 return ((high * 13) ^ (low * 997) ^ h) & SMI_MASK; | |
684 } | |
685 | 486 |
487 @override | |
488 int get namedArgumentCount => namedArguments.length; | |
489 | |
490 @override | |
491 int get positionalArgumentCount => argumentCount - namedArgumentCount; | |
492 | |
493 @override | |
686 List<String> getOrderedNamedArguments() { | 494 List<String> getOrderedNamedArguments() { |
687 if (namedArguments.isEmpty) return namedArguments; | |
688 if (!_orderedNamedArguments.isEmpty) return _orderedNamedArguments; | 495 if (!_orderedNamedArguments.isEmpty) return _orderedNamedArguments; |
689 | 496 |
690 _orderedNamedArguments.addAll(namedArguments); | 497 _orderedNamedArguments.addAll(namedArguments); |
691 _orderedNamedArguments.sort((String first, String second) { | 498 _orderedNamedArguments.sort((String first, String second) { |
692 return first.compareTo(second); | 499 return first.compareTo(second); |
693 }); | 500 }); |
694 return _orderedNamedArguments; | 501 return _orderedNamedArguments; |
695 } | 502 } |
696 | 503 |
697 String namedArgumentsToString() { | 504 @override |
698 if (namedArgumentCount > 0) { | 505 String structureToString() { |
699 StringBuffer result = new StringBuffer(); | 506 return 'arity=$argumentCount, named=[${namedArguments.join(', ')}]'; |
700 for (int i = 0; i < namedArgumentCount; i++) { | 507 } |
701 if (i != 0) result.write(', '); | 508 } |
702 result.write(namedArguments[i]); | 509 |
703 } | 510 class Selector { |
704 return "[$result]"; | 511 final SelectorKind kind; |
705 } | 512 final Name memberName; |
706 return ''; | 513 final CallStructure callStructure; |
514 | |
515 final int hashCode; | |
516 | |
517 int get argumentCount => callStructure.argumentCount; | |
518 int get namedArgumentCount => callStructure.namedArgumentCount; | |
519 int get positionalArgumentCount => callStructure.positionalArgumentCount; | |
520 List<String> get namedArguments => callStructure.namedArguments; | |
521 | |
522 String get name => memberName.text; | |
523 | |
524 LibraryElement get library => memberName.library; | |
525 | |
526 static const Name INDEX_NAME = const PublicName("[]"); | |
527 static const Name INDEX_SET_NAME = const PublicName("[]="); | |
528 static const Name CALL_NAME = const PublicName(Compiler.CALL_OPERATOR_NAME); | |
529 | |
530 Selector.internal(this.kind, | |
531 this.memberName, | |
532 this.callStructure, | |
533 this.hashCode) { | |
534 assert(kind == SelectorKind.INDEX || | |
535 (memberName != INDEX_NAME && memberName != INDEX_SET_NAME)); | |
536 assert(kind == SelectorKind.OPERATOR || | |
537 kind == SelectorKind.INDEX || | |
538 !Elements.isOperatorName(memberName.text)); | |
539 assert(kind == SelectorKind.CALL || | |
540 kind == SelectorKind.GETTER || | |
541 kind == SelectorKind.SETTER || | |
542 Elements.isOperatorName(memberName.text)); | |
543 } | |
544 | |
545 static Map<int, List<Selector>> canonicalizedValues = | |
floitsch
2015/04/07 14:09:21
To think about:
I would like to remove this static
Johnni Winther
2015/04/08 12:25:11
Added a TODO.
| |
546 new Map<int, List<Selector>>(); | |
547 | |
548 factory Selector(SelectorKind kind, | |
549 Name name, | |
550 CallStructure callStructure) { | |
551 int hashCode = computeHashCode(kind, name, callStructure); | |
552 List<Selector> list = canonicalizedValues.putIfAbsent(hashCode, | |
floitsch
2015/04/07 14:09:21
Not your code (and not for this CL):
I wonder if
Johnni Winther
2015/04/08 12:25:11
Add a TODO.
| |
553 () => <Selector>[]); | |
554 for (int i = 0; i < list.length; i++) { | |
555 Selector existing = list[i]; | |
556 if (existing.match(kind, name, callStructure)) { | |
557 assert(existing.hashCode == hashCode); | |
558 assert(existing.mask == null); | |
559 return existing; | |
560 } | |
561 } | |
562 Selector result = new Selector.internal( | |
563 kind, name, callStructure, hashCode); | |
564 list.add(result); | |
565 return result; | |
566 } | |
567 | |
568 factory Selector.fromElement(Element element) { | |
569 String name = element.name; | |
570 if (element.isFunction) { | |
571 if (name == '[]') { | |
572 return new Selector.index(); | |
573 } else if (name == '[]=') { | |
574 return new Selector.indexSet(); | |
575 } | |
576 FunctionSignature signature = | |
577 element.asFunctionElement().functionSignature; | |
578 int arity = signature.parameterCount; | |
579 List<String> namedArguments = null; | |
580 if (signature.optionalParametersAreNamed) { | |
581 namedArguments = | |
582 signature.orderedOptionalParameters.map((e) => e.name).toList(); | |
583 } | |
584 if (element.isOperator) { | |
585 // Operators cannot have named arguments, however, that doesn't prevent | |
586 // a user from declaring such an operator. | |
587 return new Selector( | |
588 SelectorKind.OPERATOR, | |
589 new PublicName(name), | |
590 new CallStructure(arity, namedArguments)); | |
591 } else { | |
592 return new Selector.call( | |
593 name, element.library, arity, namedArguments); | |
594 } | |
595 } else if (element.isSetter) { | |
596 return new Selector.setter(name, element.library); | |
597 } else if (element.isGetter) { | |
598 return new Selector.getter(name, element.library); | |
599 } else if (element.isField) { | |
600 return new Selector.getter(name, element.library); | |
601 } else if (element.isConstructor) { | |
602 return new Selector.callConstructor(name, element.library); | |
603 } else { | |
604 throw new SpannableAssertionFailure( | |
605 element, "Can't get selector from $element"); | |
606 } | |
607 } | |
608 | |
609 factory Selector.getter(String name, LibraryElement library) | |
610 => new Selector(SelectorKind.GETTER, | |
611 new Name(name, library), | |
612 CallStructure.NO_ARGS); | |
613 | |
614 factory Selector.getterFrom(Selector selector) | |
615 => new Selector(SelectorKind.GETTER, | |
616 selector.memberName, | |
617 CallStructure.NO_ARGS); | |
618 | |
619 factory Selector.setter(String name, LibraryElement library) | |
620 => new Selector(SelectorKind.SETTER, | |
621 new Name(name, library, isSetter: true), | |
622 CallStructure.ONE_ARG); | |
623 | |
624 factory Selector.unaryOperator(String name) => new Selector( | |
625 SelectorKind.OPERATOR, | |
626 new PublicName(Elements.constructOperatorName(name, true)), | |
627 CallStructure.NO_ARGS); | |
628 | |
629 factory Selector.binaryOperator(String name) => new Selector( | |
630 SelectorKind.OPERATOR, | |
631 new PublicName(Elements.constructOperatorName(name, false)), | |
632 CallStructure.ONE_ARG); | |
633 | |
634 factory Selector.index() | |
635 => new Selector(SelectorKind.INDEX, INDEX_NAME, | |
636 CallStructure.ONE_ARG); | |
637 | |
638 factory Selector.indexSet() | |
639 => new Selector(SelectorKind.INDEX, INDEX_SET_NAME, | |
640 CallStructure.TWO_ARGS); | |
641 | |
642 factory Selector.call(String name, | |
643 LibraryElement library, | |
644 int arity, | |
645 [List<String> namedArguments]) | |
646 => new Selector(SelectorKind.CALL, | |
647 new Name(name, library), | |
648 new CallStructure(arity, namedArguments)); | |
649 | |
650 factory Selector.callClosure(int arity, [List<String> namedArguments]) | |
651 => new Selector(SelectorKind.CALL, CALL_NAME, | |
652 new CallStructure(arity, namedArguments)); | |
653 | |
654 factory Selector.callClosureFrom(Selector selector) | |
655 => new Selector(SelectorKind.CALL, CALL_NAME, selector.callStructure); | |
656 | |
657 factory Selector.callConstructor(String name, LibraryElement library, | |
658 [int arity = 0, | |
659 List<String> namedArguments]) | |
660 => new Selector(SelectorKind.CALL, new Name(name, library), | |
661 new CallStructure(arity, namedArguments)); | |
662 | |
663 factory Selector.callDefaultConstructor() | |
664 => new Selector( | |
665 SelectorKind.CALL, | |
666 const PublicName(''), | |
667 CallStructure.NO_ARGS); | |
668 | |
669 bool get isGetter => kind == SelectorKind.GETTER; | |
670 bool get isSetter => kind == SelectorKind.SETTER; | |
671 bool get isCall => kind == SelectorKind.CALL; | |
672 bool get isClosureCall => isCall && memberName == CALL_NAME; | |
673 | |
674 bool get isIndex => kind == SelectorKind.INDEX && argumentCount == 1; | |
675 bool get isIndexSet => kind == SelectorKind.INDEX && argumentCount == 2; | |
676 | |
677 bool get isOperator => kind == SelectorKind.OPERATOR; | |
678 bool get isUnaryOperator => isOperator && argumentCount == 0; | |
679 | |
680 /** Check whether this is a call to 'assert'. */ | |
681 bool get isAssert => isCall && identical(name, "assert"); | |
682 | |
683 bool get hasExactMask => false; | |
684 TypeMask get mask => null; | |
685 Selector get asUntyped => this; | |
686 | |
687 /** | |
688 * The member name for invocation mirrors created from this selector. | |
689 */ | |
690 String get invocationMirrorMemberName => | |
691 isSetter ? '$name=' : name; | |
692 | |
693 int get invocationMirrorKind { | |
694 const int METHOD = 0; | |
695 const int GETTER = 1; | |
696 const int SETTER = 2; | |
697 int kind = METHOD; | |
698 if (isGetter) { | |
699 kind = GETTER; | |
700 } else if (isSetter) { | |
701 kind = SETTER; | |
702 } | |
703 return kind; | |
704 } | |
705 | |
706 bool appliesUnnamed(Element element, World world) { | |
707 assert(sameNameHack(element, world)); | |
708 return appliesUntyped(element, world); | |
709 } | |
710 | |
711 bool appliesUntyped(Element element, World world) { | |
712 assert(sameNameHack(element, world)); | |
713 if (Elements.isUnresolved(element)) return false; | |
714 if (memberName.isPrivate && memberName.library != element.library) { | |
715 // TODO(johnniwinther): Maybe this should be | |
716 // `memberName != element.memberName`. | |
717 return false; | |
718 } | |
719 if (world.isForeign(element)) return true; | |
720 if (element.isSetter) return isSetter; | |
721 if (element.isGetter) return isGetter || isCall; | |
722 if (element.isField) { | |
723 return isSetter | |
724 ? !element.isFinal && !element.isConst | |
725 : isGetter || isCall; | |
726 } | |
727 if (isGetter) return true; | |
728 if (isSetter) return false; | |
729 return signatureApplies(element); | |
730 } | |
731 | |
732 bool signatureApplies(FunctionElement function) { | |
733 return callStructure.signatureApplies(function); | |
734 } | |
735 | |
736 bool sameNameHack(Element element, World world) { | |
737 // TODO(ngeoffray): Remove workaround checks. | |
738 return element.isConstructor || | |
739 name == element.name || | |
740 name == 'assert' && world.isAssertMethod(element); | |
741 } | |
742 | |
743 bool applies(Element element, World world) { | |
744 if (!sameNameHack(element, world)) return false; | |
745 return appliesUnnamed(element, world); | |
746 } | |
747 | |
748 bool match(SelectorKind kind, | |
749 Name memberName, | |
750 CallStructure callStructure) { | |
751 return this.kind == kind | |
752 && this.memberName == memberName | |
753 && this.callStructure.match(callStructure); | |
754 } | |
755 | |
756 static int computeHashCode(SelectorKind kind, | |
757 Name name, | |
758 CallStructure callStructure) { | |
759 // Add bits from name and kind. | |
760 int hash = mixHashCodeBits(name.hashCode, kind.hashCode); | |
761 // Add bits from the call structure. | |
762 return mixHashCodeBits(hash, callStructure.hashCode); | |
707 } | 763 } |
708 | 764 |
709 String toString() { | 765 String toString() { |
710 String named = ''; | |
711 String type = ''; | 766 String type = ''; |
712 if (namedArgumentCount > 0) named = ', named=${namedArgumentsToString()}'; | |
713 if (mask != null) type = ', mask=$mask'; | 767 if (mask != null) type = ', mask=$mask'; |
714 return 'Selector($kind, $name, ' | 768 return 'Selector($kind, $name, ${callStructure.structureToString()}$type)'; |
715 'arity=$argumentCount$named$type)'; | |
716 } | 769 } |
717 | 770 |
718 Selector extendIfReachesAll(Compiler compiler) { | 771 Selector extendIfReachesAll(Compiler compiler) { |
719 return new TypedSelector( | 772 return new TypedSelector( |
720 compiler.typesTask.dynamicType, this, compiler.world); | 773 compiler.typesTask.dynamicType, this, compiler.world); |
721 } | 774 } |
722 | 775 |
723 Selector toCallSelector() => new Selector.callClosureFrom(this); | 776 Selector toCallSelector() => new Selector.callClosureFrom(this); |
724 } | 777 } |
725 | 778 |
726 class TypedSelector extends Selector { | 779 class TypedSelector extends Selector { |
727 final Selector asUntyped; | 780 final Selector asUntyped; |
728 final TypeMask mask; | 781 final TypeMask mask; |
729 | 782 |
730 TypedSelector.internal(this.mask, Selector selector, int hashCode) | 783 TypedSelector.internal(this.mask, Selector selector, int hashCode) |
731 : asUntyped = selector, | 784 : asUntyped = selector, |
732 super.internal(selector.kind, | 785 super.internal(selector.kind, |
733 selector.name, | 786 selector.memberName, |
734 selector.library, | 787 selector.callStructure, |
735 selector.argumentCount, | |
736 selector.namedArguments, | |
737 selector._orderedNamedArguments, | |
738 hashCode) { | 788 hashCode) { |
739 assert(mask != null); | 789 assert(mask != null); |
740 assert(asUntyped.mask == null); | 790 assert(asUntyped.mask == null); |
741 } | 791 } |
742 | 792 |
743 | 793 |
744 factory TypedSelector(TypeMask mask, Selector selector, World world) { | 794 factory TypedSelector(TypeMask mask, Selector selector, World world) { |
745 if (!world.hasClosedWorldAssumption) { | 795 if (!world.hasClosedWorldAssumption) { |
746 // TODO(johnniwinther): Improve use of TypedSelector in an open world. | 796 // TODO(johnniwinther): Improve use of TypedSelector in an open world. |
747 bool isNullable = mask.isNullable; | 797 bool isNullable = mask.isNullable; |
748 mask = world.compiler.typesTask.dynamicType; | 798 mask = world.compiler.typesTask.dynamicType; |
749 if (isNullable) { | 799 if (isNullable) { |
750 mask = mask.nullable(); | 800 mask = mask.nullable(); |
751 } | 801 } |
752 } | 802 } |
753 // TODO(johnniwinther): Allow more TypeSelector kinds during resoluton. | 803 // TODO(johnniwinther): Allow more TypeSelector kinds during resoluton. |
754 assert(world.isClosed || mask.isExact); | 804 assert(world.isClosed || mask.isExact); |
755 if (selector.mask == mask) return selector; | 805 if (selector.mask == mask) return selector; |
756 Selector untyped = selector.asUntyped; | 806 Selector untyped = selector.asUntyped; |
757 Map<TypeMask, TypedSelector> map = world.canonicalizedValues | 807 Map<TypeMask, TypedSelector> map = world.canonicalizedValues |
758 .putIfAbsent(untyped, () => new Map<TypeMask, TypedSelector>()); | 808 .putIfAbsent(untyped, () => new Map<TypeMask, TypedSelector>()); |
759 TypedSelector result = map[mask]; | 809 TypedSelector result = map[mask]; |
760 if (result == null) { | 810 if (result == null) { |
761 int hashCode = Selector.mixHashCodeBits(untyped.hashCode, mask.hashCode); | 811 int hashCode = mixHashCodeBits(untyped.hashCode, mask.hashCode); |
762 result = map[mask] = new TypedSelector.internal(mask, untyped, hashCode); | 812 result = map[mask] = new TypedSelector.internal(mask, untyped, hashCode); |
763 } | 813 } |
764 return result; | 814 return result; |
765 } | 815 } |
766 | 816 |
767 factory TypedSelector.exact( | 817 factory TypedSelector.exact( |
768 ClassElement base, Selector selector, World world) | 818 ClassElement base, Selector selector, World world) |
769 => new TypedSelector(new TypeMask.exact(base, world), selector, | 819 => new TypedSelector(new TypeMask.exact(base, world), selector, |
770 world); | 820 world); |
771 | 821 |
(...skipping 27 matching lines...) Expand all Loading... | |
799 | 849 |
800 Selector extendIfReachesAll(Compiler compiler) { | 850 Selector extendIfReachesAll(Compiler compiler) { |
801 bool canReachAll = compiler.enabledInvokeOn | 851 bool canReachAll = compiler.enabledInvokeOn |
802 && mask.needsNoSuchMethodHandling(this, compiler.world); | 852 && mask.needsNoSuchMethodHandling(this, compiler.world); |
803 return canReachAll | 853 return canReachAll |
804 ? new TypedSelector( | 854 ? new TypedSelector( |
805 compiler.typesTask.dynamicType, this, compiler.world) | 855 compiler.typesTask.dynamicType, this, compiler.world) |
806 : this; | 856 : this; |
807 } | 857 } |
808 } | 858 } |
OLD | NEW |