Index: Source/bindings/scripts/v8_interface.py |
diff --git a/Source/bindings/scripts/v8_interface.py b/Source/bindings/scripts/v8_interface.py |
index 4f6cf147e98c965eff7b04c1904e4000066c66ea..925a10da10fe398afd66be611d1ce9a394dd689b 100644 |
--- a/Source/bindings/scripts/v8_interface.py |
+++ b/Source/bindings/scripts/v8_interface.py |
@@ -329,15 +329,16 @@ def generate_overloads_by_type(methods): |
if method['name'] in overloaded_method_names] |
# Group by name (generally will be defined together, but not necessarily) |
- overloaded_methods.sort(key=itemgetter('name')) |
- method_overloads = dict( |
- (name, list(methods_iterator)) for name, methods_iterator in |
- itertools.groupby(overloaded_methods, itemgetter('name'))) |
+ method_overloads = sort_and_groupby(overloaded_methods, itemgetter('name')) |
# Add overload information only to overloaded methods, so template code can |
# easily verify if a function is overloaded |
for name, overloads in method_overloads.iteritems(): |
- effective_overload_set(overloads) # to test, compute but discard result |
+ effective_overloads_by_length = effective_overload_set_by_length(overloads) |
+ for effective_overloads in effective_overloads_by_length.itervalues(): |
+ # To test, compute but discard result |
+ distinguishing_argument_index(effective_overloads) |
+ |
for index, method in enumerate(overloads, 1): |
method.update({ |
'overload_index': index, |
@@ -392,7 +393,7 @@ def effective_overload_set(F): |
F: list of overloads for a given callable name. |
Returns: |
- S: list of tuples of the form <callable, type list, optionality list>. |
+ S: list of tuples of the form (callable, type list, optionality list). |
""" |
# Code closely follows the algorithm in the spec, for clarity and |
# correctness, and hence is not very Pythonic. |
@@ -414,12 +415,14 @@ def effective_overload_set(F): |
n = len(arguments) |
# 2. Let t0..n−1 be a list of types, where ti is the type of X’s |
# argument at index i. |
- t = [argument['idl_type'] for argument in arguments] # type list |
+ # (“type list”) |
+ t = tuple(argument['idl_type_object'] for argument in arguments) |
# 3. Let o0..n−1 be a list of optionality values, where oi is “variadic” |
# if X’s argument at index i is a final, variadic argument, “optional” |
# if the argument is optional, and “required” otherwise. |
- # (We're just using a boolean for optional vs. required.) |
- o = [argument['is_optional'] for argument in arguments] # optionality list |
+ # (“optionality list”) |
+ # (We’re just using a boolean for optional vs. required.) |
+ o = tuple(argument['is_optional'] for argument in arguments) |
# 4. Add to S the tuple <X, t0..n−1, o0..n−1>. |
S.append((X, t, o)) |
# 5. If X is declared to be variadic, then: |
@@ -445,6 +448,79 @@ def effective_overload_set(F): |
return S |
+def effective_overload_set_by_length(overloads): |
+ def type_list_length(entry): |
+ # Entries in the effective overload set are 3-tuples: |
+ # (callable, type list, optionality list) |
+ return len(entry[1]) |
+ |
+ effective_overloads = effective_overload_set(overloads) |
+ return sort_and_groupby(effective_overloads, type_list_length) |
+ |
+ |
+def distinguishing_argument_index(entries): |
+ """Returns the distinguishing argument index for a sequence of entries. |
+ |
+ Entries are elements of the effective overload set with the same number |
+ of arguments (formally, same type list length), each a 3-tuple of the form |
+ (callable, type list, optionality list). |
+ |
+ Spec: http://heycam.github.io/webidl/#dfn-distinguishing-argument-index |
+ |
+ If there is more than one entry in an effective overload set that has a |
+ given type list length, then for those entries there must be an index i |
+ such that for each pair of entries the types at index i are |
+ distinguishable. |
+ The lowest such index is termed the distinguishing argument index for the |
+ entries of the effective overload set with the given type list length. |
+ """ |
+ if len(entries) < 2: |
+ # Only need to distinguish if two or more entries |
+ return |
+ type_lists = [tuple(idl_type.name for idl_type in entry[1]) |
+ for entry in entries] |
+ type_list_length = len(type_lists[0]) |
+ assert all(len(type_list) == type_list_length for type_list in type_lists) |
+ name = entries[0][0]['name'] |
+ |
+ # The spec defines the distinguishing argument index by conditions it must |
+ # satisfy, but does not give an algorithm. |
+ # |
+ # We compute the distinguishing argument index by first computing the |
+ # minimum index where not all types are the same, and then checking that |
+ # all types in this position are distinguishable (and the optionality lists |
+ # up to this point are identical), since "minimum index where not all types |
+ # are the same" is a *necessary* condition, and more direct to check than |
+ # distinguishability. |
+ types_by_index = (set(types) for types in zip(*type_lists)) |
+ try: |
+ # “In addition, for each index j, where j is less than the |
+ # distinguishing argument index for a given type list length, the types |
+ # at index j in all of the entries’ type lists must be the same” |
+ index = next(i for i, types in enumerate(types_by_index) |
+ if len(types) > 1) |
+ except StopIteration: |
+ raise ValueError('No distinguishing index found for %s, length %s:\n' |
+ 'All entries have the same type list:\n' |
+ '%s' % (name, type_list_length, type_lists[0])) |
+ # Check optionality |
+ # “and the booleans in the corresponding list indicating argument |
+ # optionality must be the same.” |
+ # FIXME: spec typo: optionality value is no longer a boolean |
+ # https://www.w3.org/Bugs/Public/show_bug.cgi?id=25628 |
+ initial_optionality_lists = set(entry[2][:index] for entry in entries) |
+ if len(initial_optionality_lists) > 1: |
+ raise ValueError( |
+ 'Invalid optionality lists for %s, length %s:\n' |
+ 'Optionality lists differ below distinguishing argument index %s:\n' |
+ '%s' |
+ % (name, type_list_length, index, set(initial_optionality_lists))) |
+ |
+ # FIXME: check distinguishability |
+ |
+ return index |
+ |
+ |
def overload_resolution_expression(method): |
# Expression is an OR of ANDs: each term in the OR corresponds to a |
# possible argument count for a given method, with type checks. |
@@ -533,6 +609,18 @@ def overload_check_argument(index, argument): |
return None |
+################################################################################ |
+# Utility functions |
+################################################################################ |
+ |
+def Counter(iterable): |
+ # Once using Python 2.7, using collections.Counter |
+ counter = defaultdict(lambda: 0) |
+ for item in iterable: |
+ counter[item] += 1 |
+ return counter |
+ |
+ |
def common_value(dicts, key): |
"""Returns common value of a key across an iterable of dicts, or None. |
@@ -546,12 +634,10 @@ def common_value(dicts, key): |
return None |
-def Counter(iterable): |
- # Once using Python 2.7, using collections.Counter |
- counter = defaultdict(lambda: 0) |
- for item in iterable: |
- counter[item] += 1 |
- return counter |
+def sort_and_groupby(l, key=None): |
+ """Returns a dict of {key: list}, sorting and grouping input list by key.""" |
+ l.sort(key=key) |
+ return dict((k, list(g)) for k, g in itertools.groupby(l, key)) |
################################################################################ |