1# Copyright (C) 2013 Google Inc. All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions are
5# met:
6#
7#     * Redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer.
9#     * Redistributions in binary form must reproduce the above
10# copyright notice, this list of conditions and the following disclaimer
11# in the documentation and/or other materials provided with the
12# distribution.
13#     * Neither the name of Google Inc. nor the names of its
14# contributors may be used to endorse or promote products derived from
15# this software without specific prior written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29# pylint: disable=relative-import
30"""Generate template values for attributes.
31
32Extends IdlType with property |constructor_type_name|.
33
34Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
35"""
36
37import os
38import sys
39
40sys.path.append(
41    os.path.join(os.path.dirname(__file__), '..', '..', 'build', 'scripts'))
42from blinkbuild.name_style_converter import NameStyleConverter
43import idl_types
44from idl_types import inherits_interface
45from v8_globals import includes
46import v8_types
47import v8_utilities
48from v8_utilities import capitalize
49from v8_utilities import cpp_encoded_property_name
50from v8_utilities import cpp_name
51from v8_utilities import extended_attribute_value_as_list
52from v8_utilities import has_extended_attribute_value
53from v8_utilities import is_unforgeable
54from v8_utilities import scoped_name
55from v8_utilities import strip_suffix
56from v8_utilities import uncapitalize
57from blinkbuild.name_style_converter import NameStyleConverter
58
59
60def attribute_context(interface, attribute, interfaces, component_info):
61    """Creates a Jinja template context for an attribute of an interface.
62
63    Args:
64        interface: An interface which |attribute| belongs to
65        attribute: An attribute to create the context for
66        interfaces: A dict which maps an interface name to the definition
67            which can be referred if needed
68        component_info: A dict containing component wide information
69
70    Returns:
71        A Jinja template context for |attribute|
72    """
73
74    idl_type = attribute.idl_type
75    base_idl_type = idl_type.base_type
76    extended_attributes = attribute.extended_attributes
77
78    idl_type.add_includes_for_type(extended_attributes)
79    if idl_type.enum_values:
80        includes.add('core/inspector/console_message.h')
81        includes.add('platform/heap/heap.h')
82
83    # [CheckSecurity]
84    is_cross_origin = 'CrossOrigin' in extended_attributes
85    is_check_security_for_receiver = (has_extended_attribute_value(
86        interface, 'CheckSecurity', 'Receiver') and is_cross_origin)
87    is_check_security_for_return_value = (has_extended_attribute_value(
88        attribute, 'CheckSecurity', 'ReturnValue'))
89    if is_check_security_for_receiver or is_check_security_for_return_value:
90        includes.add('bindings/core/v8/binding_security.h')
91    if is_check_security_for_return_value:
92        includes.add('core/frame/web_feature.h')
93        includes.add('platform/instrumentation/use_counter.h')
94    # [CrossOrigin]
95    if has_extended_attribute_value(attribute, 'CrossOrigin', 'Setter'):
96        includes.add('platform/bindings/v8_cross_origin_callback_info.h')
97    # [Constructor]
98    # TODO(yukishiino): Constructors are much like methods although constructors
99    # are not methods.  Constructors must be data-type properties, and we can
100    # support them as a kind of methods.
101    constructor_type = idl_type.constructor_type_name if is_constructor_attribute(
102        attribute) else None
103    # [CEReactions]
104    is_ce_reactions = 'CEReactions' in extended_attributes
105    if is_ce_reactions:
106        includes.add('core/html/custom/ce_reactions_scope.h')
107    # [CustomElementCallbacks], [Reflect]
108    is_custom_element_callbacks = 'CustomElementCallbacks' in extended_attributes
109    is_reflect = 'Reflect' in extended_attributes
110    # [ReflectOnly]
111    reflect_only = extended_attribute_value_as_list(attribute, 'ReflectOnly')
112    if reflect_only:
113        reflect_only = map(
114            lambda v: cpp_content_attribute_value_name(interface, v),
115            reflect_only)
116    if is_custom_element_callbacks or is_reflect:
117        includes.add('core/html/custom/v0_custom_element_processing_stack.h')
118    # [PerWorldBindings]
119    if 'PerWorldBindings' in extended_attributes:
120        assert idl_type.is_wrapper_type or 'LogActivity' in \
121            extended_attributes, \
122            '[PerWorldBindings] should only be used with wrapper types: %s.%s' % \
123            (interface.name, attribute.name)
124    # [SaveSameObject]
125    is_save_same_object = ('SameObject' in attribute.extended_attributes and
126                           'SaveSameObject' in attribute.extended_attributes)
127
128    # [StringContext]
129    if idl_type.has_string_context:
130        includes.add('bindings/core/v8/generated_code_helper.h')
131
132    # [CachedAccessor]
133    is_cached_accessor = 'CachedAccessor' in extended_attributes
134
135    # [LegacyLenientSetter]
136    is_lenient_setter = 'LegacyLenientSetter' in extended_attributes
137
138    # [CachedAttribute]
139    cached_attribute_validation_method = extended_attributes.get(
140        'CachedAttribute')
141
142    keep_alive_for_gc = is_keep_alive_for_gc(interface, attribute)
143
144    does_generate_getter = (not has_custom_getter(attribute)
145                            and not constructor_type)
146    does_generate_setter = (
147        has_setter(interface, attribute)
148        and not (has_custom_setter(attribute) or is_lenient_setter))
149
150    use_private_property_in_getter = (does_generate_getter
151                                      and (cached_attribute_validation_method
152                                           or is_save_same_object
153                                           or keep_alive_for_gc))
154    use_private_property_in_setter = (does_generate_setter
155                                      and cached_attribute_validation_method)
156    private_property_is_shared_between_getter_and_setter = (
157        use_private_property_in_getter and use_private_property_in_setter)
158
159    does_use_private_property = (use_private_property_in_getter
160                                 or use_private_property_in_setter
161                                 or is_cached_accessor)
162    if does_use_private_property:
163        includes.add('platform/bindings/v8_private_property.h')
164
165    # [LogActivity]
166    if 'LogActivity' in extended_attributes:
167        includes.add('platform/bindings/v8_per_context_data.h')
168
169    # [DeprecateAs], [MeasureAs]
170    deprecate_as = v8_utilities.deprecate_as(attribute)
171    measure_as = v8_utilities.measure_as(attribute, interface)
172
173    # [HighEntropy]
174    high_entropy = v8_utilities.high_entropy(attribute)
175
176    is_lazy_data_attribute = \
177        (constructor_type and not (measure_as or deprecate_as)) or \
178        (str(idl_type) == 'Window' and attribute.name in ('frames', 'self', 'window'))
179
180    runtime_features = component_info['runtime_enabled_features']
181
182    internal_name = cpp_encoded_property_name(attribute)
183
184    cpp_type = idl_type.cpp_type
185    if idl_type.is_explicit_nullable:
186        cpp_type = v8_types.cpp_template_type('base::Optional', cpp_type)
187
188    context = {
189        # [ActivityLogging]
190        'activity_logging_world_list_for_getter':
191        v8_utilities.activity_logging_world_list(attribute, 'Getter'),
192        # [ActivityLogging]
193        'activity_logging_world_list_for_setter':
194        v8_utilities.activity_logging_world_list(attribute, 'Setter'),
195        # [ActivityLogging]
196        'activity_logging_world_check':
197        v8_utilities.activity_logging_world_check(attribute),
198        'cached_accessor_name':
199        'k%s%s' % (interface.name, attribute.name.capitalize()),
200        'cached_attribute_validation_method':
201        cached_attribute_validation_method,
202        'camel_case_name':
203        NameStyleConverter(internal_name).to_upper_camel_case(),
204        'constructor_type':
205        constructor_type,
206        'context_enabled_feature_name':
207        v8_utilities.context_enabled_feature_name(attribute),
208        'cpp_name': cpp_name(attribute),
209        'cpp_type': cpp_type,
210        'cpp_type_initializer': idl_type.cpp_type_initializer,
211        'deprecate_as': deprecate_as,
212        'does_generate_getter': does_generate_getter,
213        'does_generate_setter': does_generate_setter,
214        'enum_type': idl_type.enum_type,
215        'enum_values': idl_type.enum_values,
216        # [Exposed]
217        'exposed_test':
218        v8_utilities.exposed(attribute, interface),
219        'getter_has_no_side_effect':
220        has_extended_attribute_value(attribute, 'Affects', 'Nothing'),
221        'has_cross_origin_getter':
222            has_extended_attribute_value(attribute, 'CrossOrigin', None) or
223            has_extended_attribute_value(attribute, 'CrossOrigin', 'Getter'),
224        'has_cross_origin_setter':
225        has_extended_attribute_value(attribute, 'CrossOrigin', 'Setter'),
226        'has_custom_getter': has_custom_getter(attribute),
227        'has_custom_setter': has_custom_setter(attribute),
228        'has_promise_type': idl_type.name == 'Promise',
229        'has_setter': has_setter(interface, attribute),
230        'high_entropy': high_entropy,
231        'idl_type': str(idl_type),
232        'is_cached_accessor': is_cached_accessor,
233        'is_call_with_execution_context':
234        has_extended_attribute_value(attribute, 'CallWith', 'ExecutionContext'),
235        'is_call_with_script_state':
236        has_extended_attribute_value(attribute, 'CallWith', 'ScriptState'),
237        'is_ce_reactions': is_ce_reactions,
238        'is_check_security_for_receiver': is_check_security_for_receiver,
239        'is_check_security_for_return_value':
240        is_check_security_for_return_value,
241        'is_custom_element_callbacks': is_custom_element_callbacks,
242        # TODO(yukishiino): Make all DOM attributes accessor-type properties.
243        'is_data_type_property': is_data_type_property(interface, attribute),
244        'is_getter_raises_exception':  # [RaisesException]
245            'RaisesException' in extended_attributes and
246            extended_attributes['RaisesException'] in (None, 'Getter'),
247        'is_keep_alive_for_gc': keep_alive_for_gc,
248        'is_lazy_data_attribute': is_lazy_data_attribute,
249        'is_lenient_setter': is_lenient_setter,
250        'is_lenient_this': 'LegacyLenientThis' in extended_attributes,
251        'is_nullable': idl_type.is_nullable,
252        'is_explicit_nullable': idl_type.is_explicit_nullable,
253        'is_named_constructor': is_named_constructor_attribute(attribute),
254        'is_partial_interface_member':
255            'PartialInterfaceImplementedAs' in extended_attributes,
256        'is_per_world_bindings': 'PerWorldBindings' in extended_attributes,
257        'is_put_forwards': 'PutForwards' in extended_attributes,
258        'is_read_only': attribute.is_read_only,
259        'is_reflect': is_reflect,
260        'is_replaceable': 'Replaceable' in attribute.extended_attributes,
261        'is_save_same_object': is_save_same_object,
262        'is_static': attribute.is_static,
263        'is_url': 'URL' in extended_attributes,
264        'is_unforgeable': is_unforgeable(attribute),
265        'measure_as': measure_as,
266        'name': attribute.name,
267        'on_instance': v8_utilities.on_instance(interface, attribute),
268        'on_interface': v8_utilities.on_interface(interface, attribute),
269        'on_prototype': v8_utilities.on_prototype(interface, attribute),
270        # [RuntimeEnabled] for origin trial
271        'origin_trial_feature_name':
272            v8_utilities.origin_trial_feature_name(attribute, runtime_features),
273        'private_property_is_shared_between_getter_and_setter':
274        private_property_is_shared_between_getter_and_setter,
275        'property_attributes': property_attributes(interface, attribute),
276        'reflect_empty': cpp_content_attribute_value_name(
277            interface, extended_attributes.get('ReflectEmpty')),
278        'reflect_invalid': cpp_content_attribute_value_name(
279            interface, extended_attributes.get('ReflectInvalid', '')),
280        'reflect_missing': cpp_content_attribute_value_name(
281            interface, extended_attributes.get('ReflectMissing')),
282        'reflect_only': reflect_only,
283        # [RuntimeEnabled] if not in origin trial
284        'runtime_enabled_feature_name':
285        v8_utilities.runtime_enabled_feature_name(attribute, runtime_features),
286        # [SecureContext]
287        'secure_context_test': v8_utilities.secure_context(attribute, interface),
288        'use_output_parameter_for_result': idl_type.use_output_parameter_for_result,
289        'world_suffixes': (
290            ['', 'ForMainWorld']
291            if 'PerWorldBindings' in extended_attributes
292            else ['']),  # [PerWorldBindings]
293    }
294
295    if not has_custom_getter(attribute):
296        getter_context(interface, attribute, context)
297    if not has_custom_setter(attribute) and has_setter(interface, attribute):
298        setter_context(interface, attribute, interfaces, context)
299
300    # [RuntimeCallStatsCounter]
301    runtime_call_stats_context(interface, attribute, context)
302
303    # [CrossOrigin] is incompatible with a number of other attributes, so check
304    # for them here.
305    if is_cross_origin:
306        if context['has_cross_origin_setter'] and context['has_custom_setter']:
307            raise Exception(
308                '[CrossOrigin] and [Custom] are incompatible on the same setter: %s.%s',
309                interface.name, attribute.name)
310        if context['is_per_world_bindings']:
311            raise Exception(
312                '[CrossOrigin] and [PerWorldBindings] are incompatible: %s.%s',
313                interface.name, attribute.name)
314        if context['constructor_type']:
315            raise Exception(
316                '[CrossOrigin] cannot be used for constructors: %s.%s',
317                interface.name, attribute.name)
318
319    return context
320
321
322def runtime_call_stats_context(interface, attribute, context):
323    includes.add('platform/bindings/runtime_call_stats.h')
324    generic_counter_name = (
325        'Blink_' + v8_utilities.cpp_name(interface) + '_' + attribute.name)
326    (counter, extended_attribute_defined) = v8_utilities.rcs_counter_name(
327        attribute, generic_counter_name)
328    runtime_call_stats = {
329        'extended_attribute_defined':
330        extended_attribute_defined,
331        'getter_counter':
332        '%s_Getter' % counter,
333        'setter_counter':
334        '%s_Setter' % counter,
335        'constructor_getter_callback_counter':
336        '%s_ConstructorGetterCallback' % generic_counter_name,
337    }
338    context.update({'runtime_call_stats': runtime_call_stats})
339
340
341def is_origin_trial_enabled(attribute):
342    return bool(attribute['origin_trial_feature_name'])
343
344
345def is_secure_context(attribute):
346    return bool(attribute['secure_context_test'])
347
348
349def filter_accessors(attributes):
350    return [
351        attribute for attribute in attributes
352        if not (attribute['exposed_test'] or is_secure_context(attribute)
353                or attribute['context_enabled_feature_name']
354                or is_origin_trial_enabled(attribute)
355                or attribute['runtime_enabled_feature_name'])
356        and not attribute['is_data_type_property']
357    ]
358
359
360def is_data_attribute(attribute):
361    return (not (attribute['exposed_test'] or is_secure_context(attribute)
362                 or attribute['context_enabled_feature_name']
363                 or is_origin_trial_enabled(attribute)
364                 or attribute['runtime_enabled_feature_name'])
365            and attribute['is_data_type_property'])
366
367
368def filter_data_attributes(attributes):
369    return [
370        attribute for attribute in attributes if is_data_attribute(attribute)
371    ]
372
373
374def filter_runtime_enabled(attributes):
375    return [
376        attribute for attribute in attributes
377        if not (attribute['exposed_test'] or is_secure_context(attribute))
378        and attribute['runtime_enabled_feature_name']
379    ]
380
381
382def filter_conditionally_enabled(attributes):
383    return [
384        attribute for attribute in attributes
385        if attribute['exposed_test'] or (is_secure_context(
386            attribute) and not is_origin_trial_enabled(attribute))
387    ]
388
389
390################################################################################
391# Getter
392################################################################################
393
394
395def getter_context(interface, attribute, context):
396    idl_type = attribute.idl_type
397    base_idl_type = idl_type.base_type
398    extended_attributes = attribute.extended_attributes
399
400    cpp_value = getter_expression(interface, attribute, context)
401    # Normally we can inline the function call into the return statement to
402    # avoid the overhead of using a Ref<> temporary, but for some cases
403    # (nullable types, EventHandler, [CachedAttribute], or if there are
404    # exceptions), we need to use a local variable.
405    # FIXME: check if compilers are smart enough to inline this, and if so,
406    # always use a local variable (for readability and CG simplicity).
407    if (idl_type.is_explicit_nullable or base_idl_type == 'EventHandler'
408            or 'CachedAttribute' in extended_attributes
409            or 'ReflectOnly' in extended_attributes
410            or context['is_keep_alive_for_gc']
411            or context['is_getter_raises_exception']
412            or context['high_entropy'] == 'Direct'):
413        context['cpp_value_original'] = cpp_value
414        cpp_value = 'cpp_value'
415
416    def v8_set_return_value_statement(for_main_world=False):
417        if (context['is_keep_alive_for_gc']
418                or 'CachedAttribute' in extended_attributes):
419            return 'V8SetReturnValue(info, v8_value)'
420        if idl_type.is_explicit_nullable:
421            cpp_return_value = 'cpp_value.value()'
422            if idl_type.is_frozen_array:
423                cpp_return_value = 'FreezeV8Object(ToV8(cpp_value.value(), info.Holder(), info.GetIsolate()), info.GetIsolate())'
424            return 'V8SetReturnValue(info, {})'.format(cpp_return_value)
425        return idl_type.v8_set_return_value(
426            cpp_value,
427            extended_attributes=extended_attributes,
428            script_wrappable='impl',
429            for_main_world=for_main_world,
430            is_static=attribute.is_static)
431
432    cpp_value_to_script_wrappable = cpp_value
433    if idl_type.is_array_buffer_view_or_typed_array:
434        cpp_value_to_script_wrappable += '.View()'
435
436    context.update({
437        'cpp_value':
438        cpp_value,
439        'cpp_value_to_script_wrappable':
440        cpp_value_to_script_wrappable,
441        'cpp_value_to_v8_value':
442        idl_type.cpp_value_to_v8_value(
443            cpp_value=cpp_value,
444            creation_context='holder',
445            extended_attributes=extended_attributes),
446        'is_getter_call_with_script_state':
447        has_extended_attribute_value(attribute, 'GetterCallWith',
448                                     'ScriptState'),
449        'v8_set_return_value_for_main_world':
450        v8_set_return_value_statement(for_main_world=True),
451        'v8_set_return_value':
452        v8_set_return_value_statement(),
453    })
454
455
456def getter_expression(interface, attribute, context):
457    extra_arguments = []
458    this_getter_base_name = getter_base_name(interface, attribute,
459                                             extra_arguments)
460    getter_name = scoped_name(interface, attribute, this_getter_base_name)
461
462    arguments = v8_utilities.call_with_arguments(
463        attribute.extended_attributes.get('GetterCallWith')
464        or attribute.extended_attributes.get('CallWith'))
465    # Members of IDL partial interface definitions are implemented in C++ as
466    # static member functions, which for instance members (non-static members)
467    # take *impl as their first argument
468    if ('PartialInterfaceImplementedAs' in attribute.extended_attributes
469            and not attribute.is_static):
470        arguments.append('*impl')
471    arguments.extend(extra_arguments)
472    if context['is_getter_raises_exception']:
473        arguments.append('exception_state')
474    if attribute.idl_type.use_output_parameter_for_result:
475        arguments.append('result')
476
477    expression = '%s(%s)' % (getter_name, ', '.join(arguments))
478    # Needed to handle getter expressions returning Type& as the
479    # use site for |expression| expects Type*.
480    if (attribute.idl_type.is_interface_type and len(arguments) == 0
481            and not attribute.idl_type.is_array_buffer_view_or_typed_array):
482        return 'WTF::GetPtr(%s)' % expression
483    return expression
484
485
486CONTENT_ATTRIBUTE_GETTER_NAMES = {
487    'boolean': 'FastHasAttribute',
488    'long': 'GetIntegralAttribute',
489    'unsigned long': 'GetUnsignedIntegralAttribute',
490    'Element': 'GetElementAttribute',
491}
492
493
494def getter_base_name(interface, attribute, arguments):
495    extended_attributes = attribute.extended_attributes
496
497    if 'Reflect' not in extended_attributes:
498        name = cpp_name(attribute)
499        return name if 'ImplementedAs' in extended_attributes \
500            else uncapitalize(name)
501
502    content_attribute_name = (extended_attributes['Reflect']
503                              or attribute.name.lower())
504    if content_attribute_name in ['class', 'id', 'name']:
505        # Special-case for performance optimization.
506        return 'Get%sAttribute' % content_attribute_name.capitalize()
507
508    arguments.append(scoped_content_attribute_name(interface, attribute))
509
510    base_idl_type = attribute.idl_type.base_type
511    if base_idl_type in CONTENT_ATTRIBUTE_GETTER_NAMES:
512        return CONTENT_ATTRIBUTE_GETTER_NAMES[base_idl_type]
513    if 'URL' in attribute.extended_attributes:
514        return 'GetURLAttribute'
515    idl_type = attribute.idl_type
516    if idl_type.is_frozen_array:
517        return 'Get%sArrayAttribute' % idl_type.element_type
518    return 'FastGetAttribute'
519
520
521def is_keep_alive_for_gc(interface, attribute):
522    idl_type = attribute.idl_type
523    base_idl_type = idl_type.base_type
524    extended_attributes = attribute.extended_attributes
525    if attribute.is_static:
526        return False
527    if idl_type.is_array_buffer_or_view:
528        return False
529    return (
530        # For readonly attributes, for performance reasons we keep the attribute
531        # wrapper alive while the owner wrapper is alive, because the attribute
532        # never changes.
533        (
534            attribute.is_read_only and idl_type.is_wrapper_type and
535            # There are some exceptions, however:
536            not (
537                # Node lifetime is managed by object grouping.
538                inherits_interface(interface.name, 'Node')
539                or inherits_interface(base_idl_type, 'Node') or
540                # A self-reference is unnecessary.
541                attribute.name == 'self' or
542                # FIXME: Remove these hard-coded hacks.
543                base_idl_type in ['EventTarget', 'Window']
544                or base_idl_type.startswith(('HTML', 'SVG')))))
545
546
547################################################################################
548# Setter
549################################################################################
550
551
552def setter_context(interface, attribute, interfaces, context):
553    if 'PutForwards' in attribute.extended_attributes:
554        # Make sure the target interface and attribute exist.
555        target_interface_name = attribute.idl_type.base_type
556        target_attribute_name = attribute.extended_attributes['PutForwards']
557        interface = interfaces[target_interface_name]
558        try:
559            next(candidate for candidate in interface.attributes
560                 if candidate.name == target_attribute_name)
561        except StopIteration:
562            raise Exception('[PutForward] target not found:\n'
563                            'Attribute "%s" is not present in interface "%s"' %
564                            (target_attribute_name, target_interface_name))
565        context['target_attribute_name'] = target_attribute_name
566        return
567
568    if ('Replaceable' in attribute.extended_attributes):
569        # Create the property, and early-return if an exception is thrown.
570        # Subsequent cleanup code may not be prepared to handle a pending
571        # exception.
572        context['cpp_setter'] = (
573            'if (info.Holder()->CreateDataProperty(' +
574            'info.GetIsolate()->GetCurrentContext(), ' +
575            'property_name, v8_value).IsNothing())' + '\n  return')
576        return
577
578    extended_attributes = attribute.extended_attributes
579    idl_type = attribute.idl_type
580
581    # [RaisesException], [RaisesException=Setter]
582    is_setter_raises_exception = (
583        'RaisesException' in extended_attributes
584        and extended_attributes['RaisesException'] in [None, 'Setter'])
585
586    has_type_checking_interface = idl_type.is_wrapper_type
587
588    use_common_reflection_setter = False
589    # Enable use_common_reflection_setter if
590    #  * extended_attributes is [CEReactions, Reflect] or
591    #    [CEReactions, Reflect, RuntimeEnabled],
592    #  * the type is boolean, DOMString, or DOMString?, and
593    #  * the interface inherits from 'Element'.
594    if ('Reflect' in extended_attributes
595            and 'CEReactions' in extended_attributes
596            and str(idl_type) in ('boolean', 'DOMString', 'DOMString?')
597            and inherits_interface(interface.name, 'Element')):
598        if (len(extended_attributes) == 2
599                or (len(extended_attributes) == 3
600                    and 'RuntimeEnabled' in extended_attributes)):
601            use_common_reflection_setter = True
602
603    context.update({
604        'has_setter_exception_state':
605        is_setter_raises_exception or has_type_checking_interface
606        or idl_type.v8_conversion_needs_exception_state,
607        'has_type_checking_interface':
608        has_type_checking_interface,
609        'is_setter_call_with_execution_context':
610        has_extended_attribute_value(attribute, 'SetterCallWith',
611                                     'ExecutionContext'),
612        'is_setter_call_with_script_state':
613        has_extended_attribute_value(attribute, 'SetterCallWith',
614                                     'ScriptState'),
615        'is_setter_raises_exception':
616        is_setter_raises_exception,
617        'use_common_reflection_setter':
618        use_common_reflection_setter,
619        'v8_value_to_local_cpp_value':
620        idl_type.v8_value_to_local_cpp_value(
621            extended_attributes,
622            'v8_value',
623            'cpp_value',
624            code_generation_target='attribute_set'),
625    })
626
627    # setter_expression() depends on context values we set above.
628    context['cpp_setter'] = setter_expression(interface, attribute, context)
629
630
631def setter_expression(interface, attribute, context):
632    extended_attributes = attribute.extended_attributes
633    arguments = v8_utilities.call_with_arguments(
634        extended_attributes.get('SetterCallWith')
635        or extended_attributes.get('CallWith'))
636
637    extra_arguments = []
638    this_setter_base_name = setter_base_name(interface, attribute,
639                                             extra_arguments)
640    setter_name = scoped_name(interface, attribute, this_setter_base_name)
641
642    # Members of IDL partial interface definitions are implemented in C++ as
643    # static member functions, which for instance members (non-static members)
644    # take *impl as their first argument
645    if ('PartialInterfaceImplementedAs' in extended_attributes
646            and not attribute.is_static):
647        arguments.append('*impl')
648    arguments.extend(extra_arguments)
649    idl_type = attribute.idl_type
650    if idl_type.base_type in ('EventHandler', 'OnBeforeUnloadEventHandler',
651                              'OnErrorEventHandler'):
652        if idl_type.base_type == 'EventHandler':
653            handler_type = 'kEventHandler'
654        elif idl_type.base_type == 'OnBeforeUnloadEventHandler':
655            handler_type = 'kOnBeforeUnloadEventHandler'
656        elif idl_type.base_type == 'OnErrorEventHandler':
657            handler_type = 'kOnErrorEventHandler'
658        arguments.append('JSEventHandler::CreateOrNull(' + 'v8_value, ' +
659                         'JSEventHandler::HandlerType::' + handler_type + ')')
660    else:
661        arguments.append('cpp_value')
662    if context['is_setter_raises_exception']:
663        arguments.append('exception_state')
664    if context['use_common_reflection_setter']:
665        attr_name = scoped_content_attribute_name(interface, attribute)
666        if idl_type.base_type == 'boolean':
667            setter_name = 'V8SetReflectedBooleanAttribute'
668            arguments = [
669                'info',
670                '"%s"' % interface.name,
671                '"%s"' % attribute.name, attr_name
672            ]
673        elif idl_type.base_type == 'DOMString':
674            if idl_type.is_nullable:
675                setter_name = 'V8SetReflectedNullableDOMStringAttribute'
676            else:
677                setter_name = 'V8SetReflectedDOMStringAttribute'
678            arguments = ['info', attr_name]
679
680    return '%s(%s)' % (setter_name, ', '.join(arguments))
681
682
683CONTENT_ATTRIBUTE_SETTER_NAMES = {
684    'boolean': 'SetBooleanAttribute',
685    'long': 'SetIntegralAttribute',
686    'unsigned long': 'SetUnsignedIntegralAttribute',
687    'Element': 'SetElementAttribute',
688}
689
690
691def setter_base_name(interface, attribute, arguments):
692    if 'Reflect' not in attribute.extended_attributes:
693        return 'set%s' % capitalize(cpp_name(attribute))
694    arguments.append(scoped_content_attribute_name(interface, attribute))
695
696    base_idl_type = attribute.idl_type.base_type
697    if base_idl_type in CONTENT_ATTRIBUTE_SETTER_NAMES:
698        return CONTENT_ATTRIBUTE_SETTER_NAMES[base_idl_type]
699    idl_type = attribute.idl_type
700    if idl_type.is_frozen_array:
701        return 'Set%sArrayAttribute' % idl_type.element_type
702    return 'setAttribute'
703
704
705def scoped_content_attribute_name(interface, attribute):
706    content_attribute_name = (attribute.extended_attributes['Reflect']
707                              or attribute.name.lower())
708    symbol_name = 'k' + NameStyleConverter(
709        content_attribute_name).to_upper_camel_case()
710    if interface.name.startswith('SVG'):
711        namespace = 'svg_names'
712        includes.add('core/svg_names.h')
713    else:
714        namespace = 'html_names'
715        includes.add('core/html_names.h')
716    return '%s::%sAttr' % (namespace, symbol_name)
717
718
719def cpp_content_attribute_value_name(interface, value):
720    if value == '':
721        return 'g_empty_atom'
722    if not value:
723        return value
724    includes.add('core/keywords.h')
725    return 'keywords::' + NameStyleConverter(value).to_enum_value()
726
727
728################################################################################
729# Attribute configuration
730################################################################################
731
732
733# Property descriptor's {writable: boolean}
734def is_writable(attribute):
735    return (not attribute.is_read_only or any(
736        keyword in attribute.extended_attributes
737        for keyword in ['PutForwards', 'Replaceable', 'LegacyLenientSetter']))
738
739
740def is_data_type_property(interface, attribute):
741    return (is_constructor_attribute(attribute)
742            or 'CrossOrigin' in attribute.extended_attributes)
743
744
745# [PutForwards], [Replaceable], [LegacyLenientSetter]
746def has_setter(interface, attribute):
747    if (is_data_type_property(interface, attribute)
748            and (is_constructor_attribute(attribute)
749                 or 'Replaceable' in attribute.extended_attributes)):
750        return False
751
752    return is_writable(attribute)
753
754
755# [NotEnumerable], [LegacyUnforgeable]
756def property_attributes(interface, attribute):
757    extended_attributes = attribute.extended_attributes
758    property_attributes_list = []
759    if ('NotEnumerable' in extended_attributes
760            or is_constructor_attribute(attribute)):
761        property_attributes_list.append('v8::DontEnum')
762    if is_unforgeable(attribute):
763        property_attributes_list.append('v8::DontDelete')
764    if not is_writable(attribute):
765        property_attributes_list.append('v8::ReadOnly')
766    return property_attributes_list or ['v8::None']
767
768
769# [Custom], [Custom=Getter]
770def has_custom_getter(attribute):
771    extended_attributes = attribute.extended_attributes
772    return ('Custom' in extended_attributes
773            and extended_attributes['Custom'] in [None, 'Getter'])
774
775
776# [Custom], [Custom=Setter]
777def has_custom_setter(attribute):
778    extended_attributes = attribute.extended_attributes
779    return (not attribute.is_read_only and 'Custom' in extended_attributes
780            and extended_attributes['Custom'] in [None, 'Setter'])
781
782
783################################################################################
784# Constructors
785################################################################################
786
787idl_types.IdlType.constructor_type_name = property(
788    lambda self: strip_suffix(self.base_type, 'Constructor'))
789
790
791def is_constructor_attribute(attribute):
792    return attribute.idl_type.name.endswith('Constructor')
793
794
795def is_named_constructor_attribute(attribute):
796    return attribute.idl_type.name.endswith('ConstructorConstructor')
797