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"""Generate template values for methods.
29
30Extends IdlArgument with property |default_cpp_value|.
31Extends IdlTypeBase and IdlUnionType with property |union_arguments|.
32
33Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
34"""
35
36import os
37import sys
38
39sys.path.append(
40    os.path.join(os.path.dirname(__file__), '..', '..', 'build', 'scripts'))
41from blinkbuild.name_style_converter import NameStyleConverter
42from idl_definitions import IdlArgument, IdlOperation
43from idl_types import IdlTypeBase, IdlUnionType, inherits_interface
44from v8_globals import includes
45import v8_types
46import v8_utilities
47from v8_utilities import (has_extended_attribute_value, is_unforgeable)
48
49
50def method_is_visible(method, interface_is_partial):
51    if 'overloads' in method:
52        return method['overloads']['visible'] and not (
53            method['overloads']['has_partial_overloads']
54            and interface_is_partial)
55    return method['visible'] and 'overload_index' not in method
56
57
58def is_origin_trial_enabled(method):
59    return bool(method['origin_trial_feature_name'])
60
61
62def is_secure_context(method):
63    return bool(method['overloads']['secure_context_test_all'] if 'overloads'
64                in method else method['secure_context_test'])
65
66
67def is_conditionally_enabled(method):
68    exposed = method['overloads']['exposed_test_all'] \
69        if 'overloads' in method else method['exposed_test']
70    return exposed or is_secure_context(method)
71
72
73def filter_conditionally_enabled(methods, interface_is_partial):
74    return [
75        method for method in methods
76        if (method_is_visible(method, interface_is_partial)
77            and is_conditionally_enabled(method)
78            and not is_origin_trial_enabled(method))
79    ]
80
81
82def custom_registration(method):
83    # TODO(dcheng): Currently, bindings must create a function object for each
84    # realm as a hack to support the incumbent realm. Remove the need for custom
85    # registration when Blink properly supports the incumbent realm.
86    if method['is_cross_origin']:
87        return True
88    if 'overloads' in method:
89        return (method['overloads']['runtime_determined_lengths']
90                or (method['overloads']['runtime_enabled_all']
91                    and not is_conditionally_enabled(method)))
92    return method[
93        'runtime_enabled_feature_name'] and not is_conditionally_enabled(
94            method)
95
96
97def filter_custom_registration(methods, interface_is_partial):
98    return [
99        method for method in methods
100        if (method_is_visible(method, interface_is_partial)
101            and custom_registration(method))
102    ]
103
104
105def filter_method_configuration(methods, interface_is_partial):
106    return [
107        method for method in methods
108        if method_is_visible(method, interface_is_partial)
109        and not is_origin_trial_enabled(method)
110        and not is_conditionally_enabled(method)
111        and not custom_registration(method)
112    ]
113
114
115def method_filters():
116    return {
117        'custom_registration': filter_custom_registration,
118        'has_method_configuration': filter_method_configuration
119    }
120
121
122def use_local_result(method):
123    extended_attributes = method.extended_attributes
124    idl_type = method.idl_type
125    return (has_extended_attribute_value(method, 'CallWith', 'ScriptState')
126            or 'NewObject' in extended_attributes
127            or 'RaisesException' in extended_attributes
128            or idl_type.is_union_type or idl_type.is_dictionary
129            or idl_type.is_explicit_nullable
130            or v8_utilities.high_entropy(method) == 'Direct')
131
132
133def runtime_call_stats_context(interface, method):
134    includes.add('platform/bindings/runtime_call_stats.h')
135    generic_counter_name = (
136        'Blink_' + v8_utilities.cpp_name(interface) + '_' + method.name)
137    (method_counter, extended_attribute_defined) = \
138        v8_utilities.rcs_counter_name(method, generic_counter_name)
139    trace_event_name = interface.name + '.' + method.name
140    return {
141        'extended_attribute_defined':
142        extended_attribute_defined,
143        'method_counter':
144        method_counter,
145        'origin_safe_method_getter_counter':
146        generic_counter_name + '_OriginSafeMethodGetter',
147        'trace_event_name':
148        trace_event_name,
149    }
150
151
152def method_context(interface, method, component_info, is_visible=True):
153    arguments = method.arguments
154    extended_attributes = method.extended_attributes
155    idl_type = method.idl_type
156    is_static = method.is_static
157    name = method.name
158
159    if is_visible:
160        idl_type.add_includes_for_type(extended_attributes)
161
162    this_cpp_value = cpp_value(interface, method, len(arguments))
163
164    is_call_with_script_state = has_extended_attribute_value(
165        method, 'CallWith', 'ScriptState')
166    is_call_with_this_value = has_extended_attribute_value(
167        method, 'CallWith', 'ThisValue')
168    if is_call_with_script_state or is_call_with_this_value:
169        includes.add('platform/bindings/script_state.h')
170
171    # [CheckSecurity]
172    is_cross_origin = 'CrossOrigin' in extended_attributes
173    is_check_security_for_receiver = (has_extended_attribute_value(
174        interface, 'CheckSecurity', 'Receiver') and not is_cross_origin)
175    is_check_security_for_return_value = (has_extended_attribute_value(
176        method, 'CheckSecurity', 'ReturnValue'))
177    if is_check_security_for_receiver or is_check_security_for_return_value:
178        includes.add('bindings/core/v8/binding_security.h')
179    if is_check_security_for_return_value:
180        includes.add('core/frame/web_feature.h')
181        includes.add('platform/instrumentation/use_counter.h')
182
183    is_ce_reactions = 'CEReactions' in extended_attributes
184    if is_ce_reactions:
185        includes.add('core/html/custom/ce_reactions_scope.h')
186    is_custom_element_callbacks = 'CustomElementCallbacks' in extended_attributes
187    if is_custom_element_callbacks:
188        includes.add('core/html/custom/v0_custom_element_processing_stack.h')
189
190    is_raises_exception = 'RaisesException' in extended_attributes
191
192    if 'LegacyLenientThis' in extended_attributes:
193        raise Exception('[LegacyLenientThis] is not supported for operations.')
194
195    if has_extended_attribute_value(method, 'Affects', 'Nothing'):
196        side_effect_type = 'V8DOMConfiguration::kHasNoSideEffect'
197    else:
198        side_effect_type = 'V8DOMConfiguration::kHasSideEffect'
199
200    # [LogActivity]
201    if 'LogActivity' in extended_attributes:
202        includes.add('platform/bindings/v8_per_context_data.h')
203
204    argument_contexts = [
205        argument_context(
206            interface, method, argument, index, is_visible=is_visible)
207        for index, argument in enumerate(arguments)
208    ]
209
210    runtime_features = component_info['runtime_enabled_features']
211
212    return {
213        'activity_logging_world_list':
214        v8_utilities.activity_logging_world_list(method),  # [ActivityLogging]
215        'arguments':
216        argument_contexts,
217        'camel_case_name':
218        NameStyleConverter(name).to_upper_camel_case(),
219        'cpp_type':
220        (v8_types.cpp_template_type('base::Optional', idl_type.cpp_type)
221         if idl_type.is_explicit_nullable else v8_types.cpp_type(
222             idl_type, extended_attributes=extended_attributes)),
223        'cpp_value':
224        this_cpp_value,
225        'cpp_type_initializer':
226        idl_type.cpp_type_initializer,
227        'high_entropy':
228        v8_utilities.high_entropy(method),  # [HighEntropy]
229        'deprecate_as':
230        v8_utilities.deprecate_as(method),  # [DeprecateAs]
231        'do_not_test_new_object':
232        'DoNotTestNewObject' in extended_attributes,
233        'exposed_test':
234        v8_utilities.exposed(method, interface),  # [Exposed]
235        'has_exception_state':
236        is_raises_exception or is_check_security_for_receiver or any(
237            argument for argument in arguments
238            if argument_conversion_needs_exception_state(method, argument)),
239        'has_optional_argument_without_default_value':
240        any(True for argument_context in argument_contexts
241            if argument_context['is_optional_without_default_value']),
242        'idl_type':
243        idl_type.base_type,
244        'is_call_with_execution_context':
245        has_extended_attribute_value(method, 'CallWith', 'ExecutionContext'),
246        'is_call_with_script_state':
247        is_call_with_script_state,
248        'is_call_with_this_value':
249        is_call_with_this_value,
250        'is_ce_reactions':
251        is_ce_reactions,
252        'is_check_security_for_receiver':
253        is_check_security_for_receiver,
254        'is_check_security_for_return_value':
255        is_check_security_for_return_value,
256        'is_cross_origin':
257        'CrossOrigin' in extended_attributes,
258        'is_custom':
259        'Custom' in extended_attributes,
260        'is_custom_element_callbacks':
261        is_custom_element_callbacks,
262        'is_explicit_nullable':
263        idl_type.is_explicit_nullable,
264        'is_new_object':
265        'NewObject' in extended_attributes,
266        'is_partial_interface_member':
267        'PartialInterfaceImplementedAs' in extended_attributes,
268        'is_per_world_bindings':
269        'PerWorldBindings' in extended_attributes,
270        'is_raises_exception':
271        is_raises_exception,
272        'is_static':
273        is_static,
274        'is_unforgeable':
275        is_unforgeable(method),
276        'is_variadic':
277        arguments and arguments[-1].is_variadic,
278        'measure_as':
279        v8_utilities.measure_as(method, interface),  # [MeasureAs]
280        'name':
281        name,
282        'number_of_arguments':
283        len(arguments),
284        'number_of_required_arguments':
285        len([
286            argument for argument in arguments
287            if not (argument.is_optional or argument.is_variadic)
288        ]),
289        'number_of_required_or_variadic_arguments':
290        len([argument for argument in arguments if not argument.is_optional]),
291        'on_instance':
292        v8_utilities.on_instance(interface, method),
293        'on_interface':
294        v8_utilities.on_interface(interface, method),
295        'on_prototype':
296        v8_utilities.on_prototype(interface, method),
297        # [RuntimeEnabled] for origin trial
298        'origin_trial_feature_name':
299        v8_utilities.origin_trial_feature_name(method, runtime_features),
300        'property_attributes':
301        property_attributes(interface, method),
302        'returns_promise':
303        method.returns_promise,
304        'runtime_call_stats':
305        runtime_call_stats_context(interface, method),
306        # [RuntimeEnabled] if not in origin trial
307        'runtime_enabled_feature_name':
308        v8_utilities.runtime_enabled_feature_name(method, runtime_features),
309        # [SecureContext]
310        'secure_context_test':
311        v8_utilities.secure_context(method, interface),
312        # [Affects]
313        'side_effect_type':
314        side_effect_type,
315        'snake_case_name':
316        NameStyleConverter(name).to_snake_case(),
317        'use_output_parameter_for_result':
318        idl_type.use_output_parameter_for_result,
319        'use_local_result':
320        use_local_result(method),
321        'v8_set_return_value':
322        v8_set_return_value(interface.name, method, this_cpp_value),
323        'v8_set_return_value_for_main_world':
324        v8_set_return_value(
325            interface.name, method, this_cpp_value, for_main_world=True),
326        'visible':
327        is_visible,
328        'world_suffixes':
329        ['', 'ForMainWorld'] if 'PerWorldBindings' in extended_attributes else
330        [''],  # [PerWorldBindings],
331    }
332
333
334def argument_context(interface, method, argument, index, is_visible=True):
335    extended_attributes = argument.extended_attributes
336    idl_type = argument.idl_type
337    if idl_type.has_string_context:
338        includes.add(
339            'third_party/blink/renderer/bindings/core/v8/generated_code_helper.h'
340        )
341    if is_visible:
342        idl_type.add_includes_for_type(extended_attributes)
343    this_cpp_value = cpp_value(interface, method, index)
344    is_variadic_wrapper_type = argument.is_variadic and idl_type.is_wrapper_type
345
346    has_type_checking_interface = idl_type.is_wrapper_type
347
348    set_default_value = argument.set_default_value
349    this_cpp_type = idl_type.cpp_type_args(
350        extended_attributes=extended_attributes,
351        raw_type=True,
352        used_as_variadic_argument=argument.is_variadic)
353    snake_case_name = NameStyleConverter(argument.name).to_snake_case()
354    context = {
355        'cpp_type': (v8_types.cpp_template_type(
356            'base::Optional', this_cpp_type) if idl_type.is_explicit_nullable
357                     and not argument.is_variadic else this_cpp_type),
358        'cpp_value':
359        this_cpp_value,
360        # FIXME: check that the default value's type is compatible with the argument's
361        'enum_type':
362        idl_type.enum_type,
363        'enum_values':
364        idl_type.enum_values,
365        'handle':
366        '%s_handle' % snake_case_name,
367        # TODO(peria): remove once [DefaultValue] removed and just use
368        # argument.default_value. https://crbug.com/924419
369        'has_default':
370        'DefaultValue' in extended_attributes or set_default_value,
371        'has_type_checking_interface':
372        has_type_checking_interface,
373        # Dictionary is special-cased, but arrays and sequences shouldn't be
374        'idl_type':
375        idl_type.base_type,
376        'idl_type_object':
377        idl_type,
378        'index':
379        index,
380        'is_callback_function':
381        idl_type.is_callback_function,
382        'is_callback_interface':
383        idl_type.is_callback_interface,
384        # FIXME: Remove generic 'Dictionary' special-casing
385        'is_dictionary':
386        idl_type.is_dictionary,
387        'is_explicit_nullable':
388        idl_type.is_explicit_nullable,
389        'is_nullable':
390        idl_type.is_nullable,
391        'is_optional':
392        argument.is_optional,
393        'is_variadic':
394        argument.is_variadic,
395        'is_variadic_wrapper_type':
396        is_variadic_wrapper_type,
397        'is_wrapper_type':
398        idl_type.is_wrapper_type,
399        'local_cpp_variable':
400        snake_case_name,
401        'name':
402        argument.name,
403        'set_default_value':
404        set_default_value,
405        'use_permissive_dictionary_conversion':
406        'PermissiveDictionaryConversion' in extended_attributes,
407        'v8_set_return_value':
408        v8_set_return_value(interface.name, method, this_cpp_value),
409        'v8_set_return_value_for_main_world':
410        v8_set_return_value(
411            interface.name, method, this_cpp_value, for_main_world=True),
412        'v8_value_to_local_cpp_value':
413        v8_value_to_local_cpp_value(interface.name, method, argument, index),
414    }
415    context.update({
416        'is_optional_without_default_value':
417        context['is_optional'] and not context['has_default']
418        and not context['is_dictionary']
419        and not context['is_callback_interface'],
420    })
421    return context
422
423
424################################################################################
425# Value handling
426################################################################################
427
428
429def cpp_value(interface, method, number_of_arguments):
430    # Truncate omitted optional arguments
431    arguments = method.arguments[:number_of_arguments]
432    cpp_arguments = []
433
434    if method.is_constructor:
435        call_with_values = interface.extended_attributes.get(
436            'ConstructorCallWith')
437    else:
438        call_with_values = method.extended_attributes.get('CallWith')
439    cpp_arguments.extend(v8_utilities.call_with_arguments(call_with_values))
440
441    # Members of IDL partial interface definitions are implemented in C++ as
442    # static member functions, which for instance members (non-static members)
443    # take *impl as their first argument
444    if ('PartialInterfaceImplementedAs' in method.extended_attributes
445            and not method.is_static):
446        cpp_arguments.append('*impl')
447    for argument in arguments:
448        variable_name = NameStyleConverter(argument.name).to_snake_case()
449        cpp_arguments.append(variable_name)
450
451    # If a method returns an IDL dictionary or union type, the return value is
452    # passed as an argument to impl classes.
453    idl_type = method.idl_type
454    if idl_type and idl_type.use_output_parameter_for_result:
455        cpp_arguments.append('result')
456
457    if ('RaisesException' in method.extended_attributes
458            or (method.is_constructor and has_extended_attribute_value(
459                interface, 'RaisesException', 'Constructor'))):
460        cpp_arguments.append('exception_state')
461
462    if method.name == 'Constructor':
463        base_name = 'Create'
464    elif method.name == 'NamedConstructor':
465        base_name = 'CreateForJSConstructor'
466    else:
467        base_name = v8_utilities.cpp_name(method)
468
469    cpp_method_name = v8_utilities.scoped_name(interface, method, base_name)
470    return '%s(%s)' % (cpp_method_name, ', '.join(cpp_arguments))
471
472
473def v8_set_return_value(interface_name,
474                        method,
475                        cpp_value,
476                        for_main_world=False):
477    idl_type = method.idl_type
478    extended_attributes = method.extended_attributes
479    if not idl_type or idl_type.name == 'void':
480        # Constructors and void methods don't have a return type
481        return None
482
483    # [CallWith=ScriptState], [RaisesException]
484    if use_local_result(method):
485        if idl_type.is_explicit_nullable:
486            # result is of type base::Optional<T>
487            cpp_value = 'result.value()'
488        else:
489            cpp_value = 'result'
490
491    script_wrappable = 'impl' if inherits_interface(interface_name,
492                                                    'Node') else ''
493    return idl_type.v8_set_return_value(
494        cpp_value,
495        extended_attributes,
496        script_wrappable=script_wrappable,
497        for_main_world=for_main_world,
498        is_static=method.is_static)
499
500
501def v8_value_to_local_cpp_variadic_value(argument,
502                                         index,
503                                         for_constructor_callback=False):
504    assert argument.is_variadic
505    idl_type = v8_types.native_value_traits_type_name(
506        argument.idl_type, argument.extended_attributes, True)
507    execution_context_if_needed = ''
508    if argument.idl_type.has_string_context:
509        execution_context_if_needed = ', bindings::ExecutionContextFromV8Wrappable(impl)'
510        if for_constructor_callback:
511            execution_context_if_needed = ', CurrentExecutionContext(info.GetIsolate())'
512    assign_expression = 'ToImplArguments<%s>(info, %s, exception_state%s)' % (
513        idl_type, index, execution_context_if_needed)
514    return {
515        'assign_expression': assign_expression,
516        'check_expression': 'exception_state.HadException()',
517        'cpp_name': NameStyleConverter(argument.name).to_snake_case(),
518        'declare_variable': False,
519    }
520
521
522def v8_value_to_local_cpp_value(interface_name, method, argument, index):
523    extended_attributes = argument.extended_attributes
524    idl_type = argument.idl_type
525    name = NameStyleConverter(argument.name).to_snake_case()
526    v8_value = 'info[{index}]'.format(index=index)
527    for_constructor_callback = method.name == 'Constructor'
528
529    if argument.is_variadic:
530        return v8_value_to_local_cpp_variadic_value(
531            argument, index, for_constructor_callback=for_constructor_callback)
532    return idl_type.v8_value_to_local_cpp_value(
533        extended_attributes,
534        v8_value,
535        name,
536        declare_variable=False,
537        use_exception_state=method.returns_promise,
538        for_constructor_callback=for_constructor_callback)
539
540
541################################################################################
542# Auxiliary functions
543################################################################################
544
545
546# [NotEnumerable], [LegacyUnforgeable]
547def property_attributes(interface, method):
548    extended_attributes = method.extended_attributes
549    property_attributes_list = []
550    if 'NotEnumerable' in extended_attributes:
551        property_attributes_list.append('v8::DontEnum')
552    if is_unforgeable(method):
553        property_attributes_list.append('v8::ReadOnly')
554        property_attributes_list.append('v8::DontDelete')
555    return property_attributes_list
556
557
558def argument_set_default_value(argument):
559    idl_type = argument.idl_type
560    default_value = argument.default_value
561    variable_name = NameStyleConverter(argument.name).to_snake_case()
562    if not default_value:
563        return None
564    if idl_type.is_dictionary:
565        if argument.default_value.is_null:
566            return None
567        if argument.default_value.value == '{}':
568            return None
569        raise Exception('invalid default value for dictionary type')
570    if idl_type.is_array_or_sequence_type:
571        if default_value.value != '[]':
572            raise Exception('invalid default value for sequence type: %s' %
573                            default_value.value)
574        # Nothing to do when we set an empty sequence as default value, but we
575        # need to return non-empty value so that we don't generate method calls
576        # without this argument.
577        return '/* Nothing to do */'
578    if idl_type.is_union_type:
579        if argument.default_value.is_null:
580            if not idl_type.includes_nullable_type:
581                raise Exception(
582                    'invalid default value for union type: null for %s' %
583                    idl_type.name)
584            # Union container objects are "null" initially.
585            return '/* null default value */'
586        if default_value.value == "{}":
587            member_type = idl_type.dictionary_member_type
588        elif isinstance(default_value.value, basestring):
589            member_type = idl_type.string_member_type
590        elif isinstance(default_value.value, (int, float)):
591            member_type = idl_type.numeric_member_type
592        elif isinstance(default_value.value, bool):
593            member_type = idl_type.boolean_member_type
594        else:
595            member_type = None
596        if member_type is None:
597            raise Exception('invalid default value for union type: %r for %s' %
598                            (default_value.value, idl_type.name))
599        member_type_name = (member_type.inner_type.name
600                            if member_type.is_nullable else member_type.name)
601        return '%s.Set%s(%s)' % (variable_name, member_type_name,
602                                 member_type.literal_cpp_value(default_value))
603    return '%s = %s' % (variable_name,
604                        idl_type.literal_cpp_value(default_value))
605
606
607IdlArgument.set_default_value = property(argument_set_default_value)
608
609
610def method_returns_promise(method):
611    return method.idl_type and method.idl_type.name == 'Promise'
612
613
614IdlOperation.returns_promise = property(method_returns_promise)
615
616
617def argument_conversion_needs_exception_state(method, argument):
618    idl_type = argument.idl_type
619    return (idl_type.v8_conversion_needs_exception_state
620            or argument.is_variadic
621            or (method.returns_promise and idl_type.is_string_type))
622