Chromium Code Reviews| Index: Source/bindings/dart/scripts/dart_interface.py |
| diff --git a/Source/bindings/dart/scripts/dart_interface.py b/Source/bindings/dart/scripts/dart_interface.py |
| index ba0e721bcc103c49a1a60f43b7b58025edf6dc35..bac92a95a4cbae64187c7a7aceba0f577ab39dc6 100644 |
| --- a/Source/bindings/dart/scripts/dart_interface.py |
| +++ b/Source/bindings/dart/scripts/dart_interface.py |
| @@ -1,4 +1,5 @@ |
| # Copyright (C) 2013 Google Inc. All rights reserved. |
| +# coding=utf-8 |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| @@ -32,6 +33,8 @@ Design doc: http://www.chromium.org/developers/design-documents/idl-compiler |
| """ |
| from collections import defaultdict |
| +import itertools |
| +from operator import itemgetter |
| import idl_types |
| from idl_types import IdlType, inherits_interface, IdlArrayOrSequenceType, IdlArrayType |
| @@ -397,21 +400,6 @@ def _suppress_extended_attributes(extended_attributes): |
| return False |
| -def add_native_entries(interface, constructors, is_custom): |
| - for constructor in constructors: |
| - types = None |
| - if not is_custom: |
| - types = [arg['preprocessed_type'] |
| - for arg in constructor['arguments']] |
| - argument_names = [arg['name'] for arg in constructor['arguments']] |
| - native_entry = \ |
| - DartUtilities.generate_native_entry(interface.name, constructor, |
| - None, 'Constructor', None, |
| - argument_names, types) |
| - constructor.update({'native_entry': native_entry}) |
| - |
| - |
| -# TODO(terry): Rename generate_interface - interface_context |
| def generate_interface(interface): |
| includes.clear() |
| includes.update(INTERFACE_CPP_INCLUDES) |
| @@ -539,7 +527,8 @@ def generate_interface(interface): |
| # (currently needed for Perl compatibility) |
| # Handle named constructors separately |
| if constructor.name == 'Constructor'] |
| - generate_constructor_overloads(constructors) |
| + if len(constructors) > 1: |
| + template_contents['constructor_overloads'] = overloads_context(constructors) |
| # [CustomConstructor] |
| custom_constructors = [generate_custom_constructor(interface, constructor) |
| @@ -624,7 +613,7 @@ def generate_interface(interface): |
| not _suppress_method(interface.name, method.name) and |
| not _suppress_extended_attributes(method.extended_attributes) and |
| not 'DartSuppress' in method.extended_attributes)] |
| - generate_overloads(methods) |
| + compute_method_overloads_context(methods) |
| for method in methods: |
| method['do_generate_method_configuration'] = ( |
| method['do_not_check_signature'] and |
| @@ -699,23 +688,12 @@ def generate_constant(constant): |
| } |
| -################################################################################ |
| -# Overloads |
| -################################################################################ |
| - |
| -def generate_method_native_entry(interface, method, count, optional_index, kind): |
| - types = None |
| - if not method['is_custom']: |
| - types = [arg['preprocessed_type'] for arg in method['arguments'][0:count]] |
| - if method['is_call_with_script_arguments']: |
| - types.append("object") |
| +def generate_method_native_entry(interface, method, count, kind): |
| argument_names = [arg['name'] for arg in method['arguments'][0:count]] |
| name = method.get('name') |
| native_entry = \ |
| DartUtilities.generate_native_entry(interface.name, method, |
| - name, kind, |
| - optional_index, |
| - argument_names, types) |
| + name, kind, argument_names) |
| return native_entry |
| @@ -724,178 +702,505 @@ def generate_method_native_entries(interface, methods, kind): |
| native_entries = [] |
| required_arg_count = method['number_of_required_arguments'] |
| arg_count = method['number_of_arguments'] |
| - if required_arg_count != arg_count: |
| - for x in range(required_arg_count, arg_count + 1): |
| - # This is really silly, but is here for now just to match up |
| - # the existing name generation in the old dart:html scripts |
| - index = arg_count - x + 1 |
| - native_entry = \ |
| - generate_method_native_entry(interface, method, x, index, kind) |
| - native_entries.append(native_entry) |
| - else: |
| - # Eventually, we should probably always generate an unindexed |
| - # native entry, to handle cases like |
| - # addEventListener in which we suppress the optionality, |
| - # and in general to make us more robust against optional changes |
| + for x in range(required_arg_count, arg_count + 1): |
| native_entry = \ |
| - generate_method_native_entry(interface, method, arg_count, None, kind) |
| + generate_method_native_entry(interface, method, x, kind) |
| native_entries.append(native_entry) |
| method.update({'native_entries': native_entries}) |
| -def generate_overloads(methods): |
| - generate_overloads_by_type(methods, is_static=False) # Regular methods |
| - generate_overloads_by_type(methods, is_static=True) |
| +################################################################################ |
| +# Overloads |
| +################################################################################ |
| + |
| +def compute_method_overloads_context(methods): |
|
terry
2014/10/21 21:26:11
methods => method_contexts ?
We use method(s) as
Leaf
2014/10/22 16:31:19
Kind of trying to minimize text changes vs v8 to m
|
| + # Regular methods |
| + compute_method_overloads_context_by_type([method for method in methods |
| + if not method['is_static']]) |
| + # Static methods |
| + compute_method_overloads_context_by_type([method for method in methods |
| + if method['is_static']]) |
| -def generate_overloads_by_type(methods, is_static): |
| - # Generates |overloads| template values and modifies |methods| in place; |
| - # |is_static| flag used (instead of partitioning list in 2) because need to |
| - # iterate over original list of methods to modify in place |
| - method_counts = defaultdict(lambda: 0) |
| - for method in methods: |
| - if method['is_static'] != is_static: |
| - continue |
| - name = method['name'] |
| - # FIXME(vsm): We don't seem to capture optional param |
| - # overloads here. |
| - method_counts[name] += 1 |
| - # Filter to only methods that are actually overloaded |
| - overloaded_method_counts = dict((name, count) |
| - for name, count in method_counts.iteritems() |
| - if count > 1) |
| +def compute_method_overloads_context_by_type(methods): |
| + """Computes |method.overload*| template values. |
| + Called separately for static and non-static (regular) methods, |
| + as these are overloaded separately. |
| + Modifies |method| in place for |method| in |methods|. |
| + Doesn't change the |methods| list itself (only the values, i.e. individual |
| + methods), so ok to treat these separately. |
| + """ |
| # Add overload information only to overloaded methods, so template code can |
| # easily verify if a function is overloaded |
| - method_overloads = defaultdict(list) |
| - for method in methods: |
| - name = method['name'] |
| - if (method['is_static'] != is_static or |
| - name not in overloaded_method_counts): |
| - continue |
| - # Overload index includes self, so first append, then compute index |
| - method_overloads[name].append(method) |
| - method.update({ |
| - 'overload_index': len(method_overloads[name]), |
| - 'overload_resolution_expression': overload_resolution_expression(method), |
| - }) |
| - # FIXME(vsm): Looks like we only handle optional parameters if |
| - # the method is already overloaded. For a non-overloaded method |
| - # with optional parameters, we never get here. |
| - |
| - # Resolution function is generated after last overloaded function; |
| - # package necessary information into |method.overloads| for that method. |
| - for method in methods: |
| - if (method['is_static'] != is_static or |
| - 'overload_index' not in method): |
| - continue |
| - name = method['name'] |
| - if method['overload_index'] != overloaded_method_counts[name]: |
| - continue |
| - overloads = method_overloads[name] |
| - minimum_number_of_required_arguments = min( |
| - overload['number_of_required_arguments'] |
| - for overload in overloads) |
| - method['overloads'] = { |
| - 'has_exception_state': bool(minimum_number_of_required_arguments), |
| - 'methods': overloads, |
| - 'minimum_number_of_required_arguments': minimum_number_of_required_arguments, |
| - 'name': name, |
| - } |
| + for name, overloads in method_overloads_by_name(methods): |
| + # Resolution function is generated after last overloaded function; |
| + # package necessary information into |method.overloads| for that method. |
| + overloads[-1]['overloads'] = overloads_context(overloads) |
| + overloads[-1]['overloads']['name'] = name |
| + |
| +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.iteritems() |
| + 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')) |
| + |
| + |
| +def overloads_context(overloads): |
| + """Returns |overloads| template values for a single name. |
| + |
| + Sets |method.overload_index| in place for |method| in |overloads| |
| + and returns dict of overall overload template values. |
| + """ |
| + assert len(overloads) > 1 # only apply to overloaded names |
| + for index, method in enumerate(overloads, 1): |
| + method['overload_index'] = index |
| + |
| + effective_overloads_by_length = effective_overload_set_by_length(overloads) |
| + lengths = [length for length, _ in effective_overloads_by_length] |
| + name = overloads[0].get('name', '<constructor>') |
| + |
| + # Check and fail if all overloads with the shortest acceptable arguments |
| + # list are runtime enabled, since we would otherwise set 'length' on the |
| + # function object to an incorrect value when none of those overloads were |
| + # actually enabled at runtime. The exception is if all overloads are |
| + # controlled by the same runtime enabled feature, in which case there would |
| + # be no function object at all if it is not enabled. |
| + shortest_overloads = effective_overloads_by_length[0][1] |
| + if (all(method.get('runtime_enabled_function') |
| + for method, _, _ in shortest_overloads) and |
| + not common_value(overloads, 'runtime_enabled_function')): |
| + raise ValueError('Function.length of %s depends on runtime enabled features' % name) |
| + |
| + return { |
| + 'deprecate_all_as': common_value(overloads, 'deprecate_as'), # [DeprecateAs] |
| + 'exposed_test_all': common_value(overloads, 'exposed_test'), # [Exposed] |
| + 'length_tests_methods': length_tests_methods(effective_overloads_by_length), |
| + # 1. Let maxarg be the length of the longest type list of the |
| + # entries in S. |
| + 'maxarg': lengths[-1], |
| + 'measure_all_as': common_value(overloads, 'measure_as'), # [MeasureAs] |
| + 'minarg': lengths[0], |
| + 'per_context_enabled_function_all': common_value(overloads, 'per_context_enabled_function'), # [PerContextEnabled] |
| + 'runtime_enabled_function_all': common_value(overloads, 'runtime_enabled_function'), # [RuntimeEnabled] |
| + 'valid_arities': lengths |
| + # Only need to report valid arities if there is a gap in the |
| + # sequence of possible lengths, otherwise invalid length means |
| + # "not enough arguments". |
| + if lengths[-1] - lengths[0] != len(lengths) - 1 else None, |
| + } |
| -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. |
| - # FIXME: Blink's overload resolution algorithm is incorrect, per: |
| - # Implement WebIDL overload resolution algorithm. |
| - # https://code.google.com/p/chromium/issues/detail?id=293561 |
| + |
| +def effective_overload_set(F): |
|
terry
2014/10/21 21:26:12
F or f, used f for function in below methods.
Leaf
2014/10/22 16:31:19
I think the odd capitalization and typography in t
|
| + """Returns the effective overload set of an overloaded function. |
| + |
| + An effective overload set is the set of overloaded functions + signatures |
| + (type list of arguments, with optional and variadic arguments included or |
| + not), and is used in the overload resolution algorithm. |
| + |
| + For example, given input [f1(optional long x), f2(DOMString s)], the output |
| + is informally [f1(), f1(long), f2(DOMString)], and formally |
| + [(f1, [], []), (f1, [long], [optional]), (f2, [DOMString], [required])]. |
| + |
| + Currently the optionality list is a list of |is_optional| booleans (True |
| + means optional, False means required); to support variadics this needs to |
| + be tri-valued as required, optional, or variadic. |
| + |
| + Formally: |
| + An effective overload set represents the allowable invocations for a |
| + particular operation, constructor (specified with [Constructor] or |
| + [NamedConstructor]), legacy caller or callback function. |
| + |
| + An additional argument N (argument count) is needed when overloading |
| + variadics, but we don't use that currently. |
| + |
| + 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, type list, optionality list). |
| + """ |
| + # Code closely follows the algorithm in the spec, for clarity and |
| + # correctness, and hence is not very Pythonic. |
| + |
| + # 1. Initialize S to ∅. |
| + # (We use a list because we can't use a set, as noted above.) |
| + S = [] |
|
terry
2014/10/21 21:26:12
capitalized? as well as others
|
| + |
| + # 2. Let F be a set with elements as follows, according to the kind of |
| + # effective overload set: |
| + # (Passed as argument, nothing to do.) |
| + |
| + # 3. & 4. (maxarg, m) are only needed for variadics, not used. |
| + |
| + # 5. For each operation, extended attribute or callback function X in F: |
| + for X in F: # X is the "callable", F is the overloads. |
| + arguments = X['arguments'] |
| + # 1. Let n be the number of arguments X is declared to take. |
| + 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. |
| + # (“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. |
| + # (“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: |
| + # (Not used, so not implemented.) |
| + # 6. Initialize i to n−1. |
| + i = n - 1 |
| + # 7. While i ≥ 0: |
| + # Spec bug (fencepost error); should be “While i > 0:” |
| + # https://www.w3.org/Bugs/Public/show_bug.cgi?id=25590 |
| + while i > 0: |
| + # 1. If argument i of X is not optional, then break this loop. |
| + if not o[i]: |
| + break |
| + # 2. Otherwise, add to S the tuple <X, t0..i−1, o0..i−1>. |
| + S.append((X, t[:i], o[:i])) |
| + # 3. Set i to i−1. |
| + i = i - 1 |
| + # 8. If n > 0 and all arguments of X are optional, then add to S the |
| + # tuple <X, (), ()> (where “()” represents the empty list). |
| + if n > 0 and all(oi for oi in o): |
| + S.append((X, [], [])) |
| + # 6. The effective overload set is 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 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. |
| + """ |
| + # Only applicable “If there is more than one entry” |
| + assert len(entries) > 1 |
| + type_lists = [tuple(idl_type.name for idl_type in entry[1]) |
| + for entry in entries] |
| + type_list_length = len(type_lists[0]) |
| + # Only applicable for entries that “[have] a given type list length” |
| + assert all(len(type_list) == type_list_length for type_list in type_lists) |
| + name = entries[0][0].get('name', 'Constructor') # for error reporting |
| + |
| + # The spec defines the distinguishing argument index by conditions it must |
| + # satisfy, but does not give an algorithm. |
| # |
| - # Currently if distinguishing non-primitive type from primitive type, |
| - # (e.g., sequence<DOMString> from DOMString or Dictionary from double) |
| - # the method with a non-primitive type argument must appear *first* in the |
| - # IDL file, since we're not adding a check to primitive types. |
| - # FIXME: Once fixed, check IDLs, as usually want methods with primitive |
| - # types to appear first (style-wise). |
| + # 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))) |
| + |
| + # Check distinguishability |
| + # http://heycam.github.io/webidl/#dfn-distinguishable |
| + # Use names to check for distinct types, since objects are distinct |
| + # FIXME: check distinguishability more precisely, for validation |
| + distinguishing_argument_type_names = [type_list[index] |
| + for type_list in type_lists] |
| + if (len(set(distinguishing_argument_type_names)) != |
| + len(distinguishing_argument_type_names)): |
| + raise ValueError('Types in distinguishing argument are not distinct:\n' |
| + '%s' % distinguishing_argument_type_names) |
| + |
| + return index |
| + |
| + |
| +def length_tests_methods(effective_overloads_by_length): |
| + """Returns sorted list of resolution tests and associated methods, by length. |
| + |
| + This builds the main data structure for the overload resolution loop. |
| + For a given argument length, bindings test argument at distinguishing |
| + argument index, in order given by spec: if it is compatible with |
| + (optionality or) type required by an overloaded method, resolve to that |
| + method. |
| + |
| + Returns: |
| + [(length, [(test, method)])] |
| + """ |
| + return [(length, list(resolution_tests_methods(effective_overloads))) |
| + for length, effective_overloads in effective_overloads_by_length] |
| + |
| + |
| +DART_CHECK_TYPE = { |
| + 'ArrayBufferView': 'Dart_IsTypedData({cpp_value})', |
| + 'ArrayBuffer': 'Dart_IsByteBuffer({cpp_value})', |
| + 'Uint8Array': 'DartUtilities::isUint8Array({cpp_value})', |
| + 'Uint8ClampedArray': 'DartUtilities::isUint8ClampedArray({cpp_value})', |
| +} |
| + |
| + |
| +def resolution_tests_methods(effective_overloads): |
| + """Yields resolution test and associated method, in resolution order, for effective overloads of a given length. |
|
terry
2014/10/21 21:26:11
linebreak?
|
| + |
| + This is the heart of the resolution algorithm. |
| + http://heycam.github.io/webidl/#dfn-overload-resolution-algorithm |
| + |
| + Note that a given method can be listed multiple times, with different tests! |
| + This is to handle implicit type conversion. |
| + |
| + Returns: |
| + [(test, method)] |
| + """ |
| + methods = [effective_overload[0] |
| + for effective_overload in effective_overloads] |
| + if len(methods) == 1: |
| + # If only one method with a given length, no test needed |
| + yield 'true', methods[0] |
| + return |
| + |
| + # 6. If there is more than one entry in S, then set d to be the |
| + # distinguishing argument index for the entries of S. |
| + index = distinguishing_argument_index(effective_overloads) |
| + # (7-9 are for handling |undefined| values for optional arguments before |
| + # the distinguishing argument (as “missing”), so you can specify only some |
| + # optional arguments. We don’t support this, so we skip these steps.) |
| + # 10. If i = d, then: |
| + # (d is the distinguishing argument index) |
| + # 1. Let V be argi. |
| + # Note: This is the argument that will be used to resolve which |
| + # overload is selected. |
| + cpp_value = 'Dart_GetNativeArgument(args, %s + argOffset)' % index |
| + |
| + # Extract argument and IDL type to simplify accessing these in each loop. |
| + arguments = [method['arguments'][index] for method in methods] |
| + arguments_methods = zip(arguments, methods) |
| + idl_types = [argument['idl_type_object'] for argument in arguments] |
| + idl_types_methods = zip(idl_types, methods) |
| + |
| + # We can’t do a single loop through all methods or simply sort them, because |
| + # a method may be listed in multiple steps of the resolution algorithm, and |
| + # which test to apply differs depending on the step. |
| # |
| - # Properly: |
| - # 1. Compute effective overload set. |
| - # 2. First check type list length. |
| - # 3. If multiple entries for given length, compute distinguishing argument |
| - # index and have check for that type. |
| - arguments = method['arguments'] |
| - overload_checks = [overload_check_expression(method, index) |
| - # check *omitting* optional arguments at |index| and up: |
| - # index 0 => argument_count 0 (no arguments) |
| - # index 1 => argument_count 1 (index 0 argument only) |
| - for index, argument in enumerate(arguments) |
| - if argument['is_optional']] |
| - # FIXME: this is wrong if a method has optional arguments and a variadic |
| - # one, though there are not yet any examples of this |
| - if not method['is_variadic']: |
| - # Includes all optional arguments (len = last index + 1) |
| - overload_checks.append(overload_check_expression(method, len(arguments))) |
| - return ' || '.join('(%s)' % check for check in overload_checks) |
| - |
| - |
| -def overload_check_expression(method, argument_count): |
| - overload_checks = ['info.Length() == %s' % argument_count] |
| - arguments = method['arguments'][:argument_count] |
| - overload_checks.extend(overload_check_argument(index, argument) |
| - for index, argument in |
| - enumerate(arguments)) |
| - return ' && '.join('(%s)' % check for check in overload_checks if check) |
| - |
| - |
| -def overload_check_argument(index, argument): |
| - def null_or_optional_check(): |
| - # If undefined is passed for an optional argument, the argument should |
| - # be treated as missing; otherwise undefined is not allowed. |
| - |
| - # FIXME(vsm): We need Dart specific checks here. |
| - if idl_type.is_nullable: |
| - if argument['is_optional']: |
| - return 'isUndefinedOrNull(%s)' |
| - return '%s->IsNull()' |
| - if argument['is_optional']: |
| - return '%s->IsUndefined()' |
| - return None |
| + # Instead, we need to go through all methods at each step, either finding |
| + # first match (if only one test is allowed) or filtering to matches (if |
| + # multiple tests are allowed), and generating an appropriate tests. |
| - cpp_value = 'info[%s]' % index |
| - idl_type = argument['idl_type_object'] |
| - # FIXME(vsm): We need Dart specific checks for the rest of this method. |
| - # FIXME: proper type checking, sharing code with attributes and methods |
| - # FIXME(terry): DartStrictTypeChecking no longer supported; TypeChecking is |
| - # new extended attribute. |
| - if idl_type.name == 'String' and argument['is_strict_type_checking']: |
| - return ' || '.join(['isUndefinedOrNull(%s)' % cpp_value, |
| - '%s->IsString()' % cpp_value, |
| - '%s->IsObject()' % cpp_value]) |
| - if idl_type.native_array_element_type: |
| - return '%s->IsArray()' % cpp_value |
| - if idl_type.is_callback_interface: |
| - return ' || '.join(['%s->IsNull()' % cpp_value, |
| - '%s->IsFunction()' % cpp_value]) |
| - if idl_type.is_wrapper_type: |
| - type_check = 'V8{idl_type}::hasInstance({cpp_value}, info.GetIsolate())'.format(idl_type=idl_type.base_type, cpp_value=cpp_value) |
| - if idl_type.is_nullable: |
| - type_check = ' || '.join(['%s->IsNull()' % cpp_value, type_check]) |
| - return type_check |
| - if idl_type.is_interface_type: |
| - # Non-wrapper types are just objects: we don't distinguish type |
| - # We only allow undefined for non-wrapper types (notably Dictionary), |
| - # as we need it for optional Dictionary arguments, but we don't want to |
| - # change behavior of existing bindings for other types. |
| - type_check = '%s->IsObject()' % cpp_value |
| - added_check_template = null_or_optional_check() |
| - if added_check_template: |
| - type_check = ' || '.join([added_check_template % cpp_value, |
| - type_check]) |
| - return type_check |
| + # 2. If V is undefined, and there is an entry in S whose list of |
| + # optionality values has “optional” at index i, then remove from S all |
| + # other entries. |
| + try: |
| + method = next(method for argument, method in arguments_methods |
| + if argument['is_optional']) |
| + test = 'Dart_IsNull(%s)' % cpp_value |
| + yield test, method |
| + except StopIteration: |
| + pass |
| + |
| + # 3. Otherwise: if V is null or undefined, and there is an entry in S that |
| + # has one of the following types at position i of its type list, |
| + # • a nullable type |
| + try: |
| + method = next(method for idl_type, method in idl_types_methods |
| + if idl_type.is_nullable) |
| + test = 'Dart_IsNull(%s)' % cpp_value |
| + yield test, method |
| + except StopIteration: |
| + pass |
| + |
| + # 4. Otherwise: if V is a platform object – but not a platform array |
| + # object – and there is an entry in S that has one of the following |
| + # types at position i of its type list, |
| + # • an interface type that V implements |
| + # (Unlike most of these tests, this can return multiple methods, since we |
| + # test if it implements an interface. Thus we need a for loop, not a next.) |
| + # (We distinguish wrapper types from built-in interface types.) |
| + for idl_type, method in ((idl_type, method) |
| + for idl_type, method in idl_types_methods |
| + if idl_type.is_wrapper_type): |
| + fmtstr = 'Dart{idl_type}::hasInstance({cpp_value})' |
| + if idl_type.base_type in DART_CHECK_TYPE: |
| + fmtstr = DART_CHECK_TYPE[idl_type.base_type] |
| + test = fmtstr.format(idl_type=idl_type.base_type, cpp_value=cpp_value) |
| + yield test, method |
| + |
| + # 8. Otherwise: if V is any kind of object except for a native Date object, |
| + # a native RegExp object, and there is an entry in S that has one of the |
| + # following types at position i of its type list, |
| + # • an array type |
| + # • a sequence type |
| + # ... |
| + # • a dictionary |
| + try: |
| + # FIXME: IDL dictionary not implemented, so use Blink Dictionary |
| + # http://crbug.com/321462 |
| + idl_type, method = next((idl_type, method) |
| + for idl_type, method in idl_types_methods |
| + if (idl_type.native_array_element_type or |
| + idl_type.name == 'Dictionary')) |
| + if idl_type.native_array_element_type: |
| + # (We test for Array instead of generic Object to type-check.) |
| + # FIXME: test for Object during resolution, then have type check for |
| + # Array in overloaded method: http://crbug.com/262383 |
| + test = 'Dart_IsList(%s)' % cpp_value |
| + else: |
| + # FIXME: should be '{1}->IsObject() && !{1}->IsDate() && !{1}->IsRegExp()'.format(cpp_value) |
| + # FIXME: the IsDate and IsRegExp checks can be skipped if we've |
| + # already generated tests for them. |
| + test = 'Dart_IsInstance(%s)' % cpp_value |
| + yield test, method |
| + except StopIteration: |
| + pass |
| + |
| + # (Check for exact type matches before performing automatic type conversion; |
| + # only needed if distinguishing between primitive types.) |
| + if len([idl_type.is_primitive_type for idl_type in idl_types]) > 1: |
| + # (Only needed if match in step 11, otherwise redundant.) |
| + if any(idl_type.is_string_type or idl_type.is_enum |
| + for idl_type in idl_types): |
| + # 10. Otherwise: if V is a Number value, and there is an entry in S |
| + # that has one of the following types at position i of its type |
| + # list, |
| + # • a numeric type |
| + try: |
| + method = next(method for idl_type, method in idl_types_methods |
| + if idl_type.is_numeric_type) |
| + test = 'Dart_IsNumber(%s)' % cpp_value |
| + yield test, method |
| + except StopIteration: |
| + pass |
| + |
| + # (Perform automatic type conversion, in order. If any of these match, |
| + # that’s the end, and no other tests are needed.) To keep this code simple, |
| + # we rely on the C++ compiler's dead code elimination to deal with the |
| + # redundancy if both cases below trigger. |
| + |
| + # 11. Otherwise: if there is an entry in S that has one of the following |
| + # types at position i of its type list, |
| + # • DOMString |
| + # • ByteString |
| + # • ScalarValueString [a DOMString typedef, per definition.] |
| + # • an enumeration type |
| + try: |
| + method = next(method for idl_type, method in idl_types_methods |
| + if idl_type.is_string_type or idl_type.is_enum) |
| + yield 'true', method |
| + except StopIteration: |
| + pass |
| + |
| + # 12. Otherwise: if there is an entry in S that has one of the following |
| + # types at position i of its type list, |
| + # • a numeric type |
| + try: |
| + method = next(method for idl_type, method in idl_types_methods |
| + if idl_type.is_numeric_type) |
| + yield 'true', method |
| + except StopIteration: |
| + pass |
| + |
| + |
| +################################################################################ |
| +# 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(dicts, f): |
| + """Returns common result of f across an iterable of dicts, or None. |
| + |
| + Call f for each dict and return its result if the same across all dicts. |
| + """ |
| + values = (f(d) for d in dicts) |
| + first_value = next(values) |
| + if all(value == first_value for value in values): |
| + return first_value |
| return None |
| +def common_key(dicts, key): |
| + """Returns common presence of a key across an iterable of dicts, or None. |
| + |
| + True if all dicts have the key, False if none of the dicts have the key, |
| + and None if some but not all dicts have the key. |
| + """ |
| + return common(dicts, lambda d: key in d) |
| + |
| + |
| +def common_value(dicts, key): |
| + """Returns common value of a key across an iterable of dicts, or None. |
| + |
| + Auxiliary function for overloads, so can consolidate an extended attribute |
| + that appears with the same value on all items in an overload set. |
| + """ |
| + return common(dicts, lambda d: d.get(key)) |
| + |
| + |
| +def sort_and_groupby(l, key=None): |
| + """Returns a generator of (key, list), sorting and grouping list by key.""" |
| + l.sort(key=key) |
| + return ((k, list(g)) for k, g in itertools.groupby(l, key)) |
| + |
| + |
| ################################################################################ |
| # Constructors |
| ################################################################################ |
| @@ -950,17 +1255,6 @@ def generate_constructor(interface, constructor): |
| } |
| -def generate_constructor_overloads(constructors): |
| - if len(constructors) <= 1: |
| - return |
| - for overload_index, constructor in enumerate(constructors): |
| - constructor.update({ |
| - 'overload_index': overload_index + 1, |
| - 'overload_resolution_expression': |
| - overload_resolution_expression(constructor), |
| - }) |
| - |
| - |
| # [NamedConstructor] |
| def generate_named_constructor(interface): |
| extended_attributes = interface.extended_attributes |