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 15 matching lines...) Expand all Loading... |
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | 29 |
30 # pylint: disable=relative-import | 30 # pylint: disable=relative-import |
31 | 31 |
32 """Generate template values for an interface. | 32 """Generate template values for an interface. |
33 | 33 |
34 Design doc: http://www.chromium.org/developers/design-documents/idl-compiler | 34 Design doc: http://www.chromium.org/developers/design-documents/idl-compiler |
35 """ | 35 """ |
36 | 36 from operator import or_ |
37 from collections import defaultdict | |
38 import itertools | |
39 from operator import itemgetter, or_ | |
40 | 37 |
41 from idl_definitions import IdlOperation, IdlArgument | 38 from idl_definitions import IdlOperation, IdlArgument |
42 from idl_types import IdlType, inherits_interface | 39 from idl_types import IdlType, inherits_interface |
| 40 from overload_set_algorithm import effective_overload_set_by_length |
| 41 from overload_set_algorithm import method_overloads_by_name |
| 42 |
43 import v8_attributes | 43 import v8_attributes |
44 from v8_globals import includes | 44 from v8_globals import includes |
45 import v8_methods | 45 import v8_methods |
46 import v8_types | 46 import v8_types |
47 import v8_utilities | 47 import v8_utilities |
48 from v8_utilities import (cpp_name_or_partial, cpp_name, has_extended_attribute_
value, | 48 from v8_utilities import (cpp_name_or_partial, cpp_name, has_extended_attribute_
value, |
49 runtime_enabled_feature_name, is_legacy_interface_type
_checking) | 49 runtime_enabled_feature_name, is_legacy_interface_type
_checking) |
50 | 50 |
51 | 51 |
52 INTERFACE_H_INCLUDES = frozenset([ | 52 INTERFACE_H_INCLUDES = frozenset([ |
(...skipping 685 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
738 """ | 738 """ |
739 # Add overload information only to overloaded methods, so template code can | 739 # Add overload information only to overloaded methods, so template code can |
740 # easily verify if a function is overloaded | 740 # easily verify if a function is overloaded |
741 for name, overloads in method_overloads_by_name(methods): | 741 for name, overloads in method_overloads_by_name(methods): |
742 # Resolution function is generated after last overloaded function; | 742 # Resolution function is generated after last overloaded function; |
743 # package necessary information into |method.overloads| for that method. | 743 # package necessary information into |method.overloads| for that method. |
744 overloads[-1]['overloads'] = overloads_context(interface, overloads) | 744 overloads[-1]['overloads'] = overloads_context(interface, overloads) |
745 overloads[-1]['overloads']['name'] = name | 745 overloads[-1]['overloads']['name'] = name |
746 | 746 |
747 | 747 |
748 def method_overloads_by_name(methods): | |
749 """Returns generator of overloaded methods by name: [name, [method]]""" | |
750 # Filter to only methods that are actually overloaded | |
751 method_counts = Counter(method['name'] for method in methods) | |
752 overloaded_method_names = set(name | |
753 for name, count in method_counts.iteritems() | |
754 if count > 1) | |
755 overloaded_methods = [method for method in methods | |
756 if method['name'] in overloaded_method_names] | |
757 | |
758 # Group by name (generally will be defined together, but not necessarily) | |
759 return sort_and_groupby(overloaded_methods, itemgetter('name')) | |
760 | 748 |
761 | 749 |
762 def overloads_context(interface, overloads): | 750 def overloads_context(interface, overloads): |
763 """Returns |overloads| template values for a single name. | 751 """Returns |overloads| template values for a single name. |
764 | 752 |
765 Sets |method.overload_index| in place for |method| in |overloads| | 753 Sets |method.overload_index| in place for |method| in |overloads| |
766 and returns dict of overall overload template values. | 754 and returns dict of overall overload template values. |
767 """ | 755 """ |
768 assert len(overloads) > 1 # only apply to overloaded names | 756 assert len(overloads) > 1 # only apply to overloaded names |
769 for index, method in enumerate(overloads, 1): | 757 for index, method in enumerate(overloads, 1): |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
871 'valid_arities': (lengths | 859 'valid_arities': (lengths |
872 # Only need to report valid arities if there is a gap
in the | 860 # Only need to report valid arities if there is a gap
in the |
873 # sequence of possible lengths, otherwise invalid leng
th means | 861 # sequence of possible lengths, otherwise invalid leng
th means |
874 # "not enough arguments". | 862 # "not enough arguments". |
875 if lengths[-1] - lengths[0] != len(lengths) - 1 else N
one), | 863 if lengths[-1] - lengths[0] != len(lengths) - 1 else N
one), |
876 'visible': has_overload_visible, | 864 'visible': has_overload_visible, |
877 'has_partial_overloads': has_partial_overloads, | 865 'has_partial_overloads': has_partial_overloads, |
878 } | 866 } |
879 | 867 |
880 | 868 |
881 def effective_overload_set(F): | |
882 """Returns the effective overload set of an overloaded function. | |
883 | |
884 An effective overload set is the set of overloaded functions + signatures | |
885 (type list of arguments, with optional and variadic arguments included or | |
886 not), and is used in the overload resolution algorithm. | |
887 | |
888 For example, given input [f1(optional long x), f2(DOMString s)], the output | |
889 is informally [f1(), f1(long), f2(DOMString)], and formally | |
890 [(f1, [], []), (f1, [long], [optional]), (f2, [DOMString], [required])]. | |
891 | |
892 Currently the optionality list is a list of |is_optional| booleans (True | |
893 means optional, False means required); to support variadics this needs to | |
894 be tri-valued as required, optional, or variadic. | |
895 | |
896 Formally: | |
897 An effective overload set represents the allowable invocations for a | |
898 particular operation, constructor (specified with [Constructor] or | |
899 [NamedConstructor]), legacy caller or callback function. | |
900 | |
901 An additional argument N (argument count) is needed when overloading | |
902 variadics, but we don't use that currently. | |
903 | |
904 Spec: http://heycam.github.io/webidl/#dfn-effective-overload-set | |
905 | |
906 Formally the input and output lists are sets, but methods are stored | |
907 internally as dicts, which can't be stored in a set because they are not | |
908 hashable, so we use lists instead. | |
909 | |
910 Arguments: | |
911 F: list of overloads for a given callable name. | |
912 | |
913 Returns: | |
914 S: list of tuples of the form (callable, type list, optionality list). | |
915 """ | |
916 # Code closely follows the algorithm in the spec, for clarity and | |
917 # correctness, and hence is not very Pythonic. | |
918 | |
919 # 1. Initialize S to ∅. | |
920 # (We use a list because we can't use a set, as noted above.) | |
921 S = [] | |
922 | |
923 # 2. Let F be a set with elements as follows, according to the kind of | |
924 # effective overload set: | |
925 # (Passed as argument, nothing to do.) | |
926 | |
927 # 3. & 4. (maxarg, m) are only needed for variadics, not used. | |
928 | |
929 # 5. For each operation, extended attribute or callback function X in F: | |
930 for X in F: # X is the "callable", F is the overloads. | |
931 arguments = X['arguments'] | |
932 # 1. Let n be the number of arguments X is declared to take. | |
933 n = len(arguments) | |
934 # 2. Let t0..n−1 be a list of types, where ti is the type of X’s | |
935 # argument at index i. | |
936 # (“type list”) | |
937 t = tuple(argument['idl_type_object'] for argument in arguments) | |
938 # 3. Let o0..n−1 be a list of optionality values, where oi is “variadic” | |
939 # if X’s argument at index i is a final, variadic argument, “optional” | |
940 # if the argument is optional, and “required” otherwise. | |
941 # (“optionality list”) | |
942 # (We’re just using a boolean for optional/variadic vs. required.) | |
943 o = tuple(argument['is_optional'] or argument['is_variadic'] | |
944 for argument in arguments) | |
945 # 4. Add to S the tuple <X, t0..n−1, o0..n−1>. | |
946 S.append((X, t, o)) | |
947 # 5. If X is declared to be variadic, then: | |
948 # (Not used, so not implemented.) | |
949 # 6. Initialize i to n−1. | |
950 i = n - 1 | |
951 # 7. While i ≥ 0: | |
952 # Spec bug (fencepost error); should be “While i > 0:” | |
953 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=25590 | |
954 while i > 0: | |
955 # 1. If argument i of X is not optional, then break this loop. | |
956 if not o[i]: | |
957 break | |
958 # 2. Otherwise, add to S the tuple <X, t0..i−1, o0..i−1>. | |
959 S.append((X, t[:i], o[:i])) | |
960 # 3. Set i to i−1. | |
961 i = i - 1 | |
962 # 8. If n > 0 and all arguments of X are optional, then add to S the | |
963 # tuple <X, (), ()> (where “()” represents the empty list). | |
964 if n > 0 and all(oi for oi in o): | |
965 S.append((X, [], [])) | |
966 # 6. The effective overload set is S. | |
967 return S | |
968 | |
969 | |
970 def effective_overload_set_by_length(overloads): | |
971 def type_list_length(entry): | |
972 # Entries in the effective overload set are 3-tuples: | |
973 # (callable, type list, optionality list) | |
974 return len(entry[1]) | |
975 | |
976 effective_overloads = effective_overload_set(overloads) | |
977 return list(sort_and_groupby(effective_overloads, type_list_length)) | |
978 | |
979 | 869 |
980 def distinguishing_argument_index(entries): | 870 def distinguishing_argument_index(entries): |
981 """Returns the distinguishing argument index for a sequence of entries. | 871 """Returns the distinguishing argument index for a sequence of entries. |
982 | 872 |
983 Entries are elements of the effective overload set with the same number | 873 Entries are elements of the effective overload set with the same number |
984 of arguments (formally, same type list length), each a 3-tuple of the form | 874 of arguments (formally, same type list length), each a 3-tuple of the form |
985 (callable, type list, optionality list). | 875 (callable, type list, optionality list). |
986 | 876 |
987 Spec: http://heycam.github.io/webidl/#dfn-distinguishing-argument-index | 877 Spec: http://heycam.github.io/webidl/#dfn-distinguishing-argument-index |
988 | 878 |
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1249 if idl_type.is_numeric_type) | 1139 if idl_type.is_numeric_type) |
1250 yield 'true', method | 1140 yield 'true', method |
1251 except StopIteration: | 1141 except StopIteration: |
1252 pass | 1142 pass |
1253 | 1143 |
1254 | 1144 |
1255 ################################################################################ | 1145 ################################################################################ |
1256 # Utility functions | 1146 # Utility functions |
1257 ################################################################################ | 1147 ################################################################################ |
1258 | 1148 |
1259 def Counter(iterable): | |
1260 # Once using Python 2.7, using collections.Counter | |
1261 counter = defaultdict(lambda: 0) | |
1262 for item in iterable: | |
1263 counter[item] += 1 | |
1264 return counter | |
1265 | |
1266 | |
1267 def common(dicts, f): | 1149 def common(dicts, f): |
1268 """Returns common result of f across an iterable of dicts, or None. | 1150 """Returns common result of f across an iterable of dicts, or None. |
1269 | 1151 |
1270 Call f for each dict and return its result if the same across all dicts. | 1152 Call f for each dict and return its result if the same across all dicts. |
1271 """ | 1153 """ |
1272 values = (f(d) for d in dicts) | 1154 values = (f(d) for d in dicts) |
1273 first_value = next(values) | 1155 first_value = next(values) |
1274 if all(value == first_value for value in values): | 1156 if all(value == first_value for value in values): |
1275 return first_value | 1157 return first_value |
1276 return None | 1158 return None |
(...skipping 10 matching lines...) Expand all Loading... |
1287 | 1169 |
1288 def common_value(dicts, key): | 1170 def common_value(dicts, key): |
1289 """Returns common value of a key across an iterable of dicts, or None. | 1171 """Returns common value of a key across an iterable of dicts, or None. |
1290 | 1172 |
1291 Auxiliary function for overloads, so can consolidate an extended attribute | 1173 Auxiliary function for overloads, so can consolidate an extended attribute |
1292 that appears with the same value on all items in an overload set. | 1174 that appears with the same value on all items in an overload set. |
1293 """ | 1175 """ |
1294 return common(dicts, lambda d: d.get(key)) | 1176 return common(dicts, lambda d: d.get(key)) |
1295 | 1177 |
1296 | 1178 |
1297 def sort_and_groupby(l, key=None): | |
1298 """Returns a generator of (key, list), sorting and grouping list by key.""" | |
1299 l.sort(key=key) | |
1300 return ((k, list(g)) for k, g in itertools.groupby(l, key)) | |
1301 | |
1302 | |
1303 ################################################################################ | 1179 ################################################################################ |
1304 # Constructors | 1180 # Constructors |
1305 ################################################################################ | 1181 ################################################################################ |
1306 | 1182 |
1307 # [Constructor] | 1183 # [Constructor] |
1308 def constructor_context(interface, constructor): | 1184 def constructor_context(interface, constructor): |
1309 # [RaisesException=Constructor] | 1185 # [RaisesException=Constructor] |
1310 is_constructor_raises_exception = \ | 1186 is_constructor_raises_exception = \ |
1311 interface.extended_attributes.get('RaisesException') == 'Constructor' | 1187 interface.extended_attributes.get('RaisesException') == 'Constructor' |
1312 | 1188 |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1484 extended_attributes = deleter.extended_attributes | 1360 extended_attributes = deleter.extended_attributes |
1485 is_call_with_script_state = v8_utilities.has_extended_attribute_value(delete
r, 'CallWith', 'ScriptState') | 1361 is_call_with_script_state = v8_utilities.has_extended_attribute_value(delete
r, 'CallWith', 'ScriptState') |
1486 is_ce_reactions = 'CEReactions' in extended_attributes | 1362 is_ce_reactions = 'CEReactions' in extended_attributes |
1487 return { | 1363 return { |
1488 'is_call_with_script_state': is_call_with_script_state, | 1364 'is_call_with_script_state': is_call_with_script_state, |
1489 'is_ce_reactions': is_ce_reactions, | 1365 'is_ce_reactions': is_ce_reactions, |
1490 'is_custom': 'Custom' in extended_attributes, | 1366 'is_custom': 'Custom' in extended_attributes, |
1491 'is_raises_exception': 'RaisesException' in extended_attributes, | 1367 'is_raises_exception': 'RaisesException' in extended_attributes, |
1492 'name': cpp_name(deleter), | 1368 'name': cpp_name(deleter), |
1493 } | 1369 } |
OLD | NEW |