OLD | NEW |
---|---|
1 # Copyright (C) 2013 Google Inc. All rights reserved. | 1 # Copyright (C) 2013 Google Inc. All rights reserved. |
2 # coding=utf-8 | 2 # coding=utf-8 |
3 # | 3 # |
4 # Redistribution and use in source and binary forms, with or without | 4 # Redistribution and use in source and binary forms, with or without |
5 # modification, are permitted provided that the following conditions are | 5 # modification, are permitted provided that the following conditions are |
6 # met: | 6 # met: |
7 # | 7 # |
8 # * Redistributions of source code must retain the above copyright | 8 # * Redistributions of source code must retain the above copyright |
9 # notice, this list of conditions and the following disclaimer. | 9 # notice, this list of conditions and the following disclaimer. |
10 # * Redistributions in binary form must reproduce the above | 10 # * Redistributions in binary form must reproduce the above |
(...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
322 """ | 322 """ |
323 # Filter to only methods that are actually overloaded | 323 # Filter to only methods that are actually overloaded |
324 method_counts = Counter(method['name'] for method in methods) | 324 method_counts = Counter(method['name'] for method in methods) |
325 overloaded_method_names = set(name | 325 overloaded_method_names = set(name |
326 for name, count in method_counts.iteritems() | 326 for name, count in method_counts.iteritems() |
327 if count > 1) | 327 if count > 1) |
328 overloaded_methods = [method for method in methods | 328 overloaded_methods = [method for method in methods |
329 if method['name'] in overloaded_method_names] | 329 if method['name'] in overloaded_method_names] |
330 | 330 |
331 # Group by name (generally will be defined together, but not necessarily) | 331 # Group by name (generally will be defined together, but not necessarily) |
332 overloaded_methods.sort(key=itemgetter('name')) | 332 method_overloads = sort_and_groupby(overloaded_methods, itemgetter('name')) |
333 method_overloads = dict( | |
334 (name, list(methods_iterator)) for name, methods_iterator in | |
335 itertools.groupby(overloaded_methods, itemgetter('name'))) | |
336 | 333 |
337 # Add overload information only to overloaded methods, so template code can | 334 # Add overload information only to overloaded methods, so template code can |
338 # easily verify if a function is overloaded | 335 # easily verify if a function is overloaded |
339 for name, overloads in method_overloads.iteritems(): | 336 for name, overloads in method_overloads.iteritems(): |
340 effective_overload_set(overloads) # to test, compute but discard result | 337 effective_overloads_by_length = effective_overload_set_by_length(overloa ds) |
338 for effective_overloads in effective_overloads_by_length.itervalues(): | |
339 # To test, compute but discard result | |
340 distinguishing_argument_index(effective_overloads) | |
341 | |
341 for index, method in enumerate(overloads, 1): | 342 for index, method in enumerate(overloads, 1): |
342 method.update({ | 343 method.update({ |
343 'overload_index': index, | 344 'overload_index': index, |
344 'overload_resolution_expression': | 345 'overload_resolution_expression': |
345 overload_resolution_expression(method), | 346 overload_resolution_expression(method), |
346 }) | 347 }) |
347 | 348 |
348 # Resolution function is generated after last overloaded function; | 349 # Resolution function is generated after last overloaded function; |
349 # package necessary information into |method.overloads| for that method. | 350 # package necessary information into |method.overloads| for that method. |
350 minimum_number_of_required_arguments = min( | 351 minimum_number_of_required_arguments = min( |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
385 Spec: http://heycam.github.io/webidl/#dfn-effective-overload-set | 386 Spec: http://heycam.github.io/webidl/#dfn-effective-overload-set |
386 | 387 |
387 Formally the input and output lists are sets, but methods are stored | 388 Formally the input and output lists are sets, but methods are stored |
388 internally as dicts, which can't be stored in a set because they are not | 389 internally as dicts, which can't be stored in a set because they are not |
389 hashable, so we use lists instead. | 390 hashable, so we use lists instead. |
390 | 391 |
391 Arguments: | 392 Arguments: |
392 F: list of overloads for a given callable name. | 393 F: list of overloads for a given callable name. |
393 | 394 |
394 Returns: | 395 Returns: |
395 S: list of tuples of the form <callable, type list, optionality list>. | 396 S: list of tuples of the form (callable, type list, optionality list). |
396 """ | 397 """ |
397 # Code closely follows the algorithm in the spec, for clarity and | 398 # Code closely follows the algorithm in the spec, for clarity and |
398 # correctness, and hence is not very Pythonic. | 399 # correctness, and hence is not very Pythonic. |
399 | 400 |
400 # 1. Initialize S to ∅. | 401 # 1. Initialize S to ∅. |
401 # (We use a list because we can't use a set, as noted above.) | 402 # (We use a list because we can't use a set, as noted above.) |
402 S = [] | 403 S = [] |
403 | 404 |
404 # 2. Let F be a set with elements as follows, according to the kind of | 405 # 2. Let F be a set with elements as follows, according to the kind of |
405 # effective overload set: | 406 # effective overload set: |
406 # (Passed as argument, nothing to do.) | 407 # (Passed as argument, nothing to do.) |
407 | 408 |
408 # 3. & 4. (maxarg, m) are only needed for variadics, not used. | 409 # 3. & 4. (maxarg, m) are only needed for variadics, not used. |
409 | 410 |
410 # 5. For each operation, extended attribute or callback function X in F: | 411 # 5. For each operation, extended attribute or callback function X in F: |
411 for X in F: # X is the "callable", F is the overloads. | 412 for X in F: # X is the "callable", F is the overloads. |
412 arguments = X['arguments'] | 413 arguments = X['arguments'] |
413 # 1. Let n be the number of arguments X is declared to take. | 414 # 1. Let n be the number of arguments X is declared to take. |
414 n = len(arguments) | 415 n = len(arguments) |
415 # 2. Let t0..n−1 be a list of types, where ti is the type of X’s | 416 # 2. Let t0..n−1 be a list of types, where ti is the type of X’s |
416 # argument at index i. | 417 # argument at index i. |
417 t = [argument['idl_type'] for argument in arguments] # type list | 418 # (“type list”) |
419 t = tuple(argument['idl_type_object'] for argument in arguments) | |
418 # 3. Let o0..n−1 be a list of optionality values, where oi is “variadic” | 420 # 3. Let o0..n−1 be a list of optionality values, where oi is “variadic” |
419 # if X’s argument at index i is a final, variadic argument, “optional” | 421 # if X’s argument at index i is a final, variadic argument, “optional” |
420 # if the argument is optional, and “required” otherwise. | 422 # if the argument is optional, and “required” otherwise. |
421 # (We're just using a boolean for optional vs. required.) | 423 # (“optionality list”) |
422 o = [argument['is_optional'] for argument in arguments] # optionality l ist | 424 # (We’re just using a boolean for optional vs. required.) |
425 o = tuple(argument['is_optional'] for argument in arguments) | |
423 # 4. Add to S the tuple <X, t0..n−1, o0..n−1>. | 426 # 4. Add to S the tuple <X, t0..n−1, o0..n−1>. |
424 S.append((X, t, o)) | 427 S.append((X, t, o)) |
425 # 5. If X is declared to be variadic, then: | 428 # 5. If X is declared to be variadic, then: |
426 # (Not used, so not implemented.) | 429 # (Not used, so not implemented.) |
427 # 6. Initialize i to n−1. | 430 # 6. Initialize i to n−1. |
428 i = n - 1 | 431 i = n - 1 |
429 # 7. While i ≥ 0: | 432 # 7. While i ≥ 0: |
430 # Spec bug (fencepost error); should be “While i > 0:” | 433 # Spec bug (fencepost error); should be “While i > 0:” |
431 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=25590 | 434 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=25590 |
432 while i > 0: | 435 while i > 0: |
433 # 1. If argument i of X is not optional, then break this loop. | 436 # 1. If argument i of X is not optional, then break this loop. |
434 if not o[i]: | 437 if not o[i]: |
435 break | 438 break |
436 # 2. Otherwise, add to S the tuple <X, t0..i−1, o0..i−1>. | 439 # 2. Otherwise, add to S the tuple <X, t0..i−1, o0..i−1>. |
437 S.append((X, t[:i], o[:i])) | 440 S.append((X, t[:i], o[:i])) |
438 # 3. Set i to i−1. | 441 # 3. Set i to i−1. |
439 i = i - 1 | 442 i = i - 1 |
440 # 8. If n > 0 and all arguments of X are optional, then add to S the | 443 # 8. If n > 0 and all arguments of X are optional, then add to S the |
441 # tuple <X, (), ()> (where “()” represents the empty list). | 444 # tuple <X, (), ()> (where “()” represents the empty list). |
442 if n > 0 and all(oi for oi in o): | 445 if n > 0 and all(oi for oi in o): |
443 S.append((X, [], [])) | 446 S.append((X, [], [])) |
444 # 6. The effective overload set is S. | 447 # 6. The effective overload set is S. |
445 return S | 448 return S |
446 | 449 |
447 | 450 |
451 def effective_overload_set_by_length(overloads): | |
452 def type_list_length(entry): | |
453 # Entries in the effective overload set are 3-tuples: | |
454 # (callable, type list, optionality list) | |
455 return len(entry[1]) | |
456 | |
457 effective_overloads = effective_overload_set(overloads) | |
458 return sort_and_groupby(effective_overloads, type_list_length) | |
459 | |
460 | |
461 def distinguishing_argument_index(entries): | |
462 """Returns the distinguishing argument index for a sequence of entries. | |
463 | |
464 Entries are elements of the effective overload set with the same number | |
465 of arguments (formally, same type list length), each a 3-tuple of the form | |
466 (callable, type list, optionality list). | |
467 | |
468 Spec: http://heycam.github.io/webidl/#dfn-distinguishing-argument-index | |
469 | |
470 If there is more than one entry in an effective overload set that has a | |
471 given type list length, then for those entries there must be an index i | |
472 such that for each pair of entries the types at index i are | |
473 distinguishable. | |
474 The lowest such index is termed the distinguishing argument index for the | |
475 entries of the effective overload set with the given type list length. | |
476 """ | |
477 if len(entries) < 2: | |
478 # Only need to distinguish if two or more entries | |
479 return | |
480 type_lists = [tuple(idl_type.name for idl_type in entry[1]) | |
481 for entry in entries] | |
482 type_list_length = len(type_lists[0]) | |
483 assert all(len(type_list) == type_list_length for type_list in type_lists) | |
484 name = entries[0][0]['name'] | |
485 | |
486 # The spec defines the distinguishing argument index by conditions it must | |
487 # satisfy, but does not give an algorithm. | |
488 # | |
489 # We compute the distinguishing argument index by first computing the | |
490 # minimum index where not all types are the same, and then checking that | |
491 # all types in this position are distinguishable (and the optionality lists | |
492 # up to this point are identical), since "minimum index where not all types | |
493 # are the same" is a *necessary* condition, and more direct to check than | |
494 # distinguishability. | |
495 try: | |
496 # “In addition, for each index j, where j is less than the | |
497 # distinguishing argument index for a given type list length, the types | |
498 # at index j in all of the entries’ type lists must be the same” | |
499 index = next(i for i, types in enumerate(zip(*type_lists)) | |
500 if len(set(types)) > 1) | |
501 except: | |
Jens Widell
2014/05/09 06:08:05
Maybe "except StopIteration:" instead, to avoid ca
Nils Barth (inactive)
2014/05/09 06:17:09
(>.<)
Thanks, lint should have caught this, but gu
| |
502 raise ValueError('No distinguishing index found for %s, length %s:\n' | |
503 'All entries have the same type list:\n' | |
504 '%s' % (name, type_list_length, type_lists[0])) | |
505 # Check optionality | |
506 # “and the booleans in the corresponding list indicating argument | |
507 # optionality must be the same.” | |
508 # FIXME: spec typo: optionality value is no longer a boolean | |
509 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=25628 | |
510 initial_optionality_lists = set(entry[2][:index] for entry in entries) | |
511 if len(initial_optionality_lists) > 1: | |
512 raise ValueError( | |
513 'Invalid optionality lists for %s, length %s:\n' | |
514 'Optionality lists differ below distinguishing argument index %s:\n' | |
515 '%s' | |
516 % (name, type_list_length, index, set(initial_optionality_lists))) | |
517 | |
518 # FIXME: check distinguishability | |
519 | |
520 return index | |
521 | |
522 | |
448 def overload_resolution_expression(method): | 523 def overload_resolution_expression(method): |
449 # Expression is an OR of ANDs: each term in the OR corresponds to a | 524 # Expression is an OR of ANDs: each term in the OR corresponds to a |
450 # possible argument count for a given method, with type checks. | 525 # possible argument count for a given method, with type checks. |
451 # FIXME: Blink's overload resolution algorithm is incorrect, per: | 526 # FIXME: Blink's overload resolution algorithm is incorrect, per: |
452 # Implement WebIDL overload resolution algorithm. http://crbug.com/293561 | 527 # Implement WebIDL overload resolution algorithm. http://crbug.com/293561 |
453 # | 528 # |
454 # Currently if distinguishing non-primitive type from primitive type, | 529 # Currently if distinguishing non-primitive type from primitive type, |
455 # (e.g., sequence<DOMString> from DOMString or Dictionary from double) | 530 # (e.g., sequence<DOMString> from DOMString or Dictionary from double) |
456 # the method with a non-primitive type argument must appear *first* in the | 531 # the method with a non-primitive type argument must appear *first* in the |
457 # IDL file, since we're not adding a check to primitive types. | 532 # IDL file, since we're not adding a check to primitive types. |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
526 # change behavior of existing bindings for other types. | 601 # change behavior of existing bindings for other types. |
527 type_check = '%s->IsObject()' % cpp_value | 602 type_check = '%s->IsObject()' % cpp_value |
528 added_check_template = null_or_optional_check() | 603 added_check_template = null_or_optional_check() |
529 if added_check_template: | 604 if added_check_template: |
530 type_check = ' || '.join([added_check_template % cpp_value, | 605 type_check = ' || '.join([added_check_template % cpp_value, |
531 type_check]) | 606 type_check]) |
532 return type_check | 607 return type_check |
533 return None | 608 return None |
534 | 609 |
535 | 610 |
611 ################################################################################ | |
612 # Utility functions | |
613 ################################################################################ | |
614 | |
615 def Counter(iterable): | |
616 # Once using Python 2.7, using collections.Counter | |
617 counter = defaultdict(lambda: 0) | |
618 for item in iterable: | |
619 counter[item] += 1 | |
620 return counter | |
621 | |
622 | |
536 def common_value(dicts, key): | 623 def common_value(dicts, key): |
537 """Returns common value of a key across an iterable of dicts, or None. | 624 """Returns common value of a key across an iterable of dicts, or None. |
538 | 625 |
539 Auxiliary function for overloads, so can consolidate an extended attribute | 626 Auxiliary function for overloads, so can consolidate an extended attribute |
540 that appears with the same value on all items in an overload set. | 627 that appears with the same value on all items in an overload set. |
541 """ | 628 """ |
542 values = (d[key] for d in dicts) | 629 values = (d[key] for d in dicts) |
543 first_value = next(values) | 630 first_value = next(values) |
544 if all(value == first_value for value in values): | 631 if all(value == first_value for value in values): |
545 return first_value | 632 return first_value |
546 return None | 633 return None |
547 | 634 |
548 | 635 |
549 def Counter(iterable): | 636 def sort_and_groupby(l, key=None): |
550 # Once using Python 2.7, using collections.Counter | 637 """Returns a dict of {key: list}, sorting and grouping input list by key.""" |
551 counter = defaultdict(lambda: 0) | 638 l.sort(key=key) |
552 for item in iterable: | 639 return dict((k, list(g)) for k, g in itertools.groupby(l, key)) |
553 counter[item] += 1 | |
554 return counter | |
555 | 640 |
556 | 641 |
557 ################################################################################ | 642 ################################################################################ |
558 # Constructors | 643 # Constructors |
559 ################################################################################ | 644 ################################################################################ |
560 | 645 |
561 # [Constructor] | 646 # [Constructor] |
562 def generate_constructor(interface, constructor): | 647 def generate_constructor(interface, constructor): |
563 return { | 648 return { |
564 'argument_list': constructor_argument_list(interface, constructor), | 649 'argument_list': constructor_argument_list(interface, constructor), |
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
825 deleter = next( | 910 deleter = next( |
826 method | 911 method |
827 for method in interface.operations | 912 for method in interface.operations |
828 if ('deleter' in method.specials and | 913 if ('deleter' in method.specials and |
829 len(method.arguments) == 1 and | 914 len(method.arguments) == 1 and |
830 str(method.arguments[0].idl_type) == 'DOMString')) | 915 str(method.arguments[0].idl_type) == 'DOMString')) |
831 except StopIteration: | 916 except StopIteration: |
832 return None | 917 return None |
833 | 918 |
834 return property_deleter(deleter) | 919 return property_deleter(deleter) |
OLD | NEW |