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