| # 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 |
| # met: |
| # |
| # * Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # * Redistributions in binary form must reproduce the above |
| # copyright notice, this list of conditions and the following disclaimer |
| # in the documentation and/or other materials provided with the |
| # distribution. |
| # * Neither the name of Google Inc. nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| # pylint: disable=relative-import |
| |
| """Generate template values for an interface. |
| |
| Design doc: http://www.chromium.org/developers/design-documents/idl-compiler |
| """ |
| from operator import or_ |
| |
| from idl_definitions import IdlOperation, IdlArgument |
| from idl_types import IdlType, inherits_interface |
| from overload_set_algorithm import effective_overload_set_by_length |
| from overload_set_algorithm import method_overloads_by_name |
| |
| import v8_attributes |
| from v8_globals import includes |
| import v8_methods |
| import v8_types |
| import v8_utilities |
| from v8_utilities import (cpp_name_or_partial, cpp_name, has_extended_attribute_value, |
| runtime_enabled_feature_name, is_legacy_interface_type_checking) |
| |
| |
| INTERFACE_H_INCLUDES = frozenset([ |
| 'bindings/core/v8/GeneratedCodeHelper.h', |
| 'bindings/core/v8/NativeValueTraits.h', |
| 'bindings/core/v8/ScriptWrappable.h', |
| 'bindings/core/v8/ToV8.h', |
| 'bindings/core/v8/V8Binding.h', |
| 'bindings/core/v8/V8DOMWrapper.h', |
| 'bindings/core/v8/WrapperTypeInfo.h', |
| 'platform/heap/Handle.h', |
| ]) |
| INTERFACE_CPP_INCLUDES = frozenset([ |
| 'bindings/core/v8/ExceptionState.h', |
| 'bindings/core/v8/V8DOMConfiguration.h', |
| 'bindings/core/v8/V8ObjectConstructor.h', |
| 'core/dom/Document.h', |
| 'wtf/GetPtr.h', |
| 'wtf/RefPtr.h', |
| ]) |
| |
| |
| def filter_has_constant_configuration(constants): |
| return [constant for constant in constants if |
| not constant['measure_as'] and |
| not constant['deprecate_as'] and |
| not constant['runtime_enabled_feature_name'] and |
| not constant['origin_trial_feature_name']] |
| |
| |
| def filter_has_special_getter(constants): |
| return [constant for constant in constants if |
| constant['measure_as'] or |
| constant['deprecate_as']] |
| |
| |
| def filter_runtime_enabled(constants): |
| return [constant for constant in constants if |
| constant['runtime_enabled_feature_name']] |
| |
| |
| def filter_origin_trial_enabled(constants): |
| return [constant for constant in constants if |
| constant['origin_trial_feature_name']] |
| |
| |
| def constant_filters(): |
| return {'has_constant_configuration': filter_has_constant_configuration, |
| 'has_special_getter': filter_has_special_getter, |
| 'runtime_enabled_constants': filter_runtime_enabled, |
| 'origin_trial_enabled_constants': filter_origin_trial_enabled} |
| |
| |
| def origin_trial_features(interface, constants, attributes, methods): |
| """ Returns a list of the origin trial features used in this interface. |
| |
| Each element is a dictionary with keys 'name' and 'needs_instance'. |
| 'needs_instance' is true if any member associated with the interface needs |
| to be installed on every instance of the interface. This list is the union |
| of the sets of features used for constants, attributes and methods. |
| """ |
| KEY = 'origin_trial_feature_name' # pylint: disable=invalid-name |
| |
| def member_filter(members): |
| return sorted([member for member in members if member.get(KEY) and not member.get('exposed_test')]) |
| |
| def member_filter_by_name(members, name): |
| return [member for member in members if member[KEY] == name] |
| |
| # Collect all members visible on this interface with a defined origin trial |
| origin_trial_constants = member_filter(constants) |
| origin_trial_attributes = member_filter(attributes) |
| origin_trial_methods = member_filter([method for method in methods |
| if v8_methods.method_is_visible(method, interface.is_partial) and |
| not v8_methods.conditionally_exposed(method) and |
| not v8_methods.custom_registration(method)]) |
| |
| feature_names = set([member[KEY] for member in origin_trial_constants + origin_trial_attributes + origin_trial_methods]) |
| |
| # Construct the list of dictionaries. 'needs_instance' will be true if any |
| # member for the feature has 'on_instance' defined as true. |
| features = [{'name': name, |
| 'constants': member_filter_by_name(origin_trial_constants, name), |
| 'attributes': member_filter_by_name(origin_trial_attributes, name), |
| 'methods': member_filter_by_name(origin_trial_methods, name)} |
| for name in feature_names] |
| for feature in features: |
| members = feature['constants'] + feature['attributes'] + feature['methods'] |
| feature['needs_instance'] = reduce(or_, (member.get('on_instance', False) for member in members)) |
| |
| if features: |
| includes.add('bindings/core/v8/ScriptState.h') |
| includes.add('core/origin_trials/OriginTrials.h') |
| return sorted(features) |
| |
| |
| def interface_context(interface, interfaces): |
| """Creates a Jinja template context for an interface. |
| |
| Args: |
| interface: An interface to create the context for |
| interfaces: A dict which maps an interface name to the definition |
| which can be referred if needed |
| |
| Returns: |
| A Jinja template context for |interface| |
| """ |
| |
| includes.clear() |
| includes.update(INTERFACE_CPP_INCLUDES) |
| header_includes = set(INTERFACE_H_INCLUDES) |
| |
| if interface.is_partial: |
| # A partial interface definition cannot specify that the interface |
| # inherits from another interface. Inheritance must be specified on |
| # the original interface definition. |
| parent_interface = None |
| is_event_target = False |
| # partial interface needs the definition of its original interface. |
| includes.add('bindings/core/v8/V8%s.h' % interface.name) |
| else: |
| parent_interface = interface.parent |
| if parent_interface: |
| header_includes.update(v8_types.includes_for_interface(parent_interface)) |
| is_event_target = inherits_interface(interface.name, 'EventTarget') |
| |
| extended_attributes = interface.extended_attributes |
| |
| is_array_buffer_or_view = interface.idl_type.is_array_buffer_or_view |
| is_typed_array_type = interface.idl_type.is_typed_array |
| if is_array_buffer_or_view: |
| includes.update(('bindings/core/v8/V8ArrayBuffer.h', |
| 'bindings/core/v8/V8SharedArrayBuffer.h')) |
| if interface.name == 'ArrayBufferView': |
| includes.update(( |
| 'bindings/core/v8/V8Int8Array.h', |
| 'bindings/core/v8/V8Int16Array.h', |
| 'bindings/core/v8/V8Int32Array.h', |
| 'bindings/core/v8/V8Uint8Array.h', |
| 'bindings/core/v8/V8Uint8ClampedArray.h', |
| 'bindings/core/v8/V8Uint16Array.h', |
| 'bindings/core/v8/V8Uint32Array.h', |
| 'bindings/core/v8/V8Float32Array.h', |
| 'bindings/core/v8/V8Float64Array.h', |
| 'bindings/core/v8/V8DataView.h')) |
| |
| # [ActiveScriptWrappable] |
| active_scriptwrappable = 'ActiveScriptWrappable' in extended_attributes |
| |
| # [CheckSecurity] |
| is_check_security = 'CheckSecurity' in extended_attributes |
| if is_check_security: |
| includes.add('bindings/core/v8/BindingSecurity.h') |
| includes.add('core/frame/LocalDOMWindow.h') |
| |
| # [DependentLifetime] |
| is_dependent_lifetime = 'DependentLifetime' in extended_attributes |
| |
| # [PrimaryGlobal] and [Global] |
| is_global = ('PrimaryGlobal' in extended_attributes or |
| 'Global' in extended_attributes) |
| |
| # [ImmutablePrototype] |
| # TODO(littledan): Is it possible to deduce this based on inheritance, |
| # as in the WebIDL spec? |
| is_immutable_prototype = is_global or 'ImmutablePrototype' in extended_attributes |
| |
| wrapper_class_id = ('NodeClassId' if inherits_interface(interface.name, 'Node') else 'ObjectClassId') |
| |
| # [ActiveScriptWrappable] must be accompanied with [DependentLifetime]. |
| if active_scriptwrappable and not is_dependent_lifetime: |
| raise Exception('[ActiveScriptWrappable] interface must also specify ' |
| '[DependentLifetime]: %s' % interface.name) |
| |
| v8_class_name = v8_utilities.v8_class_name(interface) |
| cpp_class_name = cpp_name(interface) |
| cpp_class_name_or_partial = cpp_name_or_partial(interface) |
| v8_class_name_or_partial = v8_utilities.v8_class_name_or_partial(interface) |
| |
| # TODO(peria): Generate the target list from 'Window' and 'HTMLDocument'. |
| needs_runtime_enabled_installer = v8_class_name in [ |
| 'V8Window', 'V8HTMLDocument', 'V8Document', 'V8Node', 'V8EventTarget'] |
| |
| context = { |
| 'cpp_class': cpp_class_name, |
| 'cpp_class_or_partial': cpp_class_name_or_partial, |
| 'is_gc_type': True, |
| # FIXME: Remove 'EventTarget' special handling, http://crbug.com/383699 |
| 'has_access_check_callbacks': (is_check_security and |
| interface.name != 'EventTarget'), |
| 'has_custom_legacy_call_as_function': has_extended_attribute_value(interface, 'Custom', 'LegacyCallAsFunction'), # [Custom=LegacyCallAsFunction] |
| 'has_partial_interface': len(interface.partial_interfaces) > 0, |
| 'header_includes': header_includes, |
| 'interface_name': interface.name, |
| 'is_array_buffer_or_view': is_array_buffer_or_view, |
| 'is_check_security': is_check_security, |
| 'is_event_target': is_event_target, |
| 'is_exception': interface.is_exception, |
| 'is_global': is_global, |
| 'is_immutable_prototype': is_immutable_prototype, |
| 'is_node': inherits_interface(interface.name, 'Node'), |
| 'is_partial': interface.is_partial, |
| 'is_typed_array_type': is_typed_array_type, |
| 'lifetime': 'Dependent' if is_dependent_lifetime else 'Independent', |
| 'measure_as': v8_utilities.measure_as(interface, None), # [MeasureAs] |
| 'needs_runtime_enabled_installer': needs_runtime_enabled_installer, |
| 'origin_trial_enabled_function': v8_utilities.origin_trial_enabled_function_name(interface), |
| 'parent_interface': parent_interface, |
| 'pass_cpp_type': cpp_name(interface) + '*', |
| 'active_scriptwrappable': active_scriptwrappable, |
| 'runtime_enabled_feature_name': runtime_enabled_feature_name(interface), # [RuntimeEnabled] |
| 'v8_class': v8_class_name, |
| 'v8_class_or_partial': v8_class_name_or_partial, |
| 'wrapper_class_id': wrapper_class_id, |
| } |
| |
| # Constructors |
| constructors = [constructor_context(interface, constructor) |
| for constructor in interface.constructors |
| # FIXME: shouldn't put named constructors with constructors |
| # (currently needed for Perl compatibility) |
| # Handle named constructors separately |
| if constructor.name == 'Constructor'] |
| if len(constructors) > 1: |
| context['constructor_overloads'] = overloads_context(interface, constructors) |
| |
| # [CustomConstructor] |
| custom_constructors = [{ # Only needed for computing interface length |
| 'number_of_required_arguments': |
| number_of_required_arguments(constructor), |
| } for constructor in interface.custom_constructors] |
| |
| # [HTMLConstructor] |
| has_html_constructor = 'HTMLConstructor' in extended_attributes |
| # https://html.spec.whatwg.org/multipage/dom.html#html-element-constructors |
| if has_html_constructor: |
| if ('Constructor' in extended_attributes) or ('NoInterfaceObject' in extended_attributes): |
| raise Exception('[Constructor] and [NoInterfaceObject] MUST NOT be' |
| ' specified with [HTMLConstructor]: ' |
| '%s' % interface.name) |
| includes.add('bindings/core/v8/V8HTMLConstructor.h') |
| |
| # [NamedConstructor] |
| named_constructor = named_constructor_context(interface) |
| |
| if constructors or custom_constructors or named_constructor: |
| if interface.is_partial: |
| raise Exception('[Constructor] and [NamedConstructor] MUST NOT be' |
| ' specified on partial interface definitions: ' |
| '%s' % interface.name) |
| if named_constructor: |
| includes.add('bindings/core/v8/V8PrivateProperty.h') |
| |
| includes.add('bindings/core/v8/V8ObjectConstructor.h') |
| includes.add('core/frame/LocalDOMWindow.h') |
| elif 'Measure' in extended_attributes or 'MeasureAs' in extended_attributes: |
| if not interface.is_partial: |
| raise Exception('[Measure] or [MeasureAs] specified for interface without a constructor: ' |
| '%s' % interface.name) |
| |
| # [Unscopable] attributes and methods |
| unscopables = [] |
| for attribute in interface.attributes: |
| if 'Unscopable' in attribute.extended_attributes: |
| unscopables.append((attribute.name, runtime_enabled_feature_name(attribute))) |
| for method in interface.operations: |
| if 'Unscopable' in method.extended_attributes: |
| unscopables.append((method.name, runtime_enabled_feature_name(method))) |
| |
| # [CEReactions] |
| setter_or_deleters = ( |
| interface.indexed_property_setter, |
| interface.indexed_property_deleter, |
| interface.named_property_setter, |
| interface.named_property_deleter, |
| ) |
| has_ce_reactions = any(setter_or_deleter and 'CEReactions' in setter_or_deleter.extended_attributes |
| for setter_or_deleter in setter_or_deleters) |
| if has_ce_reactions: |
| includes.add('core/dom/custom/CEReactionsScope.h') |
| |
| context.update({ |
| 'constructors': constructors, |
| 'has_custom_constructor': bool(custom_constructors), |
| 'has_html_constructor': has_html_constructor, |
| 'interface_length': |
| interface_length(constructors + custom_constructors), |
| 'is_constructor_raises_exception': extended_attributes.get('RaisesException') == 'Constructor', # [RaisesException=Constructor] |
| 'named_constructor': named_constructor, |
| 'unscopables': sorted(unscopables), |
| }) |
| |
| # Constants |
| context.update({ |
| 'constants': [constant_context(constant, interface) for constant in interface.constants], |
| 'do_not_check_constants': 'DoNotCheckConstants' in extended_attributes, |
| }) |
| |
| # Attributes |
| attributes = [v8_attributes.attribute_context(interface, attribute, interfaces) |
| for attribute in interface.attributes] |
| |
| has_conditional_attributes = any(attribute['exposed_test'] for attribute in attributes) |
| if has_conditional_attributes and interface.is_partial: |
| raise Exception('Conditional attributes between partial interfaces in modules and the original interfaces(%s) in core are not allowed.' % interface.name) |
| |
| context.update({ |
| 'attributes': attributes, |
| # Elements in attributes are broken in following members. |
| 'accessors': v8_attributes.filter_accessors(attributes), |
| 'data_attributes': v8_attributes.filter_data_attributes(attributes), |
| 'lazy_data_attributes': v8_attributes.filter_lazy_data_attributes(attributes), |
| 'origin_trial_attributes': v8_attributes.filter_origin_trial_enabled(attributes), |
| 'runtime_enabled_attributes': v8_attributes.filter_runtime_enabled(attributes), |
| }) |
| |
| # Methods |
| methods, iterator_method = methods_context(interface) |
| context.update({ |
| 'has_origin_safe_method_setter': any(method['is_cross_origin'] and not method['is_unforgeable'] |
| for method in methods), |
| 'iterator_method': iterator_method, |
| 'methods': methods, |
| }) |
| |
| # Window.idl in Blink has indexed properties, but the spec says Window |
| # interface doesn't have indexed properties, instead the WindowProxy exotic |
| # object has indexed properties. Thus, Window interface must not support |
| # iterators. |
| has_array_iterator = (not interface.is_partial and |
| interface.has_indexed_elements and |
| interface.name != 'Window') |
| context.update({ |
| 'has_array_iterator': has_array_iterator, |
| 'iterable': interface.iterable, |
| }) |
| |
| # Conditionally enabled members |
| has_conditional_attributes_on_prototype = any( # pylint: disable=invalid-name |
| (attribute['exposed_test'] or attribute['secure_context_test']) and attribute['on_prototype'] |
| for attribute in attributes) |
| context.update({ |
| 'has_conditional_attributes_on_prototype': |
| has_conditional_attributes_on_prototype, |
| }) |
| |
| prepare_prototype_and_interface_object_func = None # pylint: disable=invalid-name |
| if (unscopables or has_conditional_attributes_on_prototype or |
| v8_methods.filter_conditionally_exposed(methods, interface.is_partial)): |
| prepare_prototype_and_interface_object_func = '%s::preparePrototypeAndInterfaceObject' % v8_class_name_or_partial # pylint: disable=invalid-name |
| |
| context.update({ |
| 'prepare_prototype_and_interface_object_func': prepare_prototype_and_interface_object_func, |
| }) |
| |
| context.update({ |
| 'legacy_caller': legacy_caller(interface.legacy_caller, interface), |
| 'indexed_property_getter': property_getter(interface.indexed_property_getter, ['index']), |
| 'indexed_property_setter': property_setter(interface.indexed_property_setter, interface), |
| 'indexed_property_deleter': property_deleter(interface.indexed_property_deleter), |
| 'is_override_builtins': 'OverrideBuiltins' in extended_attributes, |
| 'named_property_getter': property_getter(interface.named_property_getter, ['name']), |
| 'named_property_setter': property_setter(interface.named_property_setter, interface), |
| 'named_property_deleter': property_deleter(interface.named_property_deleter), |
| }) |
| context.update({ |
| 'has_named_properties_object': is_global and context['named_property_getter'], |
| }) |
| |
| # Origin Trials |
| context.update({ |
| 'origin_trial_features': origin_trial_features(interface, context['constants'], context['attributes'], context['methods']), |
| }) |
| |
| # Cross-origin interceptors |
| has_cross_origin_named_getter = False |
| has_cross_origin_named_setter = False |
| has_cross_origin_indexed_getter = False |
| |
| for attribute in attributes: |
| if attribute['has_cross_origin_getter']: |
| has_cross_origin_named_getter = True |
| if attribute['has_cross_origin_setter']: |
| has_cross_origin_named_setter = True |
| |
| # Methods are exposed as getter attributes on the interface: e.g. |
| # window.location gets the location attribute on the Window interface. For |
| # the cross-origin case, this attribute getter is guaranteed to only return |
| # a Function object, which the actual call is dispatched against. |
| for method in methods: |
| if method['is_cross_origin']: |
| has_cross_origin_named_getter = True |
| |
| has_cross_origin_named_enumerator = has_cross_origin_named_getter or has_cross_origin_named_setter # pylint: disable=invalid-name |
| |
| if context['named_property_getter'] and context['named_property_getter']['is_cross_origin']: |
| has_cross_origin_named_getter = True |
| |
| if context['indexed_property_getter'] and context['indexed_property_getter']['is_cross_origin']: |
| has_cross_origin_indexed_getter = True |
| |
| context.update({ |
| 'has_cross_origin_named_getter': has_cross_origin_named_getter, |
| 'has_cross_origin_named_setter': has_cross_origin_named_setter, |
| 'has_cross_origin_named_enumerator': has_cross_origin_named_enumerator, |
| 'has_cross_origin_indexed_getter': has_cross_origin_indexed_getter, |
| }) |
| |
| return context |
| |
| |
| def methods_context(interface): |
| """Creates a list of Jinja template contexts for methods of an interface. |
| |
| Args: |
| interface: An interface to create contexts for |
| |
| Returns: |
| A list of method contexts, and an iterator context if available or None |
| """ |
| |
| methods = [] |
| |
| if interface.original_interface: |
| methods.extend([v8_methods.method_context(interface, operation, is_visible=False) |
| for operation in interface.original_interface.operations |
| if operation.name]) |
| methods.extend([v8_methods.method_context(interface, method) |
| for method in interface.operations |
| if method.name]) # Skip anonymous special operations (methods) |
| if interface.partial_interfaces: |
| assert len(interface.partial_interfaces) == len(set(interface.partial_interfaces)) |
| for partial_interface in interface.partial_interfaces: |
| methods.extend([v8_methods.method_context(interface, operation, is_visible=False) |
| for operation in partial_interface.operations |
| if operation.name]) |
| compute_method_overloads_context(interface, methods) |
| |
| def generated_method(return_type, name, arguments=None, extended_attributes=None, implemented_as=None): |
| operation = IdlOperation() |
| operation.idl_type = return_type |
| operation.name = name |
| if arguments: |
| operation.arguments = arguments |
| if extended_attributes: |
| operation.extended_attributes.update(extended_attributes) |
| if implemented_as is None: |
| implemented_as = name + 'ForBinding' |
| operation.extended_attributes['ImplementedAs'] = implemented_as |
| return v8_methods.method_context(interface, operation) |
| |
| def generated_argument(idl_type, name, is_optional=False, extended_attributes=None): |
| argument = IdlArgument() |
| argument.idl_type = idl_type |
| argument.name = name |
| argument.is_optional = is_optional |
| if extended_attributes: |
| argument.extended_attributes.update(extended_attributes) |
| return argument |
| |
| # [Iterable], iterable<>, maplike<> and setlike<> |
| iterator_method = None |
| |
| # FIXME: support Iterable in partial interfaces. However, we don't |
| # need to support iterator overloads between interface and |
| # partial interface definitions. |
| # http://heycam.github.io/webidl/#idl-overloading |
| if (not interface.is_partial and ( |
| interface.iterable or interface.maplike or interface.setlike or |
| interface.has_indexed_elements or |
| 'Iterable' in interface.extended_attributes)): |
| |
| used_extended_attributes = {} |
| |
| if interface.iterable: |
| used_extended_attributes.update(interface.iterable.extended_attributes) |
| elif interface.maplike: |
| used_extended_attributes.update(interface.maplike.extended_attributes) |
| elif interface.setlike: |
| used_extended_attributes.update(interface.setlike.extended_attributes) |
| |
| if 'RaisesException' in used_extended_attributes: |
| raise ValueError('[RaisesException] is implied for iterable<>/maplike<>/setlike<>') |
| if 'CallWith' in used_extended_attributes: |
| raise ValueError('[CallWith=ScriptState] is implied for iterable<>/maplike<>/setlike<>') |
| |
| used_extended_attributes.update({ |
| 'RaisesException': None, |
| 'CallWith': 'ScriptState', |
| }) |
| |
| forEach_extended_attributes = used_extended_attributes.copy() |
| forEach_extended_attributes.update({ |
| 'CallWith': ['ScriptState', 'ThisValue'], |
| }) |
| |
| def generated_iterator_method(name, implemented_as=None): |
| return generated_method( |
| return_type=IdlType('Iterator'), |
| name=name, |
| extended_attributes=used_extended_attributes, |
| implemented_as=implemented_as) |
| |
| if not interface.has_indexed_elements: |
| iterator_method = generated_iterator_method('iterator', implemented_as='iterator') |
| |
| if interface.iterable or interface.maplike or interface.setlike: |
| non_overridable_methods = [] |
| overridable_methods = [] |
| |
| is_value_iterator = interface.iterable and interface.iterable.key_type is None |
| |
| # For value iterators, the |entries|, |forEach|, |keys| and |values| are originally set |
| # to corresponding properties in %ArrayPrototype%. |
| if not is_value_iterator: |
| non_overridable_methods.extend([ |
| generated_iterator_method('keys'), |
| generated_iterator_method('values'), |
| generated_iterator_method('entries'), |
| |
| # void forEach(Function callback, [Default=Undefined] optional any thisArg) |
| generated_method(IdlType('void'), 'forEach', |
| arguments=[generated_argument(IdlType('Function'), 'callback'), |
| generated_argument(IdlType('any'), 'thisArg', |
| is_optional=True, |
| extended_attributes={'Default': 'Undefined'})], |
| extended_attributes=forEach_extended_attributes), |
| ]) |
| |
| if interface.maplike: |
| key_argument = generated_argument(interface.maplike.key_type, 'key') |
| value_argument = generated_argument(interface.maplike.value_type, 'value') |
| |
| non_overridable_methods.extend([ |
| generated_method(IdlType('boolean'), 'has', |
| arguments=[key_argument], |
| extended_attributes=used_extended_attributes), |
| generated_method(IdlType('any'), 'get', |
| arguments=[key_argument], |
| extended_attributes=used_extended_attributes), |
| ]) |
| |
| if not interface.maplike.is_read_only: |
| overridable_methods.extend([ |
| generated_method(IdlType('void'), 'clear', |
| extended_attributes=used_extended_attributes), |
| generated_method(IdlType('boolean'), 'delete', |
| arguments=[key_argument], |
| extended_attributes=used_extended_attributes), |
| generated_method(IdlType(interface.name), 'set', |
| arguments=[key_argument, value_argument], |
| extended_attributes=used_extended_attributes), |
| ]) |
| |
| if interface.setlike: |
| value_argument = generated_argument(interface.setlike.value_type, 'value') |
| |
| non_overridable_methods.extend([ |
| generated_method(IdlType('boolean'), 'has', |
| arguments=[value_argument], |
| extended_attributes=used_extended_attributes), |
| ]) |
| |
| if not interface.setlike.is_read_only: |
| overridable_methods.extend([ |
| generated_method(IdlType(interface.name), 'add', |
| arguments=[value_argument], |
| extended_attributes=used_extended_attributes), |
| generated_method(IdlType('void'), 'clear', |
| extended_attributes=used_extended_attributes), |
| generated_method(IdlType('boolean'), 'delete', |
| arguments=[value_argument], |
| extended_attributes=used_extended_attributes), |
| ]) |
| |
| methods_by_name = {} |
| for method in methods: |
| methods_by_name.setdefault(method['name'], []).append(method) |
| |
| for non_overridable_method in non_overridable_methods: |
| if non_overridable_method['name'] in methods_by_name: |
| raise ValueError( |
| 'An interface cannot define an operation called "%s()", it ' |
| 'comes from the iterable, maplike or setlike declaration ' |
| 'in the IDL.' % non_overridable_method['name']) |
| methods.append(non_overridable_method) |
| |
| for overridable_method in overridable_methods: |
| if overridable_method['name'] in methods_by_name: |
| # FIXME: Check that the existing method is compatible. |
| continue |
| methods.append(overridable_method) |
| |
| # FIXME: maplike<> and setlike<> should also imply the presence of a |
| # 'size' attribute. |
| |
| # Serializer |
| if interface.serializer: |
| serializer = interface.serializer |
| serializer_ext_attrs = serializer.extended_attributes.copy() |
| if serializer.operation: |
| return_type = serializer.operation.idl_type |
| implemented_as = serializer.operation.name |
| else: |
| return_type = IdlType('any') |
| implemented_as = None |
| if 'CallWith' not in serializer_ext_attrs: |
| serializer_ext_attrs['CallWith'] = 'ScriptState' |
| methods.append(generated_method( |
| return_type=return_type, |
| name='toJSON', |
| extended_attributes=serializer_ext_attrs, |
| implemented_as=implemented_as)) |
| |
| # Stringifier |
| if interface.stringifier: |
| stringifier = interface.stringifier |
| stringifier_ext_attrs = stringifier.extended_attributes.copy() |
| if stringifier.attribute: |
| implemented_as = stringifier.attribute.name |
| elif stringifier.operation: |
| implemented_as = stringifier.operation.name |
| else: |
| implemented_as = 'toString' |
| methods.append(generated_method( |
| return_type=IdlType('DOMString'), |
| name='toString', |
| extended_attributes=stringifier_ext_attrs, |
| implemented_as=implemented_as)) |
| |
| for method in methods: |
| # The value of the Function object’s “length” property is a Number |
| # determined as follows: |
| # 1. Let S be the effective overload set for regular operations (if the |
| # operation is a regular operation) or for static operations (if the |
| # operation is a static operation) with identifier id on interface I and |
| # with argument count 0. |
| # 2. Return the length of the shortest argument list of the entries in S. |
| # FIXME: This calculation doesn't take into account whether runtime |
| # enabled overloads are actually enabled, so length may be incorrect. |
| # E.g., [RuntimeEnabled=Foo] void f(); void f(long x); |
| # should have length 1 if Foo is not enabled, but length 0 if it is. |
| method['length'] = (method['overloads']['length'] if 'overloads' in method else |
| method['number_of_required_arguments']) |
| |
| return methods, iterator_method |
| |
| |
| def reflected_name(constant_name): |
| """Returns the name to use for the matching constant name in blink code. |
| |
| Given an all-uppercase 'CONSTANT_NAME', returns a camel-case |
| 'kConstantName'. |
| """ |
| # Check for SHOUTY_CASE constants |
| if constant_name.upper() != constant_name: |
| return constant_name |
| return 'k' + ''.join(part.title() for part in constant_name.split('_')) |
| |
| |
| # [DeprecateAs], [OriginTrialEnabled], [Reflect], [RuntimeEnabled] |
| def constant_context(constant, interface): |
| extended_attributes = constant.extended_attributes |
| |
| return { |
| 'cpp_class': extended_attributes.get('PartialInterfaceImplementedAs'), |
| 'deprecate_as': v8_utilities.deprecate_as(constant), # [DeprecateAs] |
| 'idl_type': constant.idl_type.name, |
| 'measure_as': v8_utilities.measure_as(constant, interface), # [MeasureAs] |
| 'name': constant.name, |
| 'origin_trial_enabled_function': v8_utilities.origin_trial_enabled_function_name(constant), # [OriginTrialEnabled] |
| 'origin_trial_feature_name': v8_utilities.origin_trial_feature_name(constant), # [OriginTrialEnabled] |
| # FIXME: use 'reflected_name' as correct 'name' |
| 'reflected_name': extended_attributes.get('Reflect', reflected_name(constant.name)), |
| 'runtime_enabled_feature_name': runtime_enabled_feature_name(constant), # [RuntimeEnabled] |
| 'value': constant.value, |
| } |
| |
| |
| ################################################################################ |
| # Overloads |
| ################################################################################ |
| |
| def compute_method_overloads_context(interface, methods): |
| # Regular methods |
| compute_method_overloads_context_by_type( |
| interface, [method for method in methods if not method['is_static']]) |
| # Static methods |
| compute_method_overloads_context_by_type( |
| interface, [method for method in methods if method['is_static']]) |
| |
| |
| def compute_method_overloads_context_by_type(interface, 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 |
| 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(interface, overloads) |
| overloads[-1]['overloads']['name'] = name |
| |
| |
| |
| |
| def overloads_context(interface, 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 |
| |
| # [OriginTrialEnabled] |
| # TODO(iclelland): Allow origin trials on method overloads |
| # (crbug.com/621641) |
| if any(method.get('origin_trial_feature_name') for method in overloads): |
| raise Exception('[OriginTrialEnabled] cannot be specified on ' |
| 'overloaded methods: %s.%s' % (interface.name, overloads[0]['name'])) |
| |
| 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>') |
| |
| runtime_determined_lengths = None |
| function_length = lengths[0] |
| runtime_determined_maxargs = None |
| maxarg = lengths[-1] |
| |
| # The special case handling below is not needed if all overloads are |
| # runtime enabled by the same feature. |
| if not common_value(overloads, 'runtime_enabled_feature_name'): |
| # Check if all overloads with the shortest acceptable arguments list are |
| # runtime enabled, in which case we need to have a runtime determined |
| # Function.length. |
| shortest_overloads = effective_overloads_by_length[0][1] |
| if (all(method.get('runtime_enabled_feature_name') |
| for method, _, _ in shortest_overloads)): |
| # Generate a list of (length, runtime_enabled_feature_names) tuples. |
| runtime_determined_lengths = [] |
| for length, effective_overloads in effective_overloads_by_length: |
| runtime_enabled_feature_names = set( |
| method['runtime_enabled_feature_name'] |
| for method, _, _ in effective_overloads |
| if method.get('runtime_enabled_feature_name')) |
| if not runtime_enabled_feature_names: |
| # This "length" is unconditionally enabled, so stop here. |
| runtime_determined_lengths.append((length, [None])) |
| break |
| runtime_determined_lengths.append( |
| (length, sorted(runtime_enabled_feature_names))) |
| function_length = ('%sV8Internal::%sMethodLength()' |
| % (cpp_name_or_partial(interface), name)) |
| |
| # Check if all overloads with the longest required arguments list are |
| # runtime enabled, in which case we need to have a runtime determined |
| # maximum distinguishing argument index. |
| longest_overloads = effective_overloads_by_length[-1][1] |
| if (not common_value(overloads, 'runtime_enabled_feature_name') and |
| all(method.get('runtime_enabled_feature_name') |
| for method, _, _ in longest_overloads)): |
| # Generate a list of (length, runtime_enabled_feature_name) tuples. |
| runtime_determined_maxargs = [] |
| for length, effective_overloads in reversed(effective_overloads_by_length): |
| runtime_enabled_feature_names = set( |
| method['runtime_enabled_feature_name'] |
| for method, _, _ in effective_overloads |
| if method.get('runtime_enabled_feature_name')) |
| if not runtime_enabled_feature_names: |
| # This "length" is unconditionally enabled, so stop here. |
| runtime_determined_maxargs.append((length, [None])) |
| break |
| runtime_determined_maxargs.append( |
| (length, sorted(runtime_enabled_feature_names))) |
| maxarg = ('%sV8Internal::%sMethodMaxArg()' |
| % (cpp_name_or_partial(interface), name)) |
| |
| # Check and fail if overloads disagree about whether the return type |
| # is a Promise or not. |
| promise_overload_count = sum(1 for method in overloads if method.get('returns_promise')) |
| if promise_overload_count not in (0, len(overloads)): |
| raise ValueError('Overloads of %s have conflicting Promise/non-Promise types' |
| % (name)) |
| |
| has_overload_visible = False |
| has_overload_not_visible = False |
| for overload in overloads: |
| if overload.get('visible', True): |
| # If there exists an overload which is visible, need to generate |
| # overload_resolution, i.e. overlods_visible should be True. |
| has_overload_visible = True |
| else: |
| has_overload_not_visible = True |
| |
| # If some overloads are not visible and others are visible, |
| # the method is overloaded between core and modules. |
| has_partial_overloads = has_overload_visible and has_overload_not_visible |
| |
| return { |
| 'deprecate_all_as': common_value(overloads, 'deprecate_as'), # [DeprecateAs] |
| 'exposed_test_all': common_value(overloads, 'exposed_test'), # [Exposed] |
| 'length': function_length, |
| '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': maxarg, |
| 'measure_all_as': common_value(overloads, 'measure_as'), # [MeasureAs] |
| 'returns_promise_all': promise_overload_count > 0, |
| 'runtime_determined_lengths': runtime_determined_lengths, |
| 'runtime_determined_maxargs': runtime_determined_maxargs, |
| 'runtime_enabled_all': common_value(overloads, 'runtime_enabled_feature_name'), # [RuntimeEnabled] |
| 'secure_context_test_all': common_value(overloads, 'secure_context_test'), # [SecureContext] |
| '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), |
| 'visible': has_overload_visible, |
| 'has_partial_overloads': has_partial_overloads, |
| } |
| |
| |
| |
| 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 |
| |
| def typename_without_nullable(idl_type): |
| if idl_type.is_nullable: |
| return idl_type.inner_type.name |
| return idl_type.name |
| |
| type_lists = [tuple(typename_without_nullable(idl_type) |
| 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. |
| # |
| # 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] |
| |
| |
| def resolution_tests_methods(effective_overloads): |
| """Yields resolution test and associated method, in resolution order, for effective overloads of a given length. |
| |
| 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 = 'info[%s]' % 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. |
| # |
| # 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. |
| |
| # 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 = '%s->IsUndefined()' % 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 = 'isUndefinedOrNull(%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): |
| if idl_type.is_array_buffer_or_view: |
| test = '{cpp_value}->Is{idl_type}()'.format(idl_type=idl_type.base_type, cpp_value=cpp_value) |
| else: |
| test = 'V8{idl_type}::hasInstance({cpp_value}, info.GetIsolate())'.format(idl_type=idl_type.base_type, cpp_value=cpp_value) |
| yield test, method |
| |
| # 13. Otherwise: if IsCallable(V) is true, and there is an entry in S that |
| # has one of the following types at position i of its type list, |
| # • a callback function type |
| # ... |
| # |
| # FIXME: |
| # We test for functions rather than callability, which isn't strictly the |
| # same thing. |
| try: |
| method = next(method for idl_type, method in idl_types_methods |
| if idl_type.is_custom_callback_function) |
| test = '%s->IsFunction()' % cpp_value |
| yield test, method |
| except StopIteration: |
| pass |
| |
| # 14. 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, |
| # • a sequence type |
| # ... |
| # |
| # 15. 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 dictionary |
| # |
| # FIXME: |
| # We don't strictly follow the algorithm here. The algorithm says "remove |
| # all other entries" if there is "one entry" matching, but we yield all |
| # entries to support following constructors: |
| # [constructor(sequence<DOMString> arg), constructor(Dictionary arg)] |
| # interface I { ... } |
| # (Need to check array types before objects because an array is an object) |
| for idl_type, method in idl_types_methods: |
| 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 |
| yield '%s->IsArray()' % cpp_value, method |
| for idl_type, method in idl_types_methods: |
| if idl_type.is_dictionary or idl_type.name == 'Dictionary' or \ |
| idl_type.is_callback_interface or idl_type.is_record_type: |
| # FIXME: should be '{1}->IsObject() && !{1}->IsRegExp()'.format(cpp_value) |
| # FIXME: the IsRegExp checks can be skipped if we've |
| # already generated tests for them. |
| yield '%s->IsObject()' % cpp_value, method |
| |
| # (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 = '%s->IsNumber()' % 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 |
| # • USVString |
| # • 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 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)) |
| |
| |
| ################################################################################ |
| # Constructors |
| ################################################################################ |
| |
| # [Constructor] |
| def constructor_context(interface, constructor): |
| # [RaisesException=Constructor] |
| is_constructor_raises_exception = \ |
| interface.extended_attributes.get('RaisesException') == 'Constructor' |
| |
| argument_contexts = [ |
| v8_methods.argument_context(interface, constructor, argument, index) |
| for index, argument in enumerate(constructor.arguments)] |
| |
| return { |
| 'arguments': argument_contexts, |
| 'cpp_type': cpp_name(interface) + '*', |
| 'cpp_value': v8_methods.cpp_value( |
| interface, constructor, len(constructor.arguments)), |
| 'has_exception_state': |
| is_constructor_raises_exception or |
| any(argument for argument in constructor.arguments |
| if argument.idl_type.name == 'SerializedScriptValue' or |
| argument.idl_type.v8_conversion_needs_exception_state), |
| 'has_optional_argument_without_default_value': |
| any(True for argument_context in argument_contexts |
| if argument_context['is_optional_without_default_value']), |
| 'is_call_with_document': |
| # [ConstructorCallWith=Document] |
| has_extended_attribute_value(interface, |
| 'ConstructorCallWith', 'Document'), |
| 'is_call_with_execution_context': |
| # [ConstructorCallWith=ExecutionContext] |
| has_extended_attribute_value(interface, |
| 'ConstructorCallWith', 'ExecutionContext'), |
| 'is_call_with_script_state': |
| # [ConstructorCallWith=ScriptState] |
| has_extended_attribute_value( |
| interface, 'ConstructorCallWith', 'ScriptState'), |
| 'is_constructor': True, |
| 'is_named_constructor': False, |
| 'is_raises_exception': is_constructor_raises_exception, |
| 'number_of_required_arguments': |
| number_of_required_arguments(constructor), |
| } |
| |
| |
| # [NamedConstructor] |
| def named_constructor_context(interface): |
| extended_attributes = interface.extended_attributes |
| if 'NamedConstructor' not in extended_attributes: |
| return None |
| # FIXME: parser should return named constructor separately; |
| # included in constructors (and only name stored in extended attribute) |
| # for Perl compatibility |
| idl_constructor = interface.constructors[-1] |
| assert idl_constructor.name == 'NamedConstructor' |
| context = constructor_context(interface, idl_constructor) |
| context.update({ |
| 'name': extended_attributes['NamedConstructor'], |
| 'is_named_constructor': True, |
| }) |
| return context |
| |
| |
| def number_of_required_arguments(constructor): |
| return len([argument for argument in constructor.arguments |
| if not argument.is_optional]) |
| |
| |
| def interface_length(constructors): |
| # Docs: http://heycam.github.io/webidl/#es-interface-call |
| if not constructors: |
| return 0 |
| return min(constructor['number_of_required_arguments'] |
| for constructor in constructors) |
| |
| |
| ################################################################################ |
| # Special operations (methods) |
| # http://heycam.github.io/webidl/#idl-special-operations |
| ################################################################################ |
| |
| def legacy_caller(caller, interface): |
| if not caller: |
| return None |
| |
| return v8_methods.method_context(interface, caller) |
| |
| def property_getter(getter, cpp_arguments): |
| if not getter: |
| return None |
| |
| def is_null_expression(idl_type): |
| if idl_type.use_output_parameter_for_result: |
| return 'result.isNull()' |
| if idl_type.is_string_type: |
| return 'result.isNull()' |
| if idl_type.is_interface_type: |
| return '!result' |
| if idl_type.base_type in ('any', 'object'): |
| return 'result.isEmpty()' |
| return '' |
| |
| extended_attributes = getter.extended_attributes |
| idl_type = getter.idl_type |
| idl_type.add_includes_for_type(extended_attributes) |
| is_call_with_script_state = v8_utilities.has_extended_attribute_value(getter, 'CallWith', 'ScriptState') |
| is_raises_exception = 'RaisesException' in extended_attributes |
| use_output_parameter_for_result = idl_type.use_output_parameter_for_result |
| |
| # FIXME: make more generic, so can use v8_methods.cpp_value |
| cpp_method_name = 'impl->%s' % cpp_name(getter) |
| |
| if is_call_with_script_state: |
| cpp_arguments.insert(0, 'scriptState') |
| if is_raises_exception: |
| cpp_arguments.append('exceptionState') |
| if use_output_parameter_for_result: |
| cpp_arguments.append('result') |
| |
| cpp_value = '%s(%s)' % (cpp_method_name, ', '.join(cpp_arguments)) |
| |
| return { |
| 'cpp_type': idl_type.cpp_type, |
| 'cpp_value': cpp_value, |
| 'is_call_with_script_state': is_call_with_script_state, |
| 'is_cross_origin': 'CrossOrigin' in extended_attributes, |
| 'is_custom': |
| 'Custom' in extended_attributes and |
| (not extended_attributes['Custom'] or |
| has_extended_attribute_value(getter, 'Custom', 'PropertyGetter')), |
| 'is_custom_property_enumerator': has_extended_attribute_value( |
| getter, 'Custom', 'PropertyEnumerator'), |
| 'is_custom_property_query': has_extended_attribute_value( |
| getter, 'Custom', 'PropertyQuery'), |
| 'is_enumerable': 'NotEnumerable' not in extended_attributes, |
| 'is_null_expression': is_null_expression(idl_type), |
| 'is_raises_exception': is_raises_exception, |
| 'name': cpp_name(getter), |
| 'use_output_parameter_for_result': use_output_parameter_for_result, |
| 'v8_set_return_value': idl_type.v8_set_return_value('result', extended_attributes=extended_attributes, script_wrappable='impl'), |
| } |
| |
| |
| def property_setter(setter, interface): |
| if not setter: |
| return None |
| |
| extended_attributes = setter.extended_attributes |
| idl_type = setter.arguments[1].idl_type |
| idl_type.add_includes_for_type(extended_attributes) |
| is_call_with_script_state = v8_utilities.has_extended_attribute_value(setter, 'CallWith', 'ScriptState') |
| is_raises_exception = 'RaisesException' in extended_attributes |
| is_ce_reactions = 'CEReactions' in extended_attributes |
| |
| # [LegacyInterfaceTypeChecking] |
| has_type_checking_interface = ( |
| not is_legacy_interface_type_checking(interface, setter) and |
| idl_type.is_wrapper_type) |
| |
| return { |
| 'has_exception_state': (is_raises_exception or |
| idl_type.v8_conversion_needs_exception_state), |
| 'has_type_checking_interface': has_type_checking_interface, |
| 'idl_type': idl_type.base_type, |
| 'is_call_with_script_state': is_call_with_script_state, |
| 'is_ce_reactions': is_ce_reactions, |
| 'is_custom': 'Custom' in extended_attributes, |
| 'is_nullable': idl_type.is_nullable, |
| 'is_raises_exception': is_raises_exception, |
| 'name': cpp_name(setter), |
| 'v8_value_to_local_cpp_value': idl_type.v8_value_to_local_cpp_value( |
| extended_attributes, 'v8Value', 'propertyValue'), |
| } |
| |
| |
| def property_deleter(deleter): |
| if not deleter: |
| return None |
| |
| extended_attributes = deleter.extended_attributes |
| is_call_with_script_state = v8_utilities.has_extended_attribute_value(deleter, 'CallWith', 'ScriptState') |
| is_ce_reactions = 'CEReactions' in extended_attributes |
| return { |
| 'is_call_with_script_state': is_call_with_script_state, |
| 'is_ce_reactions': is_ce_reactions, |
| 'is_custom': 'Custom' in extended_attributes, |
| 'is_raises_exception': 'RaisesException' in extended_attributes, |
| 'name': cpp_name(deleter), |
| } |