Home | History | Annotate | Download | only in scripts
      1 # Copyright (C) 2013 Google Inc. All rights reserved.
      2 # coding=utf-8
      3 #
      4 # Redistribution and use in source and binary forms, with or without
      5 # modification, are permitted provided that the following conditions are
      6 # met:
      7 #
      8 #     * Redistributions of source code must retain the above copyright
      9 # notice, this list of conditions and the following disclaimer.
     10 #     * Redistributions in binary form must reproduce the above
     11 # copyright notice, this list of conditions and the following disclaimer
     12 # in the documentation and/or other materials provided with the
     13 # distribution.
     14 #     * Neither the name of Google Inc. nor the names of its
     15 # contributors may be used to endorse or promote products derived from
     16 # this software without specific prior written permission.
     17 #
     18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     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.
     29 
     30 """Generate template values for an interface.
     31 
     32 Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
     33 """
     34 
     35 from collections import defaultdict
     36 import itertools
     37 from operator import itemgetter
     38 
     39 import idl_definitions
     40 from idl_definitions import IdlOperation
     41 import idl_types
     42 from idl_types import IdlType, inherits_interface
     43 import v8_attributes
     44 from v8_globals import includes
     45 import v8_methods
     46 import v8_types
     47 from v8_types import cpp_ptr_type, cpp_template_type
     48 import v8_utilities
     49 from v8_utilities import (capitalize, conditional_string, cpp_name, gc_type,
     50                           has_extended_attribute_value, runtime_enabled_function_name,
     51                           extended_attribute_value_as_list)
     52 
     53 
     54 INTERFACE_H_INCLUDES = frozenset([
     55     'bindings/core/v8/ScriptWrappable.h',
     56     'bindings/core/v8/V8Binding.h',
     57     'bindings/core/v8/V8DOMWrapper.h',
     58     'bindings/core/v8/WrapperTypeInfo.h',
     59     'platform/heap/Handle.h',
     60 ])
     61 INTERFACE_CPP_INCLUDES = frozenset([
     62     'bindings/core/v8/ExceptionState.h',
     63     'bindings/core/v8/V8DOMConfiguration.h',
     64     'bindings/core/v8/V8HiddenValue.h',
     65     'bindings/core/v8/V8ObjectConstructor.h',
     66     'core/dom/ContextFeatures.h',
     67     'core/dom/Document.h',
     68     'platform/RuntimeEnabledFeatures.h',
     69     'platform/TraceEvent.h',
     70     'wtf/GetPtr.h',
     71     'wtf/RefPtr.h',
     72 ])
     73 
     74 
     75 def interface_context(interface):
     76     includes.clear()
     77     includes.update(INTERFACE_CPP_INCLUDES)
     78     header_includes = set(INTERFACE_H_INCLUDES)
     79 
     80     parent_interface = interface.parent
     81     if parent_interface:
     82         header_includes.update(v8_types.includes_for_interface(parent_interface))
     83     extended_attributes = interface.extended_attributes
     84 
     85     is_audio_buffer = inherits_interface(interface.name, 'AudioBuffer')
     86     if is_audio_buffer:
     87         includes.add('modules/webaudio/AudioBuffer.h')
     88 
     89     is_document = inherits_interface(interface.name, 'Document')
     90     if is_document:
     91         includes.update(['bindings/core/v8/ScriptController.h',
     92                          'bindings/core/v8/WindowProxy.h',
     93                          'core/frame/LocalFrame.h'])
     94 
     95     # [ActiveDOMObject]
     96     is_active_dom_object = 'ActiveDOMObject' in extended_attributes
     97 
     98     # [CheckSecurity]
     99     is_check_security = 'CheckSecurity' in extended_attributes
    100     if is_check_security:
    101         includes.add('bindings/core/v8/BindingSecurity.h')
    102 
    103     # [DependentLifetime]
    104     is_dependent_lifetime = 'DependentLifetime' in extended_attributes
    105 
    106     # [Iterable]
    107     iterator_method = None
    108     if 'Iterable' in extended_attributes:
    109         iterator_operation = IdlOperation(interface.idl_name)
    110         iterator_operation.name = 'iterator'
    111         iterator_operation.idl_type = IdlType('Iterator')
    112         iterator_operation.extended_attributes['RaisesException'] = None
    113         iterator_operation.extended_attributes['CallWith'] = 'ScriptState'
    114         iterator_method = v8_methods.method_context(interface,
    115                                                     iterator_operation)
    116 
    117     # [MeasureAs]
    118     is_measure_as = 'MeasureAs' in extended_attributes
    119     if is_measure_as:
    120         includes.add('core/frame/UseCounter.h')
    121 
    122     # [SetWrapperReferenceFrom]
    123     reachable_node_function = extended_attributes.get('SetWrapperReferenceFrom')
    124     if reachable_node_function:
    125         includes.update(['bindings/core/v8/V8GCController.h',
    126                          'core/dom/Element.h'])
    127 
    128     # [SetWrapperReferenceTo]
    129     set_wrapper_reference_to_list = [{
    130         'name': argument.name,
    131         # FIXME: properly should be:
    132         # 'cpp_type': argument.idl_type.cpp_type_args(raw_type=True),
    133         # (if type is non-wrapper type like NodeFilter, normally RefPtr)
    134         # Raw pointers faster though, and NodeFilter hacky anyway.
    135         'cpp_type': argument.idl_type.implemented_as + '*',
    136         'idl_type': argument.idl_type,
    137         'v8_type': v8_types.v8_type(argument.idl_type.name),
    138     } for argument in extended_attributes.get('SetWrapperReferenceTo', [])]
    139     for set_wrapper_reference_to in set_wrapper_reference_to_list:
    140         set_wrapper_reference_to['idl_type'].add_includes_for_type()
    141 
    142     # [NotScriptWrappable]
    143     is_script_wrappable = 'NotScriptWrappable' not in extended_attributes
    144 
    145     # [SpecialWrapFor]
    146     if 'SpecialWrapFor' in extended_attributes:
    147         special_wrap_for = extended_attribute_value_as_list(interface, 'SpecialWrapFor')
    148     else:
    149         special_wrap_for = []
    150     for special_wrap_interface in special_wrap_for:
    151         v8_types.add_includes_for_interface(special_wrap_interface)
    152 
    153     # [Custom=Wrap], [SetWrapperReferenceFrom]
    154     has_visit_dom_wrapper = (
    155         has_extended_attribute_value(interface, 'Custom', 'VisitDOMWrapper') or
    156         reachable_node_function or
    157         set_wrapper_reference_to_list)
    158 
    159     this_gc_type = gc_type(interface)
    160 
    161     wrapper_class_id = ('NodeClassId' if inherits_interface(interface.name, 'Node') else 'ObjectClassId')
    162 
    163     context = {
    164         'conditional_string': conditional_string(interface),  # [Conditional]
    165         'cpp_class': cpp_name(interface),
    166         'gc_type': this_gc_type,
    167         # FIXME: Remove 'EventTarget' special handling, http://crbug.com/383699
    168         'has_access_check_callbacks': (is_check_security and
    169                                        interface.name != 'Window' and
    170                                        interface.name != 'EventTarget'),
    171         'has_custom_legacy_call_as_function': has_extended_attribute_value(interface, 'Custom', 'LegacyCallAsFunction'),  # [Custom=LegacyCallAsFunction]
    172         'has_custom_to_v8': has_extended_attribute_value(interface, 'Custom', 'ToV8'),  # [Custom=ToV8]
    173         'has_custom_wrap': has_extended_attribute_value(interface, 'Custom', 'Wrap'),  # [Custom=Wrap]
    174         'has_visit_dom_wrapper': has_visit_dom_wrapper,
    175         'header_includes': header_includes,
    176         'interface_name': interface.name,
    177         'is_active_dom_object': is_active_dom_object,
    178         'is_audio_buffer': is_audio_buffer,
    179         'is_check_security': is_check_security,
    180         'is_dependent_lifetime': is_dependent_lifetime,
    181         'is_document': is_document,
    182         'is_event_target': inherits_interface(interface.name, 'EventTarget'),
    183         'is_exception': interface.is_exception,
    184         'is_node': inherits_interface(interface.name, 'Node'),
    185         'is_script_wrappable': is_script_wrappable,
    186         'iterator_method': iterator_method,
    187         'lifetime': 'Dependent'
    188             if (has_visit_dom_wrapper or
    189                 is_active_dom_object or
    190                 is_dependent_lifetime)
    191             else 'Independent',
    192         'measure_as': v8_utilities.measure_as(interface),  # [MeasureAs]
    193         'parent_interface': parent_interface,
    194         'pass_cpp_type': cpp_template_type(
    195             cpp_ptr_type('PassRefPtr', 'RawPtr', this_gc_type),
    196             cpp_name(interface)),
    197         'reachable_node_function': reachable_node_function,
    198         'runtime_enabled_function': runtime_enabled_function_name(interface),  # [RuntimeEnabled]
    199         'set_wrapper_reference_to_list': set_wrapper_reference_to_list,
    200         'special_wrap_for': special_wrap_for,
    201         'v8_class': v8_utilities.v8_class_name(interface),
    202         'wrapper_class_id': wrapper_class_id,
    203     }
    204 
    205     # Constructors
    206     constructors = [constructor_context(interface, constructor)
    207                     for constructor in interface.constructors
    208                     # FIXME: shouldn't put named constructors with constructors
    209                     # (currently needed for Perl compatibility)
    210                     # Handle named constructors separately
    211                     if constructor.name == 'Constructor']
    212     if len(constructors) > 1:
    213         context['constructor_overloads'] = overloads_context(constructors)
    214 
    215     # [CustomConstructor]
    216     custom_constructors = [{  # Only needed for computing interface length
    217         'number_of_required_arguments':
    218             number_of_required_arguments(constructor),
    219     } for constructor in interface.custom_constructors]
    220 
    221     # [EventConstructor]
    222     has_event_constructor = 'EventConstructor' in extended_attributes
    223     any_type_attributes = [attribute for attribute in interface.attributes
    224                            if attribute.idl_type.name == 'Any']
    225     if has_event_constructor:
    226         includes.add('bindings/core/v8/Dictionary.h')
    227         if any_type_attributes:
    228             includes.add('bindings/core/v8/SerializedScriptValue.h')
    229 
    230     # [NamedConstructor]
    231     named_constructor = named_constructor_context(interface)
    232 
    233     if (constructors or custom_constructors or has_event_constructor or
    234         named_constructor):
    235         includes.add('bindings/core/v8/V8ObjectConstructor.h')
    236         includes.add('core/frame/LocalDOMWindow.h')
    237 
    238     context.update({
    239         'any_type_attributes': any_type_attributes,
    240         'constructors': constructors,
    241         'has_custom_constructor': bool(custom_constructors),
    242         'has_event_constructor': has_event_constructor,
    243         'interface_length':
    244             interface_length(interface, constructors + custom_constructors),
    245         'is_constructor_raises_exception': extended_attributes.get('RaisesException') == 'Constructor',  # [RaisesException=Constructor]
    246         'named_constructor': named_constructor,
    247     })
    248 
    249     constants = [constant_context(constant) for constant in interface.constants]
    250 
    251     special_getter_constants = []
    252     runtime_enabled_constants = []
    253     constant_configuration_constants = []
    254 
    255     for constant in constants:
    256         if constant['measure_as'] or constant['deprecate_as']:
    257             special_getter_constants.append(constant)
    258             continue
    259         if constant['runtime_enabled_function']:
    260             runtime_enabled_constants.append(constant)
    261             continue
    262         constant_configuration_constants.append(constant)
    263 
    264     # Constants
    265     context.update({
    266         'constant_configuration_constants': constant_configuration_constants,
    267         'constants': constants,
    268         'do_not_check_constants': 'DoNotCheckConstants' in extended_attributes,
    269         'has_constant_configuration': any(
    270             not constant['runtime_enabled_function']
    271             for constant in constants),
    272         'runtime_enabled_constants': runtime_enabled_constants,
    273         'special_getter_constants': special_getter_constants,
    274     })
    275 
    276     # Attributes
    277     attributes = [v8_attributes.attribute_context(interface, attribute)
    278                   for attribute in interface.attributes]
    279     context.update({
    280         'attributes': attributes,
    281         'has_accessors': any(attribute['is_expose_js_accessors'] and attribute['should_be_exposed_to_script'] for attribute in attributes),
    282         'has_attribute_configuration': any(
    283              not (attribute['is_expose_js_accessors'] or
    284                   attribute['is_static'] or
    285                   attribute['runtime_enabled_function'] or
    286                   attribute['per_context_enabled_function'])
    287              and attribute['should_be_exposed_to_script']
    288              for attribute in attributes),
    289         'has_conditional_attributes': any(attribute['per_context_enabled_function'] or attribute['exposed_test'] for attribute in attributes),
    290         'has_constructor_attributes': any(attribute['constructor_type'] for attribute in attributes),
    291         'has_replaceable_attributes': any(attribute['is_replaceable'] for attribute in attributes),
    292     })
    293 
    294     # Methods
    295     methods = [v8_methods.method_context(interface, method)
    296                for method in interface.operations
    297                if method.name]  # Skip anonymous special operations (methods)
    298     compute_method_overloads_context(methods)
    299 
    300     # Stringifier
    301     if interface.stringifier:
    302         stringifier = interface.stringifier
    303         method = IdlOperation(interface.idl_name)
    304         method.name = 'toString'
    305         method.idl_type = IdlType('DOMString')
    306         method.extended_attributes.update(stringifier.extended_attributes)
    307         if stringifier.attribute:
    308             method.extended_attributes['ImplementedAs'] = stringifier.attribute.name
    309         elif stringifier.operation:
    310             method.extended_attributes['ImplementedAs'] = stringifier.operation.name
    311         methods.append(v8_methods.method_context(interface, method))
    312 
    313     conditionally_enabled_methods = []
    314     custom_registration_methods = []
    315     method_configuration_methods = []
    316 
    317     for method in methods:
    318         # Skip all but one method in each set of overloaded methods.
    319         if 'overload_index' in method and 'overloads' not in method:
    320             continue
    321 
    322         if 'overloads' in method:
    323             overloads = method['overloads']
    324             per_context_enabled_function = overloads['per_context_enabled_function_all']
    325             conditionally_exposed_function = overloads['exposed_test_all']
    326             runtime_enabled_function = overloads['runtime_enabled_function_all']
    327             has_custom_registration = overloads['has_custom_registration_all']
    328         else:
    329             per_context_enabled_function = method['per_context_enabled_function']
    330             conditionally_exposed_function = method['exposed_test']
    331             runtime_enabled_function = method['runtime_enabled_function']
    332             has_custom_registration = method['has_custom_registration']
    333 
    334         if per_context_enabled_function or conditionally_exposed_function:
    335             conditionally_enabled_methods.append(method)
    336             continue
    337         if runtime_enabled_function or has_custom_registration:
    338             custom_registration_methods.append(method)
    339             continue
    340         if method['should_be_exposed_to_script']:
    341             method_configuration_methods.append(method)
    342 
    343     for method in methods:
    344         # The value of the Function objects length property is a Number
    345         # determined as follows:
    346         # 1. Let S be the effective overload set for regular operations (if the
    347         # operation is a regular operation) or for static operations (if the
    348         # operation is a static operation) with identifier id on interface I and
    349         # with argument count 0.
    350         # 2. Return the length of the shortest argument list of the entries in S.
    351         # FIXME: This calculation doesn't take into account whether runtime
    352         # enabled overloads are actually enabled, so length may be incorrect.
    353         # E.g., [RuntimeEnabled=Foo] void f(); void f(long x);
    354         # should have length 1 if Foo is not enabled, but length 0 if it is.
    355         method['length'] = (method['overloads']['minarg'] if 'overloads' in method else
    356                             method['number_of_required_arguments'])
    357 
    358     context.update({
    359         'conditionally_enabled_methods': conditionally_enabled_methods,
    360         'custom_registration_methods': custom_registration_methods,
    361         'has_origin_safe_method_setter': any(
    362             method['is_check_security_for_frame'] and not method['is_read_only']
    363             for method in methods),
    364         'has_private_script': any(attribute['is_implemented_in_private_script'] for attribute in attributes) or
    365             any(method['is_implemented_in_private_script'] for method in methods),
    366         'method_configuration_methods': method_configuration_methods,
    367         'methods': methods,
    368     })
    369 
    370     context.update({
    371         'indexed_property_getter': indexed_property_getter(interface),
    372         'indexed_property_setter': indexed_property_setter(interface),
    373         'indexed_property_deleter': indexed_property_deleter(interface),
    374         'is_override_builtins': 'OverrideBuiltins' in extended_attributes,
    375         'named_property_getter': named_property_getter(interface),
    376         'named_property_setter': named_property_setter(interface),
    377         'named_property_deleter': named_property_deleter(interface),
    378     })
    379 
    380     return context
    381 
    382 
    383 # [DeprecateAs], [Reflect], [RuntimeEnabled]
    384 def constant_context(constant):
    385     extended_attributes = constant.extended_attributes
    386     return {
    387         'cpp_class': extended_attributes.get('PartialInterfaceImplementedAs'),
    388         'deprecate_as': v8_utilities.deprecate_as(constant),  # [DeprecateAs]
    389         'idl_type': constant.idl_type.name,
    390         'measure_as': v8_utilities.measure_as(constant),  # [MeasureAs]
    391         'name': constant.name,
    392         # FIXME: use 'reflected_name' as correct 'name'
    393         'reflected_name': extended_attributes.get('Reflect', constant.name),
    394         'runtime_enabled_function': runtime_enabled_function_name(constant),
    395         'value': constant.value,
    396     }
    397 
    398 
    399 ################################################################################
    400 # Overloads
    401 ################################################################################
    402 
    403 def compute_method_overloads_context(methods):
    404     # Regular methods
    405     compute_method_overloads_context_by_type([method for method in methods
    406                                               if not method['is_static']])
    407     # Static methods
    408     compute_method_overloads_context_by_type([method for method in methods
    409                                               if method['is_static']])
    410 
    411 
    412 def compute_method_overloads_context_by_type(methods):
    413     """Computes |method.overload*| template values.
    414 
    415     Called separately for static and non-static (regular) methods,
    416     as these are overloaded separately.
    417     Modifies |method| in place for |method| in |methods|.
    418     Doesn't change the |methods| list itself (only the values, i.e. individual
    419     methods), so ok to treat these separately.
    420     """
    421     # Add overload information only to overloaded methods, so template code can
    422     # easily verify if a function is overloaded
    423     for name, overloads in method_overloads_by_name(methods):
    424         # Resolution function is generated after last overloaded function;
    425         # package necessary information into |method.overloads| for that method.
    426         overloads[-1]['overloads'] = overloads_context(overloads)
    427         overloads[-1]['overloads']['name'] = name
    428 
    429 
    430 def method_overloads_by_name(methods):
    431     """Returns generator of overloaded methods by name: [name, [method]]"""
    432     # Filter to only methods that are actually overloaded
    433     method_counts = Counter(method['name'] for method in methods)
    434     overloaded_method_names = set(name
    435                                   for name, count in method_counts.iteritems()
    436                                   if count > 1)
    437     overloaded_methods = [method for method in methods
    438                           if method['name'] in overloaded_method_names]
    439 
    440     # Group by name (generally will be defined together, but not necessarily)
    441     return sort_and_groupby(overloaded_methods, itemgetter('name'))
    442 
    443 
    444 def overloads_context(overloads):
    445     """Returns |overloads| template values for a single name.
    446 
    447     Sets |method.overload_index| in place for |method| in |overloads|
    448     and returns dict of overall overload template values.
    449     """
    450     assert len(overloads) > 1  # only apply to overloaded names
    451     for index, method in enumerate(overloads, 1):
    452         method['overload_index'] = index
    453 
    454     effective_overloads_by_length = effective_overload_set_by_length(overloads)
    455     lengths = [length for length, _ in effective_overloads_by_length]
    456     name = overloads[0].get('name', '<constructor>')
    457 
    458     # Check and fail if all overloads with the shortest acceptable arguments
    459     # list are runtime enabled, since we would otherwise set 'length' on the
    460     # function object to an incorrect value when none of those overloads were
    461     # actually enabled at runtime. The exception is if all overloads are
    462     # controlled by the same runtime enabled feature, in which case there would
    463     # be no function object at all if it is not enabled.
    464     shortest_overloads = effective_overloads_by_length[0][1]
    465     if (all(method.get('runtime_enabled_function')
    466             for method, _, _ in shortest_overloads) and
    467         not common_value(overloads, 'runtime_enabled_function')):
    468         raise ValueError('Function.length of %s depends on runtime enabled features' % name)
    469 
    470     # Check and fail if overloads disagree on any of the extended attributes
    471     # that affect how the method should be registered.
    472     # Skip the check for overloaded constructors, since they don't support any
    473     # of the extended attributes in question.
    474     if not overloads[0].get('is_constructor'):
    475         overload_extended_attributes = [
    476             method['custom_registration_extended_attributes']
    477             for method in overloads]
    478         for extended_attribute in v8_methods.CUSTOM_REGISTRATION_EXTENDED_ATTRIBUTES:
    479             if common_key(overload_extended_attributes, extended_attribute) is None:
    480                 raise ValueError('Overloads of %s have conflicting extended attribute %s'
    481                                  % (name, extended_attribute))
    482 
    483     # Check and fail if overloads disagree about whether the return type
    484     # is a Promise or not.
    485     promise_overload_count = sum(1 for method in overloads if method.get('idl_type') == 'Promise')
    486     if promise_overload_count not in (0, len(overloads)):
    487         raise ValueError('Overloads of %s have conflicting Promise/non-Promise types'
    488                          % (name))
    489 
    490     return {
    491         'deprecate_all_as': common_value(overloads, 'deprecate_as'),  # [DeprecateAs]
    492         'exposed_test_all': common_value(overloads, 'exposed_test'),  # [Exposed]
    493         'has_custom_registration_all': common_value(overloads, 'has_custom_registration'),
    494         'length_tests_methods': length_tests_methods(effective_overloads_by_length),
    495         # 1. Let maxarg be the length of the longest type list of the
    496         # entries in S.
    497         'maxarg': lengths[-1],
    498         'measure_all_as': common_value(overloads, 'measure_as'),  # [MeasureAs]
    499         'minarg': lengths[0],
    500         'per_context_enabled_function_all': common_value(overloads, 'per_context_enabled_function'),  # [PerContextEnabled]
    501         'runtime_enabled_function_all': common_value(overloads, 'runtime_enabled_function'),  # [RuntimeEnabled]
    502         'valid_arities': lengths
    503             # Only need to report valid arities if there is a gap in the
    504             # sequence of possible lengths, otherwise invalid length means
    505             # "not enough arguments".
    506             if lengths[-1] - lengths[0] != len(lengths) - 1 else None,
    507     }
    508 
    509 
    510 def effective_overload_set(F):
    511     """Returns the effective overload set of an overloaded function.
    512 
    513     An effective overload set is the set of overloaded functions + signatures
    514     (type list of arguments, with optional and variadic arguments included or
    515     not), and is used in the overload resolution algorithm.
    516 
    517     For example, given input [f1(optional long x), f2(DOMString s)], the output
    518     is informally [f1(), f1(long), f2(DOMString)], and formally
    519     [(f1, [], []), (f1, [long], [optional]), (f2, [DOMString], [required])].
    520 
    521     Currently the optionality list is a list of |is_optional| booleans (True
    522     means optional, False means required); to support variadics this needs to
    523     be tri-valued as required, optional, or variadic.
    524 
    525     Formally:
    526     An effective overload set represents the allowable invocations for a
    527     particular operation, constructor (specified with [Constructor] or
    528     [NamedConstructor]), legacy caller or callback function.
    529 
    530     An additional argument N (argument count) is needed when overloading
    531     variadics, but we don't use that currently.
    532 
    533     Spec: http://heycam.github.io/webidl/#dfn-effective-overload-set
    534 
    535     Formally the input and output lists are sets, but methods are stored
    536     internally as dicts, which can't be stored in a set because they are not
    537     hashable, so we use lists instead.
    538 
    539     Arguments:
    540         F: list of overloads for a given callable name.
    541 
    542     Returns:
    543         S: list of tuples of the form (callable, type list, optionality list).
    544     """
    545     # Code closely follows the algorithm in the spec, for clarity and
    546     # correctness, and hence is not very Pythonic.
    547 
    548     # 1. Initialize S to .
    549     # (We use a list because we can't use a set, as noted above.)
    550     S = []
    551 
    552     # 2. Let F be a set with elements as follows, according to the kind of
    553     # effective overload set:
    554     # (Passed as argument, nothing to do.)
    555 
    556     # 3. & 4. (maxarg, m) are only needed for variadics, not used.
    557 
    558     # 5. For each operation, extended attribute or callback function X in F:
    559     for X in F:  # X is the "callable", F is the overloads.
    560         arguments = X['arguments']
    561         # 1. Let n be the number of arguments X is declared to take.
    562         n = len(arguments)
    563         # 2. Let t0..n1 be a list of types, where ti is the type of Xs
    564         # argument at index i.
    565         # (type list)
    566         t = tuple(argument['idl_type_object'] for argument in arguments)
    567         # 3. Let o0..n1 be a list of optionality values, where oi is variadic
    568         # if Xs argument at index i is a final, variadic argument, optional
    569         # if the argument is optional, and required otherwise.
    570         # (optionality list)
    571         # (Were just using a boolean for optional vs. required.)
    572         o = tuple(argument['is_optional'] for argument in arguments)
    573         # 4. Add to S the tuple <X, t0..n1, o0..n1>.
    574         S.append((X, t, o))
    575         # 5. If X is declared to be variadic, then:
    576         # (Not used, so not implemented.)
    577         # 6. Initialize i to n1.
    578         i = n - 1
    579         # 7. While i  0:
    580         # Spec bug (fencepost error); should be While i > 0:
    581         # https://www.w3.org/Bugs/Public/show_bug.cgi?id=25590
    582         while i > 0:
    583             # 1. If argument i of X is not optional, then break this loop.
    584             if not o[i]:
    585                 break
    586             # 2. Otherwise, add to S the tuple <X, t0..i1, o0..i1>.
    587             S.append((X, t[:i], o[:i]))
    588             # 3. Set i to i1.
    589             i = i - 1
    590         # 8. If n > 0 and all arguments of X are optional, then add to S the
    591         # tuple <X, (), ()> (where () represents the empty list).
    592         if n > 0 and all(oi for oi in o):
    593             S.append((X, [], []))
    594     # 6. The effective overload set is S.
    595     return S
    596 
    597 
    598 def effective_overload_set_by_length(overloads):
    599     def type_list_length(entry):
    600         # Entries in the effective overload set are 3-tuples:
    601         # (callable, type list, optionality list)
    602         return len(entry[1])
    603 
    604     effective_overloads = effective_overload_set(overloads)
    605     return list(sort_and_groupby(effective_overloads, type_list_length))
    606 
    607 
    608 def distinguishing_argument_index(entries):
    609     """Returns the distinguishing argument index for a sequence of entries.
    610 
    611     Entries are elements of the effective overload set with the same number
    612     of arguments (formally, same type list length), each a 3-tuple of the form
    613     (callable, type list, optionality list).
    614 
    615     Spec: http://heycam.github.io/webidl/#dfn-distinguishing-argument-index
    616 
    617     If there is more than one entry in an effective overload set that has a
    618     given type list length, then for those entries there must be an index i
    619     such that for each pair of entries the types at index i are
    620     distinguishable.
    621     The lowest such index is termed the distinguishing argument index for the
    622     entries of the effective overload set with the given type list length.
    623     """
    624     # Only applicable If there is more than one entry
    625     assert len(entries) > 1
    626     type_lists = [tuple(idl_type.name for idl_type in entry[1])
    627                   for entry in entries]
    628     type_list_length = len(type_lists[0])
    629     # Only applicable for entries that [have] a given type list length
    630     assert all(len(type_list) == type_list_length for type_list in type_lists)
    631     name = entries[0][0].get('name', 'Constructor')  # for error reporting
    632 
    633     # The spec defines the distinguishing argument index by conditions it must
    634     # satisfy, but does not give an algorithm.
    635     #
    636     # We compute the distinguishing argument index by first computing the
    637     # minimum index where not all types are the same, and then checking that
    638     # all types in this position are distinguishable (and the optionality lists
    639     # up to this point are identical), since "minimum index where not all types
    640     # are the same" is a *necessary* condition, and more direct to check than
    641     # distinguishability.
    642     types_by_index = (set(types) for types in zip(*type_lists))
    643     try:
    644         # In addition, for each index j, where j is less than the
    645         #  distinguishing argument index for a given type list length, the types
    646         #  at index j in all of the entries type lists must be the same
    647         index = next(i for i, types in enumerate(types_by_index)
    648                      if len(types) > 1)
    649     except StopIteration:
    650         raise ValueError('No distinguishing index found for %s, length %s:\n'
    651                          'All entries have the same type list:\n'
    652                          '%s' % (name, type_list_length, type_lists[0]))
    653     # Check optionality
    654     # and the booleans in the corresponding list indicating argument
    655     #  optionality must be the same.
    656     # FIXME: spec typo: optionality value is no longer a boolean
    657     # https://www.w3.org/Bugs/Public/show_bug.cgi?id=25628
    658     initial_optionality_lists = set(entry[2][:index] for entry in entries)
    659     if len(initial_optionality_lists) > 1:
    660         raise ValueError(
    661             'Invalid optionality lists for %s, length %s:\n'
    662             'Optionality lists differ below distinguishing argument index %s:\n'
    663             '%s'
    664             % (name, type_list_length, index, set(initial_optionality_lists)))
    665 
    666     # Check distinguishability
    667     # http://heycam.github.io/webidl/#dfn-distinguishable
    668     # Use names to check for distinct types, since objects are distinct
    669     # FIXME: check distinguishability more precisely, for validation
    670     distinguishing_argument_type_names = [type_list[index]
    671                                           for type_list in type_lists]
    672     if (len(set(distinguishing_argument_type_names)) !=
    673         len(distinguishing_argument_type_names)):
    674         raise ValueError('Types in distinguishing argument are not distinct:\n'
    675                          '%s' % distinguishing_argument_type_names)
    676 
    677     return index
    678 
    679 
    680 def length_tests_methods(effective_overloads_by_length):
    681     """Returns sorted list of resolution tests and associated methods, by length.
    682 
    683     This builds the main data structure for the overload resolution loop.
    684     For a given argument length, bindings test argument at distinguishing
    685     argument index, in order given by spec: if it is compatible with
    686     (optionality or) type required by an overloaded method, resolve to that
    687     method.
    688 
    689     Returns:
    690         [(length, [(test, method)])]
    691     """
    692     return [(length, list(resolution_tests_methods(effective_overloads)))
    693             for length, effective_overloads in effective_overloads_by_length]
    694 
    695 
    696 def resolution_tests_methods(effective_overloads):
    697     """Yields resolution test and associated method, in resolution order, for effective overloads of a given length.
    698 
    699     This is the heart of the resolution algorithm.
    700     http://heycam.github.io/webidl/#dfn-overload-resolution-algorithm
    701 
    702     Note that a given method can be listed multiple times, with different tests!
    703     This is to handle implicit type conversion.
    704 
    705     Returns:
    706         [(test, method)]
    707     """
    708     methods = [effective_overload[0]
    709                for effective_overload in effective_overloads]
    710     if len(methods) == 1:
    711         # If only one method with a given length, no test needed
    712         yield 'true', methods[0]
    713         return
    714 
    715     # 6. If there is more than one entry in S, then set d to be the
    716     # distinguishing argument index for the entries of S.
    717     index = distinguishing_argument_index(effective_overloads)
    718     # (7-9 are for handling |undefined| values for optional arguments before
    719     # the distinguishing argument (as missing), so you can specify only some
    720     # optional arguments. We dont support this, so we skip these steps.)
    721     # 10. If i = d, then:
    722     # (d is the distinguishing argument index)
    723     # 1. Let V be argi.
    724     #     Note: This is the argument that will be used to resolve which
    725     #           overload is selected.
    726     cpp_value = 'info[%s]' % index
    727 
    728     # Extract argument and IDL type to simplify accessing these in each loop.
    729     arguments = [method['arguments'][index] for method in methods]
    730     arguments_methods = zip(arguments, methods)
    731     idl_types = [argument['idl_type_object'] for argument in arguments]
    732     idl_types_methods = zip(idl_types, methods)
    733 
    734     # We cant do a single loop through all methods or simply sort them, because
    735     # a method may be listed in multiple steps of the resolution algorithm, and
    736     # which test to apply differs depending on the step.
    737     #
    738     # Instead, we need to go through all methods at each step, either finding
    739     # first match (if only one test is allowed) or filtering to matches (if
    740     # multiple tests are allowed), and generating an appropriate tests.
    741 
    742     # 2. If V is undefined, and there is an entry in S whose list of
    743     # optionality values has optional at index i, then remove from S all
    744     # other entries.
    745     try:
    746         method = next(method for argument, method in arguments_methods
    747                       if argument['is_optional'])
    748         test = '%s->IsUndefined()' % cpp_value
    749         yield test, method
    750     except StopIteration:
    751         pass
    752 
    753     # 3. Otherwise: if V is null or undefined, and there is an entry in S that
    754     # has one of the following types at position i of its type list,
    755     #  a nullable type
    756     try:
    757         method = next(method for idl_type, method in idl_types_methods
    758                       if idl_type.is_nullable)
    759         test = 'isUndefinedOrNull(%s)' % cpp_value
    760         yield test, method
    761     except StopIteration:
    762         pass
    763 
    764     # 4. Otherwise: if V is a platform object  but not a platform array
    765     # object  and there is an entry in S that has one of the following
    766     # types at position i of its type list,
    767     #  an interface type that V implements
    768     # (Unlike most of these tests, this can return multiple methods, since we
    769     #  test if it implements an interface. Thus we need a for loop, not a next.)
    770     # (We distinguish wrapper types from built-in interface types.)
    771     for idl_type, method in ((idl_type, method)
    772                              for idl_type, method in idl_types_methods
    773                              if idl_type.is_wrapper_type):
    774         test = 'V8{idl_type}::hasInstance({cpp_value}, info.GetIsolate())'.format(idl_type=idl_type.base_type, cpp_value=cpp_value)
    775         yield test, method
    776 
    777     # 8. Otherwise: if V is any kind of object except for a native Date object,
    778     # a native RegExp object, and there is an entry in S that has one of the
    779     # following types at position i of its type list,
    780     #  an array type
    781     #  a sequence type
    782     # ...
    783     #  a dictionary
    784     #
    785     # FIXME:
    786     # We don't strictly follow the algorithm here. The algorithm says "remove
    787     # all other entries" if there is "one entry" matching, but we yield all
    788     # entries to support following constructors:
    789     # [constructor(sequence<DOMString> arg), constructor(Dictionary arg)]
    790     # interface I { ... }
    791     # (Need to check array types before objects because an array is an object)
    792     for idl_type, method in idl_types_methods:
    793         if idl_type.native_array_element_type:
    794             # (We test for Array instead of generic Object to type-check.)
    795             # FIXME: test for Object during resolution, then have type check for
    796             # Array in overloaded method: http://crbug.com/262383
    797             yield '%s->IsArray()' % cpp_value, method
    798     for idl_type, method in idl_types_methods:
    799         if idl_type.is_dictionary or idl_type.name == 'Dictionary':
    800             # FIXME: should be '{1}->IsObject() && !{1}->IsDate() && !{1}->IsRegExp()'.format(cpp_value)
    801             # FIXME: the IsDate and IsRegExp checks can be skipped if we've
    802             # already generated tests for them.
    803             yield '%s->IsObject()' % cpp_value, method
    804 
    805     # (Check for exact type matches before performing automatic type conversion;
    806     # only needed if distinguishing between primitive types.)
    807     if len([idl_type.is_primitive_type for idl_type in idl_types]) > 1:
    808         # (Only needed if match in step 11, otherwise redundant.)
    809         if any(idl_type.is_string_type or idl_type.is_enum
    810                for idl_type in idl_types):
    811             # 10. Otherwise: if V is a Number value, and there is an entry in S
    812             # that has one of the following types at position i of its type
    813             # list,
    814             #  a numeric type
    815             try:
    816                 method = next(method for idl_type, method in idl_types_methods
    817                               if idl_type.is_numeric_type)
    818                 test = '%s->IsNumber()' % cpp_value
    819                 yield test, method
    820             except StopIteration:
    821                 pass
    822 
    823     # (Perform automatic type conversion, in order. If any of these match,
    824     # thats the end, and no other tests are needed.) To keep this code simple,
    825     # we rely on the C++ compiler's dead code elimination to deal with the
    826     # redundancy if both cases below trigger.
    827 
    828     # 11. Otherwise: if there is an entry in S that has one of the following
    829     # types at position i of its type list,
    830     #  DOMString
    831     #  ByteString
    832     #  ScalarValueString [a DOMString typedef, per definition.]
    833     #  an enumeration type
    834     try:
    835         method = next(method for idl_type, method in idl_types_methods
    836                       if idl_type.is_string_type or idl_type.is_enum)
    837         yield 'true', method
    838     except StopIteration:
    839         pass
    840 
    841     # 12. Otherwise: if there is an entry in S that has one of the following
    842     # types at position i of its type list,
    843     #  a numeric type
    844     try:
    845         method = next(method for idl_type, method in idl_types_methods
    846                       if idl_type.is_numeric_type)
    847         yield 'true', method
    848     except StopIteration:
    849         pass
    850 
    851 
    852 ################################################################################
    853 # Utility functions
    854 ################################################################################
    855 
    856 def Counter(iterable):
    857     # Once using Python 2.7, using collections.Counter
    858     counter = defaultdict(lambda: 0)
    859     for item in iterable:
    860         counter[item] += 1
    861     return counter
    862 
    863 
    864 def common(dicts, f):
    865     """Returns common result of f across an iterable of dicts, or None.
    866 
    867     Call f for each dict and return its result if the same across all dicts.
    868     """
    869     values = (f(d) for d in dicts)
    870     first_value = next(values)
    871     if all(value == first_value for value in values):
    872         return first_value
    873     return None
    874 
    875 
    876 def common_key(dicts, key):
    877     """Returns common presence of a key across an iterable of dicts, or None.
    878 
    879     True if all dicts have the key, False if none of the dicts have the key,
    880     and None if some but not all dicts have the key.
    881     """
    882     return common(dicts, lambda d: key in d)
    883 
    884 
    885 def common_value(dicts, key):
    886     """Returns common value of a key across an iterable of dicts, or None.
    887 
    888     Auxiliary function for overloads, so can consolidate an extended attribute
    889     that appears with the same value on all items in an overload set.
    890     """
    891     return common(dicts, lambda d: d.get(key))
    892 
    893 
    894 def sort_and_groupby(l, key=None):
    895     """Returns a generator of (key, list), sorting and grouping list by key."""
    896     l.sort(key=key)
    897     return ((k, list(g)) for k, g in itertools.groupby(l, key))
    898 
    899 
    900 ################################################################################
    901 # Constructors
    902 ################################################################################
    903 
    904 # [Constructor]
    905 def constructor_context(interface, constructor):
    906     # [RaisesException=Constructor]
    907     is_constructor_raises_exception = \
    908         interface.extended_attributes.get('RaisesException') == 'Constructor'
    909 
    910     return {
    911         'arguments': [v8_methods.argument_context(interface, constructor, argument, index)
    912                       for index, argument in enumerate(constructor.arguments)],
    913         'cpp_type': cpp_template_type(
    914             cpp_ptr_type('RefPtr', 'RawPtr', gc_type(interface)),
    915             cpp_name(interface)),
    916         'cpp_value': v8_methods.cpp_value(
    917             interface, constructor, len(constructor.arguments)),
    918         'has_exception_state':
    919             is_constructor_raises_exception or
    920             any(argument for argument in constructor.arguments
    921                 if argument.idl_type.name == 'SerializedScriptValue' or
    922                    argument.idl_type.v8_conversion_needs_exception_state),
    923         'is_call_with_document':
    924             # [ConstructorCallWith=Document]
    925             has_extended_attribute_value(interface,
    926                 'ConstructorCallWith', 'Document'),
    927         'is_call_with_execution_context':
    928             # [ConstructorCallWith=ExecutionContext]
    929             has_extended_attribute_value(interface,
    930                 'ConstructorCallWith', 'ExecutionContext'),
    931         'is_constructor': True,
    932         'is_named_constructor': False,
    933         'is_raises_exception': is_constructor_raises_exception,
    934         'number_of_required_arguments':
    935             number_of_required_arguments(constructor),
    936     }
    937 
    938 
    939 # [NamedConstructor]
    940 def named_constructor_context(interface):
    941     extended_attributes = interface.extended_attributes
    942     if 'NamedConstructor' not in extended_attributes:
    943         return None
    944     # FIXME: parser should return named constructor separately;
    945     # included in constructors (and only name stored in extended attribute)
    946     # for Perl compatibility
    947     idl_constructor = interface.constructors[-1]
    948     assert idl_constructor.name == 'NamedConstructor'
    949     context = constructor_context(interface, idl_constructor)
    950     context.update({
    951         'name': extended_attributes['NamedConstructor'],
    952         'is_named_constructor': True,
    953     })
    954     return context
    955 
    956 
    957 def number_of_required_arguments(constructor):
    958     return len([argument for argument in constructor.arguments
    959                 if not argument.is_optional])
    960 
    961 
    962 def interface_length(interface, constructors):
    963     # Docs: http://heycam.github.io/webidl/#es-interface-call
    964     if 'EventConstructor' in interface.extended_attributes:
    965         return 1
    966     if not constructors:
    967         return 0
    968     return min(constructor['number_of_required_arguments']
    969                for constructor in constructors)
    970 
    971 
    972 ################################################################################
    973 # Special operations (methods)
    974 # http://heycam.github.io/webidl/#idl-special-operations
    975 ################################################################################
    976 
    977 def property_getter(getter, cpp_arguments):
    978     def is_null_expression(idl_type):
    979         if idl_type.is_union_type:
    980             notnull = ' || '.join([
    981                     member_argument['null_check_value']
    982                     for member_argument in idl_type.union_arguments])
    983             return '!(%s)' % notnull
    984         if idl_type.name == 'String':
    985             return 'result.isNull()'
    986         if idl_type.is_interface_type:
    987             return '!result'
    988         return ''
    989 
    990     idl_type = getter.idl_type
    991     extended_attributes = getter.extended_attributes
    992     is_raises_exception = 'RaisesException' in extended_attributes
    993 
    994     # FIXME: make more generic, so can use v8_methods.cpp_value
    995     cpp_method_name = 'impl->%s' % cpp_name(getter)
    996 
    997     if is_raises_exception:
    998         cpp_arguments.append('exceptionState')
    999     union_arguments = idl_type.union_arguments
   1000     if union_arguments:
   1001         cpp_arguments.extend([member_argument['cpp_value']
   1002                               for member_argument in union_arguments])
   1003 
   1004     cpp_value = '%s(%s)' % (cpp_method_name, ', '.join(cpp_arguments))
   1005 
   1006     return {
   1007         'cpp_type': idl_type.cpp_type,
   1008         'cpp_value': cpp_value,
   1009         'is_custom':
   1010             'Custom' in extended_attributes and
   1011             (not extended_attributes['Custom'] or
   1012              has_extended_attribute_value(getter, 'Custom', 'PropertyGetter')),
   1013         'is_custom_property_enumerator': has_extended_attribute_value(
   1014             getter, 'Custom', 'PropertyEnumerator'),
   1015         'is_custom_property_query': has_extended_attribute_value(
   1016             getter, 'Custom', 'PropertyQuery'),
   1017         'is_enumerable': 'NotEnumerable' not in extended_attributes,
   1018         'is_null_expression': is_null_expression(idl_type),
   1019         'is_raises_exception': is_raises_exception,
   1020         'name': cpp_name(getter),
   1021         'union_arguments': union_arguments,
   1022         'v8_set_return_value': idl_type.v8_set_return_value('result', extended_attributes=extended_attributes, script_wrappable='impl', release=idl_type.release),
   1023     }
   1024 
   1025 
   1026 def property_setter(setter):
   1027     idl_type = setter.arguments[1].idl_type
   1028     extended_attributes = setter.extended_attributes
   1029     is_raises_exception = 'RaisesException' in extended_attributes
   1030     return {
   1031         'has_type_checking_interface':
   1032             has_extended_attribute_value(setter, 'TypeChecking', 'Interface') and
   1033             idl_type.is_wrapper_type,
   1034         'idl_type': idl_type.base_type,
   1035         'is_custom': 'Custom' in extended_attributes,
   1036         'has_exception_state': (is_raises_exception or
   1037                                 idl_type.v8_conversion_needs_exception_state),
   1038         'is_raises_exception': is_raises_exception,
   1039         'name': cpp_name(setter),
   1040         'v8_value_to_local_cpp_value': idl_type.v8_value_to_local_cpp_value(
   1041             extended_attributes, 'v8Value', 'propertyValue'),
   1042     }
   1043 
   1044 
   1045 def property_deleter(deleter):
   1046     idl_type = deleter.idl_type
   1047     if str(idl_type) != 'boolean':
   1048         raise Exception(
   1049             'Only deleters with boolean type are allowed, but type is "%s"' %
   1050             idl_type)
   1051     extended_attributes = deleter.extended_attributes
   1052     return {
   1053         'is_custom': 'Custom' in extended_attributes,
   1054         'is_raises_exception': 'RaisesException' in extended_attributes,
   1055         'name': cpp_name(deleter),
   1056     }
   1057 
   1058 
   1059 ################################################################################
   1060 # Indexed properties
   1061 # http://heycam.github.io/webidl/#idl-indexed-properties
   1062 ################################################################################
   1063 
   1064 def indexed_property_getter(interface):
   1065     try:
   1066         # Find indexed property getter, if present; has form:
   1067         # getter TYPE [OPTIONAL_IDENTIFIER](unsigned long ARG1)
   1068         getter = next(
   1069             method
   1070             for method in interface.operations
   1071             if ('getter' in method.specials and
   1072                 len(method.arguments) == 1 and
   1073                 str(method.arguments[0].idl_type) == 'unsigned long'))
   1074     except StopIteration:
   1075         return None
   1076 
   1077     return property_getter(getter, ['index'])
   1078 
   1079 
   1080 def indexed_property_setter(interface):
   1081     try:
   1082         # Find indexed property setter, if present; has form:
   1083         # setter RETURN_TYPE [OPTIONAL_IDENTIFIER](unsigned long ARG1, ARG_TYPE ARG2)
   1084         setter = next(
   1085             method
   1086             for method in interface.operations
   1087             if ('setter' in method.specials and
   1088                 len(method.arguments) == 2 and
   1089                 str(method.arguments[0].idl_type) == 'unsigned long'))
   1090     except StopIteration:
   1091         return None
   1092 
   1093     return property_setter(setter)
   1094 
   1095 
   1096 def indexed_property_deleter(interface):
   1097     try:
   1098         # Find indexed property deleter, if present; has form:
   1099         # deleter TYPE [OPTIONAL_IDENTIFIER](unsigned long ARG)
   1100         deleter = next(
   1101             method
   1102             for method in interface.operations
   1103             if ('deleter' in method.specials and
   1104                 len(method.arguments) == 1 and
   1105                 str(method.arguments[0].idl_type) == 'unsigned long'))
   1106     except StopIteration:
   1107         return None
   1108 
   1109     return property_deleter(deleter)
   1110 
   1111 
   1112 ################################################################################
   1113 # Named properties
   1114 # http://heycam.github.io/webidl/#idl-named-properties
   1115 ################################################################################
   1116 
   1117 def named_property_getter(interface):
   1118     try:
   1119         # Find named property getter, if present; has form:
   1120         # getter TYPE [OPTIONAL_IDENTIFIER](DOMString ARG1)
   1121         getter = next(
   1122             method
   1123             for method in interface.operations
   1124             if ('getter' in method.specials and
   1125                 len(method.arguments) == 1 and
   1126                 str(method.arguments[0].idl_type) == 'DOMString'))
   1127     except StopIteration:
   1128         return None
   1129 
   1130     getter.name = getter.name or 'anonymousNamedGetter'
   1131     return property_getter(getter, ['propertyName'])
   1132 
   1133 
   1134 def named_property_setter(interface):
   1135     try:
   1136         # Find named property setter, if present; has form:
   1137         # setter RETURN_TYPE [OPTIONAL_IDENTIFIER](DOMString ARG1, ARG_TYPE ARG2)
   1138         setter = next(
   1139             method
   1140             for method in interface.operations
   1141             if ('setter' in method.specials and
   1142                 len(method.arguments) == 2 and
   1143                 str(method.arguments[0].idl_type) == 'DOMString'))
   1144     except StopIteration:
   1145         return None
   1146 
   1147     return property_setter(setter)
   1148 
   1149 
   1150 def named_property_deleter(interface):
   1151     try:
   1152         # Find named property deleter, if present; has form:
   1153         # deleter TYPE [OPTIONAL_IDENTIFIER](DOMString ARG)
   1154         deleter = next(
   1155             method
   1156             for method in interface.operations
   1157             if ('deleter' in method.specials and
   1158                 len(method.arguments) == 1 and
   1159                 str(method.arguments[0].idl_type) == 'DOMString'))
   1160     except StopIteration:
   1161         return None
   1162 
   1163     return property_deleter(deleter)
   1164