blob: 309de695493a9f4dd3c9e9ec82cadb3102aeb37d [file] [log] [blame]
# Copyright 2017 The Chromium Authors. All rights reserved.
# coding=utf-8
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from collections import Counter
import itertools
from operator import itemgetter
def sort_and_groupby(list_to_sort, key=None):
"""Returns a generator of (key, list), sorting and grouping list by key."""
list_to_sort.sort(key=key)
return ((k, list(g)) for k, g in itertools.groupby(list_to_sort, key))
def effective_overload_set(F): # pylint: disable=invalid-name
"""Returns the effective overload set of an overloaded function.
An effective overload set is the set of overloaded functions + signatures
derived from the set F by transforming it into the distinct permutations of
function invocations possible given the argument types. It is used by the
overload resolution algorithm.
For example, given input:
[
f1(DOMString a),
f2(Node a, DOMString b, double... c),
f3(),
f4(Event a, DOMString b, optional DOMString c, double... d)
]
The output is:
[
(f1, [DOMString], [required]),
(f2, [Node, DOMString], [required, required]),
(f2, [Node, DOMString, double], [required, required, variadic]),
(f2, [Node, DOMString, double, double], [required, required, variadic, variadic]),
(f3, [], []),
(f4, [Event, DOMString], [required, required],
(f4, [Event, DOMString, DOMString], [required, required, optional]),
(f4, [Event, DOMString, DOMString, double], [required, required, optional, variadic])
]
Spec: http://heycam.github.io/webidl/#dfn-effective-overload-set.
Formally the input and output lists are sets, but methods are stored
internally as dicts, which can't be stored in a set because they are not
hashable, so we use lists instead.
Arguments:
F: list of overloads for a given callable name.
Returns:
S: list of tuples of the form:
(callable, tuple(type list), tuple(optionality list)).
"""
# Code closely follows the algorithm in the spec, for clarity and
# correctness, and hence is not very Pythonic.
# 1. Let S be an ordered set.
# (We use a list because we can't use a set, as noted above.)
S = [] # pylint: disable=invalid-name
# 2. Let F be an ordered set with items as follows, according to the kind of
# effective overload set:
# (Passed as argument, nothing to do.)
# 3. Let maxarg be the maximum number of arguments the operations,
# constructor extended attributes or callback functions in F are declared
# to take. For variadic operations and constructor extended attributes,
# the argument on which the ellipsis appears counts as a single argument.
# Note: So void f(long x, long... y); is considered to be declared to take
# two arguments.
# X is the "callable".
maxarg = max([len(X['arguments']) for X in F])
# 4. Let max be max(maxarg, N).
# Per: https://github.com/heycam/webidl/issues/600.
# The effective overload set as defined in the Web IDL spec is used at
# runtime as an input to the overload resolution algorithm. The runtime
# portion of the overload resolution algorithm includes coercing arguments
# into the proper type. To perform that coercion, the effective overload set
# must produce variadic entries in the type list to match the number of
# arguments supplied for the invocation (N) of the function.
# Our use of the effective overload set, however, is limited to determining
# which function overload should handle the invocation. Coercion of
# arguments is a separate problem making N irrelevant and max always equal
# to maxarg.
# 5. For each operation, extended attribute or callback function X in F:
for X in F: # pylint: disable=invalid-name
# 5.1. Let arguments be the list of arguments X is declared to take.
arguments = X['arguments'] # pylint: disable=invalid-name
# 5.2. Let n be the size of arguments.
n = len(arguments) # pylint: disable=invalid-name
# 5.3. Let types be a type list.
# 5.4. Let optionalityValues be an optionality list.
types, optionality_values = [], []
# 5.5. For each argument in arguments:
for argument in arguments:
# 5.5.1. Append the type of argument to types.
types.append(argument['idl_type_object'])
# 5.5.2. Append "variadic" to optionalityValues if argument is a
# final, variadic argument, "optional" if argument is optional,
# and "required" otherwise.
if argument['is_variadic']:
optionality_values.append('variadic')
elif argument['is_optional']:
optionality_values.append('optional')
else:
optionality_values.append('required')
# 5.6. Append the tuple (X, types, optionalityValues) to S.
S.append((X, tuple(types), tuple(optionality_values)))
# 5.7. If X is declared to be variadic, then:
if optionality_values and optionality_values[-1] == 'variadic':
# 5.7.1. For each i in the range n to max - 1, inclusive:
for i in range(n, maxarg):
# 5.7.1.1. Let t be a type list.
# 5.7.1.2. Let o be an optionality list.
type_list, optionality_list = [], []
# 5.7.1.3. For each j in the range 0 to n-1, inclusive:
for j in range(0, n):
# 5.7.1.3.1. Append types[j] to t.
type_list.append(types[j])
# 5.7.1.3.2. Append optionalityValues[j] to o.
optionality_list.append(optionality_values[j])
# 5.7.1.4. For each j in the range n to i, inclusive:
for j in range(n, i + 1):
# 5.7.1.4.1. Append types[n - 1] to t.
type_list.append(types[n - 1])
# 5.7.1.4.2. Append "variadic" to o.
optionality_list.append('variadic')
# 5.7.1.5. Append the tuple (X, t, o) to S.
S.append((X, tuple(type_list), tuple(optionality_list)))
# 5.8. Let i be n - 1.
i = n - 1
# 5.9. While i ≥ 0:
while i >= 0:
# 5.9.1. If arguments[i] is not optional (i.e., it is not marked as
# "optional" and is not a final, variadic argument), then break.
if optionality_values[i] == 'required':
break
# 5.9.2. Let t be a type list.
# 5.9.3. Let o be an optionality list.
type_list, optionality_list = [], []
# 5.9.4. For each j in the range 0 to i-1, inclusive:
for j in range(0, i):
# 5.9.4.1. Append types[j] to t.
type_list.append(types[j])
# 5.9.4.2. Append optionalityValues[j] to o.
optionality_list.append(optionality_values[j])
# 5.9.5. Append the tuple (X, t, o) to S.
# Note: if i is 0, this means to add to S the tuple (X, « », « »);
# (where "« »" represents an empty list).
S.append((X, tuple(type_list), tuple(optionality_list)))
# 5.9.6. Set i to i−1.
i = i - 1
# 6. Return S.
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 list(sort_and_groupby(effective_overloads, type_list_length))
def method_overloads_by_name(methods):
"""Returns generator of overloaded methods by name: [name, [method]]"""
# Filter to only methods that are actually overloaded
method_counts = Counter(method['name'] for method in methods)
overloaded_method_names = set(
name for name, count in method_counts.items() if count > 1)
overloaded_methods = [
method for method in methods
if method['name'] in overloaded_method_names
]
# Group by name (generally will be defined together, but not necessarily)
return sort_and_groupby(overloaded_methods, itemgetter('name'))