Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(168)

Side by Side Diff: Source/bindings/scripts/v8_interface.py

Issue 275763002: Compute distinguishing argument index (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Simplify index computation Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | Source/bindings/tests/idls/TestObject.idl » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 # (Were 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 types_by_index = (set(types) for types in zip(*type_lists))
496 try:
497 # “In addition, for each index j, where j is less than the
498 # distinguishing argument index for a given type list length, the types
499 # at index j in all of the entries’ type lists must be the same”
500 index = next(i for i, types in enumerate(types_by_index)
501 if len(types) > 1)
502 except StopIteration:
503 raise ValueError('No distinguishing index found for %s, length %s:\n'
504 'All entries have the same type list:\n'
505 '%s' % (name, type_list_length, type_lists[0]))
506 # Check optionality
507 # “and the booleans in the corresponding list indicating argument
508 # optionality must be the same.”
509 # FIXME: spec typo: optionality value is no longer a boolean
510 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=25628
511 initial_optionality_lists = set(entry[2][:index] for entry in entries)
512 if len(initial_optionality_lists) > 1:
513 raise ValueError(
514 'Invalid optionality lists for %s, length %s:\n'
515 'Optionality lists differ below distinguishing argument index %s:\n'
516 '%s'
517 % (name, type_list_length, index, set(initial_optionality_lists)))
518
519 # FIXME: check distinguishability
520
521 return index
522
523
448 def overload_resolution_expression(method): 524 def overload_resolution_expression(method):
449 # Expression is an OR of ANDs: each term in the OR corresponds to a 525 # 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. 526 # possible argument count for a given method, with type checks.
451 # FIXME: Blink's overload resolution algorithm is incorrect, per: 527 # FIXME: Blink's overload resolution algorithm is incorrect, per:
452 # Implement WebIDL overload resolution algorithm. http://crbug.com/293561 528 # Implement WebIDL overload resolution algorithm. http://crbug.com/293561
453 # 529 #
454 # Currently if distinguishing non-primitive type from primitive type, 530 # Currently if distinguishing non-primitive type from primitive type,
455 # (e.g., sequence<DOMString> from DOMString or Dictionary from double) 531 # (e.g., sequence<DOMString> from DOMString or Dictionary from double)
456 # the method with a non-primitive type argument must appear *first* in the 532 # 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. 533 # IDL file, since we're not adding a check to primitive types.
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
526 # change behavior of existing bindings for other types. 602 # change behavior of existing bindings for other types.
527 type_check = '%s->IsObject()' % cpp_value 603 type_check = '%s->IsObject()' % cpp_value
528 added_check_template = null_or_optional_check() 604 added_check_template = null_or_optional_check()
529 if added_check_template: 605 if added_check_template:
530 type_check = ' || '.join([added_check_template % cpp_value, 606 type_check = ' || '.join([added_check_template % cpp_value,
531 type_check]) 607 type_check])
532 return type_check 608 return type_check
533 return None 609 return None
534 610
535 611
612 ################################################################################
613 # Utility functions
614 ################################################################################
615
616 def Counter(iterable):
617 # Once using Python 2.7, using collections.Counter
618 counter = defaultdict(lambda: 0)
619 for item in iterable:
620 counter[item] += 1
621 return counter
622
623
536 def common_value(dicts, key): 624 def common_value(dicts, key):
537 """Returns common value of a key across an iterable of dicts, or None. 625 """Returns common value of a key across an iterable of dicts, or None.
538 626
539 Auxiliary function for overloads, so can consolidate an extended attribute 627 Auxiliary function for overloads, so can consolidate an extended attribute
540 that appears with the same value on all items in an overload set. 628 that appears with the same value on all items in an overload set.
541 """ 629 """
542 values = (d[key] for d in dicts) 630 values = (d[key] for d in dicts)
543 first_value = next(values) 631 first_value = next(values)
544 if all(value == first_value for value in values): 632 if all(value == first_value for value in values):
545 return first_value 633 return first_value
546 return None 634 return None
547 635
548 636
549 def Counter(iterable): 637 def sort_and_groupby(l, key=None):
550 # Once using Python 2.7, using collections.Counter 638 """Returns a dict of {key: list}, sorting and grouping input list by key."""
551 counter = defaultdict(lambda: 0) 639 l.sort(key=key)
552 for item in iterable: 640 return dict((k, list(g)) for k, g in itertools.groupby(l, key))
553 counter[item] += 1
554 return counter
555 641
556 642
557 ################################################################################ 643 ################################################################################
558 # Constructors 644 # Constructors
559 ################################################################################ 645 ################################################################################
560 646
561 # [Constructor] 647 # [Constructor]
562 def generate_constructor(interface, constructor): 648 def generate_constructor(interface, constructor):
563 return { 649 return {
564 'argument_list': constructor_argument_list(interface, constructor), 650 'argument_list': constructor_argument_list(interface, constructor),
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after
825 deleter = next( 911 deleter = next(
826 method 912 method
827 for method in interface.operations 913 for method in interface.operations
828 if ('deleter' in method.specials and 914 if ('deleter' in method.specials and
829 len(method.arguments) == 1 and 915 len(method.arguments) == 1 and
830 str(method.arguments[0].idl_type) == 'DOMString')) 916 str(method.arguments[0].idl_type) == 'DOMString'))
831 except StopIteration: 917 except StopIteration:
832 return None 918 return None
833 919
834 return property_deleter(deleter) 920 return property_deleter(deleter)
OLDNEW
« no previous file with comments | « no previous file | Source/bindings/tests/idls/TestObject.idl » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698