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