Codegen.py (918394B)
1 # This Source Code Form is subject to the terms of the Mozilla Public 2 # License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 # You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 # Common codegen classes. 6 7 import functools 8 import math 9 from operator import attrgetter 10 import os 11 import re 12 import string 13 import textwrap 14 15 from Configuration import ( 16 Configuration, 17 Descriptor, 18 MemberIsLegacyUnforgeable, 19 NoSuchDescriptorError, 20 getAllTypes, 21 getTypesFromCallback, 22 getTypesFromDescriptor, 23 getTypesFromDictionary, 24 ) 25 from perfecthash import PerfectHash 26 from WebIDL import ( 27 BuiltinTypes, 28 IDLAttribute, 29 IDLBuiltinType, 30 IDLDefaultDictionaryValue, 31 IDLDictionary, 32 IDLEmptySequenceValue, 33 IDLInterfaceMember, 34 IDLNullValue, 35 IDLSequenceType, 36 IDLType, 37 IDLTypedef, 38 IDLUndefinedValue, 39 ) 40 41 AUTOGENERATED_WARNING_COMMENT = ( 42 "/* THIS FILE IS AUTOGENERATED BY Codegen.py - DO NOT EDIT */\n\n" 43 ) 44 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT = ( 45 "/* THIS FILE IS AUTOGENERATED FROM %s BY Codegen.py - DO NOT EDIT */\n\n" 46 ) 47 FINALIZE_HOOK_NAME = "_finalize" 48 CONSTRUCT_HOOK_NAME = "_constructor" 49 LEGACYCALLER_HOOK_NAME = "_legacycaller" 50 RESOLVE_HOOK_NAME = "_resolve" 51 MAY_RESOLVE_HOOK_NAME = "_mayResolve" 52 NEW_ENUMERATE_HOOK_NAME = "_newEnumerate" 53 INSTANCE_RESERVED_SLOTS = 1 54 55 # This size is arbitrary. It is a power of 2 to make using it as a modulo 56 # operand cheap, and is usually around 1/3-1/5th of the set size (sometimes 57 # smaller for very large sets). 58 GLOBAL_NAMES_PHF_SIZE = 256 59 60 # If you have to change this list (which you shouldn't!), make sure it 61 # continues to match the list in test_Object.prototype_props.html 62 JS_OBJECT_PROTOTYPE_PROPERTIES = [ 63 "constructor", 64 "toString", 65 "toLocaleString", 66 "valueOf", 67 "hasOwnProperty", 68 "isPrototypeOf", 69 "propertyIsEnumerable", 70 "__defineGetter__", 71 "__defineSetter__", 72 "__lookupGetter__", 73 "__lookupSetter__", 74 "__proto__", 75 ] 76 77 78 def reservedSlot(slotIndex, forXray): 79 base = "DOM_EXPANDO_RESERVED_SLOTS" if forXray else "DOM_INSTANCE_RESERVED_SLOTS" 80 return "(%s + %d)" % (base, slotIndex) 81 82 83 def getSlotIndex(member, descriptor): 84 slotIndex = member.slotIndices[descriptor.interface.identifier.name] 85 return slotIndex[0] if isinstance(slotIndex, tuple) else slotIndex 86 87 88 def memberReservedSlot(member, descriptor): 89 return reservedSlot(getSlotIndex(member, descriptor), False) 90 91 92 def memberXrayExpandoReservedSlot(member, descriptor): 93 return reservedSlot(getSlotIndex(member, descriptor), True) 94 95 96 def mayUseXrayExpandoSlots(descriptor, attr): 97 assert not attr.getExtendedAttribute("NewObject") 98 # For attributes whose type is a Gecko interface we always use 99 # slots on the reflector for caching. Also, for interfaces that 100 # don't want Xrays we obviously never use the Xray expando slot. 101 return descriptor.wantsXrays and not attr.type.isGeckoInterface() 102 103 104 def reflectedHTMLAttributesArrayIndex(descriptor, attr): 105 slots = attr.slotIndices[descriptor.interface.identifier.name] 106 return slots[1] 107 108 109 def getReflectedHTMLAttributesIface(descriptor): 110 iface = descriptor.interface 111 while iface: 112 if iface.reflectedHTMLAttributesReturningFrozenArray: 113 return iface 114 iface = iface.parent 115 return None 116 117 118 def toStringBool(arg): 119 """ 120 Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false) 121 """ 122 return str(not not arg).lower() 123 124 125 def toBindingNamespace(arg): 126 return arg + "_Binding" 127 128 129 def isTypeCopyConstructible(type): 130 # Nullable and sequence stuff doesn't affect copy-constructibility 131 type = type.unroll() 132 return ( 133 type.isUndefined() 134 or type.isPrimitive() 135 or type.isString() 136 or type.isEnum() 137 or (type.isUnion() and CGUnionStruct.isUnionCopyConstructible(type)) 138 or ( 139 type.isDictionary() 140 and CGDictionary.isDictionaryCopyConstructible(type.inner) 141 ) 142 or 143 # Interface types are only copy-constructible if they're Gecko 144 # interfaces. SpiderMonkey interfaces are not copy-constructible 145 # because of rooting issues. 146 (type.isInterface() and type.isGeckoInterface()) 147 ) 148 149 150 class CycleCollectionUnsupported(TypeError): 151 def __init__(self, message): 152 TypeError.__init__(self, message) 153 154 155 def idlTypeNeedsCycleCollection(type): 156 type = type.unroll() # Takes care of sequences and nullables 157 if ( 158 (type.isPrimitive() and type.tag() in builtinNames) 159 or type.isUndefined() 160 or type.isEnum() 161 or type.isString() 162 or type.isAny() 163 or type.isObject() 164 or type.isSpiderMonkeyInterface() 165 ): 166 return False 167 elif type.isCallback() or type.isPromise() or type.isGeckoInterface(): 168 return True 169 elif type.isUnion(): 170 return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes) 171 elif type.isRecord(): 172 if idlTypeNeedsCycleCollection(type.inner): 173 raise CycleCollectionUnsupported( 174 "Cycle collection for type %s is not supported" % type 175 ) 176 return False 177 elif type.isDictionary(): 178 return CGDictionary.dictionaryNeedsCycleCollection(type.inner) 179 else: 180 raise CycleCollectionUnsupported( 181 "Don't know whether to cycle-collect type %s" % type 182 ) 183 184 185 def idlTypeNeedsCallContext(type, descriptor=None, allowTreatNonCallableAsNull=False): 186 """ 187 Returns whether the given type needs error reporting via a 188 BindingCallContext for JS-to-C++ conversions. This will happen when the 189 conversion can throw an exception due to logic in the IDL spec or 190 Gecko-specific security checks. In particular, a type needs a 191 BindingCallContext if and only if the JS-to-C++ conversion for that type can 192 end up calling ThrowErrorMessage. 193 194 For some types this depends on the descriptor (e.g. because we do certain 195 checks only for some kinds of interfaces). 196 197 The allowTreatNonCallableAsNull optimization is there so we can avoid 198 generating an unnecessary BindingCallContext for all the event handler 199 attribute setters. 200 201 """ 202 while True: 203 if type.isSequence(): 204 # Sequences can always throw "not an object" 205 return True 206 if type.nullable(): 207 # treatNonObjectAsNull() and treatNonCallableAsNull() are 208 # only sane things to test on nullable types, so do that now. 209 if ( 210 allowTreatNonCallableAsNull 211 and type.isCallback() 212 and (type.treatNonObjectAsNull() or type.treatNonCallableAsNull()) 213 ): 214 # This can't throw. so never needs a method description. 215 return False 216 type = type.inner 217 else: 218 break 219 220 if type.isUndefined(): 221 # Clearly doesn't need a method description; we can only get here from 222 # CGHeaders trying to decide whether to include the method description 223 # header. 224 return False 225 # The float check needs to come before the isPrimitive() check, 226 # because floats are primitives too. 227 if type.isFloat(): 228 # Floats can throw if restricted. 229 return not type.isUnrestricted() 230 if type.isPrimitive() and type.tag() in builtinNames: 231 # Numbers can throw if enforcing range. 232 return type.hasEnforceRange() 233 if type.isEnum(): 234 # Can throw on invalid value. 235 return True 236 if type.isString(): 237 # Can throw if it's a ByteString 238 return type.isByteString() 239 if type.isAny(): 240 # JS-implemented interfaces do extra security checks so need a 241 # method description here. If we have no descriptor, this 242 # might be JS-implemented thing, so it will do the security 243 # check and we need the method description. 244 return not descriptor or descriptor.interface.isJSImplemented() 245 if type.isPromise(): 246 # JS-to-Promise conversion won't cause us to throw any 247 # specific exceptions, so does not need a method description. 248 return False 249 if ( 250 type.isObject() 251 or type.isInterface() 252 or type.isCallback() 253 or type.isDictionary() 254 or type.isRecord() 255 or type.isObservableArray() 256 ): 257 # These can all throw if a primitive is passed in, at the very least. 258 # There are some rare cases when we know we have an object, but those 259 # are not worth the complexity of optimizing for. 260 # 261 # Note that we checked the [LegacyTreatNonObjectAsNull] case already when 262 # unwrapping nullables. 263 return True 264 if type.isUnion(): 265 # Can throw if a type not in the union is passed in. 266 return True 267 raise TypeError("Don't know whether type '%s' needs a method description" % type) 268 269 270 # TryPreserveWrapper uses the addProperty hook to preserve the wrapper of 271 # non-nsISupports cycle collected objects, so if wantsPreservedWrapper is changed 272 # to not cover that case then TryPreserveWrapper will need to be changed. 273 def wantsPreservedWrapper(desc): 274 return desc.concrete and desc.wrapperCache and not desc.isGlobal() 275 276 277 def wantsGetWrapperCache(desc): 278 return ( 279 desc.concrete and desc.wrapperCache and not desc.isGlobal() and not desc.proxy 280 ) 281 282 283 def indent(s, indentLevel=2): 284 """ 285 Indent C++ code. 286 287 Weird secret feature: this doesn't indent lines that start with # (such as 288 #include lines or #ifdef/#endif). 289 """ 290 291 # We'll want to insert the indent at the beginnings of lines, but we 292 # don't want to indent empty lines. 293 padding = indentLevel * " " 294 return "\n".join( 295 [ 296 (padding + line) if line and line[0] != "#" else line 297 for line in s.split("\n") 298 ] 299 ) 300 301 302 # dedent() and fill() are often called on the same string multiple 303 # times. We want to memoize their return values so we don't keep 304 # recomputing them all the time. 305 def memoize(fn): 306 """ 307 Decorator to memoize a function of one argument. The cache just 308 grows without bound. 309 """ 310 cache = {} 311 312 @functools.wraps(fn) 313 def wrapper(arg): 314 retval = cache.get(arg) 315 if retval is None: 316 retval = cache[arg] = fn(arg) 317 return retval 318 319 return wrapper 320 321 322 @memoize 323 def dedent(s): 324 """ 325 Remove all leading whitespace from s, and remove a blank line 326 at the beginning. 327 """ 328 if s.startswith("\n"): 329 s = s[1:] 330 return textwrap.dedent(s) 331 332 333 # This works by transforming the fill()-template to an equivalent 334 # string.Template. 335 fill_multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?") 336 337 338 find_substitutions = re.compile(r"\${") 339 340 341 @memoize 342 def compile_fill_template(template): 343 """ 344 Helper function for fill(). Given the template string passed to fill(), 345 do the reusable part of template processing and return a pair (t, 346 argModList) that can be used every time fill() is called with that 347 template argument. 348 349 argsModList is list of tuples that represent modifications to be 350 made to args. Each modification has, in order: i) the arg name, 351 ii) the modified name, iii) the indent depth. 352 """ 353 t = dedent(template) 354 assert t.endswith("\n") or "\n" not in t 355 argModList = [] 356 357 def replace(match): 358 """ 359 Replaces a line like ' $*{xyz}\n' with '${xyz_n}', 360 where n is the indent depth, and add a corresponding entry to 361 argModList. 362 363 Note that this needs to close over argModList, so it has to be 364 defined inside compile_fill_template(). 365 """ 366 indentation, name, nl = match.groups() 367 depth = len(indentation) 368 369 # Check that $*{xyz} appears by itself on a line. 370 prev = match.string[: match.start()] 371 if (prev and not prev.endswith("\n")) or nl is None: 372 raise ValueError( 373 "Invalid fill() template: $*{%s} must appear by itself on a line" % name 374 ) 375 376 # Now replace this whole line of template with the indented equivalent. 377 modified_name = name + "_" + str(depth) 378 argModList.append((name, modified_name, depth)) 379 return "${" + modified_name + "}" 380 381 t = re.sub(fill_multiline_substitution_re, replace, t) 382 if not re.search(find_substitutions, t): 383 raise TypeError("Using fill() when dedent() would do.") 384 return (string.Template(t), argModList) 385 386 387 def fill(template, **args): 388 """ 389 Convenience function for filling in a multiline template. 390 391 `fill(template, name1=v1, name2=v2)` is a lot like 392 `string.Template(template).substitute({"name1": v1, "name2": v2})`. 393 394 However, it's shorter, and has a few nice features: 395 396 * If `template` is indented, fill() automatically dedents it! 397 This makes code using fill() with Python's multiline strings 398 much nicer to look at. 399 400 * If `template` starts with a blank line, fill() strips it off. 401 (Again, convenient with multiline strings.) 402 403 * fill() recognizes a special kind of substitution 404 of the form `$*{name}`. 405 406 Use this to paste in, and automatically indent, multiple lines. 407 (Mnemonic: The `*` is for "multiple lines"). 408 409 A `$*` substitution must appear by itself on a line, with optional 410 preceding indentation (spaces only). The whole line is replaced by the 411 corresponding keyword argument, indented appropriately. If the 412 argument is an empty string, no output is generated, not even a blank 413 line. 414 """ 415 416 t, argModList = compile_fill_template(template) 417 # Now apply argModList to args 418 for name, modified_name, depth in argModList: 419 if not (args[name] == "" or args[name].endswith("\n")): 420 raise ValueError( 421 "Argument %s with value %r is missing a newline" % (name, args[name]) 422 ) 423 args[modified_name] = indent(args[name], depth) 424 425 return t.substitute(args) 426 427 428 class CGThing: 429 """ 430 Abstract base class for things that spit out code. 431 """ 432 433 def __init__(self): 434 pass # Nothing for now 435 436 def declare(self): 437 """Produce code for a header file.""" 438 assert False # Override me! 439 440 def define(self): 441 """Produce code for a cpp file.""" 442 assert False # Override me! 443 444 def forward_declare(self): 445 """Produce code for a header file.""" 446 return "" # This can be skipped for most of the classes 447 448 def deps(self): 449 """Produce the deps for a pp file""" 450 assert False # Override me! 451 452 453 class CGStringTable(CGThing): 454 """ 455 Generate a function accessor for a WebIDL string table, using the existing 456 concatenated names string and mapping indexes to offsets in that string: 457 458 const char *accessorName(unsigned int index) { 459 static const uint16_t offsets = { ... }; 460 return BindingName(offsets[index]); 461 } 462 463 This is more efficient than the more natural: 464 465 const char *table[] = { 466 ... 467 }; 468 469 The uint16_t offsets are smaller than the pointer equivalents, and the 470 concatenated string requires no runtime relocations. 471 """ 472 473 def __init__(self, accessorName, strings, static=False): 474 CGThing.__init__(self) 475 self.accessorName = accessorName 476 self.strings = strings 477 self.static = static 478 479 def declare(self): 480 if self.static: 481 return "" 482 return "const char *%s(unsigned int aIndex);\n" % self.accessorName 483 484 def define(self): 485 offsets = [] 486 for s in self.strings: 487 offsets.append(BindingNamesOffsetEnum(s)) 488 return fill( 489 """ 490 ${static}const char *${name}(unsigned int aIndex) 491 { 492 static const BindingNamesOffset offsets[] = { 493 $*{offsets} 494 }; 495 return BindingName(offsets[aIndex]); 496 } 497 """, 498 static="static " if self.static else "", 499 name=self.accessorName, 500 offsets="".join("BindingNamesOffset::%s,\n" % o for o in offsets), 501 ) 502 503 504 class CGNativePropertyHooks(CGThing): 505 """ 506 Generate a NativePropertyHooks for a given descriptor 507 """ 508 509 def __init__(self, descriptor, properties): 510 CGThing.__init__(self) 511 assert descriptor.wantsXrays 512 self.descriptor = descriptor 513 self.properties = properties 514 515 def declare(self): 516 return "" 517 518 def define(self): 519 if ( 520 self.descriptor.concrete 521 and self.descriptor.proxy 522 and not self.descriptor.isMaybeCrossOriginObject() 523 ): 524 if self.descriptor.needsXrayNamedDeleterHook(): 525 deleteNamedProperty = "DeleteNamedProperty" 526 else: 527 deleteNamedProperty = "nullptr" 528 namedOrIndexed = fill( 529 """ 530 const NativeNamedOrIndexedPropertyHooks sNativeNamedOrIndexedPropertyHooks = { 531 binding_detail::ResolveOwnProperty, 532 binding_detail::EnumerateOwnProperties, 533 ${deleteNamedProperty} 534 }; 535 """, 536 deleteNamedProperty=deleteNamedProperty, 537 ) 538 namedOrIndexedPointer = "&sNativeNamedOrIndexedPropertyHooks" 539 elif self.descriptor.needsXrayResolveHooks(): 540 namedOrIndexed = dedent( 541 """ 542 const NativeNamedOrIndexedPropertyHooks sNativeNamedOrIndexedPropertyHooks = { 543 ResolveOwnPropertyViaResolve, 544 EnumerateOwnPropertiesViaGetOwnPropertyNames, 545 nullptr 546 }; 547 """ 548 ) 549 namedOrIndexedPointer = "&sNativeNamedOrIndexedPropertyHooks" 550 else: 551 namedOrIndexed = "" 552 namedOrIndexedPointer = "nullptr" 553 if self.properties.hasNonChromeOnly(): 554 regular = "sNativeProperties.Upcast()" 555 else: 556 regular = "nullptr" 557 if self.properties.hasChromeOnly(): 558 chrome = "sChromeOnlyNativeProperties.Upcast()" 559 else: 560 chrome = "nullptr" 561 constructorID = "constructors::id::" 562 if self.descriptor.interface.hasInterfaceObject(): 563 constructorID += self.descriptor.name 564 else: 565 constructorID += "_ID_Count" 566 prototypeID = "prototypes::id::" 567 if self.descriptor.interface.hasInterfacePrototypeObject(): 568 prototypeID += self.descriptor.name 569 else: 570 prototypeID += "_ID_Count" 571 572 if self.descriptor.wantsXrayExpandoClass: 573 expandoClass = "&sXrayExpandoObjectClass" 574 else: 575 expandoClass = "&DefaultXrayExpandoObjectClass" 576 577 return namedOrIndexed + fill( 578 """ 579 bool sNativePropertiesInited = false; 580 const NativePropertyHooks sNativePropertyHooks = { 581 ${namedOrIndexedPointer}, 582 { ${regular}, ${chrome}, &sNativePropertiesInited }, 583 ${prototypeID}, 584 ${constructorID}, 585 ${expandoClass} 586 }; 587 """, 588 namedOrIndexedPointer=namedOrIndexedPointer, 589 regular=regular, 590 chrome=chrome, 591 prototypeID=prototypeID, 592 constructorID=constructorID, 593 expandoClass=expandoClass, 594 ) 595 596 597 def NativePropertyHooks(descriptor): 598 return ( 599 "&sEmptyNativePropertyHooks" 600 if not descriptor.wantsXrays 601 else "&sNativePropertyHooks" 602 ) 603 604 605 def DOMClass(descriptor): 606 protoList = ["prototypes::id::" + proto for proto in descriptor.prototypeNameChain] 607 # Pad out the list to the right length with _ID_Count so we 608 # guarantee that all the lists are the same length. _ID_Count 609 # is never the ID of any prototype, so it's safe to use as 610 # padding. 611 protoList.extend( 612 ["prototypes::id::_ID_Count"] 613 * (descriptor.config.maxProtoChainLength - len(protoList)) 614 ) 615 616 if descriptor.interface.isSerializable(): 617 serializer = "Serialize" 618 else: 619 serializer = "nullptr" 620 621 if wantsGetWrapperCache(descriptor): 622 wrapperCacheGetter = ( 623 "NativeTypeHelpers<%s>::GetWrapperCache" % descriptor.nativeType 624 ) 625 else: 626 wrapperCacheGetter = "nullptr" 627 628 if descriptor.hasOrdinaryObjectPrototype(): 629 getProto = "JS::GetRealmObjectPrototypeHandle" 630 else: 631 getProto = "GetProtoObjectHandle" 632 633 return fill( 634 """ 635 { ${protoChain} }, 636 std::is_base_of_v<nsISupports, ${nativeType}>, 637 ${hooks}, 638 FindAssociatedGlobalForNative<${nativeType}>::Get, 639 ${getProto}, 640 GetCCParticipant<${nativeType}>::Get(), 641 ${serializer}, 642 ${wrapperCacheGetter} 643 """, 644 protoChain=", ".join(protoList), 645 nativeType=descriptor.nativeType, 646 hooks=NativePropertyHooks(descriptor), 647 serializer=serializer, 648 wrapperCacheGetter=wrapperCacheGetter, 649 getProto=getProto, 650 ) 651 652 653 def InstanceReservedSlots(descriptor): 654 slots = INSTANCE_RESERVED_SLOTS + descriptor.interface.totalMembersInSlots 655 if descriptor.isMaybeCrossOriginObject(): 656 # We need a slot for the cross-origin holder too. 657 if descriptor.interface.hasChildInterfaces(): 658 raise TypeError( 659 "We don't support non-leaf cross-origin interfaces " 660 "like %s" % descriptor.interface.identifier.name 661 ) 662 slots += 1 663 return slots 664 665 666 class CGDOMJSClass(CGThing): 667 """ 668 Generate a DOMJSClass for a given descriptor 669 """ 670 671 def __init__(self, descriptor): 672 CGThing.__init__(self) 673 self.descriptor = descriptor 674 675 def declare(self): 676 return "" 677 678 def define(self): 679 callHook = ( 680 LEGACYCALLER_HOOK_NAME 681 if self.descriptor.operations["LegacyCaller"] 682 else "nullptr" 683 ) 684 if self.descriptor.wrapperCache: 685 classExtension = ( 686 "&NativeTypeHelpers<%s>::sClassExtension" % self.descriptor.nativeType 687 ) 688 else: 689 classExtension = "JS_NULL_CLASS_EXT" 690 slotCount = InstanceReservedSlots(self.descriptor) 691 classFlags = "JSCLASS_IS_DOMJSCLASS | JSCLASS_FOREGROUND_FINALIZE | " 692 if self.descriptor.isGlobal(): 693 classFlags += ( 694 "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)" 695 ) 696 traceHook = "JS_GlobalObjectTraceHook" 697 reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS" 698 else: 699 classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount 700 iface = getReflectedHTMLAttributesIface(self.descriptor) 701 if iface: 702 traceHook = ( 703 "%s::ReflectedHTMLAttributeSlots::Trace" 704 % toBindingNamespace(iface.identifier.name) 705 ) 706 else: 707 traceHook = "nullptr" 708 reservedSlots = slotCount 709 if self.descriptor.interface.hasProbablyShortLivingWrapper(): 710 if not self.descriptor.wrapperCache: 711 raise TypeError( 712 "Need a wrapper cache to support nursery " 713 "allocation of DOM objects" 714 ) 715 classFlags += " | JSCLASS_SKIP_NURSERY_FINALIZE" 716 717 if wantsPreservedWrapper(self.descriptor): 718 classFlags += " | JSCLASS_PRESERVES_WRAPPER" 719 720 if self.descriptor.interface.getExtendedAttribute("NeedResolve"): 721 resolveHook = RESOLVE_HOOK_NAME 722 mayResolveHook = MAY_RESOLVE_HOOK_NAME 723 newEnumerateHook = NEW_ENUMERATE_HOOK_NAME 724 elif self.descriptor.isGlobal(): 725 resolveHook = "mozilla::dom::ResolveGlobal" 726 mayResolveHook = "mozilla::dom::MayResolveGlobal" 727 newEnumerateHook = "mozilla::dom::EnumerateGlobal" 728 else: 729 resolveHook = "nullptr" 730 mayResolveHook = "nullptr" 731 newEnumerateHook = "nullptr" 732 733 return fill( 734 """ 735 static const JSClassOps sClassOps = { 736 nullptr, /* addProperty */ 737 nullptr, /* delProperty */ 738 nullptr, /* enumerate */ 739 ${newEnumerate}, /* newEnumerate */ 740 ${resolve}, /* resolve */ 741 ${mayResolve}, /* mayResolve */ 742 ${finalize}, /* finalize */ 743 ${call}, /* call */ 744 nullptr, /* construct */ 745 ${trace}, /* trace */ 746 }; 747 748 static const DOMJSClass sClass = { 749 { "${name}", 750 ${flags}, 751 &sClassOps, 752 JS_NULL_CLASS_SPEC, 753 ${classExtension}, 754 JS_NULL_OBJECT_OPS 755 }, 756 $*{descriptor} 757 }; 758 static_assert(${instanceReservedSlots} == DOM_INSTANCE_RESERVED_SLOTS, 759 "Must have the right minimal number of reserved slots."); 760 static_assert(${reservedSlots} >= ${slotCount}, 761 "Must have enough reserved slots."); 762 """, 763 name=self.descriptor.interface.getClassName(), 764 flags=classFlags, 765 newEnumerate=newEnumerateHook, 766 resolve=resolveHook, 767 mayResolve=mayResolveHook, 768 finalize=FINALIZE_HOOK_NAME, 769 call=callHook, 770 trace=traceHook, 771 classExtension=classExtension, 772 descriptor=DOMClass(self.descriptor), 773 instanceReservedSlots=INSTANCE_RESERVED_SLOTS, 774 reservedSlots=reservedSlots, 775 slotCount=slotCount, 776 ) 777 778 779 class CGDOMProxyJSClass(CGThing): 780 """ 781 Generate a DOMJSClass for a given proxy descriptor 782 """ 783 784 def __init__(self, descriptor): 785 CGThing.__init__(self) 786 self.descriptor = descriptor 787 788 def declare(self): 789 return "" 790 791 def define(self): 792 slotCount = InstanceReservedSlots(self.descriptor) 793 # We need one reserved slot (DOM_OBJECT_SLOT). 794 flags = ["JSCLASS_IS_DOMJSCLASS", "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount] 795 # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because 796 # we don't want people ever adding that to any interface other than 797 # HTMLAllCollection. So just hardcode it here. 798 if self.descriptor.interface.identifier.name == "HTMLAllCollection": 799 flags.append("JSCLASS_EMULATES_UNDEFINED") 800 return fill( 801 """ 802 static const DOMJSClass sClass = { 803 PROXY_CLASS_DEF("${name}", 804 ${flags}), 805 $*{descriptor} 806 }; 807 """, 808 name=self.descriptor.interface.identifier.name, 809 flags=" | ".join(flags), 810 descriptor=DOMClass(self.descriptor), 811 ) 812 813 814 class CGXrayExpandoJSClass(CGThing): 815 """ 816 Generate a JSClass for an Xray expando object. This is only 817 needed if we have members in slots (for [Cached] or [StoreInSlot] 818 stuff). 819 """ 820 821 def __init__(self, descriptor): 822 assert descriptor.interface.totalMembersInSlots != 0 823 assert descriptor.wantsXrays 824 assert descriptor.wantsXrayExpandoClass 825 CGThing.__init__(self) 826 self.descriptor = descriptor 827 828 def declare(self): 829 return "" 830 831 def define(self): 832 iface = getReflectedHTMLAttributesIface(self.descriptor) 833 if iface: 834 ops = ( 835 "&%s::ReflectedHTMLAttributeSlots::sXrayExpandoObjectClassOps" 836 % toBindingNamespace(iface.identifier.name) 837 ) 838 else: 839 ops = "&xpc::XrayExpandoObjectClassOps" 840 return fill( 841 """ 842 // This may allocate too many slots, because we only really need 843 // slots for our non-interface-typed members that we cache. But 844 // allocating slots only for those would make the slot index 845 // computations much more complicated, so let's do this the simple 846 // way for now. 847 DEFINE_XRAY_EXPANDO_CLASS_WITH_OPS(static, sXrayExpandoObjectClass, ${memberSlots}, 848 ${ops}); 849 """, 850 memberSlots=self.descriptor.interface.totalMembersInSlots, 851 ops=ops, 852 ) 853 854 855 def PrototypeIDAndDepth(descriptor): 856 prototypeID = "prototypes::id::" 857 if descriptor.interface.hasInterfacePrototypeObject(): 858 prototypeID += descriptor.interface.identifier.name 859 depth = "PrototypeTraits<%s>::Depth" % prototypeID 860 else: 861 prototypeID += "_ID_Count" 862 depth = "0" 863 return (prototypeID, depth) 864 865 866 def InterfacePrototypeObjectProtoGetter(descriptor): 867 """ 868 Returns a tuple with two elements: 869 870 1) The name of the function to call to get the prototype to use for the 871 interface prototype object as a JSObject*. 872 873 2) The name of the function to call to get the prototype to use for the 874 interface prototype object as a JS::Handle<JSObject*> or None if no 875 such function exists. 876 """ 877 parentProtoName = descriptor.parentPrototypeName 878 if descriptor.hasNamedPropertiesObject: 879 protoGetter = "GetNamedPropertiesObject" 880 protoHandleGetter = None 881 elif parentProtoName is None: 882 protoHandleGetter = None 883 if descriptor.interface.getExtendedAttribute("ExceptionClass"): 884 protoGetter = "JS::GetRealmErrorPrototype" 885 elif descriptor.interface.isIteratorInterface(): 886 protoGetter = "JS::GetRealmIteratorPrototype" 887 elif descriptor.interface.isAsyncIteratorInterface(): 888 protoGetter = "JS::GetRealmAsyncIteratorPrototype" 889 else: 890 protoGetter = "JS::GetRealmObjectPrototype" 891 protoHandleGetter = "JS::GetRealmObjectPrototypeHandle" 892 else: 893 prefix = toBindingNamespace(parentProtoName) 894 protoGetter = prefix + "::GetProtoObject" 895 protoHandleGetter = prefix + "::GetProtoObjectHandle" 896 897 return (protoGetter, protoHandleGetter) 898 899 900 class CGPrototypeJSClass(CGThing): 901 def __init__(self, descriptor, properties): 902 CGThing.__init__(self) 903 self.descriptor = descriptor 904 self.properties = properties 905 906 def declare(self): 907 # We're purely for internal consumption 908 return "" 909 910 def define(self): 911 prototypeID, depth = PrototypeIDAndDepth(self.descriptor) 912 slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE" 913 # Globals handle unforgeables directly in Wrap() instead of 914 # via a holder. 915 if ( 916 self.descriptor.hasLegacyUnforgeableMembers 917 and not self.descriptor.isGlobal() 918 ): 919 slotCount += ( 920 " + 1 /* slot for the JSObject holding the unforgeable properties */" 921 ) 922 (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor) 923 type = ( 924 "eGlobalInterfacePrototype" 925 if self.descriptor.isGlobal() 926 else "eInterfacePrototype" 927 ) 928 return fill( 929 """ 930 static const DOMIfaceAndProtoJSClass sPrototypeClass = { 931 { 932 "${name}Prototype", 933 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}), 934 JS_NULL_CLASS_OPS, 935 JS_NULL_CLASS_SPEC, 936 JS_NULL_CLASS_EXT, 937 JS_NULL_OBJECT_OPS 938 }, 939 ${type}, 940 ${prototypeID}, 941 ${depth}, 942 ${hooks}, 943 ${protoGetter} 944 }; 945 """, 946 name=self.descriptor.interface.getClassName(), 947 slotCount=slotCount, 948 type=type, 949 hooks=NativePropertyHooks(self.descriptor), 950 prototypeID=prototypeID, 951 depth=depth, 952 protoGetter=protoGetter, 953 ) 954 955 956 def InterfaceObjectProtoGetter(descriptor): 957 """ 958 Returns the name of the function to call to get the prototype to use for the 959 interface object's prototype as a JS::Handle<JSObject*>. 960 """ 961 assert not descriptor.interface.isNamespace() 962 parentInterface = descriptor.interface.parent 963 if parentInterface: 964 parentIfaceName = parentInterface.identifier.name 965 parentDesc = descriptor.getDescriptor(parentIfaceName) 966 prefix = toBindingNamespace(parentDesc.name) 967 protoHandleGetter = prefix + "::GetConstructorObjectHandle" 968 else: 969 protoHandleGetter = "JS::GetRealmFunctionPrototypeHandle" 970 return protoHandleGetter 971 972 973 class CGNamespaceObjectJSClass(CGThing): 974 def __init__(self, descriptor): 975 CGThing.__init__(self) 976 self.descriptor = descriptor 977 978 def declare(self): 979 # We're purely for internal consumption 980 return "" 981 982 def define(self): 983 classString = self.descriptor.interface.getExtendedAttribute("ClassString") 984 if classString is None: 985 classString = self.descriptor.interface.identifier.name 986 else: 987 classString = classString[0] 988 return fill( 989 """ 990 static const DOMIfaceAndProtoJSClass sNamespaceObjectClass = { 991 { 992 "${classString}", 993 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS, 994 JS_NULL_CLASS_OPS, 995 JS_NULL_CLASS_SPEC, 996 JS_NULL_CLASS_EXT, 997 JS_NULL_OBJECT_OPS 998 }, 999 eNamespace, 1000 prototypes::id::_ID_Count, 1001 0, 1002 ${hooks}, 1003 // This isn't strictly following the spec (see 1004 // https://console.spec.whatwg.org/#ref-for-dfn-namespace-object), 1005 // but should be ok for Xrays. 1006 JS::GetRealmObjectPrototype 1007 }; 1008 """, 1009 classString=classString, 1010 hooks=NativePropertyHooks(self.descriptor), 1011 ) 1012 1013 1014 class CGInterfaceObjectInfo(CGThing): 1015 def __init__(self, descriptor): 1016 CGThing.__init__(self) 1017 self.descriptor = descriptor 1018 1019 def declare(self): 1020 # We're purely for internal consumption 1021 return "" 1022 1023 def define(self): 1024 if self.descriptor.interface.ctor(): 1025 ctorname = CONSTRUCT_HOOK_NAME 1026 constructorArgs = methodLength(self.descriptor.interface.ctor()) 1027 else: 1028 ctorname = "ThrowingConstructor" 1029 constructorArgs = 0 1030 constructorName = self.descriptor.interface.getClassName() 1031 wantsIsInstance = self.descriptor.interface.hasInterfacePrototypeObject() 1032 1033 prototypeID, depth = PrototypeIDAndDepth(self.descriptor) 1034 protoHandleGetter = InterfaceObjectProtoGetter(self.descriptor) 1035 1036 return fill( 1037 """ 1038 static const DOMInterfaceInfo sInterfaceObjectInfo = { 1039 { ${ctorname}, ${hooks} }, 1040 ${protoHandleGetter}, 1041 ${depth}, 1042 ${prototypeID}, 1043 ${wantsIsInstance}, 1044 ${constructorArgs}, 1045 "${constructorName}", 1046 }; 1047 """, 1048 ctorname=ctorname, 1049 hooks=NativePropertyHooks(self.descriptor), 1050 protoHandleGetter=protoHandleGetter, 1051 depth=depth, 1052 prototypeID=prototypeID, 1053 wantsIsInstance=toStringBool(wantsIsInstance), 1054 constructorArgs=constructorArgs, 1055 constructorName=constructorName, 1056 ) 1057 1058 1059 class CGList(CGThing): 1060 """ 1061 Generate code for a list of GCThings. Just concatenates them together, with 1062 an optional joiner string. "\n" is a common joiner. 1063 """ 1064 1065 def __init__(self, children, joiner=""): 1066 CGThing.__init__(self) 1067 # Make a copy of the kids into a list, because if someone passes in a 1068 # generator we won't be able to both declare and define ourselves, or 1069 # define ourselves more than once! 1070 self.children = list(children) 1071 self.joiner = joiner 1072 1073 def append(self, child): 1074 self.children.append(child) 1075 1076 def prepend(self, child): 1077 self.children.insert(0, child) 1078 1079 def extend(self, kids): 1080 self.children.extend(kids) 1081 1082 def join(self, iterable): 1083 return self.joiner.join(s for s in iterable if len(s) > 0) 1084 1085 def declare(self): 1086 return self.join( 1087 child.declare() for child in self.children if child is not None 1088 ) 1089 1090 def define(self): 1091 return self.join(child.define() for child in self.children if child is not None) 1092 1093 def forward_declare(self): 1094 return self.join( 1095 child.forward_declare() for child in self.children if child is not None 1096 ) 1097 1098 def deps(self): 1099 deps = set() 1100 for child in self.children: 1101 if child is None: 1102 continue 1103 deps = deps.union(child.deps()) 1104 return deps 1105 1106 def __len__(self): 1107 return len(self.children) 1108 1109 1110 class CGGeneric(CGThing): 1111 """ 1112 A class that spits out a fixed string into the codegen. Can spit out a 1113 separate string for the declaration too. 1114 """ 1115 1116 def __init__(self, define="", declare="", forward_declare=""): 1117 self.declareText = declare 1118 self.defineText = define 1119 self.forwardDeclareText = forward_declare 1120 1121 def declare(self): 1122 return self.declareText 1123 1124 def define(self): 1125 return self.defineText 1126 1127 def forward_declare(self): 1128 return self.forwardDeclareText 1129 1130 def deps(self): 1131 return set() 1132 1133 1134 class CGIndenter(CGThing): 1135 """ 1136 A class that takes another CGThing and generates code that indents that 1137 CGThing by some number of spaces. The default indent is two spaces. 1138 """ 1139 1140 def __init__(self, child, indentLevel=2, declareOnly=False): 1141 assert isinstance(child, CGThing) 1142 CGThing.__init__(self) 1143 self.child = child 1144 self.indentLevel = indentLevel 1145 self.declareOnly = declareOnly 1146 1147 def declare(self): 1148 return indent(self.child.declare(), self.indentLevel) 1149 1150 def define(self): 1151 defn = self.child.define() 1152 if self.declareOnly: 1153 return defn 1154 else: 1155 return indent(defn, self.indentLevel) 1156 1157 1158 class CGWrapper(CGThing): 1159 """ 1160 Generic CGThing that wraps other CGThings with pre and post text. 1161 """ 1162 1163 def __init__( 1164 self, 1165 child: CGThing, 1166 pre="", 1167 post="", 1168 declarePre=None, 1169 declarePost=None, 1170 definePre=None, 1171 definePost=None, 1172 forwardDeclarePre=None, 1173 forwardDeclarePost=None, 1174 declareOnly=False, 1175 defineOnly=False, 1176 reindent=False, 1177 ): 1178 CGThing.__init__(self) 1179 self.child = child 1180 self.declarePre = declarePre or pre 1181 self.declarePost = declarePost or post 1182 self.definePre = definePre or pre 1183 self.definePost = definePost or post 1184 self.forwardDeclarePre = forwardDeclarePre or pre 1185 self.forwardDeclarePost = forwardDeclarePost or post 1186 self.declareOnly = declareOnly 1187 self.defineOnly = defineOnly 1188 self.reindent = reindent 1189 1190 def declare(self): 1191 if self.defineOnly: 1192 return "" 1193 decl = self.child.declare() 1194 if self.reindent: 1195 decl = self.reindentString(decl, self.declarePre) 1196 return self.declarePre + decl + self.declarePost 1197 1198 def define(self): 1199 if self.declareOnly: 1200 return "" 1201 defn = self.child.define() 1202 if self.reindent: 1203 defn = self.reindentString(defn, self.definePre) 1204 return self.definePre + defn + self.definePost 1205 1206 def forward_declare(self): 1207 if self.defineOnly: 1208 return "" 1209 decl = self.child.forward_declare() 1210 if self.reindent: 1211 decl = self.reindentString(decl, self.forwardDeclarePre) 1212 return self.forwardDeclarePre + decl + self.forwardDeclarePost 1213 1214 @staticmethod 1215 def reindentString(stringToIndent, widthString): 1216 # We don't use lineStartDetector because we don't want to 1217 # insert whitespace at the beginning of our _first_ line. 1218 # Use the length of the last line of width string, in case 1219 # it is a multiline string. 1220 lastLineWidth = len(widthString.splitlines()[-1]) 1221 return stripTrailingWhitespace( 1222 stringToIndent.replace("\n", "\n" + (" " * lastLineWidth)) 1223 ) 1224 1225 def deps(self): 1226 return self.child.deps() 1227 1228 1229 class CGIfWrapper(CGList): 1230 def __init__(self, child, condition): 1231 CGList.__init__( 1232 self, 1233 [ 1234 CGWrapper( 1235 CGGeneric(condition), pre="if (", post=") {\n", reindent=True 1236 ), 1237 CGIndenter(child), 1238 CGGeneric("}\n"), 1239 ], 1240 ) 1241 1242 1243 class CGIfElseWrapper(CGList): 1244 def __init__(self, condition, ifTrue, ifFalse): 1245 CGList.__init__( 1246 self, 1247 [ 1248 CGWrapper( 1249 CGGeneric(condition), pre="if (", post=") {\n", reindent=True 1250 ), 1251 CGIndenter(ifTrue), 1252 CGGeneric("} else {\n"), 1253 CGIndenter(ifFalse), 1254 CGGeneric("}\n"), 1255 ], 1256 ) 1257 1258 1259 class CGElseChain(CGThing): 1260 """ 1261 Concatenate if statements in an if-else-if-else chain. 1262 """ 1263 1264 def __init__(self, children): 1265 self.children = [c for c in children if c is not None] 1266 1267 def declare(self): 1268 assert False 1269 1270 def define(self): 1271 if not self.children: 1272 return "" 1273 s = self.children[0].define() 1274 assert s.endswith("\n") 1275 for child in self.children[1:]: 1276 code = child.define() 1277 assert code.startswith("if") or code.startswith("{") 1278 assert code.endswith("\n") 1279 s = s.rstrip() + " else " + code 1280 return s 1281 1282 1283 class CGTemplatedType(CGWrapper): 1284 def __init__(self, templateName, child, isConst=False, isReference=False): 1285 if isinstance(child, list): 1286 child = CGList(child, ", ") 1287 const = "const " if isConst else "" 1288 pre = "%s%s<" % (const, templateName) 1289 ref = "&" if isReference else "" 1290 post = ">%s" % ref 1291 CGWrapper.__init__(self, child, pre=pre, post=post) 1292 1293 1294 class CGNamespace(CGThing): 1295 """ 1296 Generates namespace block that wraps other CGThings. 1297 """ 1298 1299 def __init__(self, namespace, child): 1300 CGThing.__init__(self) 1301 self.child = child 1302 self.pre = "namespace %s {\n" % namespace 1303 self.post = "} // namespace %s\n" % namespace 1304 1305 def declare(self): 1306 decl = self.child.declare() 1307 if len(decl.strip()) == 0: 1308 return "" 1309 return self.pre + decl + self.post 1310 1311 def define(self): 1312 defn = self.child.define() 1313 if len(defn.strip()) == 0: 1314 return "" 1315 return self.pre + defn + self.post 1316 1317 def forward_declare(self): 1318 decl = self.child.forward_declare() 1319 if len(decl.strip()) == 0: 1320 return "" 1321 return self.pre + decl + self.post 1322 1323 def deps(self): 1324 return self.child.deps() 1325 1326 @staticmethod 1327 def build(namespaces, child): 1328 """ 1329 Static helper method to build multiple wrapped namespaces. 1330 """ 1331 if not namespaces: 1332 return CGWrapper(child) 1333 return CGNamespace("::".join(namespaces), child) 1334 1335 1336 class CGIncludeGuard(CGWrapper): 1337 """ 1338 Generates include guards for a header. 1339 """ 1340 1341 def __init__(self, prefix, child): 1342 """|prefix| is the filename without the extension.""" 1343 define = "DOM_%s_H_" % prefix.upper() 1344 forward_define = "DOM_%sFWD_H_" % prefix.upper() 1345 CGWrapper.__init__( 1346 self, 1347 child, 1348 declarePre="#ifndef %s\n#define %s\n\n" % (define, define), 1349 declarePost="\n#endif // %s\n" % define, 1350 forwardDeclarePre="#ifndef %s\n#define %s\n\n" 1351 % (forward_define, forward_define), 1352 forwardDeclarePost="\n#endif // %s\n" % forward_define, 1353 ) 1354 1355 1356 class CGHeaders(CGWrapper): 1357 """ 1358 Generates the appropriate include statements. 1359 """ 1360 1361 def __init__( 1362 self, 1363 descriptors, 1364 dictionaries, 1365 callbacks, 1366 callbackDescriptors, 1367 declareIncludes, 1368 defineIncludes, 1369 prefix, 1370 child, 1371 config=None, 1372 jsImplementedDescriptors=[], 1373 ): 1374 """ 1375 Builds a set of includes to cover |descriptors|. 1376 1377 Also includes the files in |declareIncludes| in the header 1378 file and the files in |defineIncludes| in the .cpp. 1379 1380 |prefix| contains the basename of the file that we generate include 1381 statements for. 1382 """ 1383 1384 # Determine the filenames for which we need headers. 1385 interfaceDeps = [d.interface for d in descriptors] 1386 ancestors = [] 1387 for iface in interfaceDeps: 1388 if iface.parent: 1389 # We're going to need our parent's prototype, to use as the 1390 # prototype of our prototype object. 1391 ancestors.append(iface.parent) 1392 # And if we have an interface object, we'll need the nearest 1393 # ancestor with an interface object too, so we can use its 1394 # interface object as the proto of our interface object. 1395 if iface.hasInterfaceObject(): 1396 parent = iface.parent 1397 while parent and not parent.hasInterfaceObject(): 1398 parent = parent.parent 1399 if parent: 1400 ancestors.append(parent) 1401 interfaceDeps.extend(ancestors) 1402 1403 # Include parent interface headers needed for default toJSON code. 1404 jsonInterfaceParents = [] 1405 for desc in descriptors: 1406 if not desc.hasDefaultToJSON: 1407 continue 1408 parent = desc.interface.parent 1409 while parent: 1410 parentDesc = desc.getDescriptor(parent.identifier.name) 1411 if parentDesc.hasDefaultToJSON: 1412 jsonInterfaceParents.append(parentDesc.interface) 1413 parent = parent.parent 1414 interfaceDeps.extend(jsonInterfaceParents) 1415 1416 bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps) 1417 1418 # Grab all the implementation declaration files we need. 1419 implementationIncludes = set( 1420 d.headerFile for d in descriptors if d.needsHeaderInclude() 1421 ) 1422 1423 # Now find all the things we'll need as arguments because we 1424 # need to wrap or unwrap them. 1425 bindingHeaders = set() 1426 declareIncludes = set(declareIncludes) 1427 1428 def addHeadersForType(typeAndPossibleOriginType): 1429 """ 1430 Add the relevant headers for this type. We use its origin type, if 1431 passed, to decide what to do with interface types. 1432 """ 1433 t, originType = typeAndPossibleOriginType 1434 isFromDictionary = originType and originType.isDictionary() 1435 isFromCallback = originType and originType.isCallback() 1436 # Dictionaries have members that need to be actually 1437 # declared, not just forward-declared. 1438 # Callbacks have nullable union arguments that need to be actually 1439 # declared, not just forward-declared. 1440 if isFromDictionary: 1441 headerSet = declareIncludes 1442 elif isFromCallback and t.nullable() and t.isUnion(): 1443 headerSet = declareIncludes 1444 else: 1445 headerSet = bindingHeaders 1446 # Strip off outer layers and add headers they might require. (This 1447 # is conservative: only nullable non-pointer types need Nullable.h; 1448 # only sequences or observable arrays outside unions need 1449 # ForOfIterator.h; only functions that return, and attributes that 1450 # are, sequences or observable arrays in interfaces need Array.h, &c.) 1451 unrolled = t 1452 while True: 1453 if idlTypeNeedsCallContext(unrolled): 1454 bindingHeaders.add("mozilla/dom/BindingCallContext.h") 1455 if unrolled.nullable(): 1456 headerSet.add("mozilla/dom/Nullable.h") 1457 elif unrolled.isSequence() or unrolled.isObservableArray(): 1458 bindingHeaders.add("js/Array.h") 1459 bindingHeaders.add("js/ForOfIterator.h") 1460 if unrolled.isObservableArray(): 1461 bindingHeaders.add("mozilla/dom/ObservableArrayProxyHandler.h") 1462 else: 1463 break 1464 unrolled = unrolled.inner 1465 if unrolled.isUnion(): 1466 headerSet.add(self.getUnionDeclarationFilename(config, unrolled)) 1467 for t in unrolled.flatMemberTypes: 1468 addHeadersForType((t, None)) 1469 elif unrolled.isPromise(): 1470 # See comment in the isInterface() case for why we add 1471 # Promise.h to headerSet, not bindingHeaders. 1472 headerSet.add("mozilla/dom/Promise.h") 1473 # We need ToJSValue to do the Promise to JS conversion. 1474 bindingHeaders.add("mozilla/dom/ToJSValue.h") 1475 elif unrolled.isInterface(): 1476 if unrolled.isSpiderMonkeyInterface(): 1477 bindingHeaders.add("jsfriendapi.h") 1478 if jsImplementedDescriptors: 1479 # Since we can't forward-declare typed array types 1480 # (because they're typedefs), we have to go ahead and 1481 # just include their header if we need to have functions 1482 # taking references to them declared in that header. 1483 headerSet = declareIncludes 1484 headerSet.add("mozilla/dom/TypedArray.h") 1485 else: 1486 try: 1487 typeDesc = config.getDescriptor(unrolled.inner.identifier.name) 1488 except NoSuchDescriptorError: 1489 return 1490 # Dictionaries with interface members rely on the 1491 # actual class definition of that interface member 1492 # being visible in the binding header, because they 1493 # store them in RefPtr and have inline 1494 # constructors/destructors. 1495 # 1496 # XXXbz maybe dictionaries with interface members 1497 # should just have out-of-line constructors and 1498 # destructors? 1499 headerSet.add(typeDesc.headerFile) 1500 elif unrolled.isDictionary(): 1501 headerSet.add(self.getDeclarationFilename(unrolled.inner)) 1502 # And if it needs rooting, we need RootedDictionary too 1503 if typeNeedsRooting(unrolled): 1504 headerSet.add("mozilla/dom/RootedDictionary.h") 1505 elif unrolled.isCallback(): 1506 headerSet.add(self.getDeclarationFilename(unrolled.callback)) 1507 elif unrolled.isFloat() and not unrolled.isUnrestricted(): 1508 # Restricted floats are tested for finiteness 1509 bindingHeaders.add("mozilla/FloatingPoint.h") 1510 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h") 1511 elif unrolled.isEnum(): 1512 filename = self.getDeclarationFilename(unrolled.inner) 1513 declareIncludes.add(filename) 1514 elif unrolled.isPrimitive(): 1515 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h") 1516 elif unrolled.isRecord(): 1517 if isFromDictionary or jsImplementedDescriptors: 1518 declareIncludes.add("mozilla/dom/Record.h") 1519 else: 1520 bindingHeaders.add("mozilla/dom/Record.h") 1521 # Also add headers for the type the record is 1522 # parametrized over, if needed. 1523 addHeadersForType((t.inner, originType if isFromDictionary else None)) 1524 1525 for t in getAllTypes( 1526 descriptors + callbackDescriptors, dictionaries, callbacks 1527 ): 1528 addHeadersForType(t) 1529 1530 def addHeaderForFunc(func, desc): 1531 if func is None: 1532 return 1533 # Include the right class header, which we can only do 1534 # if this is a class member function. 1535 if desc is not None and not desc.headerIsDefault: 1536 # An explicit header file was provided, assume that we know 1537 # what we're doing. 1538 return 1539 1540 if "::" in func: 1541 # Strip out the function name and convert "::" to "/" 1542 bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h") 1543 1544 # Now for non-callback descriptors make sure we include any 1545 # headers needed by Func declarations and other things like that. 1546 for desc in descriptors: 1547 # If this is an iterator or an async iterator interface generated 1548 # for a separate iterable interface, skip generating type includes, 1549 # as we have what we need in IterableIterator.h 1550 if ( 1551 desc.interface.isIteratorInterface() 1552 or desc.interface.isAsyncIteratorInterface() 1553 ): 1554 continue 1555 1556 for m in desc.interface.members: 1557 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), desc) 1558 staticTypeOverride = PropertyDefiner.getStringAttr( 1559 m, "StaticClassOverride" 1560 ) 1561 if staticTypeOverride: 1562 bindingHeaders.add("/".join(staticTypeOverride.split("::")) + ".h") 1563 # getExtendedAttribute() returns a list, extract the entry. 1564 funcList = desc.interface.getExtendedAttribute("Func") 1565 if funcList is not None: 1566 addHeaderForFunc(funcList[0], desc) 1567 1568 if desc.interface.maplikeOrSetlikeOrIterable: 1569 # We need ToJSValue.h for maplike/setlike type conversions 1570 bindingHeaders.add("mozilla/dom/ToJSValue.h") 1571 # Add headers for the key and value types of the 1572 # maplike/setlike/iterable, since they'll be needed for 1573 # convenience functions 1574 if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType(): 1575 addHeadersForType( 1576 (desc.interface.maplikeOrSetlikeOrIterable.keyType, None) 1577 ) 1578 if desc.interface.maplikeOrSetlikeOrIterable.hasValueType(): 1579 addHeadersForType( 1580 (desc.interface.maplikeOrSetlikeOrIterable.valueType, None) 1581 ) 1582 1583 for d in dictionaries: 1584 if d.parent: 1585 declareIncludes.add(self.getDeclarationFilename(d.parent)) 1586 bindingHeaders.add(self.getDeclarationFilename(d)) 1587 for m in d.members: 1588 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), None) 1589 # No need to worry about Func on members of ancestors, because that 1590 # will happen automatically in whatever files those ancestors live 1591 # in. 1592 1593 for c in callbacks: 1594 bindingHeaders.add(self.getDeclarationFilename(c)) 1595 1596 for c in callbackDescriptors: 1597 bindingHeaders.add(self.getDeclarationFilename(c.interface)) 1598 1599 if len(callbacks) != 0: 1600 # We need CallbackFunction to serve as our parent class 1601 declareIncludes.add("mozilla/dom/CallbackFunction.h") 1602 # And we need ToJSValue.h so we can wrap "this" objects 1603 declareIncludes.add("mozilla/dom/ToJSValue.h") 1604 1605 if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0: 1606 # We need CallbackInterface to serve as our parent class 1607 declareIncludes.add("mozilla/dom/CallbackInterface.h") 1608 # And we need ToJSValue.h so we can wrap "this" objects 1609 declareIncludes.add("mozilla/dom/ToJSValue.h") 1610 1611 # Also need to include the headers for ancestors of 1612 # JS-implemented interfaces. 1613 for jsImplemented in jsImplementedDescriptors: 1614 jsParent = jsImplemented.interface.parent 1615 if jsParent: 1616 parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name) 1617 declareIncludes.add(parentDesc.jsImplParentHeader) 1618 1619 # Now make sure we're not trying to include the header from inside itself 1620 declareIncludes.discard(prefix + ".h") 1621 1622 # Let the machinery do its thing. 1623 def _includeString(includes): 1624 def headerName(include): 1625 # System headers are specified inside angle brackets. 1626 if include.startswith("<"): 1627 return include 1628 # Non-system headers need to be placed in quotes. 1629 return '"%s"' % include 1630 1631 return "".join(["#include %s\n" % headerName(i) for i in includes]) + "\n" 1632 1633 CGWrapper.__init__( 1634 self, 1635 child, 1636 declarePre=_includeString(sorted(declareIncludes)), 1637 definePre=_includeString( 1638 sorted( 1639 set(defineIncludes) 1640 | bindingIncludes 1641 | bindingHeaders 1642 | implementationIncludes 1643 ) 1644 ), 1645 ) 1646 1647 @staticmethod 1648 def getDeclarationFilename(decl): 1649 # Use our local version of the header, not the exported one, so that 1650 # test bindings, which don't export, will work correctly. 1651 basename = os.path.basename(decl.filename) 1652 return basename.replace(".webidl", "Binding.h") 1653 1654 @staticmethod 1655 def getUnionDeclarationFilename(config, unionType): 1656 assert unionType.isUnion() 1657 assert unionType.unroll() == unionType 1658 # If a union is "defined" in multiple files, it goes in UnionTypes.h. 1659 if len(config.filenamesPerUnion[unionType.name]) > 1: 1660 return "mozilla/dom/UnionTypes.h" 1661 # If a union is defined by a built-in typedef, it also goes in 1662 # UnionTypes.h. 1663 assert len(config.filenamesPerUnion[unionType.name]) == 1 1664 if "<unknown>" in config.filenamesPerUnion[unionType.name]: 1665 return "mozilla/dom/UnionTypes.h" 1666 return CGHeaders.getDeclarationFilename(unionType) 1667 1668 1669 def SortedDictValues(d): 1670 """ 1671 Returns a list of values from the dict sorted by key. 1672 """ 1673 return [v for k, v in sorted(d.items())] 1674 1675 1676 def UnionsForFile(config, webIDLFile): 1677 """ 1678 Returns a list of union types for all union types that are only used in 1679 webIDLFile. If webIDLFile is None this will return the list of tuples for 1680 union types that are used in more than one WebIDL file. 1681 """ 1682 return config.unionsPerFilename.get(webIDLFile, []) 1683 1684 1685 def UnionTypes(unionTypes, config): 1686 """ 1687 The unionTypes argument should be a list of union types. This is typically 1688 the list generated by UnionsForFile. 1689 1690 Returns a tuple containing a set of header filenames to include in 1691 the header for the types in unionTypes, a set of header filenames to 1692 include in the implementation file for the types in unionTypes, a set 1693 of tuples containing a type declaration and a boolean if the type is a 1694 struct for member types of the union, a list of traverse methods, 1695 unlink methods and a list of union types. These last three lists only 1696 contain unique union types. 1697 """ 1698 1699 headers = set() 1700 implheaders = set() 1701 declarations = set() 1702 unionStructs = dict() 1703 traverseMethods = dict() 1704 unlinkMethods = dict() 1705 1706 for t in unionTypes: 1707 name = str(t) 1708 if name not in unionStructs: 1709 unionStructs[name] = t 1710 1711 def addHeadersForType(f): 1712 if f.nullable(): 1713 headers.add("mozilla/dom/Nullable.h") 1714 isSequence = f.isSequence() 1715 if isSequence: 1716 # Dealing with sequences requires for-of-compatible 1717 # iteration. 1718 implheaders.add("js/ForOfIterator.h") 1719 # Sequences can always throw "not an object" exceptions. 1720 implheaders.add("mozilla/dom/BindingCallContext.h") 1721 if typeNeedsRooting(f): 1722 headers.add("mozilla/dom/RootedSequence.h") 1723 f = f.unroll() 1724 if idlTypeNeedsCallContext(f): 1725 implheaders.add("mozilla/dom/BindingCallContext.h") 1726 if f.isPromise(): 1727 headers.add("mozilla/dom/Promise.h") 1728 # We need ToJSValue to do the Promise to JS conversion. 1729 headers.add("mozilla/dom/ToJSValue.h") 1730 elif f.isInterface(): 1731 if f.isSpiderMonkeyInterface(): 1732 headers.add("js/RootingAPI.h") 1733 headers.add("js/Value.h") 1734 headers.add("mozilla/dom/TypedArray.h") 1735 else: 1736 try: 1737 typeDesc = config.getDescriptor(f.inner.identifier.name) 1738 except NoSuchDescriptorError: 1739 return 1740 if typeDesc.interface.isCallback() or isSequence: 1741 # Callback interfaces always use strong refs, so 1742 # we need to include the right header to be able 1743 # to Release() in our inlined code. 1744 # 1745 # Similarly, sequences always contain strong 1746 # refs, so we'll need the header to handler 1747 # those. 1748 headers.add(typeDesc.headerFile) 1749 elif typeDesc.interface.identifier.name == "WindowProxy": 1750 # In UnionTypes.h we need to see the declaration of the 1751 # WindowProxyHolder that we use to store the WindowProxy, so 1752 # we have its sizeof and know how big to make our union. 1753 headers.add(typeDesc.headerFile) 1754 else: 1755 declarations.add((typeDesc.nativeType, False)) 1756 implheaders.add(typeDesc.headerFile) 1757 elif f.isDictionary(): 1758 # For a dictionary, we need to see its declaration in 1759 # UnionTypes.h so we have its sizeof and know how big to 1760 # make our union. 1761 headers.add(CGHeaders.getDeclarationFilename(f.inner)) 1762 # And if it needs rooting, we need RootedDictionary too 1763 if typeNeedsRooting(f): 1764 headers.add("mozilla/dom/RootedDictionary.h") 1765 elif f.isFloat() and not f.isUnrestricted(): 1766 # Restricted floats are tested for finiteness 1767 implheaders.add("mozilla/FloatingPoint.h") 1768 implheaders.add("mozilla/dom/PrimitiveConversions.h") 1769 elif f.isEnum(): 1770 # Need to see the actual definition of the enum, 1771 # unfortunately. 1772 headers.add(CGHeaders.getDeclarationFilename(f.inner)) 1773 elif f.isPrimitive(): 1774 implheaders.add("mozilla/dom/PrimitiveConversions.h") 1775 elif f.isCallback(): 1776 # Callbacks always use strong refs, so we need to include 1777 # the right header to be able to Release() in our inlined 1778 # code. 1779 headers.add(CGHeaders.getDeclarationFilename(f.callback)) 1780 elif f.isRecord(): 1781 headers.add("mozilla/dom/Record.h") 1782 # And add headers for the type we're parametrized over 1783 addHeadersForType(f.inner) 1784 # And if it needs rooting, we need RootedRecord too 1785 if typeNeedsRooting(f): 1786 headers.add("mozilla/dom/RootedRecord.h") 1787 1788 implheaders.add(CGHeaders.getUnionDeclarationFilename(config, t)) 1789 for f in t.flatMemberTypes: 1790 assert not f.nullable() 1791 addHeadersForType(f) 1792 1793 if idlTypeNeedsCycleCollection(t): 1794 declarations.add( 1795 ("mozilla::dom::%s" % CGUnionStruct.unionTypeName(t, True), False) 1796 ) 1797 traverseMethods[name] = CGCycleCollectionTraverseForOwningUnionMethod(t) 1798 unlinkMethods[name] = CGCycleCollectionUnlinkForOwningUnionMethod(t) 1799 1800 # The order of items in CGList is important. 1801 # Since the union structs friend the unlinkMethods, the forward-declaration 1802 # for these methods should come before the class declaration. Otherwise 1803 # some compilers treat the friend declaration as a forward-declaration in 1804 # the class scope. 1805 return ( 1806 headers, 1807 implheaders, 1808 declarations, 1809 SortedDictValues(traverseMethods), 1810 SortedDictValues(unlinkMethods), 1811 SortedDictValues(unionStructs), 1812 ) 1813 1814 1815 class Argument: 1816 """ 1817 A class for outputting the type and name of an argument 1818 """ 1819 1820 def __init__(self, argType, name, default=None): 1821 self.argType = argType 1822 self.name = name 1823 self.default = default 1824 1825 def declare(self): 1826 string = self.argType + " " + self.name 1827 if self.default is not None: 1828 string += " = " + self.default 1829 return string 1830 1831 def define(self): 1832 return self.argType + " " + self.name 1833 1834 1835 class CGAbstractMethod(CGThing): 1836 """ 1837 An abstract class for generating code for a method. Subclasses 1838 should override definition_body to create the actual code. 1839 1840 descriptor is the descriptor for the interface the method is associated with 1841 1842 name is the name of the method as a string 1843 1844 returnType is the IDLType of the return value 1845 1846 args is a list of Argument objects 1847 1848 inline should be True to generate an inline method, whose body is 1849 part of the declaration. 1850 1851 alwaysInline should be True to generate an inline method annotated with 1852 MOZ_ALWAYS_INLINE. 1853 1854 static should be True to generate a static method, which only has 1855 a definition. 1856 1857 If templateArgs is not None it should be a list of strings containing 1858 template arguments, and the function will be templatized using those 1859 arguments. 1860 1861 canRunScript should be True to generate a MOZ_CAN_RUN_SCRIPT annotation. 1862 1863 signatureOnly should be True to only declare the signature (either in 1864 the header, or if static is True in the cpp file). 1865 """ 1866 1867 def __init__( 1868 self, 1869 descriptor, 1870 name, 1871 returnType, 1872 args, 1873 inline=False, 1874 alwaysInline=False, 1875 static=False, 1876 templateArgs=None, 1877 canRunScript=False, 1878 signatureOnly=False, 1879 ): 1880 CGThing.__init__(self) 1881 self.descriptor = descriptor 1882 self.name = name 1883 self.returnType = returnType 1884 self.args = args 1885 self.inline = inline 1886 self.alwaysInline = alwaysInline 1887 self.static = static 1888 self.templateArgs = templateArgs 1889 self.canRunScript = canRunScript 1890 self.signatureOnly = signatureOnly 1891 1892 def _argstring(self, declare): 1893 return ", ".join([a.declare() if declare else a.define() for a in self.args]) 1894 1895 def _template(self): 1896 if self.templateArgs is None: 1897 return "" 1898 return "template <%s>\n" % ", ".join(self.templateArgs) 1899 1900 def _decorators(self): 1901 decorators = [] 1902 if self.canRunScript: 1903 decorators.append("MOZ_CAN_RUN_SCRIPT") 1904 if self.alwaysInline: 1905 decorators.append("MOZ_ALWAYS_INLINE") 1906 elif self.inline: 1907 decorators.append("inline") 1908 if self.static: 1909 decorators.append("static") 1910 decorators.append(self.returnType) 1911 maybeNewline = " " if self.inline else "\n" 1912 return " ".join(decorators) + maybeNewline 1913 1914 def signature(self): 1915 return "%s%s%s(%s);\n" % ( 1916 self._template(), 1917 self._decorators(), 1918 self.name, 1919 self._argstring(True), 1920 ) 1921 1922 def declare(self): 1923 if self.static: 1924 return "" 1925 if self.inline: 1926 return self._define(True) 1927 return self.signature() 1928 1929 def indent_body(self, body): 1930 """ 1931 Indent the code returned by self.definition_body(). Most classes 1932 simply indent everything two spaces. This is here for 1933 CGRegisterProtos, which needs custom indentation. 1934 """ 1935 return indent(body) 1936 1937 def _define(self, fromDeclare=False): 1938 return ( 1939 self.definition_prologue(fromDeclare) 1940 + self.indent_body(self.definition_body()) 1941 + self.definition_epilogue() 1942 ) 1943 1944 def define(self): 1945 if self.signatureOnly: 1946 if self.static: 1947 # self.static makes us not output anything in the header, so output the signature here. 1948 return self.signature() 1949 return "" 1950 return "" if (self.inline and not self.static) else self._define() 1951 1952 def definition_prologue(self, fromDeclare): 1953 error_reporting_label = self.error_reporting_label() 1954 if error_reporting_label: 1955 # We're going to want a BindingCallContext. Rename our JSContext* 1956 # arg accordingly. 1957 i = 0 1958 while i < len(self.args): 1959 arg = self.args[i] 1960 if arg.argType == "JSContext*": 1961 cxname = arg.name 1962 self.args[i] = Argument(arg.argType, "cx_", arg.default) 1963 break 1964 i += 1 1965 if i == len(self.args): 1966 raise TypeError("Must have a JSContext* to create a BindingCallContext") 1967 1968 prologue = "%s%s%s(%s)\n{\n" % ( 1969 self._template(), 1970 self._decorators(), 1971 self.name, 1972 self._argstring(fromDeclare), 1973 ) 1974 if error_reporting_label: 1975 prologue += indent( 1976 fill( 1977 """ 1978 BindingCallContext ${cxname}(cx_, ${label}); 1979 """, 1980 cxname=cxname, 1981 label=error_reporting_label, 1982 ) 1983 ) 1984 1985 profiler_label = self.auto_profiler_label() 1986 if profiler_label: 1987 prologue += indent(profiler_label) + "\n" 1988 1989 return prologue 1990 1991 def definition_epilogue(self): 1992 return "}\n" 1993 1994 def definition_body(self): 1995 assert False # Override me! 1996 1997 """ 1998 Override this method to return a pair of (descriptive string, name of a 1999 JSContext* variable) in order to generate a profiler label for this method. 2000 """ 2001 2002 def auto_profiler_label(self): 2003 return None # Override me! 2004 2005 """ 2006 Override this method to return a string to be used as the label for a 2007 BindingCallContext. If this does not return None, one of the arguments of 2008 this method must be of type 'JSContext*'. Its name will be replaced with 2009 'cx_' and a BindingCallContext named 'cx' will be instantiated with the 2010 given label. 2011 """ 2012 2013 def error_reporting_label(self): 2014 return None # Override me! 2015 2016 2017 class CGAbstractStaticMethod(CGAbstractMethod): 2018 """ 2019 Abstract base class for codegen of implementation-only (no 2020 declaration) static methods. 2021 """ 2022 2023 def __init__(self, descriptor, name, returnType, args, canRunScript=False): 2024 CGAbstractMethod.__init__( 2025 self, 2026 descriptor, 2027 name, 2028 returnType, 2029 args, 2030 inline=False, 2031 static=True, 2032 canRunScript=canRunScript, 2033 ) 2034 2035 2036 class CGAbstractClassHook(CGAbstractStaticMethod): 2037 """ 2038 Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw 2039 'this' unwrapping as it assumes that the unwrapped type is always known. 2040 """ 2041 2042 def __init__(self, descriptor, name, returnType, args): 2043 CGAbstractStaticMethod.__init__(self, descriptor, name, returnType, args) 2044 2045 def definition_body_prologue(self): 2046 return "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" % ( 2047 self.descriptor.nativeType, 2048 self.descriptor.nativeType, 2049 ) 2050 2051 def definition_body(self): 2052 return self.definition_body_prologue() + self.generate_code() 2053 2054 def generate_code(self): 2055 assert False # Override me! 2056 2057 2058 class CGGetWrapperCacheHook(CGAbstractClassHook): 2059 """ 2060 A hook for GetWrapperCache, used by HasReleasedWrapper to get the 2061 nsWrapperCache pointer for a non-nsISupports object. 2062 """ 2063 2064 def __init__(self, descriptor): 2065 args = [Argument("JS::Handle<JSObject*>", "obj")] 2066 CGAbstractClassHook.__init__( 2067 self, descriptor, GETWRAPPERCACHE_HOOK_NAME, "nsWrapperCache*", args 2068 ) 2069 2070 def generate_code(self): 2071 assert self.descriptor.wrapperCache 2072 return dedent( 2073 """ 2074 return self; 2075 """ 2076 ) 2077 2078 2079 class CGDefineHTMLAttributeSlots(CGThing): 2080 """ 2081 Function to get the slots object for reflected HTML attributes that return 2082 a FrozenArray<Element> value. 2083 """ 2084 2085 def __init__(self, descriptor): 2086 self.descriptor = descriptor 2087 CGThing.__init__(self) 2088 2089 def declare(self): 2090 atts = self.descriptor.interface.reflectedHTMLAttributesReturningFrozenArray 2091 return fill( 2092 """ 2093 using ReflectedHTMLAttributeSlots = binding_detail::ReflectedHTMLAttributeSlots<${slotIndex}, ${xraySlotIndex}, ${arrayLength}>; 2094 """, 2095 slotIndex=reservedSlot(atts.slotIndex, False), 2096 xraySlotIndex=reservedSlot(atts.slotIndex, True), 2097 arrayLength=atts.totalMembersInSlots, 2098 ) 2099 2100 def define(self): 2101 return "" 2102 2103 2104 def finalizeHook(descriptor, gcx, obj): 2105 finalize = "JS::SetReservedSlot(%s, DOM_OBJECT_SLOT, JS::UndefinedValue());\n" % obj 2106 if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"): 2107 finalize += fill( 2108 """ 2109 // Either our proxy created an expando object or not. If it did, 2110 // then we would have preserved ourselves, and hence if we're going 2111 // away so is our C++ object and we should reset its expando value. 2112 // It's possible that in this situation the C++ object's reflector 2113 // pointer has been nulled out, but if not it's pointing to us. If 2114 // our proxy did _not_ create an expando object then it's possible 2115 // that we're no longer the reflector for our C++ object (and 2116 // incremental finalization is finally getting to us), and that in 2117 // the meantime the new reflector has created an expando object. 2118 // In that case we do NOT want to clear the expando pointer in the 2119 // C++ object. 2120 // 2121 // It's important to do this before we ClearWrapper, of course. 2122 JSObject* reflector = self->GetWrapperMaybeDead(); 2123 if (!reflector || reflector == ${obj}) { 2124 self->mExpandoAndGeneration.expando = JS::UndefinedValue(); 2125 } 2126 """, 2127 obj=obj, 2128 ) 2129 for m in descriptor.interface.members: 2130 if m.isAttr() and m.type.isObservableArray(): 2131 finalize += fill( 2132 """ 2133 { 2134 JS::Value val = JS::GetReservedSlot(obj, ${slot}); 2135 if (!val.isUndefined()) { 2136 JSObject* obj = &val.toObject(); 2137 js::SetProxyReservedSlot(obj, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT, JS::UndefinedValue()); 2138 } 2139 } 2140 """, 2141 slot=memberReservedSlot(m, descriptor), 2142 ) 2143 iface = getReflectedHTMLAttributesIface(descriptor) 2144 if iface: 2145 finalize += "%s::ReflectedHTMLAttributeSlots::Finalize(%s);\n" % ( 2146 toBindingNamespace(iface.identifier.name), 2147 obj, 2148 ) 2149 if descriptor.wrapperCache: 2150 finalize += "ClearWrapper(self, self, %s);\n" % obj 2151 if descriptor.isGlobal(): 2152 finalize += "mozilla::dom::FinalizeGlobal(%s, %s);\n" % (gcx, obj) 2153 finalize += fill( 2154 """ 2155 if (size_t mallocBytes = BindingJSObjectMallocBytes(self)) { 2156 JS::RemoveAssociatedMemory(${obj}, mallocBytes, 2157 JS::MemoryUse::DOMBinding); 2158 } 2159 """, 2160 obj=obj, 2161 ) 2162 finalize += "AddForDeferredFinalization<%s>(self);\n" % descriptor.nativeType 2163 return CGIfWrapper(CGGeneric(finalize), "self") 2164 2165 2166 class CGClassFinalizeHook(CGAbstractClassHook): 2167 """ 2168 A hook for finalize, used to release our native object. 2169 """ 2170 2171 def __init__(self, descriptor): 2172 args = [Argument("JS::GCContext*", "gcx"), Argument("JSObject*", "obj")] 2173 CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, "void", args) 2174 2175 def generate_code(self): 2176 return finalizeHook( 2177 self.descriptor, self.args[0].name, self.args[1].name 2178 ).define() 2179 2180 2181 def JSNativeArguments(): 2182 return [ 2183 Argument("JSContext*", "cx"), 2184 Argument("unsigned", "argc"), 2185 Argument("JS::Value*", "vp"), 2186 ] 2187 2188 2189 class CGClassConstructor(CGAbstractStaticMethod): 2190 """ 2191 JS-visible constructor for our objects 2192 """ 2193 2194 def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME): 2195 CGAbstractStaticMethod.__init__( 2196 self, descriptor, name, "bool", JSNativeArguments() 2197 ) 2198 self._ctor = ctor 2199 2200 def define(self): 2201 if not self._ctor: 2202 return "" 2203 return CGAbstractStaticMethod.define(self) 2204 2205 def definition_body(self): 2206 return self.generate_code() 2207 2208 def generate_code(self): 2209 if self._ctor.isHTMLConstructor(): 2210 # We better have a prototype object. Otherwise our proto 2211 # id won't make sense. 2212 assert self.descriptor.interface.hasInterfacePrototypeObject() 2213 # We also better have a constructor object, if this is 2214 # getting called! 2215 assert self.descriptor.interface.hasInterfaceObject() 2216 # We can't just pass null for the CreateInterfaceObjects callback, 2217 # because our newTarget might be in a different compartment, in 2218 # which case we'll need to look up constructor objects in that 2219 # compartment. 2220 return fill( 2221 """ 2222 return HTMLConstructor(cx, argc, vp, 2223 constructors::id::${name}, 2224 prototypes::id::${name}, 2225 CreateInterfaceObjects); 2226 """, 2227 name=self.descriptor.name, 2228 ) 2229 2230 # If the interface is already SecureContext, notify getConditionList to skip that check, 2231 # because the constructor won't be exposed in non-secure contexts to start with. 2232 alreadySecureContext = self.descriptor.interface.getExtendedAttribute( 2233 "SecureContext" 2234 ) 2235 2236 # We want to throw if any of the conditions returned by getConditionList are false. 2237 conditionsCheck = "" 2238 rawConditions = getRawConditionList( 2239 self._ctor, "cx", "obj", alreadySecureContext 2240 ) 2241 if len(rawConditions) > 0: 2242 notConditions = " ||\n".join("!" + cond for cond in rawConditions) 2243 failedCheckAction = CGGeneric("return ThrowingConstructor(cx, argc, vp);\n") 2244 conditionsCheck = ( 2245 CGIfWrapper(failedCheckAction, notConditions).define() + "\n" 2246 ) 2247 2248 # Additionally, we want to throw if a caller does a bareword invocation 2249 # of a constructor without |new|. 2250 ctorName = GetConstructorNameForReporting(self.descriptor, self._ctor) 2251 2252 preamble = fill( 2253 """ 2254 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 2255 JS::Rooted<JSObject*> obj(cx, &args.callee()); 2256 $*{conditionsCheck} 2257 if (!args.isConstructing()) { 2258 return ThrowConstructorWithoutNew(cx, "${ctorName}"); 2259 } 2260 2261 JS::Rooted<JSObject*> desiredProto(cx); 2262 if (!GetDesiredProto(cx, args, 2263 prototypes::id::${name}, 2264 CreateInterfaceObjects, 2265 &desiredProto)) { 2266 return false; 2267 } 2268 """, 2269 conditionsCheck=conditionsCheck, 2270 ctorName=ctorName, 2271 name=self.descriptor.name, 2272 ) 2273 2274 name = self._ctor.identifier.name 2275 nativeName = MakeNativeName(self.descriptor.binaryNameFor(name, True)) 2276 callGenerator = CGMethodCall( 2277 nativeName, True, self.descriptor, self._ctor, isConstructor=True 2278 ) 2279 return preamble + "\n" + callGenerator.define() 2280 2281 def auto_profiler_label(self): 2282 return fill( 2283 """ 2284 AUTO_PROFILER_LABEL_DYNAMIC_FAST( 2285 "${ctorName}", "constructor", DOM, cx, 2286 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); 2287 """, 2288 ctorName=GetConstructorNameForReporting(self.descriptor, self._ctor), 2289 ) 2290 2291 def error_reporting_label(self): 2292 return CGSpecializedMethod.error_reporting_label_helper( 2293 self.descriptor, self._ctor, isConstructor=True 2294 ) 2295 2296 2297 def LegacyFactoryFunctionName(m): 2298 return "_" + m.identifier.name 2299 2300 2301 class CGLegacyFactoryFunctions(CGThing): 2302 def __init__(self, descriptor): 2303 self.descriptor = descriptor 2304 CGThing.__init__(self) 2305 2306 def declare(self): 2307 return "" 2308 2309 def define(self): 2310 if len(self.descriptor.interface.legacyFactoryFunctions) == 0: 2311 return "" 2312 2313 constructorID = "constructors::id::" 2314 if self.descriptor.interface.hasInterfaceObject(): 2315 constructorID += self.descriptor.name 2316 else: 2317 constructorID += "_ID_Count" 2318 2319 legacyFactoryFunctions = "" 2320 for n in self.descriptor.interface.legacyFactoryFunctions: 2321 legacyFactoryFunctions += ( 2322 '{ "%s", { %s, &sLegacyFactoryFunctionNativePropertyHooks }, %i },\n' 2323 % (n.identifier.name, LegacyFactoryFunctionName(n), methodLength(n)) 2324 ) 2325 2326 return fill( 2327 """ 2328 bool sLegacyFactoryFunctionNativePropertiesInited = true; 2329 const NativePropertyHooks sLegacyFactoryFunctionNativePropertyHooks = { 2330 nullptr, 2331 { nullptr, nullptr, &sLegacyFactoryFunctionNativePropertiesInited }, 2332 prototypes::id::${name}, 2333 ${constructorID}, 2334 nullptr 2335 }; 2336 2337 static const LegacyFactoryFunction legacyFactoryFunctions[] = { 2338 $*{legacyFactoryFunctions} 2339 }; 2340 """, 2341 name=self.descriptor.name, 2342 constructorID=constructorID, 2343 legacyFactoryFunctions=legacyFactoryFunctions, 2344 ) 2345 2346 2347 def isChromeOnly(m): 2348 return m.getExtendedAttribute("ChromeOnly") 2349 2350 2351 def prefIdentifier(pref): 2352 return pref.replace(".", "_").replace("-", "_") 2353 2354 2355 def prefHeader(pref): 2356 return "mozilla/StaticPrefs_%s.h" % pref.partition(".")[0] 2357 2358 2359 def computeGlobalNamesFromExposureSet(exposureSet): 2360 assert exposureSet is None or isinstance(exposureSet, set) 2361 2362 if exposureSet: 2363 # Nonempty set 2364 return " | ".join(map(lambda g: "GlobalNames::%s" % g, sorted(exposureSet))) 2365 2366 return "0" 2367 2368 2369 class MemberCondition: 2370 """ 2371 An object representing the condition for a member to actually be 2372 exposed. Any of the arguments can be None. If not 2373 None, they should have the following types: 2374 2375 pref: The name of the preference. 2376 func: The name of the function. 2377 secureContext: A bool indicating whether a secure context is required. 2378 nonExposedGlobals: A set of names of globals. Can be empty, in which case 2379 it's treated the same way as None. 2380 trial: The name of the origin trial. 2381 """ 2382 2383 def __init__( 2384 self, 2385 pref=None, 2386 func=None, 2387 secureContext=False, 2388 nonExposedGlobals=None, 2389 trial=None, 2390 ): 2391 assert pref is None or isinstance(pref, str) 2392 assert func is None or isinstance(func, str) 2393 assert trial is None or isinstance(trial, str) 2394 assert isinstance(secureContext, bool) 2395 self.pref = pref 2396 if self.pref: 2397 identifier = prefIdentifier(self.pref) 2398 self.prefFuncIndex = "WebIDLPrefIndex::" + identifier 2399 else: 2400 self.prefFuncIndex = "WebIDLPrefIndex::NoPref" 2401 2402 self.secureContext = secureContext 2403 2404 def toFuncPtr(val): 2405 if val is None: 2406 return "nullptr" 2407 return "&" + val 2408 2409 self.func = toFuncPtr(func) 2410 2411 self.nonExposedGlobals = computeGlobalNamesFromExposureSet(nonExposedGlobals) 2412 2413 if trial: 2414 self.trial = "OriginTrial::" + trial 2415 else: 2416 self.trial = "OriginTrial(0)" 2417 2418 def __eq__(self, other): 2419 return ( 2420 self.pref == other.pref 2421 and self.func == other.func 2422 and self.secureContext == other.secureContext 2423 and self.nonExposedGlobals == other.nonExposedGlobals 2424 and self.trial == other.trial 2425 ) 2426 2427 def __ne__(self, other): 2428 return not self.__eq__(other) 2429 2430 def hasDisablers(self): 2431 return ( 2432 self.pref is not None 2433 or self.secureContext 2434 or self.func != "nullptr" 2435 or self.nonExposedGlobals != "0" 2436 or self.trial != "OriginTrial(0)" 2437 ) 2438 2439 2440 class PropertyDefiner: 2441 """ 2442 A common superclass for defining things on prototype objects. 2443 2444 Subclasses should implement generateArray to generate the actual arrays of 2445 things we're defining. They should also set self.chrome to the list of 2446 things only exposed to chrome and self.regular to the list of things exposed 2447 to both chrome and web pages. 2448 """ 2449 2450 def __init__(self, descriptor, name): 2451 self.descriptor = descriptor 2452 self.name = name 2453 2454 def hasChromeOnly(self): 2455 return len(self.chrome) > 0 2456 2457 def hasNonChromeOnly(self): 2458 return len(self.regular) > 0 2459 2460 def variableName(self, chrome): 2461 if chrome: 2462 if self.hasChromeOnly(): 2463 return "sChrome" + self.name 2464 else: 2465 if self.hasNonChromeOnly(): 2466 return "s" + self.name 2467 return "nullptr" 2468 2469 def usedForXrays(self): 2470 return self.descriptor.wantsXrays 2471 2472 def length(self, chrome): 2473 return len(self.chrome) if chrome else len(self.regular) 2474 2475 def __str__(self): 2476 # We only need to generate id arrays for things that will end 2477 # up used via ResolveProperty or EnumerateProperties. 2478 str = self.generateArray(self.regular, self.variableName(False)) 2479 if self.hasChromeOnly(): 2480 str += self.generateArray(self.chrome, self.variableName(True)) 2481 return str 2482 2483 @staticmethod 2484 def getStringAttr(member, name): 2485 attr = member.getExtendedAttribute(name) 2486 if attr is None: 2487 return None 2488 # It's a list of strings 2489 assert len(attr) == 1 2490 assert attr[0] is not None 2491 return attr[0] 2492 2493 @staticmethod 2494 def getControllingCondition(interfaceMember, descriptor): 2495 interface = descriptor.interface 2496 nonExposureSet = interface.exposureSet - interfaceMember.exposureSet 2497 2498 trial = PropertyDefiner.getStringAttr(interfaceMember, "Trial") 2499 if trial and interface.identifier.name in ["Window", "Document"]: 2500 raise TypeError( 2501 "[Trial] not yet supported for %s.%s, see bug 1757935" 2502 % (interface.identifier.name, interfaceMember.identifier.name) 2503 ) 2504 2505 return MemberCondition( 2506 PropertyDefiner.getStringAttr(interfaceMember, "Pref"), 2507 PropertyDefiner.getStringAttr(interfaceMember, "Func"), 2508 interfaceMember.getExtendedAttribute("SecureContext") is not None, 2509 nonExposureSet, 2510 trial, 2511 ) 2512 2513 @staticmethod 2514 def generatePrefableArrayValues( 2515 array, 2516 descriptor, 2517 specFormatter, 2518 specTerminator, 2519 getCondition, 2520 getDataTuple, 2521 switchToCondition=None, 2522 ): 2523 """ 2524 This method generates an array of spec entries for interface members. It returns 2525 a tuple containing the array of spec entries and the maximum of the number of 2526 spec entries per condition. 2527 2528 array is an array of interface members. 2529 2530 descriptor is the descriptor for the interface that array contains members of. 2531 2532 specFormatter is a function that takes a single argument, a tuple, 2533 and returns a string, a spec array entry. 2534 2535 specTerminator is a terminator for the spec array (inserted every time 2536 our controlling pref changes and at the end of the array). 2537 2538 getCondition is a callback function that takes an array entry and 2539 returns the corresponding MemberCondition. 2540 2541 getDataTuple is a callback function that takes an array entry and 2542 returns a tuple suitable to be passed to specFormatter. 2543 2544 switchToCondition is a function that takes a MemberCondition and an array of 2545 previously generated spec entries. If None is passed for this function then all 2546 the interface members should return the same value from getCondition. 2547 """ 2548 2549 def unsupportedSwitchToCondition(condition, specs): 2550 # If no specs have been added yet then this is just the first call to 2551 # switchToCondition that we call to avoid putting a specTerminator at the 2552 # front of the list. 2553 if len(specs) == 0: 2554 return 2555 raise "Not supported" 2556 2557 if switchToCondition is None: 2558 switchToCondition = unsupportedSwitchToCondition 2559 2560 specs = [] 2561 numSpecsInCurPrefable = 0 2562 maxNumSpecsInPrefable = 0 2563 2564 # So we won't put a specTerminator at the very front of the list: 2565 lastCondition = getCondition(array[0], descriptor) 2566 2567 switchToCondition(lastCondition, specs) 2568 2569 for member in array: 2570 curCondition = getCondition(member, descriptor) 2571 if lastCondition != curCondition: 2572 # Terminate previous list 2573 specs.append(specTerminator) 2574 if numSpecsInCurPrefable > maxNumSpecsInPrefable: 2575 maxNumSpecsInPrefable = numSpecsInCurPrefable 2576 numSpecsInCurPrefable = 0 2577 # And switch to our new condition 2578 switchToCondition(curCondition, specs) 2579 lastCondition = curCondition 2580 # And the actual spec 2581 specs.append(specFormatter(getDataTuple(member, descriptor))) 2582 numSpecsInCurPrefable += 1 2583 if numSpecsInCurPrefable > maxNumSpecsInPrefable: 2584 maxNumSpecsInPrefable = numSpecsInCurPrefable 2585 specs.append(specTerminator) 2586 2587 return (specs, maxNumSpecsInPrefable) 2588 2589 def generatePrefableArray( 2590 self, 2591 array, 2592 name, 2593 specFormatter, 2594 specTerminator, 2595 specType, 2596 getCondition, 2597 getDataTuple, 2598 ): 2599 """ 2600 This method generates our various arrays. 2601 2602 array is an array of interface members as passed to generateArray 2603 2604 name is the name as passed to generateArray 2605 2606 specFormatter is a function that takes a single argument, a tuple, 2607 and returns a string, a spec array entry 2608 2609 specTerminator is a terminator for the spec array (inserted every time 2610 our controlling pref changes and at the end of the array) 2611 2612 specType is the actual typename of our spec 2613 2614 getCondition is a callback function that takes an array entry and 2615 returns the corresponding MemberCondition. 2616 2617 getDataTuple is a callback function that takes an array entry and 2618 returns a tuple suitable to be passed to specFormatter. 2619 """ 2620 2621 # We want to generate a single list of specs, but with specTerminator 2622 # inserted at every point where the pref name controlling the member 2623 # changes. That will make sure the order of the properties as exposed 2624 # on the interface and interface prototype objects does not change when 2625 # pref control is added to members while still allowing us to define all 2626 # the members in the smallest number of JSAPI calls. 2627 assert len(array) != 0 2628 2629 disablers = [] 2630 prefableSpecs = [] 2631 2632 disablersTemplate = dedent( 2633 """ 2634 static const PrefableDisablers %s_disablers%d = { 2635 %s, %s, %s, %s, %s 2636 }; 2637 """ 2638 ) 2639 prefableWithDisablersTemplate = " { &%s_disablers%d, &%s_specs[%d] }" 2640 prefableWithoutDisablersTemplate = " { nullptr, &%s_specs[%d] }" 2641 2642 def switchToCondition(condition, specs): 2643 # Set up pointers to the new sets of specs inside prefableSpecs 2644 if condition.hasDisablers(): 2645 prefableSpecs.append( 2646 prefableWithDisablersTemplate % (name, len(specs), name, len(specs)) 2647 ) 2648 disablers.append( 2649 disablersTemplate 2650 % ( 2651 name, 2652 len(specs), 2653 condition.prefFuncIndex, 2654 condition.nonExposedGlobals, 2655 toStringBool(condition.secureContext), 2656 condition.trial, 2657 condition.func, 2658 ) 2659 ) 2660 else: 2661 prefableSpecs.append( 2662 prefableWithoutDisablersTemplate % (name, len(specs)) 2663 ) 2664 2665 specs, maxNumSpecsInPrefable = self.generatePrefableArrayValues( 2666 array, 2667 self.descriptor, 2668 specFormatter, 2669 specTerminator, 2670 getCondition, 2671 getDataTuple, 2672 switchToCondition, 2673 ) 2674 prefableSpecs.append(" { nullptr, nullptr }") 2675 2676 specType = "const " + specType 2677 arrays = fill( 2678 """ 2679 MOZ_GLOBINIT static ${specType} ${name}_specs[] = { 2680 ${specs} 2681 }; 2682 2683 ${disablers} 2684 static const Prefable<${specType}> ${name}[] = { 2685 ${prefableSpecs} 2686 }; 2687 2688 """, 2689 specType=specType, 2690 name=name, 2691 disablers="\n".join(disablers), 2692 specs=",\n".join(specs), 2693 prefableSpecs=",\n".join(prefableSpecs), 2694 ) 2695 2696 if self.usedForXrays(): 2697 arrays = fill( 2698 """ 2699 $*{arrays} 2700 static_assert(${numPrefableSpecs} <= 1ull << NUM_BITS_PROPERTY_INFO_PREF_INDEX, 2701 "We have a prefable index that is >= (1 << NUM_BITS_PROPERTY_INFO_PREF_INDEX)"); 2702 static_assert(${maxNumSpecsInPrefable} <= 1ull << NUM_BITS_PROPERTY_INFO_SPEC_INDEX, 2703 "We have a spec index that is >= (1 << NUM_BITS_PROPERTY_INFO_SPEC_INDEX)"); 2704 2705 """, 2706 arrays=arrays, 2707 # Minus 1 because there's a list terminator in prefableSpecs. 2708 numPrefableSpecs=len(prefableSpecs) - 1, 2709 maxNumSpecsInPrefable=maxNumSpecsInPrefable, 2710 ) 2711 2712 return arrays 2713 2714 2715 # The length of a method is the minimum of the lengths of the 2716 # argument lists of all its overloads. 2717 def overloadLength(arguments): 2718 i = len(arguments) 2719 while i > 0 and arguments[i - 1].optional: 2720 i -= 1 2721 return i 2722 2723 2724 def methodLength(method): 2725 signatures = method.signatures() 2726 return min(overloadLength(arguments) for retType, arguments in signatures) 2727 2728 2729 def clearableCachedAttrs(descriptor): 2730 return ( 2731 m 2732 for m in descriptor.interface.members 2733 if m.isAttr() and 2734 # Constants should never need clearing! 2735 m.dependsOn != "Nothing" and m.slotIndices is not None 2736 ) 2737 2738 2739 def MakeClearCachedValueNativeName(member): 2740 return "ClearCached%sValue" % MakeNativeName(member.identifier.name) 2741 2742 2743 def IDLToCIdentifier(name): 2744 return name.replace("-", "_") 2745 2746 2747 def EnumerabilityFlags(member): 2748 if member.getExtendedAttribute("NonEnumerable"): 2749 return "0" 2750 return "JSPROP_ENUMERATE" 2751 2752 2753 class MethodDefiner(PropertyDefiner): 2754 """ 2755 A class for defining methods on a prototype object. 2756 """ 2757 2758 def __init__(self, descriptor, name, crossOriginOnly, static, unforgeable=False): 2759 assert not (static and unforgeable) 2760 PropertyDefiner.__init__(self, descriptor, name) 2761 2762 # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822 2763 # We should be able to check for special operations without an 2764 # identifier. For now we check if the name starts with __ 2765 2766 # Ignore non-static methods for interfaces without a proto object 2767 if descriptor.interface.hasInterfacePrototypeObject() or static: 2768 methods = [ 2769 m 2770 for m in descriptor.interface.members 2771 if m.isMethod() 2772 and m.isStatic() == static 2773 and MemberIsLegacyUnforgeable(m, descriptor) == unforgeable 2774 and ( 2775 not crossOriginOnly or m.getExtendedAttribute("CrossOriginCallable") 2776 ) 2777 and not m.isIdentifierLess() 2778 and not m.getExtendedAttribute("Unexposed") 2779 ] 2780 else: 2781 methods = [] 2782 self.chrome = [] 2783 self.regular = [] 2784 for m in methods: 2785 method = self.methodData(m, descriptor) 2786 2787 if m.isStatic(): 2788 method["nativeName"] = CppKeywords.checkMethodName( 2789 IDLToCIdentifier(m.identifier.name) 2790 ) 2791 2792 if isChromeOnly(m): 2793 self.chrome.append(method) 2794 else: 2795 self.regular.append(method) 2796 2797 # TODO: Once iterable is implemented, use tiebreak rules instead of 2798 # failing. Also, may be more tiebreak rules to implement once spec bug 2799 # is resolved. 2800 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592 2801 def hasIterator(methods, regular): 2802 return any("@@iterator" in m.aliases for m in methods) or any( 2803 "@@iterator" == r["name"] for r in regular 2804 ) 2805 2806 # Check whether we need to output an @@iterator due to having an indexed 2807 # getter. We only do this while outputting non-static and 2808 # non-unforgeable methods, since the @@iterator function will be 2809 # neither. 2810 if not static and not unforgeable and descriptor.supportsIndexedProperties(): 2811 if hasIterator(methods, self.regular): 2812 raise TypeError( 2813 "Cannot have indexed getter/attr on " 2814 "interface %s with other members " 2815 "that generate @@iterator, such as " 2816 "maplike/setlike or aliased functions." 2817 % self.descriptor.interface.identifier.name 2818 ) 2819 self.regular.append( 2820 { 2821 "name": "@@iterator", 2822 "methodInfo": False, 2823 "selfHostedName": "$ArrayValues", 2824 "length": 0, 2825 "flags": "0", # Not enumerable, per spec. 2826 "condition": MemberCondition(), 2827 } 2828 ) 2829 2830 # Generate the keys/values/entries aliases for value iterables. 2831 maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable 2832 if ( 2833 not static 2834 and not unforgeable 2835 and maplikeOrSetlikeOrIterable 2836 and maplikeOrSetlikeOrIterable.isIterable() 2837 and maplikeOrSetlikeOrIterable.isValueIterator() 2838 ): 2839 # Add our keys/values/entries/forEach 2840 self.regular.append( 2841 { 2842 "name": "keys", 2843 "methodInfo": False, 2844 "selfHostedName": "ArrayKeys", 2845 "length": 0, 2846 "flags": "JSPROP_ENUMERATE", 2847 "condition": PropertyDefiner.getControllingCondition( 2848 maplikeOrSetlikeOrIterable, descriptor 2849 ), 2850 } 2851 ) 2852 self.regular.append( 2853 { 2854 "name": "values", 2855 "methodInfo": False, 2856 "selfHostedName": "$ArrayValues", 2857 "length": 0, 2858 "flags": "JSPROP_ENUMERATE", 2859 "condition": PropertyDefiner.getControllingCondition( 2860 maplikeOrSetlikeOrIterable, descriptor 2861 ), 2862 } 2863 ) 2864 self.regular.append( 2865 { 2866 "name": "entries", 2867 "methodInfo": False, 2868 "selfHostedName": "ArrayEntries", 2869 "length": 0, 2870 "flags": "JSPROP_ENUMERATE", 2871 "condition": PropertyDefiner.getControllingCondition( 2872 maplikeOrSetlikeOrIterable, descriptor 2873 ), 2874 } 2875 ) 2876 self.regular.append( 2877 { 2878 "name": "forEach", 2879 "methodInfo": False, 2880 "selfHostedName": "ArrayForEach", 2881 "length": 1, 2882 "flags": "JSPROP_ENUMERATE", 2883 "condition": PropertyDefiner.getControllingCondition( 2884 maplikeOrSetlikeOrIterable, descriptor 2885 ), 2886 } 2887 ) 2888 2889 if not static: 2890 stringifier = descriptor.operations["Stringifier"] 2891 if stringifier and unforgeable == MemberIsLegacyUnforgeable( 2892 stringifier, descriptor 2893 ): 2894 toStringDesc = { 2895 "name": GetWebExposedName(stringifier, descriptor), 2896 "nativeName": stringifier.identifier.name, 2897 "length": 0, 2898 "flags": "JSPROP_ENUMERATE", 2899 "condition": PropertyDefiner.getControllingCondition( 2900 stringifier, descriptor 2901 ), 2902 } 2903 if isChromeOnly(stringifier): 2904 self.chrome.append(toStringDesc) 2905 else: 2906 self.regular.append(toStringDesc) 2907 if unforgeable and descriptor.interface.getExtendedAttribute( 2908 "LegacyUnforgeable" 2909 ): 2910 # Synthesize our valueOf method 2911 self.regular.append( 2912 { 2913 "name": "valueOf", 2914 "selfHostedName": "Object_valueOf", 2915 "methodInfo": False, 2916 "length": 0, 2917 "flags": "0", # readonly/permanent added automatically. 2918 "condition": MemberCondition(), 2919 } 2920 ) 2921 2922 if descriptor.interface.isJSImplemented(): 2923 if static: 2924 if descriptor.interface.hasInterfaceObject(): 2925 self.chrome.append( 2926 { 2927 "name": "_create", 2928 "nativeName": ("%s::_Create" % descriptor.name), 2929 "methodInfo": False, 2930 "length": 2, 2931 "flags": "0", 2932 "condition": MemberCondition(), 2933 } 2934 ) 2935 2936 self.unforgeable = unforgeable 2937 2938 if static: 2939 if not descriptor.interface.hasInterfaceObject(): 2940 # static methods go on the interface object 2941 assert not self.hasChromeOnly() and not self.hasNonChromeOnly() 2942 else: 2943 if not descriptor.interface.hasInterfacePrototypeObject(): 2944 # non-static methods go on the interface prototype object 2945 assert not self.hasChromeOnly() and not self.hasNonChromeOnly() 2946 2947 @staticmethod 2948 def methodData(m, descriptor, overrideFlags=None): 2949 return { 2950 "name": m.identifier.name, 2951 "methodInfo": not m.isStatic(), 2952 "length": methodLength(m), 2953 "flags": ( 2954 EnumerabilityFlags(m) if (overrideFlags is None) else overrideFlags 2955 ), 2956 "condition": PropertyDefiner.getControllingCondition(m, descriptor), 2957 "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"), 2958 "returnsPromise": m.returnsPromise(), 2959 "hasIteratorAlias": "@@iterator" in m.aliases, 2960 } 2961 2962 @staticmethod 2963 def formatSpec(fields): 2964 if fields[0].startswith("@@"): 2965 fields = (fields[0][2:],) + fields[1:] 2966 return " JS_SYM_FNSPEC(%s, %s, %s, %s, %s, %s)" % fields 2967 return ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)' % fields 2968 2969 @staticmethod 2970 def specData(m, descriptor, unforgeable=False): 2971 def flags(m, unforgeable): 2972 unforgeable = " | JSPROP_PERMANENT | JSPROP_READONLY" if unforgeable else "" 2973 return m["flags"] + unforgeable 2974 2975 if "selfHostedName" in m: 2976 selfHostedName = '"%s"' % m["selfHostedName"] 2977 assert not m.get("methodInfo", True) 2978 accessor = "nullptr" 2979 jitinfo = "nullptr" 2980 else: 2981 selfHostedName = "nullptr" 2982 # When defining symbols, function name may not match symbol name 2983 methodName = m.get("methodName", m["name"]) 2984 accessor = m.get("nativeName", IDLToCIdentifier(methodName)) 2985 if m.get("methodInfo", True): 2986 if m.get("returnsPromise", False): 2987 exceptionPolicy = "ConvertExceptionsToPromises" 2988 else: 2989 exceptionPolicy = "ThrowExceptions" 2990 2991 # Cast this in case the methodInfo is a 2992 # JSTypedMethodJitInfo. 2993 jitinfo = ( 2994 "reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor 2995 ) 2996 if m.get("allowCrossOriginThis", False): 2997 accessor = ( 2998 "(GenericMethod<CrossOriginThisPolicy, %s>)" % exceptionPolicy 2999 ) 3000 elif descriptor.interface.hasDescendantWithCrossOriginMembers: 3001 accessor = ( 3002 "(GenericMethod<MaybeCrossOriginObjectThisPolicy, %s>)" 3003 % exceptionPolicy 3004 ) 3005 elif descriptor.interface.isOnGlobalProtoChain(): 3006 accessor = ( 3007 "(GenericMethod<MaybeGlobalThisPolicy, %s>)" % exceptionPolicy 3008 ) 3009 else: 3010 accessor = "(GenericMethod<NormalThisPolicy, %s>)" % exceptionPolicy 3011 else: 3012 if m.get("returnsPromise", False): 3013 jitinfo = "&%s_methodinfo" % accessor 3014 accessor = "StaticMethodPromiseWrapper" 3015 else: 3016 jitinfo = "nullptr" 3017 3018 return ( 3019 m["name"], 3020 accessor, 3021 jitinfo, 3022 m["length"], 3023 flags(m, unforgeable), 3024 selfHostedName, 3025 ) 3026 3027 @staticmethod 3028 def condition(m, d): 3029 return m["condition"] 3030 3031 def generateArray(self, array, name): 3032 if len(array) == 0: 3033 return "" 3034 3035 return self.generatePrefableArray( 3036 array, 3037 name, 3038 self.formatSpec, 3039 " JS_FS_END", 3040 "JSFunctionSpec", 3041 self.condition, 3042 functools.partial(self.specData, unforgeable=self.unforgeable), 3043 ) 3044 3045 3046 class AttrDefiner(PropertyDefiner): 3047 def __init__(self, descriptor, name, crossOriginOnly, static, unforgeable=False): 3048 assert not (static and unforgeable) 3049 PropertyDefiner.__init__(self, descriptor, name) 3050 self.name = name 3051 # Ignore non-static attributes for interfaces without a proto object 3052 if descriptor.interface.hasInterfacePrototypeObject() or static: 3053 idlAttrs = [ 3054 m 3055 for m in descriptor.interface.members 3056 if m.isAttr() 3057 and m.isStatic() == static 3058 and MemberIsLegacyUnforgeable(m, descriptor) == unforgeable 3059 and ( 3060 not crossOriginOnly 3061 or m.getExtendedAttribute("CrossOriginReadable") 3062 or m.getExtendedAttribute("CrossOriginWritable") 3063 ) 3064 ] 3065 else: 3066 idlAttrs = [] 3067 3068 attributes = [] 3069 for attr in idlAttrs: 3070 attributes.extend(self.attrData(attr, unforgeable)) 3071 self.chrome = [m for m in attributes if isChromeOnly(m["attr"])] 3072 self.regular = [m for m in attributes if not isChromeOnly(m["attr"])] 3073 self.static = static 3074 3075 if static: 3076 if not descriptor.interface.hasInterfaceObject(): 3077 # static attributes go on the interface object 3078 assert not self.hasChromeOnly() and not self.hasNonChromeOnly() 3079 else: 3080 if not descriptor.interface.hasInterfacePrototypeObject(): 3081 # non-static attributes go on the interface prototype object 3082 assert not self.hasChromeOnly() and not self.hasNonChromeOnly() 3083 3084 @staticmethod 3085 def attrData(attr, unforgeable=False, overrideFlags=None): 3086 if overrideFlags is None: 3087 permanent = " | JSPROP_PERMANENT" if unforgeable else "" 3088 flags = EnumerabilityFlags(attr) + permanent 3089 else: 3090 flags = overrideFlags 3091 return ( 3092 {"name": name, "attr": attr, "flags": flags} 3093 for name in [attr.identifier.name] + attr.bindingAliases 3094 ) 3095 3096 @staticmethod 3097 def condition(m, d): 3098 return PropertyDefiner.getControllingCondition(m["attr"], d) 3099 3100 @staticmethod 3101 def specData(entry, descriptor, static=False, crossOriginOnly=False): 3102 def getter(attr): 3103 if crossOriginOnly and not attr.getExtendedAttribute("CrossOriginReadable"): 3104 return "nullptr, nullptr" 3105 if static: 3106 if attr.type.isPromise(): 3107 raise TypeError( 3108 "Don't know how to handle " 3109 "static Promise-returning " 3110 "attribute %s.%s" % (descriptor.name, attr.identifier.name) 3111 ) 3112 accessor = "get_" + IDLToCIdentifier(attr.identifier.name) 3113 jitinfo = "nullptr" 3114 else: 3115 if attr.type.isPromise(): 3116 exceptionPolicy = "ConvertExceptionsToPromises" 3117 else: 3118 exceptionPolicy = "ThrowExceptions" 3119 3120 if attr.hasLegacyLenientThis(): 3121 if attr.getExtendedAttribute("CrossOriginReadable"): 3122 raise TypeError( 3123 "Can't handle lenient cross-origin " 3124 "readable attribute %s.%s" 3125 % (descriptor.name, attr.identifier.name) 3126 ) 3127 if descriptor.interface.hasDescendantWithCrossOriginMembers: 3128 accessor = ( 3129 "GenericGetter<MaybeCrossOriginObjectLenientThisPolicy, %s>" 3130 % exceptionPolicy 3131 ) 3132 else: 3133 accessor = ( 3134 "GenericGetter<LenientThisPolicy, %s>" % exceptionPolicy 3135 ) 3136 elif attr.getExtendedAttribute("CrossOriginReadable"): 3137 accessor = ( 3138 "GenericGetter<CrossOriginThisPolicy, %s>" % exceptionPolicy 3139 ) 3140 elif descriptor.interface.hasDescendantWithCrossOriginMembers: 3141 accessor = ( 3142 "GenericGetter<MaybeCrossOriginObjectThisPolicy, %s>" 3143 % exceptionPolicy 3144 ) 3145 elif descriptor.interface.isOnGlobalProtoChain(): 3146 accessor = ( 3147 "GenericGetter<MaybeGlobalThisPolicy, %s>" % exceptionPolicy 3148 ) 3149 else: 3150 accessor = "GenericGetter<NormalThisPolicy, %s>" % exceptionPolicy 3151 jitinfo = "&%s_getterinfo" % IDLToCIdentifier(attr.identifier.name) 3152 return "%s, %s" % (accessor, jitinfo) 3153 3154 def setter(attr): 3155 if ( 3156 attr.readonly 3157 and attr.getExtendedAttribute("PutForwards") is None 3158 and attr.getExtendedAttribute("Replaceable") is None 3159 and attr.getExtendedAttribute("LegacyLenientSetter") is None 3160 ): 3161 return "nullptr, nullptr" 3162 if crossOriginOnly and not attr.getExtendedAttribute("CrossOriginWritable"): 3163 return "nullptr, nullptr" 3164 if static: 3165 accessor = "set_" + IDLToCIdentifier(attr.identifier.name) 3166 jitinfo = "nullptr" 3167 else: 3168 if attr.hasLegacyLenientThis(): 3169 if attr.getExtendedAttribute("CrossOriginWritable"): 3170 raise TypeError( 3171 "Can't handle lenient cross-origin " 3172 "writable attribute %s.%s" 3173 % (descriptor.name, attr.identifier.name) 3174 ) 3175 if descriptor.interface.hasDescendantWithCrossOriginMembers: 3176 accessor = ( 3177 "GenericSetter<MaybeCrossOriginObjectLenientThisPolicy>" 3178 ) 3179 else: 3180 accessor = "GenericSetter<LenientThisPolicy>" 3181 elif attr.getExtendedAttribute("CrossOriginWritable"): 3182 accessor = "GenericSetter<CrossOriginThisPolicy>" 3183 elif descriptor.interface.hasDescendantWithCrossOriginMembers: 3184 accessor = "GenericSetter<MaybeCrossOriginObjectThisPolicy>" 3185 elif descriptor.interface.isOnGlobalProtoChain(): 3186 accessor = "GenericSetter<MaybeGlobalThisPolicy>" 3187 else: 3188 accessor = "GenericSetter<NormalThisPolicy>" 3189 jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name) 3190 return "%s, %s" % (accessor, jitinfo) 3191 3192 name, attr, flags = entry["name"], entry["attr"], entry["flags"] 3193 return (name, flags, getter(attr), setter(attr)) 3194 3195 @staticmethod 3196 def formatSpec(fields): 3197 return ' JSPropertySpec::nativeAccessors("%s", %s, %s, %s)' % fields 3198 3199 def generateArray(self, array, name): 3200 if len(array) == 0: 3201 return "" 3202 3203 return self.generatePrefableArray( 3204 array, 3205 name, 3206 self.formatSpec, 3207 " JS_PS_END", 3208 "JSPropertySpec", 3209 self.condition, 3210 functools.partial(self.specData, static=self.static), 3211 ) 3212 3213 3214 class ConstDefiner(PropertyDefiner): 3215 """ 3216 A class for definining constants on the interface object 3217 """ 3218 3219 def __init__(self, descriptor, name): 3220 PropertyDefiner.__init__(self, descriptor, name) 3221 self.name = name 3222 constants = [m for m in descriptor.interface.members if m.isConst()] 3223 self.chrome = [m for m in constants if isChromeOnly(m)] 3224 self.regular = [m for m in constants if not isChromeOnly(m)] 3225 3226 def generateArray(self, array, name): 3227 if len(array) == 0: 3228 return "" 3229 3230 def specData(const, descriptor): 3231 return (const.identifier.name, convertConstIDLValueToJSVal(const.value)) 3232 3233 return self.generatePrefableArray( 3234 array, 3235 name, 3236 lambda fields: ' { "%s", %s }' % fields, 3237 " { 0, JS::UndefinedValue() }", 3238 "ConstantSpec", 3239 PropertyDefiner.getControllingCondition, 3240 specData, 3241 ) 3242 3243 3244 class PropertyArrays: 3245 def __init__(self, descriptor, crossOriginOnly=False): 3246 self.staticMethods = MethodDefiner( 3247 descriptor, "StaticMethods", crossOriginOnly, static=True 3248 ) 3249 self.staticAttrs = AttrDefiner( 3250 descriptor, "StaticAttributes", crossOriginOnly, static=True 3251 ) 3252 self.methods = MethodDefiner( 3253 descriptor, "Methods", crossOriginOnly, static=False 3254 ) 3255 self.attrs = AttrDefiner( 3256 descriptor, "Attributes", crossOriginOnly, static=False 3257 ) 3258 self.unforgeableMethods = MethodDefiner( 3259 descriptor, 3260 "UnforgeableMethods", 3261 crossOriginOnly, 3262 static=False, 3263 unforgeable=True, 3264 ) 3265 self.unforgeableAttrs = AttrDefiner( 3266 descriptor, 3267 "UnforgeableAttributes", 3268 crossOriginOnly, 3269 static=False, 3270 unforgeable=True, 3271 ) 3272 self.consts = ConstDefiner(descriptor, "Constants") 3273 3274 @staticmethod 3275 def arrayNames(): 3276 return [ 3277 "staticMethods", 3278 "staticAttrs", 3279 "methods", 3280 "attrs", 3281 "unforgeableMethods", 3282 "unforgeableAttrs", 3283 "consts", 3284 ] 3285 3286 def hasChromeOnly(self): 3287 return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames()) 3288 3289 def hasNonChromeOnly(self): 3290 return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames()) 3291 3292 def __str__(self): 3293 define = "" 3294 for array in self.arrayNames(): 3295 define += str(getattr(self, array)) 3296 return define 3297 3298 3299 class CGConstDefinition(CGThing): 3300 """ 3301 Given a const member of an interface, return the C++ static const definition 3302 for the member. Should be part of the interface namespace in the header 3303 file. 3304 """ 3305 3306 def __init__(self, member): 3307 assert ( 3308 member.isConst() 3309 and member.value.type.isPrimitive() 3310 and not member.value.type.nullable() 3311 ) 3312 3313 name = CppKeywords.checkMethodName(IDLToCIdentifier(member.identifier.name)) 3314 tag = member.value.type.tag() 3315 value = member.value.value 3316 if tag == IDLType.Tags.bool: 3317 value = toStringBool(member.value.value) 3318 self.const = "static const %s %s = %s;" % (builtinNames[tag], name, value) 3319 3320 def declare(self): 3321 return self.const 3322 3323 def define(self): 3324 return "" 3325 3326 def deps(self): 3327 return [] 3328 3329 3330 class CGNativeProperties(CGList): 3331 def __init__(self, descriptor, properties): 3332 def generateNativeProperties(name, chrome): 3333 def check(p): 3334 return p.hasChromeOnly() if chrome else p.hasNonChromeOnly() 3335 3336 nativePropsInts = [] 3337 nativePropsPtrs = [] 3338 nativePropsDuos = [] 3339 3340 duosOffset = 0 3341 idsOffset = 0 3342 for array in properties.arrayNames(): 3343 propertyArray = getattr(properties, array) 3344 if check(propertyArray): 3345 varName = propertyArray.variableName(chrome) 3346 bitfields = "true, %d /* %s */" % (duosOffset, varName) 3347 duosOffset += 1 3348 nativePropsInts.append(CGGeneric(bitfields)) 3349 3350 if propertyArray.usedForXrays(): 3351 ids = "&%s_propertyInfos[%d]" % (name, idsOffset) 3352 idsOffset += propertyArray.length(chrome) 3353 else: 3354 ids = "nullptr" 3355 duo = "{ %s, %s }" % (varName, ids) 3356 nativePropsDuos.append(CGGeneric(duo)) 3357 else: 3358 bitfields = "false, 0" 3359 nativePropsInts.append(CGGeneric(bitfields)) 3360 3361 iteratorAliasIndex = -1 3362 for index, item in enumerate(properties.methods.regular): 3363 if item.get("hasIteratorAlias"): 3364 iteratorAliasIndex = index 3365 break 3366 nativePropsInts.append(CGGeneric(str(iteratorAliasIndex))) 3367 3368 nativePropsDuos = [ 3369 CGWrapper( 3370 CGIndenter(CGList(nativePropsDuos, ",\n")), pre="{\n", post="\n}" 3371 ) 3372 ] 3373 3374 pre = "static const NativePropertiesN<%d> %s = {\n" % (duosOffset, name) 3375 post = "\n};\n" 3376 if descriptor.wantsXrays: 3377 pre = fill( 3378 """ 3379 static uint16_t ${name}_sortedPropertyIndices[${size}]; 3380 static PropertyInfo ${name}_propertyInfos[${size}]; 3381 3382 $*{pre} 3383 """, 3384 name=name, 3385 size=idsOffset, 3386 pre=pre, 3387 ) 3388 if iteratorAliasIndex > 0: 3389 # The iteratorAliasMethodIndex is a signed integer, so the 3390 # max value it can store is 2^(nbits-1)-1. 3391 post = fill( 3392 """ 3393 $*{post} 3394 static_assert(${iteratorAliasIndex} < 1ull << (CHAR_BIT * sizeof(${name}.iteratorAliasMethodIndex) - 1), 3395 "We have an iterator alias index that is oversized"); 3396 """, 3397 post=post, 3398 iteratorAliasIndex=iteratorAliasIndex, 3399 name=name, 3400 ) 3401 post = fill( 3402 """ 3403 $*{post} 3404 static_assert(${propertyInfoCount} < 1ull << (CHAR_BIT * sizeof(${name}.propertyInfoCount)), 3405 "We have a property info count that is oversized"); 3406 """, 3407 post=post, 3408 propertyInfoCount=idsOffset, 3409 name=name, 3410 ) 3411 nativePropsInts.append(CGGeneric("%d" % idsOffset)) 3412 nativePropsPtrs.append(CGGeneric("%s_sortedPropertyIndices" % name)) 3413 else: 3414 nativePropsInts.append(CGGeneric("0")) 3415 nativePropsPtrs.append(CGGeneric("nullptr")) 3416 nativeProps = nativePropsInts + nativePropsPtrs + nativePropsDuos 3417 return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")), pre=pre, post=post) 3418 3419 nativeProperties = [] 3420 if properties.hasNonChromeOnly(): 3421 nativeProperties.append( 3422 generateNativeProperties("sNativeProperties", False) 3423 ) 3424 if properties.hasChromeOnly(): 3425 nativeProperties.append( 3426 generateNativeProperties("sChromeOnlyNativeProperties", True) 3427 ) 3428 3429 CGList.__init__(self, nativeProperties, "\n") 3430 3431 def declare(self): 3432 return "" 3433 3434 def define(self): 3435 return CGList.define(self) 3436 3437 3438 class CGCollectJSONAttributesMethod(CGAbstractMethod): 3439 """ 3440 Generate the CollectJSONAttributes method for an interface descriptor 3441 """ 3442 3443 def __init__(self, descriptor, toJSONMethod): 3444 args = [ 3445 Argument("JSContext*", "cx"), 3446 Argument("JS::Handle<JSObject*>", "obj"), 3447 Argument("%s*" % descriptor.nativeType, "self"), 3448 Argument("JS::Rooted<JSObject*>&", "result"), 3449 ] 3450 CGAbstractMethod.__init__( 3451 self, descriptor, "CollectJSONAttributes", "bool", args, canRunScript=True 3452 ) 3453 self.toJSONMethod = toJSONMethod 3454 3455 def definition_body(self): 3456 ret = "" 3457 interface = self.descriptor.interface 3458 toJSONCondition = PropertyDefiner.getControllingCondition( 3459 self.toJSONMethod, self.descriptor 3460 ) 3461 needUnwrappedObj = False 3462 for m in interface.members: 3463 if m.isAttr() and not m.isStatic() and m.type.isJSONType(): 3464 getAndDefine = fill( 3465 """ 3466 JS::Rooted<JS::Value> temp(cx); 3467 if (!get_${name}(cx, obj, self, JSJitGetterCallArgs(&temp))) { 3468 return false; 3469 } 3470 if (!JS_DefineProperty(cx, result, "${name}", temp, JSPROP_ENUMERATE)) { 3471 return false; 3472 } 3473 """, 3474 name=IDLToCIdentifier(m.identifier.name), 3475 ) 3476 # Make sure we don't include things which are supposed to be 3477 # disabled. Things that either don't have disablers or whose 3478 # disablers match the disablers for our toJSON method can't 3479 # possibly be disabled, but other things might be. 3480 condition = PropertyDefiner.getControllingCondition(m, self.descriptor) 3481 if condition.hasDisablers() and condition != toJSONCondition: 3482 needUnwrappedObj = True 3483 ret += fill( 3484 """ 3485 // This is unfortunately a linear scan through sAttributes, but we 3486 // only do it for things which _might_ be disabled, which should 3487 // help keep the performance problems down. 3488 if (IsGetterEnabled(cx, unwrappedObj, (JSJitGetterOp)get_${name}, sAttributes)) { 3489 $*{getAndDefine} 3490 } 3491 """, 3492 name=IDLToCIdentifier(m.identifier.name), 3493 getAndDefine=getAndDefine, 3494 ) 3495 else: 3496 ret += fill( 3497 """ 3498 { // scope for "temp" 3499 $*{getAndDefine} 3500 } 3501 """, 3502 getAndDefine=getAndDefine, 3503 ) 3504 ret += "return true;\n" 3505 3506 if needUnwrappedObj: 3507 # If we started allowing cross-origin objects here, we'd need to 3508 # use CheckedUnwrapDynamic and figure out whether it makes sense. 3509 # But in practice no one is trying to add toJSON methods to those, 3510 # so let's just guard against it. 3511 assert not self.descriptor.isMaybeCrossOriginObject() 3512 ret = fill( 3513 """ 3514 JS::Rooted<JSObject*> unwrappedObj(cx, js::CheckedUnwrapStatic(obj)); 3515 if (!unwrappedObj) { 3516 // How did that happen? We managed to get called with that 3517 // object as "this"! Just give up on sanity. 3518 return false; 3519 } 3520 3521 $*{ret} 3522 """, 3523 ret=ret, 3524 ) 3525 3526 return ret 3527 3528 3529 class CGCreateInterfaceObjectsMethod(CGAbstractMethod): 3530 """ 3531 Generate the CreateInterfaceObjects method for an interface descriptor. 3532 3533 properties should be a PropertyArrays instance. 3534 """ 3535 3536 def __init__( 3537 self, descriptor, properties, haveUnscopables, haveLegacyWindowAliases, static 3538 ): 3539 args = [ 3540 Argument("JSContext*", "aCx"), 3541 Argument("JS::Handle<JSObject*>", "aGlobal"), 3542 Argument("ProtoAndIfaceCache&", "aProtoAndIfaceCache"), 3543 Argument("DefineInterfaceProperty", "aDefineOnGlobal"), 3544 ] 3545 CGAbstractMethod.__init__( 3546 self, descriptor, "CreateInterfaceObjects", "void", args, static=static 3547 ) 3548 self.properties = properties 3549 self.haveUnscopables = haveUnscopables 3550 self.haveLegacyWindowAliases = haveLegacyWindowAliases 3551 3552 def definition_body(self): 3553 needInterfaceObject = self.descriptor.interface.hasInterfaceObject() 3554 if needInterfaceObject and self.descriptor.isExposedConditionally(): 3555 # This code might be called when we're trying to create an object 3556 # in a non-system compartment, for example when system code is 3557 # calling a constructor through Xrays. In that case we do want to 3558 # create an interface object in the non-system compartment, but we 3559 # don't want to expose the name on the non-system global if the 3560 # interface itself is marked as ChromeOnly. 3561 defineOnGlobal = ( 3562 "ShouldExpose<%s::ConstructorEnabled>(aCx, aGlobal, aDefineOnGlobal)" 3563 % toBindingNamespace(self.descriptor.name) 3564 ) 3565 else: 3566 defineOnGlobal = "aDefineOnGlobal != DefineInterfaceProperty::No" 3567 if needInterfaceObject: 3568 if self.descriptor.interface.isNamespace(): 3569 if self.descriptor.interface.getExtendedAttribute("ProtoObjectHack"): 3570 getConstructorProto = "GetHackedNamespaceProtoObject" 3571 else: 3572 getConstructorProto = "JS::GetRealmObjectPrototype" 3573 getConstructorProto = "aCx, " + getConstructorProto 3574 constructorProtoType = "Rooted" 3575 else: 3576 getConstructorProto = InterfaceObjectProtoGetter(self.descriptor) 3577 constructorProtoType = "Handle" 3578 3579 getConstructorProto = fill( 3580 """ 3581 JS::${type}<JSObject*> constructorProto(${getConstructorProto}(aCx)); 3582 if (!constructorProto) { 3583 return; 3584 } 3585 """, 3586 type=constructorProtoType, 3587 getConstructorProto=getConstructorProto, 3588 ) 3589 3590 interfaceInfo = "&sInterfaceObjectInfo" 3591 interfaceCache = ( 3592 "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)" 3593 % self.descriptor.name 3594 ) 3595 getConstructorProto = CGGeneric(getConstructorProto) 3596 constructorProto = "constructorProto" 3597 else: 3598 # We don't have slots to store the legacy factory functions. 3599 assert len(self.descriptor.interface.legacyFactoryFunctions) == 0 3600 interfaceInfo = "nullptr" 3601 interfaceCache = "nullptr" 3602 getConstructorProto = None 3603 constructorProto = "nullptr" 3604 3605 if self.properties.hasNonChromeOnly(): 3606 properties = "sNativeProperties.Upcast()" 3607 else: 3608 properties = "nullptr" 3609 if self.properties.hasChromeOnly(): 3610 chromeProperties = "sChromeOnlyNativeProperties.Upcast()" 3611 else: 3612 chromeProperties = "nullptr" 3613 3614 # We use getClassName here. This should be the right thing to pass as 3615 # the name argument to CreateInterfaceObjects. This is generally the 3616 # interface identifier, except for the synthetic interfaces created for 3617 # the default iterator objects. If needInterfaceObject is true then 3618 # we'll use the name to install a property on the global object, so 3619 # there shouldn't be any spaces in the name. 3620 name = self.descriptor.interface.getClassName() 3621 assert not (needInterfaceObject and " " in name) 3622 3623 if self.descriptor.interface.isNamespace(): 3624 # If we don't need to create anything, why are we generating this? 3625 assert needInterfaceObject 3626 3627 call = fill( 3628 """ 3629 JS::Heap<JSObject*>* interfaceCache = ${interfaceCache}; 3630 dom::CreateNamespaceObject(aCx, aGlobal, ${constructorProto}, 3631 sNamespaceObjectClass, 3632 interfaceCache, 3633 ${properties}, 3634 ${chromeProperties}, 3635 "${name}", 3636 ${defineOnGlobal}); 3637 """, 3638 interfaceCache=interfaceCache, 3639 constructorProto=constructorProto, 3640 properties=properties, 3641 chromeProperties=chromeProperties, 3642 name=name, 3643 defineOnGlobal=defineOnGlobal, 3644 ) 3645 return CGList( 3646 [ 3647 getConstructorProto, 3648 CGGeneric(call), 3649 ], 3650 "\n", 3651 ).define() 3652 3653 needInterfacePrototypeObject = ( 3654 self.descriptor.interface.hasInterfacePrototypeObject() 3655 ) 3656 3657 # If we don't need to create anything, why are we generating this? 3658 assert needInterfaceObject or needInterfacePrototypeObject 3659 3660 if needInterfacePrototypeObject: 3661 (protoGetter, protoHandleGetter) = InterfacePrototypeObjectProtoGetter( 3662 self.descriptor 3663 ) 3664 if protoHandleGetter is None: 3665 parentProtoType = "Rooted" 3666 getParentProto = "aCx, " + protoGetter 3667 else: 3668 parentProtoType = "Handle" 3669 getParentProto = protoHandleGetter 3670 3671 getParentProto = fill( 3672 """ 3673 JS::${type}<JSObject*> parentProto(${getParentProto}(aCx)); 3674 if (!parentProto) { 3675 return; 3676 } 3677 """, 3678 type=parentProtoType, 3679 getParentProto=getParentProto, 3680 ) 3681 3682 protoClass = "&sPrototypeClass" 3683 protoCache = ( 3684 "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)" 3685 % self.descriptor.name 3686 ) 3687 parentProto = "parentProto" 3688 getParentProto = CGGeneric(getParentProto) 3689 else: 3690 protoClass = "nullptr" 3691 protoCache = "nullptr" 3692 parentProto = "nullptr" 3693 getParentProto = None 3694 3695 if self.descriptor.interface.ctor(): 3696 constructArgs = methodLength(self.descriptor.interface.ctor()) 3697 isConstructorChromeOnly = isChromeOnly(self.descriptor.interface.ctor()) 3698 else: 3699 constructArgs = 0 3700 isConstructorChromeOnly = False 3701 if len(self.descriptor.interface.legacyFactoryFunctions) > 0: 3702 legacyFactoryFunctions = "Span(legacyFactoryFunctions)" 3703 else: 3704 legacyFactoryFunctions = "Span<const LegacyFactoryFunction, 0>{}" 3705 3706 isGlobal = self.descriptor.isGlobal() is not None 3707 3708 ensureCaches = fill( 3709 """ 3710 JS::Heap<JSObject*>* protoCache = ${protoCache}; 3711 JS::Heap<JSObject*>* interfaceCache = ${interfaceCache}; 3712 """, 3713 protoCache=protoCache, 3714 interfaceCache=interfaceCache, 3715 ) 3716 call = fill( 3717 """ 3718 dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto}, 3719 ${protoClass}, protoCache, 3720 ${constructorProto}, ${interfaceInfo}, ${constructArgs}, ${isConstructorChromeOnly}, ${legacyFactoryFunctions}, 3721 interfaceCache, 3722 ${properties}, 3723 ${chromeProperties}, 3724 "${name}", 3725 ${defineOnGlobal}, 3726 ${unscopableNames}, 3727 ${isGlobal}, 3728 ${legacyWindowAliases}); 3729 """, 3730 protoClass=protoClass, 3731 parentProto=parentProto, 3732 constructorProto=constructorProto, 3733 interfaceInfo=interfaceInfo, 3734 constructArgs=constructArgs, 3735 isConstructorChromeOnly=toStringBool(isConstructorChromeOnly), 3736 legacyFactoryFunctions=legacyFactoryFunctions, 3737 properties=properties, 3738 chromeProperties=chromeProperties, 3739 name=name, 3740 defineOnGlobal=defineOnGlobal, 3741 unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr", 3742 isGlobal=toStringBool(isGlobal), 3743 legacyWindowAliases=( 3744 "legacyWindowAliases" if self.haveLegacyWindowAliases else "nullptr" 3745 ), 3746 ) 3747 3748 # If we fail after here, we must clear interface and prototype caches 3749 # using this code: intermediate failure must not expose the interface in 3750 # partially-constructed state. Note that every case after here needs an 3751 # interface prototype object. 3752 failureCode = dedent( 3753 """ 3754 *protoCache = nullptr; 3755 if (interfaceCache) { 3756 *interfaceCache = nullptr; 3757 } 3758 return; 3759 """ 3760 ) 3761 3762 needProtoVar = False 3763 3764 aliasedMembers = [ 3765 m for m in self.descriptor.interface.members if m.isMethod() and m.aliases 3766 ] 3767 if aliasedMembers: 3768 assert needInterfacePrototypeObject 3769 3770 def defineAlias(alias): 3771 if alias == "@@iterator" or alias == "@@asyncIterator": 3772 name = alias[2:] 3773 3774 symbolJSID = ( 3775 "JS::GetWellKnownSymbolKey(aCx, JS::SymbolCode::%s)" % name 3776 ) 3777 prop = "%sId" % name 3778 getSymbolJSID = CGGeneric( 3779 fill( 3780 "JS::Rooted<jsid> ${prop}(aCx, ${symbolJSID});", 3781 prop=prop, 3782 symbolJSID=symbolJSID, 3783 ) 3784 ) 3785 defineFn = "JS_DefinePropertyById" 3786 enumFlags = "0" # Not enumerable, per spec. 3787 elif alias.startswith("@@"): 3788 raise TypeError( 3789 "Can't handle any well-known Symbol other than @@iterator and @@asyncIterator" 3790 ) 3791 else: 3792 getSymbolJSID = None 3793 defineFn = "JS_DefineProperty" 3794 prop = '"%s"' % alias 3795 # XXX If we ever create non-enumerable properties that can 3796 # be aliased, we should consider making the aliases 3797 # match the enumerability of the property being aliased. 3798 enumFlags = "JSPROP_ENUMERATE" 3799 return CGList( 3800 [ 3801 getSymbolJSID, 3802 CGGeneric( 3803 fill( 3804 """ 3805 if (!${defineFn}(aCx, proto, ${prop}, aliasedVal, ${enumFlags})) { 3806 $*{failureCode} 3807 } 3808 """, 3809 defineFn=defineFn, 3810 prop=prop, 3811 enumFlags=enumFlags, 3812 failureCode=failureCode, 3813 ) 3814 ), 3815 ], 3816 "\n", 3817 ) 3818 3819 def defineAliasesFor(m): 3820 return CGList( 3821 [ 3822 CGGeneric( 3823 fill( 3824 """ 3825 if (!JS_GetProperty(aCx, proto, \"${prop}\", &aliasedVal)) { 3826 $*{failureCode} 3827 } 3828 """, 3829 failureCode=failureCode, 3830 prop=m.identifier.name, 3831 ) 3832 ) 3833 ] 3834 + [defineAlias(alias) for alias in sorted(m.aliases)] 3835 ) 3836 3837 defineAliases = CGList( 3838 [ 3839 CGGeneric( 3840 dedent( 3841 """ 3842 // Set up aliases on the interface prototype object we just created. 3843 """ 3844 ) 3845 ), 3846 CGGeneric("JS::Rooted<JS::Value> aliasedVal(aCx);\n\n"), 3847 ] 3848 + [ 3849 defineAliasesFor(m) 3850 for m in sorted(aliasedMembers, key=lambda m: m.identifier.name) 3851 ] 3852 ) 3853 needProtoVar = True 3854 else: 3855 defineAliases = None 3856 3857 # Globals handle unforgeables directly in Wrap() instead of 3858 # via a holder. 3859 if ( 3860 self.descriptor.hasLegacyUnforgeableMembers 3861 and not self.descriptor.isGlobal() 3862 ): 3863 assert needInterfacePrototypeObject 3864 3865 # We want to use the same JSClass and prototype as the object we'll 3866 # end up defining the unforgeable properties on in the end, so that 3867 # we can use JS_InitializePropertiesFromCompatibleNativeObject to do 3868 # a fast copy. In the case of proxies that's null, because the 3869 # expando object is a vanilla object, but in the case of other DOM 3870 # objects it's whatever our class is. 3871 if self.descriptor.proxy: 3872 holderClass = "nullptr" 3873 holderProto = "nullptr" 3874 else: 3875 holderClass = "sClass.ToJSClass()" 3876 holderProto = "proto" 3877 needProtoVar = True 3878 createUnforgeableHolder = CGGeneric( 3879 fill( 3880 """ 3881 JS::Rooted<JSObject*> unforgeableHolder( 3882 aCx, JS_NewObjectWithoutMetadata(aCx, ${holderClass}, ${holderProto})); 3883 if (!unforgeableHolder) { 3884 $*{failureCode} 3885 } 3886 """, 3887 holderProto=holderProto, 3888 holderClass=holderClass, 3889 failureCode=failureCode, 3890 ) 3891 ) 3892 defineUnforgeables = InitUnforgeablePropertiesOnHolder( 3893 self.descriptor, self.properties, failureCode 3894 ) 3895 createUnforgeableHolder = CGList( 3896 [createUnforgeableHolder, defineUnforgeables] 3897 ) 3898 3899 installUnforgeableHolder = CGGeneric( 3900 dedent( 3901 """ 3902 if (*protoCache) { 3903 JS::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE, 3904 JS::ObjectValue(*unforgeableHolder)); 3905 } 3906 """ 3907 ) 3908 ) 3909 3910 unforgeableHolderSetup = CGList( 3911 [createUnforgeableHolder, installUnforgeableHolder], "\n" 3912 ) 3913 else: 3914 unforgeableHolderSetup = None 3915 3916 if ( 3917 self.descriptor.interface.isOnGlobalProtoChain() 3918 and needInterfacePrototypeObject 3919 ): 3920 makeProtoPrototypeImmutable = CGGeneric( 3921 fill( 3922 """ 3923 { 3924 bool succeeded; 3925 if (!JS_SetImmutablePrototype(aCx, proto, &succeeded)) { 3926 $*{failureCode} 3927 } 3928 3929 MOZ_ASSERT(succeeded, 3930 "making a fresh prototype object's [[Prototype]] " 3931 "immutable can internally fail, but it should " 3932 "never be unsuccessful"); 3933 } 3934 """, 3935 protoCache=protoCache, 3936 failureCode=failureCode, 3937 ) 3938 ) 3939 needProtoVar = True 3940 else: 3941 makeProtoPrototypeImmutable = None 3942 3943 if needProtoVar: 3944 defineProtoVar = CGGeneric( 3945 fill( 3946 """ 3947 JS::AssertObjectIsNotGray(*protoCache); 3948 JS::Handle<JSObject*> proto = JS::Handle<JSObject*>::fromMarkedLocation(protoCache->unsafeAddress()); 3949 if (!proto) { 3950 $*{failureCode} 3951 } 3952 """, 3953 failureCode=failureCode, 3954 ) 3955 ) 3956 else: 3957 defineProtoVar = None 3958 3959 # ensureCaches needs to come first as it crashes on failure (like OOM). 3960 # We want to make sure that the caches do exist before we try to return 3961 # to the caller, so it can rely on that (and detect other failures by 3962 # checking for null in the caches). 3963 return CGList( 3964 [ 3965 CGGeneric(ensureCaches), 3966 getParentProto, 3967 getConstructorProto, 3968 CGGeneric(call), 3969 defineProtoVar, 3970 defineAliases, 3971 unforgeableHolderSetup, 3972 makeProtoPrototypeImmutable, 3973 ], 3974 "\n", 3975 ).define() 3976 3977 3978 class CGCreateAndDefineOnGlobalMethod(CGAbstractMethod): 3979 """ 3980 A method for creating the interface or namespace object and defining 3981 properties for it on the global. 3982 """ 3983 3984 def __init__(self, descriptor): 3985 CGAbstractMethod.__init__( 3986 self, 3987 descriptor, 3988 "CreateAndDefineOnGlobal", 3989 "bool", 3990 [ 3991 Argument("JSContext*", "aCx"), 3992 ], 3993 inline=True, 3994 ) 3995 3996 def definition_body(self): 3997 return fill( 3998 """ 3999 // Get the interface or namespace object for this class. This will 4000 // create the object as needed and always define the properties for 4001 // it on the global. The caller should make sure the interface or 4002 // namespace is exposed on the global before calling this. 4003 return GetPerInterfaceObjectHandle(aCx, constructors::id::${name}, 4004 &CreateInterfaceObjects, 4005 DefineInterfaceProperty::Always); 4006 4007 """, 4008 name=self.descriptor.name, 4009 ) 4010 4011 4012 class CGGetProtoObjectHandleMethod(CGAbstractMethod): 4013 """ 4014 A method for getting the interface prototype object. 4015 """ 4016 4017 def __init__(self, descriptor, static, signatureOnly=False): 4018 CGAbstractMethod.__init__( 4019 self, 4020 descriptor, 4021 "GetProtoObjectHandle", 4022 "JS::Handle<JSObject*>", 4023 [Argument("JSContext*", "aCx")], 4024 static=static, 4025 signatureOnly=signatureOnly, 4026 ) 4027 4028 def definition_body(self): 4029 return fill( 4030 """ 4031 /* Get the interface prototype object for this class. This will create the 4032 object as needed. */ 4033 return GetPerInterfaceObjectHandle(aCx, prototypes::id::${name}, 4034 &CreateInterfaceObjects, 4035 DefineInterfaceProperty::CheckExposure); 4036 4037 """, 4038 name=self.descriptor.name, 4039 ) 4040 4041 4042 class CGGetProtoObjectMethod(CGAbstractMethod): 4043 """ 4044 A method for getting the interface prototype object. 4045 """ 4046 4047 def __init__(self, descriptor): 4048 CGAbstractMethod.__init__( 4049 self, 4050 descriptor, 4051 "GetProtoObject", 4052 "JSObject*", 4053 [Argument("JSContext*", "aCx")], 4054 ) 4055 4056 def definition_body(self): 4057 return "return GetProtoObjectHandle(aCx);\n" 4058 4059 4060 class CGGetConstructorObjectHandleMethod(CGAbstractMethod): 4061 """ 4062 A method for getting the interface constructor object. 4063 """ 4064 4065 def __init__(self, descriptor): 4066 CGAbstractMethod.__init__( 4067 self, 4068 descriptor, 4069 "GetConstructorObjectHandle", 4070 "JS::Handle<JSObject*>", 4071 [ 4072 Argument("JSContext*", "aCx"), 4073 ], 4074 ) 4075 4076 def definition_body(self): 4077 return fill( 4078 """ 4079 /* Get the interface object for this class. This will create the object as 4080 needed. */ 4081 4082 return GetPerInterfaceObjectHandle(aCx, constructors::id::${name}, 4083 &CreateInterfaceObjects, 4084 DefineInterfaceProperty::CheckExposure); 4085 """, 4086 name=self.descriptor.name, 4087 ) 4088 4089 4090 class CGGetNamedPropertiesObjectMethod(CGAbstractStaticMethod): 4091 def __init__(self, descriptor): 4092 args = [Argument("JSContext*", "aCx")] 4093 CGAbstractStaticMethod.__init__( 4094 self, descriptor, "GetNamedPropertiesObject", "JSObject*", args 4095 ) 4096 4097 def definition_body(self): 4098 parentProtoName = self.descriptor.parentPrototypeName 4099 if parentProtoName is None: 4100 getParentProto = "" 4101 parentProto = "nullptr" 4102 else: 4103 getParentProto = fill( 4104 """ 4105 JS::Rooted<JSObject*> parentProto(aCx, ${parent}::GetProtoObjectHandle(aCx)); 4106 if (!parentProto) { 4107 return nullptr; 4108 } 4109 """, 4110 parent=toBindingNamespace(parentProtoName), 4111 ) 4112 parentProto = "parentProto" 4113 return fill( 4114 """ 4115 /* Make sure our global is sane. Hopefully we can remove this sometime */ 4116 JSObject* global = JS::CurrentGlobalOrNull(aCx); 4117 if (!(JS::GetClass(global)->flags & JSCLASS_DOM_GLOBAL)) { 4118 return nullptr; 4119 } 4120 4121 /* Check to see whether the named properties object has already been created */ 4122 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global); 4123 4124 JS::Heap<JSObject*>& namedPropertiesObject = protoAndIfaceCache.EntrySlotOrCreate(namedpropertiesobjects::id::${ifaceName}); 4125 if (!namedPropertiesObject) { 4126 $*{getParentProto} 4127 namedPropertiesObject = ${nativeType}::CreateNamedPropertiesObject(aCx, ${parentProto}); 4128 DebugOnly<const DOMIfaceAndProtoJSClass*> clasp = 4129 DOMIfaceAndProtoJSClass::FromJSClass(JS::GetClass(namedPropertiesObject)); 4130 MOZ_ASSERT(clasp->mType == eNamedPropertiesObject, 4131 "Expected ${nativeType}::CreateNamedPropertiesObject to return a named properties object"); 4132 MOZ_ASSERT(clasp->mNativeHooks, 4133 "The named properties object for ${nativeType} should have NativePropertyHooks."); 4134 MOZ_ASSERT(!clasp->mNativeHooks->mIndexedOrNamedNativeProperties || 4135 !clasp->mNativeHooks->mIndexedOrNamedNativeProperties->mResolveOwnProperty, 4136 "Shouldn't resolve the properties of the named properties object for ${nativeType} for Xrays."); 4137 MOZ_ASSERT(!clasp->mNativeHooks->mIndexedOrNamedNativeProperties || 4138 !clasp->mNativeHooks->mIndexedOrNamedNativeProperties->mEnumerateOwnProperties, 4139 "Shouldn't enumerate the properties of the named properties object for ${nativeType} for Xrays."); 4140 } 4141 return namedPropertiesObject.get(); 4142 """, 4143 getParentProto=getParentProto, 4144 ifaceName=self.descriptor.name, 4145 parentProto=parentProto, 4146 nativeType=self.descriptor.nativeType, 4147 ) 4148 4149 4150 def getRawConditionList(idlobj, cxName, objName, ignoreSecureContext=False): 4151 """ 4152 Get the list of conditions for idlobj (to be used in "is this enabled" 4153 checks). This will be returned as a CGList with " &&\n" as the separator, 4154 for readability. 4155 4156 objName is the name of the object that we're working with, because some of 4157 our test functions want that. 4158 4159 ignoreSecureContext is used only for constructors in which the WebIDL interface 4160 itself is already marked as [SecureContext]. There is no need to do the work twice. 4161 """ 4162 conditions = [] 4163 pref = idlobj.getExtendedAttribute("Pref") 4164 if pref: 4165 assert isinstance(pref, list) and len(pref) == 1 4166 conditions.append("StaticPrefs::%s()" % prefIdentifier(pref[0])) 4167 if isChromeOnly(idlobj): 4168 conditions.append("nsContentUtils::ThreadsafeIsSystemCaller(%s)" % cxName) 4169 func = idlobj.getExtendedAttribute("Func") 4170 if func: 4171 assert isinstance(func, list) and len(func) == 1 4172 conditions.append("%s(%s, %s)" % (func[0], cxName, objName)) 4173 trial = idlobj.getExtendedAttribute("Trial") 4174 if trial: 4175 assert isinstance(trial, list) and len(trial) == 1 4176 conditions.append( 4177 "OriginTrials::IsEnabled(%s, %s, OriginTrial::%s)" 4178 % (cxName, objName, trial[0]) 4179 ) 4180 if not ignoreSecureContext and idlobj.getExtendedAttribute("SecureContext"): 4181 conditions.append( 4182 "mozilla::dom::IsSecureContextOrObjectIsFromSecureContext(%s, %s)" 4183 % (cxName, objName) 4184 ) 4185 return conditions 4186 4187 4188 def getConditionList(idlobj, cxName, objName, ignoreSecureContext=False): 4189 """ 4190 Get the list of conditions from getRawConditionList 4191 See comment on getRawConditionList above for more info about arguments. 4192 4193 The return value is a possibly-empty conjunctive CGList of conditions. 4194 """ 4195 conditions = getRawConditionList(idlobj, cxName, objName, ignoreSecureContext) 4196 return CGList((CGGeneric(cond) for cond in conditions), " &&\n") 4197 4198 4199 class CGConstructorEnabled(CGAbstractMethod): 4200 """ 4201 A method for testing whether we should be exposing this interface object. 4202 This can perform various tests depending on what conditions are specified 4203 on the interface. 4204 """ 4205 4206 def __init__(self, descriptor): 4207 CGAbstractMethod.__init__( 4208 self, 4209 descriptor, 4210 "ConstructorEnabled", 4211 "bool", 4212 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")], 4213 ) 4214 4215 def definition_body(self): 4216 body = CGList([], "\n") 4217 4218 iface = self.descriptor.interface 4219 4220 if not iface.isExposedInWindow(): 4221 exposedInWindowCheck = dedent( 4222 """ 4223 MOZ_ASSERT(!NS_IsMainThread(), "Why did we even get called?"); 4224 """ 4225 ) 4226 body.append(CGGeneric(exposedInWindowCheck)) 4227 4228 if iface.isExposedInSomeButNotAllWorkers(): 4229 workerGlobals = sorted(iface.getWorkerExposureSet()) 4230 workerCondition = CGList( 4231 ( 4232 CGGeneric('strcmp(name, "%s")' % workerGlobal) 4233 for workerGlobal in workerGlobals 4234 ), 4235 " && ", 4236 ) 4237 exposedInWorkerCheck = fill( 4238 """ 4239 const char* name = JS::GetClass(aObj)->name; 4240 if (${workerCondition}) { 4241 return false; 4242 } 4243 """, 4244 workerCondition=workerCondition.define(), 4245 ) 4246 exposedInWorkerCheck = CGGeneric(exposedInWorkerCheck) 4247 if iface.isExposedInWindow(): 4248 exposedInWorkerCheck = CGIfWrapper( 4249 exposedInWorkerCheck, "!NS_IsMainThread()" 4250 ) 4251 body.append(exposedInWorkerCheck) 4252 4253 conditions = getConditionList(iface, "aCx", "aObj") 4254 4255 # We should really have some conditions 4256 assert len(body) or len(conditions) 4257 4258 conditionsWrapper = "" 4259 if len(conditions): 4260 conditionsWrapper = CGWrapper( 4261 conditions, pre="return ", post=";\n", reindent=True 4262 ) 4263 else: 4264 conditionsWrapper = CGGeneric("return true;\n") 4265 4266 body.append(conditionsWrapper) 4267 return body.define() 4268 4269 4270 def StructuredCloneTag(name): 4271 return "SCTAG_DOM_%s" % name.upper() 4272 4273 4274 class CGSerializer(CGAbstractStaticMethod): 4275 """ 4276 Implementation of serialization for things marked [Serializable]. 4277 This gets stored in our DOMJSClass, so it can be static. 4278 4279 The caller is expected to pass in the object whose DOMJSClass it 4280 used to get the serializer. 4281 """ 4282 4283 def __init__(self, descriptor): 4284 args = [ 4285 Argument("JSContext*", "aCx"), 4286 Argument("JSStructuredCloneWriter*", "aWriter"), 4287 Argument("JS::Handle<JSObject*>", "aObj"), 4288 ] 4289 CGAbstractStaticMethod.__init__(self, descriptor, "Serialize", "bool", args) 4290 4291 def definition_body(self): 4292 return fill( 4293 """ 4294 MOZ_ASSERT(IsDOMObject(aObj), "Non-DOM object passed"); 4295 MOZ_ASSERT(GetDOMClass(aObj)->mSerializer == &Serialize, 4296 "Wrong object passed"); 4297 return JS_WriteUint32Pair(aWriter, ${tag}, 0) && 4298 UnwrapDOMObject<${type}>(aObj)->WriteStructuredClone(aCx, aWriter); 4299 """, 4300 tag=StructuredCloneTag(self.descriptor.name), 4301 type=self.descriptor.nativeType, 4302 ) 4303 4304 4305 class CGDeserializer(CGAbstractMethod): 4306 """ 4307 Implementation of deserialization for things marked [Serializable]. 4308 This will need to be accessed from WebIDLSerializable, so can't be static. 4309 """ 4310 4311 def __init__(self, descriptor): 4312 args = [ 4313 Argument("JSContext*", "aCx"), 4314 Argument("nsIGlobalObject*", "aGlobal"), 4315 Argument("JSStructuredCloneReader*", "aReader"), 4316 ] 4317 CGAbstractMethod.__init__(self, descriptor, "Deserialize", "JSObject*", args) 4318 4319 def definition_body(self): 4320 # WrapObject has different signatures depending on whether 4321 # the object is wrappercached. 4322 if self.descriptor.wrapperCache: 4323 wrapCall = dedent( 4324 """ 4325 result = obj->WrapObject(aCx, nullptr); 4326 if (!result) { 4327 return nullptr; 4328 } 4329 """ 4330 ) 4331 else: 4332 wrapCall = dedent( 4333 """ 4334 if (!obj->WrapObject(aCx, nullptr, &result)) { 4335 return nullptr; 4336 } 4337 """ 4338 ) 4339 4340 return fill( 4341 """ 4342 // Protect the result from a moving GC in ~RefPtr 4343 JS::Rooted<JSObject*> result(aCx); 4344 { // Scope for the RefPtr 4345 RefPtr<${type}> obj = ${type}::ReadStructuredClone(aCx, aGlobal, aReader); 4346 if (!obj) { 4347 return nullptr; 4348 } 4349 $*{wrapCall} 4350 } 4351 return result; 4352 """, 4353 type=self.descriptor.nativeType, 4354 wrapCall=wrapCall, 4355 ) 4356 4357 4358 def CreateBindingJSObject(descriptor): 4359 objDecl = "BindingJSObjectCreator<%s> creator(aCx);\n" % descriptor.nativeType 4360 4361 # We don't always need to root obj, but there are a variety 4362 # of cases where we do, so for simplicity, just always root it. 4363 if descriptor.proxy: 4364 if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"): 4365 assert not descriptor.isMaybeCrossOriginObject() 4366 create = dedent( 4367 """ 4368 aObject->mExpandoAndGeneration.expando.setUndefined(); 4369 JS::Rooted<JS::Value> expandoValue(aCx, JS::PrivateValue(&aObject->mExpandoAndGeneration)); 4370 creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(), 4371 proto, /* aLazyProto = */ false, aObject, 4372 expandoValue, aReflector); 4373 """ 4374 ) 4375 else: 4376 if descriptor.isMaybeCrossOriginObject(): 4377 proto = "nullptr" 4378 lazyProto = "true" 4379 else: 4380 proto = "proto" 4381 lazyProto = "false" 4382 create = fill( 4383 """ 4384 creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(), 4385 ${proto}, /* aLazyProto = */ ${lazyProto}, 4386 aObject, JS::UndefinedHandleValue, aReflector); 4387 """, 4388 proto=proto, 4389 lazyProto=lazyProto, 4390 ) 4391 else: 4392 create = dedent( 4393 """ 4394 creator.CreateObject(aCx, sClass.ToJSClass(), proto, aObject, aReflector); 4395 """ 4396 ) 4397 return ( 4398 objDecl 4399 + create 4400 + dedent( 4401 """ 4402 if (!aReflector) { 4403 return false; 4404 } 4405 """ 4406 ) 4407 ) 4408 4409 4410 def InitUnforgeablePropertiesOnHolder( 4411 descriptor, properties, failureCode, holderName="unforgeableHolder" 4412 ): 4413 """ 4414 Define the unforgeable properties on the unforgeable holder for 4415 the interface represented by descriptor. 4416 4417 properties is a PropertyArrays instance. 4418 4419 """ 4420 assert ( 4421 properties.unforgeableAttrs.hasNonChromeOnly() 4422 or properties.unforgeableAttrs.hasChromeOnly() 4423 or properties.unforgeableMethods.hasNonChromeOnly() 4424 or properties.unforgeableMethods.hasChromeOnly() 4425 ) 4426 4427 unforgeables = [] 4428 4429 defineUnforgeableAttrs = fill( 4430 """ 4431 if (!DefineLegacyUnforgeableAttributes(aCx, ${holderName}, %s)) { 4432 $*{failureCode} 4433 } 4434 """, 4435 failureCode=failureCode, 4436 holderName=holderName, 4437 ) 4438 defineUnforgeableMethods = fill( 4439 """ 4440 if (!DefineLegacyUnforgeableMethods(aCx, ${holderName}, %s)) { 4441 $*{failureCode} 4442 } 4443 """, 4444 failureCode=failureCode, 4445 holderName=holderName, 4446 ) 4447 4448 unforgeableMembers = [ 4449 (defineUnforgeableAttrs, properties.unforgeableAttrs), 4450 (defineUnforgeableMethods, properties.unforgeableMethods), 4451 ] 4452 for template, array in unforgeableMembers: 4453 if array.hasNonChromeOnly(): 4454 unforgeables.append(CGGeneric(template % array.variableName(False))) 4455 if array.hasChromeOnly(): 4456 unforgeables.append( 4457 CGIfWrapper( 4458 CGGeneric(template % array.variableName(True)), 4459 "nsContentUtils::ThreadsafeIsSystemCaller(aCx)", 4460 ) 4461 ) 4462 4463 if descriptor.interface.getExtendedAttribute("LegacyUnforgeable"): 4464 # We do our undefined toPrimitive here, not as a regular property 4465 # because we don't have a concept of value props anywhere in IDL. 4466 unforgeables.append( 4467 CGGeneric( 4468 fill( 4469 """ 4470 JS::Rooted<JS::PropertyKey> toPrimitive(aCx, 4471 JS::GetWellKnownSymbolKey(aCx, JS::SymbolCode::toPrimitive)); 4472 if (!JS_DefinePropertyById(aCx, ${holderName}, toPrimitive, 4473 JS::UndefinedHandleValue, 4474 JSPROP_READONLY | JSPROP_PERMANENT)) { 4475 $*{failureCode} 4476 } 4477 """, 4478 failureCode=failureCode, 4479 holderName=holderName, 4480 ) 4481 ) 4482 ) 4483 4484 return CGWrapper(CGList(unforgeables), pre="\n") 4485 4486 4487 def CopyUnforgeablePropertiesToInstance(descriptor, failureCode): 4488 """ 4489 Copy the unforgeable properties from the unforgeable holder for 4490 this interface to the instance object we have. 4491 """ 4492 assert not descriptor.isGlobal() 4493 4494 if not descriptor.hasLegacyUnforgeableMembers: 4495 return "" 4496 4497 copyCode = [ 4498 CGGeneric( 4499 dedent( 4500 """ 4501 // Important: do unforgeable property setup after we have handed 4502 // over ownership of the C++ object to obj as needed, so that if 4503 // we fail and it ends up GCed it won't have problems in the 4504 // finalizer trying to drop its ownership of the C++ object. 4505 """ 4506 ) 4507 ) 4508 ] 4509 4510 # For proxies, we want to define on the expando object, not directly on the 4511 # reflector, so we can make sure we don't get confused by named getters. 4512 if descriptor.proxy: 4513 copyCode.append( 4514 CGGeneric( 4515 fill( 4516 """ 4517 JS::Rooted<JSObject*> expando(aCx, 4518 DOMProxyHandler::EnsureExpandoObject(aCx, aReflector)); 4519 if (!expando) { 4520 $*{failureCode} 4521 } 4522 """, 4523 failureCode=failureCode, 4524 ) 4525 ) 4526 ) 4527 obj = "expando" 4528 else: 4529 obj = "aReflector" 4530 4531 copyCode.append( 4532 CGGeneric( 4533 fill( 4534 """ 4535 JS::Rooted<JSObject*> unforgeableHolder(aCx, 4536 &JS::GetReservedSlot(canonicalProto, DOM_INTERFACE_PROTO_SLOTS_BASE).toObject()); 4537 if (!JS_InitializePropertiesFromCompatibleNativeObject(aCx, ${obj}, unforgeableHolder)) { 4538 $*{failureCode} 4539 } 4540 """, 4541 obj=obj, 4542 failureCode=failureCode, 4543 ) 4544 ) 4545 ) 4546 4547 return CGWrapper(CGList(copyCode), pre="\n").define() 4548 4549 4550 def AssertInheritanceChain(descriptor): 4551 # We can skip the reinterpret_cast check for the descriptor's nativeType 4552 # if aObject is a pointer of that type. 4553 asserts = fill( 4554 """ 4555 static_assert(std::is_same_v<decltype(aObject), ${nativeType}*>); 4556 """, 4557 nativeType=descriptor.nativeType, 4558 ) 4559 iface = descriptor.interface 4560 while iface.parent: 4561 iface = iface.parent 4562 desc = descriptor.getDescriptor(iface.identifier.name) 4563 asserts += ( 4564 "MOZ_ASSERT(static_cast<%s*>(aObject) == \n" 4565 " reinterpret_cast<%s*>(aObject),\n" 4566 ' "Multiple inheritance for %s is broken.");\n' 4567 % (desc.nativeType, desc.nativeType, desc.nativeType) 4568 ) 4569 asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n" 4570 return asserts 4571 4572 4573 def InitMemberSlots(descriptor, failureCode): 4574 """ 4575 Initialize member slots on our JS object if we're supposed to have some. 4576 4577 Note that this is called after the SetWrapper() call in the 4578 wrapperCache case, since that can affect how our getters behave 4579 and we plan to invoke them here. So if we fail, we need to 4580 ClearWrapper. 4581 """ 4582 if not descriptor.interface.hasMembersInSlots(): 4583 return "" 4584 return fill( 4585 """ 4586 if (!UpdateMemberSlots(aCx, aReflector, aObject)) { 4587 $*{failureCode} 4588 } 4589 """, 4590 failureCode=failureCode, 4591 ) 4592 4593 4594 def DeclareProto(descriptor, noGivenProto=False): 4595 """ 4596 Declare the canonicalProto and proto we have for our wrapping operation. 4597 """ 4598 getCanonical = dedent( 4599 """ 4600 JS::Handle<JSObject*> ${canonicalProto} = GetProtoObjectHandle(aCx); 4601 if (!${canonicalProto}) { 4602 return false; 4603 } 4604 """ 4605 ) 4606 4607 if noGivenProto: 4608 return fill(getCanonical, canonicalProto="proto") 4609 4610 getCanonical = fill(getCanonical, canonicalProto="canonicalProto") 4611 4612 preamble = getCanonical + dedent( 4613 """ 4614 JS::Rooted<JSObject*> proto(aCx); 4615 """ 4616 ) 4617 if descriptor.isMaybeCrossOriginObject(): 4618 return preamble + dedent( 4619 """ 4620 MOZ_ASSERT(!aGivenProto, 4621 "Shouldn't have constructors on cross-origin objects"); 4622 // Set proto to canonicalProto to avoid preserving our wrapper if 4623 // we don't have to. 4624 proto = canonicalProto; 4625 """ 4626 ) 4627 4628 return preamble + dedent( 4629 """ 4630 if (aGivenProto) { 4631 proto = aGivenProto; 4632 // Unfortunately, while aGivenProto was in the compartment of aCx 4633 // coming in, we changed compartments to that of "parent" so may need 4634 // to wrap the proto here. 4635 if (js::GetContextCompartment(aCx) != JS::GetCompartment(proto)) { 4636 if (!JS_WrapObject(aCx, &proto)) { 4637 return false; 4638 } 4639 } 4640 } else { 4641 proto = canonicalProto; 4642 } 4643 """ 4644 ) 4645 4646 4647 class CGWrapWithCacheMethod(CGAbstractMethod): 4648 """ 4649 Create a wrapper JSObject for a given native that implements nsWrapperCache. 4650 """ 4651 4652 def __init__(self, descriptor): 4653 assert descriptor.interface.hasInterfacePrototypeObject() 4654 args = [ 4655 Argument("JSContext*", "aCx"), 4656 Argument(descriptor.nativeType + "*", "aObject"), 4657 Argument("nsWrapperCache*", "aCache"), 4658 Argument("JS::Handle<JSObject*>", "aGivenProto"), 4659 Argument("JS::MutableHandle<JSObject*>", "aReflector"), 4660 ] 4661 CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args) 4662 4663 def definition_body(self): 4664 failureCode = dedent( 4665 """ 4666 aCache->ReleaseWrapper(aObject); 4667 aCache->ClearWrapper(); 4668 return false; 4669 """ 4670 ) 4671 4672 if self.descriptor.proxy: 4673 finalize = "DOMProxyHandler::getInstance()->finalize" 4674 else: 4675 finalize = FINALIZE_HOOK_NAME 4676 4677 return fill( 4678 """ 4679 static_assert(!std::is_base_of_v<NonRefcountedDOMObject, ${nativeType}>, 4680 "Shouldn't have wrappercached things that are not refcounted."); 4681 $*{assertInheritance} 4682 MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx)); 4683 MOZ_ASSERT(!aCache->GetWrapper(), 4684 "You should probably not be using Wrap() directly; use " 4685 "GetOrCreateDOMReflector instead"); 4686 4687 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache), 4688 "nsISupports must be on our primary inheritance chain"); 4689 4690 // If the wrapper cache contains a dead reflector then finalize that 4691 // now, ensuring that the finalizer for the old reflector always 4692 // runs before the new reflector is created and attached. This 4693 // avoids the awkward situation where there are multiple reflector 4694 // objects that contain pointers to the same native. 4695 4696 if (JSObject* oldReflector = aCache->GetWrapperMaybeDead()) { 4697 ${finalize}(nullptr /* unused */, oldReflector); 4698 MOZ_ASSERT(!aCache->GetWrapperMaybeDead()); 4699 } 4700 4701 JS::Rooted<JSObject*> global(aCx, FindAssociatedGlobal(aCx, aObject->GetParentObject())); 4702 if (!global) { 4703 return false; 4704 } 4705 MOZ_ASSERT(JS_IsGlobalObject(global)); 4706 JS::AssertObjectIsNotGray(global); 4707 4708 // That might have ended up wrapping us already, due to the wonders 4709 // of XBL. Check for that, and bail out as needed. 4710 aReflector.set(aCache->GetWrapper()); 4711 if (aReflector) { 4712 #ifdef DEBUG 4713 AssertReflectorHasGivenProto(aCx, aReflector, aGivenProto); 4714 #endif // DEBUG 4715 return true; 4716 } 4717 4718 JSAutoRealm ar(aCx, global); 4719 $*{declareProto} 4720 4721 $*{createObject} 4722 4723 aCache->SetWrapper(aReflector); 4724 $*{unforgeable} 4725 $*{slots} 4726 creator.InitializationSucceeded(); 4727 4728 MOZ_ASSERT(aCache->GetWrapperPreserveColor() && 4729 aCache->GetWrapperPreserveColor() == aReflector); 4730 // If proto != canonicalProto, we have to preserve our wrapper; 4731 // otherwise we won't be able to properly recreate it later, since 4732 // we won't know what proto to use. Note that we don't check 4733 // aGivenProto here, since it's entirely possible (and even 4734 // somewhat common) to have a non-null aGivenProto which is the 4735 // same as canonicalProto. 4736 if (proto != canonicalProto) { 4737 PreserveWrapper(aObject); 4738 } 4739 4740 return true; 4741 """, 4742 nativeType=self.descriptor.nativeType, 4743 assertInheritance=AssertInheritanceChain(self.descriptor), 4744 declareProto=DeclareProto(self.descriptor), 4745 createObject=CreateBindingJSObject(self.descriptor), 4746 unforgeable=CopyUnforgeablePropertiesToInstance( 4747 self.descriptor, failureCode 4748 ), 4749 slots=InitMemberSlots(self.descriptor, failureCode), 4750 finalize=finalize, 4751 ) 4752 4753 4754 class CGWrapMethod(CGAbstractMethod): 4755 def __init__(self, descriptor): 4756 # XXX can we wrap if we don't have an interface prototype object? 4757 assert descriptor.interface.hasInterfacePrototypeObject() 4758 args = [ 4759 Argument("JSContext*", "aCx"), 4760 Argument("T*", "aObject"), 4761 Argument("JS::Handle<JSObject*>", "aGivenProto"), 4762 ] 4763 CGAbstractMethod.__init__( 4764 self, 4765 descriptor, 4766 "Wrap", 4767 "JSObject*", 4768 args, 4769 inline=True, 4770 templateArgs=["class T"], 4771 ) 4772 4773 def definition_body(self): 4774 return dedent( 4775 """ 4776 JS::Rooted<JSObject*> reflector(aCx); 4777 return Wrap(aCx, aObject, aObject, aGivenProto, &reflector) ? reflector.get() : nullptr; 4778 """ 4779 ) 4780 4781 4782 class CGWrapNonWrapperCacheMethod(CGAbstractMethod): 4783 """ 4784 Create a wrapper JSObject for a given native that does not implement 4785 nsWrapperCache. 4786 """ 4787 4788 def __init__(self, descriptor, static=False, signatureOnly=False): 4789 # XXX can we wrap if we don't have an interface prototype object? 4790 assert descriptor.interface.hasInterfacePrototypeObject() 4791 self.noGivenProto = ( 4792 descriptor.interface.isIteratorInterface() 4793 or descriptor.interface.isAsyncIteratorInterface() 4794 ) 4795 args = [ 4796 Argument("JSContext*", "aCx"), 4797 Argument(descriptor.nativeType + "*", "aObject"), 4798 ] 4799 if not self.noGivenProto: 4800 args.append(Argument("JS::Handle<JSObject*>", "aGivenProto")) 4801 args.append(Argument("JS::MutableHandle<JSObject*>", "aReflector")) 4802 CGAbstractMethod.__init__( 4803 self, 4804 descriptor, 4805 "Wrap", 4806 "bool", 4807 args, 4808 static=static, 4809 signatureOnly=signatureOnly, 4810 ) 4811 4812 def definition_body(self): 4813 failureCode = "return false;\n" 4814 4815 declareProto = DeclareProto(self.descriptor, noGivenProto=self.noGivenProto) 4816 if self.noGivenProto: 4817 assertGivenProto = "" 4818 else: 4819 assertGivenProto = dedent( 4820 """ 4821 MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx)); 4822 """ 4823 ) 4824 return fill( 4825 """ 4826 $*{assertions} 4827 $*{assertGivenProto} 4828 4829 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx)); 4830 $*{declareProto} 4831 4832 $*{createObject} 4833 4834 $*{unforgeable} 4835 4836 $*{slots} 4837 4838 creator.InitializationSucceeded(); 4839 return true; 4840 """, 4841 assertions=AssertInheritanceChain(self.descriptor), 4842 assertGivenProto=assertGivenProto, 4843 declareProto=declareProto, 4844 createObject=CreateBindingJSObject(self.descriptor), 4845 unforgeable=CopyUnforgeablePropertiesToInstance( 4846 self.descriptor, failureCode 4847 ), 4848 slots=InitMemberSlots(self.descriptor, failureCode), 4849 ) 4850 4851 4852 class CGWrapGlobalMethod(CGAbstractMethod): 4853 """ 4854 Create a wrapper JSObject for a global. The global must implement 4855 nsWrapperCache. 4856 4857 properties should be a PropertyArrays instance. 4858 """ 4859 4860 def __init__(self, descriptor, properties): 4861 assert ( 4862 descriptor.interface.hasInterfacePrototypeObject() 4863 or descriptor.hasOrdinaryObjectPrototype() 4864 ) 4865 args = [ 4866 Argument("JSContext*", "aCx"), 4867 Argument(descriptor.nativeType + "*", "aObject"), 4868 Argument("nsWrapperCache*", "aCache"), 4869 Argument("JS::RealmOptions&", "aOptions"), 4870 Argument("JSPrincipals*", "aPrincipal"), 4871 Argument("JS::MutableHandle<JSObject*>", "aReflector"), 4872 ] 4873 CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args) 4874 self.descriptor = descriptor 4875 self.properties = properties 4876 4877 def definition_body(self): 4878 if self.properties.hasNonChromeOnly(): 4879 properties = "sNativeProperties.Upcast()" 4880 else: 4881 properties = "nullptr" 4882 if self.properties.hasChromeOnly(): 4883 chromeProperties = "nsContentUtils::ThreadsafeIsSystemCaller(aCx) ? sChromeOnlyNativeProperties.Upcast() : nullptr" 4884 else: 4885 chromeProperties = "nullptr" 4886 4887 failureCode = dedent( 4888 """ 4889 aCache->ReleaseWrapper(aObject); 4890 aCache->ClearWrapper(); 4891 return false; 4892 """ 4893 ) 4894 4895 if self.descriptor.hasLegacyUnforgeableMembers: 4896 unforgeable = InitUnforgeablePropertiesOnHolder( 4897 self.descriptor, self.properties, failureCode, "aReflector" 4898 ).define() 4899 else: 4900 unforgeable = "" 4901 4902 if self.descriptor.hasOrdinaryObjectPrototype(): 4903 getProto = "JS::GetRealmObjectPrototypeHandle" 4904 else: 4905 getProto = "GetProtoObjectHandle" 4906 return fill( 4907 """ 4908 $*{assertions} 4909 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache), 4910 "nsISupports must be on our primary inheritance chain"); 4911 4912 if (!CreateGlobal<${nativeType}, ${getProto}>(aCx, 4913 aObject, 4914 aCache, 4915 sClass.ToJSClass(), 4916 aOptions, 4917 aPrincipal, 4918 aReflector)) { 4919 $*{failureCode} 4920 } 4921 4922 // aReflector is a new global, so has a new realm. Enter it 4923 // before doing anything with it. 4924 JSAutoRealm ar(aCx, aReflector); 4925 4926 if (!DefineProperties(aCx, aReflector, ${properties}, ${chromeProperties})) { 4927 $*{failureCode} 4928 } 4929 $*{unforgeable} 4930 4931 $*{slots} 4932 4933 return true; 4934 """, 4935 assertions=AssertInheritanceChain(self.descriptor), 4936 nativeType=self.descriptor.nativeType, 4937 getProto=getProto, 4938 properties=properties, 4939 chromeProperties=chromeProperties, 4940 failureCode=failureCode, 4941 unforgeable=unforgeable, 4942 slots=InitMemberSlots(self.descriptor, failureCode), 4943 ) 4944 4945 4946 class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod): 4947 def __init__(self, descriptor): 4948 args = [ 4949 Argument("JSContext*", "aCx"), 4950 Argument("JS::Handle<JSObject*>", "aWrapper"), 4951 Argument(descriptor.nativeType + "*", "aObject"), 4952 ] 4953 CGAbstractStaticMethod.__init__( 4954 self, descriptor, "UpdateMemberSlots", "bool", args 4955 ) 4956 4957 def definition_body(self): 4958 body = "JS::Rooted<JS::Value> temp(aCx);\n" "JSJitGetterCallArgs args(&temp);\n" 4959 for m in self.descriptor.interface.members: 4960 if m.isAttr() and m.getExtendedAttribute("StoreInSlot"): 4961 # Skip doing this for the "window" and "self" attributes on the 4962 # Window interface, because those can't be gotten safely until 4963 # we have hooked it up correctly to the outer window. The 4964 # window code handles doing the get itself. 4965 if self.descriptor.interface.identifier.name == "Window" and ( 4966 m.identifier.name == "window" or m.identifier.name == "self" 4967 ): 4968 continue 4969 body += fill( 4970 """ 4971 4972 static_assert(${slot} < JS::shadow::Object::MAX_FIXED_SLOTS, 4973 "Not enough fixed slots to fit '${interface}.${member}. Ion's visitGetDOMMemberV/visitGetDOMMemberT assume StoreInSlot things are all in fixed slots."); 4974 if (!get_${member}(aCx, aWrapper, aObject, args)) { 4975 return false; 4976 } 4977 // Getter handled setting our reserved slots 4978 """, 4979 slot=memberReservedSlot(m, self.descriptor), 4980 interface=self.descriptor.interface.identifier.name, 4981 member=m.identifier.name, 4982 ) 4983 4984 body += "\nreturn true;\n" 4985 return body 4986 4987 4988 class CGClearCachedValueMethod(CGAbstractMethod): 4989 def __init__(self, descriptor, member): 4990 self.member = member 4991 # If we're StoreInSlot, we'll need to call the getter 4992 if member.getExtendedAttribute("StoreInSlot"): 4993 args = [Argument("JSContext*", "aCx")] 4994 returnType = "bool" 4995 else: 4996 args = [] 4997 returnType = "void" 4998 args.append(Argument(descriptor.nativeType + "*", "aObject")) 4999 name = MakeClearCachedValueNativeName(member) 5000 CGAbstractMethod.__init__(self, descriptor, name, returnType, args) 5001 5002 def definition_body(self): 5003 slotIndex = memberReservedSlot(self.member, self.descriptor) 5004 clearCachedValue = fill( 5005 """ 5006 JS::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue()); 5007 """, 5008 slotIndex=slotIndex, 5009 ) 5010 if self.member.getExtendedAttribute("StoreInSlot"): 5011 # We have to root things and save the old value in case 5012 # regetting fails, so we can restore it. 5013 declObj = "JS::Rooted<JSObject*> obj(aCx);\n" 5014 noopRetval = " true" 5015 saveMember = ( 5016 "JS::Rooted<JS::Value> oldValue(aCx, JS::GetReservedSlot(obj, %s));\n" 5017 % slotIndex 5018 ) 5019 regetMember = fill( 5020 """ 5021 JS::Rooted<JS::Value> temp(aCx); 5022 JSJitGetterCallArgs args(&temp); 5023 JSAutoRealm ar(aCx, obj); 5024 if (!get_${name}(aCx, obj, aObject, args)) { 5025 JS::SetReservedSlot(obj, ${slotIndex}, oldValue); 5026 return false; 5027 } 5028 return true; 5029 """, 5030 name=self.member.identifier.name, 5031 slotIndex=slotIndex, 5032 ) 5033 else: 5034 declObj = "JSObject* obj;\n" 5035 noopRetval = "" 5036 saveMember = "" 5037 if self.member.getExtendedAttribute( 5038 "ReflectedHTMLAttributeReturningFrozenArray" 5039 ): 5040 clearCachedValue = fill( 5041 """ 5042 ReflectedHTMLAttributeSlots::Clear(obj, ${arrayIndex}); 5043 """, 5044 arrayIndex=reflectedHTMLAttributesArrayIndex( 5045 self.descriptor, self.member 5046 ), 5047 ) 5048 regetMember = "" 5049 5050 if self.descriptor.wantsXrays: 5051 if self.member.getExtendedAttribute("StoreInSlot"): 5052 cx = "JS::RootingContext::get(aCx)" 5053 else: 5054 cx = "RootingCx()" 5055 if self.member.getExtendedAttribute( 5056 "ReflectedHTMLAttributeReturningFrozenArray" 5057 ): 5058 clearXrayExpandoSlots = fill( 5059 """ 5060 ReflectedHTMLAttributeSlots::ClearInXrays(${cx}, obj, ${arrayIndex}); 5061 """, 5062 cx=cx, 5063 arrayIndex=reflectedHTMLAttributesArrayIndex( 5064 self.descriptor, self.member 5065 ), 5066 ) 5067 else: 5068 clearXrayExpandoSlots = fill( 5069 """ 5070 ClearXrayExpandoSlots(${cx}, obj, ${xraySlotIndex}); 5071 """, 5072 cx=cx, 5073 xraySlotIndex=memberXrayExpandoReservedSlot( 5074 self.member, self.descriptor 5075 ), 5076 ) 5077 else: 5078 clearXrayExpandoSlots = "" 5079 5080 return fill( 5081 """ 5082 $*{declObj} 5083 obj = aObject->GetWrapper(); 5084 if (!obj) { 5085 return${noopRetval}; 5086 } 5087 $*{saveMember} 5088 $*{clearCachedValue} 5089 $*{clearXrayExpandoSlots} 5090 $*{regetMember} 5091 """, 5092 declObj=declObj, 5093 noopRetval=noopRetval, 5094 saveMember=saveMember, 5095 slotIndex=slotIndex, 5096 clearCachedValue=clearCachedValue, 5097 clearXrayExpandoSlots=clearXrayExpandoSlots, 5098 regetMember=regetMember, 5099 ) 5100 5101 5102 class CGCrossOriginProperties(CGThing): 5103 def __init__(self, descriptor): 5104 attrs = [] 5105 chromeOnlyAttrs = [] 5106 methods = [] 5107 chromeOnlyMethods = [] 5108 for m in descriptor.interface.members: 5109 if m.isAttr() and ( 5110 m.getExtendedAttribute("CrossOriginReadable") 5111 or m.getExtendedAttribute("CrossOriginWritable") 5112 ): 5113 if m.isStatic(): 5114 raise TypeError( 5115 "Don't know how to deal with static method %s" 5116 % m.identifier.name 5117 ) 5118 if PropertyDefiner.getControllingCondition( 5119 m, descriptor 5120 ).hasDisablers(): 5121 raise TypeError( 5122 "Don't know how to deal with disabler for %s" 5123 % m.identifier.name 5124 ) 5125 if len(m.bindingAliases) > 0: 5126 raise TypeError( 5127 "Don't know how to deal with aliases for %s" % m.identifier.name 5128 ) 5129 if m.getExtendedAttribute("ChromeOnly") is not None: 5130 chromeOnlyAttrs.extend(AttrDefiner.attrData(m, overrideFlags="0")) 5131 else: 5132 attrs.extend(AttrDefiner.attrData(m, overrideFlags="0")) 5133 elif m.isMethod() and m.getExtendedAttribute("CrossOriginCallable"): 5134 if m.isStatic(): 5135 raise TypeError( 5136 "Don't know how to deal with static method %s" 5137 % m.identifier.name 5138 ) 5139 if PropertyDefiner.getControllingCondition( 5140 m, descriptor 5141 ).hasDisablers(): 5142 raise TypeError( 5143 "Don't know how to deal with disabler for %s" 5144 % m.identifier.name 5145 ) 5146 if len(m.aliases) > 0: 5147 raise TypeError( 5148 "Don't know how to deal with aliases for %s" % m.identifier.name 5149 ) 5150 if m.getExtendedAttribute("ChromeOnly") is not None: 5151 chromeOnlyMethods.append( 5152 MethodDefiner.methodData( 5153 m, descriptor, overrideFlags="JSPROP_READONLY" 5154 ) 5155 ) 5156 else: 5157 methods.append( 5158 MethodDefiner.methodData( 5159 m, descriptor, overrideFlags="JSPROP_READONLY" 5160 ) 5161 ) 5162 5163 if len(attrs) > 0: 5164 self.attributeSpecs, _ = PropertyDefiner.generatePrefableArrayValues( 5165 attrs, 5166 descriptor, 5167 AttrDefiner.formatSpec, 5168 " JS_PS_END\n", 5169 AttrDefiner.condition, 5170 functools.partial(AttrDefiner.specData, crossOriginOnly=True), 5171 ) 5172 else: 5173 self.attributeSpecs = [" JS_PS_END\n"] 5174 if len(methods) > 0: 5175 self.methodSpecs, _ = PropertyDefiner.generatePrefableArrayValues( 5176 methods, 5177 descriptor, 5178 MethodDefiner.formatSpec, 5179 " JS_FS_END\n", 5180 MethodDefiner.condition, 5181 MethodDefiner.specData, 5182 ) 5183 else: 5184 self.methodSpecs = [" JS_FS_END\n"] 5185 5186 if len(chromeOnlyAttrs) > 0: 5187 ( 5188 self.chromeOnlyAttributeSpecs, 5189 _, 5190 ) = PropertyDefiner.generatePrefableArrayValues( 5191 chromeOnlyAttrs, 5192 descriptor, 5193 AttrDefiner.formatSpec, 5194 " JS_PS_END\n", 5195 AttrDefiner.condition, 5196 functools.partial(AttrDefiner.specData, crossOriginOnly=True), 5197 ) 5198 else: 5199 self.chromeOnlyAttributeSpecs = [] 5200 if len(chromeOnlyMethods) > 0: 5201 self.chromeOnlyMethodSpecs, _ = PropertyDefiner.generatePrefableArrayValues( 5202 chromeOnlyMethods, 5203 descriptor, 5204 MethodDefiner.formatSpec, 5205 " JS_FS_END\n", 5206 MethodDefiner.condition, 5207 MethodDefiner.specData, 5208 ) 5209 else: 5210 self.chromeOnlyMethodSpecs = [] 5211 5212 def declare(self): 5213 return dedent( 5214 """ 5215 extern const CrossOriginProperties sCrossOriginProperties; 5216 """ 5217 ) 5218 5219 def define(self): 5220 def defineChromeOnly(name, specs, specType): 5221 if len(specs) == 0: 5222 return ("", "nullptr") 5223 name = "sChromeOnlyCrossOrigin" + name 5224 define = fill( 5225 """ 5226 static const ${specType} ${name}[] = { 5227 $*{specs} 5228 }; 5229 """, 5230 specType=specType, 5231 name=name, 5232 specs=",\n".join(specs), 5233 ) 5234 return (define, name) 5235 5236 chromeOnlyAttributes = defineChromeOnly( 5237 "Attributes", self.chromeOnlyAttributeSpecs, "JSPropertySpec" 5238 ) 5239 chromeOnlyMethods = defineChromeOnly( 5240 "Methods", self.chromeOnlyMethodSpecs, "JSFunctionSpec" 5241 ) 5242 return fill( 5243 """ 5244 static const JSPropertySpec sCrossOriginAttributes[] = { 5245 $*{attributeSpecs} 5246 }; 5247 static const JSFunctionSpec sCrossOriginMethods[] = { 5248 $*{methodSpecs} 5249 }; 5250 $*{chromeOnlyAttributeSpecs} 5251 $*{chromeOnlyMethodSpecs} 5252 const CrossOriginProperties sCrossOriginProperties = { 5253 sCrossOriginAttributes, 5254 sCrossOriginMethods, 5255 ${chromeOnlyAttributes}, 5256 ${chromeOnlyMethods} 5257 }; 5258 """, 5259 attributeSpecs=",\n".join(self.attributeSpecs), 5260 methodSpecs=",\n".join(self.methodSpecs), 5261 chromeOnlyAttributeSpecs=chromeOnlyAttributes[0], 5262 chromeOnlyMethodSpecs=chromeOnlyMethods[0], 5263 chromeOnlyAttributes=chromeOnlyAttributes[1], 5264 chromeOnlyMethods=chromeOnlyMethods[1], 5265 ) 5266 5267 5268 class CGCycleCollectionTraverseForOwningUnionMethod(CGAbstractMethod): 5269 """ 5270 ImplCycleCollectionUnlink for owning union type. 5271 """ 5272 5273 def __init__(self, type): 5274 self.type = type 5275 args = [ 5276 Argument("nsCycleCollectionTraversalCallback&", "aCallback"), 5277 Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion"), 5278 Argument("const char*", "aName"), 5279 Argument("uint32_t", "aFlags", "0"), 5280 ] 5281 CGAbstractMethod.__init__( 5282 self, None, "ImplCycleCollectionTraverse", "void", args 5283 ) 5284 5285 def deps(self): 5286 return self.type.getDeps() 5287 5288 def definition_body(self): 5289 memberNames = [ 5290 getUnionMemberName(t) 5291 for t in self.type.flatMemberTypes 5292 if idlTypeNeedsCycleCollection(t) 5293 ] 5294 assert memberNames 5295 5296 conditionTemplate = "aUnion.Is%s()" 5297 functionCallTemplate = ( 5298 'ImplCycleCollectionTraverse(aCallback, aUnion.GetAs%s(), "m%s", aFlags);\n' 5299 ) 5300 5301 ifStaments = ( 5302 CGIfWrapper(CGGeneric(functionCallTemplate % (m, m)), conditionTemplate % m) 5303 for m in memberNames 5304 ) 5305 5306 return CGElseChain(ifStaments).define() 5307 5308 5309 class CGCycleCollectionUnlinkForOwningUnionMethod(CGAbstractMethod): 5310 """ 5311 ImplCycleCollectionUnlink for owning union type. 5312 """ 5313 5314 def __init__(self, type): 5315 self.type = type 5316 args = [Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion")] 5317 CGAbstractMethod.__init__(self, None, "ImplCycleCollectionUnlink", "void", args) 5318 5319 def deps(self): 5320 return self.type.getDeps() 5321 5322 def definition_body(self): 5323 return "aUnion.Uninit();\n" 5324 5325 5326 builtinNames = { 5327 IDLType.Tags.bool: "bool", 5328 IDLType.Tags.int8: "int8_t", 5329 IDLType.Tags.int16: "int16_t", 5330 IDLType.Tags.int32: "int32_t", 5331 IDLType.Tags.int64: "int64_t", 5332 IDLType.Tags.uint8: "uint8_t", 5333 IDLType.Tags.uint16: "uint16_t", 5334 IDLType.Tags.uint32: "uint32_t", 5335 IDLType.Tags.uint64: "uint64_t", 5336 IDLType.Tags.unrestricted_float: "float", 5337 IDLType.Tags.float: "float", 5338 IDLType.Tags.unrestricted_double: "double", 5339 IDLType.Tags.double: "double", 5340 } 5341 5342 numericSuffixes = { 5343 IDLType.Tags.int8: "", 5344 IDLType.Tags.uint8: "", 5345 IDLType.Tags.int16: "", 5346 IDLType.Tags.uint16: "", 5347 IDLType.Tags.int32: "", 5348 IDLType.Tags.uint32: "U", 5349 IDLType.Tags.int64: "LL", 5350 IDLType.Tags.uint64: "ULL", 5351 IDLType.Tags.unrestricted_float: "F", 5352 IDLType.Tags.float: "F", 5353 IDLType.Tags.unrestricted_double: "", 5354 IDLType.Tags.double: "", 5355 } 5356 5357 5358 def numericValue(t, v): 5359 if t == IDLType.Tags.unrestricted_double or t == IDLType.Tags.unrestricted_float: 5360 typeName = builtinNames[t] 5361 if v == float("inf"): 5362 return "mozilla::PositiveInfinity<%s>()" % typeName 5363 if v == float("-inf"): 5364 return "mozilla::NegativeInfinity<%s>()" % typeName 5365 if math.isnan(v): 5366 return "mozilla::UnspecifiedNaN<%s>()" % typeName 5367 return "%s%s" % (v, numericSuffixes[t]) 5368 5369 5370 class CastableObjectUnwrapper: 5371 """ 5372 A class for unwrapping an object stored in a JS Value (or 5373 MutableHandle<Value> or Handle<Value>) named by the "source" and 5374 "mutableSource" arguments based on the passed-in descriptor and storing it 5375 in a variable called by the name in the "target" argument. The "source" 5376 argument should be able to produce a Value or Handle<Value>; the 5377 "mutableSource" argument should be able to produce a MutableHandle<Value> 5378 5379 codeOnFailure is the code to run if unwrapping fails. 5380 5381 If isCallbackReturnValue is "JSImpl" and our descriptor is also 5382 JS-implemented, fall back to just creating the right object if what we 5383 have isn't one already. 5384 """ 5385 5386 def __init__( 5387 self, 5388 descriptor, 5389 source, 5390 mutableSource, 5391 target, 5392 codeOnFailure, 5393 exceptionCode=None, 5394 isCallbackReturnValue=False, 5395 ): 5396 self.substitution = { 5397 "type": descriptor.nativeType, 5398 "protoID": "prototypes::id::" + descriptor.name, 5399 "target": target, 5400 "codeOnFailure": codeOnFailure, 5401 "source": source, 5402 "mutableSource": mutableSource, 5403 } 5404 5405 if isCallbackReturnValue == "JSImpl" and descriptor.interface.isJSImplemented(): 5406 exceptionCode = exceptionCode or codeOnFailure 5407 self.substitution["codeOnFailure"] = fill( 5408 """ 5409 // Be careful to not wrap random DOM objects here, even if 5410 // they're wrapped in opaque security wrappers for some reason. 5411 // XXXbz Wish we could check for a JS-implemented object 5412 // that already has a content reflection... 5413 if (!IsDOMObject(js::UncheckedUnwrap(&${source}.toObject()))) { 5414 nsCOMPtr<nsIGlobalObject> contentGlobal; 5415 JS::Rooted<JSObject*> callback(cx, CallbackOrNull()); 5416 if (!callback || 5417 !GetContentGlobalForJSImplementedObject(cx, callback, getter_AddRefs(contentGlobal))) { 5418 $*{exceptionCode} 5419 } 5420 JS::Rooted<JSObject*> jsImplSourceObj(cx, &${source}.toObject()); 5421 MOZ_RELEASE_ASSERT(!js::IsWrapper(jsImplSourceObj), 5422 "Don't return JS implementations from other compartments"); 5423 JS::Rooted<JSObject*> jsImplSourceGlobal(cx, JS::GetNonCCWObjectGlobal(jsImplSourceObj)); 5424 ${target} = new ${type}(jsImplSourceObj, jsImplSourceGlobal, contentGlobal); 5425 } else { 5426 $*{codeOnFailure} 5427 } 5428 """, 5429 exceptionCode=exceptionCode, 5430 **self.substitution, 5431 ) 5432 else: 5433 self.substitution["codeOnFailure"] = codeOnFailure 5434 5435 def __str__(self): 5436 substitution = self.substitution.copy() 5437 substitution["codeOnFailure"] %= { 5438 "securityError": "rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO" 5439 } 5440 return fill( 5441 """ 5442 { 5443 // Our JSContext should be in the right global to do unwrapping in. 5444 nsresult rv = UnwrapObject<${protoID}, ${type}>(${mutableSource}, ${target}, cx); 5445 if (NS_FAILED(rv)) { 5446 $*{codeOnFailure} 5447 } 5448 } 5449 """, 5450 **substitution, 5451 ) 5452 5453 5454 class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper): 5455 """ 5456 As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails 5457 """ 5458 5459 def __init__( 5460 self, 5461 descriptor, 5462 source, 5463 mutableSource, 5464 target, 5465 exceptionCode, 5466 isCallbackReturnValue, 5467 sourceDescription, 5468 ): 5469 CastableObjectUnwrapper.__init__( 5470 self, 5471 descriptor, 5472 source, 5473 mutableSource, 5474 target, 5475 'cx.ThrowErrorMessage<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("%s", "%s");\n' 5476 "%s" 5477 % (sourceDescription, descriptor.interface.identifier.name, exceptionCode), 5478 exceptionCode, 5479 isCallbackReturnValue, 5480 ) 5481 5482 5483 def getCallbackConversionInfo( 5484 type, idlObject, isMember, isCallbackReturnValue, isOptional 5485 ): 5486 """ 5487 Returns a tuple containing the declType, declArgs, and basic 5488 conversion for the given callback type, with the given callback 5489 idl object in the given context (isMember/isCallbackReturnValue/isOptional). 5490 """ 5491 name = idlObject.identifier.name 5492 5493 # We can't use fast callbacks if isOptional because then we get an 5494 # Optional<RootedCallback> thing, which is not transparent to consumers. 5495 useFastCallback = ( 5496 (not isMember or isMember == "Union") 5497 and not isCallbackReturnValue 5498 and not isOptional 5499 ) 5500 if useFastCallback: 5501 name = "binding_detail::Fast%s" % name 5502 rootArgs = "" 5503 args = "&${val}.toObject(), JS::CurrentGlobalOrNull(cx)" 5504 else: 5505 rootArgs = dedent( 5506 """ 5507 JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject()); 5508 JS::Rooted<JSObject*> tempGlobalRoot(cx, JS::CurrentGlobalOrNull(cx)); 5509 """ 5510 ) 5511 args = "cx, tempRoot, tempGlobalRoot, GetIncumbentGlobal()" 5512 5513 if type.nullable() or isCallbackReturnValue: 5514 declType = CGGeneric("RefPtr<%s>" % name) 5515 else: 5516 declType = CGGeneric("OwningNonNull<%s>" % name) 5517 5518 if useFastCallback: 5519 declType = CGTemplatedType("RootedCallback", declType) 5520 declArgs = "cx" 5521 else: 5522 declArgs = None 5523 5524 conversion = fill( 5525 """ 5526 { // scope for tempRoot and tempGlobalRoot if needed 5527 $*{rootArgs} 5528 $${declName} = new ${name}(${args}); 5529 } 5530 """, 5531 rootArgs=rootArgs, 5532 name=name, 5533 args=args, 5534 ) 5535 return (declType, declArgs, conversion) 5536 5537 5538 class JSToNativeConversionInfo: 5539 """ 5540 An object representing information about a JS-to-native conversion. 5541 """ 5542 5543 def __init__( 5544 self, 5545 template, 5546 declType=None, 5547 holderType=None, 5548 dealWithOptional=False, 5549 declArgs=None, 5550 holderArgs=None, 5551 ): 5552 """ 5553 template: A string representing the conversion code. This will have 5554 template substitution performed on it as follows: 5555 5556 ${val} is a handle to the JS::Value in question 5557 ${maybeMutableVal} May be a mutable handle to the JS::Value in 5558 question. This is only OK to use if ${val} is 5559 known to not be undefined. 5560 ${holderName} replaced by the holder's name, if any 5561 ${declName} replaced by the declaration's name 5562 ${haveValue} replaced by an expression that evaluates to a boolean 5563 for whether we have a JS::Value. Only used when 5564 defaultValue is not None or when True is passed for 5565 checkForValue to instantiateJSToNativeConversion. 5566 This expression may not be already-parenthesized, so if 5567 you use it with && or || make sure to put parens 5568 around it. 5569 ${passedToJSImpl} replaced by an expression that evaluates to a boolean 5570 for whether this value is being passed to a JS- 5571 implemented interface. 5572 5573 declType: A CGThing representing the native C++ type we're converting 5574 to. This is allowed to be None if the conversion code is 5575 supposed to be used as-is. 5576 5577 holderType: A CGThing representing the type of a "holder" which will 5578 hold a possible reference to the C++ thing whose type we 5579 returned in declType, or None if no such holder is needed. 5580 5581 dealWithOptional: A boolean indicating whether the caller has to do 5582 optional-argument handling. This should only be set 5583 to true if the JS-to-native conversion is being done 5584 for an optional argument or dictionary member with no 5585 default value and if the returned template expects 5586 both declType and holderType to be wrapped in 5587 Optional<>, with ${declName} and ${holderName} 5588 adjusted to point to the Value() of the Optional, and 5589 Construct() calls to be made on the Optional<>s as 5590 needed. 5591 5592 declArgs: If not None, the arguments to pass to the ${declName} 5593 constructor. These will have template substitution performed 5594 on them so you can use things like ${val}. This is a 5595 single string, not a list of strings. 5596 5597 holderArgs: If not None, the arguments to pass to the ${holderName} 5598 constructor. These will have template substitution 5599 performed on them so you can use things like ${val}. 5600 This is a single string, not a list of strings. 5601 5602 ${declName} must be in scope before the code from 'template' is entered. 5603 5604 If holderType is not None then ${holderName} must be in scope before 5605 the code from 'template' is entered. 5606 """ 5607 assert isinstance(template, str) 5608 assert declType is None or isinstance(declType, CGThing) 5609 assert holderType is None or isinstance(holderType, CGThing) 5610 self.template = template 5611 self.declType = declType 5612 self.holderType = holderType 5613 self.dealWithOptional = dealWithOptional 5614 self.declArgs = declArgs 5615 self.holderArgs = holderArgs 5616 5617 5618 def getHandleDefault(defaultValue): 5619 tag = defaultValue.type.tag() 5620 if tag in numericSuffixes: 5621 # Some numeric literals require a suffix to compile without warnings 5622 return numericValue(tag, defaultValue.value) 5623 assert tag == IDLType.Tags.bool 5624 return toStringBool(defaultValue.value) 5625 5626 5627 def handleDefaultStringValue(defaultValue, method): 5628 """ 5629 Returns a string which ends up calling 'method' with a (char_t*, length) 5630 pair that sets this string default value. This string is suitable for 5631 passing as the second argument of handleDefault. 5632 """ 5633 assert ( 5634 defaultValue.type.isDOMString() 5635 or defaultValue.type.isUSVString() 5636 or defaultValue.type.isUTF8String() 5637 or defaultValue.type.isByteString() 5638 ) 5639 # There shouldn't be any non-ASCII or embedded nulls in here; if 5640 # it ever sneaks in we will need to think about how to properly 5641 # represent that in the C++. 5642 assert all(ord(c) < 128 and ord(c) > 0 for c in defaultValue.value) 5643 if defaultValue.type.isByteString() or defaultValue.type.isUTF8String(): 5644 prefix = "" 5645 else: 5646 prefix = "u" 5647 return fill( 5648 """ 5649 ${method}(${prefix}"${value}"); 5650 """, 5651 method=method, 5652 prefix=prefix, 5653 value=defaultValue.value, 5654 ) 5655 5656 5657 def recordKeyType(recordType): 5658 assert recordType.keyType.isString() 5659 if recordType.keyType.isByteString() or recordType.keyType.isUTF8String(): 5660 return "nsCString" 5661 return "nsString" 5662 5663 5664 def recordKeyDeclType(recordType): 5665 return CGGeneric(recordKeyType(recordType)) 5666 5667 5668 def initializerForType(type): 5669 """ 5670 Get the right initializer for the given type for a data location where we 5671 plan to then initialize it from a JS::Value. Some types need to always be 5672 initialized even before we start the JS::Value-to-IDL-value conversion. 5673 5674 Returns a string or None if no initialization is needed. 5675 """ 5676 if type.isObject(): 5677 return "nullptr" 5678 # We could probably return CGDictionary.getNonInitializingCtorArg() for the 5679 # dictionary case, but code outside DictionaryBase subclasses can't use 5680 # that, so we can't do it across the board. 5681 return None 5682 5683 5684 # If this function is modified, modify CGNativeMember.getArg and 5685 # CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype 5686 # and holdertype we end up using, because it needs to be able to return the code 5687 # that will convert those to the actual return value of the callback function. 5688 def getJSToNativeConversionInfo( 5689 type, 5690 descriptorProvider, 5691 failureCode=None, 5692 isDefinitelyObject=False, 5693 isMember=False, 5694 isOptional=False, 5695 invalidEnumValueFatal=True, 5696 defaultValue=None, 5697 isNullOrUndefined=False, 5698 isKnownMissing=False, 5699 exceptionCode=None, 5700 lenientFloatCode=None, 5701 allowTreatNonCallableAsNull=False, 5702 isCallbackReturnValue=False, 5703 sourceDescription="value", 5704 nestingLevel="", 5705 ): 5706 """ 5707 Get a template for converting a JS value to a native object based on the 5708 given type and descriptor. If failureCode is given, then we're actually 5709 testing whether we can convert the argument to the desired type. That 5710 means that failures to convert due to the JS value being the wrong type of 5711 value need to use failureCode instead of throwing exceptions. Failures to 5712 convert that are due to JS exceptions (from toString or valueOf methods) or 5713 out of memory conditions need to throw exceptions no matter what 5714 failureCode is. However what actually happens when throwing an exception 5715 can be controlled by exceptionCode. The only requirement on that is that 5716 exceptionCode must end up doing a return, and every return from this 5717 function must happen via exceptionCode if exceptionCode is not None. 5718 5719 If isDefinitelyObject is True, that means we have a value and the value 5720 tests true for isObject(), so we have no need to recheck that. 5721 5722 If isNullOrUndefined is True, that means we have a value and the value 5723 tests true for isNullOrUndefined(), so we have no need to recheck that. 5724 5725 If isKnownMissing is True, that means that we are known-missing, and for 5726 cases when we have a default value we only need to output the default value. 5727 5728 if isMember is not False, we're being converted from a property of some JS 5729 object, not from an actual method argument, so we can't rely on our jsval 5730 being rooted or outliving us in any way. Callers can pass "Dictionary", 5731 "Variadic", "Sequence", "Union", or "OwningUnion" to indicate that the conversion 5732 is for something that is a dictionary member, a variadic argument, a sequence, 5733 an union, or an owning union respectively. 5734 XXX Once we swtich *Rooter to Rooted* for Record and Sequence type entirely, 5735 we could remove "Union" from isMember. 5736 5737 If isOptional is true, then we are doing conversion of an optional 5738 argument with no default value. 5739 5740 invalidEnumValueFatal controls whether an invalid enum value conversion 5741 attempt will throw (if true) or simply return without doing anything (if 5742 false). 5743 5744 If defaultValue is not None, it's the IDL default value for this conversion 5745 5746 If isEnforceRange is true, we're converting an integer and throwing if the 5747 value is out of range. 5748 5749 If isClamp is true, we're converting an integer and clamping if the 5750 value is out of range. 5751 5752 If isAllowShared is false, we're converting a buffer source and throwing if 5753 it is a SharedArrayBuffer or backed by a SharedArrayBuffer. 5754 5755 If lenientFloatCode is not None, it should be used in cases when 5756 we're a non-finite float that's not unrestricted. 5757 5758 If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and 5759 [LegacyTreatNonObjectAsNull] extended attributes on nullable callback functions 5760 will be honored. 5761 5762 If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be 5763 adjusted to make it easier to return from a callback. Since that type is 5764 never directly observable by any consumers of the callback code, this is OK. 5765 Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior 5766 of the FailureFatalCastableObjectUnwrapper conversion; this is used for 5767 implementing auto-wrapping of JS-implemented return values from a 5768 JS-implemented interface. 5769 5770 sourceDescription is a description of what this JS value represents, to be 5771 used in error reporting. Callers should assume that it might get placed in 5772 the middle of a sentence. If it ends up at the beginning of a sentence, its 5773 first character will be automatically uppercased. 5774 5775 The return value from this function is a JSToNativeConversionInfo. 5776 """ 5777 # If we have a defaultValue then we're not actually optional for 5778 # purposes of what we need to be declared as. 5779 assert defaultValue is None or not isOptional 5780 5781 # Also, we should not have a defaultValue if we know we're an object 5782 assert not isDefinitelyObject or defaultValue is None 5783 5784 # And we can't both be an object and be null or undefined 5785 assert not isDefinitelyObject or not isNullOrUndefined 5786 5787 isClamp = type.hasClamp() 5788 isEnforceRange = type.hasEnforceRange() 5789 isAllowShared = type.hasAllowShared() 5790 5791 # If exceptionCode is not set, we'll just rethrow the exception we got. 5792 # Note that we can't just set failureCode to exceptionCode, because setting 5793 # failureCode will prevent pending exceptions from being set in cases when 5794 # they really should be! 5795 if exceptionCode is None: 5796 exceptionCode = "return false;\n" 5797 5798 # Unfortunately, .capitalize() on a string will lowercase things inside the 5799 # string, which we do not want. 5800 def firstCap(string): 5801 return string[0].upper() + string[1:] 5802 5803 # Helper functions for dealing with failures due to the JS value being the 5804 # wrong type of value 5805 def onFailureNotAnObject(failureCode): 5806 return CGGeneric( 5807 failureCode 5808 or ( 5809 'cx.ThrowErrorMessage<MSG_NOT_OBJECT>("%s");\n' 5810 "%s" % (firstCap(sourceDescription), exceptionCode) 5811 ) 5812 ) 5813 5814 def onFailureBadType(failureCode, typeName): 5815 return CGGeneric( 5816 failureCode 5817 or ( 5818 'cx.ThrowErrorMessage<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("%s", "%s");\n' 5819 "%s" % (firstCap(sourceDescription), typeName, exceptionCode) 5820 ) 5821 ) 5822 5823 # It's a failure in the committed-to conversion, not a failure to match up 5824 # to a type, so we don't want to use failureCode in here. We want to just 5825 # throw an exception unconditionally. 5826 def onFailureIsShared(): 5827 return CGGeneric( 5828 'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_SHARED>("%s");\n' 5829 "%s" % (firstCap(sourceDescription), exceptionCode) 5830 ) 5831 5832 def onFailureIsLarge(): 5833 return CGGeneric( 5834 'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_LARGE>("%s");\n' 5835 "%s" % (firstCap(sourceDescription), exceptionCode) 5836 ) 5837 5838 def onFailureIsResizable(): 5839 return CGGeneric( 5840 'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_RESIZABLE>("%s");\n' 5841 "%s" % (firstCap(sourceDescription), exceptionCode) 5842 ) 5843 5844 def onFailureIsImmutable(): 5845 desc = firstCap(sourceDescription) 5846 return CGGeneric( 5847 f'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_IMMUTABLE>("{desc}");\n' 5848 f"{exceptionCode}" 5849 ) 5850 5851 def onFailureNotCallable(failureCode): 5852 return CGGeneric( 5853 failureCode 5854 or ( 5855 'cx.ThrowErrorMessage<MSG_NOT_CALLABLE>("%s");\n' 5856 "%s" % (firstCap(sourceDescription), exceptionCode) 5857 ) 5858 ) 5859 5860 # A helper function for handling default values. Takes a template 5861 # body and the C++ code to set the default value and wraps the 5862 # given template body in handling for the default value. 5863 def handleDefault(template, setDefault): 5864 if defaultValue is None: 5865 return template 5866 if isKnownMissing: 5867 return fill( 5868 """ 5869 { 5870 // scope for any temporaries our default value setting needs. 5871 $*{setDefault} 5872 } 5873 """, 5874 setDefault=setDefault, 5875 ) 5876 return fill( 5877 """ 5878 if ($${haveValue}) { 5879 $*{templateBody} 5880 } else { 5881 $*{setDefault} 5882 } 5883 """, 5884 templateBody=template, 5885 setDefault=setDefault, 5886 ) 5887 5888 # A helper function for wrapping up the template body for 5889 # possibly-nullable objecty stuff 5890 def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None): 5891 if isNullOrUndefined and type.nullable(): 5892 # Just ignore templateBody and set ourselves to null. 5893 # Note that we don't have to worry about default values 5894 # here either, since we already examined this value. 5895 return codeToSetNull 5896 5897 if not isDefinitelyObject: 5898 # Handle the non-object cases by wrapping up the whole 5899 # thing in an if cascade. 5900 if type.nullable(): 5901 elifLine = "} else if (${val}.isNullOrUndefined()) {\n" 5902 elifBody = codeToSetNull 5903 else: 5904 elifLine = "" 5905 elifBody = "" 5906 5907 # Note that $${val} below expands to ${val}. This string is 5908 # used as a template later, and val will be filled in then. 5909 templateBody = fill( 5910 """ 5911 if ($${val}.isObject()) { 5912 $*{templateBody} 5913 $*{elifLine} 5914 $*{elifBody} 5915 } else { 5916 $*{failureBody} 5917 } 5918 """, 5919 templateBody=templateBody, 5920 elifLine=elifLine, 5921 elifBody=elifBody, 5922 failureBody=onFailureNotAnObject(failureCode).define(), 5923 ) 5924 5925 if isinstance(defaultValue, IDLNullValue): 5926 assert type.nullable() # Parser should enforce this 5927 templateBody = handleDefault(templateBody, codeToSetNull) 5928 elif isinstance(defaultValue, IDLEmptySequenceValue): 5929 # Our caller will handle it 5930 pass 5931 else: 5932 assert defaultValue is None 5933 5934 return templateBody 5935 5936 # A helper function for converting things that look like a JSObject*. 5937 def handleJSObjectType( 5938 type, isMember, failureCode, exceptionCode, sourceDescription 5939 ): 5940 if not isMember or isMember == "Union": 5941 if isOptional: 5942 # We have a specialization of Optional that will use a 5943 # Rooted for the storage here. 5944 declType = CGGeneric("JS::Handle<JSObject*>") 5945 else: 5946 declType = CGGeneric("JS::Rooted<JSObject*>") 5947 declArgs = "cx" 5948 else: 5949 assert isMember in ( 5950 "Sequence", 5951 "Variadic", 5952 "Dictionary", 5953 "OwningUnion", 5954 "Record", 5955 ) 5956 # We'll get traced by the sequence or dictionary or union tracer 5957 declType = CGGeneric("JSObject*") 5958 declArgs = None 5959 templateBody = "${declName} = &${val}.toObject();\n" 5960 5961 # For JS-implemented APIs, we refuse to allow passing objects that the 5962 # API consumer does not subsume. The extra parens around 5963 # ($${passedToJSImpl}) suppress unreachable code warnings when 5964 # $${passedToJSImpl} is the literal `false`. But Apple is shipping a 5965 # buggy clang (clang 3.9) in Xcode 8.3, so there even the parens are not 5966 # enough. So we manually disable some warnings in clang. 5967 if ( 5968 not isinstance(descriptorProvider, Descriptor) 5969 or descriptorProvider.interface.isJSImplemented() 5970 ): 5971 templateBody = ( 5972 fill( 5973 """ 5974 #ifdef __clang__ 5975 #pragma clang diagnostic push 5976 #pragma clang diagnostic ignored "-Wunreachable-code" 5977 #pragma clang diagnostic ignored "-Wunreachable-code-return" 5978 #endif // __clang__ 5979 if (($${passedToJSImpl}) && !CallerSubsumes($${val})) { 5980 cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}"); 5981 $*{exceptionCode} 5982 } 5983 #ifdef __clang__ 5984 #pragma clang diagnostic pop 5985 #endif // __clang__ 5986 """, 5987 sourceDescription=sourceDescription, 5988 exceptionCode=exceptionCode, 5989 ) 5990 + templateBody 5991 ) 5992 5993 setToNullCode = "${declName} = nullptr;\n" 5994 template = wrapObjectTemplate(templateBody, type, setToNullCode, failureCode) 5995 return JSToNativeConversionInfo( 5996 template, declType=declType, dealWithOptional=isOptional, declArgs=declArgs 5997 ) 5998 5999 def incrementNestingLevel(): 6000 if nestingLevel == "": 6001 return 1 6002 return nestingLevel + 1 6003 6004 assert not (isEnforceRange and isClamp) # These are mutually exclusive 6005 6006 if type.isSequence() or type.isObservableArray(): 6007 assert not isEnforceRange and not isClamp and not isAllowShared 6008 6009 if failureCode is None: 6010 notSequence = ( 6011 'cx.ThrowErrorMessage<MSG_CONVERSION_ERROR>("%s", "%s");\n' 6012 "%s" 6013 % ( 6014 firstCap(sourceDescription), 6015 "sequence" if type.isSequence() else "observable array", 6016 exceptionCode, 6017 ) 6018 ) 6019 else: 6020 notSequence = failureCode 6021 6022 nullable = type.nullable() 6023 # Be very careful not to change "type": we need it later 6024 if nullable: 6025 elementType = type.inner.inner 6026 else: 6027 elementType = type.inner 6028 6029 # We want to use auto arrays if we can, but we have to be careful with 6030 # reallocation behavior for arrays. In particular, if we use auto 6031 # arrays for sequences and have a sequence of elements which are 6032 # themselves sequences or have sequences as members, we have a problem. 6033 # In that case, resizing the outermost AutoTArray to the right size 6034 # will memmove its elements, but AutoTArrays are not memmovable and 6035 # hence will end up with pointers to bogus memory, which is bad. To 6036 # deal with this, we typically map WebIDL sequences to our Sequence 6037 # type, which is in fact memmovable. The one exception is when we're 6038 # passing in a sequence directly as an argument without any sort of 6039 # optional or nullable complexity going on. In that situation, we can 6040 # use an AutoSequence instead. We have to keep using Sequence in the 6041 # nullable and optional cases because we don't want to leak the 6042 # AutoSequence type to consumers, which would be unavoidable with 6043 # Nullable<AutoSequence> or Optional<AutoSequence>. 6044 if ( 6045 (isMember and isMember != "Union") 6046 or isOptional 6047 or nullable 6048 or isCallbackReturnValue 6049 ): 6050 sequenceClass = "Sequence" 6051 else: 6052 sequenceClass = "binding_detail::AutoSequence" 6053 6054 # XXXbz we can't include the index in the sourceDescription, because 6055 # we don't really have a way to pass one in dynamically at runtime... 6056 elementInfo = getJSToNativeConversionInfo( 6057 elementType, 6058 descriptorProvider, 6059 isMember="Sequence", 6060 exceptionCode=exceptionCode, 6061 lenientFloatCode=lenientFloatCode, 6062 isCallbackReturnValue=isCallbackReturnValue, 6063 sourceDescription="element of %s" % sourceDescription, 6064 nestingLevel=incrementNestingLevel(), 6065 ) 6066 if elementInfo.dealWithOptional: 6067 raise TypeError("Shouldn't have optional things in sequences") 6068 if elementInfo.holderType is not None: 6069 raise TypeError("Shouldn't need holders for sequences") 6070 6071 typeName = CGTemplatedType(sequenceClass, elementInfo.declType) 6072 sequenceType = typeName.define() 6073 6074 if isMember == "Union" and typeNeedsRooting(type): 6075 assert not nullable 6076 typeName = CGTemplatedType( 6077 "binding_detail::RootedAutoSequence", elementInfo.declType 6078 ) 6079 elif nullable: 6080 typeName = CGTemplatedType("Nullable", typeName) 6081 6082 if nullable: 6083 arrayRef = "${declName}.SetValue()" 6084 else: 6085 arrayRef = "${declName}" 6086 6087 elementConversion = string.Template(elementInfo.template).substitute( 6088 { 6089 "val": "temp" + str(nestingLevel), 6090 "maybeMutableVal": "&temp" + str(nestingLevel), 6091 "declName": "slot" + str(nestingLevel), 6092 # We only need holderName here to handle isExternal() 6093 # interfaces, which use an internal holder for the 6094 # conversion even when forceOwningType ends up true. 6095 "holderName": "tempHolder" + str(nestingLevel), 6096 "passedToJSImpl": "${passedToJSImpl}", 6097 } 6098 ) 6099 6100 elementInitializer = initializerForType(elementType) 6101 if elementInitializer is None: 6102 elementInitializer = "" 6103 else: 6104 elementInitializer = elementInitializer + ", " 6105 6106 # NOTE: Keep this in sync with variadic conversions as needed 6107 templateBody = fill( 6108 """ 6109 JS::ForOfIterator iter${nestingLevel}(cx); 6110 if (!iter${nestingLevel}.init($${val}, JS::ForOfIterator::AllowNonIterable)) { 6111 $*{exceptionCode} 6112 } 6113 if (!iter${nestingLevel}.valueIsIterable()) { 6114 $*{notSequence} 6115 } 6116 ${sequenceType} &arr${nestingLevel} = ${arrayRef}; 6117 JS::Rooted<JS::Value> temp${nestingLevel}(cx); 6118 while (true) { 6119 bool done${nestingLevel}; 6120 if (!iter${nestingLevel}.next(&temp${nestingLevel}, &done${nestingLevel})) { 6121 $*{exceptionCode} 6122 } 6123 if (done${nestingLevel}) { 6124 break; 6125 } 6126 ${elementType}* slotPtr${nestingLevel} = arr${nestingLevel}.AppendElement(${elementInitializer}mozilla::fallible); 6127 if (!slotPtr${nestingLevel}) { 6128 JS_ReportOutOfMemory(cx); 6129 $*{exceptionCode} 6130 } 6131 ${elementType}& slot${nestingLevel} = *slotPtr${nestingLevel}; 6132 $*{elementConversion} 6133 } 6134 """, 6135 exceptionCode=exceptionCode, 6136 notSequence=notSequence, 6137 sequenceType=sequenceType, 6138 arrayRef=arrayRef, 6139 elementType=elementInfo.declType.define(), 6140 elementConversion=elementConversion, 6141 elementInitializer=elementInitializer, 6142 nestingLevel=str(nestingLevel), 6143 ) 6144 6145 templateBody = wrapObjectTemplate( 6146 templateBody, type, "${declName}.SetNull();\n", notSequence 6147 ) 6148 if isinstance(defaultValue, IDLEmptySequenceValue): 6149 if type.nullable(): 6150 codeToSetEmpty = "${declName}.SetValue();\n" 6151 else: 6152 codeToSetEmpty = ( 6153 "/* ${declName} array is already empty; nothing to do */\n" 6154 ) 6155 templateBody = handleDefault(templateBody, codeToSetEmpty) 6156 6157 declArgs = None 6158 holderType = None 6159 holderArgs = None 6160 # Sequence arguments that might contain traceable things need 6161 # to get traced 6162 if typeNeedsRooting(elementType): 6163 if not isMember: 6164 holderType = CGTemplatedType("SequenceRooter", elementInfo.declType) 6165 # If our sequence is nullable, this will set the Nullable to be 6166 # not-null, but that's ok because we make an explicit SetNull() call 6167 # on it as needed if our JS value is actually null. 6168 holderArgs = "cx, &%s" % arrayRef 6169 elif isMember == "Union": 6170 declArgs = "cx" 6171 6172 return JSToNativeConversionInfo( 6173 templateBody, 6174 declType=typeName, 6175 declArgs=declArgs, 6176 holderType=holderType, 6177 dealWithOptional=isOptional, 6178 holderArgs=holderArgs, 6179 ) 6180 6181 if type.isRecord(): 6182 assert not isEnforceRange and not isClamp and not isAllowShared 6183 if failureCode is None: 6184 notRecord = 'cx.ThrowErrorMessage<MSG_NOT_OBJECT>("%s");\n' "%s" % ( 6185 firstCap(sourceDescription), 6186 exceptionCode, 6187 ) 6188 else: 6189 notRecord = failureCode 6190 6191 nullable = type.nullable() 6192 # Be very careful not to change "type": we need it later 6193 if nullable: 6194 recordType = type.inner 6195 else: 6196 recordType = type 6197 valueType = recordType.inner 6198 6199 valueInfo = getJSToNativeConversionInfo( 6200 valueType, 6201 descriptorProvider, 6202 isMember="Record", 6203 exceptionCode=exceptionCode, 6204 lenientFloatCode=lenientFloatCode, 6205 isCallbackReturnValue=isCallbackReturnValue, 6206 sourceDescription="value in %s" % sourceDescription, 6207 nestingLevel=incrementNestingLevel(), 6208 ) 6209 if valueInfo.dealWithOptional: 6210 raise TypeError("Shouldn't have optional things in record") 6211 if valueInfo.holderType is not None: 6212 raise TypeError("Shouldn't need holders for record") 6213 6214 declType = CGTemplatedType( 6215 "Record", [recordKeyDeclType(recordType), valueInfo.declType] 6216 ) 6217 typeName = declType.define() 6218 6219 if isMember == "Union" and typeNeedsRooting(type): 6220 assert not nullable 6221 declType = CGTemplatedType( 6222 "RootedRecord", [recordKeyDeclType(recordType), valueInfo.declType] 6223 ) 6224 elif nullable: 6225 declType = CGTemplatedType("Nullable", declType) 6226 6227 if nullable: 6228 recordRef = "${declName}.SetValue()" 6229 else: 6230 recordRef = "${declName}" 6231 6232 valueConversion = string.Template(valueInfo.template).substitute( 6233 { 6234 "val": "temp", 6235 "maybeMutableVal": "&temp", 6236 "declName": "slot", 6237 # We only need holderName here to handle isExternal() 6238 # interfaces, which use an internal holder for the 6239 # conversion even when forceOwningType ends up true. 6240 "holderName": "tempHolder", 6241 "passedToJSImpl": "${passedToJSImpl}", 6242 } 6243 ) 6244 6245 keyType = recordKeyType(recordType) 6246 if recordType.keyType.isJSString(): 6247 raise TypeError( 6248 "Have do deal with JSString record type, but don't know how" 6249 ) 6250 if recordType.keyType.isByteString() or recordType.keyType.isUTF8String(): 6251 hashKeyType = "nsCStringHashKey" 6252 if recordType.keyType.isByteString(): 6253 keyConversionFunction = "ConvertJSValueToByteString" 6254 else: 6255 keyConversionFunction = "ConvertJSValueToString" 6256 6257 else: 6258 hashKeyType = "nsStringHashKey" 6259 if recordType.keyType.isDOMString(): 6260 keyConversionFunction = "ConvertJSValueToString" 6261 else: 6262 assert recordType.keyType.isUSVString() 6263 keyConversionFunction = "ConvertJSValueToUSVString" 6264 6265 templateBody = fill( 6266 """ 6267 auto& recordEntries = ${recordRef}.Entries(); 6268 6269 JS::Rooted<JSObject*> recordObj(cx, &$${val}.toObject()); 6270 JS::RootedVector<jsid> ids(cx); 6271 if (!js::GetPropertyKeys(cx, recordObj, 6272 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &ids)) { 6273 $*{exceptionCode} 6274 } 6275 if (!recordEntries.SetCapacity(ids.length(), mozilla::fallible)) { 6276 JS_ReportOutOfMemory(cx); 6277 $*{exceptionCode} 6278 } 6279 JS::Rooted<JS::Value> propNameValue(cx); 6280 JS::Rooted<JS::Value> temp(cx); 6281 JS::Rooted<jsid> curId(cx); 6282 JS::Rooted<JS::Value> idVal(cx); 6283 // Use a hashset to keep track of ids seen, to avoid 6284 // introducing nasty O(N^2) behavior scanning for them all the 6285 // time. Ideally we'd use a data structure with O(1) lookup 6286 // _and_ ordering for the MozMap, but we don't have one lying 6287 // around. 6288 nsTHashtable<${hashKeyType}> idsSeen; 6289 for (size_t i = 0; i < ids.length(); ++i) { 6290 curId = ids[i]; 6291 6292 JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc(cx); 6293 if (!JS_GetOwnPropertyDescriptorById(cx, recordObj, curId, 6294 &desc)) { 6295 $*{exceptionCode} 6296 } 6297 6298 if (desc.isNothing() || !desc->enumerable()) { 6299 continue; 6300 } 6301 6302 idVal = js::IdToValue(curId); 6303 ${keyType} propName; 6304 // This will just throw if idVal is a Symbol, like the spec says 6305 // to do. 6306 if (!${keyConversionFunction}(cx, idVal, "key of ${sourceDescription}", propName)) { 6307 $*{exceptionCode} 6308 } 6309 6310 if (!JS_GetPropertyById(cx, recordObj, curId, &temp)) { 6311 $*{exceptionCode} 6312 } 6313 6314 ${typeName}::EntryType* entry; 6315 if (!idsSeen.EnsureInserted(propName)) { 6316 // Find the existing entry. 6317 auto idx = recordEntries.IndexOf(propName); 6318 MOZ_ASSERT(idx != recordEntries.NoIndex, 6319 "Why is it not found?"); 6320 // Now blow it away to make it look like it was just added 6321 // to the array, because it's not obvious that it's 6322 // safe to write to its already-initialized mValue via our 6323 // normal codegen conversions. For example, the value 6324 // could be a union and this would change its type, but 6325 // codegen assumes we won't do that. 6326 entry = recordEntries.ReconstructElementAt(idx); 6327 } else { 6328 // Safe to do an infallible append here, because we did a 6329 // SetCapacity above to the right capacity. 6330 entry = recordEntries.AppendElement(); 6331 } 6332 entry->mKey = propName; 6333 ${valueType}& slot = entry->mValue; 6334 $*{valueConversion} 6335 } 6336 """, 6337 exceptionCode=exceptionCode, 6338 recordRef=recordRef, 6339 hashKeyType=hashKeyType, 6340 keyType=keyType, 6341 keyConversionFunction=keyConversionFunction, 6342 sourceDescription=sourceDescription, 6343 typeName=typeName, 6344 valueType=valueInfo.declType.define(), 6345 valueConversion=valueConversion, 6346 ) 6347 6348 templateBody = wrapObjectTemplate( 6349 templateBody, type, "${declName}.SetNull();\n", notRecord 6350 ) 6351 6352 declArgs = None 6353 holderType = None 6354 holderArgs = None 6355 # record arguments that might contain traceable things need 6356 # to get traced 6357 if not isMember and isCallbackReturnValue: 6358 # Go ahead and just convert directly into our actual return value 6359 declType = CGWrapper(declType, post="&") 6360 declArgs = "aRetVal" 6361 elif typeNeedsRooting(valueType): 6362 if not isMember: 6363 holderType = CGTemplatedType( 6364 "RecordRooter", [recordKeyDeclType(recordType), valueInfo.declType] 6365 ) 6366 # If our record is nullable, this will set the Nullable to be 6367 # not-null, but that's ok because we make an explicit SetNull() call 6368 # on it as needed if our JS value is actually null. 6369 holderArgs = "cx, &%s" % recordRef 6370 elif isMember == "Union": 6371 declArgs = "cx" 6372 6373 return JSToNativeConversionInfo( 6374 templateBody, 6375 declType=declType, 6376 declArgs=declArgs, 6377 holderType=holderType, 6378 dealWithOptional=isOptional, 6379 holderArgs=holderArgs, 6380 ) 6381 6382 if type.isUnion(): 6383 nullable = type.nullable() 6384 if nullable: 6385 type = type.inner 6386 6387 isOwningUnion = (isMember and isMember != "Union") or isCallbackReturnValue 6388 unionArgumentObj = "${declName}" 6389 if nullable: 6390 if isOptional and not isOwningUnion: 6391 unionArgumentObj += ".Value()" 6392 # If we're owning, we're a Nullable, which hasn't been told it has 6393 # a value. Otherwise we're an already-constructed Maybe. 6394 unionArgumentObj += ".SetValue()" 6395 6396 templateBody = CGIfWrapper( 6397 CGGeneric(exceptionCode), 6398 '!%s.Init(cx, ${val}, "%s", ${passedToJSImpl})' 6399 % (unionArgumentObj, firstCap(sourceDescription)), 6400 ) 6401 6402 if type.hasNullableType: 6403 assert not nullable 6404 # Make sure to handle a null default value here 6405 if defaultValue and isinstance(defaultValue, IDLNullValue): 6406 assert defaultValue.type == type 6407 templateBody = CGIfElseWrapper( 6408 "!(${haveValue})", 6409 CGGeneric("%s.SetNull();\n" % unionArgumentObj), 6410 templateBody, 6411 ) 6412 6413 typeName = CGUnionStruct.unionTypeDecl(type, isOwningUnion) 6414 argumentTypeName = typeName + "Argument" 6415 if nullable: 6416 typeName = "Nullable<" + typeName + " >" 6417 6418 declType = CGGeneric(typeName) 6419 if isOwningUnion: 6420 holderType = None 6421 else: 6422 holderType = CGGeneric(argumentTypeName) 6423 if nullable: 6424 holderType = CGTemplatedType("Maybe", holderType) 6425 6426 # If we're isOptional and not nullable the normal optional handling will 6427 # handle lazy construction of our holder. If we're nullable and not 6428 # owning we do it all by hand because we do not want our holder 6429 # constructed if we're null. But if we're owning we don't have a 6430 # holder anyway, so we can do the normal Optional codepath. 6431 declLoc = "${declName}" 6432 constructDecl = None 6433 if nullable: 6434 if isOptional and not isOwningUnion: 6435 declType = CGTemplatedType("Optional", declType) 6436 constructDecl = CGGeneric("${declName}.Construct();\n") 6437 declLoc = "${declName}.Value()" 6438 6439 if not isMember and isCallbackReturnValue: 6440 declType = CGWrapper(declType, post="&") 6441 declArgs = "aRetVal" 6442 else: 6443 declArgs = None 6444 6445 if ( 6446 defaultValue 6447 and not isinstance(defaultValue, IDLNullValue) 6448 and not isinstance(defaultValue, IDLDefaultDictionaryValue) 6449 ): 6450 tag = defaultValue.type.tag() 6451 6452 if tag in numericSuffixes or tag is IDLType.Tags.bool: 6453 defaultStr = getHandleDefault(defaultValue) 6454 # Make sure we actually construct the thing inside the nullable. 6455 value = declLoc + (".SetValue()" if nullable else "") 6456 name = getUnionMemberName(defaultValue.type) 6457 default = CGGeneric( 6458 "%s.RawSetAs%s() = %s;\n" % (value, name, defaultStr) 6459 ) 6460 elif isinstance(defaultValue, IDLEmptySequenceValue): 6461 name = getUnionMemberName(defaultValue.type) 6462 # Make sure we actually construct the thing inside the nullable. 6463 value = declLoc + (".SetValue()" if nullable else "") 6464 if not isOwningUnion and typeNeedsRooting(defaultValue.type): 6465 ctorArgs = "cx" 6466 else: 6467 ctorArgs = "" 6468 # It's enough to set us to the right type; that will 6469 # create an empty array, which is all we need here. 6470 default = CGGeneric( 6471 "(void)%s.RawSetAs%s(%s);\n" % (value, name, ctorArgs) 6472 ) 6473 elif defaultValue.type.isEnum(): 6474 name = getUnionMemberName(defaultValue.type) 6475 # Make sure we actually construct the thing inside the nullable. 6476 value = declLoc + (".SetValue()" if nullable else "") 6477 default = CGGeneric( 6478 "%s.RawSetAs%s() = %s::%s;\n" 6479 % ( 6480 value, 6481 name, 6482 defaultValue.type.inner.identifier.name, 6483 getEnumValueName(defaultValue.value), 6484 ) 6485 ) 6486 else: 6487 default = CGGeneric( 6488 handleDefaultStringValue( 6489 defaultValue, "%s.SetStringLiteral" % unionArgumentObj 6490 ) 6491 ) 6492 6493 templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody) 6494 6495 if nullable: 6496 assert not type.hasNullableType 6497 if defaultValue: 6498 if isinstance(defaultValue, IDLNullValue): 6499 extraConditionForNull = "!(${haveValue}) || " 6500 else: 6501 extraConditionForNull = "(${haveValue}) && " 6502 else: 6503 extraConditionForNull = "" 6504 6505 hasUndefinedType = any(t.isUndefined() for t in type.flatMemberTypes) 6506 assert not hasUndefinedType or defaultValue is None 6507 6508 nullTest = ( 6509 "${val}.isNull()" if hasUndefinedType else "${val}.isNullOrUndefined()" 6510 ) 6511 templateBody = CGIfElseWrapper( 6512 extraConditionForNull + nullTest, 6513 CGGeneric("%s.SetNull();\n" % declLoc), 6514 templateBody, 6515 ) 6516 elif ( 6517 not type.hasNullableType 6518 and defaultValue 6519 and isinstance(defaultValue, IDLDefaultDictionaryValue) 6520 ): 6521 assert type.hasDictionaryType() 6522 assert defaultValue.type.isDictionary() 6523 if not isOwningUnion and typeNeedsRooting(defaultValue.type): 6524 ctorArgs = "cx" 6525 else: 6526 ctorArgs = "" 6527 initDictionaryWithNull = CGIfWrapper( 6528 CGGeneric("return false;\n"), 6529 ( 6530 '!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")' 6531 % ( 6532 declLoc, 6533 getUnionMemberName(defaultValue.type), 6534 ctorArgs, 6535 type.prettyName(), 6536 ) 6537 ), 6538 ) 6539 templateBody = CGIfElseWrapper( 6540 "!(${haveValue})", initDictionaryWithNull, templateBody 6541 ) 6542 6543 templateBody = CGList([constructDecl, templateBody]) 6544 6545 return JSToNativeConversionInfo( 6546 templateBody.define(), 6547 declType=declType, 6548 declArgs=declArgs, 6549 dealWithOptional=isOptional and (not nullable or isOwningUnion), 6550 ) 6551 6552 if type.isPromise(): 6553 assert not type.nullable() 6554 assert defaultValue is None 6555 6556 # We always have to hold a strong ref to Promise here, because 6557 # Promise::resolve returns an addrefed thing. 6558 argIsPointer = isCallbackReturnValue 6559 if argIsPointer: 6560 declType = CGGeneric("RefPtr<Promise>") 6561 else: 6562 declType = CGGeneric("OwningNonNull<Promise>") 6563 6564 # Per spec, what we're supposed to do is take the original 6565 # Promise.resolve and call it with the original Promise as this 6566 # value to make a Promise out of whatever value we actually have 6567 # here. The question is which global we should use. There are 6568 # several cases to consider: 6569 # 6570 # 1) Normal call to API with a Promise argument. This is a case the 6571 # spec covers, and we should be using the current Realm's 6572 # Promise. That means the current compartment. 6573 # 2) Call to API with a Promise argument over Xrays. In practice, 6574 # this sort of thing seems to be used for giving an API 6575 # implementation a way to wait for conclusion of an asyc 6576 # operation, _not_ to expose the Promise to content code. So we 6577 # probably want to allow callers to use such an API in a 6578 # "natural" way, by passing chrome-side promises; indeed, that 6579 # may be all that the caller has to represent their async 6580 # operation. That means we really need to do the 6581 # Promise.resolve() in the caller (chrome) compartment: if we do 6582 # it in the content compartment, we will try to call .then() on 6583 # the chrome promise while in the content compartment, which will 6584 # throw and we'll just get a rejected Promise. Note that this is 6585 # also the reason why a caller who has a chrome Promise 6586 # representing an async operation can't itself convert it to a 6587 # content-side Promise (at least not without some serious 6588 # gyrations). 6589 # 3) Promise return value from a callback or callback interface. 6590 # Per spec, this should use the Realm of the callback object. In 6591 # our case, that's the compartment of the underlying callback, 6592 # not the current compartment (which may be the compartment of 6593 # some cross-compartment wrapper around said callback). 6594 # 4) Return value from a JS-implemented interface. In this case we 6595 # have a problem. Our current compartment is the compartment of 6596 # the JS implementation. But if the JS implementation returned 6597 # a page-side Promise (which is a totally sane thing to do, and 6598 # in fact the right thing to do given that this return value is 6599 # going right to content script) then we don't want to 6600 # Promise.resolve with our current compartment Promise, because 6601 # that will wrap it up in a chrome-side Promise, which is 6602 # decidedly _not_ what's desired here. So in that case we 6603 # should really unwrap the return value and use the global of 6604 # the result. CheckedUnwrapStatic should be good enough for that; 6605 # if it fails, then we're failing unwrap while in a 6606 # system-privileged compartment, so presumably we have a dead 6607 # object wrapper. Just error out. Do NOT fall back to using 6608 # the current compartment instead: that will return a 6609 # system-privileged rejected (because getting .then inside 6610 # resolve() failed) Promise to the caller, which they won't be 6611 # able to touch. That's not helpful. If we error out, on the 6612 # other hand, they will get a content-side rejected promise. 6613 # Same thing if the value returned is not even an object. 6614 if isCallbackReturnValue == "JSImpl": 6615 # Case 4 above. Note that globalObj defaults to the current 6616 # compartment global. Note that we don't use $*{exceptionCode} 6617 # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED) 6618 # which we don't really want here. 6619 assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n" 6620 getPromiseGlobal = fill( 6621 """ 6622 if (!$${val}.isObject()) { 6623 aRv.ThrowTypeError<MSG_NOT_OBJECT>("${sourceDescription}"); 6624 return nullptr; 6625 } 6626 JSObject* unwrappedVal = js::CheckedUnwrapStatic(&$${val}.toObject()); 6627 if (!unwrappedVal) { 6628 // A slight lie, but not much of one, for a dead object wrapper. 6629 aRv.ThrowTypeError<MSG_NOT_OBJECT>("${sourceDescription}"); 6630 return nullptr; 6631 } 6632 globalObj = JS::GetNonCCWObjectGlobal(unwrappedVal); 6633 """, 6634 sourceDescription=sourceDescription, 6635 ) 6636 elif isCallbackReturnValue == "Callback": 6637 getPromiseGlobal = dedent( 6638 """ 6639 // We basically want our entry global here. Play it safe 6640 // and use GetEntryGlobal() to get it, with whatever 6641 // principal-clamping it ends up doing. 6642 globalObj = GetEntryGlobal()->GetGlobalJSObject(); 6643 """ 6644 ) 6645 else: 6646 getPromiseGlobal = dedent( 6647 """ 6648 globalObj = JS::CurrentGlobalOrNull(cx); 6649 """ 6650 ) 6651 6652 templateBody = fill( 6653 """ 6654 { // Scope for our GlobalObject, FastErrorResult, JSAutoRealm, 6655 // etc. 6656 6657 JS::Rooted<JSObject*> globalObj(cx); 6658 $*{getPromiseGlobal} 6659 JSAutoRealm ar(cx, globalObj); 6660 GlobalObject promiseGlobal(cx, globalObj); 6661 if (promiseGlobal.Failed()) { 6662 $*{exceptionCode} 6663 } 6664 6665 JS::Rooted<JS::Value> valueToResolve(cx, $${val}); 6666 if (!JS_WrapValue(cx, &valueToResolve)) { 6667 $*{exceptionCode} 6668 } 6669 binding_detail::FastErrorResult promiseRv; 6670 nsCOMPtr<nsIGlobalObject> global = 6671 do_QueryInterface(promiseGlobal.GetAsSupports()); 6672 if (!global) { 6673 promiseRv.Throw(NS_ERROR_UNEXPECTED); 6674 MOZ_ALWAYS_TRUE(promiseRv.MaybeSetPendingException(cx)); 6675 $*{exceptionCode} 6676 } 6677 $${declName} = Promise::Resolve(global, cx, valueToResolve, 6678 promiseRv); 6679 if (promiseRv.MaybeSetPendingException(cx)) { 6680 $*{exceptionCode} 6681 } 6682 } 6683 """, 6684 getPromiseGlobal=getPromiseGlobal, 6685 exceptionCode=exceptionCode, 6686 ) 6687 6688 return JSToNativeConversionInfo( 6689 templateBody, declType=declType, dealWithOptional=isOptional 6690 ) 6691 6692 if type.isGeckoInterface(): 6693 assert not isEnforceRange and not isClamp and not isAllowShared 6694 6695 descriptor = descriptorProvider.getDescriptor( 6696 type.unroll().inner.identifier.name 6697 ) 6698 6699 assert descriptor.nativeType != "JSObject" 6700 6701 if descriptor.interface.isCallback(): 6702 (declType, declArgs, conversion) = getCallbackConversionInfo( 6703 type, descriptor.interface, isMember, isCallbackReturnValue, isOptional 6704 ) 6705 template = wrapObjectTemplate( 6706 conversion, type, "${declName} = nullptr;\n", failureCode 6707 ) 6708 return JSToNativeConversionInfo( 6709 template, 6710 declType=declType, 6711 declArgs=declArgs, 6712 dealWithOptional=isOptional, 6713 ) 6714 6715 if descriptor.interface.identifier.name == "WindowProxy": 6716 declType = CGGeneric("mozilla::dom::WindowProxyHolder") 6717 if type.nullable(): 6718 declType = CGTemplatedType("Nullable", declType) 6719 windowProxyHolderRef = "${declName}.SetValue()" 6720 else: 6721 windowProxyHolderRef = "${declName}" 6722 6723 failureCode = onFailureBadType( 6724 failureCode, descriptor.interface.identifier.name 6725 ).define() 6726 templateBody = fill( 6727 """ 6728 JS::Rooted<JSObject*> source(cx, &$${val}.toObject()); 6729 if (NS_FAILED(UnwrapWindowProxyArg(cx, source, ${windowProxyHolderRef}))) { 6730 $*{onFailure} 6731 } 6732 """, 6733 windowProxyHolderRef=windowProxyHolderRef, 6734 onFailure=failureCode, 6735 ) 6736 templateBody = wrapObjectTemplate( 6737 templateBody, type, "${declName}.SetNull();\n", failureCode 6738 ) 6739 return JSToNativeConversionInfo( 6740 templateBody, declType=declType, dealWithOptional=isOptional 6741 ) 6742 6743 # This is an interface that we implement as a concrete class 6744 # or an XPCOM interface. 6745 6746 # Allow null pointers for nullable types and old-binding classes, and 6747 # use an RefPtr or raw pointer for callback return values to make 6748 # them easier to return. 6749 argIsPointer = ( 6750 type.nullable() or type.unroll().inner.isExternal() or isCallbackReturnValue 6751 ) 6752 6753 # Sequence and dictionary members, as well as owning unions (which can 6754 # appear here as return values in JS-implemented interfaces) have to 6755 # hold a strong ref to the thing being passed down. Those all set 6756 # isMember. 6757 # 6758 # Also, callback return values always end up addrefing anyway, so there 6759 # is no point trying to avoid it here and it makes other things simpler 6760 # since we can assume the return value is a strong ref. 6761 assert not descriptor.interface.isCallback() 6762 forceOwningType = (isMember and isMember != "Union") or isCallbackReturnValue 6763 6764 typeName = descriptor.nativeType 6765 typePtr = typeName + "*" 6766 6767 # Compute a few things: 6768 # - declType is the type we want to return as the first element of our 6769 # tuple. 6770 # - holderType is the type we want to return as the third element 6771 # of our tuple. 6772 6773 # Set up some sensible defaults for these things insofar as we can. 6774 holderType = None 6775 if argIsPointer: 6776 if forceOwningType: 6777 declType = "RefPtr<" + typeName + ">" 6778 else: 6779 declType = typePtr 6780 else: 6781 if forceOwningType: 6782 declType = "OwningNonNull<" + typeName + ">" 6783 else: 6784 declType = "NonNull<" + typeName + ">" 6785 6786 templateBody = "" 6787 if forceOwningType: 6788 templateBody += fill( 6789 """ 6790 static_assert(IsRefcounted<${typeName}>::value, "We can only store refcounted classes."); 6791 """, 6792 typeName=typeName, 6793 ) 6794 6795 if not descriptor.interface.isExternal(): 6796 if failureCode is not None: 6797 templateBody += str( 6798 CastableObjectUnwrapper( 6799 descriptor, 6800 "${val}", 6801 "${maybeMutableVal}", 6802 "${declName}", 6803 failureCode, 6804 ) 6805 ) 6806 else: 6807 templateBody += str( 6808 FailureFatalCastableObjectUnwrapper( 6809 descriptor, 6810 "${val}", 6811 "${maybeMutableVal}", 6812 "${declName}", 6813 exceptionCode, 6814 isCallbackReturnValue, 6815 firstCap(sourceDescription), 6816 ) 6817 ) 6818 else: 6819 # External interface. We always have a holder for these, because we 6820 # don't actually know whether we have to addref when unwrapping or not. 6821 # So we just pass an getter_AddRefs(RefPtr) to XPConnect and if we'll 6822 # need a release it'll put a non-null pointer in there. 6823 if forceOwningType: 6824 # Don't return a holderType in this case; our declName 6825 # will just own stuff. 6826 templateBody += "RefPtr<" + typeName + "> ${holderName};\n" 6827 else: 6828 holderType = "RefPtr<" + typeName + ">" 6829 templateBody += ( 6830 "JS::Rooted<JSObject*> source(cx, &${val}.toObject());\n" 6831 + "if (NS_FAILED(UnwrapArg<" 6832 + typeName 6833 + ">(cx, source, getter_AddRefs(${holderName})))) {\n" 6834 ) 6835 templateBody += CGIndenter( 6836 onFailureBadType(failureCode, descriptor.interface.identifier.name) 6837 ).define() 6838 templateBody += "}\n" "MOZ_ASSERT(${holderName});\n" 6839 6840 # And store our value in ${declName} 6841 templateBody += "${declName} = ${holderName};\n" 6842 6843 # Just pass failureCode, not onFailureBadType, here, so we'll report 6844 # the thing as not an object as opposed to not implementing whatever 6845 # our interface is. 6846 templateBody = wrapObjectTemplate( 6847 templateBody, type, "${declName} = nullptr;\n", failureCode 6848 ) 6849 6850 declType = CGGeneric(declType) 6851 if holderType is not None: 6852 holderType = CGGeneric(holderType) 6853 return JSToNativeConversionInfo( 6854 templateBody, 6855 declType=declType, 6856 holderType=holderType, 6857 dealWithOptional=isOptional, 6858 ) 6859 6860 if type.isSpiderMonkeyInterface(): 6861 assert not isEnforceRange and not isClamp 6862 name = type.unroll().name # unroll() because it may be nullable 6863 interfaceType = CGGeneric(name) 6864 declType = interfaceType 6865 if type.nullable(): 6866 declType = CGTemplatedType("Nullable", declType) 6867 objRef = "${declName}.SetValue()" 6868 else: 6869 objRef = "${declName}" 6870 6871 # Again, this is a bit strange since we are actually building a 6872 # template string here. ${objRef} and $*{badType} below are filled in 6873 # right now; $${val} expands to ${val}, to be filled in later. 6874 template = fill( 6875 """ 6876 if (!${objRef}.Init(&$${val}.toObject())) { 6877 $*{badType} 6878 } 6879 """, 6880 objRef=objRef, 6881 badType=onFailureBadType(failureCode, type.name).define(), 6882 ) 6883 if type.isBufferSource(): 6884 if type.isArrayBuffer(): 6885 isSharedMethod = "JS::IsSharedArrayBufferObject" 6886 isLargeMethod = "JS::IsLargeArrayBufferMaybeShared" 6887 isResizableMethod = "JS::IsResizableArrayBufferMaybeShared" 6888 isImmutableMethod = "JS::IsImmutableArrayBufferMaybeShared" 6889 else: 6890 assert type.isArrayBufferView() or type.isTypedArray() 6891 isSharedMethod = "JS::IsArrayBufferViewShared" 6892 isLargeMethod = "JS::IsLargeArrayBufferView" 6893 isResizableMethod = "JS::IsResizableArrayBufferView" 6894 isImmutableMethod = "JS::IsImmutableArrayBufferView" 6895 if not isAllowShared: 6896 template += fill( 6897 """ 6898 if (${isSharedMethod}(${objRef}.Obj())) { 6899 $*{badType} 6900 } 6901 """, 6902 isSharedMethod=isSharedMethod, 6903 objRef=objRef, 6904 badType=onFailureIsShared().define(), 6905 ) 6906 # For now reject large (> 2 GB) ArrayBuffers and ArrayBufferViews. 6907 # Supporting this will require changing dom::TypedArray and 6908 # consumers. 6909 template += fill( 6910 """ 6911 if (${isLargeMethod}(${objRef}.Obj())) { 6912 $*{badType} 6913 } 6914 """, 6915 isLargeMethod=isLargeMethod, 6916 objRef=objRef, 6917 badType=onFailureIsLarge().define(), 6918 ) 6919 # For now reject resizable ArrayBuffers and growable 6920 # SharedArrayBuffers. Supporting this will require changing 6921 # dom::TypedArray and consumers. 6922 template += fill( 6923 """ 6924 if (${isResizableMethod}(${objRef}.Obj())) { 6925 $*{badType} 6926 } 6927 """, 6928 isResizableMethod=isResizableMethod, 6929 objRef=objRef, 6930 badType=onFailureIsResizable().define(), 6931 ) 6932 # For now reject immutable ArrayBuffers. Supporting this will 6933 # require changing dom::TypedArray and consumers. 6934 template += fill( 6935 """ 6936 if (${isImmutableMethod}(${objRef}.Obj())) { 6937 $*{badType} 6938 } 6939 """, 6940 isImmutableMethod=isImmutableMethod, 6941 objRef=objRef, 6942 badType=onFailureIsImmutable().define(), 6943 ) 6944 template = wrapObjectTemplate( 6945 template, type, "${declName}.SetNull();\n", failureCode 6946 ) 6947 if not isMember or isMember == "Union": 6948 # This is a bit annoying. In a union we don't want to have a 6949 # holder, since unions don't support that. But if we're optional we 6950 # want to have a holder, so that the callee doesn't see 6951 # Optional<RootedSpiderMonkeyInterface<InterfaceType>>. So do a 6952 # holder if we're optional and use a RootedSpiderMonkeyInterface 6953 # otherwise. 6954 if isOptional: 6955 holderType = CGTemplatedType( 6956 "SpiderMonkeyInterfaceRooter", interfaceType 6957 ) 6958 # If our SpiderMonkey interface is nullable, this will set the 6959 # Nullable to be not-null, but that's ok because we make an 6960 # explicit SetNull() call on it as needed if our JS value is 6961 # actually null. XXXbz Because "Maybe" takes const refs for 6962 # constructor arguments, we can't pass a reference here; have 6963 # to pass a pointer. 6964 holderArgs = "cx, &%s" % objRef 6965 declArgs = None 6966 else: 6967 holderType = None 6968 holderArgs = None 6969 declType = CGTemplatedType("RootedSpiderMonkeyInterface", declType) 6970 declArgs = "cx" 6971 else: 6972 holderType = None 6973 holderArgs = None 6974 declArgs = None 6975 return JSToNativeConversionInfo( 6976 template, 6977 declType=declType, 6978 holderType=holderType, 6979 dealWithOptional=isOptional, 6980 declArgs=declArgs, 6981 holderArgs=holderArgs, 6982 ) 6983 6984 if type.isJSString(): 6985 assert not isEnforceRange and not isClamp and not isAllowShared 6986 if type.nullable(): 6987 raise TypeError("Nullable JSString not supported") 6988 6989 declArgs = "cx" 6990 if isMember: 6991 raise TypeError("JSString not supported as member") 6992 else: 6993 declType = "JS::Rooted<JSString*>" 6994 6995 if isOptional: 6996 raise TypeError("JSString not supported as optional") 6997 templateBody = fill( 6998 """ 6999 if (!($${declName} = ConvertJSValueToJSString(cx, $${val}))) { 7000 $*{exceptionCode} 7001 } 7002 """, 7003 exceptionCode=exceptionCode, 7004 ) 7005 7006 if defaultValue is not None: 7007 assert not isinstance(defaultValue, IDLNullValue) 7008 defaultCode = fill( 7009 """ 7010 static const char data[] = { ${data} }; 7011 $${declName} = JS_NewStringCopyN(cx, data, std::size(data) - 1); 7012 if (!$${declName}) { 7013 $*{exceptionCode} 7014 } 7015 """, 7016 data=", ".join( 7017 ["'" + char + "'" for char in defaultValue.value] + ["0"] 7018 ), 7019 exceptionCode=exceptionCode, 7020 ) 7021 7022 templateBody = handleDefault(templateBody, defaultCode) 7023 return JSToNativeConversionInfo( 7024 templateBody, declType=CGGeneric(declType), declArgs=declArgs 7025 ) 7026 7027 if type.isDOMString() or type.isUSVString() or type.isUTF8String(): 7028 assert not isEnforceRange and not isClamp and not isAllowShared 7029 7030 treatAs = { 7031 "Default": "eStringify", 7032 "EmptyString": "eEmpty", 7033 "Null": "eNull", 7034 } 7035 if type.nullable(): 7036 # For nullable strings null becomes a null string. 7037 treatNullAs = "Null" 7038 # For nullable strings undefined also becomes a null string. 7039 undefinedBehavior = "eNull" 7040 else: 7041 undefinedBehavior = "eStringify" 7042 if type.legacyNullToEmptyString: 7043 treatNullAs = "EmptyString" 7044 else: 7045 treatNullAs = "Default" 7046 nullBehavior = treatAs[treatNullAs] 7047 7048 def getConversionCode(varName): 7049 normalizeCode = "" 7050 if type.isUSVString(): 7051 normalizeCode = fill( 7052 """ 7053 if (!NormalizeUSVString(${var})) { 7054 JS_ReportOutOfMemory(cx); 7055 $*{exceptionCode} 7056 } 7057 """, 7058 var=varName, 7059 exceptionCode=exceptionCode, 7060 ) 7061 7062 conversionCode = fill( 7063 """ 7064 if (!ConvertJSValueToString(cx, $${val}, ${nullBehavior}, ${undefinedBehavior}, ${varName})) { 7065 $*{exceptionCode} 7066 } 7067 $*{normalizeCode} 7068 """, 7069 nullBehavior=nullBehavior, 7070 undefinedBehavior=undefinedBehavior, 7071 varName=varName, 7072 exceptionCode=exceptionCode, 7073 normalizeCode=normalizeCode, 7074 ) 7075 7076 if defaultValue is None: 7077 return conversionCode 7078 7079 if isinstance(defaultValue, IDLNullValue): 7080 assert type.nullable() 7081 defaultCode = "%s.SetIsVoid(true);\n" % varName 7082 else: 7083 defaultCode = handleDefaultStringValue( 7084 defaultValue, "%s.AssignLiteral" % varName 7085 ) 7086 return handleDefault(conversionCode, defaultCode) 7087 7088 if isMember and isMember != "Union": 7089 # Convert directly into the ns[C]String member we have. 7090 if type.isUTF8String(): 7091 declType = "nsCString" 7092 else: 7093 declType = "nsString" 7094 return JSToNativeConversionInfo( 7095 getConversionCode("${declName}"), 7096 declType=CGGeneric(declType), 7097 dealWithOptional=isOptional, 7098 ) 7099 7100 if isOptional: 7101 if type.isUTF8String(): 7102 declType = "Optional<nsACString>" 7103 holderType = CGGeneric("binding_detail::FakeString<char>") 7104 else: 7105 declType = "Optional<nsAString>" 7106 holderType = CGGeneric("binding_detail::FakeString<char16_t>") 7107 conversionCode = "%s" "${declName} = &${holderName};\n" % getConversionCode( 7108 "${holderName}" 7109 ) 7110 else: 7111 if type.isUTF8String(): 7112 declType = "binding_detail::FakeString<char>" 7113 else: 7114 declType = "binding_detail::FakeString<char16_t>" 7115 holderType = None 7116 conversionCode = getConversionCode("${declName}") 7117 7118 # No need to deal with optional here; we handled it already 7119 return JSToNativeConversionInfo( 7120 conversionCode, declType=CGGeneric(declType), holderType=holderType 7121 ) 7122 7123 if type.isByteString(): 7124 assert not isEnforceRange and not isClamp and not isAllowShared 7125 7126 nullable = toStringBool(type.nullable()) 7127 7128 conversionCode = fill( 7129 """ 7130 if (!ConvertJSValueToByteString(cx, $${val}, ${nullable}, "${sourceDescription}", $${declName})) { 7131 $*{exceptionCode} 7132 } 7133 """, 7134 nullable=nullable, 7135 sourceDescription=sourceDescription, 7136 exceptionCode=exceptionCode, 7137 ) 7138 7139 if defaultValue is not None: 7140 if isinstance(defaultValue, IDLNullValue): 7141 assert type.nullable() 7142 defaultCode = "${declName}.SetIsVoid(true);\n" 7143 else: 7144 defaultCode = handleDefaultStringValue( 7145 defaultValue, "${declName}.AssignLiteral" 7146 ) 7147 conversionCode = handleDefault(conversionCode, defaultCode) 7148 7149 return JSToNativeConversionInfo( 7150 conversionCode, declType=CGGeneric("nsCString"), dealWithOptional=isOptional 7151 ) 7152 7153 if type.isEnum(): 7154 assert not isEnforceRange and not isClamp and not isAllowShared 7155 7156 enumName = type.unroll().inner.identifier.name 7157 declType = CGGeneric(enumName) 7158 if type.nullable(): 7159 declType = CGTemplatedType("Nullable", declType) 7160 declType = declType.define() 7161 enumLoc = "${declName}.SetValue()" 7162 else: 7163 enumLoc = "${declName}" 7164 declType = declType.define() 7165 7166 if invalidEnumValueFatal: 7167 handleInvalidEnumValueCode = "MOZ_ASSERT(index >= 0);\n" 7168 else: 7169 # invalidEnumValueFatal is false only for attributes. So we won't 7170 # have a non-default exceptionCode here unless attribute "arg 7171 # conversion" code starts passing in an exceptionCode. At which 7172 # point we'll need to figure out what that even means. 7173 assert exceptionCode == "return false;\n" 7174 handleInvalidEnumValueCode = dedent( 7175 """ 7176 if (index < 0) { 7177 return true; 7178 } 7179 """ 7180 ) 7181 7182 template = fill( 7183 """ 7184 { 7185 int index; 7186 if (!binding_detail::FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val}, 7187 binding_detail::EnumStrings<${enumtype}>::Values, 7188 "${enumtype}", "${sourceDescription}", 7189 &index)) { 7190 $*{exceptionCode} 7191 } 7192 $*{handleInvalidEnumValueCode} 7193 ${enumLoc} = static_cast<${enumtype}>(index); 7194 } 7195 """, 7196 enumtype=enumName, 7197 invalidEnumValueFatal=toStringBool(invalidEnumValueFatal), 7198 handleInvalidEnumValueCode=handleInvalidEnumValueCode, 7199 exceptionCode=exceptionCode, 7200 enumLoc=enumLoc, 7201 sourceDescription=sourceDescription, 7202 ) 7203 7204 setNull = "${declName}.SetNull();\n" 7205 7206 if type.nullable(): 7207 template = CGIfElseWrapper( 7208 "${val}.isNullOrUndefined()", CGGeneric(setNull), CGGeneric(template) 7209 ).define() 7210 7211 if defaultValue is not None: 7212 if isinstance(defaultValue, IDLNullValue): 7213 assert type.nullable() 7214 template = handleDefault(template, setNull) 7215 else: 7216 assert defaultValue.type.tag() == IDLType.Tags.domstring 7217 template = handleDefault( 7218 template, 7219 ( 7220 "%s = %s::%s;\n" 7221 % (enumLoc, enumName, getEnumValueName(defaultValue.value)) 7222 ), 7223 ) 7224 return JSToNativeConversionInfo( 7225 template, declType=CGGeneric(declType), dealWithOptional=isOptional 7226 ) 7227 7228 if type.isCallback(): 7229 assert not isEnforceRange and not isClamp and not isAllowShared 7230 assert not type.treatNonCallableAsNull() or type.nullable() 7231 assert not type.treatNonObjectAsNull() or type.nullable() 7232 assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull() 7233 7234 callback = type.unroll().callback 7235 name = callback.identifier.name 7236 (declType, declArgs, conversion) = getCallbackConversionInfo( 7237 type, callback, isMember, isCallbackReturnValue, isOptional 7238 ) 7239 7240 if allowTreatNonCallableAsNull and type.treatNonCallableAsNull(): 7241 haveCallable = "JS::IsCallable(&${val}.toObject())" 7242 if not isDefinitelyObject: 7243 haveCallable = "${val}.isObject() && " + haveCallable 7244 if defaultValue is not None: 7245 assert isinstance(defaultValue, IDLNullValue) 7246 haveCallable = "(${haveValue}) && " + haveCallable 7247 template = ( 7248 ("if (%s) {\n" % haveCallable) + conversion + "} else {\n" 7249 " ${declName} = nullptr;\n" 7250 "}\n" 7251 ) 7252 elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull(): 7253 if not isDefinitelyObject: 7254 haveObject = "${val}.isObject()" 7255 if defaultValue is not None: 7256 assert isinstance(defaultValue, IDLNullValue) 7257 haveObject = "(${haveValue}) && " + haveObject 7258 template = CGIfElseWrapper( 7259 haveObject, 7260 CGGeneric(conversion), 7261 CGGeneric("${declName} = nullptr;\n"), 7262 ).define() 7263 else: 7264 template = conversion 7265 else: 7266 template = wrapObjectTemplate( 7267 "if (JS::IsCallable(&${val}.toObject())) {\n" 7268 + conversion 7269 + "} else {\n" 7270 + indent(onFailureNotCallable(failureCode).define()) 7271 + "}\n", 7272 type, 7273 "${declName} = nullptr;\n", 7274 failureCode, 7275 ) 7276 return JSToNativeConversionInfo( 7277 template, declType=declType, declArgs=declArgs, dealWithOptional=isOptional 7278 ) 7279 7280 if type.isAny(): 7281 assert not isEnforceRange and not isClamp and not isAllowShared 7282 7283 declArgs = None 7284 if isMember in ("Variadic", "Sequence", "Dictionary", "Record"): 7285 # Rooting is handled by the sequence and dictionary tracers. 7286 declType = "JS::Value" 7287 else: 7288 assert not isMember 7289 declType = "JS::Rooted<JS::Value>" 7290 declArgs = "cx" 7291 7292 assert not isOptional 7293 templateBody = "${declName} = ${val};\n" 7294 7295 # For JS-implemented APIs, we refuse to allow passing objects that the 7296 # API consumer does not subsume. The extra parens around 7297 # ($${passedToJSImpl}) suppress unreachable code warnings when 7298 # $${passedToJSImpl} is the literal `false`. But Apple is shipping a 7299 # buggy clang (clang 3.9) in Xcode 8.3, so there even the parens are not 7300 # enough. So we manually disable some warnings in clang. 7301 if ( 7302 not isinstance(descriptorProvider, Descriptor) 7303 or descriptorProvider.interface.isJSImplemented() 7304 ): 7305 templateBody = ( 7306 fill( 7307 """ 7308 #ifdef __clang__ 7309 #pragma clang diagnostic push 7310 #pragma clang diagnostic ignored "-Wunreachable-code" 7311 #pragma clang diagnostic ignored "-Wunreachable-code-return" 7312 #endif // __clang__ 7313 if (($${passedToJSImpl}) && !CallerSubsumes($${val})) { 7314 cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}"); 7315 $*{exceptionCode} 7316 } 7317 #ifdef __clang__ 7318 #pragma clang diagnostic pop 7319 #endif // __clang__ 7320 """, 7321 sourceDescription=sourceDescription, 7322 exceptionCode=exceptionCode, 7323 ) 7324 + templateBody 7325 ) 7326 7327 # We may not have a default value if we're being converted for 7328 # a setter, say. 7329 if defaultValue: 7330 if isinstance(defaultValue, IDLNullValue): 7331 defaultHandling = "${declName} = JS::NullValue();\n" 7332 else: 7333 assert isinstance(defaultValue, IDLUndefinedValue) 7334 defaultHandling = "${declName} = JS::UndefinedValue();\n" 7335 templateBody = handleDefault(templateBody, defaultHandling) 7336 return JSToNativeConversionInfo( 7337 templateBody, declType=CGGeneric(declType), declArgs=declArgs 7338 ) 7339 7340 if type.isObject(): 7341 assert not isEnforceRange and not isClamp and not isAllowShared 7342 return handleJSObjectType( 7343 type, isMember, failureCode, exceptionCode, sourceDescription 7344 ) 7345 7346 if type.isDictionary(): 7347 # There are no nullable dictionary-typed arguments or dictionary-typed 7348 # dictionary members. 7349 assert ( 7350 not type.nullable() 7351 or isCallbackReturnValue 7352 or (isMember and isMember != "Dictionary") 7353 ) 7354 # All optional dictionary-typed arguments always have default values, 7355 # but dictionary-typed dictionary members can be optional. 7356 assert not isOptional or isMember == "Dictionary" 7357 # In the callback return value case we never have to worry 7358 # about a default value; we always have a value. 7359 assert not isCallbackReturnValue or defaultValue is None 7360 7361 typeName = CGDictionary.makeDictionaryName(type.unroll().inner) 7362 if (not isMember or isMember == "Union") and not isCallbackReturnValue: 7363 # Since we're not a member and not nullable or optional, no one will 7364 # see our real type, so we can do the fast version of the dictionary 7365 # that doesn't pre-initialize members. 7366 typeName = "binding_detail::Fast" + typeName 7367 7368 declType = CGGeneric(typeName) 7369 7370 # We do manual default value handling here, because we actually do want 7371 # a jsval, and we only handle the default-dictionary case (which we map 7372 # into initialization with the JS value `null`) anyway 7373 # NOTE: if isNullOrUndefined or isDefinitelyObject are true, 7374 # we know we have a value, so we don't have to worry about the 7375 # default value. 7376 if ( 7377 not isNullOrUndefined 7378 and not isDefinitelyObject 7379 and defaultValue is not None 7380 ): 7381 assert isinstance(defaultValue, IDLDefaultDictionaryValue) 7382 # Initializing from JS null does the right thing to give 7383 # us a default-initialized dictionary. 7384 val = "(${haveValue}) ? ${val} : JS::NullHandleValue" 7385 else: 7386 val = "${val}" 7387 7388 dictLoc = "${declName}" 7389 if type.nullable(): 7390 dictLoc += ".SetValue()" 7391 7392 if type.unroll().inner.needsConversionFromJS: 7393 args = "cx, %s, " % val 7394 else: 7395 # We can end up in this case if a dictionary that does not need 7396 # conversion from JS has a dictionary-typed member with a default 7397 # value of {}. 7398 args = "" 7399 conversionCode = fill( 7400 """ 7401 if (!${dictLoc}.Init(${args}"${desc}", $${passedToJSImpl})) { 7402 $*{exceptionCode} 7403 } 7404 """, 7405 dictLoc=dictLoc, 7406 args=args, 7407 desc=firstCap(sourceDescription), 7408 exceptionCode=exceptionCode, 7409 ) 7410 7411 if failureCode is not None: 7412 # This means we're part of an overload or union conversion, and 7413 # should simply skip stuff if our value is not convertible to 7414 # dictionary, instead of trying and throwing. If we're either 7415 # isDefinitelyObject or isNullOrUndefined then we're convertible to 7416 # dictionary and don't need to check here. 7417 if isDefinitelyObject or isNullOrUndefined: 7418 template = conversionCode 7419 else: 7420 template = fill( 7421 """ 7422 if (!IsConvertibleToDictionary(${val})) { 7423 $*{failureCode} 7424 } 7425 $*{conversionCode} 7426 """, 7427 val=val, 7428 failureCode=failureCode, 7429 conversionCode=conversionCode, 7430 ) 7431 else: 7432 template = conversionCode 7433 7434 if type.nullable(): 7435 declType = CGTemplatedType("Nullable", declType) 7436 template = CGIfElseWrapper( 7437 "${val}.isNullOrUndefined()", 7438 CGGeneric("${declName}.SetNull();\n"), 7439 CGGeneric(template), 7440 ).define() 7441 7442 # Dictionary arguments that might contain traceable things need to get 7443 # traced 7444 if (not isMember or isMember == "Union") and isCallbackReturnValue: 7445 # Go ahead and just convert directly into our actual return value 7446 declType = CGWrapper(declType, post="&") 7447 declArgs = "aRetVal" 7448 elif (not isMember or isMember == "Union") and typeNeedsRooting(type): 7449 declType = CGTemplatedType("RootedDictionary", declType) 7450 declArgs = "cx" 7451 else: 7452 declArgs = None 7453 7454 return JSToNativeConversionInfo( 7455 template, declType=declType, declArgs=declArgs, dealWithOptional=isOptional 7456 ) 7457 7458 if type.isUndefined(): 7459 assert not isOptional 7460 # This one only happens for return values, and its easy: Just 7461 # ignore the jsval. 7462 return JSToNativeConversionInfo("") 7463 7464 if not type.isPrimitive(): 7465 raise TypeError("Need conversion for argument type '%s'" % str(type)) 7466 7467 typeName = builtinNames[type.tag()] 7468 7469 conversionBehavior = "eDefault" 7470 if isEnforceRange: 7471 assert type.isInteger() 7472 conversionBehavior = "eEnforceRange" 7473 elif isClamp: 7474 assert type.isInteger() 7475 conversionBehavior = "eClamp" 7476 7477 alwaysNull = False 7478 if type.nullable(): 7479 declType = CGGeneric("Nullable<" + typeName + ">") 7480 writeLoc = "${declName}.SetValue()" 7481 readLoc = "${declName}.Value()" 7482 nullCondition = "${val}.isNullOrUndefined()" 7483 if defaultValue is not None and isinstance(defaultValue, IDLNullValue): 7484 nullCondition = "!(${haveValue}) || " + nullCondition 7485 if isKnownMissing: 7486 alwaysNull = True 7487 template = dedent( 7488 """ 7489 ${declName}.SetNull(); 7490 """ 7491 ) 7492 if not alwaysNull: 7493 template = fill( 7494 """ 7495 if (${nullCondition}) { 7496 $${declName}.SetNull(); 7497 } else if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, "${sourceDescription}", &${writeLoc})) { 7498 $*{exceptionCode} 7499 } 7500 """, 7501 nullCondition=nullCondition, 7502 typeName=typeName, 7503 conversionBehavior=conversionBehavior, 7504 sourceDescription=firstCap(sourceDescription), 7505 writeLoc=writeLoc, 7506 exceptionCode=exceptionCode, 7507 ) 7508 else: 7509 assert defaultValue is None or not isinstance(defaultValue, IDLNullValue) 7510 writeLoc = "${declName}" 7511 readLoc = writeLoc 7512 template = fill( 7513 """ 7514 if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, "${sourceDescription}", &${writeLoc})) { 7515 $*{exceptionCode} 7516 } 7517 """, 7518 typeName=typeName, 7519 conversionBehavior=conversionBehavior, 7520 sourceDescription=firstCap(sourceDescription), 7521 writeLoc=writeLoc, 7522 exceptionCode=exceptionCode, 7523 ) 7524 declType = CGGeneric(typeName) 7525 7526 if type.isFloat() and not type.isUnrestricted() and not alwaysNull: 7527 if lenientFloatCode is not None: 7528 nonFiniteCode = lenientFloatCode 7529 else: 7530 nonFiniteCode = 'cx.ThrowErrorMessage<MSG_NOT_FINITE>("%s");\n' "%s" % ( 7531 firstCap(sourceDescription), 7532 exceptionCode, 7533 ) 7534 7535 # We're appending to an if-block brace, so strip trailing whitespace 7536 # and add an extra space before the else. 7537 template = template.rstrip() 7538 template += fill( 7539 """ 7540 else if (!std::isfinite(${readLoc})) { 7541 $*{nonFiniteCode} 7542 } 7543 """, 7544 readLoc=readLoc, 7545 nonFiniteCode=nonFiniteCode, 7546 ) 7547 7548 if ( 7549 defaultValue is not None 7550 and 7551 # We already handled IDLNullValue, so just deal with the other ones 7552 not isinstance(defaultValue, IDLNullValue) 7553 ): 7554 tag = defaultValue.type.tag() 7555 defaultStr = getHandleDefault(defaultValue) 7556 template = handleDefault(template, "%s = %s;\n" % (writeLoc, defaultStr)) 7557 7558 return JSToNativeConversionInfo( 7559 template, declType=declType, dealWithOptional=isOptional 7560 ) 7561 7562 7563 def instantiateJSToNativeConversion(info, replacements, checkForValue=False): 7564 """ 7565 Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo 7566 and a set of replacements as required by the strings in such an object, and 7567 generate code to convert into stack C++ types. 7568 7569 If checkForValue is True, then the conversion will get wrapped in 7570 a check for ${haveValue}. 7571 """ 7572 templateBody, declType, holderType, dealWithOptional = ( 7573 info.template, 7574 info.declType, 7575 info.holderType, 7576 info.dealWithOptional, 7577 ) 7578 7579 if dealWithOptional and not checkForValue: 7580 raise TypeError("Have to deal with optional things, but don't know how") 7581 if checkForValue and declType is None: 7582 raise TypeError( 7583 "Need to predeclare optional things, so they will be " 7584 "outside the check for big enough arg count!" 7585 ) 7586 7587 # We can't precompute our holder constructor arguments, since 7588 # those might depend on ${declName}, which we change below. Just 7589 # compute arguments at the point when we need them as we go. 7590 def getArgsCGThing(args): 7591 return CGGeneric(string.Template(args).substitute(replacements)) 7592 7593 result = CGList([]) 7594 # Make a copy of "replacements" since we may be about to start modifying it 7595 replacements = dict(replacements) 7596 originalDeclName = replacements["declName"] 7597 if declType is not None: 7598 if dealWithOptional: 7599 replacements["declName"] = "%s.Value()" % originalDeclName 7600 declType = CGTemplatedType("Optional", declType) 7601 declCtorArgs = None 7602 elif info.declArgs is not None: 7603 declCtorArgs = CGWrapper(getArgsCGThing(info.declArgs), pre="(", post=")") 7604 else: 7605 declCtorArgs = None 7606 result.append( 7607 CGList( 7608 [ 7609 declType, 7610 CGGeneric(" "), 7611 CGGeneric(originalDeclName), 7612 declCtorArgs, 7613 CGGeneric(";\n"), 7614 ] 7615 ) 7616 ) 7617 7618 originalHolderName = replacements["holderName"] 7619 if holderType is not None: 7620 if dealWithOptional: 7621 replacements["holderName"] = "%s.ref()" % originalHolderName 7622 holderType = CGTemplatedType("Maybe", holderType) 7623 holderCtorArgs = None 7624 elif info.holderArgs is not None: 7625 holderCtorArgs = CGWrapper( 7626 getArgsCGThing(info.holderArgs), pre="(", post=")" 7627 ) 7628 else: 7629 holderCtorArgs = None 7630 result.append( 7631 CGList( 7632 [ 7633 holderType, 7634 CGGeneric(" "), 7635 CGGeneric(originalHolderName), 7636 holderCtorArgs, 7637 CGGeneric(";\n"), 7638 ] 7639 ) 7640 ) 7641 7642 if "maybeMutableVal" not in replacements: 7643 replacements["maybeMutableVal"] = replacements["val"] 7644 7645 conversion = CGGeneric(string.Template(templateBody).substitute(replacements)) 7646 7647 if checkForValue: 7648 if dealWithOptional: 7649 declConstruct = CGIndenter( 7650 CGGeneric( 7651 "%s.Construct(%s);\n" 7652 % ( 7653 originalDeclName, 7654 getArgsCGThing(info.declArgs).define() if info.declArgs else "", 7655 ) 7656 ) 7657 ) 7658 if holderType is not None: 7659 holderConstruct = CGIndenter( 7660 CGGeneric( 7661 "%s.emplace(%s);\n" 7662 % ( 7663 originalHolderName, 7664 ( 7665 getArgsCGThing(info.holderArgs).define() 7666 if info.holderArgs 7667 else "" 7668 ), 7669 ) 7670 ) 7671 ) 7672 else: 7673 holderConstruct = None 7674 else: 7675 declConstruct = None 7676 holderConstruct = None 7677 7678 conversion = CGList( 7679 [ 7680 CGGeneric( 7681 string.Template("if (${haveValue}) {\n").substitute(replacements) 7682 ), 7683 declConstruct, 7684 holderConstruct, 7685 CGIndenter(conversion), 7686 CGGeneric("}\n"), 7687 ] 7688 ) 7689 7690 result.append(conversion) 7691 return result 7692 7693 7694 def convertConstIDLValueToJSVal(value): 7695 if isinstance(value, IDLNullValue): 7696 return "JS::NullValue()" 7697 if isinstance(value, IDLUndefinedValue): 7698 return "JS::UndefinedValue()" 7699 tag = value.type.tag() 7700 if tag in [ 7701 IDLType.Tags.int8, 7702 IDLType.Tags.uint8, 7703 IDLType.Tags.int16, 7704 IDLType.Tags.uint16, 7705 IDLType.Tags.int32, 7706 ]: 7707 return "JS::Int32Value(%s)" % (value.value) 7708 if tag == IDLType.Tags.uint32: 7709 return "JS::NumberValue(%sU)" % (value.value) 7710 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]: 7711 return "JS::CanonicalizedDoubleValue(%s)" % numericValue(tag, value.value) 7712 if tag == IDLType.Tags.bool: 7713 return "JS::BooleanValue(%s)" % (toStringBool(value.value)) 7714 if tag in [IDLType.Tags.float, IDLType.Tags.double]: 7715 return "JS::CanonicalizedDoubleValue(%s)" % (value.value) 7716 raise TypeError("Const value of unhandled type: %s" % value.type) 7717 7718 7719 class CGArgumentConverter(CGThing): 7720 """ 7721 A class that takes an IDL argument object and its index in the 7722 argument list and generates code to unwrap the argument to the 7723 right native type. 7724 7725 argDescription is a description of the argument for error-reporting 7726 purposes. Callers should assume that it might get placed in the middle of a 7727 sentence. If it ends up at the beginning of a sentence, its first character 7728 will be automatically uppercased. 7729 """ 7730 7731 def __init__( 7732 self, 7733 argument, 7734 index, 7735 descriptorProvider, 7736 argDescription, 7737 member, 7738 invalidEnumValueFatal=True, 7739 lenientFloatCode=None, 7740 ): 7741 CGThing.__init__(self) 7742 self.argument = argument 7743 self.argDescription = argDescription 7744 assert not argument.defaultValue or argument.optional 7745 7746 replacer = {"index": index, "argc": "args.length()"} 7747 self.replacementVariables = { 7748 "declName": "arg%d" % index, 7749 "holderName": ("arg%d" % index) + "_holder", 7750 "obj": "obj", 7751 "passedToJSImpl": toStringBool( 7752 isJSImplementedDescriptor(descriptorProvider) 7753 ), 7754 } 7755 # If we have a method generated by the maplike/setlike portion of an 7756 # interface, arguments can possibly be undefined, but will need to be 7757 # converted to the key/value type of the backing object. In this case, 7758 # use .get() instead of direct access to the argument. This won't 7759 # matter for iterable since generated functions for those interface 7760 # don't take arguments. 7761 if member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod(): 7762 self.replacementVariables["val"] = string.Template( 7763 "args.get(${index})" 7764 ).substitute(replacer) 7765 self.replacementVariables["maybeMutableVal"] = string.Template( 7766 "args[${index}]" 7767 ).substitute(replacer) 7768 else: 7769 self.replacementVariables["val"] = string.Template( 7770 "args[${index}]" 7771 ).substitute(replacer) 7772 haveValueCheck = string.Template("args.hasDefined(${index})").substitute( 7773 replacer 7774 ) 7775 self.replacementVariables["haveValue"] = haveValueCheck 7776 self.descriptorProvider = descriptorProvider 7777 if self.argument.canHaveMissingValue(): 7778 self.argcAndIndex = replacer 7779 else: 7780 self.argcAndIndex = None 7781 self.invalidEnumValueFatal = invalidEnumValueFatal 7782 self.lenientFloatCode = lenientFloatCode 7783 7784 def define(self): 7785 typeConversion = getJSToNativeConversionInfo( 7786 self.argument.type, 7787 self.descriptorProvider, 7788 isOptional=(self.argcAndIndex is not None and not self.argument.variadic), 7789 invalidEnumValueFatal=self.invalidEnumValueFatal, 7790 defaultValue=self.argument.defaultValue, 7791 lenientFloatCode=self.lenientFloatCode, 7792 isMember="Variadic" if self.argument.variadic else False, 7793 allowTreatNonCallableAsNull=self.argument.allowTreatNonCallableAsNull(), 7794 sourceDescription=self.argDescription, 7795 ) 7796 7797 if not self.argument.variadic: 7798 return instantiateJSToNativeConversion( 7799 typeConversion, self.replacementVariables, self.argcAndIndex is not None 7800 ).define() 7801 7802 # Variadic arguments get turned into a sequence. 7803 if typeConversion.dealWithOptional: 7804 raise TypeError("Shouldn't have optional things in variadics") 7805 if typeConversion.holderType is not None: 7806 raise TypeError("Shouldn't need holders for variadics") 7807 7808 replacer = dict(self.argcAndIndex, **self.replacementVariables) 7809 replacer["seqType"] = CGTemplatedType( 7810 "AutoSequence", typeConversion.declType 7811 ).define() 7812 if typeNeedsRooting(self.argument.type): 7813 rooterDecl = ( 7814 "SequenceRooter<%s> ${holderName}(cx, &${declName});\n" 7815 % typeConversion.declType.define() 7816 ) 7817 else: 7818 rooterDecl = "" 7819 replacer["elemType"] = typeConversion.declType.define() 7820 7821 replacer["elementInitializer"] = initializerForType(self.argument.type) or "" 7822 7823 # NOTE: Keep this in sync with sequence conversions as needed 7824 variadicConversion = string.Template( 7825 "${seqType} ${declName};\n" 7826 + rooterDecl 7827 + dedent( 7828 """ 7829 if (${argc} > ${index}) { 7830 if (!${declName}.SetCapacity(${argc} - ${index}, mozilla::fallible)) { 7831 JS_ReportOutOfMemory(cx); 7832 return false; 7833 } 7834 for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) { 7835 // OK to do infallible append here, since we ensured capacity already. 7836 ${elemType}& slot = *${declName}.AppendElement(${elementInitializer}); 7837 """ 7838 ) 7839 ).substitute(replacer) 7840 7841 val = string.Template("args[variadicArg]").substitute(replacer) 7842 variadicConversion += indent( 7843 string.Template(typeConversion.template).substitute( 7844 { 7845 "val": val, 7846 "maybeMutableVal": val, 7847 "declName": "slot", 7848 # We only need holderName here to handle isExternal() 7849 # interfaces, which use an internal holder for the 7850 # conversion even when forceOwningType ends up true. 7851 "holderName": "tempHolder", 7852 # Use the same ${obj} as for the variadic arg itself 7853 "obj": replacer["obj"], 7854 "passedToJSImpl": toStringBool( 7855 isJSImplementedDescriptor(self.descriptorProvider) 7856 ), 7857 } 7858 ), 7859 4, 7860 ) 7861 7862 variadicConversion += " }\n" "}\n" 7863 return variadicConversion 7864 7865 7866 def getMaybeWrapValueFuncForType(type): 7867 if type.isJSString(): 7868 return "MaybeWrapStringValue" 7869 # Callbacks might actually be DOM objects; nothing prevents a page from 7870 # doing that. 7871 if type.isCallback() or type.isCallbackInterface() or type.isObject(): 7872 if type.nullable(): 7873 return "MaybeWrapObjectOrNullValue" 7874 return "MaybeWrapObjectValue" 7875 # SpiderMonkey interfaces are never DOM objects. Neither are sequences or 7876 # dictionaries, since those are always plain JS objects. 7877 if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence(): 7878 if type.nullable(): 7879 return "MaybeWrapNonDOMObjectOrNullValue" 7880 return "MaybeWrapNonDOMObjectValue" 7881 if type.isAny(): 7882 return "MaybeWrapValue" 7883 7884 # For other types, just go ahead an fall back on MaybeWrapValue for now: 7885 # it's always safe to do, and shouldn't be particularly slow for any of 7886 # them 7887 return "MaybeWrapValue" 7888 7889 7890 sequenceWrapLevel = 0 7891 recordWrapLevel = 0 7892 7893 7894 def getWrapTemplateForType( 7895 type, 7896 descriptorProvider, 7897 result, 7898 successCode, 7899 returnsNewObject, 7900 exceptionCode, 7901 spiderMonkeyInterfacesAreStructs, 7902 isConstructorRetval=False, 7903 ): 7904 """ 7905 Reflect a C++ value stored in "result", of IDL type "type" into JS. The 7906 "successCode" is the code to run once we have successfully done the 7907 conversion and must guarantee that execution of the conversion template 7908 stops once the successCode has executed (e.g. by doing a 'return', or by 7909 doing a 'break' if the entire conversion template is inside a block that 7910 the 'break' will exit). 7911 7912 If spiderMonkeyInterfacesAreStructs is true, then if the type is a 7913 SpiderMonkey interface, "result" is one of the 7914 dom::SpiderMonkeyInterfaceObjectStorage subclasses, not a JSObject*. 7915 7916 The resulting string should be used with string.Template. It 7917 needs the following keys when substituting: 7918 7919 jsvalHandle: something that can be passed to methods taking a 7920 JS::MutableHandle<JS::Value>. This can be a 7921 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*. 7922 jsvalRef: something that can have .address() called on it to get a 7923 JS::Value* and .set() called on it to set it to a JS::Value. 7924 This can be a JS::MutableHandle<JS::Value> or a 7925 JS::Rooted<JS::Value>. 7926 obj: a JS::Handle<JSObject*>. 7927 7928 Returns (templateString, infallibility of conversion template) 7929 """ 7930 if successCode is None: 7931 successCode = "return true;\n" 7932 7933 def setUndefined(): 7934 return _setValue("", setter="setUndefined") 7935 7936 def setNull(): 7937 return _setValue("", setter="setNull") 7938 7939 def setInt32(value): 7940 return _setValue(value, setter="setInt32") 7941 7942 def setString(value): 7943 return _setValue(value, wrapAsType=type, setter="setString") 7944 7945 def setObject(value, wrapAsType=None): 7946 return _setValue(value, wrapAsType=wrapAsType, setter="setObject") 7947 7948 def setObjectOrNull(value, wrapAsType=None): 7949 return _setValue(value, wrapAsType=wrapAsType, setter="setObjectOrNull") 7950 7951 def setUint32(value): 7952 return _setValue(value, setter="setNumber") 7953 7954 def setDouble(value): 7955 return _setValue("JS_NumberValue(%s)" % value) 7956 7957 def setBoolean(value): 7958 return _setValue(value, setter="setBoolean") 7959 7960 def _setValue(value, wrapAsType=None, setter="set"): 7961 """ 7962 Returns the code to set the jsval to value. 7963 7964 If wrapAsType is not None, then will wrap the resulting value using the 7965 function that getMaybeWrapValueFuncForType(wrapAsType) returns. 7966 Otherwise, no wrapping will be done. 7967 """ 7968 if wrapAsType is None: 7969 tail = successCode 7970 else: 7971 tail = fill( 7972 """ 7973 if (!${maybeWrap}(cx, $${jsvalHandle})) { 7974 $*{exceptionCode} 7975 } 7976 $*{successCode} 7977 """, 7978 maybeWrap=getMaybeWrapValueFuncForType(wrapAsType), 7979 exceptionCode=exceptionCode, 7980 successCode=successCode, 7981 ) 7982 return ("${jsvalRef}.%s(%s);\n" % (setter, value)) + tail 7983 7984 def wrapAndSetPtr(wrapCall, failureCode=None): 7985 """ 7986 Returns the code to set the jsval by calling "wrapCall". "failureCode" 7987 is the code to run if calling "wrapCall" fails 7988 """ 7989 if failureCode is None: 7990 failureCode = exceptionCode 7991 return fill( 7992 """ 7993 if (!${wrapCall}) { 7994 $*{failureCode} 7995 } 7996 $*{successCode} 7997 """, 7998 wrapCall=wrapCall, 7999 failureCode=failureCode, 8000 successCode=successCode, 8001 ) 8002 8003 if type is None or type.isUndefined(): 8004 return (setUndefined(), True) 8005 8006 if (type.isSequence() or type.isRecord()) and type.nullable(): 8007 # These are both wrapped in Nullable<> 8008 recTemplate, recInfall = getWrapTemplateForType( 8009 type.inner, 8010 descriptorProvider, 8011 "%s.Value()" % result, 8012 successCode, 8013 returnsNewObject, 8014 exceptionCode, 8015 spiderMonkeyInterfacesAreStructs, 8016 ) 8017 code = fill( 8018 """ 8019 8020 if (${result}.IsNull()) { 8021 $*{setNull} 8022 } 8023 $*{recTemplate} 8024 """, 8025 result=result, 8026 setNull=setNull(), 8027 recTemplate=recTemplate, 8028 ) 8029 return code, recInfall 8030 8031 if type.isSequence(): 8032 # Now do non-nullable sequences. Our success code is just to break to 8033 # where we set the element in the array. Note that we bump the 8034 # sequenceWrapLevel around this call so that nested sequence conversions 8035 # will use different iteration variables. 8036 global sequenceWrapLevel 8037 index = "sequenceIdx%d" % sequenceWrapLevel 8038 sequenceWrapLevel += 1 8039 innerTemplate = wrapForType( 8040 type.inner, 8041 descriptorProvider, 8042 { 8043 "result": "%s[%s]" % (result, index), 8044 "successCode": "break;\n", 8045 "jsvalRef": "tmp", 8046 "jsvalHandle": "&tmp", 8047 "returnsNewObject": returnsNewObject, 8048 "exceptionCode": exceptionCode, 8049 "obj": "returnArray", 8050 "spiderMonkeyInterfacesAreStructs": spiderMonkeyInterfacesAreStructs, 8051 }, 8052 ) 8053 sequenceWrapLevel -= 1 8054 code = fill( 8055 """ 8056 8057 uint32_t length = ${result}.Length(); 8058 JS::Rooted<JSObject*> returnArray(cx, JS::NewArrayObject(cx, length)); 8059 if (!returnArray) { 8060 $*{exceptionCode} 8061 } 8062 // Scope for 'tmp' 8063 { 8064 JS::Rooted<JS::Value> tmp(cx); 8065 for (uint32_t ${index} = 0; ${index} < length; ++${index}) { 8066 // Control block to let us common up the JS_DefineElement calls when there 8067 // are different ways to succeed at wrapping the object. 8068 do { 8069 $*{innerTemplate} 8070 } while (false); 8071 if (!JS_DefineElement(cx, returnArray, ${index}, tmp, 8072 JSPROP_ENUMERATE)) { 8073 $*{exceptionCode} 8074 } 8075 } 8076 } 8077 $*{set} 8078 """, 8079 result=result, 8080 exceptionCode=exceptionCode, 8081 index=index, 8082 innerTemplate=innerTemplate, 8083 set=setObject("*returnArray"), 8084 ) 8085 8086 return (code, False) 8087 8088 if type.isRecord(): 8089 # Now do non-nullable record. Our success code is just to break to 8090 # where we define the property on the object. Note that we bump the 8091 # recordWrapLevel around this call so that nested record conversions 8092 # will use different temp value names. 8093 global recordWrapLevel 8094 valueName = "recordValue%d" % recordWrapLevel 8095 recordWrapLevel += 1 8096 innerTemplate = wrapForType( 8097 type.inner, 8098 descriptorProvider, 8099 { 8100 "result": valueName, 8101 "successCode": "break;\n", 8102 "jsvalRef": "tmp", 8103 "jsvalHandle": "&tmp", 8104 "returnsNewObject": returnsNewObject, 8105 "exceptionCode": exceptionCode, 8106 "obj": "returnObj", 8107 "spiderMonkeyInterfacesAreStructs": spiderMonkeyInterfacesAreStructs, 8108 }, 8109 ) 8110 recordWrapLevel -= 1 8111 if type.keyType.isByteString(): 8112 # There is no length-taking JS_DefineProperty. So to keep 8113 # things sane with embedded nulls, we want to byte-inflate 8114 # to an nsAString. The only byte-inflation function we 8115 # have around is AppendASCIItoUTF16, which luckily doesn't 8116 # assert anything about the input being ASCII. 8117 expandedKeyDecl = "NS_ConvertASCIItoUTF16 expandedKey(entry.mKey);\n" 8118 keyName = "expandedKey" 8119 elif type.keyType.isUTF8String(): 8120 # We do the same as above for utf8 strings. We could do better if 8121 # we had a DefineProperty API that takes utf-8 property names. 8122 expandedKeyDecl = "NS_ConvertUTF8toUTF16 expandedKey(entry.mKey);\n" 8123 keyName = "expandedKey" 8124 else: 8125 expandedKeyDecl = "" 8126 keyName = "entry.mKey" 8127 8128 code = fill( 8129 """ 8130 8131 JS::Rooted<JSObject*> returnObj(cx, JS_NewPlainObject(cx)); 8132 if (!returnObj) { 8133 $*{exceptionCode} 8134 } 8135 // Scope for 'tmp' 8136 { 8137 JS::Rooted<JS::Value> tmp(cx); 8138 for (auto& entry : ${result}.Entries()) { 8139 auto& ${valueName} = entry.mValue; 8140 // Control block to let us common up the JS_DefineUCProperty calls when there 8141 // are different ways to succeed at wrapping the value. 8142 do { 8143 $*{innerTemplate} 8144 } while (false); 8145 $*{expandedKeyDecl} 8146 if (!JS_DefineUCProperty(cx, returnObj, 8147 ${keyName}.BeginReading(), 8148 ${keyName}.Length(), tmp, 8149 JSPROP_ENUMERATE)) { 8150 $*{exceptionCode} 8151 } 8152 } 8153 } 8154 $*{set} 8155 """, 8156 result=result, 8157 exceptionCode=exceptionCode, 8158 valueName=valueName, 8159 innerTemplate=innerTemplate, 8160 expandedKeyDecl=expandedKeyDecl, 8161 keyName=keyName, 8162 set=setObject("*returnObj"), 8163 ) 8164 8165 return (code, False) 8166 8167 if type.isPromise(): 8168 assert not type.nullable() 8169 # The use of ToJSValue here is a bit annoying because the Promise 8170 # version is not inlined. But we can't put an inline version in either 8171 # ToJSValue.h or BindingUtils.h, because Promise.h includes ToJSValue.h 8172 # and that includes BindingUtils.h, so we'd get an include loop if 8173 # either of those headers included Promise.h. And trying to write the 8174 # conversion by hand here is pretty annoying because we have to handle 8175 # the various RefPtr, rawptr, NonNull, etc cases, which ToJSValue will 8176 # handle for us. So just eat the cost of the function call. 8177 return (wrapAndSetPtr("ToJSValue(cx, %s, ${jsvalHandle})" % result), False) 8178 8179 if type.isGeckoInterface() and not type.isCallbackInterface(): 8180 descriptor = descriptorProvider.getDescriptor( 8181 type.unroll().inner.identifier.name 8182 ) 8183 if type.nullable(): 8184 if descriptor.interface.identifier.name == "WindowProxy": 8185 template, infal = getWrapTemplateForType( 8186 type.inner, 8187 descriptorProvider, 8188 "%s.Value()" % result, 8189 successCode, 8190 returnsNewObject, 8191 exceptionCode, 8192 spiderMonkeyInterfacesAreStructs, 8193 ) 8194 return ( 8195 "if (%s.IsNull()) {\n" % result 8196 + indent(setNull()) 8197 + "}\n" 8198 + template, 8199 infal, 8200 ) 8201 8202 wrappingCode = "if (!%s) {\n" % (result) + indent(setNull()) + "}\n" 8203 else: 8204 wrappingCode = "" 8205 8206 if not descriptor.interface.isExternal(): 8207 if descriptor.wrapperCache: 8208 wrapMethod = "GetOrCreateDOMReflector" 8209 wrapArgs = "cx, %s, ${jsvalHandle}" % result 8210 else: 8211 wrapMethod = "WrapNewBindingNonWrapperCachedObject" 8212 wrapArgs = "cx, ${obj}, %s, ${jsvalHandle}" % result 8213 if isConstructorRetval: 8214 wrapArgs += ", desiredProto" 8215 wrap = "%s(%s)" % (wrapMethod, wrapArgs) 8216 # Can only fail to wrap as a new-binding object if they already 8217 # threw an exception. 8218 failed = "MOZ_ASSERT(JS_IsExceptionPending(cx));\n" + exceptionCode 8219 else: 8220 if descriptor.notflattened: 8221 getIID = "&NS_GET_IID(%s), " % descriptor.nativeType 8222 else: 8223 getIID = "" 8224 wrap = "WrapObject(cx, %s, %s${jsvalHandle})" % (result, getIID) 8225 failed = None 8226 8227 wrappingCode += wrapAndSetPtr(wrap, failed) 8228 return (wrappingCode, False) 8229 8230 if type.isJSString(): 8231 return (setString(result), False) 8232 8233 if type.isDOMString() or type.isUSVString(): 8234 if type.nullable(): 8235 return ( 8236 wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result), 8237 False, 8238 ) 8239 else: 8240 return ( 8241 wrapAndSetPtr( 8242 "xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result 8243 ), 8244 False, 8245 ) 8246 8247 if type.isByteString(): 8248 if type.nullable(): 8249 return ( 8250 wrapAndSetPtr("ByteStringToJsval(cx, %s, ${jsvalHandle})" % result), 8251 False, 8252 ) 8253 else: 8254 return ( 8255 wrapAndSetPtr( 8256 "NonVoidByteStringToJsval(cx, %s, ${jsvalHandle})" % result 8257 ), 8258 False, 8259 ) 8260 8261 if type.isUTF8String(): 8262 if type.nullable(): 8263 return ( 8264 wrapAndSetPtr("UTF8StringToJsval(cx, %s, ${jsvalHandle})" % result), 8265 False, 8266 ) 8267 else: 8268 return ( 8269 wrapAndSetPtr( 8270 "NonVoidUTF8StringToJsval(cx, %s, ${jsvalHandle})" % result 8271 ), 8272 False, 8273 ) 8274 8275 if type.isEnum(): 8276 if type.nullable(): 8277 resultLoc = "%s.Value()" % result 8278 else: 8279 resultLoc = result 8280 conversion = fill( 8281 """ 8282 if (!ToJSValue(cx, ${result}, $${jsvalHandle})) { 8283 $*{exceptionCode} 8284 } 8285 $*{successCode} 8286 """, 8287 result=resultLoc, 8288 exceptionCode=exceptionCode, 8289 successCode=successCode, 8290 ) 8291 8292 if type.nullable(): 8293 conversion = CGIfElseWrapper( 8294 "%s.IsNull()" % result, CGGeneric(setNull()), CGGeneric(conversion) 8295 ).define() 8296 return conversion, False 8297 8298 if type.isCallback() or type.isCallbackInterface(): 8299 # Callbacks can store null if we nuked the compartments their 8300 # objects lived in. 8301 wrapCode = setObjectOrNull( 8302 "GetCallbackFromCallbackObject(cx, %(result)s)", wrapAsType=type 8303 ) 8304 if type.nullable(): 8305 wrapCode = ( 8306 "if (%(result)s) {\n" 8307 + indent(wrapCode) 8308 + "} else {\n" 8309 + indent(setNull()) 8310 + "}\n" 8311 ) 8312 wrapCode = wrapCode % {"result": result} 8313 return wrapCode, False 8314 8315 if type.isAny(): 8316 # See comments in GetOrCreateDOMReflector explaining why we need 8317 # to wrap here. 8318 # NB: _setValue(..., type-that-is-any) calls JS_WrapValue(), so is fallible 8319 head = "JS::ExposeValueToActiveJS(%s);\n" % result 8320 return (head + _setValue(result, wrapAsType=type), False) 8321 8322 if type.isObject() or ( 8323 type.isSpiderMonkeyInterface() and not spiderMonkeyInterfacesAreStructs 8324 ): 8325 # See comments in GetOrCreateDOMReflector explaining why we need 8326 # to wrap here. 8327 if type.nullable(): 8328 toValue = "%s" 8329 setter = setObjectOrNull 8330 head = """if (%s) { 8331 JS::ExposeObjectToActiveJS(%s); 8332 } 8333 """ % ( 8334 result, 8335 result, 8336 ) 8337 else: 8338 toValue = "*%s" 8339 setter = setObject 8340 head = "JS::ExposeObjectToActiveJS(%s);\n" % result 8341 # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible 8342 return (head + setter(toValue % result, wrapAsType=type), False) 8343 8344 if type.isObservableArray(): 8345 # This first argument isn't used at all for now, the attribute getter 8346 # for ObservableArray type are generated in getObservableArrayGetterBody 8347 # instead. 8348 return "", False 8349 8350 if not ( 8351 type.isUnion() 8352 or type.isPrimitive() 8353 or type.isDictionary() 8354 or (type.isSpiderMonkeyInterface() and spiderMonkeyInterfacesAreStructs) 8355 ): 8356 raise TypeError("Need to learn to wrap %s" % type) 8357 8358 if type.nullable(): 8359 recTemplate, recInfal = getWrapTemplateForType( 8360 type.inner, 8361 descriptorProvider, 8362 "%s.Value()" % result, 8363 successCode, 8364 returnsNewObject, 8365 exceptionCode, 8366 spiderMonkeyInterfacesAreStructs, 8367 ) 8368 return ( 8369 "if (%s.IsNull()) {\n" % result + indent(setNull()) + "}\n" + recTemplate, 8370 recInfal, 8371 ) 8372 8373 if type.isSpiderMonkeyInterface(): 8374 assert spiderMonkeyInterfacesAreStructs 8375 # See comments in GetOrCreateDOMReflector explaining why we need 8376 # to wrap here. 8377 # NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible 8378 return (setObject("*%s.Obj()" % result, wrapAsType=type), False) 8379 8380 if type.isUnion(): 8381 return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result), False) 8382 8383 if type.isDictionary(): 8384 return ( 8385 wrapAndSetPtr("%s.ToObjectInternal(cx, ${jsvalHandle})" % result), 8386 False, 8387 ) 8388 8389 tag = type.tag() 8390 8391 if tag in [ 8392 IDLType.Tags.int8, 8393 IDLType.Tags.uint8, 8394 IDLType.Tags.int16, 8395 IDLType.Tags.uint16, 8396 IDLType.Tags.int32, 8397 ]: 8398 return (setInt32("int32_t(%s)" % result), True) 8399 8400 elif tag in [ 8401 IDLType.Tags.int64, 8402 IDLType.Tags.uint64, 8403 IDLType.Tags.unrestricted_float, 8404 IDLType.Tags.float, 8405 IDLType.Tags.unrestricted_double, 8406 IDLType.Tags.double, 8407 ]: 8408 # XXXbz will cast to double do the "even significand" thing that webidl 8409 # calls for for 64-bit ints? Do we care? 8410 return (setDouble("double(%s)" % result), True) 8411 8412 elif tag == IDLType.Tags.uint32: 8413 return (setUint32(result), True) 8414 8415 elif tag == IDLType.Tags.bool: 8416 return (setBoolean(result), True) 8417 8418 else: 8419 raise TypeError("Need to learn to wrap primitive: %s" % type) 8420 8421 8422 def wrapForType(type, descriptorProvider, templateValues): 8423 """ 8424 Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict 8425 that should contain: 8426 8427 * 'jsvalRef': something that can have .address() called on it to get a 8428 JS::Value* and .set() called on it to set it to a JS::Value. 8429 This can be a JS::MutableHandle<JS::Value> or a 8430 JS::Rooted<JS::Value>. 8431 * 'jsvalHandle': something that can be passed to methods taking a 8432 JS::MutableHandle<JS::Value>. This can be a 8433 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*. 8434 * 'obj' (optional): the name of the variable that contains the JSObject to 8435 use as a scope when wrapping, if not supplied 'obj' 8436 will be used as the name 8437 * 'result' (optional): the name of the variable in which the C++ value is 8438 stored, if not supplied 'result' will be used as 8439 the name 8440 * 'successCode' (optional): the code to run once we have successfully 8441 done the conversion, if not supplied 'return 8442 true;' will be used as the code. The 8443 successCode must ensure that once it runs no 8444 more of the conversion template will be 8445 executed (e.g. by doing a 'return' or 'break' 8446 as appropriate). 8447 * 'returnsNewObject' (optional): If true, we're wrapping for the return 8448 value of a [NewObject] method. Assumed 8449 false if not set. 8450 * 'exceptionCode' (optional): Code to run when a JS exception is thrown. 8451 The default is "return false;". The code 8452 passed here must return. 8453 * 'isConstructorRetval' (optional): If true, we're wrapping a constructor 8454 return value. 8455 """ 8456 wrap = getWrapTemplateForType( 8457 type, 8458 descriptorProvider, 8459 templateValues.get("result", "result"), 8460 templateValues.get("successCode", None), 8461 templateValues.get("returnsNewObject", False), 8462 templateValues.get("exceptionCode", "return false;\n"), 8463 templateValues.get("spiderMonkeyInterfacesAreStructs", False), 8464 isConstructorRetval=templateValues.get("isConstructorRetval", False), 8465 )[0] 8466 8467 defaultValues = {"obj": "obj"} 8468 return string.Template(wrap).substitute(defaultValues, **templateValues) 8469 8470 8471 def infallibleForMember(member, type, descriptorProvider): 8472 """ 8473 Determine the fallibility of changing a C++ value of IDL type "type" into 8474 JS for the given attribute. Apart from returnsNewObject, all the defaults 8475 are used, since the fallbility does not change based on the boolean values, 8476 and the template will be discarded. 8477 8478 CURRENT ASSUMPTIONS: 8479 We assume that successCode for wrapping up return values cannot contain 8480 failure conditions. 8481 """ 8482 return getWrapTemplateForType( 8483 type, 8484 descriptorProvider, 8485 "result", 8486 None, 8487 memberReturnsNewObject(member), 8488 "return false;\n", 8489 False, 8490 )[1] 8491 8492 8493 def leafTypeNeedsCx(type, retVal): 8494 return ( 8495 type.isAny() 8496 or type.isObject() 8497 or type.isJSString() 8498 or (retVal and type.isSpiderMonkeyInterface()) 8499 ) 8500 8501 8502 def leafTypeNeedsScopeObject(type, retVal): 8503 return retVal and type.isSpiderMonkeyInterface() 8504 8505 8506 def leafTypeNeedsRooting(type): 8507 return leafTypeNeedsCx(type, False) or type.isSpiderMonkeyInterface() 8508 8509 8510 def typeNeedsRooting(type): 8511 return typeMatchesLambda(type, lambda t: leafTypeNeedsRooting(t)) 8512 8513 8514 def typeNeedsCx(type, retVal=False): 8515 return typeMatchesLambda(type, lambda t: leafTypeNeedsCx(t, retVal)) 8516 8517 8518 def typeNeedsScopeObject(type, retVal=False): 8519 return typeMatchesLambda(type, lambda t: leafTypeNeedsScopeObject(t, retVal)) 8520 8521 8522 def typeMatchesLambda(type, func): 8523 if type is None: 8524 return False 8525 if type.nullable(): 8526 return typeMatchesLambda(type.inner, func) 8527 if type.isSequence() or type.isRecord(): 8528 return typeMatchesLambda(type.inner, func) 8529 if type.isUnion(): 8530 return any(typeMatchesLambda(t, func) for t in type.unroll().flatMemberTypes) 8531 if type.isDictionary(): 8532 return dictionaryMatchesLambda(type.inner, func) 8533 return func(type) 8534 8535 8536 def dictionaryMatchesLambda(dictionary, func): 8537 return any(typeMatchesLambda(m.type, func) for m in dictionary.members) or ( 8538 dictionary.parent and dictionaryMatchesLambda(dictionary.parent, func) 8539 ) 8540 8541 8542 # Whenever this is modified, please update CGNativeMember.getRetvalInfo as 8543 # needed to keep the types compatible. 8544 def getRetvalDeclarationForType(returnType, descriptorProvider, isMember=False): 8545 """ 8546 Returns a tuple containing five things: 8547 8548 1) A CGThing for the type of the return value, or None if there is no need 8549 for a return value. 8550 8551 2) A value indicating the kind of ourparam to pass the value as. Valid 8552 options are None to not pass as an out param at all, "ref" (to pass a 8553 reference as an out param), and "ptr" (to pass a pointer as an out 8554 param). 8555 8556 3) A CGThing for a tracer for the return value, or None if no tracing is 8557 needed. 8558 8559 4) An argument string to pass to the retval declaration 8560 constructor or None if there are no arguments. 8561 8562 5) The name of a function that needs to be called with the return value 8563 before using it, or None if no function needs to be called. 8564 """ 8565 if returnType is None or returnType.isUndefined(): 8566 # Nothing to declare 8567 return None, None, None, None, None 8568 if returnType.isPrimitive() and returnType.tag() in builtinNames: 8569 result = CGGeneric(builtinNames[returnType.tag()]) 8570 if returnType.nullable(): 8571 result = CGTemplatedType("Nullable", result) 8572 return result, None, None, None, None 8573 if returnType.isJSString(): 8574 if isMember: 8575 raise TypeError("JSString not supported as return type member") 8576 return CGGeneric("JS::Rooted<JSString*>"), "ptr", None, "cx", None 8577 if returnType.isDOMString() or returnType.isUSVString(): 8578 if isMember: 8579 return CGGeneric("nsString"), "ref", None, None, None 8580 return CGGeneric("DOMString"), "ref", None, None, None 8581 if returnType.isByteString() or returnType.isUTF8String(): 8582 if isMember: 8583 return CGGeneric("nsCString"), "ref", None, None, None 8584 return CGGeneric("nsAutoCString"), "ref", None, None, None 8585 if returnType.isEnum(): 8586 result = CGGeneric(returnType.unroll().inner.identifier.name) 8587 if returnType.nullable(): 8588 result = CGTemplatedType("Nullable", result) 8589 return result, None, None, None, None 8590 if returnType.isGeckoInterface() or returnType.isPromise(): 8591 if returnType.isGeckoInterface(): 8592 typeName = returnType.unroll().inner.identifier.name 8593 if typeName == "WindowProxy": 8594 result = CGGeneric("WindowProxyHolder") 8595 if returnType.nullable(): 8596 result = CGTemplatedType("Nullable", result) 8597 return result, None, None, None, None 8598 8599 typeName = descriptorProvider.getDescriptor(typeName).nativeType 8600 else: 8601 typeName = "Promise" 8602 if isMember: 8603 conversion = None 8604 result = CGGeneric("StrongPtrForMember<%s>" % typeName) 8605 else: 8606 conversion = CGGeneric("StrongOrRawPtr<%s>" % typeName) 8607 result = CGGeneric("auto") 8608 return result, None, None, None, conversion 8609 if returnType.isCallback(): 8610 name = returnType.unroll().callback.identifier.name 8611 return CGGeneric("RefPtr<%s>" % name), None, None, None, None 8612 if returnType.isAny(): 8613 if isMember: 8614 return CGGeneric("JS::Value"), None, None, None, None 8615 return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx", None 8616 if returnType.isObject() or returnType.isSpiderMonkeyInterface(): 8617 if isMember: 8618 return CGGeneric("JSObject*"), None, None, None, None 8619 return CGGeneric("JS::Rooted<JSObject*>"), "ptr", None, "cx", None 8620 if returnType.isSequence(): 8621 nullable = returnType.nullable() 8622 if nullable: 8623 returnType = returnType.inner 8624 result, _, _, _, _ = getRetvalDeclarationForType( 8625 returnType.inner, descriptorProvider, isMember="Sequence" 8626 ) 8627 # While we have our inner type, set up our rooter, if needed 8628 if not isMember and typeNeedsRooting(returnType): 8629 rooter = CGGeneric( 8630 "SequenceRooter<%s > resultRooter(cx, &result);\n" % result.define() 8631 ) 8632 else: 8633 rooter = None 8634 result = CGTemplatedType("nsTArray", result) 8635 if nullable: 8636 result = CGTemplatedType("Nullable", result) 8637 return result, "ref", rooter, None, None 8638 if returnType.isRecord(): 8639 nullable = returnType.nullable() 8640 if nullable: 8641 returnType = returnType.inner 8642 result, _, _, _, _ = getRetvalDeclarationForType( 8643 returnType.inner, descriptorProvider, isMember="Record" 8644 ) 8645 # While we have our inner type, set up our rooter, if needed 8646 if not isMember and typeNeedsRooting(returnType): 8647 rooter = CGGeneric( 8648 "RecordRooter<%s> resultRooter(cx, &result);\n" 8649 % (recordKeyType(returnType) + ", " + result.define()) 8650 ) 8651 else: 8652 rooter = None 8653 result = CGTemplatedType("Record", [recordKeyDeclType(returnType), result]) 8654 if nullable: 8655 result = CGTemplatedType("Nullable", result) 8656 return result, "ref", rooter, None, None 8657 if returnType.isDictionary(): 8658 nullable = returnType.nullable() 8659 dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner) 8660 result = CGGeneric(dictName) 8661 if not isMember and typeNeedsRooting(returnType): 8662 if nullable: 8663 result = CGTemplatedType("NullableRootedDictionary", result) 8664 else: 8665 result = CGTemplatedType("RootedDictionary", result) 8666 resultArgs = "cx" 8667 else: 8668 if nullable: 8669 result = CGTemplatedType("Nullable", result) 8670 resultArgs = None 8671 return result, "ref", None, resultArgs, None 8672 if returnType.isUnion(): 8673 result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True)) 8674 if not isMember and typeNeedsRooting(returnType): 8675 if returnType.nullable(): 8676 result = CGTemplatedType("NullableRootedUnion", result) 8677 else: 8678 result = CGTemplatedType("RootedUnion", result) 8679 resultArgs = "cx" 8680 else: 8681 if returnType.nullable(): 8682 result = CGTemplatedType("Nullable", result) 8683 resultArgs = None 8684 return result, "ref", None, resultArgs, None 8685 raise TypeError("Don't know how to declare return value for %s" % returnType) 8686 8687 8688 def needCx(returnType, arguments, extendedAttributes, considerTypes, static=False): 8689 return ( 8690 not static 8691 and considerTypes 8692 and ( 8693 typeNeedsCx(returnType, True) or any(typeNeedsCx(a.type) for a in arguments) 8694 ) 8695 or "implicitJSContext" in extendedAttributes 8696 ) 8697 8698 8699 def needScopeObject( 8700 returnType, arguments, extendedAttributes, isWrapperCached, considerTypes, isMember 8701 ): 8702 """ 8703 isMember should be true if we're dealing with an attribute 8704 annotated as [StoreInSlot]. 8705 """ 8706 return ( 8707 considerTypes 8708 and not isWrapperCached 8709 and ( 8710 (not isMember and typeNeedsScopeObject(returnType, True)) 8711 or any(typeNeedsScopeObject(a.type) for a in arguments) 8712 ) 8713 ) 8714 8715 8716 def callerTypeGetterForDescriptor(descriptor): 8717 if descriptor.interface.isExposedInAnyWorker(): 8718 systemCallerGetter = "nsContentUtils::ThreadsafeIsSystemCaller" 8719 else: 8720 systemCallerGetter = "nsContentUtils::IsSystemCaller" 8721 return "%s(cx) ? CallerType::System : CallerType::NonSystem" % systemCallerGetter 8722 8723 8724 class CGCallGenerator(CGThing): 8725 """ 8726 A class to generate an actual call to a C++ object. Assumes that the C++ 8727 object is stored in a variable whose name is given by the |object| argument. 8728 8729 needsCallerType is a boolean indicating whether the call should receive 8730 a PrincipalType for the caller. 8731 8732 needsErrorResult is a boolean indicating whether the call should be 8733 fallible and thus needs ErrorResult parameter. 8734 8735 resultVar: If the returnType is not void, then the result of the call is 8736 stored in a C++ variable named by resultVar. The caller is responsible for 8737 declaring the result variable. If the caller doesn't care about the result 8738 value, resultVar can be omitted. 8739 8740 context: The context string to pass to MaybeSetPendingException. 8741 """ 8742 8743 def __init__( 8744 self, 8745 needsErrorResult, 8746 needsCallerType, 8747 isChromeOnly, 8748 arguments, 8749 argsPre, 8750 returnType, 8751 extendedAttributes, 8752 descriptor, 8753 nativeMethodName, 8754 static, 8755 object="self", 8756 argsPost=[], 8757 resultVar=None, 8758 context="nullptr", 8759 ): 8760 CGThing.__init__(self) 8761 8762 ( 8763 result, 8764 resultOutParam, 8765 resultRooter, 8766 resultArgs, 8767 resultConversion, 8768 ) = getRetvalDeclarationForType(returnType, descriptor) 8769 8770 args = CGList([CGGeneric(arg) for arg in argsPre], ", ") 8771 for a, name in arguments: 8772 arg = CGGeneric(name) 8773 8774 # Now constify the things that need it 8775 def needsConst(a): 8776 if a.type.isDictionary(): 8777 return True 8778 if a.type.isSequence(): 8779 return True 8780 if a.type.isRecord(): 8781 return True 8782 # isObject() types are always a JS::Rooted, whether 8783 # nullable or not, and it turns out a const JS::Rooted 8784 # is not very helpful at all (in particular, it won't 8785 # even convert to a JS::Handle). 8786 # XXX bz Well, why not??? 8787 if a.type.nullable() and not a.type.isObject(): 8788 return True 8789 if a.type.isString(): 8790 return True 8791 if a.canHaveMissingValue(): 8792 # This will need an Optional or it's a variadic; 8793 # in both cases it should be const. 8794 return True 8795 if a.type.isUnion(): 8796 return True 8797 if a.type.isSpiderMonkeyInterface(): 8798 return True 8799 return False 8800 8801 if needsConst(a): 8802 arg = CGWrapper(arg, pre="Constify(", post=")") 8803 # And convert NonNull<T> to T& 8804 if ( 8805 (a.type.isGeckoInterface() or a.type.isCallback() or a.type.isPromise()) 8806 and not a.type.nullable() 8807 ) or a.type.isDOMString(): 8808 arg = CGWrapper(arg, pre="NonNullHelper(", post=")") 8809 8810 # If it's a refcounted object, let the static analysis know it's 8811 # alive for the duration of the call. 8812 if a.type.isGeckoInterface() or a.type.isCallback(): 8813 arg = CGWrapper(arg, pre="MOZ_KnownLive(", post=")") 8814 8815 args.append(arg) 8816 8817 needResultDecl = False 8818 8819 # Build up our actual call 8820 self.cgRoot = CGList([]) 8821 8822 # Return values that go in outparams go here 8823 if resultOutParam is not None: 8824 if resultVar is None: 8825 needResultDecl = True 8826 resultVar = "result" 8827 if resultOutParam == "ref": 8828 args.append(CGGeneric(resultVar)) 8829 else: 8830 assert resultOutParam == "ptr" 8831 args.append(CGGeneric("&" + resultVar)) 8832 8833 needsSubjectPrincipal = "needsSubjectPrincipal" in extendedAttributes 8834 if needsSubjectPrincipal: 8835 needsNonSystemPrincipal = ( 8836 "needsNonSystemSubjectPrincipal" in extendedAttributes 8837 ) 8838 if needsNonSystemPrincipal: 8839 principalType = "nsIPrincipal*" 8840 subjectPrincipalArg = "subjectPrincipal" 8841 checkPrincipal = dedent( 8842 """ 8843 if (principal->IsSystemPrincipal()) { 8844 principal = nullptr; 8845 } 8846 """ 8847 ) 8848 else: 8849 principalType = "NonNull<nsIPrincipal>" 8850 subjectPrincipalArg = "NonNullHelper(subjectPrincipal)" 8851 checkPrincipal = "" 8852 8853 self.cgRoot.append( 8854 CGGeneric( 8855 fill( 8856 """ 8857 ${principalType} subjectPrincipal; 8858 { 8859 JS::Realm* realm = js::GetContextRealm(cx); 8860 MOZ_ASSERT(realm); 8861 JSPrincipals* principals = JS::GetRealmPrincipals(realm); 8862 nsIPrincipal* principal = nsJSPrincipals::get(principals); 8863 ${checkPrincipal} 8864 subjectPrincipal = principal; 8865 } 8866 """, 8867 principalType=principalType, 8868 checkPrincipal=checkPrincipal, 8869 ) 8870 ) 8871 ) 8872 8873 args.append(CGGeneric("MOZ_KnownLive(%s)" % subjectPrincipalArg)) 8874 8875 if needsCallerType: 8876 if isChromeOnly: 8877 args.append(CGGeneric("SystemCallerGuarantee()")) 8878 else: 8879 args.append(CGGeneric(callerTypeGetterForDescriptor(descriptor))) 8880 8881 canOOM = "canOOM" in extendedAttributes 8882 if needsErrorResult: 8883 args.append(CGGeneric("rv")) 8884 elif canOOM: 8885 args.append(CGGeneric("OOMReporter::From(rv)")) 8886 args.extend(CGGeneric(arg) for arg in argsPost) 8887 8888 call = CGGeneric(nativeMethodName) 8889 if not static: 8890 call = CGWrapper(call, pre="%s->" % object) 8891 call = CGList([call, CGWrapper(args, pre="(", post=")")]) 8892 if returnType is None or returnType.isUndefined() or resultOutParam is not None: 8893 assert resultConversion is None 8894 call = CGList( 8895 [ 8896 CGWrapper( 8897 call, 8898 pre=( 8899 "// NOTE: This assert does NOT call the function.\n" 8900 "static_assert(std::is_void_v<decltype(" 8901 ), 8902 post=')>, "Should be returning void here");', 8903 ), 8904 call, 8905 ], 8906 "\n", 8907 ) 8908 elif resultConversion is not None: 8909 call = CGList([resultConversion, CGWrapper(call, pre="(", post=")")]) 8910 if resultVar is None and result is not None: 8911 needResultDecl = True 8912 resultVar = "result" 8913 8914 if needResultDecl: 8915 if resultArgs is not None: 8916 resultArgsStr = "(%s)" % resultArgs 8917 else: 8918 resultArgsStr = "" 8919 result = CGWrapper(result, post=(" %s%s" % (resultVar, resultArgsStr))) 8920 if resultOutParam is None and resultArgs is None: 8921 call = CGList([result, CGWrapper(call, pre="(", post=")")]) 8922 else: 8923 self.cgRoot.append(CGWrapper(result, post=";\n")) 8924 if resultOutParam is None: 8925 call = CGWrapper(call, pre=resultVar + " = ") 8926 if resultRooter is not None: 8927 self.cgRoot.append(resultRooter) 8928 elif result is not None: 8929 assert resultOutParam is None 8930 call = CGWrapper(call, pre=resultVar + " = ") 8931 8932 call = CGWrapper(call, post=";\n") 8933 self.cgRoot.append(call) 8934 8935 if needsErrorResult or canOOM: 8936 self.cgRoot.prepend(CGGeneric("FastErrorResult rv;\n")) 8937 self.cgRoot.append( 8938 CGGeneric( 8939 fill( 8940 """ 8941 if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx, ${context}))) { 8942 return false; 8943 } 8944 """, 8945 context=context, 8946 ) 8947 ) 8948 ) 8949 8950 self.cgRoot.append(CGGeneric("MOZ_ASSERT(!JS_IsExceptionPending(cx));\n")) 8951 8952 def define(self): 8953 return self.cgRoot.define() 8954 8955 8956 def getUnionMemberName(type): 8957 # Promises can't be in unions, because they're not distinguishable 8958 # from anything else. 8959 assert not type.isPromise() 8960 if type.isGeckoInterface(): 8961 return type.inner.identifier.name 8962 if type.isEnum(): 8963 return type.inner.identifier.name 8964 return type.name 8965 8966 8967 # A counter for making sure that when we're wrapping up things in 8968 # nested sequences we don't use the same variable name to iterate over 8969 # different sequences. 8970 sequenceWrapLevel = 0 8971 recordWrapLevel = 0 8972 8973 8974 def wrapTypeIntoCurrentCompartment(type, value, isMember=True): 8975 """ 8976 Take the thing named by "value" and if it contains "any", 8977 "object", or spidermonkey-interface types inside return a CGThing 8978 that will wrap them into the current compartment. 8979 """ 8980 if type.isAny(): 8981 assert not type.nullable() 8982 if isMember: 8983 value = "JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" % value 8984 else: 8985 value = "&" + value 8986 return CGGeneric( 8987 "if (!JS_WrapValue(cx, %s)) {\n" " return false;\n" "}\n" % value 8988 ) 8989 8990 if type.isObject(): 8991 if isMember: 8992 value = "JS::MutableHandle<JSObject*>::fromMarkedLocation(&%s)" % value 8993 else: 8994 value = "&" + value 8995 return CGGeneric( 8996 "if (!JS_WrapObject(cx, %s)) {\n" " return false;\n" "}\n" % value 8997 ) 8998 8999 if type.isSpiderMonkeyInterface(): 9000 origValue = value 9001 if type.nullable(): 9002 value = "%s.Value()" % value 9003 wrapCode = CGGeneric( 9004 "if (!%s.WrapIntoNewCompartment(cx)) {\n" " return false;\n" "}\n" % value 9005 ) 9006 if type.nullable(): 9007 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue) 9008 return wrapCode 9009 9010 if type.isSequence(): 9011 origValue = value 9012 origType = type 9013 if type.nullable(): 9014 type = type.inner 9015 value = "%s.Value()" % value 9016 global sequenceWrapLevel 9017 index = "indexName%d" % sequenceWrapLevel 9018 sequenceWrapLevel += 1 9019 wrapElement = wrapTypeIntoCurrentCompartment( 9020 type.inner, "%s[%s]" % (value, index) 9021 ) 9022 sequenceWrapLevel -= 1 9023 if not wrapElement: 9024 return None 9025 wrapCode = CGWrapper( 9026 CGIndenter(wrapElement), 9027 pre=( 9028 "for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n" 9029 % (index, index, value, index) 9030 ), 9031 post="}\n", 9032 ) 9033 if origType.nullable(): 9034 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue) 9035 return wrapCode 9036 9037 if type.isRecord(): 9038 origType = type 9039 if type.nullable(): 9040 type = type.inner 9041 recordRef = "%s.Value()" % value 9042 else: 9043 recordRef = value 9044 global recordWrapLevel 9045 entryRef = "mapEntry%d" % recordWrapLevel 9046 recordWrapLevel += 1 9047 wrapElement = wrapTypeIntoCurrentCompartment(type.inner, "%s.mValue" % entryRef) 9048 recordWrapLevel -= 1 9049 if not wrapElement: 9050 return None 9051 wrapCode = CGWrapper( 9052 CGIndenter(wrapElement), 9053 pre=("for (auto& %s : %s.Entries()) {\n" % (entryRef, recordRef)), 9054 post="}\n", 9055 ) 9056 if origType.nullable(): 9057 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % value) 9058 return wrapCode 9059 9060 if type.isDictionary(): 9061 assert not type.nullable() 9062 myDict = type.inner 9063 memberWraps = [] 9064 while myDict: 9065 for member in myDict.members: 9066 memberWrap = wrapArgIntoCurrentCompartment( 9067 member, 9068 "%s.%s" 9069 % (value, CGDictionary.makeMemberName(member.identifier.name)), 9070 ) 9071 if memberWrap: 9072 memberWraps.append(memberWrap) 9073 myDict = myDict.parent 9074 return CGList(memberWraps) if len(memberWraps) != 0 else None 9075 9076 if type.isUnion(): 9077 origValue = value 9078 origType = type 9079 if type.nullable(): 9080 type = type.inner 9081 value = "%s.Value()" % value 9082 memberWraps = [] 9083 for member in type.flatMemberTypes: 9084 memberName = getUnionMemberName(member) 9085 memberWrap = wrapTypeIntoCurrentCompartment( 9086 member, "%s.GetAs%s()" % (value, memberName) 9087 ) 9088 if memberWrap: 9089 memberWrap = CGIfWrapper(memberWrap, "%s.Is%s()" % (value, memberName)) 9090 memberWraps.append(memberWrap) 9091 if len(memberWraps) == 0: 9092 return None 9093 wrapCode = CGList(memberWraps, "else ") 9094 if origType.nullable(): 9095 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue) 9096 return wrapCode 9097 9098 if ( 9099 type.isUndefined() 9100 or type.isString() 9101 or type.isPrimitive() 9102 or type.isEnum() 9103 or type.isGeckoInterface() 9104 or type.isCallback() 9105 or type.isPromise() 9106 ): 9107 # All of these don't need wrapping. 9108 return None 9109 9110 raise TypeError( 9111 "Unknown type; we don't know how to wrap it in constructor " 9112 "arguments: %s" % type 9113 ) 9114 9115 9116 def wrapArgIntoCurrentCompartment(arg, value, isMember=True): 9117 """ 9118 As wrapTypeIntoCurrentCompartment but handles things being optional 9119 """ 9120 origValue = value 9121 isOptional = arg.canHaveMissingValue() 9122 if isOptional: 9123 value = value + ".Value()" 9124 wrap = wrapTypeIntoCurrentCompartment(arg.type, value, isMember) 9125 if wrap and isOptional: 9126 wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue) 9127 return wrap 9128 9129 9130 def needsCallerType(m): 9131 return m.getExtendedAttribute("NeedsCallerType") 9132 9133 9134 class CGPerSignatureCall(CGThing): 9135 """ 9136 This class handles the guts of generating code for a particular 9137 call signature. A call signature consists of four things: 9138 9139 1) A return type, which can be None to indicate that there is no 9140 actual return value (e.g. this is an attribute setter) or an 9141 IDLType if there's an IDL type involved (including |void|). 9142 2) An argument list, which is allowed to be empty. 9143 3) A name of a native method to call. It is ignored for methods 9144 annotated with the "[WebExtensionStub=...]" extended attribute. 9145 4) Whether or not this method is static. Note that this only controls how 9146 the method is called (|self->nativeMethodName(...)| vs 9147 |nativeMethodName(...)|). 9148 9149 We also need to know whether this is a method or a getter/setter 9150 to do error reporting correctly. 9151 9152 The idlNode parameter can be either a method or an attr. We can query 9153 |idlNode.identifier| in both cases, so we can be agnostic between the two. 9154 9155 dontSetSlot should be set to True if the value should not be cached in a 9156 slot (even if the attribute is marked as StoreInSlot or Cached in the 9157 WebIDL). 9158 9159 errorReportingLabel can contain a custom label to use for error reporting. 9160 It will be inserted as is in the code, so if it needs to be a literal 9161 string in C++ it should be quoted. 9162 9163 additionalArgsPre contains additional arguments that are added after the 9164 arguments that CGPerSignatureCall itself adds (JSContext, global, …), and 9165 before the actual arguments. 9166 """ 9167 9168 # XXXbz For now each entry in the argument list is either an 9169 # IDLArgument or a FakeArgument, but longer-term we may want to 9170 # have ways of flagging things like JSContext* or optional_argc in 9171 # there. 9172 9173 def __init__( 9174 self, 9175 returnType, 9176 arguments, 9177 nativeMethodName, 9178 static, 9179 descriptor, 9180 idlNode, 9181 argConversionStartsAt=0, 9182 getter=False, 9183 setter=False, 9184 isConstructor=False, 9185 useCounterName=None, 9186 resultVar=None, 9187 objectName="obj", 9188 dontSetSlot=False, 9189 extendedAttributes=None, 9190 errorReportingLabel=None, 9191 additionalArgsPre=[], 9192 ): 9193 assert idlNode.isMethod() == (not getter and not setter) 9194 assert idlNode.isAttr() == (getter or setter) 9195 # Constructors are always static 9196 assert not isConstructor or static 9197 9198 CGThing.__init__(self) 9199 self.returnType = returnType 9200 self.descriptor = descriptor 9201 self.idlNode = idlNode 9202 if extendedAttributes is None: 9203 extendedAttributes = descriptor.getExtendedAttributes( 9204 idlNode, getter=getter, setter=setter 9205 ) 9206 self.extendedAttributes = extendedAttributes 9207 self.arguments = arguments 9208 self.argCount = len(arguments) 9209 self.isConstructor = isConstructor 9210 self.setSlot = ( 9211 not dontSetSlot and idlNode.isAttr() and idlNode.slotIndices is not None 9212 ) 9213 cgThings = [] 9214 9215 deprecated = idlNode.getExtendedAttribute("Deprecated") or ( 9216 idlNode.isStatic() 9217 and descriptor.interface.getExtendedAttribute("Deprecated") 9218 ) 9219 if deprecated: 9220 cgThings.append( 9221 CGGeneric( 9222 dedent( 9223 """ 9224 DeprecationWarning(cx, obj, DeprecatedOperations::e%s); 9225 """ 9226 % deprecated[0] 9227 ) 9228 ) 9229 ) 9230 9231 lenientFloatCode = None 9232 if idlNode.getExtendedAttribute("LenientFloat") is not None and ( 9233 setter or idlNode.isMethod() 9234 ): 9235 cgThings.append( 9236 CGGeneric( 9237 dedent( 9238 """ 9239 bool foundNonFiniteFloat = false; 9240 """ 9241 ) 9242 ) 9243 ) 9244 lenientFloatCode = "foundNonFiniteFloat = true;\n" 9245 9246 argsPre = [] 9247 if idlNode.isStatic(): 9248 # If we're a constructor, "obj" may not be a function, so calling 9249 # XrayAwareCalleeGlobal() on it is not safe. Of course in the 9250 # constructor case either "obj" is an Xray or we're already in the 9251 # content compartment, not the Xray compartment, so just 9252 # constructing the GlobalObject from "obj" is fine. 9253 if isConstructor: 9254 objForGlobalObject = "obj" 9255 else: 9256 objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)" 9257 cgThings.append( 9258 CGGeneric( 9259 fill( 9260 """ 9261 GlobalObject global(cx, ${obj}); 9262 if (global.Failed()) { 9263 return false; 9264 } 9265 9266 """, 9267 obj=objForGlobalObject, 9268 ) 9269 ) 9270 ) 9271 argsPre.append("global") 9272 9273 # For JS-implemented interfaces we do not want to base the 9274 # needsCx decision on the types involved, just on our extended 9275 # attributes. Also, JSContext is not needed for the static case 9276 # since GlobalObject already contains the context. 9277 needsCx = needCx( 9278 returnType, 9279 arguments, 9280 self.extendedAttributes, 9281 not descriptor.interface.isJSImplemented(), 9282 static, 9283 ) 9284 if needsCx: 9285 argsPre.append("cx") 9286 9287 needsUnwrap = False 9288 argsPost = [] 9289 runConstructorInCallerCompartment = descriptor.interface.getExtendedAttribute( 9290 "RunConstructorInCallerCompartment" 9291 ) 9292 if isConstructor and not runConstructorInCallerCompartment: 9293 needsUnwrap = True 9294 needsUnwrappedVar = False 9295 unwrappedVar = "obj" 9296 if descriptor.interface.isJSImplemented(): 9297 # We need the desired proto in our constructor, because the 9298 # constructor will actually construct our reflector. 9299 argsPost.append("desiredProto") 9300 elif descriptor.interface.isJSImplemented(): 9301 if not idlNode.isStatic(): 9302 needsUnwrap = True 9303 needsUnwrappedVar = True 9304 argsPost.append( 9305 "(unwrappedObj ? js::GetNonCCWObjectRealm(*unwrappedObj) : js::GetContextRealm(cx))" 9306 ) 9307 elif needScopeObject( 9308 returnType, 9309 arguments, 9310 self.extendedAttributes, 9311 descriptor.wrapperCache, 9312 True, 9313 idlNode.getExtendedAttribute("StoreInSlot"), 9314 ): 9315 # If we ever end up with APIs like this on cross-origin objects, 9316 # figure out how the CheckedUnwrapDynamic bits should work. Chances 9317 # are, just calling it with "cx" is fine... For now, though, just 9318 # assert that it does not matter. 9319 assert not descriptor.isMaybeCrossOriginObject() 9320 # The scope object should always be from the relevant 9321 # global. Make sure to unwrap it as needed. 9322 cgThings.append( 9323 CGGeneric( 9324 dedent( 9325 """ 9326 JS::Rooted<JSObject*> unwrappedObj(cx, js::CheckedUnwrapStatic(obj)); 9327 // Caller should have ensured that "obj" can be unwrapped already. 9328 MOZ_DIAGNOSTIC_ASSERT(unwrappedObj); 9329 """ 9330 ) 9331 ) 9332 ) 9333 argsPre.append("unwrappedObj") 9334 9335 if needsUnwrap and needsUnwrappedVar: 9336 # We cannot assign into obj because it's a Handle, not a 9337 # MutableHandle, so we need a separate Rooted. 9338 cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;\n")) 9339 unwrappedVar = "unwrappedObj.ref()" 9340 9341 if idlNode.isMethod() and idlNode.isLegacycaller(): 9342 # If we can have legacycaller with identifier, we can't 9343 # just use the idlNode to determine whether we're 9344 # generating code for the legacycaller or not. 9345 assert idlNode.isIdentifierLess() 9346 # Pass in our thisVal 9347 argsPre.append("args.thisv()") 9348 9349 if idlNode.isMethod(): 9350 argDescription = "argument %(index)d" 9351 elif setter: 9352 argDescription = "value being assigned" 9353 else: 9354 assert self.argCount == 0 9355 9356 if needsUnwrap: 9357 # It's very important that we construct our unwrappedObj, if we need 9358 # to do it, before we might start setting up Rooted things for our 9359 # arguments, so that we don't violate the stack discipline Rooted 9360 # depends on. 9361 cgThings.append( 9362 CGGeneric("bool objIsXray = xpc::WrapperFactory::IsXrayWrapper(obj);\n") 9363 ) 9364 if needsUnwrappedVar: 9365 cgThings.append( 9366 CGIfWrapper( 9367 CGGeneric("unwrappedObj.emplace(cx, obj);\n"), "objIsXray" 9368 ) 9369 ) 9370 9371 for i in range(argConversionStartsAt, self.argCount): 9372 cgThings.append( 9373 CGArgumentConverter( 9374 arguments[i], 9375 i, 9376 self.descriptor, 9377 argDescription % {"index": i + 1}, 9378 idlNode, 9379 invalidEnumValueFatal=not setter, 9380 lenientFloatCode=lenientFloatCode, 9381 ) 9382 ) 9383 9384 # Now that argument processing is done, enforce the LenientFloat stuff 9385 if lenientFloatCode: 9386 if setter: 9387 foundNonFiniteFloatBehavior = "return true;\n" 9388 else: 9389 assert idlNode.isMethod() 9390 foundNonFiniteFloatBehavior = dedent( 9391 """ 9392 args.rval().setUndefined(); 9393 return true; 9394 """ 9395 ) 9396 cgThings.append( 9397 CGGeneric( 9398 fill( 9399 """ 9400 if (foundNonFiniteFloat) { 9401 $*{returnSteps} 9402 } 9403 """, 9404 returnSteps=foundNonFiniteFloatBehavior, 9405 ) 9406 ) 9407 ) 9408 9409 if needsUnwrap: 9410 # Something depends on having the unwrapped object, so unwrap it now. 9411 xraySteps = [] 9412 # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is 9413 # not null. 9414 xraySteps.append( 9415 CGGeneric( 9416 fill( 9417 """ 9418 // Since our object is an Xray, we can just CheckedUnwrapStatic: 9419 // we know Xrays have no dynamic unwrap behavior. 9420 ${obj} = js::CheckedUnwrapStatic(${obj}); 9421 if (!${obj}) { 9422 return false; 9423 } 9424 """, 9425 obj=unwrappedVar, 9426 ) 9427 ) 9428 ) 9429 if isConstructor: 9430 # If we're called via an xray, we need to enter the underlying 9431 # object's compartment and then wrap up all of our arguments into 9432 # that compartment as needed. This is all happening after we've 9433 # already done the conversions from JS values to WebIDL (C++) 9434 # values, so we only need to worry about cases where there are 'any' 9435 # or 'object' types, or other things that we represent as actual 9436 # JSAPI types, present. Effectively, we're emulating a 9437 # CrossCompartmentWrapper, but working with the C++ types, not the 9438 # original list of JS::Values. 9439 cgThings.append(CGGeneric("Maybe<JSAutoRealm> ar;\n")) 9440 xraySteps.append(CGGeneric("ar.emplace(cx, obj);\n")) 9441 xraySteps.append( 9442 CGGeneric( 9443 dedent( 9444 """ 9445 if (!JS_WrapObject(cx, &desiredProto)) { 9446 return false; 9447 } 9448 """ 9449 ) 9450 ) 9451 ) 9452 xraySteps.extend( 9453 wrapArgIntoCurrentCompartment(arg, argname, isMember=False) 9454 for arg, argname in self.getArguments() 9455 ) 9456 9457 cgThings.append(CGIfWrapper(CGList(xraySteps), "objIsXray")) 9458 9459 if idlNode.getExtendedAttribute("CEReactions") is not None and not getter: 9460 cgThings.append( 9461 CGGeneric( 9462 dedent( 9463 """ 9464 Maybe<AutoCEReaction> ceReaction; 9465 DocGroup* docGroup = self->GetDocGroup(); 9466 if (docGroup) { 9467 ceReaction.emplace(docGroup->CustomElementReactionsStack(), cx); 9468 } 9469 """ 9470 ) 9471 ) 9472 ) 9473 9474 # If this is a method that was generated by a maplike/setlike 9475 # interface, use the maplike/setlike generator to fill in the body. 9476 # Otherwise, use CGCallGenerator to call the native method. 9477 if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod(): 9478 if ( 9479 idlNode.maplikeOrSetlikeOrIterable.isMaplike() 9480 or idlNode.maplikeOrSetlikeOrIterable.isSetlike() 9481 ): 9482 cgThings.append( 9483 CGMaplikeOrSetlikeMethodGenerator( 9484 descriptor, 9485 idlNode.maplikeOrSetlikeOrIterable, 9486 idlNode.identifier.name, 9487 ) 9488 ) 9489 else: 9490 cgThings.append( 9491 CGIterableMethodGenerator( 9492 descriptor, 9493 idlNode.identifier.name, 9494 self.getArgumentNames(), 9495 ) 9496 ) 9497 elif idlNode.isAttr() and idlNode.type.isObservableArray(): 9498 assert setter 9499 cgThings.append(CGObservableArraySetterGenerator(descriptor, idlNode)) 9500 else: 9501 if errorReportingLabel is None: 9502 context = GetLabelForErrorReporting(descriptor, idlNode, isConstructor) 9503 if getter: 9504 context = context + " getter" 9505 elif setter: 9506 context = context + " setter" 9507 # Callee expects a quoted string for the context if 9508 # there's a context. 9509 context = '"%s"' % context 9510 else: 9511 context = errorReportingLabel 9512 9513 if idlNode.isMethod() and idlNode.getExtendedAttribute("WebExtensionStub"): 9514 [ 9515 nativeMethodName, 9516 argsPre, 9517 args, 9518 ] = self.processWebExtensionStubAttribute(cgThings) 9519 else: 9520 args = self.getArguments() 9521 9522 cgThings.append( 9523 CGCallGenerator( 9524 self.needsErrorResult(), 9525 needsCallerType(idlNode), 9526 isChromeOnly(idlNode), 9527 args, 9528 argsPre + additionalArgsPre, 9529 returnType, 9530 self.extendedAttributes, 9531 descriptor, 9532 nativeMethodName, 9533 static, 9534 # We know our "self" must be being kept alive; otherwise we have 9535 # a serious problem. In common cases it's just an argument and 9536 # we're MOZ_CAN_RUN_SCRIPT, but in some cases it's on the stack 9537 # and being kept alive via references from JS. 9538 object="MOZ_KnownLive(self)", 9539 argsPost=argsPost, 9540 resultVar=resultVar, 9541 context=context, 9542 ) 9543 ) 9544 9545 if useCounterName: 9546 # Generate a telemetry call for when [UseCounter] is used. 9547 windowCode = fill( 9548 """ 9549 SetUseCounter(obj, eUseCounter_${useCounterName}); 9550 """, 9551 useCounterName=useCounterName, 9552 ) 9553 workerCode = fill( 9554 """ 9555 SetUseCounter(UseCounterWorker::${useCounterName}); 9556 """, 9557 useCounterName=useCounterName, 9558 ) 9559 code = "" 9560 if idlNode.isExposedInWindow() and idlNode.isExposedInAnyWorker(): 9561 code += fill( 9562 """ 9563 if (NS_IsMainThread()) { 9564 ${windowCode} 9565 } else { 9566 ${workerCode} 9567 } 9568 """, 9569 windowCode=windowCode, 9570 workerCode=workerCode, 9571 ) 9572 elif idlNode.isExposedInWindow(): 9573 code += windowCode 9574 elif idlNode.isExposedInAnyWorker(): 9575 code += workerCode 9576 9577 cgThings.append(CGGeneric(code)) 9578 9579 self.cgRoot = CGList(cgThings) 9580 9581 def getArgumentNames(self): 9582 return ["arg" + str(i) for i in range(len(self.arguments))] 9583 9584 def getArguments(self): 9585 return list(zip(self.arguments, self.getArgumentNames())) 9586 9587 def processWebExtensionStubAttribute(self, cgThings): 9588 nativeMethodName = "CallWebExtMethod" 9589 stubNameSuffix = self.idlNode.getExtendedAttribute("WebExtensionStub") 9590 if isinstance(stubNameSuffix, list): 9591 nativeMethodName += stubNameSuffix[0] 9592 9593 argsLength = len(self.getArguments()) 9594 singleVariadicArg = argsLength == 1 and self.getArguments()[0][0].variadic 9595 9596 # If the method signature does only include a single variadic arguments, 9597 # then `arg0` is already a Sequence of JS values and we can pass that 9598 # to the WebExtensions Stub method as is. 9599 if singleVariadicArg: 9600 argsPre = [ 9601 "cx", 9602 'u"%s"_ns' % self.idlNode.identifier.name, 9603 "Constify(%s)" % "arg0", 9604 ] 9605 args = [] 9606 return [nativeMethodName, argsPre, args] 9607 9608 argsPre = [ 9609 "cx", 9610 'u"%s"_ns' % self.idlNode.identifier.name, 9611 "Constify(%s)" % "args_sequence", 9612 ] 9613 args = [] 9614 9615 # Determine the maximum number of elements of the js values sequence argument, 9616 # skipping the last optional callback argument if any: 9617 # 9618 # if this WebExtensions API method does expect a last optional callback argument, 9619 # then it is the callback parameter supported for chrome-compatibility 9620 # reasons, and we want it as a separate argument passed to the WebExtension 9621 # stub method and skip it from the js values sequence including all other 9622 # arguments. 9623 maxArgsSequenceLen = argsLength 9624 if argsLength > 0: 9625 lastArg = self.getArguments()[argsLength - 1] 9626 isCallback = lastArg[0].type.tag() == IDLType.Tags.callback 9627 if isCallback and lastArg[0].optional: 9628 argsPre.append( 9629 "MOZ_KnownLive(NonNullHelper(Constify(%s)))" % lastArg[1] 9630 ) 9631 maxArgsSequenceLen = argsLength - 1 9632 9633 cgThings.append( 9634 CGGeneric( 9635 dedent( 9636 fill( 9637 """ 9638 // Collecting all args js values into the single sequence argument 9639 // passed to the webextensions stub method. 9640 // 9641 // NOTE: The stub method will receive the original non-normalized js values, 9642 // but those arguments will still be normalized on the main thread by the 9643 // WebExtensions API request handler using the same JSONSchema defnition 9644 // used by the non-webIDL webextensions API bindings. 9645 AutoSequence<JS::Value> args_sequence; 9646 SequenceRooter<JS::Value> args_sequence_holder(cx, &args_sequence); 9647 9648 // maximum number of arguments expected by the WebExtensions API method 9649 // excluding the last optional chrome-compatible callback argument (which 9650 // is being passed to the stub method as a separate additional argument). 9651 uint32_t maxArgsSequenceLen = ${maxArgsSequenceLen}; 9652 9653 uint32_t sequenceArgsLen = args.length() <= maxArgsSequenceLen ? 9654 args.length() : maxArgsSequenceLen; 9655 9656 if (sequenceArgsLen > 0) { 9657 if (!args_sequence.SetCapacity(sequenceArgsLen, mozilla::fallible)) { 9658 JS_ReportOutOfMemory(cx); 9659 return false; 9660 } 9661 for (uint32_t argIdx = 0; argIdx < sequenceArgsLen; ++argIdx) { 9662 // OK to do infallible append here, since we ensured capacity already. 9663 JS::Value& slot = *args_sequence.AppendElement(); 9664 slot = args[argIdx]; 9665 } 9666 } 9667 """, 9668 maxArgsSequenceLen=maxArgsSequenceLen, 9669 ) 9670 ) 9671 ) 9672 ) 9673 9674 return [nativeMethodName, argsPre, args] 9675 9676 def needsErrorResult(self): 9677 return "needsErrorResult" in self.extendedAttributes 9678 9679 def wrap_return_value(self): 9680 wrapCode = "" 9681 9682 returnsNewObject = memberReturnsNewObject(self.idlNode) 9683 if returnsNewObject and ( 9684 self.returnType.isGeckoInterface() or self.returnType.isPromise() 9685 ): 9686 wrapCode += dedent( 9687 """ 9688 static_assert(!std::is_pointer_v<decltype(result)>, 9689 "NewObject implies that we need to keep the object alive with a strong reference."); 9690 """ 9691 ) 9692 9693 if self.setSlot: 9694 # For attributes in slots, we want to do some 9695 # post-processing once we've wrapped them. 9696 successCode = "break;\n" 9697 else: 9698 successCode = None 9699 9700 resultTemplateValues = { 9701 "jsvalRef": "args.rval()", 9702 "jsvalHandle": "args.rval()", 9703 "returnsNewObject": returnsNewObject, 9704 "isConstructorRetval": self.isConstructor, 9705 "successCode": successCode, 9706 # 'obj' in this dictionary is the thing whose compartment we are 9707 # trying to do the to-JS conversion in. We're going to put that 9708 # thing in a variable named "conversionScope" if setSlot is true. 9709 # Otherwise, just use "obj" for lack of anything better. 9710 "obj": "conversionScope" if self.setSlot else "obj", 9711 } 9712 9713 wrapCode += wrapForType(self.returnType, self.descriptor, resultTemplateValues) 9714 9715 if self.setSlot: 9716 if self.idlNode.isStatic(): 9717 raise TypeError( 9718 "Attribute %s.%s is static, so we don't have a useful slot " 9719 "to cache it in, because we don't have support for that on " 9720 "interface objects. See " 9721 "https://bugzilla.mozilla.org/show_bug.cgi?id=1363870" 9722 % ( 9723 self.descriptor.interface.identifier.name, 9724 self.idlNode.identifier.name, 9725 ) 9726 ) 9727 9728 # When using a slot on the Xray expando, we need to make sure that 9729 # our initial conversion to a JS::Value is done in the caller 9730 # compartment. When using a slot on our reflector, we want to do 9731 # the conversion in the compartment of that reflector (that is, 9732 # slotStorage). In both cases we want to make sure that we finally 9733 # set up args.rval() to be in the caller compartment. We also need 9734 # to make sure that the conversion steps happen inside a do/while 9735 # that they can break out of on success. 9736 # 9737 # Of course we always have to wrap the value into the slotStorage 9738 # compartment before we store it in slotStorage. 9739 9740 # postConversionSteps are the steps that run while we're still in 9741 # the compartment we do our conversion in but after we've finished 9742 # the initial conversion into args.rval(). 9743 postConversionSteps = "" 9744 if self.idlNode.getExtendedAttribute("Frozen"): 9745 assert ( 9746 self.idlNode.type.isSequence() or self.idlNode.type.isDictionary() 9747 ) 9748 freezeValue = CGGeneric( 9749 "JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());\n" 9750 "if (!JS_FreezeObject(cx, rvalObj)) {\n" 9751 " return false;\n" 9752 "}\n" 9753 ) 9754 if self.idlNode.type.nullable(): 9755 freezeValue = CGIfWrapper(freezeValue, "args.rval().isObject()") 9756 postConversionSteps += freezeValue.define() 9757 9758 # slotStorageSteps are steps that run once we have entered the 9759 # slotStorage compartment. 9760 if self.idlNode.getExtendedAttribute( 9761 "ReflectedHTMLAttributeReturningFrozenArray" 9762 ): 9763 storeInSlot = fill( 9764 """ 9765 array[${arrayIndex}] = storedVal; 9766 """, 9767 arrayIndex=reflectedHTMLAttributesArrayIndex( 9768 self.descriptor, self.idlNode 9769 ), 9770 ) 9771 else: 9772 storeInSlot = dedent( 9773 """ 9774 JS::SetReservedSlot(slotStorage, slotIndex, storedVal); 9775 """ 9776 ) 9777 9778 slotStorageSteps = fill( 9779 """ 9780 // Make a copy so that we don't do unnecessary wrapping on args.rval(). 9781 JS::Rooted<JS::Value> storedVal(cx, args.rval()); 9782 if (!${maybeWrap}(cx, &storedVal)) { 9783 return false; 9784 } 9785 $*{storeInSlot} 9786 """, 9787 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type), 9788 storeInSlot=storeInSlot, 9789 ) 9790 9791 checkForXray = mayUseXrayExpandoSlots(self.descriptor, self.idlNode) 9792 9793 # For the case of Cached attributes, go ahead and preserve our 9794 # wrapper if needed. We need to do this because otherwise the 9795 # wrapper could get garbage-collected and the cached value would 9796 # suddenly disappear, but the whole premise of cached values is that 9797 # they never change without explicit action on someone's part. We 9798 # don't do this for StoreInSlot, since those get dealt with during 9799 # wrapper setup, and failure would involve us trying to clear an 9800 # already-preserved wrapper. 9801 if ( 9802 self.idlNode.getExtendedAttribute("Cached") 9803 or self.idlNode.getExtendedAttribute( 9804 "ReflectedHTMLAttributeReturningFrozenArray" 9805 ) 9806 ) and self.descriptor.wrapperCache: 9807 preserveWrapper = dedent( 9808 """ 9809 PreserveWrapper(self); 9810 """ 9811 ) 9812 if checkForXray: 9813 preserveWrapper = fill( 9814 """ 9815 if (!isXray) { 9816 // In the Xray case we don't need to do this, because getting the 9817 // expando object already preserved our wrapper. 9818 $*{preserveWrapper} 9819 } 9820 """, 9821 preserveWrapper=preserveWrapper, 9822 ) 9823 slotStorageSteps += preserveWrapper 9824 9825 if checkForXray: 9826 # In the Xray case we use the current global as conversion 9827 # scope, as explained in the big compartment/conversion comment 9828 # above. 9829 conversionScope = "isXray ? JS::CurrentGlobalOrNull(cx) : slotStorage" 9830 else: 9831 conversionScope = "slotStorage" 9832 9833 wrapCode = fill( 9834 """ 9835 { 9836 JS::Rooted<JSObject*> conversionScope(cx, ${conversionScope}); 9837 JSAutoRealm ar(cx, conversionScope); 9838 do { // block we break out of when done wrapping 9839 $*{wrapCode} 9840 } while (false); 9841 $*{postConversionSteps} 9842 } 9843 { // And now store things in the realm of our slotStorage. 9844 JSAutoRealm ar(cx, slotStorage); 9845 $*{slotStorageSteps} 9846 } 9847 // And now make sure args.rval() is in the caller realm. 9848 return ${maybeWrap}(cx, args.rval()); 9849 """, 9850 conversionScope=conversionScope, 9851 wrapCode=wrapCode, 9852 postConversionSteps=postConversionSteps, 9853 slotStorageSteps=slotStorageSteps, 9854 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type), 9855 ) 9856 return wrapCode 9857 9858 def define(self): 9859 return self.cgRoot.define() + self.wrap_return_value() 9860 9861 9862 class CGSwitch(CGList): 9863 """ 9864 A class to generate code for a switch statement. 9865 9866 Takes three constructor arguments: an expression, a list of cases, 9867 and an optional default. 9868 9869 Each case is a CGCase. The default is a CGThing for the body of 9870 the default case, if any. 9871 """ 9872 9873 def __init__(self, expression, cases, default=None): 9874 CGList.__init__(self, [CGIndenter(c) for c in cases]) 9875 self.prepend(CGGeneric("switch (" + expression + ") {\n")) 9876 if default is not None: 9877 self.append( 9878 CGIndenter( 9879 CGWrapper(CGIndenter(default), pre="default: {\n", post="}\n") 9880 ) 9881 ) 9882 9883 self.append(CGGeneric("}\n")) 9884 9885 9886 class CGCase(CGList): 9887 """ 9888 A class to generate code for a case statement. 9889 9890 Takes three constructor arguments: an expression, a CGThing for 9891 the body (allowed to be None if there is no body), and an optional 9892 argument for whether add a break, add fallthrough annotation or add nothing 9893 (defaulting to add a break). 9894 """ 9895 9896 ADD_BREAK = 0 9897 ADD_FALLTHROUGH = 1 9898 DONT_ADD_BREAK = 2 9899 9900 def __init__(self, expression, body, breakOrFallthrough=ADD_BREAK): 9901 CGList.__init__(self, []) 9902 9903 assert ( 9904 breakOrFallthrough == CGCase.ADD_BREAK 9905 or breakOrFallthrough == CGCase.ADD_FALLTHROUGH 9906 or breakOrFallthrough == CGCase.DONT_ADD_BREAK 9907 ) 9908 9909 self.append(CGGeneric("case " + expression + ": {\n")) 9910 bodyList = CGList([body]) 9911 if breakOrFallthrough == CGCase.ADD_FALLTHROUGH: 9912 bodyList.append(CGGeneric("[[fallthrough]];\n")) 9913 elif breakOrFallthrough == CGCase.ADD_BREAK: 9914 bodyList.append(CGGeneric("break;\n")) 9915 self.append(CGIndenter(bodyList)) 9916 self.append(CGGeneric("}\n")) 9917 9918 9919 class CGMethodCall(CGThing): 9920 """ 9921 A class to generate selection of a method signature from a set of 9922 signatures and generation of a call to that signature. 9923 """ 9924 9925 def __init__( 9926 self, nativeMethodName, static, descriptor, method, isConstructor=False 9927 ): 9928 CGThing.__init__(self) 9929 9930 methodName = GetLabelForErrorReporting(descriptor, method, isConstructor) 9931 argDesc = "argument %d" 9932 9933 if method.getExtendedAttribute("UseCounter"): 9934 useCounterName = methodName.replace(".", "_").replace(" ", "_") 9935 else: 9936 useCounterName = None 9937 9938 if method.isStatic(): 9939 nativeType = descriptor.nativeType 9940 staticTypeOverride = PropertyDefiner.getStringAttr( 9941 method, "StaticClassOverride" 9942 ) 9943 if staticTypeOverride: 9944 nativeType = staticTypeOverride 9945 nativeMethodName = "%s::%s" % (nativeType, nativeMethodName) 9946 9947 def requiredArgCount(signature): 9948 arguments = signature[1] 9949 if len(arguments) == 0: 9950 return 0 9951 requiredArgs = len(arguments) 9952 while requiredArgs and arguments[requiredArgs - 1].optional: 9953 requiredArgs -= 1 9954 return requiredArgs 9955 9956 def getPerSignatureCall(signature, argConversionStartsAt=0): 9957 return CGPerSignatureCall( 9958 signature[0], 9959 signature[1], 9960 nativeMethodName, 9961 static, 9962 descriptor, 9963 method, 9964 argConversionStartsAt=argConversionStartsAt, 9965 isConstructor=isConstructor, 9966 useCounterName=useCounterName, 9967 ) 9968 9969 signatures = method.signatures() 9970 if len(signatures) == 1: 9971 # Special case: we can just do a per-signature method call 9972 # here for our one signature and not worry about switching 9973 # on anything. 9974 signature = signatures[0] 9975 self.cgRoot = CGList([getPerSignatureCall(signature)]) 9976 requiredArgs = requiredArgCount(signature) 9977 9978 # Skip required arguments check for maplike/setlike interfaces, as 9979 # they can have arguments which are not passed, and are treated as 9980 # if undefined had been explicitly passed. 9981 if requiredArgs > 0 and not method.isMaplikeOrSetlikeOrIterableMethod(): 9982 code = fill( 9983 """ 9984 if (!args.requireAtLeast(cx, "${methodName}", ${requiredArgs})) { 9985 return false; 9986 } 9987 """, 9988 requiredArgs=requiredArgs, 9989 methodName=methodName, 9990 ) 9991 self.cgRoot.prepend(CGGeneric(code)) 9992 return 9993 9994 # Need to find the right overload 9995 maxArgCount = method.maxArgCount 9996 allowedArgCounts = method.allowedArgCounts 9997 9998 argCountCases = [] 9999 for argCountIdx, argCount in enumerate(allowedArgCounts): 10000 possibleSignatures = method.signaturesForArgCount(argCount) 10001 10002 # Try to optimize away cases when the next argCount in the list 10003 # will have the same code as us; if it does, we can fall through to 10004 # that case. 10005 if argCountIdx + 1 < len(allowedArgCounts): 10006 nextPossibleSignatures = method.signaturesForArgCount( 10007 allowedArgCounts[argCountIdx + 1] 10008 ) 10009 else: 10010 nextPossibleSignatures = None 10011 if possibleSignatures == nextPossibleSignatures: 10012 # Same set of signatures means we better have the same 10013 # distinguishing index. So we can in fact just fall through to 10014 # the next case here. 10015 assert len(possibleSignatures) == 1 or ( 10016 method.distinguishingIndexForArgCount(argCount) 10017 == method.distinguishingIndexForArgCount( 10018 allowedArgCounts[argCountIdx + 1] 10019 ) 10020 ) 10021 argCountCases.append( 10022 CGCase(str(argCount), None, CGCase.ADD_FALLTHROUGH) 10023 ) 10024 continue 10025 10026 if len(possibleSignatures) == 1: 10027 # easy case! 10028 signature = possibleSignatures[0] 10029 argCountCases.append( 10030 CGCase(str(argCount), getPerSignatureCall(signature)) 10031 ) 10032 continue 10033 10034 distinguishingIndex = method.distinguishingIndexForArgCount(argCount) 10035 10036 def distinguishingArgument(signature): 10037 args = signature[1] 10038 if distinguishingIndex < len(args): 10039 return args[distinguishingIndex] 10040 assert args[-1].variadic 10041 return args[-1] 10042 10043 def distinguishingType(signature): 10044 return distinguishingArgument(signature).type 10045 10046 for sig in possibleSignatures: 10047 # We should not have "any" args at distinguishingIndex, 10048 # since we have multiple possible signatures remaining, 10049 # but "any" is never distinguishable from anything else. 10050 assert not distinguishingType(sig).isAny() 10051 # We can't handle unions at the distinguishing index. 10052 if distinguishingType(sig).isUnion(): 10053 raise TypeError( 10054 "No support for unions as distinguishing " 10055 "arguments yet: %s" % distinguishingArgument(sig).location 10056 ) 10057 # We don't support variadics as the distinguishingArgument yet. 10058 # If you want to add support, consider this case: 10059 # 10060 # undefined(long... foo); 10061 # undefined(long bar, Int32Array baz); 10062 # 10063 # in which we have to convert argument 0 to long before picking 10064 # an overload... but all the variadic stuff needs to go into a 10065 # single array in case we pick that overload, so we have to have 10066 # machinery for converting argument 0 to long and then either 10067 # placing it in the variadic bit or not. Or something. We may 10068 # be able to loosen this restriction if the variadic arg is in 10069 # fact at distinguishingIndex, perhaps. Would need to 10070 # double-check. 10071 if distinguishingArgument(sig).variadic: 10072 raise TypeError( 10073 "No support for variadics as distinguishing " 10074 "arguments yet: %s" % distinguishingArgument(sig).location 10075 ) 10076 10077 # Convert all our arguments up to the distinguishing index. 10078 # Doesn't matter which of the possible signatures we use, since 10079 # they all have the same types up to that point; just use 10080 # possibleSignatures[0] 10081 caseBody = [ 10082 CGArgumentConverter( 10083 possibleSignatures[0][1][i], 10084 i, 10085 descriptor, 10086 argDesc % (i + 1), 10087 method, 10088 ) 10089 for i in range(0, distinguishingIndex) 10090 ] 10091 10092 # Select the right overload from our set. 10093 distinguishingArg = "args[%d]" % distinguishingIndex 10094 10095 def tryCall( 10096 signature, indent, isDefinitelyObject=False, isNullOrUndefined=False 10097 ): 10098 assert not isDefinitelyObject or not isNullOrUndefined 10099 assert isDefinitelyObject or isNullOrUndefined 10100 if isDefinitelyObject: 10101 failureCode = "break;\n" 10102 else: 10103 failureCode = None 10104 type = distinguishingType(signature) 10105 # The argument at index distinguishingIndex can't possibly be 10106 # unset here, because we've already checked that argc is large 10107 # enough that we can examine this argument. But note that we 10108 # still want to claim that optional arguments are optional, in 10109 # case undefined was passed in. 10110 argIsOptional = distinguishingArgument(signature).canHaveMissingValue() 10111 testCode = instantiateJSToNativeConversion( 10112 getJSToNativeConversionInfo( 10113 type, 10114 descriptor, 10115 failureCode=failureCode, 10116 isDefinitelyObject=isDefinitelyObject, 10117 isNullOrUndefined=isNullOrUndefined, 10118 isOptional=argIsOptional, 10119 sourceDescription=(argDesc % (distinguishingIndex + 1)), 10120 ), 10121 { 10122 "declName": "arg%d" % distinguishingIndex, 10123 "holderName": ("arg%d" % distinguishingIndex) + "_holder", 10124 "val": distinguishingArg, 10125 "obj": "obj", 10126 "haveValue": "args.hasDefined(%d)" % distinguishingIndex, 10127 "passedToJSImpl": toStringBool( 10128 isJSImplementedDescriptor(descriptor) 10129 ), 10130 }, 10131 checkForValue=argIsOptional, 10132 ) 10133 caseBody.append(CGIndenter(testCode, indent)) 10134 10135 # If we got this far, we know we unwrapped to the right 10136 # C++ type, so just do the call. Start conversion with 10137 # distinguishingIndex + 1, since we already converted 10138 # distinguishingIndex. 10139 caseBody.append( 10140 CGIndenter( 10141 getPerSignatureCall(signature, distinguishingIndex + 1), indent 10142 ) 10143 ) 10144 10145 def hasConditionalConversion(type): 10146 """ 10147 Return whether the argument conversion for this type will be 10148 conditional on the type of incoming JS value. For example, for 10149 interface types the conversion is conditional on the incoming 10150 value being isObject(). 10151 10152 For the types for which this returns false, we do not have to 10153 output extra isUndefined() or isNullOrUndefined() cases, because 10154 null/undefined values will just fall through into our 10155 unconditional conversion. 10156 """ 10157 if type.isString() or type.isEnum(): 10158 return False 10159 if type.isBoolean(): 10160 distinguishingTypes = ( 10161 distinguishingType(s) for s in possibleSignatures 10162 ) 10163 return any( 10164 t.isString() or t.isEnum() or t.isNumeric() 10165 for t in distinguishingTypes 10166 ) 10167 if type.isNumeric(): 10168 distinguishingTypes = ( 10169 distinguishingType(s) for s in possibleSignatures 10170 ) 10171 return any(t.isString() or t.isEnum() for t in distinguishingTypes) 10172 return True 10173 10174 def needsNullOrUndefinedCase(type): 10175 """ 10176 Return true if the type needs a special isNullOrUndefined() case 10177 """ 10178 return ( 10179 type.nullable() and hasConditionalConversion(type) 10180 ) or type.isDictionary() 10181 10182 # First check for undefined and optional distinguishing arguments 10183 # and output a special branch for that case. Note that we don't 10184 # use distinguishingArgument here because we actualy want to 10185 # exclude variadic arguments. Also note that we skip this check if 10186 # we plan to output a isNullOrUndefined() special case for this 10187 # argument anyway, since that will subsume our isUndefined() check. 10188 # This is safe, because there can be at most one nullable 10189 # distinguishing argument, so if we're it we'll definitely get 10190 # picked up by the nullable handling. Also, we can skip this check 10191 # if the argument has an unconditional conversion later on. 10192 undefSigs = [ 10193 s 10194 for s in possibleSignatures 10195 if distinguishingIndex < len(s[1]) 10196 and s[1][distinguishingIndex].optional 10197 and hasConditionalConversion(s[1][distinguishingIndex].type) 10198 and not needsNullOrUndefinedCase(s[1][distinguishingIndex].type) 10199 ] 10200 # Can't have multiple signatures with an optional argument at the 10201 # same index. 10202 assert len(undefSigs) < 2 10203 if len(undefSigs) > 0: 10204 caseBody.append( 10205 CGGeneric("if (%s.isUndefined()) {\n" % distinguishingArg) 10206 ) 10207 tryCall(undefSigs[0], 2, isNullOrUndefined=True) 10208 caseBody.append(CGGeneric("}\n")) 10209 10210 # Next, check for null or undefined. That means looking for 10211 # nullable arguments at the distinguishing index and outputting a 10212 # separate branch for them. But if the nullable argument has an 10213 # unconditional conversion, we don't need to do that. The reason 10214 # for that is that at most one argument at the distinguishing index 10215 # is nullable (since two nullable arguments are not 10216 # distinguishable), and null/undefined values will always fall 10217 # through to the unconditional conversion we have, if any, since 10218 # they will fail whatever the conditions on the input value are for 10219 # our other conversions. 10220 nullOrUndefSigs = [ 10221 s 10222 for s in possibleSignatures 10223 if needsNullOrUndefinedCase(distinguishingType(s)) 10224 ] 10225 # Can't have multiple nullable types here 10226 assert len(nullOrUndefSigs) < 2 10227 if len(nullOrUndefSigs) > 0: 10228 caseBody.append( 10229 CGGeneric("if (%s.isNullOrUndefined()) {\n" % distinguishingArg) 10230 ) 10231 tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True) 10232 caseBody.append(CGGeneric("}\n")) 10233 10234 # Now check for distinguishingArg being various kinds of objects. 10235 # The spec says to check for the following things in order: 10236 # 1) A platform object that's not a platform array object, being 10237 # passed to an interface or "object" arg. 10238 # 2) A callable object being passed to a callback or "object" arg. 10239 # 3) An iterable object being passed to a sequence arg. 10240 # 4) Any object being passed to a array or callback interface or 10241 # dictionary or "object" arg. 10242 10243 # First grab all the overloads that have a non-callback interface 10244 # (which includes SpiderMonkey interfaces) at the distinguishing 10245 # index. We can also include the ones that have an "object" here, 10246 # since if those are present no other object-typed argument will 10247 # be. 10248 objectSigs = [ 10249 s 10250 for s in possibleSignatures 10251 if ( 10252 distinguishingType(s).isObject() 10253 or distinguishingType(s).isNonCallbackInterface() 10254 ) 10255 ] 10256 10257 # And all the overloads that take callbacks 10258 objectSigs.extend( 10259 s for s in possibleSignatures if distinguishingType(s).isCallback() 10260 ) 10261 10262 # And all the overloads that take sequences 10263 objectSigs.extend( 10264 s for s in possibleSignatures if distinguishingType(s).isSequence() 10265 ) 10266 10267 # Now append all the overloads that take a dictionary or callback 10268 # interface or record. There should be only one of these! 10269 genericObjectSigs = [ 10270 s 10271 for s in possibleSignatures 10272 if ( 10273 distinguishingType(s).isDictionary() 10274 or distinguishingType(s).isRecord() 10275 or distinguishingType(s).isCallbackInterface() 10276 ) 10277 ] 10278 assert len(genericObjectSigs) <= 1 10279 objectSigs.extend(genericObjectSigs) 10280 10281 # There might be more than one thing in objectSigs; we need to check 10282 # which ones we unwrap to. 10283 if len(objectSigs) > 0: 10284 # Here it's enough to guard on our argument being an object. 10285 # The code for unwrapping non-callback interfaces, spiderMonkey 10286 # interfaces, and sequences will just bail out and move 10287 # on to the next overload if the object fails to unwrap 10288 # correctly, while "object" accepts any object anyway. We 10289 # could even not do the isObject() check up front here, but in 10290 # cases where we have multiple object overloads it makes sense 10291 # to do it only once instead of for each overload. That will 10292 # also allow the unwrapping test to skip having to do codegen 10293 # for the null-or-undefined case, which we already handled 10294 # above. 10295 caseBody.append(CGGeneric("if (%s.isObject()) {\n" % distinguishingArg)) 10296 for sig in objectSigs: 10297 caseBody.append(CGIndenter(CGGeneric("do {\n"))) 10298 # Indent by 4, since we need to indent further 10299 # than our "do" statement 10300 tryCall(sig, 4, isDefinitelyObject=True) 10301 caseBody.append(CGIndenter(CGGeneric("} while (false);\n"))) 10302 10303 caseBody.append(CGGeneric("}\n")) 10304 10305 # Now we only have to consider booleans, numerics, and strings. If 10306 # we only have one of them, then we can just output it. But if not, 10307 # then we need to output some of the cases conditionally: if we have 10308 # a string overload, then boolean and numeric are conditional, and 10309 # if not then boolean is conditional if we have a numeric overload. 10310 def findUniqueSignature(filterLambda): 10311 sigs = [s for s in possibleSignatures if filterLambda(s)] 10312 assert len(sigs) < 2 10313 if len(sigs) > 0: 10314 return sigs[0] 10315 return None 10316 10317 stringSignature = findUniqueSignature( 10318 lambda s: ( 10319 distinguishingType(s).isString() or distinguishingType(s).isEnum() 10320 ) 10321 ) 10322 numericSignature = findUniqueSignature( 10323 lambda s: distinguishingType(s).isNumeric() 10324 ) 10325 booleanSignature = findUniqueSignature( 10326 lambda s: distinguishingType(s).isBoolean() 10327 ) 10328 10329 if stringSignature or numericSignature: 10330 booleanCondition = "%s.isBoolean()" 10331 else: 10332 booleanCondition = None 10333 10334 if stringSignature: 10335 numericCondition = "%s.isNumber()" 10336 else: 10337 numericCondition = None 10338 10339 def addCase(sig, condition): 10340 sigCode = getPerSignatureCall(sig, distinguishingIndex) 10341 if condition: 10342 sigCode = CGIfWrapper(sigCode, condition % distinguishingArg) 10343 caseBody.append(sigCode) 10344 10345 if booleanSignature: 10346 addCase(booleanSignature, booleanCondition) 10347 if numericSignature: 10348 addCase(numericSignature, numericCondition) 10349 if stringSignature: 10350 addCase(stringSignature, None) 10351 10352 if not booleanSignature and not numericSignature and not stringSignature: 10353 # Just throw; we have no idea what we're supposed to 10354 # do with this. 10355 caseBody.append( 10356 CGGeneric( 10357 'return cx.ThrowErrorMessage<MSG_OVERLOAD_RESOLUTION_FAILED>("%d", "%d");\n' 10358 % (distinguishingIndex + 1, argCount) 10359 ) 10360 ) 10361 10362 argCountCases.append(CGCase(str(argCount), CGList(caseBody))) 10363 10364 overloadCGThings = [] 10365 overloadCGThings.append( 10366 CGGeneric( 10367 "unsigned argcount = std::min(args.length(), %du);\n" % maxArgCount 10368 ) 10369 ) 10370 overloadCGThings.append( 10371 CGSwitch( 10372 "argcount", 10373 argCountCases, 10374 CGGeneric( 10375 dedent( 10376 """ 10377 // Using nsPrintfCString here would require including that 10378 // header. Let's not worry about it. 10379 nsAutoCString argCountStr; 10380 argCountStr.AppendPrintf("%u", args.length()); 10381 return cx.ThrowErrorMessage<MSG_INVALID_OVERLOAD_ARGCOUNT>(argCountStr.get()); 10382 """ 10383 ) 10384 ), 10385 ) 10386 ) 10387 overloadCGThings.append( 10388 CGGeneric( 10389 'MOZ_CRASH("We have an always-returning default case");\n' 10390 "return false;\n" 10391 ) 10392 ) 10393 self.cgRoot = CGList(overloadCGThings) 10394 10395 def define(self): 10396 return self.cgRoot.define() 10397 10398 10399 class CGGetterCall(CGPerSignatureCall): 10400 """ 10401 A class to generate a native object getter call for a particular IDL 10402 getter. 10403 """ 10404 10405 def __init__( 10406 self, 10407 returnType, 10408 nativeMethodName, 10409 descriptor, 10410 attr, 10411 errorReportingLabel=None, 10412 argsPre=[], 10413 dontSetSlot=False, 10414 extendedAttributes=None, 10415 preConversionCode=None, 10416 ): 10417 self.preConversionCode = preConversionCode 10418 if attr.getExtendedAttribute("UseCounter"): 10419 useCounterName = "%s_%s_getter" % ( 10420 descriptor.interface.identifier.name, 10421 attr.identifier.name, 10422 ) 10423 else: 10424 useCounterName = None 10425 if attr.isStatic(): 10426 nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName) 10427 CGPerSignatureCall.__init__( 10428 self, 10429 returnType, 10430 [], 10431 nativeMethodName, 10432 attr.isStatic(), 10433 descriptor, 10434 attr, 10435 getter=True, 10436 useCounterName=useCounterName, 10437 dontSetSlot=dontSetSlot, 10438 extendedAttributes=extendedAttributes, 10439 errorReportingLabel=errorReportingLabel, 10440 additionalArgsPre=argsPre, 10441 ) 10442 10443 def wrap_return_value(self): 10444 wrap = CGPerSignatureCall.wrap_return_value(self) 10445 if self.preConversionCode is not None: 10446 wrap = self.preConversionCode + wrap 10447 return wrap 10448 10449 10450 class FakeIdentifier: 10451 def __init__(self, name): 10452 self.name = name 10453 10454 10455 class FakeArgument: 10456 """ 10457 A class that quacks like an IDLArgument. This is used to make 10458 setters look like method calls or for special operations. 10459 """ 10460 10461 def __init__(self, type, name="arg", allowTreatNonCallableAsNull=False): 10462 self.type = type 10463 self.optional = False 10464 self.variadic = False 10465 self.defaultValue = None 10466 self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull 10467 10468 self.identifier = FakeIdentifier(name) 10469 10470 def allowTreatNonCallableAsNull(self): 10471 return self._allowTreatNonCallableAsNull 10472 10473 def canHaveMissingValue(self): 10474 return False 10475 10476 10477 class CGSetterCall(CGPerSignatureCall): 10478 """ 10479 A class to generate a native object setter call for a particular IDL 10480 setter. 10481 """ 10482 10483 def __init__( 10484 self, 10485 argType, 10486 nativeMethodName, 10487 descriptor, 10488 attr, 10489 errorReportingLabel=None, 10490 argsPre=[], 10491 ): 10492 if attr.getExtendedAttribute("UseCounter"): 10493 useCounterName = "%s_%s_setter" % ( 10494 descriptor.interface.identifier.name, 10495 attr.identifier.name, 10496 ) 10497 else: 10498 useCounterName = None 10499 if attr.isStatic(): 10500 nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName) 10501 CGPerSignatureCall.__init__( 10502 self, 10503 None, 10504 [FakeArgument(argType, allowTreatNonCallableAsNull=True)], 10505 nativeMethodName, 10506 attr.isStatic(), 10507 descriptor, 10508 attr, 10509 setter=True, 10510 useCounterName=useCounterName, 10511 errorReportingLabel=errorReportingLabel, 10512 additionalArgsPre=argsPre, 10513 ) 10514 10515 def wrap_return_value(self): 10516 attr = self.idlNode 10517 clearSlot = "" 10518 if self.descriptor.wrapperCache and attr.slotIndices is not None: 10519 if attr.getExtendedAttribute("StoreInSlot"): 10520 clearSlot = "%s(cx, self);\n" % MakeClearCachedValueNativeName( 10521 self.idlNode 10522 ) 10523 elif attr.getExtendedAttribute("Cached"): 10524 clearSlot = "%s(self);\n" % MakeClearCachedValueNativeName(self.idlNode) 10525 10526 # We have no return value 10527 return "\n" "%s" "return true;\n" % clearSlot 10528 10529 10530 class CGAbstractBindingMethod(CGAbstractStaticMethod): 10531 """ 10532 Common class to generate some of our class hooks. This will generate the 10533 function declaration, get a reference to the JS object for our binding 10534 object (which might be an argument of the class hook or something we get 10535 from a JS::CallArgs), and unwrap into the right C++ type. Subclasses are 10536 expected to override the generate_code function to do the rest of the work. 10537 This function should return a CGThing which is already properly indented. 10538 10539 getThisObj should be code for getting a JSObject* for the binding 10540 object. "" can be passed in if the binding object is already stored in 10541 'obj'. 10542 10543 callArgs should be code for getting a JS::CallArgs into a variable 10544 called 'args'. This can be "" if there is already such a variable 10545 around or if the body does not need a JS::CallArgs. 10546 10547 """ 10548 10549 def __init__( 10550 self, 10551 descriptor, 10552 name, 10553 args, 10554 getThisObj, 10555 callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n", 10556 ): 10557 CGAbstractStaticMethod.__init__( 10558 self, descriptor, name, "bool", args, canRunScript=True 10559 ) 10560 10561 # This can't ever happen, because we only use this for class hooks. 10562 self.unwrapFailureCode = fill( 10563 """ 10564 MOZ_CRASH("Unexpected object in '${name}' hook"); 10565 return false; 10566 """, 10567 name=name, 10568 ) 10569 10570 if getThisObj == "": 10571 self.getThisObj = None 10572 else: 10573 self.getThisObj = CGGeneric( 10574 "JS::Rooted<JSObject*> obj(cx, %s);\n" % getThisObj 10575 ) 10576 self.callArgs = callArgs 10577 10578 def definition_body(self): 10579 body = self.callArgs 10580 if self.getThisObj is not None: 10581 body += self.getThisObj.define() + "\n" 10582 body += "%s* self;\n" % self.descriptor.nativeType 10583 body += dedent( 10584 """ 10585 JS::Rooted<JS::Value> rootSelf(cx, JS::ObjectValue(*obj)); 10586 """ 10587 ) 10588 10589 body += str( 10590 CastableObjectUnwrapper( 10591 self.descriptor, "rootSelf", "&rootSelf", "self", self.unwrapFailureCode 10592 ) 10593 ) 10594 10595 return body + self.generate_code().define() 10596 10597 def generate_code(self): 10598 assert False # Override me 10599 10600 10601 class CGAbstractStaticBindingMethod(CGAbstractStaticMethod): 10602 """ 10603 Common class to generate the JSNatives for all our static methods, getters 10604 and setters. This will generate the function declaration and unwrap the 10605 global object. Subclasses are expected to override the generate_code 10606 function to do the rest of the work. This function should return a 10607 CGThing which is already properly indented. 10608 """ 10609 10610 def __init__(self, descriptor, name): 10611 CGAbstractStaticMethod.__init__( 10612 self, descriptor, name, "bool", JSNativeArguments(), canRunScript=True 10613 ) 10614 10615 def definition_body(self): 10616 # Make sure that "obj" is in the same compartment as "cx", since we'll 10617 # later use it to wrap return values. 10618 unwrap = dedent( 10619 """ 10620 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 10621 JS::Rooted<JSObject*> obj(cx, &args.callee()); 10622 10623 """ 10624 ) 10625 return unwrap + self.generate_code().define() 10626 10627 def generate_code(self): 10628 assert False # Override me 10629 10630 10631 def MakeNativeName(name): 10632 return name[0].upper() + IDLToCIdentifier(name[1:]) 10633 10634 10635 def GetWebExposedName(idlObject, descriptor): 10636 if idlObject == descriptor.operations["Stringifier"]: 10637 return "toString" 10638 name = idlObject.identifier.name 10639 if name == "__namedsetter": 10640 return "named setter" 10641 if name == "__namedgetter": 10642 return "named getter" 10643 if name == "__indexedsetter": 10644 return "indexed setter" 10645 if name == "__indexedgetter": 10646 return "indexed getter" 10647 if name == "__legacycaller": 10648 return "legacy caller" 10649 return name 10650 10651 10652 def GetConstructorNameForReporting(descriptor, ctor): 10653 # Figure out the name of our constructor for reporting purposes. 10654 # For unnamed webidl constructors, identifier.name is "constructor" but 10655 # the name JS sees is the interface name; for legacy factory functions 10656 # identifier.name is the actual name. 10657 ctorName = ctor.identifier.name 10658 if ctorName == "constructor": 10659 return descriptor.interface.identifier.name 10660 return ctorName 10661 10662 10663 def GetLabelForErrorReporting(descriptor, idlObject, isConstructor): 10664 """ 10665 descriptor is the descriptor for the interface involved 10666 10667 idlObject is the method (regular or static), attribute (regular or 10668 static), or constructor (named or not) involved. 10669 10670 isConstructor is true if idlObject is a constructor and false otherwise. 10671 """ 10672 if isConstructor: 10673 return "%s constructor" % GetConstructorNameForReporting(descriptor, idlObject) 10674 10675 namePrefix = descriptor.interface.identifier.name 10676 name = GetWebExposedName(idlObject, descriptor) 10677 if " " in name: 10678 # It's got a space already, so just space-separate. 10679 return "%s %s" % (namePrefix, name) 10680 10681 return "%s.%s" % (namePrefix, name) 10682 10683 10684 class CGSpecializedMethod(CGAbstractStaticMethod): 10685 """ 10686 A class for generating the C++ code for a specialized method that the JIT 10687 can call with lower overhead. 10688 """ 10689 10690 def __init__(self, descriptor, method): 10691 self.method = method 10692 name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name)) 10693 args = [ 10694 Argument("JSContext*", "cx"), 10695 Argument("JS::Handle<JSObject*>", "obj"), 10696 Argument("void*", "void_self"), 10697 Argument("const JSJitMethodCallArgs&", "args"), 10698 ] 10699 CGAbstractStaticMethod.__init__( 10700 self, descriptor, name, "bool", args, canRunScript=True 10701 ) 10702 10703 def definition_body(self): 10704 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method) 10705 call = CGMethodCall( 10706 nativeName, self.method.isStatic(), self.descriptor, self.method 10707 ).define() 10708 prefix = "" 10709 if self.method.getExtendedAttribute("CrossOriginCallable"): 10710 for signature in self.method.signatures(): 10711 # non-undefined signatures would require us to deal with remote proxies for the 10712 # return value here. 10713 if not signature[0].isUndefined(): 10714 raise TypeError( 10715 "We don't support a method marked as CrossOriginCallable " 10716 "with non-undefined return type" 10717 ) 10718 prototypeID, _ = PrototypeIDAndDepth(self.descriptor) 10719 prefix = fill( 10720 """ 10721 // CrossOriginThisPolicy::UnwrapThisObject stores a ${nativeType}::RemoteProxy in void_self 10722 // if obj is a proxy with a RemoteObjectProxy handler for the right type, or else it stores 10723 // a ${nativeType}. If we get here from the JIT (without going through UnwrapThisObject) we 10724 // know void_self contains a ${nativeType}; we don't have special cases in the JIT to deal 10725 // with remote object proxies. 10726 if (IsRemoteObjectProxy(obj, ${prototypeID})) { 10727 auto* self = static_cast<${nativeType}::RemoteProxy*>(void_self); 10728 $*{call} 10729 } 10730 """, 10731 prototypeID=prototypeID, 10732 nativeType=self.descriptor.nativeType, 10733 call=call, 10734 ) 10735 return prefix + fill( 10736 """ 10737 auto* self = static_cast<${nativeType}*>(void_self); 10738 $*{call} 10739 """, 10740 nativeType=self.descriptor.nativeType, 10741 call=call, 10742 ) 10743 10744 def auto_profiler_label(self): 10745 interface_name = self.descriptor.interface.identifier.name 10746 method_name = self.method.identifier.name 10747 return fill( 10748 """ 10749 AUTO_PROFILER_LABEL_DYNAMIC_FAST( 10750 "${interface_name}", "${method_name}", DOM, cx, 10751 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD) | 10752 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); 10753 """, 10754 interface_name=interface_name, 10755 method_name=method_name, 10756 ) 10757 10758 @staticmethod 10759 def should_have_method_description(descriptor, idlMethod): 10760 """ 10761 Returns whether the given IDL method (static, non-static, constructor) 10762 should have a method description declaration, for use in error 10763 reporting. 10764 """ 10765 # If a method has overloads, it needs a method description, because it 10766 # can throw MSG_INVALID_OVERLOAD_ARGCOUNT at the very least. 10767 if len(idlMethod.signatures()) != 1: 10768 return True 10769 10770 # Methods with only one signature need a method description if one of 10771 # their args needs it. 10772 sig = idlMethod.signatures()[0] 10773 args = sig[1] 10774 return any( 10775 idlTypeNeedsCallContext( 10776 arg.type, 10777 descriptor, 10778 allowTreatNonCallableAsNull=arg.allowTreatNonCallableAsNull(), 10779 ) 10780 for arg in args 10781 ) 10782 10783 @staticmethod 10784 def error_reporting_label_helper(descriptor, idlMethod, isConstructor): 10785 """ 10786 Returns the method description to use for error reporting for the given 10787 IDL method. Used to implement common error_reporting_label() functions 10788 across different classes. 10789 """ 10790 if not CGSpecializedMethod.should_have_method_description( 10791 descriptor, idlMethod 10792 ): 10793 return None 10794 return '"%s"' % GetLabelForErrorReporting(descriptor, idlMethod, isConstructor) 10795 10796 def error_reporting_label(self): 10797 return CGSpecializedMethod.error_reporting_label_helper( 10798 self.descriptor, self.method, isConstructor=False 10799 ) 10800 10801 @staticmethod 10802 def makeNativeName(descriptor, method): 10803 if method.underlyingAttr: 10804 return CGSpecializedGetterCommon.makeNativeName( 10805 descriptor, method.underlyingAttr 10806 ) 10807 name = method.identifier.name 10808 return MakeNativeName(descriptor.binaryNameFor(name, method.isStatic())) 10809 10810 10811 class CGMethodPromiseWrapper(CGAbstractStaticMethod): 10812 """ 10813 A class for generating a wrapper around another method that will 10814 convert exceptions to promises. 10815 """ 10816 10817 def __init__(self, descriptor, methodToWrap): 10818 self.method = methodToWrap 10819 name = self.makeName(methodToWrap.name) 10820 args = list(methodToWrap.args) 10821 CGAbstractStaticMethod.__init__( 10822 self, descriptor, name, "bool", args, canRunScript=True 10823 ) 10824 10825 def definition_body(self): 10826 return fill( 10827 """ 10828 bool ok = ${methodName}(${args}); 10829 if (ok) { 10830 return true; 10831 } 10832 return ConvertExceptionToPromise(cx, args.rval()); 10833 """, 10834 methodName=self.method.name, 10835 args=", ".join(arg.name for arg in self.args), 10836 ) 10837 10838 @staticmethod 10839 def makeName(methodName): 10840 return methodName + "_promiseWrapper" 10841 10842 10843 class CGDefaultToJSONMethod(CGSpecializedMethod): 10844 def __init__(self, descriptor, method): 10845 assert method.isDefaultToJSON() 10846 CGSpecializedMethod.__init__(self, descriptor, method) 10847 10848 def definition_body(self): 10849 ret = fill( 10850 """ 10851 auto* self = static_cast<${nativeType}*>(void_self); 10852 JS::Rooted<JSObject*> result(cx, JS_NewPlainObject(cx)); 10853 if (!result) { 10854 return false; 10855 } 10856 """, 10857 nativeType=self.descriptor.nativeType, 10858 ) 10859 10860 jsonDescriptors = [self.descriptor] 10861 interface = self.descriptor.interface.parent 10862 while interface: 10863 descriptor = self.descriptor.getDescriptor(interface.identifier.name) 10864 if descriptor.hasDefaultToJSON: 10865 jsonDescriptors.append(descriptor) 10866 interface = interface.parent 10867 10868 # Iterate the array in reverse: oldest ancestor first 10869 for descriptor in jsonDescriptors[::-1]: 10870 ret += fill( 10871 """ 10872 if (!${parentclass}::CollectJSONAttributes(cx, obj, MOZ_KnownLive(self), result)) { 10873 return false; 10874 } 10875 """, 10876 parentclass=toBindingNamespace(descriptor.name), 10877 ) 10878 ret += "args.rval().setObject(*result);\n" "return true;\n" 10879 return ret 10880 10881 10882 class CGLegacyCallHook(CGAbstractBindingMethod): 10883 """ 10884 Call hook for our object 10885 """ 10886 10887 def __init__(self, descriptor): 10888 self._legacycaller = descriptor.operations["LegacyCaller"] 10889 # Our "self" is actually the callee in this case, not the thisval. 10890 CGAbstractBindingMethod.__init__( 10891 self, 10892 descriptor, 10893 LEGACYCALLER_HOOK_NAME, 10894 JSNativeArguments(), 10895 getThisObj="&args.callee()", 10896 ) 10897 10898 def define(self): 10899 if not self._legacycaller: 10900 return "" 10901 return CGAbstractBindingMethod.define(self) 10902 10903 def generate_code(self): 10904 name = self._legacycaller.identifier.name 10905 nativeName = MakeNativeName(self.descriptor.binaryNameFor(name, False)) 10906 return CGMethodCall(nativeName, False, self.descriptor, self._legacycaller) 10907 10908 def error_reporting_label(self): 10909 # Should act like methods. 10910 return CGSpecializedMethod.error_reporting_label_helper( 10911 self.descriptor, self._legacycaller, isConstructor=False 10912 ) 10913 10914 10915 class CGResolveHook(CGAbstractClassHook): 10916 """ 10917 Resolve hook for objects that have the NeedResolve extended attribute. 10918 """ 10919 10920 def __init__(self, descriptor): 10921 assert descriptor.interface.getExtendedAttribute("NeedResolve") 10922 10923 args = [ 10924 Argument("JSContext*", "cx"), 10925 Argument("JS::Handle<JSObject*>", "obj"), 10926 Argument("JS::Handle<jsid>", "id"), 10927 Argument("bool*", "resolvedp"), 10928 ] 10929 CGAbstractClassHook.__init__(self, descriptor, RESOLVE_HOOK_NAME, "bool", args) 10930 10931 def generate_code(self): 10932 return dedent( 10933 """ 10934 JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc(cx); 10935 if (!self->DoResolve(cx, obj, id, &desc)) { 10936 return false; 10937 } 10938 if (desc.isNothing()) { 10939 return true; 10940 } 10941 // If desc.value() is undefined, then the DoResolve call 10942 // has already defined it on the object. Don't try to also 10943 // define it. 10944 MOZ_ASSERT(desc->isDataDescriptor()); 10945 if (!desc->value().isUndefined()) { 10946 JS::Rooted<JS::PropertyDescriptor> defineDesc(cx, *desc); 10947 defineDesc.setResolving(true); 10948 if (!JS_DefinePropertyById(cx, obj, id, defineDesc)) { 10949 return false; 10950 } 10951 } 10952 *resolvedp = true; 10953 return true; 10954 """ 10955 ) 10956 10957 def definition_body(self): 10958 if self.descriptor.isGlobal(): 10959 # Resolve standard classes 10960 prefix = dedent( 10961 """ 10962 if (!ResolveGlobal(cx, obj, id, resolvedp)) { 10963 return false; 10964 } 10965 if (*resolvedp) { 10966 return true; 10967 } 10968 10969 """ 10970 ) 10971 else: 10972 prefix = "" 10973 return prefix + CGAbstractClassHook.definition_body(self) 10974 10975 10976 class CGMayResolveHook(CGAbstractStaticMethod): 10977 """ 10978 Resolve hook for objects that have the NeedResolve extended attribute. 10979 """ 10980 10981 def __init__(self, descriptor): 10982 assert descriptor.interface.getExtendedAttribute("NeedResolve") 10983 10984 args = [ 10985 Argument("const JSAtomState&", "names"), 10986 Argument("jsid", "id"), 10987 Argument("JSObject*", "maybeObj"), 10988 ] 10989 CGAbstractStaticMethod.__init__( 10990 self, descriptor, MAY_RESOLVE_HOOK_NAME, "bool", args 10991 ) 10992 10993 def definition_body(self): 10994 if self.descriptor.isGlobal(): 10995 # Check whether this would resolve as a standard class. 10996 prefix = dedent( 10997 """ 10998 if (MayResolveGlobal(names, id, maybeObj)) { 10999 return true; 11000 } 11001 11002 """ 11003 ) 11004 else: 11005 prefix = "" 11006 return prefix + "return %s::MayResolve(id);\n" % self.descriptor.nativeType 11007 11008 11009 class CGEnumerateHook(CGAbstractBindingMethod): 11010 """ 11011 Enumerate hook for objects with custom hooks. 11012 """ 11013 11014 def __init__(self, descriptor): 11015 assert descriptor.interface.getExtendedAttribute("NeedResolve") 11016 11017 args = [ 11018 Argument("JSContext*", "cx"), 11019 Argument("JS::Handle<JSObject*>", "obj"), 11020 Argument("JS::MutableHandleVector<jsid>", "properties"), 11021 Argument("bool", "enumerableOnly"), 11022 ] 11023 # Our "self" is actually the "obj" argument in this case, not the thisval. 11024 CGAbstractBindingMethod.__init__( 11025 self, descriptor, NEW_ENUMERATE_HOOK_NAME, args, getThisObj="", callArgs="" 11026 ) 11027 11028 def generate_code(self): 11029 return CGGeneric( 11030 dedent( 11031 """ 11032 FastErrorResult rv; 11033 self->GetOwnPropertyNames(cx, properties, enumerableOnly, rv); 11034 if (rv.MaybeSetPendingException(cx)) { 11035 return false; 11036 } 11037 return true; 11038 """ 11039 ) 11040 ) 11041 11042 def definition_body(self): 11043 if self.descriptor.isGlobal(): 11044 # Enumerate standard classes 11045 prefix = dedent( 11046 """ 11047 if (!EnumerateGlobal(cx, obj, properties, enumerableOnly)) { 11048 return false; 11049 } 11050 11051 """ 11052 ) 11053 else: 11054 prefix = "" 11055 return prefix + CGAbstractBindingMethod.definition_body(self) 11056 11057 11058 class CppKeywords: 11059 """ 11060 A class for checking if method names declared in webidl 11061 are not in conflict with C++ keywords. 11062 """ 11063 11064 keywords = frozenset( 11065 [ 11066 "alignas", 11067 "alignof", 11068 "and", 11069 "and_eq", 11070 "asm", 11071 "assert", 11072 "auto", 11073 "bitand", 11074 "bitor", 11075 "bool", 11076 "break", 11077 "case", 11078 "catch", 11079 "char", 11080 "char16_t", 11081 "char32_t", 11082 "class", 11083 "compl", 11084 "const", 11085 "constexpr", 11086 "const_cast", 11087 "continue", 11088 "decltype", 11089 "default", 11090 "delete", 11091 "do", 11092 "double", 11093 "dynamic_cast", 11094 "else", 11095 "enum", 11096 "explicit", 11097 "export", 11098 "extern", 11099 "false", 11100 "final", 11101 "float", 11102 "for", 11103 "friend", 11104 "goto", 11105 "if", 11106 "inline", 11107 "int", 11108 "long", 11109 "mutable", 11110 "namespace", 11111 "new", 11112 "noexcept", 11113 "not", 11114 "not_eq", 11115 "nullptr", 11116 "operator", 11117 "or", 11118 "or_eq", 11119 "override", 11120 "private", 11121 "protected", 11122 "public", 11123 "register", 11124 "reinterpret_cast", 11125 "return", 11126 "short", 11127 "signed", 11128 "sizeof", 11129 "static", 11130 "static_assert", 11131 "static_cast", 11132 "struct", 11133 "switch", 11134 "template", 11135 "this", 11136 "thread_local", 11137 "throw", 11138 "true", 11139 "try", 11140 "typedef", 11141 "typeid", 11142 "typename", 11143 "union", 11144 "unsigned", 11145 "using", 11146 "virtual", 11147 "void", 11148 "volatile", 11149 "wchar_t", 11150 "while", 11151 "xor", 11152 "xor_eq", 11153 ] 11154 ) 11155 11156 @staticmethod 11157 def checkMethodName(name): 11158 # Double '_' because 'assert' and '_assert' cannot be used in MS2013 compiler. 11159 # Bug 964892 and bug 963560. 11160 if name in CppKeywords.keywords: 11161 name = "_" + name + "_" 11162 return name 11163 11164 11165 class CGStaticMethod(CGAbstractStaticBindingMethod): 11166 """ 11167 A class for generating the C++ code for an IDL static method. 11168 """ 11169 11170 def __init__(self, descriptor, method): 11171 self.method = method 11172 name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name)) 11173 CGAbstractStaticBindingMethod.__init__(self, descriptor, name) 11174 11175 def generate_code(self): 11176 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method) 11177 return CGMethodCall(nativeName, True, self.descriptor, self.method) 11178 11179 def auto_profiler_label(self): 11180 interface_name = self.descriptor.interface.identifier.name 11181 method_name = self.method.identifier.name 11182 return fill( 11183 """ 11184 AUTO_PROFILER_LABEL_DYNAMIC_FAST( 11185 "${interface_name}", "${method_name}", DOM, cx, 11186 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD) | 11187 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); 11188 """, 11189 interface_name=interface_name, 11190 method_name=method_name, 11191 ) 11192 11193 def error_reporting_label(self): 11194 return CGSpecializedMethod.error_reporting_label_helper( 11195 self.descriptor, self.method, isConstructor=False 11196 ) 11197 11198 11199 class CGSpecializedGetterCommon(CGAbstractStaticMethod): 11200 """ 11201 A class for generating the code for a specialized attribute getter 11202 that the JIT can call with lower overhead. 11203 """ 11204 11205 def __init__( 11206 self, 11207 descriptor, 11208 name, 11209 nativeName, 11210 attr, 11211 args, 11212 errorReportingLabel=None, 11213 additionalArg=None, 11214 ): 11215 self.nativeName = nativeName 11216 self.errorReportingLabel = errorReportingLabel 11217 self.additionalArgs = [] if additionalArg is None else [additionalArg] 11218 # StoreInSlot attributes have their getters called from Wrap(). We 11219 # really hope they can't run script, and don't want to annotate Wrap() 11220 # methods as doing that anyway, so let's not annotate them as 11221 # MOZ_CAN_RUN_SCRIPT. 11222 CGAbstractStaticMethod.__init__( 11223 self, 11224 descriptor, 11225 name, 11226 "bool", 11227 args + self.additionalArgs, 11228 canRunScript=not attr.getExtendedAttribute("StoreInSlot"), 11229 ) 11230 11231 def definition_body(self): 11232 prefix = fill( 11233 """ 11234 auto* self = static_cast<${nativeType}*>(void_self); 11235 """, 11236 nativeType=self.descriptor.nativeType, 11237 ) 11238 11239 if self.attr.isMaplikeOrSetlikeAttr(): 11240 assert not self.attr.getExtendedAttribute("CrossOriginReadable") 11241 # If the interface is maplike/setlike, there will be one getter 11242 # method for the size property of the backing object. Due to having 11243 # to unpack the backing object from the slot, this requires its own 11244 # generator. 11245 return prefix + getMaplikeOrSetlikeSizeGetterBody( 11246 self.descriptor, self.attr 11247 ) 11248 11249 if self.attr.type.isObservableArray(): 11250 assert not self.attr.getExtendedAttribute("CrossOriginReadable") 11251 # If the attribute is observableArray, due to having to unpack the 11252 # backing object from the slot, this requires its own generator. 11253 return prefix + getObservableArrayGetterBody(self.descriptor, self.attr) 11254 11255 if self.nativeName is None: 11256 nativeName = CGSpecializedGetterCommon.makeNativeName( 11257 self.descriptor, self.attr 11258 ) 11259 else: 11260 nativeName = self.nativeName 11261 11262 type = self.attr.type 11263 if self.attr.getExtendedAttribute("CrossOriginReadable"): 11264 remoteType = type 11265 extendedAttributes = self.descriptor.getExtendedAttributes( 11266 self.attr, getter=True 11267 ) 11268 if ( 11269 remoteType.isGeckoInterface() 11270 and not remoteType.unroll().inner.isExternal() 11271 and remoteType.unroll().inner.getExtendedAttribute("ChromeOnly") is None 11272 ): 11273 # We'll use a JSObject. It might make more sense to use remoteType's 11274 # RemoteProxy, but it's not easy to construct a type for that from here. 11275 remoteType = BuiltinTypes[IDLBuiltinType.Types.object] 11276 if "needsErrorResult" not in extendedAttributes: 11277 extendedAttributes.append("needsErrorResult") 11278 prototypeID, _ = PrototypeIDAndDepth(self.descriptor) 11279 prefix = ( 11280 fill( 11281 """ 11282 if (IsRemoteObjectProxy(obj, ${prototypeID})) { 11283 ${nativeType}::RemoteProxy* self = static_cast<${nativeType}::RemoteProxy*>(void_self); 11284 $*{call} 11285 } 11286 """, 11287 prototypeID=prototypeID, 11288 nativeType=self.descriptor.nativeType, 11289 call=CGGetterCall( 11290 remoteType, 11291 nativeName, 11292 self.descriptor, 11293 self.attr, 11294 self.errorReportingLabel, 11295 argsPre=[a.name for a in self.additionalArgs], 11296 dontSetSlot=True, 11297 extendedAttributes=extendedAttributes, 11298 ).define(), 11299 ) 11300 + prefix 11301 ) 11302 11303 argsPre = [a.name for a in self.additionalArgs] 11304 maybeReturnCachedVal = None 11305 if self.attr.slotIndices is not None: 11306 # We're going to store this return value in a slot on some object, 11307 # to cache it. The question is, which object? For dictionary and 11308 # sequence return values, we want to use a slot on the Xray expando 11309 # if we're called via Xrays, and a slot on our reflector otherwise. 11310 # On the other hand, when dealing with some interface types 11311 # (e.g. window.document) we want to avoid calling the getter more 11312 # than once. In the case of window.document, it's because the 11313 # getter can start returning null, which would get hidden in the 11314 # non-Xray case by the fact that it's [StoreOnSlot], so the cached 11315 # version is always around. 11316 # 11317 # The upshot is that we use the reflector slot for any getter whose 11318 # type is a gecko interface, whether we're called via Xrays or not. 11319 # Since [Cached] and [StoreInSlot] cannot be used with "NewObject", 11320 # we know that in the interface type case the returned object is 11321 # wrappercached. So creating Xrays to it is reasonable. 11322 if mayUseXrayExpandoSlots(self.descriptor, self.attr): 11323 prefix += dedent( 11324 """ 11325 // Have to either root across the getter call or reget after. 11326 bool isXray; 11327 JS::Rooted<JSObject*> slotStorage(cx, GetCachedSlotStorageObject(cx, obj, &isXray)); 11328 if (!slotStorage) { 11329 return false; 11330 } 11331 """ 11332 ) 11333 else: 11334 prefix += dedent( 11335 """ 11336 // Have to either root across the getter call or reget after. 11337 JS::Rooted<JSObject*> slotStorage(cx, js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false)); 11338 MOZ_ASSERT(IsDOMObject(slotStorage)); 11339 """ 11340 ) 11341 11342 if self.attr.getExtendedAttribute( 11343 "ReflectedHTMLAttributeReturningFrozenArray" 11344 ): 11345 argsPre.append("hasCachedValue ? &useCachedValue : nullptr") 11346 isXray = ( 11347 "isXray" 11348 if mayUseXrayExpandoSlots(self.descriptor, self.attr) 11349 else "false" 11350 ) 11351 prefix += fill( 11352 """ 11353 auto& array = ReflectedHTMLAttributeSlots::GetOrCreate(slotStorage, ${isXray}); 11354 JS::Rooted<JS::Value> cachedVal(cx, array[${arrayIndex}]); 11355 bool hasCachedValue = !cachedVal.isUndefined(); 11356 bool useCachedValue = false; 11357 """, 11358 isXray=isXray, 11359 arrayIndex=reflectedHTMLAttributesArrayIndex( 11360 self.descriptor, self.attr 11361 ), 11362 ) 11363 maybeReturnCachedVal = fill( 11364 """ 11365 MOZ_ASSERT_IF(useCachedValue, hasCachedValue); 11366 if (hasCachedValue && useCachedValue) { 11367 args.rval().set(cachedVal); 11368 // The cached value is in the compartment of slotStorage, 11369 // so wrap into the caller compartment as needed. 11370 return ${maybeWrap}(cx, args.rval()); 11371 } 11372 11373 ${clearCachedValue}(self); 11374 11375 """, 11376 maybeWrap=getMaybeWrapValueFuncForType(self.attr.type), 11377 clearCachedValue=MakeClearCachedValueNativeName(self.attr), 11378 ) 11379 else: 11380 if mayUseXrayExpandoSlots(self.descriptor, self.attr): 11381 prefix += fill( 11382 """ 11383 const size_t slotIndex = isXray ? ${xraySlotIndex} : ${slotIndex}; 11384 """, 11385 xraySlotIndex=memberXrayExpandoReservedSlot( 11386 self.attr, self.descriptor 11387 ), 11388 slotIndex=memberReservedSlot(self.attr, self.descriptor), 11389 ) 11390 else: 11391 prefix += fill( 11392 """ 11393 const size_t slotIndex = ${slotIndex}; 11394 """, 11395 slotIndex=memberReservedSlot(self.attr, self.descriptor), 11396 ) 11397 prefix += fill( 11398 """ 11399 MOZ_ASSERT(slotIndex < JSCLASS_RESERVED_SLOTS(JS::GetClass(slotStorage))); 11400 { 11401 // Scope for cachedVal 11402 JS::Value cachedVal = JS::GetReservedSlot(slotStorage, slotIndex); 11403 if (!cachedVal.isUndefined()) { 11404 args.rval().set(cachedVal); 11405 // The cached value is in the compartment of slotStorage, 11406 // so wrap into the caller compartment as needed. 11407 return ${maybeWrap}(cx, args.rval()); 11408 } 11409 } 11410 11411 """, 11412 maybeWrap=getMaybeWrapValueFuncForType(self.attr.type), 11413 ) 11414 11415 return ( 11416 prefix 11417 + CGGetterCall( 11418 type, 11419 nativeName, 11420 self.descriptor, 11421 self.attr, 11422 self.errorReportingLabel, 11423 argsPre=argsPre, 11424 preConversionCode=maybeReturnCachedVal, 11425 ).define() 11426 ) 11427 11428 def auto_profiler_label(self, profilerLabel=None): 11429 if profilerLabel is None: 11430 profilerLabel = '"' + self.attr.identifier.name + '"' 11431 interface_name = self.descriptor.interface.identifier.name 11432 return fill( 11433 """ 11434 AUTO_PROFILER_LABEL_DYNAMIC_FAST( 11435 "${interface_name}", ${attr_name}, DOM, cx, 11436 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) | 11437 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); 11438 """, 11439 interface_name=interface_name, 11440 attr_name=profilerLabel, 11441 ) 11442 11443 def error_reporting_label(self): 11444 # Getters never need a BindingCallContext. 11445 return None 11446 11447 @staticmethod 11448 def makeNativeName(descriptor, attr): 11449 name = attr.identifier.name 11450 nativeName = MakeNativeName(descriptor.binaryNameFor(name, attr.isStatic())) 11451 _, resultOutParam, _, _, _ = getRetvalDeclarationForType(attr.type, descriptor) 11452 extendedAttrs = descriptor.getExtendedAttributes(attr, getter=True) 11453 canFail = "needsErrorResult" in extendedAttrs or "canOOM" in extendedAttrs 11454 if resultOutParam or attr.type.nullable() or canFail: 11455 nativeName = "Get" + nativeName 11456 return nativeName 11457 11458 11459 class CGSpecializedGetter(CGSpecializedGetterCommon): 11460 """ 11461 A class for generating the code for a specialized attribute getter 11462 that the JIT can call with lower overhead. 11463 """ 11464 11465 def __init__(self, descriptor, attr): 11466 self.attr = attr 11467 name = "get_" + IDLToCIdentifier(attr.identifier.name) 11468 args = [ 11469 Argument("JSContext*", "cx"), 11470 Argument("JS::Handle<JSObject*>", "obj"), 11471 Argument("void*", "void_self"), 11472 Argument("JSJitGetterCallArgs", "args"), 11473 ] 11474 CGSpecializedGetterCommon.__init__(self, descriptor, name, None, attr, args) 11475 11476 11477 class CGTemplateForSpecializedGetter(CGSpecializedGetterCommon): 11478 """ 11479 A class for generating the code for a specialized attribute getter 11480 that can be used as the common getter that templated attribute 11481 getters can forward to. 11482 """ 11483 11484 def __init__(self, descriptor, template): 11485 self.attr = template.attr 11486 self.attrNameString = template.attrNameString 11487 args = [ 11488 Argument("JSContext*", "cx"), 11489 Argument("JS::Handle<JSObject*>", "obj"), 11490 Argument("void*", "void_self"), 11491 Argument("JSJitGetterCallArgs", "args"), 11492 ] 11493 errorDescription = ( 11494 'ErrorDescriptionFor<ErrorFor::getter>{ "%s", attrName }' 11495 % descriptor.interface.identifier.name 11496 ) 11497 CGSpecializedGetterCommon.__init__( 11498 self, 11499 descriptor, 11500 template.getter, 11501 template.getter, 11502 self.attr, 11503 args, 11504 errorReportingLabel=errorDescription, 11505 additionalArg=Argument(template.argument.type, template.argument.name), 11506 ) 11507 11508 def auto_profiler_label(self): 11509 return ( 11510 fill( 11511 """ 11512 const char* attrName = ${attrNameString}; 11513 """, 11514 attrNameString=self.attrNameString, 11515 ) 11516 + CGSpecializedGetterCommon.auto_profiler_label(self, "attrName") 11517 ) 11518 11519 11520 class CGSpecializedTemplatedGetter(CGAbstractStaticMethod): 11521 """ 11522 A class for generating the code for a specialized templated attribute 11523 getter that forwards to a common template getter. 11524 """ 11525 11526 def __init__(self, descriptor, attr, template, additionalArg): 11527 self.attr = attr 11528 self.template = template 11529 self.additionalArg = additionalArg 11530 name = "get_" + IDLToCIdentifier(attr.identifier.name) 11531 args = [ 11532 Argument("JSContext*", "cx"), 11533 Argument("JS::Handle<JSObject*>", "obj"), 11534 Argument("void*", "void_self"), 11535 Argument("JSJitGetterCallArgs", "args"), 11536 ] 11537 assert not attr.getExtendedAttribute("StoreInSlot") 11538 CGAbstractStaticMethod.__init__( 11539 self, 11540 descriptor, 11541 name, 11542 "bool", 11543 args, 11544 canRunScript=True, 11545 ) 11546 11547 def definition_body(self): 11548 if self.additionalArg is None: 11549 additionalArg = self.attr.identifier.name 11550 else: 11551 additionalArg = self.additionalArg 11552 11553 return fill( 11554 """ 11555 return ${namespace}::${getter}(cx, obj, void_self, args, ${additionalArg}); 11556 """, 11557 namespace=toBindingNamespace( 11558 self.template.descriptor.interface.identifier.name 11559 ), 11560 getter=self.template.getter, 11561 additionalArg=additionalArg, 11562 ) 11563 11564 11565 class CGGetterPromiseWrapper(CGAbstractStaticMethod): 11566 """ 11567 A class for generating a wrapper around another getter that will 11568 convert exceptions to promises. 11569 """ 11570 11571 def __init__(self, descriptor, getterToWrap): 11572 self.getter = getterToWrap 11573 name = self.makeName(getterToWrap.name) 11574 args = list(getterToWrap.args) 11575 CGAbstractStaticMethod.__init__( 11576 self, descriptor, name, "bool", args, canRunScript=True 11577 ) 11578 11579 def definition_body(self): 11580 return fill( 11581 """ 11582 bool ok = ${getterName}(${args}); 11583 if (ok) { 11584 return true; 11585 } 11586 return ConvertExceptionToPromise(cx, args.rval()); 11587 """, 11588 getterName=self.getter.name, 11589 args=", ".join(arg.name for arg in self.args), 11590 ) 11591 11592 @staticmethod 11593 def makeName(getterName): 11594 return getterName + "_promiseWrapper" 11595 11596 11597 class CGStaticGetter(CGAbstractStaticBindingMethod): 11598 """ 11599 A class for generating the C++ code for an IDL static attribute getter. 11600 """ 11601 11602 def __init__(self, descriptor, attr): 11603 self.attr = attr 11604 name = "get_" + IDLToCIdentifier(attr.identifier.name) 11605 CGAbstractStaticBindingMethod.__init__(self, descriptor, name) 11606 11607 def generate_code(self): 11608 nativeName = CGSpecializedGetterCommon.makeNativeName( 11609 self.descriptor, self.attr 11610 ) 11611 return CGGetterCall(self.attr.type, nativeName, self.descriptor, self.attr) 11612 11613 def auto_profiler_label(self): 11614 interface_name = self.descriptor.interface.identifier.name 11615 attr_name = self.attr.identifier.name 11616 return fill( 11617 """ 11618 AUTO_PROFILER_LABEL_DYNAMIC_FAST( 11619 "${interface_name}", "${attr_name}", DOM, cx, 11620 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) | 11621 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); 11622 """, 11623 interface_name=interface_name, 11624 attr_name=attr_name, 11625 ) 11626 11627 def error_reporting_label(self): 11628 # Getters never need a BindingCallContext. 11629 return None 11630 11631 11632 class CGSpecializedSetterCommon(CGAbstractStaticMethod): 11633 """ 11634 A class for generating the code for a specialized attribute setter 11635 that the JIT can call with lower overhead. 11636 """ 11637 11638 def __init__( 11639 self, 11640 descriptor, 11641 name, 11642 nativeName, 11643 attr, 11644 args, 11645 errorReportingLabel=None, 11646 additionalArg=None, 11647 ): 11648 self.nativeName = nativeName 11649 self.errorReportingLabel = errorReportingLabel 11650 self.additionalArgs = [] if additionalArg is None else [additionalArg] 11651 CGAbstractStaticMethod.__init__( 11652 self, 11653 descriptor, 11654 name, 11655 "bool", 11656 args + self.additionalArgs, 11657 canRunScript=True, 11658 ) 11659 11660 def definition_body(self): 11661 type = self.attr.type 11662 call = CGSetterCall( 11663 type, 11664 self.nativeName, 11665 self.descriptor, 11666 self.attr, 11667 self.errorReportingLabel, 11668 [a.name for a in self.additionalArgs], 11669 ).define() 11670 prefix = "" 11671 if self.attr.getExtendedAttribute("CrossOriginWritable"): 11672 if type.isGeckoInterface() and not type.unroll().inner.isExternal(): 11673 # a setter taking a Gecko interface would require us to deal with remote 11674 # proxies for the value here. 11675 raise TypeError( 11676 "We don't support the setter of %s marked as " 11677 "CrossOriginWritable because it takes a Gecko interface " 11678 "as the value", 11679 self.attr.identifier.name, 11680 ) 11681 prototypeID, _ = PrototypeIDAndDepth(self.descriptor) 11682 prefix = fill( 11683 """ 11684 if (IsRemoteObjectProxy(obj, ${prototypeID})) { 11685 auto* self = static_cast<${nativeType}::RemoteProxy*>(void_self); 11686 $*{call} 11687 } 11688 """, 11689 prototypeID=prototypeID, 11690 nativeType=self.descriptor.nativeType, 11691 call=call, 11692 ) 11693 11694 return prefix + fill( 11695 """ 11696 auto* self = static_cast<${nativeType}*>(void_self); 11697 $*{call} 11698 """, 11699 nativeType=self.descriptor.nativeType, 11700 call=call, 11701 ) 11702 11703 def auto_profiler_label(self, profilerLabel=None): 11704 interface_name = self.descriptor.interface.identifier.name 11705 if profilerLabel is None: 11706 profilerLabel = '"' + self.attr.identifier.name + '"' 11707 return fill( 11708 """ 11709 AUTO_PROFILER_LABEL_DYNAMIC_FAST( 11710 "${interface_name}", ${attr_name}, DOM, cx, 11711 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER) | 11712 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); 11713 """, 11714 interface_name=interface_name, 11715 attr_name=profilerLabel, 11716 ) 11717 11718 @staticmethod 11719 def error_reporting_label_helper(descriptor, attr): 11720 # Setters need a BindingCallContext if the type of the attribute needs 11721 # one. 11722 if not idlTypeNeedsCallContext( 11723 attr.type, descriptor, allowTreatNonCallableAsNull=True 11724 ): 11725 return None 11726 return '"%s"' % ( 11727 GetLabelForErrorReporting(descriptor, attr, isConstructor=False) + " setter" 11728 ) 11729 11730 def error_reporting_label(self): 11731 errorReportingLabel = CGSpecializedSetterCommon.error_reporting_label_helper( 11732 self.descriptor, self.attr 11733 ) 11734 if errorReportingLabel is None: 11735 return None 11736 if self.errorReportingLabel: 11737 return self.errorReportingLabel 11738 return errorReportingLabel 11739 11740 @staticmethod 11741 def makeNativeName(descriptor, attr): 11742 name = attr.identifier.name 11743 return "Set" + MakeNativeName(descriptor.binaryNameFor(name, attr.isStatic())) 11744 11745 11746 class CGSpecializedSetter(CGSpecializedSetterCommon): 11747 """ 11748 A class for generating the code for a specialized attribute setter 11749 that the JIT can call with lower overhead. 11750 """ 11751 11752 def __init__(self, descriptor, attr): 11753 self.attr = attr 11754 name = "set_" + IDLToCIdentifier(attr.identifier.name) 11755 args = [ 11756 Argument("JSContext*", "cx"), 11757 Argument("JS::Handle<JSObject*>", "obj"), 11758 Argument("void*", "void_self"), 11759 Argument("JSJitSetterCallArgs", "args"), 11760 ] 11761 CGSpecializedSetterCommon.__init__( 11762 self, 11763 descriptor, 11764 name, 11765 CGSpecializedSetterCommon.makeNativeName(descriptor, attr), 11766 attr, 11767 args, 11768 ) 11769 11770 11771 class CGTemplateForSpecializedSetter(CGSpecializedSetterCommon): 11772 """ 11773 A class for generating the code for a specialized attribute setter 11774 that can be used as the common setter that templated attribute 11775 setters can forward to. 11776 """ 11777 11778 def __init__(self, descriptor, template): 11779 self.attr = template.attr 11780 self.attrNameString = template.attrNameString 11781 args = [ 11782 Argument("JSContext*", "cx"), 11783 Argument("JS::Handle<JSObject*>", "obj"), 11784 Argument("void*", "void_self"), 11785 Argument("JSJitSetterCallArgs", "args"), 11786 ] 11787 errorDescription = ( 11788 'ErrorDescriptionFor<ErrorFor::setter>{ "%s", attrName }' 11789 % descriptor.interface.identifier.name 11790 ) 11791 CGSpecializedSetterCommon.__init__( 11792 self, 11793 descriptor, 11794 template.setter, 11795 template.setter, 11796 self.attr, 11797 args, 11798 errorReportingLabel=errorDescription, 11799 additionalArg=Argument(template.argument.type, template.argument.name), 11800 ) 11801 11802 def auto_profiler_label(self): 11803 return ( 11804 fill( 11805 """ 11806 const char* attrName = ${attrNameString}; 11807 """, 11808 attrNameString=self.attrNameString, 11809 ) 11810 + CGSpecializedSetterCommon.auto_profiler_label(self, "attrName") 11811 ) 11812 11813 11814 class CGSpecializedTemplatedSetter(CGAbstractStaticMethod): 11815 """ 11816 A class for generating the code for a specialized templated attribute 11817 setter that forwards to a common template setter. 11818 """ 11819 11820 def __init__(self, descriptor, attr, template, additionalArg): 11821 self.attr = attr 11822 self.template = template 11823 self.additionalArg = additionalArg 11824 name = "set_" + IDLToCIdentifier(attr.identifier.name) 11825 args = [ 11826 Argument("JSContext*", "cx"), 11827 Argument("JS::Handle<JSObject*>", "obj"), 11828 Argument("void*", "void_self"), 11829 Argument("JSJitSetterCallArgs", "args"), 11830 ] 11831 CGAbstractStaticMethod.__init__( 11832 self, descriptor, name, "bool", args, canRunScript=True 11833 ) 11834 11835 def definition_body(self): 11836 additionalArgs = [] 11837 if self.additionalArg is None: 11838 additionalArgs.append(self.attr.identifier.name) 11839 else: 11840 additionalArgs.append(self.additionalArg) 11841 11842 return fill( 11843 """ 11844 return ${namespace}::${setter}(cx, obj, void_self, args, ${additionalArgs}); 11845 """, 11846 namespace=toBindingNamespace( 11847 self.template.descriptor.interface.identifier.name 11848 ), 11849 setter=self.template.setter, 11850 additionalArgs=", ".join(additionalArgs), 11851 ) 11852 11853 11854 class CGStaticSetter(CGAbstractStaticBindingMethod): 11855 """ 11856 A class for generating the C++ code for an IDL static attribute setter. 11857 """ 11858 11859 def __init__(self, descriptor, attr): 11860 self.attr = attr 11861 name = "set_" + IDLToCIdentifier(attr.identifier.name) 11862 CGAbstractStaticBindingMethod.__init__(self, descriptor, name) 11863 11864 def generate_code(self): 11865 nativeName = CGSpecializedSetterCommon.makeNativeName( 11866 self.descriptor, self.attr 11867 ) 11868 checkForArg = CGGeneric( 11869 fill( 11870 """ 11871 if (!args.requireAtLeast(cx, "${name} setter", 1)) { 11872 return false; 11873 } 11874 """, 11875 name=self.attr.identifier.name, 11876 ) 11877 ) 11878 call = CGSetterCall(self.attr.type, nativeName, self.descriptor, self.attr) 11879 return CGList([checkForArg, call]) 11880 11881 def auto_profiler_label(self): 11882 interface_name = self.descriptor.interface.identifier.name 11883 attr_name = self.attr.identifier.name 11884 return fill( 11885 """ 11886 AUTO_PROFILER_LABEL_DYNAMIC_FAST( 11887 "${interface_name}", "${attr_name}", DOM, cx, 11888 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER) | 11889 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); 11890 """, 11891 interface_name=interface_name, 11892 attr_name=attr_name, 11893 ) 11894 11895 def error_reporting_label(self): 11896 return CGSpecializedSetterCommon.error_reporting_label_helper( 11897 self.descriptor, self.attr 11898 ) 11899 11900 11901 class CGSpecializedForwardingSetter(CGSpecializedSetter): 11902 """ 11903 A class for generating the code for a specialized attribute setter with 11904 PutForwards that the JIT can call with lower overhead. 11905 """ 11906 11907 def __init__(self, descriptor, attr): 11908 CGSpecializedSetter.__init__(self, descriptor, attr) 11909 11910 def definition_body(self): 11911 attrName = self.attr.identifier.name 11912 forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0] 11913 # JS_GetProperty and JS_SetProperty can only deal with ASCII 11914 assert all(ord(c) < 128 for c in attrName) 11915 assert all(ord(c) < 128 for c in forwardToAttrName) 11916 return fill( 11917 """ 11918 JS::Rooted<JS::Value> v(cx); 11919 if (!JS_GetProperty(cx, obj, "${attr}", &v)) { 11920 return false; 11921 } 11922 11923 if (!v.isObject()) { 11924 return cx.ThrowErrorMessage<MSG_NOT_OBJECT>("${interface}.${attr}"); 11925 } 11926 11927 JS::Rooted<JSObject*> targetObj(cx, &v.toObject()); 11928 return JS_SetProperty(cx, targetObj, "${forwardToAttrName}", args[0]); 11929 """, 11930 attr=attrName, 11931 interface=self.descriptor.interface.identifier.name, 11932 forwardToAttrName=forwardToAttrName, 11933 ) 11934 11935 def error_reporting_label(self): 11936 # We always need to be able to throw. 11937 return '"%s"' % ( 11938 GetLabelForErrorReporting(self.descriptor, self.attr, isConstructor=False) 11939 + " setter" 11940 ) 11941 11942 11943 class CGSpecializedReplaceableSetter(CGSpecializedSetter): 11944 """ 11945 A class for generating the code for a specialized attribute setter with 11946 Replaceable that the JIT can call with lower overhead. 11947 """ 11948 11949 def __init__(self, descriptor, attr): 11950 CGSpecializedSetter.__init__(self, descriptor, attr) 11951 11952 def definition_body(self): 11953 attrName = self.attr.identifier.name 11954 # JS_DefineProperty can only deal with ASCII 11955 assert all(ord(c) < 128 for c in attrName) 11956 return ( 11957 'return JS_DefineProperty(cx, obj, "%s", args[0], JSPROP_ENUMERATE);\n' 11958 % attrName 11959 ) 11960 11961 def error_reporting_label(self): 11962 # We never throw directly. 11963 return None 11964 11965 11966 class CGSpecializedLenientSetter(CGSpecializedSetter): 11967 """ 11968 A class for generating the code for a specialized attribute setter with 11969 LenientSetter that the JIT can call with lower overhead. 11970 """ 11971 11972 def __init__(self, descriptor, attr): 11973 CGSpecializedSetter.__init__(self, descriptor, attr) 11974 11975 def definition_body(self): 11976 attrName = self.attr.identifier.name 11977 # JS_DefineProperty can only deal with ASCII 11978 assert all(ord(c) < 128 for c in attrName) 11979 return dedent( 11980 """ 11981 DeprecationWarning(cx, obj, DeprecatedOperations::eLenientSetter); 11982 return true; 11983 """ 11984 ) 11985 11986 def error_reporting_label(self): 11987 # We never throw; that's the whole point. 11988 return None 11989 11990 11991 def memberReturnsNewObject(member): 11992 return member.getExtendedAttribute("NewObject") is not None 11993 11994 11995 class CGMemberJITInfo(CGThing): 11996 """ 11997 A class for generating the JITInfo for a property that points to 11998 our specialized getter and setter. 11999 """ 12000 12001 def __init__(self, descriptor, member): 12002 self.member = member 12003 self.descriptor = descriptor 12004 12005 def declare(self): 12006 return "" 12007 12008 def defineJitInfo( 12009 self, 12010 infoName, 12011 opName, 12012 opType, 12013 infallible, 12014 movable, 12015 eliminatable, 12016 aliasSet, 12017 alwaysInSlot, 12018 lazilyInSlot, 12019 slotIndex, 12020 returnTypes, 12021 args, 12022 ): 12023 """ 12024 aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit. 12025 12026 args is None if we don't want to output argTypes for some 12027 reason (e.g. we have overloads or we're not a method) and 12028 otherwise an iterable of the arguments for this method. 12029 """ 12030 assert ( 12031 not movable or aliasSet != "AliasEverything" 12032 ) # Can't move write-aliasing things 12033 assert ( 12034 not alwaysInSlot or movable 12035 ) # Things always in slots had better be movable 12036 assert ( 12037 not eliminatable or aliasSet != "AliasEverything" 12038 ) # Can't eliminate write-aliasing things 12039 assert ( 12040 not alwaysInSlot or eliminatable 12041 ) # Things always in slots had better be eliminatable 12042 12043 def jitInfoInitializer(isTypedMethod): 12044 initializer = fill( 12045 """ 12046 { 12047 { ${opName} }, 12048 { prototypes::id::${name} }, 12049 { PrototypeTraits<prototypes::id::${name}>::Depth }, 12050 JSJitInfo::${opType}, 12051 JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */ 12052 ${returnType}, /* returnType. Not relevant for setters. */ 12053 ${isInfallible}, /* isInfallible. False in setters. */ 12054 ${isMovable}, /* isMovable. Not relevant for setters. */ 12055 ${isEliminatable}, /* isEliminatable. Not relevant for setters. */ 12056 ${isAlwaysInSlot}, /* isAlwaysInSlot. Only relevant for getters. */ 12057 ${isLazilyCachedInSlot}, /* isLazilyCachedInSlot. Only relevant for getters. */ 12058 ${isTypedMethod}, /* isTypedMethod. Only relevant for methods. */ 12059 ${slotIndex} /* Reserved slot index, if we're stored in a slot, else 0. */ 12060 } 12061 """, 12062 opName=opName, 12063 name=self.descriptor.name, 12064 opType=opType, 12065 aliasSet=aliasSet, 12066 returnType=functools.reduce( 12067 CGMemberJITInfo.getSingleReturnType, returnTypes, "" 12068 ), 12069 isInfallible=toStringBool(infallible), 12070 isMovable=toStringBool(movable), 12071 isEliminatable=toStringBool(eliminatable), 12072 isAlwaysInSlot=toStringBool(alwaysInSlot), 12073 isLazilyCachedInSlot=toStringBool(lazilyInSlot), 12074 isTypedMethod=toStringBool(isTypedMethod), 12075 slotIndex="0" if slotIndex is None else slotIndex, 12076 ) 12077 return initializer.rstrip() 12078 12079 if slotIndex is not None: 12080 slotAssert = fill( 12081 """ 12082 static_assert(${slotIndex} <= JSJitInfo::maxSlotIndex, "We won't fit"); 12083 static_assert(${slotIndex} < ${classReservedSlots}, "There is no slot for us"); 12084 """, 12085 slotIndex=slotIndex, 12086 classReservedSlots=INSTANCE_RESERVED_SLOTS 12087 + self.descriptor.interface.totalMembersInSlots, 12088 ) 12089 else: 12090 slotAssert = "" 12091 if args is not None: 12092 argTypes = "%s_argTypes" % infoName 12093 args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args] 12094 args.append("JSJitInfo::ArgTypeListEnd") 12095 argTypesDecl = "static const JSJitInfo::ArgType %s[] = { %s };\n" % ( 12096 argTypes, 12097 ", ".join(args), 12098 ) 12099 return fill( 12100 """ 12101 $*{argTypesDecl} 12102 static const JSTypedMethodJitInfo ${infoName} = { 12103 ${jitInfo}, 12104 ${argTypes} 12105 }; 12106 $*{slotAssert} 12107 """, 12108 argTypesDecl=argTypesDecl, 12109 infoName=infoName, 12110 jitInfo=indent(jitInfoInitializer(True)), 12111 argTypes=argTypes, 12112 slotAssert=slotAssert, 12113 ) 12114 12115 # Unexposed things are meant to be used from C++ directly, so we make 12116 # their jitinfo non-static. That way C++ can get at it. 12117 if self.member.getExtendedAttribute("Unexposed"): 12118 storageClass = "extern" 12119 else: 12120 storageClass = "static" 12121 12122 return fill( 12123 """ 12124 ${storageClass} const JSJitInfo ${infoName} = ${jitInfo}; 12125 $*{slotAssert} 12126 """, 12127 storageClass=storageClass, 12128 infoName=infoName, 12129 jitInfo=jitInfoInitializer(False), 12130 slotAssert=slotAssert, 12131 ) 12132 12133 def define(self): 12134 if self.member.isAttr(): 12135 getterinfo = "%s_getterinfo" % IDLToCIdentifier(self.member.identifier.name) 12136 name = IDLToCIdentifier(self.member.identifier.name) 12137 if self.member.type.isPromise(): 12138 name = CGGetterPromiseWrapper.makeName(name) 12139 getter = "get_%s" % name 12140 extendedAttrs = self.descriptor.getExtendedAttributes( 12141 self.member, getter=True 12142 ) 12143 getterinfal = "needsErrorResult" not in extendedAttrs 12144 12145 # At this point getterinfal is true if our getter either can't throw 12146 # at all, or can only throw OOM. In both cases, it's safe to move, 12147 # or dead-code-eliminate, the getter, because throwing OOM is not 12148 # semantically meaningful, so code can't rely on it happening. Note 12149 # that this makes the behavior consistent for OOM thrown from the 12150 # getter itself and OOM thrown from the to-JS conversion of the 12151 # return value (see the "canOOM" and "infallibleForMember" checks 12152 # below). 12153 movable = self.mayBeMovable() and getterinfal 12154 eliminatable = self.mayBeEliminatable() and getterinfal 12155 aliasSet = self.aliasSet() 12156 12157 # Now we have to set getterinfal to whether we can _really_ ever 12158 # throw, from the point of view of the JS engine. 12159 getterinfal = ( 12160 getterinfal 12161 and "canOOM" not in extendedAttrs 12162 and infallibleForMember(self.member, self.member.type, self.descriptor) 12163 ) 12164 isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot") 12165 12166 if self.member.slotIndices is not None: 12167 assert ( 12168 isAlwaysInSlot 12169 or self.member.getExtendedAttribute("Cached") 12170 or self.member.getExtendedAttribute( 12171 "ReflectedHTMLAttributeReturningFrozenArray" 12172 ) 12173 or self.member.type.isObservableArray() 12174 ) 12175 isLazilyCachedInSlot = ( 12176 not isAlwaysInSlot 12177 and not self.member.getExtendedAttribute( 12178 "ReflectedHTMLAttributeReturningFrozenArray" 12179 ) 12180 ) 12181 slotIndex = memberReservedSlot(self.member, self.descriptor) 12182 # We'll statically assert that this is not too big in 12183 # CGUpdateMemberSlotsMethod, in the case when 12184 # isAlwaysInSlot is true. 12185 else: 12186 isLazilyCachedInSlot = False 12187 slotIndex = None 12188 12189 result = self.defineJitInfo( 12190 getterinfo, 12191 getter, 12192 "Getter", 12193 getterinfal, 12194 movable, 12195 eliminatable, 12196 aliasSet, 12197 isAlwaysInSlot, 12198 isLazilyCachedInSlot, 12199 slotIndex, 12200 [self.member.type], 12201 None, 12202 ) 12203 if ( 12204 not self.member.readonly 12205 or self.member.getExtendedAttribute("PutForwards") is not None 12206 or self.member.getExtendedAttribute("Replaceable") is not None 12207 or self.member.getExtendedAttribute("LegacyLenientSetter") is not None 12208 ): 12209 setterinfo = "%s_setterinfo" % IDLToCIdentifier( 12210 self.member.identifier.name 12211 ) 12212 # Actually a JSJitSetterOp, but JSJitGetterOp is first in the 12213 # union. 12214 setter = "(JSJitGetterOp)set_%s" % IDLToCIdentifier( 12215 self.member.identifier.name 12216 ) 12217 # Setters are always fallible, since they have to do a typed unwrap. 12218 result += self.defineJitInfo( 12219 setterinfo, 12220 setter, 12221 "Setter", 12222 False, 12223 False, 12224 False, 12225 "AliasEverything", 12226 False, 12227 False, 12228 None, 12229 [BuiltinTypes[IDLBuiltinType.Types.undefined]], 12230 None, 12231 ) 12232 return result 12233 if self.member.isMethod(): 12234 methodinfo = "%s_methodinfo" % IDLToCIdentifier(self.member.identifier.name) 12235 name = CppKeywords.checkMethodName( 12236 IDLToCIdentifier(self.member.identifier.name) 12237 ) 12238 if self.member.returnsPromise(): 12239 name = CGMethodPromiseWrapper.makeName(name) 12240 # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union. 12241 method = "(JSJitGetterOp)%s" % name 12242 12243 # Methods are infallible if they are infallible, have no arguments 12244 # to unwrap, and have a return type that's infallible to wrap up for 12245 # return. 12246 sigs = self.member.signatures() 12247 if len(sigs) != 1: 12248 # Don't handle overloading. If there's more than one signature, 12249 # one of them must take arguments. 12250 methodInfal = False 12251 args = None 12252 movable = False 12253 eliminatable = False 12254 else: 12255 sig = sigs[0] 12256 # For methods that affect nothing, it's OK to set movable to our 12257 # notion of infallible on the C++ side, without considering 12258 # argument conversions, since argument conversions that can 12259 # reliably throw would be effectful anyway and the jit doesn't 12260 # move effectful things. 12261 extendedAttrs = self.descriptor.getExtendedAttributes(self.member) 12262 hasInfallibleImpl = "needsErrorResult" not in extendedAttrs 12263 # At this point hasInfallibleImpl is true if our method either 12264 # can't throw at all, or can only throw OOM. In both cases, it 12265 # may be safe to move, or dead-code-eliminate, the method, 12266 # because throwing OOM is not semantically meaningful, so code 12267 # can't rely on it happening. Note that this makes the behavior 12268 # consistent for OOM thrown from the method itself and OOM 12269 # thrown from the to-JS conversion of the return value (see the 12270 # "canOOM" and "infallibleForMember" checks below). 12271 movable = self.mayBeMovable() and hasInfallibleImpl 12272 eliminatable = self.mayBeEliminatable() and hasInfallibleImpl 12273 # XXXbz can we move the smarts about fallibility due to arg 12274 # conversions into the JIT, using our new args stuff? 12275 if len(sig[1]) != 0 or not infallibleForMember( 12276 self.member, sig[0], self.descriptor 12277 ): 12278 # We have arguments or our return-value boxing can fail 12279 methodInfal = False 12280 else: 12281 methodInfal = hasInfallibleImpl and "canOOM" not in extendedAttrs 12282 # For now, only bother to output args if we're side-effect-free. 12283 if self.member.affects == "Nothing": 12284 args = sig[1] 12285 else: 12286 args = None 12287 12288 aliasSet = self.aliasSet() 12289 result = self.defineJitInfo( 12290 methodinfo, 12291 method, 12292 "Method", 12293 methodInfal, 12294 movable, 12295 eliminatable, 12296 aliasSet, 12297 False, 12298 False, 12299 None, 12300 [s[0] for s in sigs], 12301 args, 12302 ) 12303 return result 12304 raise TypeError("Illegal member type to CGPropertyJITInfo") 12305 12306 def mayBeMovable(self): 12307 """ 12308 Returns whether this attribute or method may be movable, just 12309 based on Affects/DependsOn annotations. 12310 """ 12311 affects = self.member.affects 12312 dependsOn = self.member.dependsOn 12313 assert affects in IDLInterfaceMember.AffectsValues 12314 assert dependsOn in IDLInterfaceMember.DependsOnValues 12315 # Things that are DependsOn=DeviceState are not movable, because we 12316 # don't want them coalesced with each other or loop-hoisted, since 12317 # their return value can change even if nothing is going on from our 12318 # point of view. 12319 return affects == "Nothing" and ( 12320 dependsOn != "Everything" and dependsOn != "DeviceState" 12321 ) 12322 12323 def mayBeEliminatable(self): 12324 """ 12325 Returns whether this attribute or method may be eliminatable, just 12326 based on Affects/DependsOn annotations. 12327 """ 12328 # dependsOn shouldn't affect this decision at all, except in jitinfo we 12329 # have no way to express "Depends on everything, affects nothing", 12330 # because we only have three alias set values: AliasNone ("depends on 12331 # nothing, affects nothing"), AliasDOMSets ("depends on DOM sets, 12332 # affects nothing"), AliasEverything ("depends on everything, affects 12333 # everything"). So the [Affects=Nothing, DependsOn=Everything] case 12334 # gets encoded as AliasEverything and defineJitInfo asserts that if our 12335 # alias state is AliasEverything then we're not eliminatable (because it 12336 # thinks we might have side-effects at that point). Bug 1155796 is 12337 # tracking possible solutions for this. 12338 affects = self.member.affects 12339 dependsOn = self.member.dependsOn 12340 assert affects in IDLInterfaceMember.AffectsValues 12341 assert dependsOn in IDLInterfaceMember.DependsOnValues 12342 return affects == "Nothing" and dependsOn != "Everything" 12343 12344 def aliasSet(self): 12345 """ 12346 Returns the alias set to store in the jitinfo. This may not be the 12347 effective alias set the JIT uses, depending on whether we have enough 12348 information about our args to allow the JIT to prove that effectful 12349 argument conversions won't happen. 12350 """ 12351 dependsOn = self.member.dependsOn 12352 assert dependsOn in IDLInterfaceMember.DependsOnValues 12353 12354 if dependsOn == "Nothing" or dependsOn == "DeviceState": 12355 assert self.member.affects == "Nothing" 12356 return "AliasNone" 12357 12358 if dependsOn == "DOMState": 12359 assert self.member.affects == "Nothing" 12360 return "AliasDOMSets" 12361 12362 return "AliasEverything" 12363 12364 @staticmethod 12365 def getJSReturnTypeTag(t): 12366 if t.nullable(): 12367 # Sometimes it might return null, sometimes not 12368 return "JSVAL_TYPE_UNKNOWN" 12369 if t.isUndefined(): 12370 # No return, every time 12371 return "JSVAL_TYPE_UNDEFINED" 12372 if t.isSequence(): 12373 return "JSVAL_TYPE_OBJECT" 12374 if t.isRecord(): 12375 return "JSVAL_TYPE_OBJECT" 12376 if t.isPromise(): 12377 return "JSVAL_TYPE_OBJECT" 12378 if t.isGeckoInterface(): 12379 return "JSVAL_TYPE_OBJECT" 12380 if t.isString(): 12381 return "JSVAL_TYPE_STRING" 12382 if t.isEnum(): 12383 return "JSVAL_TYPE_STRING" 12384 if t.isCallback(): 12385 return "JSVAL_TYPE_OBJECT" 12386 if t.isAny(): 12387 # The whole point is to return various stuff 12388 return "JSVAL_TYPE_UNKNOWN" 12389 if t.isObject(): 12390 return "JSVAL_TYPE_OBJECT" 12391 if t.isSpiderMonkeyInterface(): 12392 return "JSVAL_TYPE_OBJECT" 12393 if t.isUnion(): 12394 u = t.unroll() 12395 if u.hasNullableType: 12396 # Might be null or not 12397 return "JSVAL_TYPE_UNKNOWN" 12398 return functools.reduce( 12399 CGMemberJITInfo.getSingleReturnType, u.flatMemberTypes, "" 12400 ) 12401 if t.isDictionary(): 12402 return "JSVAL_TYPE_OBJECT" 12403 if t.isObservableArray(): 12404 return "JSVAL_TYPE_OBJECT" 12405 if not t.isPrimitive(): 12406 raise TypeError("No idea what type " + str(t) + " is.") 12407 tag = t.tag() 12408 if tag == IDLType.Tags.bool: 12409 return "JSVAL_TYPE_BOOLEAN" 12410 if tag in [ 12411 IDLType.Tags.int8, 12412 IDLType.Tags.uint8, 12413 IDLType.Tags.int16, 12414 IDLType.Tags.uint16, 12415 IDLType.Tags.int32, 12416 ]: 12417 return "JSVAL_TYPE_INT32" 12418 if tag in [ 12419 IDLType.Tags.int64, 12420 IDLType.Tags.uint64, 12421 IDLType.Tags.unrestricted_float, 12422 IDLType.Tags.float, 12423 IDLType.Tags.unrestricted_double, 12424 IDLType.Tags.double, 12425 ]: 12426 # These all use JS_NumberValue, which can return int or double. 12427 # But TI treats "double" as meaning "int or double", so we're 12428 # good to return JSVAL_TYPE_DOUBLE here. 12429 return "JSVAL_TYPE_DOUBLE" 12430 if tag != IDLType.Tags.uint32: 12431 raise TypeError("No idea what type " + str(t) + " is.") 12432 # uint32 is sometimes int and sometimes double. 12433 return "JSVAL_TYPE_DOUBLE" 12434 12435 @staticmethod 12436 def getSingleReturnType(existingType, t): 12437 type = CGMemberJITInfo.getJSReturnTypeTag(t) 12438 if existingType == "": 12439 # First element of the list; just return its type 12440 return type 12441 12442 if type == existingType: 12443 return existingType 12444 if (type == "JSVAL_TYPE_DOUBLE" and existingType == "JSVAL_TYPE_INT32") or ( 12445 existingType == "JSVAL_TYPE_DOUBLE" and type == "JSVAL_TYPE_INT32" 12446 ): 12447 # Promote INT32 to DOUBLE as needed 12448 return "JSVAL_TYPE_DOUBLE" 12449 # Different types 12450 return "JSVAL_TYPE_UNKNOWN" 12451 12452 @staticmethod 12453 def getJSArgType(t): 12454 assert not t.isUndefined() 12455 if t.nullable(): 12456 # Sometimes it might return null, sometimes not 12457 return ( 12458 "JSJitInfo::ArgType(JSJitInfo::Null | %s)" 12459 % CGMemberJITInfo.getJSArgType(t.inner) 12460 ) 12461 if t.isSequence(): 12462 return "JSJitInfo::Object" 12463 if t.isPromise(): 12464 return "JSJitInfo::Object" 12465 if t.isGeckoInterface(): 12466 return "JSJitInfo::Object" 12467 if t.isString(): 12468 return "JSJitInfo::String" 12469 if t.isEnum(): 12470 return "JSJitInfo::String" 12471 if t.isCallback(): 12472 return "JSJitInfo::Object" 12473 if t.isAny(): 12474 # The whole point is to return various stuff 12475 return "JSJitInfo::Any" 12476 if t.isObject(): 12477 return "JSJitInfo::Object" 12478 if t.isSpiderMonkeyInterface(): 12479 return "JSJitInfo::Object" 12480 if t.isUnion(): 12481 u = t.unroll() 12482 type = "JSJitInfo::Null" if u.hasNullableType else "" 12483 return "JSJitInfo::ArgType(%s)" % functools.reduce( 12484 CGMemberJITInfo.getSingleArgType, u.flatMemberTypes, type 12485 ) 12486 if t.isDictionary(): 12487 return "JSJitInfo::Object" 12488 if not t.isPrimitive(): 12489 raise TypeError("No idea what type " + str(t) + " is.") 12490 tag = t.tag() 12491 if tag == IDLType.Tags.bool: 12492 return "JSJitInfo::Boolean" 12493 if tag in [ 12494 IDLType.Tags.int8, 12495 IDLType.Tags.uint8, 12496 IDLType.Tags.int16, 12497 IDLType.Tags.uint16, 12498 IDLType.Tags.int32, 12499 ]: 12500 return "JSJitInfo::Integer" 12501 if tag in [ 12502 IDLType.Tags.int64, 12503 IDLType.Tags.uint64, 12504 IDLType.Tags.unrestricted_float, 12505 IDLType.Tags.float, 12506 IDLType.Tags.unrestricted_double, 12507 IDLType.Tags.double, 12508 ]: 12509 # These all use JS_NumberValue, which can return int or double. 12510 # But TI treats "double" as meaning "int or double", so we're 12511 # good to return JSVAL_TYPE_DOUBLE here. 12512 return "JSJitInfo::Double" 12513 if tag != IDLType.Tags.uint32: 12514 raise TypeError("No idea what type " + str(t) + " is.") 12515 # uint32 is sometimes int and sometimes double. 12516 return "JSJitInfo::Double" 12517 12518 @staticmethod 12519 def getSingleArgType(existingType, t): 12520 type = CGMemberJITInfo.getJSArgType(t) 12521 if existingType == "": 12522 # First element of the list; just return its type 12523 return type 12524 12525 if type == existingType: 12526 return existingType 12527 return "%s | %s" % (existingType, type) 12528 12529 12530 class CGStaticMethodJitinfo(CGGeneric): 12531 """ 12532 A class for generating the JITInfo for a promise-returning static method. 12533 """ 12534 12535 def __init__(self, method): 12536 CGGeneric.__init__( 12537 self, 12538 "\n" 12539 "static const JSJitInfo %s_methodinfo = {\n" 12540 " { (JSJitGetterOp)%s },\n" 12541 " { prototypes::id::_ID_Count }, { 0 }, JSJitInfo::StaticMethod,\n" 12542 " JSJitInfo::AliasEverything, JSVAL_TYPE_OBJECT, false, false,\n" 12543 " false, false, 0\n" 12544 "};\n" 12545 % ( 12546 IDLToCIdentifier(method.identifier.name), 12547 CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name)), 12548 ), 12549 ) 12550 12551 12552 def getEnumValueName(value): 12553 # Some enum values can be empty strings. Others might have weird 12554 # characters in them. Deal with the former by returning "_empty", 12555 # deal with possible name collisions from that by throwing if the 12556 # enum value is actually "_empty", and throw on any value 12557 # containing non-ASCII chars for now. Replace all chars other than 12558 # [0-9A-Za-z_] with '_'. 12559 if re.match("[^\x20-\x7E]", value): 12560 raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters') 12561 if re.match("^[0-9]", value): 12562 value = "_" + value 12563 value = re.sub(r"[^0-9A-Za-z_]", "_", value) 12564 if re.match("^_[A-Z]|__", value): 12565 raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec') 12566 if value == "_empty": 12567 raise SyntaxError('"_empty" is not an IDL enum value we support yet') 12568 if value == "": 12569 return "_empty" 12570 return MakeNativeName(value) 12571 12572 12573 class CGEnumToJSValue(CGAbstractMethod): 12574 def __init__(self, enum): 12575 enumType = enum.identifier.name 12576 self.stringsArray = "binding_detail::EnumStrings<" + enumType + ">::Values" 12577 CGAbstractMethod.__init__( 12578 self, 12579 None, 12580 "ToJSValue", 12581 "bool", 12582 [ 12583 Argument("JSContext*", "aCx"), 12584 Argument(enumType, "aArgument"), 12585 Argument("JS::MutableHandle<JS::Value>", "aValue"), 12586 ], 12587 ) 12588 12589 def definition_body(self): 12590 return fill( 12591 """ 12592 MOZ_ASSERT(uint32_t(aArgument) < std::size(${strings})); 12593 JSString* resultStr = 12594 JS_NewStringCopyN(aCx, ${strings}[uint32_t(aArgument)].BeginReading(), 12595 ${strings}[uint32_t(aArgument)].Length()); 12596 if (!resultStr) { 12597 return false; 12598 } 12599 aValue.setString(resultStr); 12600 return true; 12601 """, 12602 strings=self.stringsArray, 12603 ) 12604 12605 12606 class CGEnum(CGThing): 12607 def __init__(self, enum): 12608 CGThing.__init__(self) 12609 self.enum = enum 12610 strings = CGNamespace( 12611 "binding_detail", 12612 CGGeneric( 12613 declare=fill( 12614 """ 12615 template <> struct EnumStrings<${name}> { 12616 static constexpr nsLiteralCString Values[${count}] { 12617 $*{entries} 12618 }; 12619 }; 12620 """, 12621 name=self.enum.identifier.name, 12622 count=self.nEnumStrings(), 12623 entries="".join('"%s"_ns,\n' % val for val in self.enum.values()), 12624 ), 12625 define=fill( 12626 """ 12627 constexpr nsLiteralCString EnumStrings<${name}>::Values[${count}]; 12628 """, 12629 name=self.enum.identifier.name, 12630 count=self.nEnumStrings(), 12631 ), 12632 ), 12633 ) 12634 toJSValue = CGEnumToJSValue(enum) 12635 self.cgThings = CGList([strings, toJSValue], "\n") 12636 12637 def nEnumStrings(self): 12638 return len(self.enum.values()) 12639 12640 @staticmethod 12641 def underlyingType(enum): 12642 count = len(enum.values()) 12643 if count <= 256: 12644 return "uint8_t" 12645 if count <= 65536: 12646 return "uint16_t" 12647 raise ValueError("Enum " + enum.identifier.name + " has more than 65536 values") 12648 12649 def declare(self): 12650 decl = fill( 12651 """ 12652 enum class ${name} : ${ty} { 12653 $*{enums} 12654 }; 12655 """, 12656 name=self.enum.identifier.name, 12657 ty=CGEnum.underlyingType(self.enum), 12658 enums=",\n".join(map(getEnumValueName, self.enum.values())) + ",\n", 12659 ) 12660 12661 return decl + "\n" + self.cgThings.declare() 12662 12663 def define(self): 12664 return self.cgThings.define() 12665 12666 def deps(self): 12667 return self.enum.getDeps() 12668 12669 12670 class CGMaxContiguousEnumValue(CGThing): 12671 def __init__(self, enum): 12672 CGThing.__init__(self) 12673 self.enum = enum 12674 12675 def declare(self): 12676 enumValues = self.enum.values() 12677 return fill( 12678 """ 12679 template <> 12680 struct MaxContiguousEnumValue<dom::${name}> 12681 { 12682 static constexpr dom::${name} value = dom::${name}::${maxValue}; 12683 12684 static_assert(static_cast<${ty}>(dom::${name}::${minValue}) == 0, 12685 "We rely on this in ContiguousEnumValues"); 12686 static_assert(std::size(dom::binding_detail::EnumStrings<dom::${name}>::Values) - 1 == UnderlyingValue(value), 12687 "Mismatch between enum strings and enum count"); 12688 }; 12689 """, 12690 name=self.enum.identifier.name, 12691 ty=CGEnum.underlyingType(self.enum), 12692 maxValue=getEnumValueName(enumValues[-1]), 12693 minValue=getEnumValueName(enumValues[0]), 12694 ) 12695 12696 def define(self): 12697 return "" 12698 12699 def deps(self): 12700 return self.enum.getDeps() 12701 12702 12703 class CGUnionTypedef(CGThing): 12704 def __init__(self, typedef: IDLTypedef, config: Configuration): 12705 assert typedef.innerType.isUnion, "only union typedefs are supported" 12706 super().__init__() 12707 self.typedef = typedef 12708 12709 builder = ForwardDeclarationBuilder() 12710 builder.forwardDeclareForType(typedef.innerType, config) 12711 12712 name = self.typedef.identifier.name 12713 innerName = self.typedef.innerType.name 12714 declare = dedent( 12715 f""" 12716 using {name} = {innerName}; 12717 using Owning{name} = Owning{innerName}; 12718 """ 12719 ) 12720 self.root = CGList( 12721 [ 12722 builder.build(), 12723 CGNamespace.build( 12724 ["mozilla", "dom"], CGGeneric(forward_declare=declare) 12725 ), 12726 ], 12727 joiner="\n", 12728 ) 12729 12730 def declare(self): 12731 return "" 12732 12733 def define(self): 12734 return "" 12735 12736 def forward_declare(self): 12737 return self.root.forward_declare() 12738 12739 def deps(self): 12740 return self.typedef.getDeps() 12741 12742 12743 def getUnionAccessorSignatureType(type, descriptorProvider): 12744 """ 12745 Returns the types that are used in the getter and setter signatures for 12746 union types 12747 """ 12748 # Flat member types have already unwrapped nullables. 12749 assert not type.nullable() 12750 12751 # Promise types can never appear in unions, because Promise is not 12752 # distinguishable from anything. 12753 assert not type.isPromise() 12754 12755 if type.isSequence() or type.isRecord(): 12756 if type.isSequence(): 12757 wrapperType = "Sequence" 12758 else: 12759 wrapperType = "Record" 12760 # We don't use the returned template here, so it's OK to just pass no 12761 # sourceDescription. 12762 elementInfo = getJSToNativeConversionInfo( 12763 type.inner, descriptorProvider, isMember=wrapperType 12764 ) 12765 if wrapperType == "Sequence": 12766 innerType = elementInfo.declType 12767 else: 12768 innerType = [recordKeyDeclType(type), elementInfo.declType] 12769 12770 return CGTemplatedType(wrapperType, innerType, isConst=True, isReference=True) 12771 12772 # Nested unions are unwrapped automatically into our flatMemberTypes. 12773 assert not type.isUnion() 12774 12775 if type.isGeckoInterface(): 12776 descriptor = descriptorProvider.getDescriptor( 12777 type.unroll().inner.identifier.name 12778 ) 12779 typeName = CGGeneric(descriptor.nativeType) 12780 if not type.unroll().inner.isExternal(): 12781 typeName = CGWrapper(typeName, post="&") 12782 elif descriptor.interface.identifier.name == "WindowProxy": 12783 typeName = CGGeneric("WindowProxyHolder const&") 12784 else: 12785 # Allow null pointers for old-binding classes. 12786 typeName = CGWrapper(typeName, post="*") 12787 return typeName 12788 12789 if type.isSpiderMonkeyInterface(): 12790 typeName = CGGeneric(type.name) 12791 return CGWrapper(typeName, post=" const &") 12792 12793 if type.isJSString(): 12794 raise TypeError("JSString not supported in unions") 12795 12796 if type.isDOMString() or type.isUSVString(): 12797 return CGGeneric("const nsAString&") 12798 12799 if type.isUTF8String(): 12800 return CGGeneric("const nsACString&") 12801 12802 if type.isByteString(): 12803 return CGGeneric("const nsCString&") 12804 12805 if type.isEnum(): 12806 return CGGeneric(type.inner.identifier.name) 12807 12808 if type.isCallback(): 12809 return CGGeneric("%s&" % type.unroll().callback.identifier.name) 12810 12811 if type.isAny(): 12812 return CGGeneric("JS::Value") 12813 12814 if type.isObject(): 12815 return CGGeneric("JSObject*") 12816 12817 if type.isDictionary(): 12818 return CGGeneric("const %s&" % type.inner.identifier.name) 12819 12820 if not type.isPrimitive(): 12821 raise TypeError("Need native type for argument type '%s'" % str(type)) 12822 12823 return CGGeneric(builtinNames[type.tag()]) 12824 12825 12826 def getUnionTypeTemplateVars(unionType, type, descriptorProvider, isMember=False): 12827 assert not type.isUndefined() 12828 assert not isMember or isMember in ("Union", "OwningUnion") 12829 12830 ownsMembers = isMember == "OwningUnion" 12831 name = getUnionMemberName(type) 12832 holderName = "m" + name + "Holder" 12833 12834 # By the time tryNextCode is invoked, we're guaranteed the union has been 12835 # constructed as some type, since we've been trying to convert into the 12836 # corresponding member. 12837 tryNextCode = fill( 12838 """ 12839 Destroy${name}(); 12840 tryNext = true; 12841 return true; 12842 """, 12843 name=name, 12844 ) 12845 12846 sourceDescription = "%s branch of %s" % (type.prettyName(), unionType.prettyName()) 12847 12848 conversionInfo = getJSToNativeConversionInfo( 12849 type, 12850 descriptorProvider, 12851 failureCode=tryNextCode, 12852 isDefinitelyObject=not type.isDictionary(), 12853 isMember=isMember, 12854 sourceDescription=sourceDescription, 12855 ) 12856 12857 ctorNeedsCx = conversionInfo.declArgs == "cx" 12858 ctorArgs = "cx" if ctorNeedsCx else "" 12859 12860 structType = conversionInfo.declType.define() 12861 externalType = getUnionAccessorSignatureType(type, descriptorProvider).define() 12862 12863 if type.isObject(): 12864 if ownsMembers: 12865 setValue = "mValue.mObject.SetValue(obj);" 12866 else: 12867 setValue = "mValue.mObject.SetValue(cx, obj);" 12868 12869 body = fill( 12870 """ 12871 MOZ_ASSERT(mType == eUninitialized); 12872 ${setValue} 12873 mType = eObject; 12874 """, 12875 setValue=setValue, 12876 ) 12877 12878 # It's a bit sketchy to do the security check after setting the value, 12879 # but it keeps the code cleaner and lets us avoid rooting |obj| over the 12880 # call to CallerSubsumes(). 12881 body = body + fill( 12882 """ 12883 if (passedToJSImpl && !CallerSubsumes(obj)) { 12884 cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}"); 12885 return false; 12886 } 12887 return true; 12888 """, 12889 sourceDescription=sourceDescription, 12890 ) 12891 12892 setters = [ 12893 ClassMethod( 12894 "SetToObject", 12895 "bool", 12896 [ 12897 Argument("BindingCallContext&", "cx"), 12898 Argument("JSObject*", "obj"), 12899 Argument("bool", "passedToJSImpl", default="false"), 12900 ], 12901 inline=True, 12902 bodyInHeader=True, 12903 body=body, 12904 ) 12905 ] 12906 elif type.isDictionary() and not type.inner.needsConversionFromJS: 12907 # In this case we are never initialized from JS to start with 12908 setters = None 12909 else: 12910 # Important: we need to not have our declName involve 12911 # maybe-GCing operations. 12912 jsConversion = fill( 12913 conversionInfo.template, 12914 val="value", 12915 maybeMutableVal="value", 12916 declName="memberSlot", 12917 holderName=(holderName if ownsMembers else "%s.ref()" % holderName), 12918 passedToJSImpl="passedToJSImpl", 12919 ) 12920 12921 jsConversion = fill( 12922 """ 12923 tryNext = false; 12924 { // scope for memberSlot 12925 ${structType}& memberSlot = RawSetAs${name}(${ctorArgs}); 12926 $*{jsConversion} 12927 } 12928 return true; 12929 """, 12930 structType=structType, 12931 name=name, 12932 ctorArgs=ctorArgs, 12933 jsConversion=jsConversion, 12934 ) 12935 12936 needCallContext = idlTypeNeedsCallContext(type) 12937 if needCallContext: 12938 cxType = "BindingCallContext&" 12939 else: 12940 cxType = "JSContext*" 12941 setters = [ 12942 ClassMethod( 12943 "TrySetTo" + name, 12944 "bool", 12945 [ 12946 Argument(cxType, "cx"), 12947 Argument("JS::Handle<JS::Value>", "value"), 12948 Argument("bool&", "tryNext"), 12949 Argument("bool", "passedToJSImpl", default="false"), 12950 ], 12951 visibility="private", 12952 body=jsConversion, 12953 ) 12954 ] 12955 if needCallContext: 12956 # Add a method for non-binding uses of unions to allow them to set 12957 # things in the union without providing a call context (though if 12958 # they want good error reporting they'll provide one anyway). 12959 shimBody = fill( 12960 """ 12961 BindingCallContext cx(cx_, nullptr); 12962 return TrySetTo${name}(cx, value, tryNext, passedToJSImpl); 12963 """, 12964 name=name, 12965 ) 12966 setters.append( 12967 ClassMethod( 12968 "TrySetTo" + name, 12969 "bool", 12970 [ 12971 Argument("JSContext*", "cx_"), 12972 Argument("JS::Handle<JS::Value>", "value"), 12973 Argument("bool&", "tryNext"), 12974 Argument("bool", "passedToJSImpl", default="false"), 12975 ], 12976 visibility="private", 12977 body=shimBody, 12978 ) 12979 ) 12980 12981 return { 12982 "name": name, 12983 "structType": structType, 12984 "externalType": externalType, 12985 "setters": setters, 12986 "ctorArgs": ctorArgs, 12987 "ctorArgList": [Argument("JSContext*", "cx")] if ctorNeedsCx else [], 12988 } 12989 12990 12991 def getUnionConversionTemplate(type): 12992 assert type.isUnion() 12993 assert not type.nullable() 12994 12995 memberTypes = type.flatMemberTypes 12996 prettyNames = [] 12997 12998 interfaceMemberTypes = [t for t in memberTypes if t.isNonCallbackInterface()] 12999 if len(interfaceMemberTypes) > 0: 13000 interfaceObject = [] 13001 for memberType in interfaceMemberTypes: 13002 name = getUnionMemberName(memberType) 13003 interfaceObject.append( 13004 CGGeneric( 13005 "(failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext" 13006 % name 13007 ) 13008 ) 13009 prettyNames.append(memberType.prettyName()) 13010 interfaceObject = CGWrapper( 13011 CGList(interfaceObject, " ||\n"), 13012 pre="done = ", 13013 post=";\n", 13014 reindent=True, 13015 ) 13016 else: 13017 interfaceObject = None 13018 13019 sequenceObjectMemberTypes = [t for t in memberTypes if t.isSequence()] 13020 if len(sequenceObjectMemberTypes) > 0: 13021 assert len(sequenceObjectMemberTypes) == 1 13022 memberType = sequenceObjectMemberTypes[0] 13023 name = getUnionMemberName(memberType) 13024 sequenceObject = CGGeneric( 13025 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" 13026 % name 13027 ) 13028 prettyNames.append(memberType.prettyName()) 13029 else: 13030 sequenceObject = None 13031 13032 callbackMemberTypes = [ 13033 t for t in memberTypes if t.isCallback() or t.isCallbackInterface() 13034 ] 13035 if len(callbackMemberTypes) > 0: 13036 assert len(callbackMemberTypes) == 1 13037 memberType = callbackMemberTypes[0] 13038 name = getUnionMemberName(memberType) 13039 callbackObject = CGGeneric( 13040 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" 13041 % name 13042 ) 13043 prettyNames.append(memberType.prettyName()) 13044 else: 13045 callbackObject = None 13046 13047 dictionaryMemberTypes = [t for t in memberTypes if t.isDictionary()] 13048 if len(dictionaryMemberTypes) > 0: 13049 assert len(dictionaryMemberTypes) == 1 13050 memberType = dictionaryMemberTypes[0] 13051 name = getUnionMemberName(memberType) 13052 setDictionary = CGGeneric( 13053 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" 13054 % name 13055 ) 13056 prettyNames.append(memberType.prettyName()) 13057 else: 13058 setDictionary = None 13059 13060 recordMemberTypes = [t for t in memberTypes if t.isRecord()] 13061 if len(recordMemberTypes) > 0: 13062 assert len(recordMemberTypes) == 1 13063 memberType = recordMemberTypes[0] 13064 name = getUnionMemberName(memberType) 13065 recordObject = CGGeneric( 13066 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" 13067 % name 13068 ) 13069 prettyNames.append(memberType.prettyName()) 13070 else: 13071 recordObject = None 13072 13073 objectMemberTypes = [t for t in memberTypes if t.isObject()] 13074 if len(objectMemberTypes) > 0: 13075 assert len(objectMemberTypes) == 1 13076 # Very important to NOT construct a temporary Rooted here, since the 13077 # SetToObject call can call a Rooted constructor and we need to keep 13078 # stack discipline for Rooted. 13079 object = CGGeneric( 13080 "if (!SetToObject(cx, &${val}.toObject(), ${passedToJSImpl})) {\n" 13081 " return false;\n" 13082 "}\n" 13083 "done = true;\n" 13084 ) 13085 prettyNames.append(objectMemberTypes[0].prettyName()) 13086 else: 13087 object = None 13088 13089 hasObjectTypes = ( 13090 interfaceObject or sequenceObject or callbackObject or object or recordObject 13091 ) 13092 if hasObjectTypes: 13093 # "object" is not distinguishable from other types 13094 assert not object or not ( 13095 interfaceObject or sequenceObject or callbackObject or recordObject 13096 ) 13097 if sequenceObject or callbackObject: 13098 # An object can be both an sequence object and a callback or 13099 # dictionary, but we shouldn't have both in the union's members 13100 # because they are not distinguishable. 13101 assert not (sequenceObject and callbackObject) 13102 templateBody = CGElseChain([sequenceObject, callbackObject]) 13103 else: 13104 templateBody = None 13105 if interfaceObject: 13106 assert not object 13107 if templateBody: 13108 templateBody = CGIfWrapper(templateBody, "!done") 13109 templateBody = CGList([interfaceObject, templateBody]) 13110 else: 13111 templateBody = CGList([templateBody, object]) 13112 13113 if recordObject: 13114 templateBody = CGList([templateBody, CGIfWrapper(recordObject, "!done")]) 13115 13116 templateBody = CGIfWrapper(templateBody, "${val}.isObject()") 13117 else: 13118 templateBody = CGGeneric() 13119 13120 if setDictionary: 13121 assert not object 13122 templateBody = CGList([templateBody, CGIfWrapper(setDictionary, "!done")]) 13123 13124 stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()] 13125 numericTypes = [t for t in memberTypes if t.isNumeric()] 13126 booleanTypes = [t for t in memberTypes if t.isBoolean()] 13127 if stringTypes or numericTypes or booleanTypes: 13128 assert len(stringTypes) <= 1 13129 assert len(numericTypes) <= 1 13130 assert len(booleanTypes) <= 1 13131 13132 # We will wrap all this stuff in a do { } while (0); so we 13133 # can use "break" for flow control. 13134 def getStringOrPrimitiveConversion(memberType): 13135 name = getUnionMemberName(memberType) 13136 return CGGeneric( 13137 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n" 13138 "break;\n" % name 13139 ) 13140 13141 other = CGList([]) 13142 stringConversion = [getStringOrPrimitiveConversion(t) for t in stringTypes] 13143 numericConversion = [getStringOrPrimitiveConversion(t) for t in numericTypes] 13144 booleanConversion = [getStringOrPrimitiveConversion(t) for t in booleanTypes] 13145 if stringConversion: 13146 if booleanConversion: 13147 other.append(CGIfWrapper(booleanConversion[0], "${val}.isBoolean()")) 13148 if numericConversion: 13149 other.append(CGIfWrapper(numericConversion[0], "${val}.isNumber()")) 13150 other.append(stringConversion[0]) 13151 elif numericConversion: 13152 if booleanConversion: 13153 other.append(CGIfWrapper(booleanConversion[0], "${val}.isBoolean()")) 13154 other.append(numericConversion[0]) 13155 else: 13156 assert booleanConversion 13157 other.append(booleanConversion[0]) 13158 13159 other = CGWrapper(CGIndenter(other), pre="do {\n", post="} while (false);\n") 13160 if hasObjectTypes or setDictionary: 13161 other = CGWrapper(CGIndenter(other), "{\n", post="}\n") 13162 if object: 13163 templateBody = CGElseChain([templateBody, other]) 13164 else: 13165 other = CGWrapper(other, pre="if (!done) ") 13166 templateBody = CGList([templateBody, other]) 13167 else: 13168 assert templateBody.define() == "" 13169 templateBody = other 13170 else: 13171 other = None 13172 13173 templateBody = CGWrapper( 13174 templateBody, pre="bool done = false, failed = false, tryNext;\n" 13175 ) 13176 throw = CGGeneric( 13177 fill( 13178 """ 13179 if (failed) { 13180 return false; 13181 } 13182 if (!done) { 13183 cx.ThrowErrorMessage<MSG_NOT_IN_UNION>(sourceDescription, "${names}"); 13184 return false; 13185 } 13186 """, 13187 names=", ".join(prettyNames), 13188 ) 13189 ) 13190 13191 templateBody = CGList([templateBody, throw]) 13192 13193 hasUndefinedType = any(t.isUndefined() for t in memberTypes) 13194 elseChain = [] 13195 13196 # The spec does this before anything else, but we do it after checking 13197 # for null in the case of a nullable union. In practice this shouldn't 13198 # make a difference, but it makes things easier because we first need to 13199 # call Construct on our Maybe<...>, before we can set the union type to 13200 # undefined, and we do that below after checking for null (see the 13201 # 'if nullable:' block below). 13202 if hasUndefinedType: 13203 elseChain.append( 13204 CGIfWrapper( 13205 CGGeneric("SetUndefined();\n"), 13206 "${val}.isUndefined()", 13207 ) 13208 ) 13209 13210 if type.hasNullableType: 13211 nullTest = ( 13212 "${val}.isNull()" if hasUndefinedType else "${val}.isNullOrUndefined()" 13213 ) 13214 elseChain.append( 13215 CGIfWrapper( 13216 CGGeneric("SetNull();\n"), 13217 nullTest, 13218 ) 13219 ) 13220 13221 if len(elseChain) > 0: 13222 elseChain.append(CGWrapper(CGIndenter(templateBody), pre="{\n", post="}\n")) 13223 templateBody = CGElseChain(elseChain) 13224 13225 return templateBody 13226 13227 13228 def getUnionInitMethods(type, isOwningUnion=False): 13229 assert type.isUnion() 13230 13231 template = getUnionConversionTemplate(type).define() 13232 13233 replacements = { 13234 "val": "value", 13235 "passedToJSImpl": "passedToJSImpl", 13236 } 13237 13238 initBody = fill( 13239 """ 13240 MOZ_ASSERT(mType == eUninitialized); 13241 13242 $*{conversion} 13243 return true; 13244 """, 13245 conversion=string.Template(template).substitute(replacements), 13246 ) 13247 13248 return [ 13249 ClassMethod( 13250 "Init", 13251 "bool", 13252 [ 13253 Argument("BindingCallContext&", "cx"), 13254 Argument("JS::Handle<JS::Value>", "value"), 13255 Argument("const char*", "sourceDescription", default='"Value"'), 13256 Argument("bool", "passedToJSImpl", default="false"), 13257 ], 13258 visibility="public", 13259 body=initBody, 13260 ), 13261 ClassMethod( 13262 "Init", 13263 "bool", 13264 [ 13265 Argument("JSContext*", "cx_"), 13266 Argument("JS::Handle<JS::Value>", "value"), 13267 Argument("const char*", "sourceDescription", default='"Value"'), 13268 Argument("bool", "passedToJSImpl", default="false"), 13269 ], 13270 visibility="public", 13271 body=dedent( 13272 """ 13273 BindingCallContext cx(cx_, nullptr); 13274 return Init(cx, value, sourceDescription, passedToJSImpl); 13275 """ 13276 ), 13277 ), 13278 ] 13279 13280 13281 class CGUnionStruct(CGThing): 13282 def __init__(self, type, descriptorProvider, ownsMembers=False): 13283 CGThing.__init__(self) 13284 self.type = type.unroll() 13285 self.descriptorProvider = descriptorProvider 13286 self.ownsMembers = ownsMembers 13287 self.struct = self.getStruct() 13288 13289 def declare(self): 13290 return self.struct.declare() 13291 13292 def define(self): 13293 return self.struct.define() 13294 13295 def deps(self): 13296 return self.type.getDeps() 13297 13298 def getStruct(self): 13299 members = [ 13300 ClassMember("mType", "TypeOrUninit", body="eUninitialized"), 13301 ClassMember("mValue", "Value"), 13302 ] 13303 ctor = ClassConstructor( 13304 [], bodyInHeader=True, visibility="public", explicit=True 13305 ) 13306 13307 methods = [] 13308 enumValues = ["eUninitialized"] 13309 toJSValCases = [ 13310 CGCase( 13311 "eUninitialized", CGGeneric("return false;\n"), CGCase.DONT_ADD_BREAK 13312 ) 13313 ] 13314 destructorCases = [CGCase("eUninitialized", None)] 13315 assignmentCase = CGCase( 13316 "eUninitialized", 13317 CGGeneric( 13318 "MOZ_ASSERT(mType == eUninitialized,\n" 13319 ' "We need to destroy ourselves?");\n' 13320 ), 13321 ) 13322 assignmentCases = [assignmentCase] 13323 moveCases = [assignmentCase] 13324 traceCases = [] 13325 unionValues = [] 13326 13327 def addSpecialType(typename): 13328 enumValue = "e" + typename 13329 enumValues.append(enumValue) 13330 methods.append( 13331 ClassMethod( 13332 "Is" + typename, 13333 "bool", 13334 [], 13335 const=True, 13336 inline=True, 13337 body="return mType == %s;\n" % enumValue, 13338 bodyInHeader=True, 13339 ) 13340 ) 13341 methods.append( 13342 ClassMethod( 13343 "Set" + typename, 13344 "void", 13345 [], 13346 inline=True, 13347 body=fill( 13348 """ 13349 Uninit(); 13350 mType = ${enumValue}; 13351 """, 13352 enumValue=enumValue, 13353 ), 13354 bodyInHeader=True, 13355 ) 13356 ) 13357 destructorCases.append(CGCase(enumValue, None)) 13358 assignmentCase = CGCase( 13359 enumValue, 13360 CGGeneric( 13361 fill( 13362 """ 13363 MOZ_ASSERT(mType == eUninitialized); 13364 mType = ${enumValue}; 13365 """, 13366 enumValue=enumValue, 13367 ) 13368 ), 13369 ) 13370 assignmentCases.append(assignmentCase) 13371 moveCases.append(assignmentCase) 13372 toJSValCases.append( 13373 CGCase( 13374 enumValue, 13375 CGGeneric( 13376 fill( 13377 """ 13378 rval.set${typename}(); 13379 return true; 13380 """, 13381 typename=typename, 13382 ) 13383 ), 13384 CGCase.DONT_ADD_BREAK, 13385 ) 13386 ) 13387 13388 if self.type.hasNullableType: 13389 addSpecialType("Null") 13390 13391 hasObjectType = any(t.isObject() for t in self.type.flatMemberTypes) 13392 skipToJSVal = False 13393 for t in self.type.flatMemberTypes: 13394 if t.isUndefined(): 13395 addSpecialType("Undefined") 13396 continue 13397 13398 vars = getUnionTypeTemplateVars( 13399 self.type, 13400 t, 13401 self.descriptorProvider, 13402 isMember="OwningUnion" if self.ownsMembers else "Union", 13403 ) 13404 if vars["setters"]: 13405 methods.extend(vars["setters"]) 13406 uninit = "Uninit();" 13407 if hasObjectType and not self.ownsMembers: 13408 uninit = ( 13409 'MOZ_ASSERT(mType != eObject, "This will not play well with Rooted");\n' 13410 + uninit 13411 ) 13412 if not t.isObject() or self.ownsMembers: 13413 body = fill( 13414 """ 13415 if (mType == e${name}) { 13416 return mValue.m${name}.Value(); 13417 } 13418 %s 13419 mType = e${name}; 13420 return mValue.m${name}.SetValue(${ctorArgs}); 13421 """, 13422 **vars, 13423 ) 13424 13425 # bodyInHeader must be false for return values because they own 13426 # their union members and we don't want include headers in 13427 # UnionTypes.h just to call Addref/Release 13428 methods.append( 13429 ClassMethod( 13430 "RawSetAs" + vars["name"], 13431 vars["structType"] + "&", 13432 vars["ctorArgList"], 13433 bodyInHeader=not self.ownsMembers, 13434 body=body % "MOZ_ASSERT(mType == eUninitialized);", 13435 noDiscard=True, 13436 ) 13437 ) 13438 methods.append( 13439 ClassMethod( 13440 "SetAs" + vars["name"], 13441 vars["structType"] + "&", 13442 vars["ctorArgList"], 13443 bodyInHeader=not self.ownsMembers, 13444 body=body % uninit, 13445 noDiscard=True, 13446 ) 13447 ) 13448 13449 # Provide a SetStringLiteral() method to support string defaults. 13450 if t.isByteString() or t.isUTF8String(): 13451 charType = "const nsCString::char_type" 13452 elif t.isString(): 13453 charType = "const nsString::char_type" 13454 else: 13455 charType = None 13456 13457 if charType: 13458 methods.append( 13459 ClassMethod( 13460 "SetStringLiteral", 13461 "void", 13462 # Hack, but it works... 13463 [Argument(charType, "(&aData)[N]")], 13464 inline=True, 13465 bodyInHeader=True, 13466 templateArgs=["int N"], 13467 body="RawSetAs%s().AssignLiteral(aData);\n" % t.name, 13468 ) 13469 ) 13470 13471 body = fill("return mType == e${name};\n", **vars) 13472 methods.append( 13473 ClassMethod( 13474 "Is" + vars["name"], 13475 "bool", 13476 [], 13477 const=True, 13478 bodyInHeader=True, 13479 body=body, 13480 ) 13481 ) 13482 13483 body = fill( 13484 """ 13485 MOZ_RELEASE_ASSERT(Is${name}(), "Wrong type!"); 13486 mValue.m${name}.Destroy(); 13487 mType = eUninitialized; 13488 """, 13489 **vars, 13490 ) 13491 methods.append( 13492 ClassMethod( 13493 "Destroy" + vars["name"], 13494 "void", 13495 [], 13496 visibility="private", 13497 bodyInHeader=not self.ownsMembers, 13498 body=body, 13499 ) 13500 ) 13501 13502 body = fill( 13503 """ 13504 MOZ_RELEASE_ASSERT(Is${name}(), "Wrong type!"); 13505 return mValue.m${name}.Value(); 13506 """, 13507 **vars, 13508 ) 13509 # The non-const version of GetAs* returns our internal type 13510 getterReturnType = "%s&" % vars["structType"] 13511 methods.append( 13512 ClassMethod( 13513 "GetAs" + vars["name"], 13514 getterReturnType, 13515 [], 13516 bodyInHeader=True, 13517 body=body, 13518 ) 13519 ) 13520 # The const version of GetAs* returns our internal type 13521 # for owning unions, but our external type for non-owning 13522 # ones. 13523 if self.ownsMembers: 13524 getterReturnType = "%s const &" % vars["structType"] 13525 else: 13526 getterReturnType = vars["externalType"] 13527 methods.append( 13528 ClassMethod( 13529 "GetAs" + vars["name"], 13530 getterReturnType, 13531 [], 13532 const=True, 13533 bodyInHeader=True, 13534 body=body, 13535 ) 13536 ) 13537 13538 unionValues.append(fill("UnionMember<${structType} > m${name}", **vars)) 13539 destructorCases.append( 13540 CGCase("e" + vars["name"], CGGeneric("Destroy%s();\n" % vars["name"])) 13541 ) 13542 13543 enumValues.append("e" + vars["name"]) 13544 13545 conversionToJS = self.getConversionToJS(vars, t) 13546 if conversionToJS: 13547 toJSValCases.append( 13548 CGCase("e" + vars["name"], conversionToJS, CGCase.DONT_ADD_BREAK) 13549 ) 13550 else: 13551 skipToJSVal = True 13552 13553 assignmentCases.append( 13554 CGCase( 13555 "e" + vars["name"], 13556 CGGeneric( 13557 "SetAs%s() = aOther.GetAs%s();\n" % (vars["name"], vars["name"]) 13558 ), 13559 ) 13560 ) 13561 moveCases.append( 13562 CGCase( 13563 "e" + vars["name"], 13564 CGGeneric( 13565 "mType = e%s;\n" % vars["name"] 13566 + "mValue.m%s.SetValue(std::move(aOther.mValue.m%s.Value()));\n" 13567 % (vars["name"], vars["name"]) 13568 ), 13569 ) 13570 ) 13571 if self.ownsMembers and typeNeedsRooting(t): 13572 if t.isObject(): 13573 traceCases.append( 13574 CGCase( 13575 "e" + vars["name"], 13576 CGGeneric( 13577 'JS::TraceRoot(trc, %s, "%s");\n' 13578 % ( 13579 "&mValue.m" + vars["name"] + ".Value()", 13580 "mValue.m" + vars["name"], 13581 ) 13582 ), 13583 ) 13584 ) 13585 elif t.isDictionary(): 13586 traceCases.append( 13587 CGCase( 13588 "e" + vars["name"], 13589 CGGeneric( 13590 "mValue.m%s.Value().TraceDictionary(trc);\n" 13591 % vars["name"] 13592 ), 13593 ) 13594 ) 13595 elif t.isSequence(): 13596 traceCases.append( 13597 CGCase( 13598 "e" + vars["name"], 13599 CGGeneric( 13600 "DoTraceSequence(trc, mValue.m%s.Value());\n" 13601 % vars["name"] 13602 ), 13603 ) 13604 ) 13605 elif t.isRecord(): 13606 traceCases.append( 13607 CGCase( 13608 "e" + vars["name"], 13609 CGGeneric( 13610 "TraceRecord(trc, mValue.m%s.Value());\n" % vars["name"] 13611 ), 13612 ) 13613 ) 13614 else: 13615 assert t.isSpiderMonkeyInterface() 13616 traceCases.append( 13617 CGCase( 13618 "e" + vars["name"], 13619 CGGeneric( 13620 "mValue.m%s.Value().TraceSelf(trc);\n" % vars["name"] 13621 ), 13622 ) 13623 ) 13624 13625 dtor = CGSwitch("mType", destructorCases).define() 13626 13627 methods.extend(getUnionInitMethods(self.type, isOwningUnion=self.ownsMembers)) 13628 methods.append( 13629 ClassMethod( 13630 "Uninit", 13631 "void", 13632 [], 13633 visibility="public", 13634 body=dtor, 13635 bodyInHeader=not self.ownsMembers, 13636 inline=not self.ownsMembers, 13637 ) 13638 ) 13639 13640 if not skipToJSVal: 13641 methods.append( 13642 ClassMethod( 13643 "ToJSVal", 13644 "bool", 13645 [ 13646 Argument("JSContext*", "cx"), 13647 Argument("JS::Handle<JSObject*>", "scopeObj"), 13648 Argument("JS::MutableHandle<JS::Value>", "rval"), 13649 ], 13650 body=CGSwitch( 13651 "mType", toJSValCases, default=CGGeneric("return false;\n") 13652 ).define(), 13653 const=True, 13654 ) 13655 ) 13656 13657 constructors = [ctor] 13658 selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers) 13659 if self.ownsMembers: 13660 if traceCases: 13661 traceBody = CGSwitch( 13662 "mType", traceCases, default=CGGeneric("") 13663 ).define() 13664 methods.append( 13665 ClassMethod( 13666 "TraceUnion", 13667 "void", 13668 [Argument("JSTracer*", "trc")], 13669 body=traceBody, 13670 ) 13671 ) 13672 13673 op_body = CGList([]) 13674 op_body.append(CGSwitch("aOther.mType", moveCases)) 13675 constructors.append( 13676 ClassConstructor( 13677 [Argument("%s&&" % selfName, "aOther")], 13678 visibility="public", 13679 body=op_body.define(), 13680 ) 13681 ) 13682 13683 methods.append( 13684 ClassMethod( 13685 "operator=", 13686 "%s&" % selfName, 13687 [Argument("%s&&" % selfName, "aOther")], 13688 body="this->~%s();\nnew (this) %s (std::move(aOther));\nreturn *this;\n" 13689 % (selfName, selfName), 13690 ) 13691 ) 13692 13693 body = dedent( 13694 """ 13695 MOZ_RELEASE_ASSERT(mType != eUninitialized); 13696 return static_cast<Type>(mType); 13697 """ 13698 ) 13699 methods.append( 13700 ClassMethod( 13701 "GetType", 13702 "Type", 13703 [], 13704 bodyInHeader=True, 13705 body=body, 13706 const=True, 13707 ) 13708 ) 13709 13710 if CGUnionStruct.isUnionCopyConstructible(self.type): 13711 constructors.append( 13712 ClassConstructor( 13713 [Argument("const %s&" % selfName, "aOther")], 13714 bodyInHeader=True, 13715 visibility="public", 13716 explicit=True, 13717 body="*this = aOther;\n", 13718 ) 13719 ) 13720 op_body = CGList([]) 13721 op_body.append(CGSwitch("aOther.mType", assignmentCases)) 13722 op_body.append(CGGeneric("return *this;\n")) 13723 methods.append( 13724 ClassMethod( 13725 "operator=", 13726 "%s&" % selfName, 13727 [Argument("const %s&" % selfName, "aOther")], 13728 body=op_body.define(), 13729 ) 13730 ) 13731 disallowCopyConstruction = False 13732 else: 13733 disallowCopyConstruction = True 13734 else: 13735 disallowCopyConstruction = True 13736 13737 if self.ownsMembers and idlTypeNeedsCycleCollection(self.type): 13738 friend = ( 13739 " friend void ImplCycleCollectionUnlink(%s& aUnion);\n" 13740 % CGUnionStruct.unionTypeName(self.type, True) 13741 ) 13742 else: 13743 friend = "" 13744 13745 enumValuesNoUninit = [x for x in enumValues if x != "eUninitialized"] 13746 13747 enums = [ 13748 ClassGroup( 13749 [ 13750 ClassEnum("TypeOrUninit", enumValues, visibility="private"), 13751 ClassEnum( 13752 "Type", 13753 enumValuesNoUninit, 13754 visibility="public", 13755 enumClass=True, 13756 values=["TypeOrUninit::" + x for x in enumValuesNoUninit], 13757 ), 13758 ] 13759 ) 13760 ] 13761 13762 bases = [ 13763 ClassBase("AllOwningUnionBase" if self.ownsMembers else "AllUnionBase") 13764 ] 13765 13766 typeAliases = [] 13767 bufferSourceTypes = [ 13768 t.name for t in self.type.flatMemberTypes if t.isBufferSource() 13769 ] 13770 if len(bufferSourceTypes) > 0: 13771 bases.append(ClassBase("UnionWithTypedArraysBase")) 13772 memberTypesCount = len(self.type.flatMemberTypes) 13773 if self.type.hasNullableType: 13774 memberTypesCount += 1 13775 13776 typeAliases = [ 13777 ClassUsingDeclaration( 13778 "ApplyToTypedArrays", 13779 "binding_detail::ApplyToTypedArraysHelper<%s, %s, %s>" 13780 % ( 13781 selfName, 13782 toStringBool(memberTypesCount > len(bufferSourceTypes)), 13783 ", ".join(bufferSourceTypes), 13784 ), 13785 ) 13786 ] 13787 13788 return CGClass( 13789 selfName, 13790 bases=bases, 13791 typeAliases=typeAliases, 13792 members=members, 13793 constructors=constructors, 13794 methods=methods, 13795 disallowCopyConstruction=disallowCopyConstruction, 13796 extradeclarations=friend, 13797 destructor=ClassDestructor( 13798 visibility="public", body="Uninit();\n", bodyInHeader=True 13799 ), 13800 enums=enums, 13801 unions=[ClassUnion("Value", unionValues, visibility="private")], 13802 ) 13803 13804 def getConversionToJS(self, templateVars, type): 13805 if type.isDictionary() and not type.inner.needsConversionToJS: 13806 # We won't be able to convert this dictionary to a JS value, nor 13807 # will we need to, since we don't need a ToJSVal method at all. 13808 return None 13809 13810 assert not type.nullable() # flatMemberTypes never has nullable types 13811 val = "mValue.m%(name)s.Value()" % templateVars 13812 wrapCode = wrapForType( 13813 type, 13814 self.descriptorProvider, 13815 { 13816 "jsvalRef": "rval", 13817 "jsvalHandle": "rval", 13818 "obj": "scopeObj", 13819 "result": val, 13820 "spiderMonkeyInterfacesAreStructs": True, 13821 }, 13822 ) 13823 return CGGeneric(wrapCode) 13824 13825 @staticmethod 13826 def isUnionCopyConstructible(type): 13827 return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes) 13828 13829 @staticmethod 13830 def unionTypeName(type, ownsMembers): 13831 """ 13832 Returns a string name for this known union type. 13833 """ 13834 assert type.isUnion() and not type.nullable() 13835 return ("Owning" if ownsMembers else "") + type.name 13836 13837 @staticmethod 13838 def unionTypeDecl(type, ownsMembers): 13839 """ 13840 Returns a string for declaring this possibly-nullable union type. 13841 """ 13842 assert type.isUnion() 13843 nullable = type.nullable() 13844 if nullable: 13845 type = type.inner 13846 decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers)) 13847 if nullable: 13848 decl = CGTemplatedType("Nullable", decl) 13849 return decl.define() 13850 13851 13852 class ClassItem: 13853 """Use with CGClass""" 13854 13855 def __init__(self, name, visibility): 13856 self.name = name 13857 self.visibility = visibility 13858 13859 def declare(self, cgClass): 13860 assert False 13861 13862 def define(self, cgClass): 13863 assert False 13864 13865 13866 class ClassBase(ClassItem): 13867 def __init__(self, name, visibility="public"): 13868 ClassItem.__init__(self, name, visibility) 13869 13870 def declare(self, cgClass): 13871 return "%s %s" % (self.visibility, self.name) 13872 13873 def define(self, cgClass): 13874 # Only in the header 13875 return "" 13876 13877 13878 class ClassMethod(ClassItem): 13879 def __init__( 13880 self, 13881 name, 13882 returnType, 13883 args, 13884 inline=False, 13885 static=False, 13886 virtual=False, 13887 const=False, 13888 bodyInHeader=False, 13889 templateArgs=None, 13890 visibility="public", 13891 body=None, 13892 breakAfterReturnDecl="\n", 13893 breakAfterSelf="\n", 13894 override=False, 13895 canRunScript=False, 13896 noDiscard=False, 13897 delete=False, 13898 ): 13899 """ 13900 override indicates whether to flag the method as override 13901 """ 13902 assert not override or virtual 13903 assert not (override and static) 13904 assert not (delete and body) 13905 self.returnType = returnType 13906 self.args = args 13907 self.inline = inline or bodyInHeader 13908 self.static = static 13909 self.virtual = virtual 13910 self.const = const 13911 self.bodyInHeader = bodyInHeader 13912 self.templateArgs = templateArgs 13913 self.body = body 13914 self.breakAfterReturnDecl = breakAfterReturnDecl 13915 self.breakAfterSelf = breakAfterSelf 13916 self.override = override 13917 self.canRunScript = canRunScript 13918 self.noDiscard = noDiscard 13919 self.delete = delete 13920 ClassItem.__init__(self, name, visibility) 13921 13922 def getDecorators(self, declaring): 13923 decorators = [] 13924 if self.noDiscard: 13925 decorators.append("[[nodiscard]]") 13926 if self.canRunScript: 13927 decorators.append("MOZ_CAN_RUN_SCRIPT") 13928 if self.inline: 13929 decorators.append("inline") 13930 if declaring: 13931 if self.static: 13932 decorators.append("static") 13933 if self.virtual and not self.override: 13934 decorators.append("virtual") 13935 if decorators: 13936 return " ".join(decorators) + " " 13937 return "" 13938 13939 def getBody(self): 13940 # Override me or pass a string to constructor 13941 assert self.body is not None 13942 return self.body 13943 13944 def declare(self, cgClass): 13945 templateClause = ( 13946 "template <%s>\n" % ", ".join(self.templateArgs) 13947 if self.bodyInHeader and self.templateArgs 13948 else "" 13949 ) 13950 args = ", ".join([a.declare() for a in self.args]) 13951 if self.delete: 13952 body = " = delete;\n" 13953 elif self.bodyInHeader: 13954 body = indent(self.getBody()) 13955 body = "\n{\n" + body + "}\n" 13956 else: 13957 body = ";\n" 13958 13959 return fill( 13960 "${templateClause}${decorators}${returnType}${breakAfterReturnDecl}" 13961 "${name}(${args})${const}${override}${body}" 13962 "${breakAfterSelf}", 13963 templateClause=templateClause, 13964 decorators=self.getDecorators(True), 13965 returnType=self.returnType, 13966 breakAfterReturnDecl=self.breakAfterReturnDecl, 13967 name=self.name, 13968 args=args, 13969 const=" const" if self.const else "", 13970 override=" override" if self.override else "", 13971 body=body, 13972 breakAfterSelf=self.breakAfterSelf, 13973 ) 13974 13975 def define(self, cgClass): 13976 if self.delete or self.bodyInHeader: 13977 return "" 13978 13979 templateArgs = cgClass.templateArgs 13980 if templateArgs: 13981 if cgClass.templateSpecialization: 13982 templateArgs = templateArgs[len(cgClass.templateSpecialization) :] 13983 13984 if templateArgs: 13985 templateClause = "template <%s>\n" % ", ".join( 13986 [str(a) for a in templateArgs] 13987 ) 13988 else: 13989 templateClause = "" 13990 13991 return fill( 13992 """ 13993 ${templateClause}${decorators}${returnType} 13994 ${className}::${name}(${args})${const} 13995 { 13996 $*{body} 13997 } 13998 """, 13999 templateClause=templateClause, 14000 decorators=self.getDecorators(False), 14001 returnType=self.returnType, 14002 className=cgClass.getNameString(), 14003 name=self.name, 14004 args=", ".join([a.define() for a in self.args]), 14005 const=" const" if self.const else "", 14006 body=self.getBody(), 14007 ) 14008 14009 14010 class ClassUsingDeclaration(ClassItem): 14011 """ 14012 Used for declaring an alias for a type in a CGClass 14013 14014 name is the name of the alias 14015 14016 type is the type to declare an alias of 14017 14018 visibility determines the visibility of the alias (public, 14019 protected, private), defaults to public. 14020 """ 14021 14022 def __init__(self, name, type, visibility="public"): 14023 self.type = type 14024 ClassItem.__init__(self, name, visibility) 14025 14026 def declare(self, cgClass): 14027 return "using %s = %s;\n\n" % (self.name, self.type) 14028 14029 def define(self, cgClass): 14030 return "" 14031 14032 14033 class ClassUsingFromBaseDeclaration(ClassItem): 14034 """ 14035 Used for importing a name from a base class into a CGClass 14036 14037 baseClass is the name of the base class to import the name from 14038 14039 name is the name to import 14040 14041 visibility determines the visibility of the name (public, 14042 protected, private), defaults to public. 14043 """ 14044 14045 def __init__(self, baseClass, name, visibility="public"): 14046 self.baseClass = baseClass 14047 ClassItem.__init__(self, name, visibility) 14048 14049 def declare(self, cgClass): 14050 return "using %s::%s;\n\n" % (self.baseClass, self.name) 14051 14052 def define(self, cgClass): 14053 return "" 14054 14055 14056 class ClassConstructor(ClassItem): 14057 """ 14058 Used for adding a constructor to a CGClass. 14059 14060 args is a list of Argument objects that are the arguments taken by the 14061 constructor. 14062 14063 inline should be True if the constructor should be marked inline. 14064 14065 bodyInHeader should be True if the body should be placed in the class 14066 declaration in the header. 14067 14068 default should be True if the definition of the constructor should be 14069 `= default;`. 14070 14071 visibility determines the visibility of the constructor (public, 14072 protected, private), defaults to private. 14073 14074 explicit should be True if the constructor should be marked explicit. 14075 14076 baseConstructors is a list of strings containing calls to base constructors, 14077 defaults to None. 14078 14079 body contains a string with the code for the constructor, defaults to empty. 14080 """ 14081 14082 def __init__( 14083 self, 14084 args, 14085 inline=False, 14086 bodyInHeader=False, 14087 default=False, 14088 visibility="private", 14089 explicit=False, 14090 constexpr=False, 14091 baseConstructors=None, 14092 body="", 14093 ): 14094 assert not (inline and constexpr) 14095 assert not (bodyInHeader and constexpr) 14096 assert not (default and body) 14097 self.args = args 14098 self.inline = inline or bodyInHeader 14099 self.bodyInHeader = bodyInHeader or constexpr or default 14100 self.default = default 14101 self.explicit = explicit 14102 self.constexpr = constexpr 14103 self.baseConstructors = baseConstructors or [] 14104 self.body = body 14105 ClassItem.__init__(self, None, visibility) 14106 14107 def getDecorators(self, declaring): 14108 decorators = [] 14109 if declaring: 14110 if self.explicit: 14111 decorators.append("explicit") 14112 if self.inline: 14113 decorators.append("inline") 14114 if self.constexpr: 14115 decorators.append("constexpr") 14116 if decorators: 14117 return " ".join(decorators) + " " 14118 return "" 14119 14120 def getInitializationList(self, cgClass): 14121 items = [str(c) for c in self.baseConstructors] 14122 for m in cgClass.members: 14123 if not m.static: 14124 initialize = m.body 14125 if initialize: 14126 items.append(m.name + "(" + initialize + ")") 14127 14128 if len(items) > 0: 14129 return "\n : " + ",\n ".join(items) 14130 return "" 14131 14132 def getBody(self): 14133 return self.body 14134 14135 def declare(self, cgClass): 14136 args = ", ".join([a.declare() for a in self.args]) 14137 if self.bodyInHeader: 14138 if self.default: 14139 body = " = default;\n" 14140 else: 14141 body = ( 14142 self.getInitializationList(cgClass) 14143 + "\n{\n" 14144 + indent(self.getBody()) 14145 + "}\n" 14146 ) 14147 else: 14148 body = ";\n" 14149 14150 return fill( 14151 "${decorators}${className}(${args})${body}\n", 14152 decorators=self.getDecorators(True), 14153 className=cgClass.getNameString(), 14154 args=args, 14155 body=body, 14156 ) 14157 14158 def define(self, cgClass): 14159 if self.bodyInHeader: 14160 return "" 14161 14162 return fill( 14163 """ 14164 ${decorators} 14165 ${className}::${className}(${args})${initializationList} 14166 { 14167 $*{body} 14168 } 14169 """, 14170 decorators=self.getDecorators(False), 14171 className=cgClass.getNameString(), 14172 args=", ".join([a.define() for a in self.args]), 14173 initializationList=self.getInitializationList(cgClass), 14174 body=self.getBody(), 14175 ) 14176 14177 14178 class ClassDestructor(ClassItem): 14179 """ 14180 Used for adding a destructor to a CGClass. 14181 14182 inline should be True if the destructor should be marked inline. 14183 14184 bodyInHeader should be True if the body should be placed in the class 14185 declaration in the header. 14186 14187 visibility determines the visibility of the destructor (public, 14188 protected, private), defaults to private. 14189 14190 body contains a string with the code for the destructor, defaults to empty. 14191 14192 virtual determines whether the destructor is virtual, defaults to False. 14193 """ 14194 14195 def __init__( 14196 self, 14197 inline=False, 14198 bodyInHeader=False, 14199 visibility="private", 14200 body="", 14201 virtual=False, 14202 ): 14203 self.inline = inline or bodyInHeader 14204 self.bodyInHeader = bodyInHeader 14205 self.body = body 14206 self.virtual = virtual 14207 ClassItem.__init__(self, None, visibility) 14208 14209 def getDecorators(self, declaring): 14210 decorators = [] 14211 if self.virtual and declaring: 14212 decorators.append("virtual") 14213 if self.inline and declaring: 14214 decorators.append("inline") 14215 if decorators: 14216 return " ".join(decorators) + " " 14217 return "" 14218 14219 def getBody(self): 14220 return self.body 14221 14222 def declare(self, cgClass): 14223 if self.bodyInHeader: 14224 body = "\n{\n" + indent(self.getBody()) + "}\n" 14225 else: 14226 body = ";\n" 14227 14228 return fill( 14229 "${decorators}~${className}()${body}\n", 14230 decorators=self.getDecorators(True), 14231 className=cgClass.getNameString(), 14232 body=body, 14233 ) 14234 14235 def define(self, cgClass): 14236 if self.bodyInHeader: 14237 return "" 14238 return fill( 14239 """ 14240 ${decorators} 14241 ${className}::~${className}() 14242 { 14243 $*{body} 14244 } 14245 """, 14246 decorators=self.getDecorators(False), 14247 className=cgClass.getNameString(), 14248 body=self.getBody(), 14249 ) 14250 14251 14252 class ClassMember(ClassItem): 14253 def __init__( 14254 self, 14255 name, 14256 type, 14257 visibility="private", 14258 static=False, 14259 body=None, 14260 hasIgnoreInitCheckFlag=False, 14261 ): 14262 self.type = type 14263 self.static = static 14264 self.body = body 14265 self.hasIgnoreInitCheckFlag = hasIgnoreInitCheckFlag 14266 ClassItem.__init__(self, name, visibility) 14267 14268 def declare(self, cgClass): 14269 return "%s%s%s %s;\n" % ( 14270 "static " if self.static else "", 14271 "MOZ_INIT_OUTSIDE_CTOR " if self.hasIgnoreInitCheckFlag else "", 14272 self.type, 14273 self.name, 14274 ) 14275 14276 def define(self, cgClass): 14277 if not self.static: 14278 return "" 14279 if self.body: 14280 body = " = " + self.body 14281 else: 14282 body = "" 14283 return "%s %s::%s%s;\n" % (self.type, cgClass.getNameString(), self.name, body) 14284 14285 14286 class ClassEnum(ClassItem): 14287 def __init__( 14288 self, name, entries, values=None, visibility="public", enumClass=False 14289 ): 14290 self.entries = entries 14291 self.values = values 14292 self.enumClass = enumClass 14293 ClassItem.__init__(self, name, visibility) 14294 14295 def declare(self, cgClass): 14296 entries = [] 14297 for i in range(0, len(self.entries)): 14298 if not self.values or i >= len(self.values): 14299 entry = "%s" % self.entries[i] 14300 else: 14301 entry = "%s = %s" % (self.entries[i], self.values[i]) 14302 entries.append(entry) 14303 14304 decl = ["enum"] 14305 self.enumClass and decl.append("class") 14306 self.name and decl.append(self.name) 14307 decl = " ".join(decl) 14308 14309 return "%s\n{\n%s\n};\n" % (decl, indent(",\n".join(entries))) 14310 14311 def define(self, cgClass): 14312 # Only goes in the header 14313 return "" 14314 14315 14316 class ClassUnion(ClassItem): 14317 def __init__(self, name, entries, visibility="public"): 14318 self.entries = [entry + ";\n" for entry in entries] 14319 ClassItem.__init__(self, name, visibility) 14320 14321 def declare(self, cgClass): 14322 return "union %s\n{\n%s\n};\n" % (self.name, indent("".join(self.entries))) 14323 14324 def define(self, cgClass): 14325 # Only goes in the header 14326 return "" 14327 14328 14329 class ClassGroup(ClassItem): 14330 def __init__(self, items): 14331 self.items = items 14332 ClassItem.__init__(self, "", items[0].visibility) 14333 14334 def declare(self, cgClass): 14335 assert False 14336 14337 def define(self, cgClass): 14338 assert False 14339 14340 14341 class CGClass(CGThing): 14342 def __init__( 14343 self, 14344 name, 14345 bases=[], 14346 typeAliases=[], 14347 members=[], 14348 constructors=[], 14349 destructor=None, 14350 methods=[], 14351 enums=[], 14352 unions=[], 14353 templateArgs=[], 14354 templateSpecialization=[], 14355 isStruct=False, 14356 disallowCopyConstruction=False, 14357 indent="", 14358 decorators="", 14359 extradeclarations="", 14360 extradefinitions="", 14361 ): 14362 CGThing.__init__(self) 14363 self.name = name 14364 self.bases = bases 14365 self.typeAliases = typeAliases 14366 self.members = members 14367 self.constructors = constructors 14368 # We store our single destructor in a list, since all of our 14369 # code wants lists of members. 14370 self.destructors = [destructor] if destructor else [] 14371 self.methods = methods 14372 self.enums = enums 14373 self.unions = unions 14374 self.templateArgs = templateArgs 14375 self.templateSpecialization = templateSpecialization 14376 self.isStruct = isStruct 14377 self.disallowCopyConstruction = disallowCopyConstruction 14378 self.indent = indent 14379 self.defaultVisibility = "public" if isStruct else "private" 14380 self.decorators = decorators 14381 self.extradeclarations = extradeclarations 14382 self.extradefinitions = extradefinitions 14383 14384 def getNameString(self): 14385 className = self.name 14386 if self.templateSpecialization: 14387 className += "<%s>" % ", ".join( 14388 [str(a) for a in self.templateSpecialization] 14389 ) 14390 return className 14391 14392 @staticmethod 14393 def flattenClassItemLists(l): 14394 for item in l: 14395 if isinstance(item, ClassGroup): 14396 for inner in CGClass.flattenClassItemLists(item.items): 14397 yield inner 14398 else: 14399 yield item 14400 14401 def declare(self): 14402 result = "" 14403 if self.templateArgs: 14404 templateArgs = [a.declare() for a in self.templateArgs] 14405 templateArgs = templateArgs[len(self.templateSpecialization) :] 14406 result += "template <%s>\n" % ",".join([str(a) for a in templateArgs]) 14407 14408 type = "struct" if self.isStruct else "class" 14409 14410 if self.templateSpecialization: 14411 specialization = "<%s>" % ", ".join( 14412 [str(a) for a in self.templateSpecialization] 14413 ) 14414 else: 14415 specialization = "" 14416 14417 myself = "%s %s%s" % (type, self.name, specialization) 14418 if self.decorators != "": 14419 myself += " " + self.decorators 14420 result += myself 14421 14422 if self.bases: 14423 inherit = " : " 14424 result += inherit 14425 # Grab our first base 14426 baseItems = [CGGeneric(b.declare(self)) for b in self.bases] 14427 bases = baseItems[:1] 14428 # Indent the rest 14429 bases.extend( 14430 CGIndenter(b, len(myself) + len(inherit)) for b in baseItems[1:] 14431 ) 14432 result += ",\n".join(b.define() for b in bases) 14433 14434 result += "\n{\n" 14435 14436 result += self.extradeclarations 14437 14438 def declareMembers(cgClass, memberList, defaultVisibility): 14439 members = {"private": [], "protected": [], "public": []} 14440 14441 for member in memberList: 14442 members[member.visibility].append(member) 14443 14444 if defaultVisibility == "public": 14445 order = ["public", "protected", "private"] 14446 else: 14447 order = ["private", "protected", "public"] 14448 14449 result = "" 14450 14451 lastVisibility = defaultVisibility 14452 for visibility in order: 14453 list = members[visibility] 14454 if list: 14455 for member in self.flattenClassItemLists(list): 14456 if member.visibility != lastVisibility: 14457 result += member.visibility + ":\n" 14458 result += indent(member.declare(cgClass)) 14459 lastVisibility = member.visibility 14460 return (result, lastVisibility) 14461 14462 if self.disallowCopyConstruction: 14463 14464 class DisallowedCopyConstructor(object): 14465 def __init__(self): 14466 self.visibility = "private" 14467 14468 def declare(self, cgClass): 14469 name = cgClass.getNameString() 14470 return ( 14471 "%s(const %s&) = delete;\n" 14472 "%s& operator=(const %s&) = delete;\n" 14473 % (name, name, name, name) 14474 ) 14475 14476 disallowedCopyConstructors = [DisallowedCopyConstructor()] 14477 else: 14478 disallowedCopyConstructors = [] 14479 14480 order = [ 14481 self.typeAliases, 14482 self.enums, 14483 self.unions, 14484 self.members, 14485 self.constructors + disallowedCopyConstructors, 14486 self.destructors, 14487 self.methods, 14488 ] 14489 14490 lastVisibility = self.defaultVisibility 14491 pieces = [] 14492 for memberList in order: 14493 code, lastVisibility = declareMembers(self, memberList, lastVisibility) 14494 14495 if code: 14496 code = code.rstrip() + "\n" # remove extra blank lines at the end 14497 pieces.append(code) 14498 14499 result += "\n".join(pieces) 14500 result += "};\n" 14501 result = indent(result, len(self.indent)) 14502 return result 14503 14504 def define(self): 14505 def defineMembers(cgClass, memberList, itemCount, separator=""): 14506 result = "" 14507 for member in self.flattenClassItemLists(memberList): 14508 if itemCount != 0: 14509 result = result + separator 14510 definition = member.define(cgClass) 14511 if definition: 14512 # Member variables would only produce empty lines here. 14513 result += definition 14514 itemCount += 1 14515 return (result, itemCount) 14516 14517 order = [ 14518 (self.members, ""), 14519 (self.constructors, "\n"), 14520 (self.destructors, "\n"), 14521 (self.methods, "\n"), 14522 ] 14523 14524 result = self.extradefinitions 14525 itemCount = 0 14526 for memberList, separator in order: 14527 memberString, itemCount = defineMembers( 14528 self, memberList, itemCount, separator 14529 ) 14530 result = result + memberString 14531 return result 14532 14533 14534 class CGResolveOwnPropertyViaResolve(CGAbstractBindingMethod): 14535 """ 14536 An implementation of Xray ResolveOwnProperty stuff for things that have a 14537 resolve hook. 14538 """ 14539 14540 def __init__(self, descriptor): 14541 args = [ 14542 Argument("JSContext*", "cx"), 14543 Argument("JS::Handle<JSObject*>", "wrapper"), 14544 Argument("JS::Handle<JSObject*>", "obj"), 14545 Argument("JS::Handle<jsid>", "id"), 14546 Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"), 14547 ] 14548 CGAbstractBindingMethod.__init__( 14549 self, 14550 descriptor, 14551 "ResolveOwnPropertyViaResolve", 14552 args, 14553 getThisObj="", 14554 callArgs="", 14555 ) 14556 14557 def generate_code(self): 14558 return CGGeneric( 14559 dedent( 14560 """ 14561 { 14562 // Since we're dealing with an Xray, do the resolve on the 14563 // underlying object first. That gives it a chance to 14564 // define properties on the actual object as needed, and 14565 // then use the fact that it created the objects as a flag 14566 // to avoid re-resolving the properties if someone deletes 14567 // them. 14568 JSAutoRealm ar(cx, obj); 14569 JS_MarkCrossZoneId(cx, id); 14570 JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> objDesc(cx); 14571 if (!self->DoResolve(cx, obj, id, &objDesc)) { 14572 return false; 14573 } 14574 // If desc->value() is undefined, then the DoResolve call 14575 // has already defined the property on the object. Don't 14576 // try to also define it. 14577 if (objDesc.isSome() && 14578 !objDesc->value().isUndefined()) { 14579 JS::Rooted<JS::PropertyDescriptor> defineDesc(cx, *objDesc); 14580 if (!JS_DefinePropertyById(cx, obj, id, defineDesc)) { 14581 return false; 14582 } 14583 } 14584 } 14585 return self->DoResolve(cx, wrapper, id, desc); 14586 """ 14587 ) 14588 ) 14589 14590 14591 class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod): 14592 """ 14593 An implementation of Xray EnumerateOwnProperties stuff for things 14594 that have a resolve hook. 14595 """ 14596 14597 def __init__(self, descriptor): 14598 args = [ 14599 Argument("JSContext*", "cx"), 14600 Argument("JS::Handle<JSObject*>", "wrapper"), 14601 Argument("JS::Handle<JSObject*>", "obj"), 14602 Argument("JS::MutableHandleVector<jsid>", "props"), 14603 ] 14604 CGAbstractBindingMethod.__init__( 14605 self, 14606 descriptor, 14607 "EnumerateOwnPropertiesViaGetOwnPropertyNames", 14608 args, 14609 getThisObj="", 14610 callArgs="", 14611 ) 14612 14613 def generate_code(self): 14614 return CGGeneric( 14615 dedent( 14616 """ 14617 FastErrorResult rv; 14618 // This wants all own props, not just enumerable ones. 14619 self->GetOwnPropertyNames(cx, props, false, rv); 14620 if (rv.MaybeSetPendingException(cx)) { 14621 return false; 14622 } 14623 return true; 14624 """ 14625 ) 14626 ) 14627 14628 14629 class CGPrototypeTraitsClass(CGClass): 14630 def __init__(self, descriptor, indent=""): 14631 templateArgs = [Argument("prototypes::ID", "PrototypeID")] 14632 templateSpecialization = ["prototypes::id::" + descriptor.name] 14633 enums = [ClassEnum("", ["Depth"], [descriptor.interface.inheritanceDepth()])] 14634 CGClass.__init__( 14635 self, 14636 "PrototypeTraits", 14637 indent=indent, 14638 templateArgs=templateArgs, 14639 templateSpecialization=templateSpecialization, 14640 enums=enums, 14641 isStruct=True, 14642 ) 14643 14644 def deps(self): 14645 return set() 14646 14647 14648 class CGClassForwardDeclare(CGThing): 14649 def __init__(self, name, isStruct=False): 14650 CGThing.__init__(self) 14651 self.name = name 14652 self.isStruct = isStruct 14653 14654 def declare(self): 14655 type = "struct" if self.isStruct else "class" 14656 return "%s %s;\n" % (type, self.name) 14657 14658 def define(self): 14659 # Header only 14660 return "" 14661 14662 def forward_declare(self): 14663 return self.declare() 14664 14665 def deps(self): 14666 return set() 14667 14668 14669 class CGProxySpecialOperation(CGPerSignatureCall): 14670 """ 14671 Base class for classes for calling an indexed or named special operation 14672 (don't use this directly, use the derived classes below). 14673 14674 If checkFound is False, will just assert that the prop is found instead of 14675 checking that it is before wrapping the value. 14676 14677 resultVar: See the docstring for CGCallGenerator. 14678 14679 foundVar: For getters and deleters, the generated code can also set a bool 14680 variable, declared by the caller, if the given indexed or named property 14681 already existed. If the caller wants this, it should pass the name of the 14682 bool variable as the foundVar keyword argument to the constructor. The 14683 caller is responsible for declaring the variable and initializing it to 14684 false. 14685 """ 14686 14687 def __init__( 14688 self, 14689 descriptor, 14690 operation, 14691 checkFound=True, 14692 argumentHandleValue=None, 14693 resultVar=None, 14694 foundVar=None, 14695 ): 14696 self.checkFound = checkFound 14697 self.foundVar = foundVar or "found" 14698 14699 nativeName = MakeNativeName(descriptor.binaryNameFor(operation, False)) 14700 operation = descriptor.operations[operation] 14701 assert len(operation.signatures()) == 1 14702 signature = operation.signatures()[0] 14703 14704 returnType, arguments = signature 14705 14706 # We pass len(arguments) as the final argument so that the 14707 # CGPerSignatureCall won't do any argument conversion of its own. 14708 CGPerSignatureCall.__init__( 14709 self, 14710 returnType, 14711 arguments, 14712 nativeName, 14713 False, 14714 descriptor, 14715 operation, 14716 len(arguments), 14717 resultVar=resultVar, 14718 objectName="proxy", 14719 ) 14720 14721 if operation.isSetter(): 14722 # arguments[0] is the index or name of the item that we're setting. 14723 argument = arguments[1] 14724 info = getJSToNativeConversionInfo( 14725 argument.type, 14726 descriptor, 14727 sourceDescription=( 14728 "value being assigned to %s setter" 14729 % descriptor.interface.identifier.name 14730 ), 14731 ) 14732 if argumentHandleValue is None: 14733 argumentHandleValue = "desc.value()" 14734 rootedValue = fill( 14735 """ 14736 JS::Rooted<JS::Value> rootedValue(cx, ${argumentHandleValue}); 14737 """, 14738 argumentHandleValue=argumentHandleValue, 14739 ) 14740 templateValues = { 14741 "declName": argument.identifier.name, 14742 "holderName": argument.identifier.name + "_holder", 14743 "val": argumentHandleValue, 14744 "maybeMutableVal": "&rootedValue", 14745 "obj": "obj", 14746 "passedToJSImpl": "false", 14747 } 14748 self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues)) 14749 # rootedValue needs to come before the conversion, so we 14750 # need to prepend it last. 14751 self.cgRoot.prepend(CGGeneric(rootedValue)) 14752 elif operation.isGetter() or operation.isDeleter(): 14753 if foundVar is None: 14754 self.cgRoot.prepend(CGGeneric("bool found = false;\n")) 14755 14756 def getArguments(self): 14757 args = [(a, a.identifier.name) for a in self.arguments] 14758 if self.idlNode.isGetter() or self.idlNode.isDeleter(): 14759 args.append( 14760 ( 14761 FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean]), 14762 self.foundVar, 14763 ) 14764 ) 14765 return args 14766 14767 def wrap_return_value(self): 14768 if not self.idlNode.isGetter() or self.templateValues is None: 14769 return "" 14770 14771 wrap = CGGeneric( 14772 wrapForType(self.returnType, self.descriptor, self.templateValues) 14773 ) 14774 if self.checkFound: 14775 wrap = CGIfWrapper(wrap, self.foundVar) 14776 else: 14777 wrap = CGList([CGGeneric("MOZ_ASSERT(" + self.foundVar + ");\n"), wrap]) 14778 return "\n" + wrap.define() 14779 14780 14781 class CGProxyIndexedOperation(CGProxySpecialOperation): 14782 """ 14783 Class to generate a call to an indexed operation. 14784 14785 If doUnwrap is False, the caller is responsible for making sure a variable 14786 named 'self' holds the C++ object somewhere where the code we generate 14787 will see it. 14788 14789 If checkFound is False, will just assert that the prop is found instead of 14790 checking that it is before wrapping the value. 14791 14792 resultVar: See the docstring for CGCallGenerator. 14793 14794 foundVar: See the docstring for CGProxySpecialOperation. 14795 """ 14796 14797 def __init__( 14798 self, 14799 descriptor, 14800 name, 14801 doUnwrap=True, 14802 checkFound=True, 14803 argumentHandleValue=None, 14804 resultVar=None, 14805 foundVar=None, 14806 ): 14807 self.doUnwrap = doUnwrap 14808 CGProxySpecialOperation.__init__( 14809 self, 14810 descriptor, 14811 name, 14812 checkFound, 14813 argumentHandleValue=argumentHandleValue, 14814 resultVar=resultVar, 14815 foundVar=foundVar, 14816 ) 14817 14818 def define(self): 14819 # Our first argument is the id we're getting. 14820 argName = self.arguments[0].identifier.name 14821 if argName == "index": 14822 # We already have our index in a variable with that name 14823 setIndex = "" 14824 else: 14825 setIndex = "uint32_t %s = index;\n" % argName 14826 if self.doUnwrap: 14827 unwrap = "%s* self = UnwrapProxy(proxy);\n" % self.descriptor.nativeType 14828 else: 14829 unwrap = "" 14830 return setIndex + unwrap + CGProxySpecialOperation.define(self) 14831 14832 14833 class CGProxyIndexedGetter(CGProxyIndexedOperation): 14834 """ 14835 Class to generate a call to an indexed getter. If templateValues is not None 14836 the returned value will be wrapped with wrapForType using templateValues. 14837 14838 If doUnwrap is False, the caller is responsible for making sure a variable 14839 named 'self' holds the C++ object somewhere where the code we generate 14840 will see it. 14841 14842 If checkFound is False, will just assert that the prop is found instead of 14843 checking that it is before wrapping the value. 14844 14845 foundVar: See the docstring for CGProxySpecialOperation. 14846 """ 14847 14848 def __init__( 14849 self, 14850 descriptor, 14851 templateValues=None, 14852 doUnwrap=True, 14853 checkFound=True, 14854 foundVar=None, 14855 ): 14856 self.templateValues = templateValues 14857 CGProxyIndexedOperation.__init__( 14858 self, descriptor, "IndexedGetter", doUnwrap, checkFound, foundVar=foundVar 14859 ) 14860 14861 14862 class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter): 14863 """ 14864 Class to generate a call that checks whether an indexed property exists. 14865 14866 For now, we just delegate to CGProxyIndexedGetter 14867 14868 foundVar: See the docstring for CGProxySpecialOperation. 14869 """ 14870 14871 def __init__(self, descriptor, foundVar): 14872 CGProxyIndexedGetter.__init__(self, descriptor, foundVar=foundVar) 14873 self.cgRoot.append(CGGeneric("(void)result;\n")) 14874 14875 14876 class CGProxyIndexedSetter(CGProxyIndexedOperation): 14877 """ 14878 Class to generate a call to an indexed setter. 14879 """ 14880 14881 def __init__(self, descriptor, argumentHandleValue=None): 14882 CGProxyIndexedOperation.__init__( 14883 self, descriptor, "IndexedSetter", argumentHandleValue=argumentHandleValue 14884 ) 14885 14886 14887 class CGProxyNamedOperation(CGProxySpecialOperation): 14888 """ 14889 Class to generate a call to a named operation. 14890 14891 'value' is the jsval to use for the name; None indicates that it should be 14892 gotten from the property id. 14893 14894 resultVar: See the docstring for CGCallGenerator. 14895 14896 foundVar: See the docstring for CGProxySpecialOperation. 14897 14898 tailCode: if we end up with a non-symbol string id, run this code after 14899 we do all our other work. 14900 """ 14901 14902 def __init__( 14903 self, 14904 descriptor, 14905 name, 14906 value=None, 14907 argumentHandleValue=None, 14908 resultVar=None, 14909 foundVar=None, 14910 tailCode="", 14911 ): 14912 CGProxySpecialOperation.__init__( 14913 self, 14914 descriptor, 14915 name, 14916 argumentHandleValue=argumentHandleValue, 14917 resultVar=resultVar, 14918 foundVar=foundVar, 14919 ) 14920 self.value = value 14921 self.tailCode = tailCode 14922 14923 def define(self): 14924 # Our first argument is the id we're getting. 14925 argName = self.arguments[0].identifier.name 14926 if argName == "id": 14927 # deal with the name collision 14928 decls = "JS::Rooted<jsid> id_(cx, id);\n" 14929 idName = "id_" 14930 else: 14931 decls = "" 14932 idName = "id" 14933 14934 decls += "FakeString<char16_t> %s;\n" % argName 14935 14936 main = fill( 14937 """ 14938 ${nativeType}* self = UnwrapProxy(proxy); 14939 $*{op} 14940 $*{tailCode} 14941 """, 14942 nativeType=self.descriptor.nativeType, 14943 op=CGProxySpecialOperation.define(self), 14944 tailCode=self.tailCode, 14945 ) 14946 14947 if self.value is None: 14948 return fill( 14949 """ 14950 $*{decls} 14951 bool isSymbol; 14952 if (!ConvertIdToString(cx, ${idName}, ${argName}, isSymbol)) { 14953 return false; 14954 } 14955 if (!isSymbol) { 14956 $*{main} 14957 } 14958 """, 14959 decls=decls, 14960 idName=idName, 14961 argName=argName, 14962 main=main, 14963 ) 14964 14965 # Sadly, we have to set up nameVal even if we have an atom id, 14966 # because we don't know for sure, and we can end up needing it 14967 # so it needs to be higher up the stack. Using a Maybe here 14968 # seems like probable overkill. 14969 return fill( 14970 """ 14971 $*{decls} 14972 JS::Rooted<JS::Value> nameVal(cx, ${value}); 14973 if (!nameVal.isSymbol()) { 14974 if (!ConvertJSValueToString(cx, nameVal, eStringify, eStringify, 14975 ${argName})) { 14976 return false; 14977 } 14978 $*{main} 14979 } 14980 """, 14981 decls=decls, 14982 value=self.value, 14983 argName=argName, 14984 main=main, 14985 ) 14986 14987 14988 class CGProxyNamedGetter(CGProxyNamedOperation): 14989 """ 14990 Class to generate a call to an named getter. If templateValues is not None 14991 the returned value will be wrapped with wrapForType using templateValues. 14992 'value' is the jsval to use for the name; None indicates that it should be 14993 gotten from the property id. 14994 14995 foundVar: See the docstring for CGProxySpecialOperation. 14996 """ 14997 14998 def __init__(self, descriptor, templateValues=None, value=None, foundVar=None): 14999 self.templateValues = templateValues 15000 CGProxyNamedOperation.__init__( 15001 self, descriptor, "NamedGetter", value, foundVar=foundVar 15002 ) 15003 15004 15005 class CGProxyNamedPresenceChecker(CGProxyNamedGetter): 15006 """ 15007 Class to generate a call that checks whether a named property exists. 15008 15009 For now, we just delegate to CGProxyNamedGetter 15010 15011 foundVar: See the docstring for CGProxySpecialOperation. 15012 """ 15013 15014 def __init__(self, descriptor, foundVar=None): 15015 CGProxyNamedGetter.__init__(self, descriptor, foundVar=foundVar) 15016 self.cgRoot.append(CGGeneric("(void)result;\n")) 15017 15018 15019 class CGProxyNamedSetter(CGProxyNamedOperation): 15020 """ 15021 Class to generate a call to a named setter. 15022 """ 15023 15024 def __init__(self, descriptor, tailCode, argumentHandleValue=None): 15025 CGProxyNamedOperation.__init__( 15026 self, 15027 descriptor, 15028 "NamedSetter", 15029 argumentHandleValue=argumentHandleValue, 15030 tailCode=tailCode, 15031 ) 15032 15033 15034 class CGProxyNamedDeleter(CGProxyNamedOperation): 15035 """ 15036 Class to generate a call to a named deleter. 15037 15038 resultVar: See the docstring for CGCallGenerator. 15039 15040 foundVar: See the docstring for CGProxySpecialOperation. 15041 """ 15042 15043 def __init__(self, descriptor, resultVar=None, foundVar=None): 15044 CGProxyNamedOperation.__init__( 15045 self, descriptor, "NamedDeleter", resultVar=resultVar, foundVar=foundVar 15046 ) 15047 15048 15049 class CGProxyIsProxy(CGAbstractMethod): 15050 def __init__(self, descriptor): 15051 args = [Argument("JSObject*", "obj")] 15052 CGAbstractMethod.__init__( 15053 self, descriptor, "IsProxy", "bool", args, alwaysInline=True 15054 ) 15055 15056 def declare(self): 15057 return "" 15058 15059 def definition_body(self): 15060 return "return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();\n" 15061 15062 15063 class CGProxyUnwrap(CGAbstractMethod): 15064 def __init__(self, descriptor): 15065 args = [Argument("JSObject*", "obj")] 15066 CGAbstractMethod.__init__( 15067 self, 15068 descriptor, 15069 "UnwrapProxy", 15070 descriptor.nativeType + "*", 15071 args, 15072 alwaysInline=True, 15073 ) 15074 15075 def declare(self): 15076 return "" 15077 15078 def definition_body(self): 15079 return fill( 15080 """ 15081 MOZ_ASSERT(js::IsProxy(obj)); 15082 if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) { 15083 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj)); 15084 obj = js::UncheckedUnwrap(obj); 15085 } 15086 MOZ_ASSERT(IsProxy(obj)); 15087 return static_cast<${type}*>(js::GetProxyReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate()); 15088 """, 15089 type=self.descriptor.nativeType, 15090 ) 15091 15092 15093 MISSING_PROP_PREF = "dom.missing_prop_counters.enabled" 15094 15095 15096 def missingPropUseCountersForDescriptor(desc): 15097 if not desc.needsMissingPropUseCounters: 15098 return "" 15099 15100 return fill( 15101 """ 15102 if (StaticPrefs::${pref}() && id.isAtom()) { 15103 CountMaybeMissingProperty(proxy, id); 15104 } 15105 15106 """, 15107 pref=prefIdentifier(MISSING_PROP_PREF), 15108 ) 15109 15110 15111 def findAncestorWithInstrumentedProps(desc): 15112 """ 15113 Find an ancestor of desc.interface (not including desc.interface 15114 itself) that has instrumented properties on it. May return None 15115 if there is no such ancestor. 15116 """ 15117 ancestor = desc.interface.parent 15118 while ancestor: 15119 if ancestor.getExtendedAttribute("InstrumentedProps"): 15120 return ancestor 15121 ancestor = ancestor.parent 15122 return None 15123 15124 15125 class CGAbstractPropertySwitchMethod(CGAbstractMethod): 15126 def __init__(self, *args): 15127 """ 15128 Create an optimized switch tree for matching (property) names. 15129 """ 15130 15131 CGAbstractMethod.__init__(self, *args) 15132 15133 def gen_switch(self, switchDecriptor): 15134 """ 15135 Generate a switch from the switch descriptor. The descriptor 15136 dictionary must have the following properties: 15137 15138 1) A "precondition" property that contains code to run before the 15139 switch statement. Its value is a string. 15140 2) A "condition" property for the condition. Its value is a string. 15141 3) A "cases" property. Its value is an object that has property names 15142 corresponding to the case labels. The values of those properties 15143 are either new switch descriptor dictionaries (which will then 15144 generate nested switches) or strings to use for case bodies. 15145 """ 15146 cases = [] 15147 for label, body in sorted(switchDecriptor["cases"].items()): 15148 if isinstance(body, dict): 15149 body = self.gen_switch(body) 15150 cases.append( 15151 fill( 15152 """ 15153 case ${label}: { 15154 $*{body} 15155 break; 15156 } 15157 """, 15158 label=label, 15159 body=body, 15160 ) 15161 ) 15162 return fill( 15163 """ 15164 $*{precondition} 15165 switch (${condition}) { 15166 $*{cases} 15167 } 15168 """, 15169 precondition=switchDecriptor["precondition"], 15170 condition=switchDecriptor["condition"], 15171 cases="".join(cases), 15172 ) 15173 15174 def charSwitch( 15175 self, props, charIndex, initializeChars, charIndexer, propertyMatcher 15176 ): 15177 """ 15178 Create a switch for the given props, based on the first char where 15179 they start to differ at index charIndex or more. 15180 15181 initializeChars is used for initializing the characters should be matched. 15182 charIndexer is used for matching a specific character. Must return a single char. 15183 propertyMatcher checks whether the given string really matches the property name. 15184 15185 Incoming props should be a sorted list. 15186 """ 15187 if len(props) == 1: 15188 # We're down to one string: just check whether we match it. 15189 return propertyMatcher(props[0]) 15190 15191 switch = dict() 15192 if charIndex == 0: 15193 switch["precondition"] = initializeChars 15194 else: 15195 switch["precondition"] = "" 15196 15197 # Find the first place where we might actually have a difference. 15198 while all(prop[charIndex] == props[0][charIndex] for prop in props): 15199 charIndex += 1 15200 15201 switch["condition"] = charIndexer(charIndex) 15202 switch["cases"] = dict() 15203 current_props = None 15204 curChar = None 15205 idx = 0 15206 while idx < len(props): 15207 nextChar = "'%s'" % props[idx][charIndex] 15208 if nextChar != curChar: 15209 if curChar: 15210 switch["cases"][curChar] = self.charSwitch( 15211 current_props, 15212 charIndex + 1, 15213 initializeChars, 15214 charIndexer, 15215 propertyMatcher, 15216 ) 15217 current_props = [] 15218 curChar = nextChar 15219 current_props.append(props[idx]) 15220 idx += 1 15221 switch["cases"][curChar] = self.charSwitch( 15222 current_props, charIndex + 1, initializeChars, charIndexer, propertyMatcher 15223 ) 15224 return switch 15225 15226 15227 class CGCountMaybeMissingProperty(CGAbstractPropertySwitchMethod): 15228 def __init__(self, descriptor): 15229 """ 15230 Returns whether we counted the property involved. 15231 """ 15232 CGAbstractPropertySwitchMethod.__init__( 15233 self, 15234 descriptor, 15235 "CountMaybeMissingProperty", 15236 "bool", 15237 [ 15238 Argument("JS::Handle<JSObject*>", "proxy"), 15239 Argument("JS::Handle<jsid>", "id"), 15240 ], 15241 ) 15242 15243 def definition_body(self): 15244 ancestor = findAncestorWithInstrumentedProps(self.descriptor) 15245 15246 if ancestor: 15247 body = fill( 15248 """ 15249 if (${ancestor}_Binding::CountMaybeMissingProperty(proxy, id)) { 15250 return true; 15251 } 15252 15253 """, 15254 ancestor=ancestor.identifier.name, 15255 ) 15256 else: 15257 body = "" 15258 15259 instrumentedProps = self.descriptor.instrumentedProps 15260 if not instrumentedProps: 15261 return body + dedent( 15262 """ 15263 return false; 15264 """ 15265 ) 15266 15267 initializeChars = "StringIdChars chars(nogc, str);\n" 15268 charIndexer = lambda index: "chars[%d]" % index 15269 15270 def property_matcher(name): 15271 return fill( 15272 """ 15273 if (JS_LinearStringEqualsLiteral(str, "${name}")) { 15274 counter.emplace(eUseCounter_${iface}_${name}); 15275 } 15276 """, 15277 iface=self.descriptor.name, 15278 name=name, 15279 ) 15280 15281 lengths = set(len(prop) for prop in instrumentedProps) 15282 switchDesc = {"condition": "JS::GetLinearStringLength(str)", "precondition": ""} 15283 switchDesc["cases"] = dict() 15284 for length in sorted(lengths, key=int): 15285 switchDesc["cases"][length] = self.charSwitch( 15286 list(sorted(prop for prop in instrumentedProps if len(prop) == length)), 15287 0, 15288 initializeChars, 15289 charIndexer, 15290 property_matcher, 15291 ) 15292 15293 return body + fill( 15294 """ 15295 MOZ_ASSERT(StaticPrefs::${pref}() && id.isAtom()); 15296 Maybe<UseCounter> counter; 15297 { 15298 // Scope for our no-GC section, so we don't need to rely on SetUseCounter not GCing. 15299 JS::AutoCheckCannotGC nogc; 15300 JSLinearString* str = JS::AtomToLinearString(id.toAtom()); 15301 // Don't waste time fetching the chars until we've done the length switch. 15302 $*{switch} 15303 } 15304 if (counter) { 15305 SetUseCounter(proxy, *counter); 15306 return true; 15307 } 15308 15309 return false; 15310 """, 15311 pref=prefIdentifier(MISSING_PROP_PREF), 15312 switch=self.gen_switch(switchDesc), 15313 ) 15314 15315 15316 class CGInterfaceHasProperty(CGAbstractPropertySwitchMethod): 15317 def __init__(self, descriptor): 15318 """ 15319 Returns whether the given string a property of this or any of its 15320 ancestors interfaces. 15321 """ 15322 CGAbstractPropertySwitchMethod.__init__( 15323 self, 15324 descriptor, 15325 "InterfaceHasProperty", 15326 "bool", 15327 [ 15328 Argument("const nsAString&", "name"), 15329 ], 15330 ) 15331 15332 def definition_body(self): 15333 # Include all properties of the Object.prototype. 15334 names = set(JS_OBJECT_PROTOTYPE_PROPERTIES) 15335 15336 iface = self.descriptor.interface 15337 while iface: 15338 for m in iface.members: 15339 if isChromeOnly(m): 15340 continue 15341 15342 names.add(m.identifier.name) 15343 15344 iface = iface.parent 15345 15346 initializeChars = "" 15347 charIndexer = lambda index: "name[%d]" % index 15348 15349 def property_matcher(name): 15350 return fill( 15351 """ 15352 if (name == u\"${name}\"_ns) { 15353 return true; 15354 } 15355 """, 15356 name=name, 15357 ) 15358 15359 lengths = set(len(name) for name in names) 15360 switchDesc = {"condition": "name.Length()", "precondition": ""} 15361 switchDesc["cases"] = dict() 15362 for length in sorted(lengths): 15363 switchDesc["cases"][length] = self.charSwitch( 15364 list(sorted(name for name in names if len(name) == length)), 15365 0, 15366 initializeChars, 15367 charIndexer, 15368 property_matcher, 15369 ) 15370 15371 return fill( 15372 """ 15373 $*{switch} 15374 15375 return false; 15376 """, 15377 switch=self.gen_switch(switchDesc), 15378 ) 15379 15380 15381 class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod): 15382 def __init__(self, descriptor): 15383 args = [ 15384 Argument("JSContext*", "cx"), 15385 Argument("JS::Handle<JSObject*>", "proxy"), 15386 Argument("JS::Handle<jsid>", "id"), 15387 Argument("bool", "ignoreNamedProps"), 15388 Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"), 15389 ] 15390 ClassMethod.__init__( 15391 self, 15392 "getOwnPropDescriptor", 15393 "bool", 15394 args, 15395 virtual=True, 15396 override=True, 15397 const=True, 15398 ) 15399 self.descriptor = descriptor 15400 15401 def getBody(self): 15402 indexedSetter = self.descriptor.operations["IndexedSetter"] 15403 15404 if self.descriptor.isMaybeCrossOriginObject(): 15405 xrayDecl = dedent( 15406 """ 15407 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy)); 15408 MOZ_ASSERT(IsPlatformObjectSameOrigin(cx, proxy), 15409 "getOwnPropertyDescriptor() and set() should have dealt"); 15410 MOZ_ASSERT(js::IsObjectInContextCompartment(proxy, cx), 15411 "getOwnPropertyDescriptor() and set() should have dealt"); 15412 15413 """ 15414 ) 15415 xrayCheck = "" 15416 else: 15417 xrayDecl = "bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);\n" 15418 xrayCheck = "!isXray &&" 15419 15420 if self.descriptor.supportsIndexedProperties(): 15421 attributes = [ 15422 "JS::PropertyAttribute::Configurable", 15423 "JS::PropertyAttribute::Enumerable", 15424 ] 15425 if indexedSetter is not None: 15426 attributes.append("JS::PropertyAttribute::Writable") 15427 setDescriptor = ( 15428 "desc.set(mozilla::Some(JS::PropertyDescriptor::Data(value, { %s })));\nreturn true;\n" 15429 % ", ".join(attributes) 15430 ) 15431 templateValues = { 15432 "jsvalRef": "value", 15433 "jsvalHandle": "&value", 15434 "obj": "proxy", 15435 "successCode": setDescriptor, 15436 } 15437 getIndexed = fill( 15438 """ 15439 uint32_t index = GetArrayIndexFromId(id); 15440 if (IsArrayIndex(index)) { 15441 JS::Rooted<JS::Value> value(cx); 15442 $*{callGetter} 15443 } 15444 15445 """, 15446 callGetter=CGProxyIndexedGetter( 15447 self.descriptor, templateValues 15448 ).define(), 15449 ) 15450 else: 15451 getIndexed = "" 15452 15453 missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor) 15454 15455 if self.descriptor.supportsNamedProperties(): 15456 operations = self.descriptor.operations 15457 attributes = ["JS::PropertyAttribute::Configurable"] 15458 if self.descriptor.namedPropertiesEnumerable: 15459 attributes.append("JS::PropertyAttribute::Enumerable") 15460 if operations["NamedSetter"] is not None: 15461 attributes.append("JS::PropertyAttribute::Writable") 15462 setDescriptor = ( 15463 "desc.set(mozilla::Some(JS::PropertyDescriptor::Data(value, { %s })));\nreturn true;\n" 15464 % ", ".join(attributes) 15465 ) 15466 templateValues = { 15467 "jsvalRef": "value", 15468 "jsvalHandle": "&value", 15469 "obj": "proxy", 15470 "successCode": setDescriptor, 15471 } 15472 15473 computeCondition = dedent( 15474 """ 15475 bool hasOnProto; 15476 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) { 15477 return false; 15478 } 15479 callNamedGetter = !hasOnProto; 15480 """ 15481 ) 15482 if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"): 15483 computeCondition = fill( 15484 """ 15485 if (!isXray) { 15486 callNamedGetter = true; 15487 } else { 15488 $*{hasOnProto} 15489 } 15490 """, 15491 hasOnProto=computeCondition, 15492 ) 15493 15494 outerCondition = "!ignoreNamedProps" 15495 if self.descriptor.supportsIndexedProperties(): 15496 outerCondition = "!IsArrayIndex(index) && " + outerCondition 15497 15498 namedGetCode = CGProxyNamedGetter(self.descriptor, templateValues).define() 15499 namedGet = fill( 15500 """ 15501 bool callNamedGetter = false; 15502 if (${outerCondition}) { 15503 $*{computeCondition} 15504 } 15505 if (callNamedGetter) { 15506 JS::Rooted<JS::Value> value(cx); 15507 $*{namedGetCode} 15508 } 15509 """, 15510 outerCondition=outerCondition, 15511 computeCondition=computeCondition, 15512 namedGetCode=namedGetCode, 15513 ) 15514 namedGet += "\n" 15515 else: 15516 namedGet = "" 15517 15518 return fill( 15519 """ 15520 $*{xrayDecl} 15521 $*{getIndexed} 15522 $*{missingPropUseCounters} 15523 JS::Rooted<JSObject*> expando(cx); 15524 if (${xrayCheck}(expando = GetExpandoObject(proxy))) { 15525 if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc)) { 15526 return false; 15527 } 15528 if (desc.isSome()) { 15529 return true; 15530 } 15531 } 15532 15533 $*{namedGet} 15534 desc.reset(); 15535 return true; 15536 """, 15537 xrayDecl=xrayDecl, 15538 xrayCheck=xrayCheck, 15539 getIndexed=getIndexed, 15540 missingPropUseCounters=missingPropUseCounters, 15541 namedGet=namedGet, 15542 ) 15543 15544 15545 class CGDOMJSProxyHandler_defineProperty(ClassMethod): 15546 def __init__(self, descriptor): 15547 # The usual convention is to name the ObjectOpResult out-parameter 15548 # `result`, but that name is a bit overloaded around here. 15549 args = [ 15550 Argument("JSContext*", "cx_"), 15551 Argument("JS::Handle<JSObject*>", "proxy"), 15552 Argument("JS::Handle<jsid>", "id"), 15553 Argument("JS::Handle<JS::PropertyDescriptor>", "desc"), 15554 Argument("JS::ObjectOpResult&", "opresult"), 15555 Argument("bool*", "done"), 15556 ] 15557 ClassMethod.__init__( 15558 self, 15559 "defineProperty", 15560 "bool", 15561 args, 15562 virtual=True, 15563 override=True, 15564 const=True, 15565 ) 15566 self.descriptor = descriptor 15567 15568 def getBody(self): 15569 set = "" 15570 15571 indexedSetter = self.descriptor.operations["IndexedSetter"] 15572 if indexedSetter: 15573 error_label = CGSpecializedMethod.error_reporting_label_helper( 15574 self.descriptor, indexedSetter, isConstructor=False 15575 ) 15576 if error_label: 15577 cxDecl = fill( 15578 """ 15579 BindingCallContext cx(cx_, ${error_label}); 15580 """, 15581 error_label=error_label, 15582 ) 15583 else: 15584 cxDecl = dedent( 15585 """ 15586 JSContext* cx = cx_; 15587 """ 15588 ) 15589 set += fill( 15590 """ 15591 uint32_t index = GetArrayIndexFromId(id); 15592 if (IsArrayIndex(index)) { 15593 $*{cxDecl} 15594 *done = true; 15595 // https://webidl.spec.whatwg.org/#legacy-platform-object-defineownproperty 15596 // Step 1.1. The no-indexed-setter case is handled by step 1.2. 15597 if (!desc.isDataDescriptor()) { 15598 return opresult.failNotDataDescriptor(); 15599 } 15600 15601 $*{callSetter} 15602 return opresult.succeed(); 15603 } 15604 """, 15605 cxDecl=cxDecl, 15606 callSetter=CGProxyIndexedSetter(self.descriptor).define(), 15607 ) 15608 elif self.descriptor.supportsIndexedProperties(): 15609 # We allow untrusted content to prevent Xrays from setting a 15610 # property if that property is an indexed property and we have no 15611 # indexed setter. That's how the object would normally behave if 15612 # you tried to set the property on it. That means we don't need to 15613 # do anything special for Xrays here. 15614 set += dedent( 15615 """ 15616 if (IsArrayIndex(GetArrayIndexFromId(id))) { 15617 *done = true; 15618 return opresult.failNoIndexedSetter(); 15619 } 15620 """ 15621 ) 15622 15623 namedSetter = self.descriptor.operations["NamedSetter"] 15624 if namedSetter: 15625 error_label = CGSpecializedMethod.error_reporting_label_helper( 15626 self.descriptor, namedSetter, isConstructor=False 15627 ) 15628 if error_label: 15629 set += fill( 15630 """ 15631 BindingCallContext cx(cx_, ${error_label}); 15632 """, 15633 error_label=error_label, 15634 ) 15635 else: 15636 set += dedent( 15637 """ 15638 JSContext* cx = cx_; 15639 """ 15640 ) 15641 if self.descriptor.hasLegacyUnforgeableMembers: 15642 raise TypeError( 15643 "Can't handle a named setter on an interface " 15644 "that has unforgeables. Figure out how that " 15645 "should work!" 15646 ) 15647 set += dedent( 15648 """ 15649 // https://webidl.spec.whatwg.org/#legacy-platform-object-defineownproperty 15650 // Step 2.2.2.1. 15651 if (!desc.isDataDescriptor()) { 15652 *done = true; 15653 return opresult.failNotDataDescriptor(); 15654 } 15655 """ 15656 ) 15657 tailCode = dedent( 15658 """ 15659 *done = true; 15660 return opresult.succeed(); 15661 """ 15662 ) 15663 set += CGProxyNamedSetter(self.descriptor, tailCode).define() 15664 else: 15665 # We allow untrusted content to prevent Xrays from setting a 15666 # property if that property is already a named property on the 15667 # object and we have no named setter. That's how the object would 15668 # normally behave if you tried to set the property on it. That 15669 # means we don't need to do anything special for Xrays here. 15670 if self.descriptor.supportsNamedProperties(): 15671 set += fill( 15672 """ 15673 JSContext* cx = cx_; 15674 bool found = false; 15675 $*{presenceChecker} 15676 15677 if (found) { 15678 *done = true; 15679 return opresult.failNoNamedSetter(); 15680 } 15681 """, 15682 presenceChecker=CGProxyNamedPresenceChecker( 15683 self.descriptor, foundVar="found" 15684 ).define(), 15685 ) 15686 if self.descriptor.isMaybeCrossOriginObject(): 15687 set += dedent( 15688 """ 15689 MOZ_ASSERT(IsPlatformObjectSameOrigin(cx_, proxy), 15690 "Why did the MaybeCrossOriginObject defineProperty override fail?"); 15691 MOZ_ASSERT(js::IsObjectInContextCompartment(proxy, cx_), 15692 "Why did the MaybeCrossOriginObject defineProperty override fail?"); 15693 """ 15694 ) 15695 15696 # In all cases we want to tail-call to our base class; we can 15697 # always land here for symbols. 15698 set += ( 15699 "return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n" 15700 % ", ".join(a.name for a in self.args) 15701 ) 15702 return set 15703 15704 15705 def getDeleterBody(descriptor, type, foundVar=None): 15706 """ 15707 type should be "Named" or "Indexed" 15708 15709 The possible outcomes: 15710 - an error happened (the emitted code returns false) 15711 - own property not found (foundVar=false, deleteSucceeded=true) 15712 - own property found and deleted (foundVar=true, deleteSucceeded=true) 15713 - own property found but can't be deleted (foundVar=true, deleteSucceeded=false) 15714 """ 15715 assert type in ("Named", "Indexed") 15716 deleter = descriptor.operations[type + "Deleter"] 15717 if deleter: 15718 assert type == "Named" 15719 assert foundVar is not None 15720 if descriptor.hasLegacyUnforgeableMembers: 15721 raise TypeError( 15722 "Can't handle a deleter on an interface " 15723 "that has unforgeables. Figure out how " 15724 "that should work!" 15725 ) 15726 # See if the deleter method is fallible. 15727 t = deleter.signatures()[0][0] 15728 if t.isPrimitive() and not t.nullable() and t.tag() == IDLType.Tags.bool: 15729 # The deleter method has a boolean return value. When a 15730 # property is found, the return value indicates whether it 15731 # was successfully deleted. 15732 setDS = fill( 15733 """ 15734 if (!${foundVar}) { 15735 deleteSucceeded = true; 15736 } 15737 """, 15738 foundVar=foundVar, 15739 ) 15740 else: 15741 # No boolean return value: if a property is found, 15742 # deleting it always succeeds. 15743 setDS = "deleteSucceeded = true;\n" 15744 15745 body = ( 15746 CGProxyNamedDeleter( 15747 descriptor, resultVar="deleteSucceeded", foundVar=foundVar 15748 ).define() 15749 + setDS 15750 ) 15751 elif getattr(descriptor, "supports%sProperties" % type)(): 15752 presenceCheckerClass = globals()["CGProxy%sPresenceChecker" % type] 15753 foundDecl = "" 15754 if foundVar is None: 15755 foundVar = "found" 15756 foundDecl = "bool found = false;\n" 15757 body = fill( 15758 """ 15759 $*{foundDecl} 15760 $*{presenceChecker} 15761 deleteSucceeded = !${foundVar}; 15762 """, 15763 foundDecl=foundDecl, 15764 presenceChecker=presenceCheckerClass( 15765 descriptor, foundVar=foundVar 15766 ).define(), 15767 foundVar=foundVar, 15768 ) 15769 else: 15770 body = None 15771 return body 15772 15773 15774 class CGDeleteNamedProperty(CGAbstractStaticMethod): 15775 def __init__(self, descriptor): 15776 args = [ 15777 Argument("JSContext*", "cx"), 15778 Argument("JS::Handle<JSObject*>", "xray"), 15779 Argument("JS::Handle<JSObject*>", "proxy"), 15780 Argument("JS::Handle<jsid>", "id"), 15781 Argument("JS::ObjectOpResult&", "opresult"), 15782 ] 15783 CGAbstractStaticMethod.__init__( 15784 self, descriptor, "DeleteNamedProperty", "bool", args 15785 ) 15786 15787 def definition_body(self): 15788 return fill( 15789 """ 15790 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(xray)); 15791 MOZ_ASSERT(js::IsProxy(proxy)); 15792 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy)); 15793 JSAutoRealm ar(cx, proxy); 15794 bool deleteSucceeded = false; 15795 bool found = false; 15796 $*{namedBody} 15797 if (!found || deleteSucceeded) { 15798 return opresult.succeed(); 15799 } 15800 return opresult.failCantDelete(); 15801 """, 15802 namedBody=getDeleterBody(self.descriptor, "Named", foundVar="found"), 15803 ) 15804 15805 15806 class CGDOMJSProxyHandler_delete(ClassMethod): 15807 def __init__(self, descriptor): 15808 args = [ 15809 Argument("JSContext*", "cx"), 15810 Argument("JS::Handle<JSObject*>", "proxy"), 15811 Argument("JS::Handle<jsid>", "id"), 15812 Argument("JS::ObjectOpResult&", "opresult"), 15813 ] 15814 ClassMethod.__init__( 15815 self, "delete_", "bool", args, virtual=True, override=True, const=True 15816 ) 15817 self.descriptor = descriptor 15818 15819 def getBody(self): 15820 delete = dedent( 15821 """ 15822 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), 15823 "Should not have a XrayWrapper here"); 15824 15825 """ 15826 ) 15827 15828 if self.descriptor.isMaybeCrossOriginObject(): 15829 delete += dedent( 15830 """ 15831 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 15832 return ReportCrossOriginDenial(cx, id, "delete"_ns); 15833 } 15834 15835 // Safe to enter the Realm of proxy now. 15836 JSAutoRealm ar(cx, proxy); 15837 JS_MarkCrossZoneId(cx, id); 15838 """ 15839 ) 15840 15841 indexedBody = getDeleterBody(self.descriptor, "Indexed") 15842 if indexedBody is not None: 15843 # Can't handle cross-origin objects here. 15844 assert not self.descriptor.isMaybeCrossOriginObject() 15845 delete += fill( 15846 """ 15847 uint32_t index = GetArrayIndexFromId(id); 15848 if (IsArrayIndex(index)) { 15849 bool deleteSucceeded; 15850 $*{indexedBody} 15851 return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete(); 15852 } 15853 """, 15854 indexedBody=indexedBody, 15855 ) 15856 15857 namedBody = getDeleterBody(self.descriptor, "Named", foundVar="found") 15858 if namedBody is not None: 15859 delete += dedent( 15860 """ 15861 // Try named delete only if the named property visibility 15862 // algorithm says the property is visible. 15863 bool tryNamedDelete = true; 15864 { // Scope for expando 15865 JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy)); 15866 if (expando) { 15867 bool hasProp; 15868 if (!JS_HasPropertyById(cx, expando, id, &hasProp)) { 15869 return false; 15870 } 15871 tryNamedDelete = !hasProp; 15872 } 15873 } 15874 """ 15875 ) 15876 15877 if not self.descriptor.interface.getExtendedAttribute( 15878 "LegacyOverrideBuiltIns" 15879 ): 15880 delete += dedent( 15881 """ 15882 if (tryNamedDelete) { 15883 bool hasOnProto; 15884 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) { 15885 return false; 15886 } 15887 tryNamedDelete = !hasOnProto; 15888 } 15889 """ 15890 ) 15891 15892 # We always return above for an index id in the case when we support 15893 # indexed properties, so we can just treat the id as a name 15894 # unconditionally here. 15895 delete += fill( 15896 """ 15897 if (tryNamedDelete) { 15898 bool found = false; 15899 bool deleteSucceeded; 15900 $*{namedBody} 15901 if (found) { 15902 return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete(); 15903 } 15904 } 15905 """, 15906 namedBody=namedBody, 15907 ) 15908 15909 delete += dedent( 15910 """ 15911 15912 return dom::DOMProxyHandler::delete_(cx, proxy, id, opresult); 15913 """ 15914 ) 15915 15916 return delete 15917 15918 15919 class CGDOMJSProxyHandler_ownPropNames(ClassMethod): 15920 def __init__( 15921 self, 15922 descriptor, 15923 ): 15924 args = [ 15925 Argument("JSContext*", "cx"), 15926 Argument("JS::Handle<JSObject*>", "proxy"), 15927 Argument("unsigned", "flags"), 15928 Argument("JS::MutableHandleVector<jsid>", "props"), 15929 ] 15930 ClassMethod.__init__( 15931 self, "ownPropNames", "bool", args, virtual=True, override=True, const=True 15932 ) 15933 self.descriptor = descriptor 15934 15935 def getBody(self): 15936 if self.descriptor.isMaybeCrossOriginObject(): 15937 xrayDecl = dedent( 15938 """ 15939 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy)); 15940 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 15941 if (!(flags & JSITER_HIDDEN)) { 15942 // There are no enumerable cross-origin props, so we're done. 15943 return true; 15944 } 15945 15946 JS::Rooted<JSObject*> holder(cx); 15947 if (!EnsureHolder(cx, proxy, &holder)) { 15948 return false; 15949 } 15950 15951 if (!js::GetPropertyKeys(cx, holder, flags, props)) { 15952 return false; 15953 } 15954 15955 return xpc::AppendCrossOriginWhitelistedPropNames(cx, props); 15956 } 15957 15958 """ 15959 ) 15960 xrayCheck = "" 15961 else: 15962 xrayDecl = "bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);\n" 15963 xrayCheck = "!isXray &&" 15964 15965 # Per spec, we do indices, then named props, then everything else. 15966 if self.descriptor.supportsIndexedProperties(): 15967 if self.descriptor.lengthNeedsCallerType(): 15968 callerType = callerTypeGetterForDescriptor(self.descriptor) 15969 else: 15970 callerType = "" 15971 addIndices = fill( 15972 """ 15973 15974 uint32_t length = UnwrapProxy(proxy)->Length(${callerType}); 15975 MOZ_ASSERT(int32_t(length) >= 0); 15976 for (int32_t i = 0; i < int32_t(length); ++i) { 15977 if (!props.append(JS::PropertyKey::Int(i))) { 15978 return false; 15979 } 15980 } 15981 """, 15982 callerType=callerType, 15983 ) 15984 else: 15985 addIndices = "" 15986 15987 if self.descriptor.supportsNamedProperties(): 15988 if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"): 15989 shadow = "!isXray" 15990 else: 15991 shadow = "false" 15992 15993 if self.descriptor.supportedNamesNeedCallerType(): 15994 callerType = ", " + callerTypeGetterForDescriptor(self.descriptor) 15995 else: 15996 callerType = "" 15997 15998 addNames = fill( 15999 """ 16000 nsTArray<nsString> names; 16001 UnwrapProxy(proxy)->GetSupportedNames(names${callerType}); 16002 if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) { 16003 return false; 16004 } 16005 """, 16006 callerType=callerType, 16007 shadow=shadow, 16008 ) 16009 if not self.descriptor.namedPropertiesEnumerable: 16010 addNames = CGIfWrapper( 16011 CGGeneric(addNames), "flags & JSITER_HIDDEN" 16012 ).define() 16013 addNames = "\n" + addNames 16014 else: 16015 addNames = "" 16016 16017 addExpandoProps = fill( 16018 """ 16019 JS::Rooted<JSObject*> expando(cx); 16020 if (${xrayCheck}(expando = DOMProxyHandler::GetExpandoObject(proxy)) && 16021 !js::GetPropertyKeys(cx, expando, flags, props)) { 16022 return false; 16023 } 16024 """, 16025 xrayCheck=xrayCheck, 16026 ) 16027 16028 if self.descriptor.isMaybeCrossOriginObject(): 16029 # We need to enter our compartment (which we might not be 16030 # in right now) to get the expando props. 16031 addExpandoProps = fill( 16032 """ 16033 { // Scope for accessing the expando. 16034 // Safe to enter our compartment, because IsPlatformObjectSameOrigin tested true. 16035 JSAutoRealm ar(cx, proxy); 16036 $*{addExpandoProps} 16037 } 16038 for (auto& id : props) { 16039 JS_MarkCrossZoneId(cx, id); 16040 } 16041 """, 16042 addExpandoProps=addExpandoProps, 16043 ) 16044 16045 return fill( 16046 """ 16047 $*{xrayDecl} 16048 $*{addIndices} 16049 $*{addNames} 16050 16051 $*{addExpandoProps} 16052 16053 return true; 16054 """, 16055 xrayDecl=xrayDecl, 16056 addIndices=addIndices, 16057 addNames=addNames, 16058 addExpandoProps=addExpandoProps, 16059 ) 16060 16061 16062 class CGDOMJSProxyHandler_hasOwn(ClassMethod): 16063 def __init__(self, descriptor): 16064 args = [ 16065 Argument("JSContext*", "cx"), 16066 Argument("JS::Handle<JSObject*>", "proxy"), 16067 Argument("JS::Handle<jsid>", "id"), 16068 Argument("bool*", "bp"), 16069 ] 16070 ClassMethod.__init__( 16071 self, "hasOwn", "bool", args, virtual=True, override=True, const=True 16072 ) 16073 self.descriptor = descriptor 16074 16075 def getBody(self): 16076 if self.descriptor.isMaybeCrossOriginObject(): 16077 maybeCrossOrigin = dedent( 16078 """ 16079 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 16080 // Just hand this off to BaseProxyHandler to do the slow-path thing. 16081 // The BaseProxyHandler code is OK with this happening without entering the 16082 // compartment of "proxy", which is important to get the right answers. 16083 return js::BaseProxyHandler::hasOwn(cx, proxy, id, bp); 16084 } 16085 16086 // Now safe to enter the Realm of proxy and do the rest of the work there. 16087 JSAutoRealm ar(cx, proxy); 16088 JS_MarkCrossZoneId(cx, id); 16089 """ 16090 ) 16091 else: 16092 maybeCrossOrigin = "" 16093 16094 if self.descriptor.supportsIndexedProperties(): 16095 indexed = fill( 16096 """ 16097 uint32_t index = GetArrayIndexFromId(id); 16098 if (IsArrayIndex(index)) { 16099 bool found = false; 16100 $*{presenceChecker} 16101 16102 *bp = found; 16103 return true; 16104 } 16105 16106 """, 16107 presenceChecker=CGProxyIndexedPresenceChecker( 16108 self.descriptor, foundVar="found" 16109 ).define(), 16110 ) 16111 else: 16112 indexed = "" 16113 16114 if self.descriptor.supportsNamedProperties(): 16115 # If we support indexed properties we always return above for index 16116 # property names, so no need to check for those here. 16117 named = fill( 16118 """ 16119 bool found = false; 16120 $*{presenceChecker} 16121 16122 *bp = found; 16123 """, 16124 presenceChecker=CGProxyNamedPresenceChecker( 16125 self.descriptor, foundVar="found" 16126 ).define(), 16127 ) 16128 if not self.descriptor.interface.getExtendedAttribute( 16129 "LegacyOverrideBuiltIns" 16130 ): 16131 named = fill( 16132 """ 16133 bool hasOnProto; 16134 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) { 16135 return false; 16136 } 16137 if (!hasOnProto) { 16138 $*{protoLacksProperty} 16139 return true; 16140 } 16141 """, 16142 protoLacksProperty=named, 16143 ) 16144 named += "*bp = false;\n" 16145 else: 16146 named += "\n" 16147 else: 16148 named = "*bp = false;\n" 16149 16150 missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor) 16151 16152 return fill( 16153 """ 16154 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), 16155 "Should not have a XrayWrapper here"); 16156 $*{maybeCrossOrigin} 16157 $*{indexed} 16158 16159 $*{missingPropUseCounters} 16160 JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy)); 16161 if (expando) { 16162 bool b = true; 16163 bool ok = JS_HasPropertyById(cx, expando, id, &b); 16164 *bp = !!b; 16165 if (!ok || *bp) { 16166 return ok; 16167 } 16168 } 16169 16170 $*{named} 16171 return true; 16172 """, 16173 maybeCrossOrigin=maybeCrossOrigin, 16174 indexed=indexed, 16175 missingPropUseCounters=missingPropUseCounters, 16176 named=named, 16177 ) 16178 16179 16180 class CGDOMJSProxyHandler_get(ClassMethod): 16181 def __init__(self, descriptor): 16182 args = [ 16183 Argument("JSContext*", "cx"), 16184 Argument("JS::Handle<JSObject*>", "proxy"), 16185 Argument("JS::Handle<JS::Value>", "receiver"), 16186 Argument("JS::Handle<jsid>", "id"), 16187 Argument("JS::MutableHandle<JS::Value>", "vp"), 16188 ] 16189 ClassMethod.__init__( 16190 self, "get", "bool", args, virtual=True, override=True, const=True 16191 ) 16192 self.descriptor = descriptor 16193 16194 def getBody(self): 16195 missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor) 16196 16197 getUnforgeableOrExpando = dedent( 16198 """ 16199 bool expandoHasProp = false; 16200 { // Scope for expando 16201 JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy)); 16202 if (expando) { 16203 if (!JS_HasPropertyById(cx, expando, id, &expandoHasProp)) { 16204 return false; 16205 } 16206 16207 if (expandoHasProp) { 16208 // Forward the get to the expando object, but our receiver is whatever our 16209 // receiver is. 16210 if (!JS_ForwardGetPropertyTo(cx, expando, id, ${receiver}, vp)) { 16211 return false; 16212 } 16213 } 16214 } 16215 } 16216 """ 16217 ) 16218 16219 getOnPrototype = dedent( 16220 """ 16221 bool foundOnPrototype; 16222 if (!GetPropertyOnPrototype(cx, proxy, ${receiver}, id, &foundOnPrototype, vp)) { 16223 return false; 16224 } 16225 """ 16226 ) 16227 16228 if self.descriptor.isMaybeCrossOriginObject(): 16229 # We can't handle these for cross-origin objects 16230 assert not self.descriptor.supportsIndexedProperties() 16231 assert not self.descriptor.supportsNamedProperties() 16232 16233 return fill( 16234 """ 16235 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), 16236 "Should not have a XrayWrapper here"); 16237 16238 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 16239 return CrossOriginGet(cx, proxy, receiver, id, vp); 16240 } 16241 16242 $*{missingPropUseCounters} 16243 { // Scope for the JSAutoRealm accessing expando and prototype. 16244 JSAutoRealm ar(cx, proxy); 16245 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver); 16246 if (!MaybeWrapValue(cx, &wrappedReceiver)) { 16247 return false; 16248 } 16249 JS_MarkCrossZoneId(cx, id); 16250 16251 $*{getUnforgeableOrExpando} 16252 if (!expandoHasProp) { 16253 $*{getOnPrototype} 16254 if (!foundOnPrototype) { 16255 MOZ_ASSERT(vp.isUndefined()); 16256 return true; 16257 } 16258 } 16259 } 16260 16261 return MaybeWrapValue(cx, vp); 16262 """, 16263 missingPropUseCounters=missingPropUseCountersForDescriptor( 16264 self.descriptor 16265 ), 16266 getUnforgeableOrExpando=fill( 16267 getUnforgeableOrExpando, receiver="wrappedReceiver" 16268 ), 16269 getOnPrototype=fill(getOnPrototype, receiver="wrappedReceiver"), 16270 ) 16271 16272 templateValues = {"jsvalRef": "vp", "jsvalHandle": "vp", "obj": "proxy"} 16273 16274 getUnforgeableOrExpando = fill( 16275 getUnforgeableOrExpando, receiver="receiver" 16276 ) + dedent( 16277 """ 16278 16279 if (expandoHasProp) { 16280 return true; 16281 } 16282 """ 16283 ) 16284 if self.descriptor.supportsIndexedProperties(): 16285 getIndexedOrExpando = fill( 16286 """ 16287 uint32_t index = GetArrayIndexFromId(id); 16288 if (IsArrayIndex(index)) { 16289 $*{callGetter} 16290 // Even if we don't have this index, we don't forward the 16291 // get on to our expando object. 16292 } else { 16293 $*{getUnforgeableOrExpando} 16294 } 16295 """, 16296 callGetter=CGProxyIndexedGetter( 16297 self.descriptor, templateValues 16298 ).define(), 16299 getUnforgeableOrExpando=getUnforgeableOrExpando, 16300 ) 16301 else: 16302 getIndexedOrExpando = getUnforgeableOrExpando 16303 16304 if self.descriptor.supportsNamedProperties(): 16305 getNamed = CGProxyNamedGetter(self.descriptor, templateValues) 16306 if self.descriptor.supportsIndexedProperties(): 16307 getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)") 16308 getNamed = getNamed.define() + "\n" 16309 else: 16310 getNamed = "" 16311 16312 getOnPrototype = fill(getOnPrototype, receiver="receiver") + dedent( 16313 """ 16314 16315 if (foundOnPrototype) { 16316 return true; 16317 } 16318 16319 MOZ_ASSERT(vp.isUndefined()); 16320 """ 16321 ) 16322 16323 if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"): 16324 getNamedOrOnPrototype = getNamed + getOnPrototype 16325 else: 16326 getNamedOrOnPrototype = getOnPrototype + getNamed 16327 16328 return fill( 16329 """ 16330 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), 16331 "Should not have a XrayWrapper here"); 16332 16333 $*{missingPropUseCounters} 16334 $*{indexedOrExpando} 16335 16336 $*{namedOrOnPropotype} 16337 return true; 16338 """, 16339 missingPropUseCounters=missingPropUseCounters, 16340 indexedOrExpando=getIndexedOrExpando, 16341 namedOrOnPropotype=getNamedOrOnPrototype, 16342 ) 16343 16344 16345 class CGDOMJSProxyHandler_setCustom(ClassMethod): 16346 def __init__(self, descriptor): 16347 args = [ 16348 Argument("JSContext*", "cx_"), 16349 Argument("JS::Handle<JSObject*>", "proxy"), 16350 Argument("JS::Handle<jsid>", "id"), 16351 Argument("JS::Handle<JS::Value>", "v"), 16352 Argument("bool*", "done"), 16353 ] 16354 ClassMethod.__init__( 16355 self, "setCustom", "bool", args, virtual=True, override=True, const=True 16356 ) 16357 self.descriptor = descriptor 16358 16359 def getBody(self): 16360 assertion = ( 16361 "MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n" 16362 ' "Should not have a XrayWrapper here");\n' 16363 ) 16364 16365 # Correctness first. If we have a NamedSetter and [LegacyOverrideBuiltIns], 16366 # always call the NamedSetter and never do anything else. 16367 namedSetter = self.descriptor.operations["NamedSetter"] 16368 if namedSetter is not None and self.descriptor.interface.getExtendedAttribute( 16369 "LegacyOverrideBuiltIns" 16370 ): 16371 # Check assumptions. 16372 if self.descriptor.supportsIndexedProperties(): 16373 raise ValueError( 16374 "In interface " 16375 + self.descriptor.name 16376 + ": " 16377 + "Can't cope with [LegacyOverrideBuiltIns] and an indexed getter" 16378 ) 16379 if self.descriptor.hasLegacyUnforgeableMembers: 16380 raise ValueError( 16381 "In interface " 16382 + self.descriptor.name 16383 + ": " 16384 + "Can't cope with [LegacyOverrideBuiltIns] and unforgeable members" 16385 ) 16386 16387 tailCode = dedent( 16388 """ 16389 *done = true; 16390 return true; 16391 """ 16392 ) 16393 callSetter = CGProxyNamedSetter( 16394 self.descriptor, tailCode, argumentHandleValue="v" 16395 ) 16396 error_label = CGSpecializedMethod.error_reporting_label_helper( 16397 self.descriptor, namedSetter, isConstructor=False 16398 ) 16399 if error_label: 16400 cxDecl = fill( 16401 """ 16402 BindingCallContext cx(cx_, ${error_label}); 16403 """, 16404 error_label=error_label, 16405 ) 16406 else: 16407 cxDecl = dedent( 16408 """ 16409 JSContext* cx = cx_; 16410 """ 16411 ) 16412 return fill( 16413 """ 16414 $*{assertion} 16415 $*{cxDecl} 16416 $*{callSetter} 16417 *done = false; 16418 return true; 16419 """, 16420 assertion=assertion, 16421 cxDecl=cxDecl, 16422 callSetter=callSetter.define(), 16423 ) 16424 16425 # As an optimization, if we are going to call an IndexedSetter, go 16426 # ahead and call it and have done. 16427 indexedSetter = self.descriptor.operations["IndexedSetter"] 16428 if indexedSetter is not None: 16429 error_label = CGSpecializedMethod.error_reporting_label_helper( 16430 self.descriptor, indexedSetter, isConstructor=False 16431 ) 16432 if error_label: 16433 cxDecl = fill( 16434 """ 16435 BindingCallContext cx(cx_, ${error_label}); 16436 """, 16437 error_label=error_label, 16438 ) 16439 else: 16440 cxDecl = dedent( 16441 """ 16442 JSContext* cx = cx_; 16443 """ 16444 ) 16445 setIndexed = fill( 16446 """ 16447 uint32_t index = GetArrayIndexFromId(id); 16448 if (IsArrayIndex(index)) { 16449 $*{cxDecl} 16450 $*{callSetter} 16451 *done = true; 16452 return true; 16453 } 16454 16455 """, 16456 cxDecl=cxDecl, 16457 callSetter=CGProxyIndexedSetter( 16458 self.descriptor, argumentHandleValue="v" 16459 ).define(), 16460 ) 16461 else: 16462 setIndexed = "" 16463 16464 return assertion + setIndexed + "*done = false;\n" "return true;\n" 16465 16466 16467 class CGDOMJSProxyHandler_className(ClassMethod): 16468 def __init__(self, descriptor): 16469 args = [ 16470 Argument("JSContext*", "cx"), 16471 Argument("JS::Handle<JSObject*>", "proxy"), 16472 ] 16473 ClassMethod.__init__( 16474 self, 16475 "className", 16476 "const char*", 16477 args, 16478 virtual=True, 16479 override=True, 16480 const=True, 16481 ) 16482 self.descriptor = descriptor 16483 16484 def getBody(self): 16485 if self.descriptor.isMaybeCrossOriginObject(): 16486 crossOrigin = dedent( 16487 """ 16488 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 16489 return "Object"; 16490 } 16491 16492 """ 16493 ) 16494 else: 16495 crossOrigin = "" 16496 return fill( 16497 """ 16498 $*{crossOrigin} 16499 return "${name}"; 16500 """, 16501 crossOrigin=crossOrigin, 16502 name=self.descriptor.name, 16503 ) 16504 16505 16506 class CGDOMJSProxyHandler_trace(ClassMethod): 16507 def __init__(self, descriptor): 16508 args = [Argument("JSTracer*", "trc"), Argument("JSObject*", "proxy")] 16509 ClassMethod.__init__( 16510 self, 16511 "trace", 16512 "void", 16513 args, 16514 virtual=True, 16515 override=True, 16516 const=True, 16517 ) 16518 self.descriptor = descriptor 16519 16520 def getBody(self): 16521 iface = getReflectedHTMLAttributesIface(self.descriptor) 16522 return fill( 16523 """ 16524 ${reflectedAttributesBase}::ReflectedHTMLAttributeSlots::Trace(${trc}, ${proxy}); 16525 return Base::trace(${trc}, ${proxy}); 16526 """, 16527 reflectedAttributesBase=toBindingNamespace(iface.identifier.name), 16528 trc=self.args[0].name, 16529 proxy=self.args[1].name, 16530 ) 16531 16532 16533 class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod): 16534 def __init__(self, descriptor): 16535 args = [Argument("const JS::Value&", "priv")] 16536 ClassMethod.__init__( 16537 self, 16538 "finalizeInBackground", 16539 "bool", 16540 args, 16541 virtual=True, 16542 override=True, 16543 const=True, 16544 ) 16545 self.descriptor = descriptor 16546 16547 def getBody(self): 16548 return "return false;\n" 16549 16550 16551 class CGDOMJSProxyHandler_finalize(ClassMethod): 16552 def __init__(self, descriptor): 16553 args = [Argument("JS::GCContext*", "gcx"), Argument("JSObject*", "proxy")] 16554 ClassMethod.__init__( 16555 self, "finalize", "void", args, virtual=True, override=True, const=True 16556 ) 16557 self.descriptor = descriptor 16558 16559 def getBody(self): 16560 return ( 16561 "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(proxy);\n" 16562 % (self.descriptor.nativeType, self.descriptor.nativeType) 16563 ) + finalizeHook( 16564 self.descriptor, 16565 self.args[0].name, 16566 self.args[1].name, 16567 ).define() 16568 16569 16570 class CGDOMJSProxyHandler_objectMoved(ClassMethod): 16571 def __init__(self, descriptor): 16572 args = [Argument("JSObject*", "obj"), Argument("JSObject*", "old")] 16573 ClassMethod.__init__( 16574 self, "objectMoved", "size_t", args, virtual=True, override=True, const=True 16575 ) 16576 self.descriptor = descriptor 16577 16578 def getBody(self): 16579 return ( 16580 "return NativeTypeHelpers<%s>::ObjectMoved(obj, old);\n" 16581 % self.descriptor.nativeType 16582 ) 16583 16584 16585 class CGDOMJSProxyHandler_getElements(ClassMethod): 16586 def __init__(self, descriptor): 16587 assert descriptor.supportsIndexedProperties() 16588 16589 args = [ 16590 Argument("JSContext*", "cx"), 16591 Argument("JS::Handle<JSObject*>", "proxy"), 16592 Argument("uint32_t", "begin"), 16593 Argument("uint32_t", "end"), 16594 Argument("js::ElementAdder*", "adder"), 16595 ] 16596 ClassMethod.__init__( 16597 self, "getElements", "bool", args, virtual=True, override=True, const=True 16598 ) 16599 self.descriptor = descriptor 16600 16601 def getBody(self): 16602 # Just like ownPropertyKeys we'll assume that we have no holes, so 16603 # we have all properties from 0 to length. If that ever changes 16604 # (unlikely), we'll need to do something a bit more clever with how we 16605 # forward on to our ancestor. 16606 16607 templateValues = { 16608 "jsvalRef": "temp", 16609 "jsvalHandle": "&temp", 16610 "obj": "proxy", 16611 "successCode": ( 16612 "if (!adder->append(cx, temp)) return false;\n" "continue;\n" 16613 ), 16614 } 16615 get = CGProxyIndexedGetter( 16616 self.descriptor, templateValues, False, False 16617 ).define() 16618 16619 if self.descriptor.lengthNeedsCallerType(): 16620 callerType = callerTypeGetterForDescriptor(self.descriptor) 16621 else: 16622 callerType = "" 16623 16624 return fill( 16625 """ 16626 JS::Rooted<JS::Value> temp(cx); 16627 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), 16628 "Should not have a XrayWrapper here"); 16629 16630 ${nativeType}* self = UnwrapProxy(proxy); 16631 uint32_t length = self->Length(${callerType}); 16632 // Compute the end of the indices we'll get ourselves 16633 uint32_t ourEnd = std::clamp(length, begin, end); 16634 16635 for (uint32_t index = begin; index < ourEnd; ++index) { 16636 $*{get} 16637 } 16638 16639 if (end > ourEnd) { 16640 JS::Rooted<JSObject*> proto(cx); 16641 if (!js::GetObjectProto(cx, proxy, &proto)) { 16642 return false; 16643 } 16644 return js::GetElementsWithAdder(cx, proto, proxy, ourEnd, end, adder); 16645 } 16646 16647 return true; 16648 """, 16649 nativeType=self.descriptor.nativeType, 16650 callerType=callerType, 16651 get=get, 16652 ) 16653 16654 16655 class CGJSProxyHandler_getInstance(ClassMethod): 16656 def __init__(self, type): 16657 self.type = type 16658 ClassMethod.__init__( 16659 self, "getInstance", "const %s*" % self.type, [], static=True 16660 ) 16661 16662 def getBody(self): 16663 return fill( 16664 """ 16665 static const ${type} instance; 16666 return &instance; 16667 """, 16668 type=self.type, 16669 ) 16670 16671 16672 class CGDOMJSProxyHandler_call(ClassMethod): 16673 def __init__(self): 16674 args = [ 16675 Argument("JSContext*", "cx"), 16676 Argument("JS::Handle<JSObject*>", "proxy"), 16677 Argument("const JS::CallArgs&", "args"), 16678 ] 16679 16680 ClassMethod.__init__( 16681 self, "call", "bool", args, virtual=True, override=True, const=True 16682 ) 16683 16684 def getBody(self): 16685 return fill( 16686 """ 16687 return js::ForwardToNative(cx, ${legacyCaller}, args); 16688 """, 16689 legacyCaller=LEGACYCALLER_HOOK_NAME, 16690 ) 16691 16692 16693 class CGDOMJSProxyHandler_isCallable(ClassMethod): 16694 def __init__(self): 16695 ClassMethod.__init__( 16696 self, 16697 "isCallable", 16698 "bool", 16699 [Argument("JSObject*", "obj")], 16700 virtual=True, 16701 override=True, 16702 const=True, 16703 ) 16704 16705 def getBody(self): 16706 return dedent( 16707 """ 16708 return true; 16709 """ 16710 ) 16711 16712 16713 class CGDOMJSProxyHandler_canNurseryAllocate(ClassMethod): 16714 """ 16715 Override the default canNurseryAllocate in BaseProxyHandler, for cases when 16716 we should be nursery-allocated. 16717 """ 16718 16719 def __init__(self): 16720 ClassMethod.__init__( 16721 self, 16722 "canNurseryAllocate", 16723 "bool", 16724 [], 16725 virtual=True, 16726 override=True, 16727 const=True, 16728 ) 16729 16730 def getBody(self): 16731 return dedent( 16732 """ 16733 return true; 16734 """ 16735 ) 16736 16737 16738 class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod): 16739 """ 16740 Implementation of getOwnPropertyDescriptor. We only use this for 16741 cross-origin objects. 16742 """ 16743 16744 def __init__(self, descriptor): 16745 assert descriptor.isMaybeCrossOriginObject() 16746 16747 args = [ 16748 Argument("JSContext*", "cx"), 16749 Argument("JS::Handle<JSObject*>", "proxy"), 16750 Argument("JS::Handle<jsid>", "id"), 16751 Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"), 16752 ] 16753 ClassMethod.__init__( 16754 self, 16755 "getOwnPropertyDescriptor", 16756 "bool", 16757 args, 16758 virtual=True, 16759 override=True, 16760 const=True, 16761 ) 16762 self.descriptor = descriptor 16763 16764 def getBody(self): 16765 return dedent( 16766 """ 16767 // Implementation of <https://html.spec.whatwg.org/multipage/history.html#location-getownproperty>. 16768 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy)); 16769 16770 // Step 1. 16771 if (IsPlatformObjectSameOrigin(cx, proxy)) { 16772 { // Scope so we can wrap our PropertyDescriptor back into 16773 // the caller compartment. 16774 // Enter the Realm of "proxy" so we can work with it. 16775 JSAutoRealm ar(cx, proxy); 16776 16777 JS_MarkCrossZoneId(cx, id); 16778 16779 // The spec messes around with configurability of the returned 16780 // descriptor here, but it's not clear what should actually happen 16781 // here. See <https://github.com/whatwg/html/issues/4157>. For 16782 // now, keep our old behavior and don't do any magic. 16783 if (!dom::DOMProxyHandler::getOwnPropertyDescriptor(cx, proxy, id, desc)) { 16784 return false; 16785 } 16786 } 16787 return JS_WrapPropertyDescriptor(cx, desc); 16788 } 16789 16790 // Step 2. 16791 if (!CrossOriginGetOwnPropertyHelper(cx, proxy, id, desc)) { 16792 return false; 16793 } 16794 16795 // Step 3. 16796 if (desc.isSome()) { 16797 return true; 16798 } 16799 16800 // And step 4. 16801 return CrossOriginPropertyFallback(cx, proxy, id, desc); 16802 """ 16803 ) 16804 16805 16806 class CGDOMJSProxyHandler_getSameOriginPrototype(ClassMethod): 16807 """ 16808 Implementation of getSameOriginPrototype. We only use this for 16809 cross-origin objects. 16810 """ 16811 16812 def __init__(self, descriptor): 16813 assert descriptor.isMaybeCrossOriginObject() 16814 16815 args = [Argument("JSContext*", "cx")] 16816 ClassMethod.__init__( 16817 self, 16818 "getSameOriginPrototype", 16819 "JSObject*", 16820 args, 16821 virtual=True, 16822 override=True, 16823 const=True, 16824 ) 16825 self.descriptor = descriptor 16826 16827 def getBody(self): 16828 return dedent( 16829 """ 16830 return GetProtoObjectHandle(cx); 16831 """ 16832 ) 16833 16834 16835 class CGDOMJSProxyHandler_definePropertySameOrigin(ClassMethod): 16836 """ 16837 Implementation of definePropertySameOrigin. We only use this for 16838 cross-origin objects. 16839 """ 16840 16841 def __init__(self, descriptor): 16842 assert descriptor.isMaybeCrossOriginObject() 16843 16844 args = [ 16845 Argument("JSContext*", "cx"), 16846 Argument("JS::Handle<JSObject*>", "proxy"), 16847 Argument("JS::Handle<jsid>", "id"), 16848 Argument("JS::Handle<JS::PropertyDescriptor>", "desc"), 16849 Argument("JS::ObjectOpResult&", "result"), 16850 ] 16851 ClassMethod.__init__( 16852 self, 16853 "definePropertySameOrigin", 16854 "bool", 16855 args, 16856 virtual=True, 16857 override=True, 16858 const=True, 16859 ) 16860 self.descriptor = descriptor 16861 16862 def getBody(self): 16863 return dedent( 16864 """ 16865 return dom::DOMProxyHandler::defineProperty(cx, proxy, id, desc, result); 16866 """ 16867 ) 16868 16869 16870 class CGDOMJSProxyHandler_set(ClassMethod): 16871 """ 16872 Implementation of set(). We only use this for cross-origin objects. 16873 """ 16874 16875 def __init__(self, descriptor): 16876 assert descriptor.isMaybeCrossOriginObject() 16877 16878 args = [ 16879 Argument("JSContext*", "cx"), 16880 Argument("JS::Handle<JSObject*>", "proxy"), 16881 Argument("JS::Handle<jsid>", "id"), 16882 Argument("JS::Handle<JS::Value>", "v"), 16883 Argument("JS::Handle<JS::Value>", "receiver"), 16884 Argument("JS::ObjectOpResult&", "result"), 16885 ] 16886 ClassMethod.__init__( 16887 self, "set", "bool", args, virtual=True, override=True, const=True 16888 ) 16889 self.descriptor = descriptor 16890 16891 def getBody(self): 16892 return dedent( 16893 """ 16894 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 16895 return CrossOriginSet(cx, proxy, id, v, receiver, result); 16896 } 16897 16898 // Safe to enter the Realm of proxy now, since it's same-origin with us. 16899 JSAutoRealm ar(cx, proxy); 16900 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver); 16901 if (!MaybeWrapValue(cx, &wrappedReceiver)) { 16902 return false; 16903 } 16904 16905 JS::Rooted<JS::Value> wrappedValue(cx, v); 16906 if (!MaybeWrapValue(cx, &wrappedValue)) { 16907 return false; 16908 } 16909 16910 JS_MarkCrossZoneId(cx, id); 16911 16912 return dom::DOMProxyHandler::set(cx, proxy, id, wrappedValue, wrappedReceiver, result); 16913 """ 16914 ) 16915 16916 16917 class CGDOMJSProxyHandler_EnsureHolder(ClassMethod): 16918 """ 16919 Implementation of EnsureHolder(). We only use this for cross-origin objects. 16920 """ 16921 16922 def __init__(self, descriptor): 16923 args = [ 16924 Argument("JSContext*", "cx"), 16925 Argument("JS::Handle<JSObject*>", "proxy"), 16926 Argument("JS::MutableHandle<JSObject*>", "holder"), 16927 ] 16928 ClassMethod.__init__( 16929 self, "EnsureHolder", "bool", args, virtual=True, override=True, const=True 16930 ) 16931 self.descriptor = descriptor 16932 16933 def getBody(self): 16934 return dedent( 16935 """ 16936 return EnsureHolder(cx, proxy, 16937 JSCLASS_RESERVED_SLOTS(JS::GetClass(proxy)) - 1, 16938 sCrossOriginProperties, holder); 16939 """ 16940 ) 16941 16942 16943 class CGDOMJSProxyHandler(CGClass): 16944 def __init__(self, descriptor): 16945 assert ( 16946 descriptor.supportsIndexedProperties() 16947 or descriptor.supportsNamedProperties() 16948 or descriptor.isMaybeCrossOriginObject() 16949 ) 16950 16951 if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"): 16952 assert not descriptor.isMaybeCrossOriginObject() 16953 parentClass = "ShadowingDOMProxyHandler" 16954 elif descriptor.isMaybeCrossOriginObject(): 16955 parentClass = "MaybeCrossOriginObject<mozilla::dom::DOMProxyHandler>" 16956 else: 16957 parentClass = "mozilla::dom::DOMProxyHandler" 16958 16959 typeAliases = [ 16960 ClassUsingDeclaration("Base", parentClass), 16961 ] 16962 methods = [ 16963 CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor), 16964 CGDOMJSProxyHandler_defineProperty(descriptor), 16965 ClassUsingFromBaseDeclaration( 16966 "mozilla::dom::DOMProxyHandler", "defineProperty" 16967 ), 16968 CGDOMJSProxyHandler_ownPropNames(descriptor), 16969 CGDOMJSProxyHandler_hasOwn(descriptor), 16970 CGDOMJSProxyHandler_get(descriptor), 16971 CGDOMJSProxyHandler_className(descriptor), 16972 CGDOMJSProxyHandler_finalizeInBackground(descriptor), 16973 CGDOMJSProxyHandler_finalize(descriptor), 16974 CGJSProxyHandler_getInstance("DOMProxyHandler"), 16975 CGDOMJSProxyHandler_delete(descriptor), 16976 ] 16977 if getReflectedHTMLAttributesIface(descriptor): 16978 methods.append(CGDOMJSProxyHandler_trace(descriptor)) 16979 constructors = [ 16980 ClassConstructor([], constexpr=True, visibility="public", explicit=True) 16981 ] 16982 16983 if descriptor.supportsIndexedProperties(): 16984 methods.append(CGDOMJSProxyHandler_getElements(descriptor)) 16985 if descriptor.operations["IndexedSetter"] is not None or ( 16986 descriptor.operations["NamedSetter"] is not None 16987 and descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns") 16988 ): 16989 methods.append(CGDOMJSProxyHandler_setCustom(descriptor)) 16990 if descriptor.operations["LegacyCaller"]: 16991 methods.append(CGDOMJSProxyHandler_call()) 16992 methods.append(CGDOMJSProxyHandler_isCallable()) 16993 if descriptor.interface.hasProbablyShortLivingWrapper(): 16994 if not descriptor.wrapperCache: 16995 raise TypeError( 16996 "Need a wrapper cache to support nursery " 16997 "allocation of DOM objects" 16998 ) 16999 methods.append(CGDOMJSProxyHandler_canNurseryAllocate()) 17000 if descriptor.wrapperCache: 17001 methods.append(CGDOMJSProxyHandler_objectMoved(descriptor)) 17002 17003 if descriptor.isMaybeCrossOriginObject(): 17004 methods.extend( 17005 [ 17006 CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor), 17007 CGDOMJSProxyHandler_getSameOriginPrototype(descriptor), 17008 CGDOMJSProxyHandler_definePropertySameOrigin(descriptor), 17009 CGDOMJSProxyHandler_set(descriptor), 17010 CGDOMJSProxyHandler_EnsureHolder(descriptor), 17011 ClassUsingFromBaseDeclaration( 17012 "MaybeCrossOriginObjectMixins", "EnsureHolder" 17013 ), 17014 ] 17015 ) 17016 17017 CGClass.__init__( 17018 self, 17019 "DOMProxyHandler", 17020 bases=[ClassBase(parentClass)], 17021 typeAliases=typeAliases, 17022 constructors=constructors, 17023 methods=methods, 17024 ) 17025 17026 17027 class CGDOMJSProxyHandlerDeclarer(CGThing): 17028 """ 17029 A class for declaring a DOMProxyHandler. 17030 """ 17031 17032 def __init__(self, handlerThing): 17033 self.handlerThing = handlerThing 17034 17035 def declare(self): 17036 # Our class declaration should happen when we're defining 17037 return "" 17038 17039 def define(self): 17040 return self.handlerThing.declare() 17041 17042 17043 class CGDOMJSProxyHandlerDefiner(CGThing): 17044 """ 17045 A class for defining a DOMProxyHandler. 17046 """ 17047 17048 def __init__(self, handlerThing): 17049 self.handlerThing = handlerThing 17050 17051 def declare(self): 17052 return "" 17053 17054 def define(self): 17055 return self.handlerThing.define() 17056 17057 17058 def stripTrailingWhitespace(text): 17059 tail = "\n" if text.endswith("\n") else "" 17060 lines = text.splitlines() 17061 return "\n".join(line.rstrip() for line in lines) + tail 17062 17063 17064 class MemberProperties: 17065 def __init__(self): 17066 self.isCrossOriginMethod = False 17067 self.isCrossOriginGetter = False 17068 self.isCrossOriginSetter = False 17069 17070 17071 def memberProperties(m, descriptor): 17072 props = MemberProperties() 17073 if m.isMethod(): 17074 if not m.isIdentifierLess() or m == descriptor.operations["Stringifier"]: 17075 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject(): 17076 if m.getExtendedAttribute("CrossOriginCallable"): 17077 props.isCrossOriginMethod = True 17078 elif m.isAttr(): 17079 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject(): 17080 if m.getExtendedAttribute("CrossOriginReadable"): 17081 props.isCrossOriginGetter = True 17082 if not m.readonly: 17083 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject(): 17084 if m.getExtendedAttribute("CrossOriginWritable"): 17085 props.isCrossOriginSetter = True 17086 elif m.getExtendedAttribute("PutForwards"): 17087 if m.getExtendedAttribute("CrossOriginWritable"): 17088 props.isCrossOriginSetter = True 17089 elif m.getExtendedAttribute("Replaceable") or m.getExtendedAttribute( 17090 "LegacyLenientSetter" 17091 ): 17092 if m.getExtendedAttribute("CrossOriginWritable"): 17093 props.isCrossOriginSetter = True 17094 17095 return props 17096 17097 17098 class CGDescriptor(CGThing): 17099 def __init__(self, descriptor: Descriptor, attributeTemplates): 17100 CGThing.__init__(self) 17101 17102 assert ( 17103 not descriptor.concrete 17104 or descriptor.interface.hasInterfacePrototypeObject() 17105 or descriptor.hasOrdinaryObjectPrototype() 17106 ) 17107 17108 self.name = descriptor.interface.getClassName() 17109 self._deps = descriptor.interface.getDeps() 17110 17111 iteratorCGThings = None 17112 if ( 17113 descriptor.interface.isIterable() 17114 and descriptor.interface.maplikeOrSetlikeOrIterable.isPairIterator() 17115 ) or descriptor.interface.isAsyncIterable(): 17116 # We need the Wrap function when using the [Async]IterableIterator type, so we want to declare it before we need it. We don't really want to expose it in the header file, so we make it static too. 17117 iteratorCGThings = [] 17118 itr_iface = ( 17119 descriptor.interface.maplikeOrSetlikeOrIterable.iteratorType.inner 17120 ) 17121 iteratorDescriptor = descriptor.getDescriptor(itr_iface.identifier.name) 17122 iteratorCGThings.append( 17123 CGWrapNonWrapperCacheMethod( 17124 iteratorDescriptor, static=True, signatureOnly=True 17125 ) 17126 ) 17127 iteratorCGThings = CGList( 17128 (CGIndenter(t, declareOnly=True) for t in iteratorCGThings), "\n" 17129 ) 17130 iteratorCGThings = CGWrapper(iteratorCGThings, pre="\n", post="\n") 17131 iteratorCGThings = CGWrapper( 17132 CGNamespace( 17133 toBindingNamespace(iteratorDescriptor.name), iteratorCGThings 17134 ), 17135 post="\n", 17136 ) 17137 17138 cgThings = [] 17139 17140 isIteratorInterface = ( 17141 descriptor.interface.isIteratorInterface() 17142 or descriptor.interface.isAsyncIteratorInterface() 17143 ) 17144 if not isIteratorInterface: 17145 cgThings.append( 17146 CGGeneric(declare="typedef %s NativeType;\n" % descriptor.nativeType) 17147 ) 17148 parent = descriptor.interface.parent 17149 if parent: 17150 cgThings.append( 17151 CGGeneric( 17152 "static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n" 17153 ' "Can\'t inherit from an interface with a different ownership model.");\n' 17154 % toBindingNamespace(descriptor.parentPrototypeName) 17155 ) 17156 ) 17157 17158 defaultToJSONMethod = None 17159 needCrossOriginPropertyArrays = False 17160 unscopableNames = list() 17161 17162 for n in descriptor.interface.legacyFactoryFunctions: 17163 cgThings.append( 17164 CGClassConstructor(descriptor, n, LegacyFactoryFunctionName(n)) 17165 ) 17166 17167 if descriptor.attributeTemplates is not None: 17168 for template in descriptor.attributeTemplates: 17169 if template.getter is not None: 17170 cgThings.append( 17171 CGTemplateForSpecializedGetter(descriptor, template) 17172 ) 17173 if template.setter is not None: 17174 cgThings.append( 17175 CGTemplateForSpecializedSetter(descriptor, template) 17176 ) 17177 17178 if descriptor.interface.reflectedHTMLAttributesReturningFrozenArray: 17179 cgThings.append(CGDefineHTMLAttributeSlots(descriptor)) 17180 17181 for m in descriptor.interface.members: 17182 if m.isMethod() and m.identifier.name == "QueryInterface": 17183 continue 17184 17185 props = memberProperties(m, descriptor) 17186 17187 if m.isMethod(): 17188 if m.getExtendedAttribute("Unscopable"): 17189 assert not m.isStatic() 17190 unscopableNames.append(m.identifier.name) 17191 if m.isDefaultToJSON(): 17192 defaultToJSONMethod = m 17193 elif ( 17194 not m.isIdentifierLess() 17195 or m == descriptor.operations["Stringifier"] 17196 ): 17197 if m.isStatic(): 17198 assert descriptor.interface.hasInterfaceObject() 17199 cgThings.append(CGStaticMethod(descriptor, m)) 17200 if m.returnsPromise(): 17201 cgThings.append(CGStaticMethodJitinfo(m)) 17202 elif descriptor.interface.hasInterfacePrototypeObject(): 17203 specializedMethod = CGSpecializedMethod(descriptor, m) 17204 cgThings.append(specializedMethod) 17205 if m.returnsPromise(): 17206 cgThings.append( 17207 CGMethodPromiseWrapper(descriptor, specializedMethod) 17208 ) 17209 cgThings.append(CGMemberJITInfo(descriptor, m)) 17210 if props.isCrossOriginMethod: 17211 needCrossOriginPropertyArrays = True 17212 # If we've hit the maplike/setlike member itself, go ahead and 17213 # generate its convenience functions. 17214 elif m.isMaplikeOrSetlike(): 17215 cgThings.append(CGMaplikeOrSetlikeHelperGenerator(descriptor, m)) 17216 elif m.isAttr(): 17217 if m.type.isObservableArray(): 17218 cgThings.append( 17219 CGObservableArrayProxyHandlerGenerator(descriptor, m) 17220 ) 17221 cgThings.append(CGObservableArrayHelperGenerator(descriptor, m)) 17222 if m.getExtendedAttribute("Unscopable"): 17223 assert not m.isStatic() 17224 unscopableNames.append(m.identifier.name) 17225 if m.isStatic(): 17226 assert descriptor.interface.hasInterfaceObject() 17227 cgThings.append(CGStaticGetter(descriptor, m)) 17228 elif descriptor.interface.hasInterfacePrototypeObject(): 17229 template = m.getExtendedAttribute("BindingTemplate") 17230 if template is not None: 17231 templateName = template[0][0] 17232 additionalArg = template[0][1] 17233 if not (m.type.isPrimitive() or m.type.isString()): 17234 raise TypeError( 17235 "We only support primitives or strings on templated attributes. " 17236 "Attribute '%s' on interface '%s' has type '%s' but tries to " 17237 "use template '%s'" 17238 % ( 17239 m.identifier.name, 17240 descriptor.interface.identifier.name, 17241 m.type, 17242 templateName, 17243 ) 17244 ) 17245 template = attributeTemplates.get(templateName) 17246 specializedGetter = CGSpecializedTemplatedGetter( 17247 descriptor, m, template, additionalArg 17248 ) 17249 else: 17250 specializedGetter = CGSpecializedGetter(descriptor, m) 17251 cgThings.append(specializedGetter) 17252 if m.type.isPromise(): 17253 cgThings.append( 17254 CGGetterPromiseWrapper(descriptor, specializedGetter) 17255 ) 17256 if props.isCrossOriginGetter: 17257 needCrossOriginPropertyArrays = True 17258 if not m.readonly: 17259 if m.isStatic(): 17260 assert descriptor.interface.hasInterfaceObject() 17261 cgThings.append(CGStaticSetter(descriptor, m)) 17262 elif descriptor.interface.hasInterfacePrototypeObject(): 17263 template = m.getExtendedAttribute("BindingTemplate") 17264 if template is not None: 17265 if isinstance(template[0], list): 17266 templateName = template[0][0] 17267 additionalArg = template[0][1] 17268 else: 17269 templateName = template[0] 17270 additionalArg = None 17271 template = attributeTemplates.get(templateName) 17272 specializedSetter = CGSpecializedTemplatedSetter( 17273 descriptor, m, template, additionalArg 17274 ) 17275 else: 17276 specializedSetter = CGSpecializedSetter(descriptor, m) 17277 cgThings.append(specializedSetter) 17278 if props.isCrossOriginSetter: 17279 needCrossOriginPropertyArrays = True 17280 elif m.getExtendedAttribute("PutForwards"): 17281 cgThings.append(CGSpecializedForwardingSetter(descriptor, m)) 17282 if props.isCrossOriginSetter: 17283 needCrossOriginPropertyArrays = True 17284 elif m.getExtendedAttribute("Replaceable"): 17285 cgThings.append(CGSpecializedReplaceableSetter(descriptor, m)) 17286 elif m.getExtendedAttribute("LegacyLenientSetter"): 17287 # XXX In this case, we need to add an include for mozilla/dom/Document.h to the generated cpp file. 17288 cgThings.append(CGSpecializedLenientSetter(descriptor, m)) 17289 if ( 17290 not m.isStatic() 17291 and descriptor.interface.hasInterfacePrototypeObject() 17292 ): 17293 cgThings.append(CGMemberJITInfo(descriptor, m)) 17294 if m.isConst() and m.type.isPrimitive(): 17295 cgThings.append(CGConstDefinition(m)) 17296 17297 if defaultToJSONMethod: 17298 cgThings.append(CGDefaultToJSONMethod(descriptor, defaultToJSONMethod)) 17299 cgThings.append(CGMemberJITInfo(descriptor, defaultToJSONMethod)) 17300 17301 if descriptor.concrete and not descriptor.proxy: 17302 # Always have a finalize hook, regardless of whether the class 17303 # wants a custom hook. 17304 cgThings.append(CGClassFinalizeHook(descriptor)) 17305 17306 properties = PropertyArrays(descriptor) 17307 cgThings.append(CGGeneric(define=str(properties))) 17308 cgThings.append(CGNativeProperties(descriptor, properties)) 17309 17310 if defaultToJSONMethod: 17311 # Now that we know about our property arrays, we can 17312 # output our "collect attribute values" method, which uses those. 17313 cgThings.append( 17314 CGCollectJSONAttributesMethod(descriptor, defaultToJSONMethod) 17315 ) 17316 17317 # Declare our DOMProxyHandler. 17318 if descriptor.concrete and descriptor.proxy: 17319 cgThings.append( 17320 CGGeneric( 17321 fill( 17322 """ 17323 static_assert(std::is_base_of_v<nsISupports, ${nativeType}>, 17324 "We don't support non-nsISupports native classes for " 17325 "proxy-based bindings yet"); 17326 17327 """, 17328 nativeType=descriptor.nativeType, 17329 ) 17330 ) 17331 ) 17332 if not descriptor.wrapperCache: 17333 raise TypeError( 17334 "We need a wrappercache to support expandos for proxy-based " 17335 "bindings (" + descriptor.name + ")" 17336 ) 17337 handlerThing = CGDOMJSProxyHandler(descriptor) 17338 cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing)) 17339 cgThings.append(CGProxyIsProxy(descriptor)) 17340 cgThings.append(CGProxyUnwrap(descriptor)) 17341 17342 # Set up our Xray callbacks as needed. This needs to come 17343 # after we have our DOMProxyHandler defined. 17344 if descriptor.wantsXrays: 17345 if descriptor.concrete and descriptor.proxy: 17346 if descriptor.needsXrayNamedDeleterHook(): 17347 cgThings.append(CGDeleteNamedProperty(descriptor)) 17348 elif descriptor.needsXrayResolveHooks(): 17349 cgThings.append(CGResolveOwnPropertyViaResolve(descriptor)) 17350 cgThings.append( 17351 CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor) 17352 ) 17353 if descriptor.wantsXrayExpandoClass: 17354 cgThings.append(CGXrayExpandoJSClass(descriptor)) 17355 17356 # Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff 17357 # done, set up our NativePropertyHooks. 17358 cgThings.append(CGNativePropertyHooks(descriptor, properties)) 17359 17360 if descriptor.interface.isNamespace(): 17361 cgThings.append(CGNamespaceObjectJSClass(descriptor)) 17362 elif descriptor.interface.hasInterfaceObject(): 17363 cgThings.append(CGClassConstructor(descriptor, descriptor.interface.ctor())) 17364 cgThings.append(CGInterfaceObjectInfo(descriptor)) 17365 cgThings.append(CGLegacyFactoryFunctions(descriptor)) 17366 17367 cgThings.append(CGLegacyCallHook(descriptor)) 17368 if descriptor.interface.getExtendedAttribute("NeedResolve"): 17369 cgThings.append(CGResolveHook(descriptor)) 17370 cgThings.append(CGMayResolveHook(descriptor)) 17371 cgThings.append(CGEnumerateHook(descriptor)) 17372 17373 if descriptor.hasNamedPropertiesObject: 17374 cgThings.append(CGGetNamedPropertiesObjectMethod(descriptor)) 17375 17376 if descriptor.interface.hasInterfacePrototypeObject(): 17377 cgThings.append(CGPrototypeJSClass(descriptor, properties)) 17378 17379 if ( 17380 descriptor.interface.hasInterfaceObject() 17381 and not descriptor.interface.isExternal() 17382 and descriptor.isExposedConditionally() 17383 ): 17384 cgThings.append(CGConstructorEnabled(descriptor)) 17385 17386 if ( 17387 descriptor.interface.hasMembersInSlots() 17388 and descriptor.interface.hasChildInterfaces() 17389 ): 17390 raise TypeError( 17391 "We don't support members in slots on " 17392 "non-leaf interfaces like %s" % descriptor.interface.identifier.name 17393 ) 17394 17395 if descriptor.needsMissingPropUseCounters: 17396 cgThings.append(CGCountMaybeMissingProperty(descriptor)) 17397 17398 if descriptor.interface.identifier.name == "HTMLDocument": 17399 cgThings.append(CGInterfaceHasProperty(descriptor)) 17400 17401 # CGDOMProxyJSClass/CGDOMJSClass need GetProtoObjectHandle, but we don't 17402 # want to export it for the iterator interfaces, or if we don't need it 17403 # for child interfaces or for the named properties object. 17404 protoObjectHandleGetterIsStatic = descriptor.concrete and ( 17405 isIteratorInterface 17406 or ( 17407 descriptor.interface.hasInterfacePrototypeObject() 17408 and not descriptor.interface.hasChildInterfaces() 17409 and not descriptor.hasNamedPropertiesObject 17410 ) 17411 ) 17412 if descriptor.concrete: 17413 if descriptor.interface.isSerializable(): 17414 cgThings.append(CGSerializer(descriptor)) 17415 cgThings.append(CGDeserializer(descriptor)) 17416 17417 if protoObjectHandleGetterIsStatic: 17418 cgThings.append( 17419 CGGetProtoObjectHandleMethod( 17420 descriptor, static=True, signatureOnly=True 17421 ) 17422 ) 17423 17424 if descriptor.proxy: 17425 cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing)) 17426 cgThings.append(CGDOMProxyJSClass(descriptor)) 17427 else: 17428 cgThings.append(CGDOMJSClass(descriptor)) 17429 17430 if descriptor.interface.hasMembersInSlots(): 17431 cgThings.append(CGUpdateMemberSlotsMethod(descriptor)) 17432 17433 if descriptor.isGlobal(): 17434 assert descriptor.wrapperCache 17435 cgThings.append(CGWrapGlobalMethod(descriptor, properties)) 17436 elif descriptor.wrapperCache: 17437 cgThings.append(CGWrapWithCacheMethod(descriptor)) 17438 cgThings.append(CGWrapMethod(descriptor)) 17439 else: 17440 cgThings.append( 17441 CGWrapNonWrapperCacheMethod(descriptor, static=isIteratorInterface) 17442 ) 17443 17444 # If we're not wrappercached, we don't know how to clear our 17445 # cached values, since we can't get at the JSObject. 17446 if descriptor.wrapperCache: 17447 cgThings.extend( 17448 CGClearCachedValueMethod(descriptor, m) 17449 for m in clearableCachedAttrs(descriptor) 17450 ) 17451 17452 haveUnscopables = ( 17453 len(unscopableNames) != 0 17454 and descriptor.interface.hasInterfacePrototypeObject() 17455 ) 17456 if haveUnscopables: 17457 cgThings.append( 17458 CGList( 17459 [ 17460 CGGeneric("static const char* const unscopableNames[] = {"), 17461 CGIndenter( 17462 CGList( 17463 [CGGeneric('"%s"' % name) for name in unscopableNames] 17464 + [CGGeneric("nullptr")], 17465 ",\n", 17466 ) 17467 ), 17468 CGGeneric("};\n"), 17469 ], 17470 "\n", 17471 ) 17472 ) 17473 17474 legacyWindowAliases = descriptor.interface.legacyWindowAliases 17475 haveLegacyWindowAliases = len(legacyWindowAliases) != 0 17476 if haveLegacyWindowAliases: 17477 cgThings.append( 17478 CGList( 17479 [ 17480 CGGeneric("static const char* const legacyWindowAliases[] = {"), 17481 CGIndenter( 17482 CGList( 17483 [ 17484 CGGeneric('"%s"' % name) 17485 for name in legacyWindowAliases 17486 ] 17487 + [CGGeneric("nullptr")], 17488 ",\n", 17489 ) 17490 ), 17491 CGGeneric("};\n"), 17492 ], 17493 "\n", 17494 ) 17495 ) 17496 17497 if not descriptor.hasOrdinaryObjectPrototype(): 17498 # CGCreateInterfaceObjectsMethod needs to come after our 17499 # CGDOMJSClass and unscopables, if any. 17500 cgThings.append( 17501 CGCreateInterfaceObjectsMethod( 17502 descriptor, 17503 properties, 17504 haveUnscopables, 17505 haveLegacyWindowAliases, 17506 static=isIteratorInterface, 17507 ) 17508 ) 17509 17510 # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need 17511 # to come after CGCreateInterfaceObjectsMethod. 17512 if descriptor.interface.hasInterfacePrototypeObject(): 17513 cgThings.append( 17514 CGGetProtoObjectHandleMethod( 17515 descriptor, static=protoObjectHandleGetterIsStatic 17516 ) 17517 ) 17518 if descriptor.interface.hasChildInterfaces(): 17519 assert not isIteratorInterface 17520 cgThings.append(CGGetProtoObjectMethod(descriptor)) 17521 if descriptor.interface.hasInterfaceObject(): 17522 cgThings.append(CGGetConstructorObjectHandleMethod(descriptor)) 17523 cgThings.append( 17524 CGCreateAndDefineOnGlobalMethod( 17525 descriptor, 17526 ) 17527 ) 17528 17529 # See whether we need to generate cross-origin property arrays. 17530 if needCrossOriginPropertyArrays: 17531 cgThings.append(CGCrossOriginProperties(descriptor)) 17532 17533 cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n") 17534 cgThings = CGWrapper(cgThings, pre="\n", post="\n") 17535 cgThings = CGWrapper( 17536 CGNamespace(toBindingNamespace(descriptor.name), cgThings), post="\n" 17537 ) 17538 self.cgRoot = CGList([iteratorCGThings, cgThings], "\n") 17539 17540 def declare(self): 17541 return self.cgRoot.declare() 17542 17543 def define(self): 17544 return self.cgRoot.define() 17545 17546 def forward_declare(self): 17547 return f"class {self.name};" 17548 17549 def deps(self): 17550 return self._deps 17551 17552 17553 class CGNamespacedEnum(CGThing): 17554 def __init__(self, namespace, enumName, names, values, comment=""): 17555 if not values: 17556 values = [] 17557 17558 # Account for explicit enum values. 17559 entries = [] 17560 for i in range(0, len(names)): 17561 if len(values) > i and values[i] is not None: 17562 entry = "%s = %s" % (names[i], values[i]) 17563 else: 17564 entry = names[i] 17565 entries.append(entry) 17566 17567 # Append a Count. 17568 entries.append("_" + enumName + "_Count") 17569 17570 # Indent. 17571 entries = [" " + e for e in entries] 17572 17573 # Build the enum body. 17574 enumstr = comment + "enum %s : uint16_t\n{\n%s\n};\n" % ( 17575 enumName, 17576 ",\n".join(entries), 17577 ) 17578 curr = CGGeneric(declare=enumstr) 17579 17580 # Add some whitespace padding. 17581 curr = CGWrapper(curr, pre="\n", post="\n") 17582 17583 # Add the namespace. 17584 curr = CGNamespace(namespace, curr) 17585 17586 # Add the typedef 17587 typedef = "\ntypedef %s::%s %s;\n\n" % (namespace, enumName, enumName) 17588 curr = CGList([curr, CGGeneric(declare=typedef)]) 17589 17590 # Save the result. 17591 self.node = curr 17592 17593 def declare(self): 17594 return self.node.declare() 17595 17596 def define(self): 17597 return "" 17598 17599 17600 def initIdsClassMethod(identifiers, atomCacheName): 17601 idinit = [ 17602 '!atomsCache->%s.init(cx, "%s")' % (CGDictionary.makeIdName(id), id) 17603 for id in identifiers 17604 ] 17605 idinit.reverse() 17606 body = fill( 17607 """ 17608 MOZ_ASSERT(reinterpret_cast<jsid*>(atomsCache)->isVoid()); 17609 17610 // Initialize these in reverse order so that any failure leaves the first one 17611 // uninitialized. 17612 if (${idinit}) { 17613 return false; 17614 } 17615 return true; 17616 """, 17617 idinit=" ||\n ".join(idinit), 17618 ) 17619 return ClassMethod( 17620 "InitIds", 17621 "bool", 17622 [Argument("JSContext*", "cx"), Argument("%s*" % atomCacheName, "atomsCache")], 17623 static=True, 17624 body=body, 17625 visibility="private", 17626 ) 17627 17628 17629 class CGDictionary(CGThing): 17630 def __init__(self, dictionary: IDLDictionary, descriptorProvider): 17631 self.dictionary = dictionary 17632 self.descriptorProvider = descriptorProvider 17633 self.needToInitIds = len(dictionary.members) > 0 17634 self.memberInfo = [ 17635 ( 17636 member, 17637 getJSToNativeConversionInfo( 17638 member.type, 17639 descriptorProvider, 17640 isMember="Dictionary", 17641 isOptional=member.canHaveMissingValue(), 17642 isKnownMissing=not dictionary.needsConversionFromJS, 17643 defaultValue=member.defaultValue, 17644 sourceDescription=self.getMemberSourceDescription(member), 17645 ), 17646 ) 17647 for member in dictionary.members 17648 ] 17649 17650 # If we have a union member which is going to be declared in a different 17651 # header but contains something that will be declared in the same header 17652 # as us, bail: the C++ includes won't work out. 17653 for member in dictionary.members: 17654 type = member.type.unroll() 17655 if type.isUnion() and CGHeaders.getUnionDeclarationFilename( 17656 descriptorProvider.getConfig(), type 17657 ) != CGHeaders.getDeclarationFilename(dictionary): 17658 for t in type.flatMemberTypes: 17659 if t.isDictionary() and CGHeaders.getDeclarationFilename( 17660 t.inner 17661 ) == CGHeaders.getDeclarationFilename(dictionary): 17662 raise TypeError( 17663 "Dictionary contains a union that will live in a different " 17664 "header that contains a dictionary from the same header as " 17665 "the original dictionary. This won't compile. Move the " 17666 "inner dictionary to a different Web IDL file to move it " 17667 "to a different header.\n%s\n%s" 17668 % (t.location, t.inner.location) 17669 ) 17670 self.structs = self.getStructs() 17671 17672 def declare(self): 17673 return self.structs.declare() 17674 17675 def define(self): 17676 return self.structs.define() 17677 17678 def forward_declare(self): 17679 return f"struct {self.dictionary.identifier.name};" 17680 17681 def base(self): 17682 if self.dictionary.parent: 17683 return self.makeClassName(self.dictionary.parent) 17684 return "DictionaryBase" 17685 17686 def initMethod(self): 17687 """ 17688 This function outputs the body of the Init() method for the dictionary. 17689 17690 For the most part, this is some bookkeeping for our atoms so 17691 we can avoid atomizing strings all the time, then we just spit 17692 out the getMemberConversion() output for each member, 17693 separated by newlines. 17694 17695 """ 17696 body = dedent( 17697 """ 17698 // Passing a null JSContext is OK only if we're initing from null, 17699 // Since in that case we will not have to do any property gets 17700 // Also evaluate isNullOrUndefined in order to avoid false-positive 17701 // checkers by static analysis tools 17702 MOZ_ASSERT_IF(!cx, val.isNull() && val.isNullOrUndefined()); 17703 """ 17704 ) 17705 17706 if self.needToInitIds: 17707 body += fill( 17708 """ 17709 ${dictName}Atoms* atomsCache = nullptr; 17710 if (cx) { 17711 atomsCache = GetAtomCache<${dictName}Atoms>(cx); 17712 if (reinterpret_cast<jsid*>(atomsCache)->isVoid() && 17713 !InitIds(cx, atomsCache)) { 17714 return false; 17715 } 17716 } 17717 17718 """, 17719 dictName=self.makeClassName(self.dictionary), 17720 ) 17721 17722 if self.dictionary.parent: 17723 body += fill( 17724 """ 17725 // Per spec, we init the parent's members first 17726 if (!${dictName}::Init(cx, val)) { 17727 return false; 17728 } 17729 17730 """, 17731 dictName=self.makeClassName(self.dictionary.parent), 17732 ) 17733 else: 17734 body += dedent( 17735 """ 17736 if (!IsConvertibleToDictionary(val)) { 17737 return cx.ThrowErrorMessage<MSG_CONVERSION_ERROR>(sourceDescription, "dictionary"); 17738 } 17739 17740 """ 17741 ) 17742 17743 memberInits = [self.getMemberConversion(m).define() for m in self.memberInfo] 17744 if memberInits: 17745 body += fill( 17746 """ 17747 bool isNull = val.isNullOrUndefined(); 17748 // We only need these if !isNull, in which case we have |cx|. 17749 Maybe<JS::Rooted<JSObject *> > object; 17750 Maybe<JS::Rooted<JS::Value> > temp; 17751 if (!isNull) { 17752 MOZ_ASSERT(cx); 17753 object.emplace(cx, &val.toObject()); 17754 temp.emplace(cx); 17755 } 17756 $*{memberInits} 17757 """, 17758 memberInits="\n".join(memberInits), 17759 ) 17760 17761 body += "return true;\n" 17762 17763 return ClassMethod( 17764 "Init", 17765 "bool", 17766 [ 17767 Argument("BindingCallContext&", "cx"), 17768 Argument("JS::Handle<JS::Value>", "val"), 17769 Argument("const char*", "sourceDescription", default='"Value"'), 17770 Argument("bool", "passedToJSImpl", default="false"), 17771 ], 17772 body=body, 17773 ) 17774 17775 def initWithoutCallContextMethod(self): 17776 """ 17777 This function outputs the body of an Init() method for the dictionary 17778 that takes just a JSContext*. This is needed for non-binding consumers. 17779 """ 17780 body = dedent( 17781 """ 17782 // We don't want to use sourceDescription for our context here; 17783 // that's not really what it's formatted for. 17784 BindingCallContext cx(cx_, nullptr); 17785 return Init(cx, val, sourceDescription, passedToJSImpl); 17786 """ 17787 ) 17788 return ClassMethod( 17789 "Init", 17790 "bool", 17791 [ 17792 Argument("JSContext*", "cx_"), 17793 Argument("JS::Handle<JS::Value>", "val"), 17794 Argument("const char*", "sourceDescription", default='"Value"'), 17795 Argument("bool", "passedToJSImpl", default="false"), 17796 ], 17797 body=body, 17798 ) 17799 17800 def simpleInitMethod(self): 17801 """ 17802 This function outputs the body of the Init() method for the dictionary, 17803 for cases when we are just default-initializing it. 17804 17805 """ 17806 relevantMembers = [ 17807 m 17808 for m in self.memberInfo 17809 # We only need to init the things that can have 17810 # default values. 17811 if m[0].optional and m[0].defaultValue 17812 ] 17813 17814 # We mostly avoid outputting code that uses cx in our native-to-JS 17815 # conversions, but there is one exception: we may have a 17816 # dictionary-typed member that _does_ generally support conversion from 17817 # JS. If we have such a thing, we can pass it a null JSContext and 17818 # JS::NullHandleValue to default-initialize it, but since the 17819 # native-to-JS templates hardcode `cx` as the JSContext value, we're 17820 # going to need to provide that. 17821 haveMemberThatNeedsCx = any( 17822 m[0].type.isDictionary() and m[0].type.unroll().inner.needsConversionFromJS 17823 for m in relevantMembers 17824 ) 17825 if haveMemberThatNeedsCx: 17826 body = dedent( 17827 """ 17828 JSContext* cx = nullptr; 17829 """ 17830 ) 17831 else: 17832 body = "" 17833 17834 if self.dictionary.parent: 17835 if self.dictionary.parent.needsConversionFromJS: 17836 args = "nullptr, JS::NullHandleValue" 17837 else: 17838 args = "" 17839 body += fill( 17840 """ 17841 // We init the parent's members first 17842 if (!${dictName}::Init(${args})) { 17843 return false; 17844 } 17845 17846 """, 17847 dictName=self.makeClassName(self.dictionary.parent), 17848 args=args, 17849 ) 17850 17851 memberInits = [ 17852 self.getMemberConversion(m, isKnownMissing=True).define() 17853 for m in relevantMembers 17854 ] 17855 if memberInits: 17856 body += fill( 17857 """ 17858 $*{memberInits} 17859 """, 17860 memberInits="\n".join(memberInits), 17861 ) 17862 17863 body += "return true;\n" 17864 17865 return ClassMethod( 17866 "Init", 17867 "bool", 17868 [ 17869 Argument("const char*", "sourceDescription", default='"Value"'), 17870 Argument("bool", "passedToJSImpl", default="false"), 17871 ], 17872 body=body, 17873 ) 17874 17875 def initFromJSONMethod(self): 17876 return ClassMethod( 17877 "Init", 17878 "bool", 17879 [Argument("const nsAString&", "aJSON")], 17880 body=dedent( 17881 """ 17882 AutoJSAPI jsapi; 17883 JSObject* cleanGlobal = SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::BindingDetail); 17884 if (!cleanGlobal) { 17885 return false; 17886 } 17887 if (!jsapi.Init(cleanGlobal)) { 17888 return false; 17889 } 17890 JSContext* cx = jsapi.cx(); 17891 JS::Rooted<JS::Value> json(cx); 17892 bool ok = ParseJSON(cx, aJSON, &json); 17893 NS_ENSURE_TRUE(ok, false); 17894 return Init(cx, json); 17895 """ 17896 ), 17897 ) 17898 17899 def toJSONMethod(self): 17900 return ClassMethod( 17901 "ToJSON", 17902 "bool", 17903 [Argument("nsAString&", "aJSON")], 17904 body=dedent( 17905 """ 17906 AutoJSAPI jsapi; 17907 jsapi.Init(); 17908 JSContext *cx = jsapi.cx(); 17909 // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here 17910 // because we'll only be creating objects, in ways that have no 17911 // side-effects, followed by a call to JS::ToJSONMaybeSafely, 17912 // which likewise guarantees no side-effects for the sorts of 17913 // things we will pass it. 17914 JSObject* scope = UnprivilegedJunkScopeOrWorkerGlobal(fallible); 17915 if (!scope) { 17916 JS_ReportOutOfMemory(cx); 17917 return false; 17918 } 17919 JSAutoRealm ar(cx, scope); 17920 JS::Rooted<JS::Value> val(cx); 17921 if (!ToObjectInternal(cx, &val)) { 17922 return false; 17923 } 17924 JS::Rooted<JSObject*> obj(cx, &val.toObject()); 17925 return StringifyToJSON(cx, obj, aJSON); 17926 """ 17927 ), 17928 const=True, 17929 ) 17930 17931 def toObjectInternalMethod(self): 17932 body = "" 17933 if self.needToInitIds: 17934 body += fill( 17935 """ 17936 ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx); 17937 if (reinterpret_cast<jsid*>(atomsCache)->isVoid() && 17938 !InitIds(cx, atomsCache)) { 17939 return false; 17940 } 17941 17942 """, 17943 dictName=self.makeClassName(self.dictionary), 17944 ) 17945 17946 if self.dictionary.parent: 17947 body += fill( 17948 """ 17949 // Per spec, we define the parent's members first 17950 if (!${dictName}::ToObjectInternal(cx, rval)) { 17951 return false; 17952 } 17953 JS::Rooted<JSObject*> obj(cx, &rval.toObject()); 17954 17955 """, 17956 dictName=self.makeClassName(self.dictionary.parent), 17957 ) 17958 else: 17959 body += dedent( 17960 """ 17961 JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx)); 17962 if (!obj) { 17963 return false; 17964 } 17965 rval.set(JS::ObjectValue(*obj)); 17966 17967 """ 17968 ) 17969 17970 if self.memberInfo: 17971 body += "\n".join( 17972 self.getMemberDefinition(m).define() for m in self.memberInfo 17973 ) 17974 body += "\nreturn true;\n" 17975 17976 return ClassMethod( 17977 "ToObjectInternal", 17978 "bool", 17979 [ 17980 Argument("JSContext*", "cx"), 17981 Argument("JS::MutableHandle<JS::Value>", "rval"), 17982 ], 17983 const=True, 17984 body=body, 17985 ) 17986 17987 def initIdsMethod(self): 17988 assert self.needToInitIds 17989 return initIdsClassMethod( 17990 [m.identifier.name for m in self.dictionary.members], 17991 "%sAtoms" % self.makeClassName(self.dictionary), 17992 ) 17993 17994 def traceDictionaryMethod(self): 17995 body = "" 17996 if self.dictionary.parent: 17997 cls = self.makeClassName(self.dictionary.parent) 17998 body += "%s::TraceDictionary(trc);\n" % cls 17999 18000 memberTraces = [ 18001 self.getMemberTrace(m) 18002 for m in self.dictionary.members 18003 if typeNeedsRooting(m.type) 18004 ] 18005 18006 if memberTraces: 18007 body += "\n".join(memberTraces) 18008 18009 return ClassMethod( 18010 "TraceDictionary", 18011 "void", 18012 [ 18013 Argument("JSTracer*", "trc"), 18014 ], 18015 body=body, 18016 ) 18017 18018 @staticmethod 18019 def dictionaryNeedsCycleCollection(dictionary): 18020 return any(idlTypeNeedsCycleCollection(m.type) for m in dictionary.members) or ( 18021 dictionary.parent 18022 and CGDictionary.dictionaryNeedsCycleCollection(dictionary.parent) 18023 ) 18024 18025 def traverseForCCMethod(self): 18026 body = "" 18027 if self.dictionary.parent and self.dictionaryNeedsCycleCollection( 18028 self.dictionary.parent 18029 ): 18030 cls = self.makeClassName(self.dictionary.parent) 18031 body += "%s::TraverseForCC(aCallback, aFlags);\n" % cls 18032 18033 for m, _ in self.memberInfo: 18034 if idlTypeNeedsCycleCollection(m.type): 18035 memberName = self.makeMemberName(m.identifier.name) 18036 body += ( 18037 'ImplCycleCollectionTraverse(aCallback, %s, "%s", aFlags);\n' 18038 % (memberName, memberName) 18039 ) 18040 18041 return ClassMethod( 18042 "TraverseForCC", 18043 "void", 18044 [ 18045 Argument("nsCycleCollectionTraversalCallback&", "aCallback"), 18046 Argument("uint32_t", "aFlags"), 18047 ], 18048 body=body, 18049 # Inline so we don't pay a codesize hit unless someone actually uses 18050 # this traverse method. 18051 inline=True, 18052 bodyInHeader=True, 18053 ) 18054 18055 def unlinkForCCMethod(self): 18056 body = "" 18057 if self.dictionary.parent and self.dictionaryNeedsCycleCollection( 18058 self.dictionary.parent 18059 ): 18060 cls = self.makeClassName(self.dictionary.parent) 18061 body += "%s::UnlinkForCC();\n" % cls 18062 18063 for m, _ in self.memberInfo: 18064 if idlTypeNeedsCycleCollection(m.type): 18065 memberName = self.makeMemberName(m.identifier.name) 18066 body += "ImplCycleCollectionUnlink(%s);\n" % memberName 18067 18068 return ClassMethod( 18069 "UnlinkForCC", 18070 "void", 18071 [], 18072 body=body, 18073 # Inline so we don't pay a codesize hit unless someone actually uses 18074 # this unlink method. 18075 inline=True, 18076 bodyInHeader=True, 18077 ) 18078 18079 def assignmentOperator(self): 18080 body = CGList([]) 18081 body.append(CGGeneric("%s::operator=(aOther);\n" % self.base())) 18082 18083 for m, _ in self.memberInfo: 18084 memberName = self.makeMemberName(m.identifier.name) 18085 if m.canHaveMissingValue(): 18086 memberAssign = CGGeneric( 18087 fill( 18088 """ 18089 ${name}.Reset(); 18090 if (aOther.${name}.WasPassed()) { 18091 ${name}.Construct(aOther.${name}.Value()); 18092 } 18093 """, 18094 name=memberName, 18095 ) 18096 ) 18097 else: 18098 memberAssign = CGGeneric("%s = aOther.%s;\n" % (memberName, memberName)) 18099 body.append(memberAssign) 18100 body.append(CGGeneric("return *this;\n")) 18101 return ClassMethod( 18102 "operator=", 18103 "%s&" % self.makeClassName(self.dictionary), 18104 [Argument("const %s&" % self.makeClassName(self.dictionary), "aOther")], 18105 body=body.define(), 18106 ) 18107 18108 def equalityOperator(self): 18109 # For now we only allow equality operators if our members have a string 18110 # type, a primitive type or an enum type. 18111 if not all( 18112 m.type.isString() or m.type.isPrimitive() or m.type.isEnum() 18113 for m in self.dictionary.members 18114 ): 18115 err = ( 18116 "[GenerateEqualityOperator] set on %s, but it" 18117 % self.dictionary.needsEqualityOperator.identifier.name 18118 ) 18119 if self.dictionary.needsEqualityOperator != self.dictionary: 18120 err += "s ancestor %s" % self.dictionary.identifier.name 18121 err += " contains types other than string, primitive or enum types." 18122 raise TypeError(err) 18123 18124 body = CGList([]) 18125 18126 if self.dictionary.parent: 18127 # If we have a parent dictionary we have to call its equals 18128 # operator. 18129 parentTest = CGGeneric( 18130 fill( 18131 """ 18132 if (!${base}::operator==(aOther)) { 18133 return false; 18134 } 18135 """, 18136 base=self.makeClassName(self.dictionary.parent), 18137 ) 18138 ) 18139 body.append(parentTest) 18140 18141 for m, _ in self.memberInfo: 18142 memberName = self.makeMemberName(m.identifier.name) 18143 memberTest = CGGeneric( 18144 fill( 18145 """ 18146 if (${memberName} != aOther.${memberName}) { 18147 return false; 18148 } 18149 """, 18150 memberName=memberName, 18151 ) 18152 ) 18153 body.append(memberTest) 18154 body.append(CGGeneric("return true;\n")) 18155 return ClassMethod( 18156 "operator==", 18157 "bool", 18158 [Argument("const %s&" % self.makeClassName(self.dictionary), "aOther")], 18159 const=True, 18160 body=body.define(), 18161 ) 18162 18163 def getStructs(self): 18164 d = self.dictionary 18165 selfName = self.makeClassName(d) 18166 members = [ 18167 ClassMember( 18168 self.makeMemberName(m[0].identifier.name), 18169 self.getMemberType(m), 18170 visibility="public", 18171 body=self.getMemberInitializer(m), 18172 hasIgnoreInitCheckFlag=True, 18173 ) 18174 for m in self.memberInfo 18175 ] 18176 if d.parent: 18177 # We always want to init our parent with our non-initializing 18178 # constructor arg, because either we're about to init ourselves (and 18179 # hence our parent) or we don't want any init happening. 18180 baseConstructors = [ 18181 "%s(%s)" 18182 % (self.makeClassName(d.parent), self.getNonInitializingCtorArg()) 18183 ] 18184 else: 18185 baseConstructors = None 18186 18187 if d.needsConversionFromJS: 18188 initArgs = "nullptr, JS::NullHandleValue" 18189 else: 18190 initArgs = "" 18191 ctors = [ 18192 ClassConstructor( 18193 [], 18194 visibility="public", 18195 baseConstructors=baseConstructors, 18196 body=( 18197 "// Safe to pass a null context if we pass a null value\n" 18198 "Init(%s);\n" % initArgs 18199 ), 18200 ), 18201 ClassConstructor( 18202 [Argument("const FastDictionaryInitializer&", "")], 18203 visibility="public", 18204 baseConstructors=baseConstructors, 18205 explicit=True, 18206 bodyInHeader=True, 18207 body='// Do nothing here; this is used by our "Fast" subclass\n', 18208 ), 18209 ] 18210 methods = [] 18211 18212 if self.needToInitIds: 18213 methods.append(self.initIdsMethod()) 18214 18215 if d.needsConversionFromJS: 18216 methods.append(self.initMethod()) 18217 methods.append(self.initWithoutCallContextMethod()) 18218 else: 18219 methods.append(self.simpleInitMethod()) 18220 18221 canBeRepresentedAsJSON = self.dictionarySafeToJSONify(d) 18222 if canBeRepresentedAsJSON and d.getExtendedAttribute("GenerateInitFromJSON"): 18223 methods.append(self.initFromJSONMethod()) 18224 18225 if d.needsConversionToJS: 18226 methods.append(self.toObjectInternalMethod()) 18227 18228 if canBeRepresentedAsJSON and d.getExtendedAttribute("GenerateToJSON"): 18229 methods.append(self.toJSONMethod()) 18230 18231 methods.append(self.traceDictionaryMethod()) 18232 18233 try: 18234 if self.dictionaryNeedsCycleCollection(d): 18235 methods.append(self.traverseForCCMethod()) 18236 methods.append(self.unlinkForCCMethod()) 18237 except CycleCollectionUnsupported: 18238 # We have some member that we don't know how to CC. Don't output 18239 # our cycle collection overloads, so attempts to CC us will fail to 18240 # compile instead of misbehaving. 18241 pass 18242 18243 ctors.append( 18244 ClassConstructor( 18245 [Argument("%s&&" % selfName, "aOther")], 18246 default=True, 18247 visibility="public", 18248 baseConstructors=baseConstructors, 18249 ) 18250 ) 18251 18252 if CGDictionary.isDictionaryCopyConstructible(d): 18253 disallowCopyConstruction = False 18254 # Note: gcc's -Wextra has a warning against not initializng our 18255 # base explicitly. If we have one. Use our non-initializing base 18256 # constructor to get around that. 18257 ctors.append( 18258 ClassConstructor( 18259 [Argument("const %s&" % selfName, "aOther")], 18260 bodyInHeader=True, 18261 visibility="public", 18262 baseConstructors=baseConstructors, 18263 explicit=True, 18264 body="*this = aOther;\n", 18265 ) 18266 ) 18267 methods.append(self.assignmentOperator()) 18268 else: 18269 disallowCopyConstruction = True 18270 18271 if d.needsEqualityOperator: 18272 methods.append(self.equalityOperator()) 18273 elif d.parent and d.parent.needsEqualityOperator: 18274 methods.append( 18275 ClassMethod( 18276 "operator==", 18277 "bool", 18278 [ 18279 Argument( 18280 "const %s&" % self.makeClassName(self.dictionary), "aOther" 18281 ) 18282 ], 18283 visibility="public", 18284 delete=True, 18285 ) 18286 ) 18287 18288 struct = CGClass( 18289 selfName, 18290 bases=[ClassBase(self.base())], 18291 members=members, 18292 constructors=ctors, 18293 methods=methods, 18294 isStruct=True, 18295 disallowCopyConstruction=disallowCopyConstruction, 18296 ) 18297 18298 fastDictionaryCtor = ClassConstructor( 18299 [], 18300 visibility="public", 18301 bodyInHeader=True, 18302 baseConstructors=["%s(%s)" % (selfName, self.getNonInitializingCtorArg())], 18303 body="// Doesn't matter what int we pass to the parent constructor\n", 18304 ) 18305 18306 fastStruct = CGClass( 18307 "Fast" + selfName, 18308 bases=[ClassBase(selfName)], 18309 constructors=[fastDictionaryCtor], 18310 isStruct=True, 18311 ) 18312 18313 return CGList([struct, CGNamespace("binding_detail", fastStruct)], "\n") 18314 18315 def deps(self): 18316 return self.dictionary.getDeps() 18317 18318 @staticmethod 18319 def makeDictionaryName(dictionary): 18320 return dictionary.identifier.name 18321 18322 def makeClassName(self, dictionary): 18323 return self.makeDictionaryName(dictionary) 18324 18325 @staticmethod 18326 def makeMemberName(name): 18327 return "m" + name[0].upper() + IDLToCIdentifier(name[1:]) 18328 18329 def getMemberType(self, memberInfo): 18330 member, conversionInfo = memberInfo 18331 # We can't handle having a holderType here 18332 assert conversionInfo.holderType is None 18333 18334 if member.getExtendedAttribute("BinaryType"): 18335 return member.getExtendedAttribute("BinaryType")[0] 18336 18337 declType = conversionInfo.declType 18338 if conversionInfo.dealWithOptional: 18339 declType = CGTemplatedType("Optional", declType) 18340 return declType.define() 18341 18342 def getMemberConversion(self, memberInfo, isKnownMissing=False): 18343 """ 18344 A function that outputs the initialization of a single dictionary 18345 member from the given dictionary value. 18346 18347 We start with our conversionInfo, which tells us how to 18348 convert a JS::Value to whatever type this member is. We 18349 substiture the template from the conversionInfo with values 18350 that point to our "temp" JS::Value and our member (which is 18351 the C++ value we want to produce). The output is a string of 18352 code to do the conversion. We store this string in 18353 conversionReplacements["convert"]. 18354 18355 Now we have three different ways we might use (or skip) this 18356 string of code, depending on whether the value is required, 18357 optional with default value, or optional without default 18358 value. We set up a template in the 'conversion' variable for 18359 exactly how to do this, then substitute into it from the 18360 conversionReplacements dictionary. 18361 """ 18362 member, conversionInfo = memberInfo 18363 18364 # We should only be initializing things with default values if 18365 # we're always-missing. 18366 assert not isKnownMissing or (member.optional and member.defaultValue) 18367 18368 replacements = { 18369 "declName": self.makeMemberName(member.identifier.name), 18370 # We need a holder name for external interfaces, but 18371 # it's scoped down to the conversion so we can just use 18372 # anything we want. 18373 "holderName": "holder", 18374 "passedToJSImpl": "passedToJSImpl", 18375 } 18376 18377 if isKnownMissing: 18378 replacements["val"] = "(JS::NullHandleValue)" 18379 else: 18380 replacements["val"] = "temp.ref()" 18381 replacements["maybeMutableVal"] = "temp.ptr()" 18382 18383 # We can't handle having a holderType here 18384 assert conversionInfo.holderType is None 18385 if conversionInfo.dealWithOptional: 18386 replacements["declName"] = "(" + replacements["declName"] + ".Value())" 18387 if member.defaultValue: 18388 if isKnownMissing: 18389 replacements["haveValue"] = "false" 18390 else: 18391 replacements["haveValue"] = "!isNull && !temp->isUndefined()" 18392 18393 propId = self.makeIdName(member.identifier.name) 18394 propGet = "JS_GetPropertyById(cx, *object, atomsCache->%s, temp.ptr())" % propId 18395 18396 conversionReplacements = { 18397 "prop": self.makeMemberName(member.identifier.name), 18398 "convert": string.Template(conversionInfo.template).substitute( 18399 replacements 18400 ), 18401 "propGet": propGet, 18402 } 18403 # The conversion code will only run where a default value or a value passed 18404 # by the author needs to get converted, so we can remember if we have any 18405 # members present here. 18406 conversionReplacements["convert"] += "mIsAnyMemberPresent = true;\n" 18407 if isKnownMissing: 18408 conversion = "" 18409 else: 18410 setTempValue = CGGeneric( 18411 dedent( 18412 """ 18413 if (!${propGet}) { 18414 return false; 18415 } 18416 """ 18417 ) 18418 ) 18419 conditions = getConditionList(member, "cx", "*object") 18420 if len(conditions) != 0: 18421 setTempValue = CGIfElseWrapper( 18422 conditions.define(), 18423 setTempValue, 18424 CGGeneric("temp->setUndefined();\n"), 18425 ) 18426 setTempValue = CGIfWrapper(setTempValue, "!isNull") 18427 conversion = setTempValue.define() 18428 18429 if member.defaultValue: 18430 if member.type.isUnion() and ( 18431 not member.type.nullable() 18432 or not isinstance(member.defaultValue, IDLNullValue) 18433 ): 18434 # Since this has a default value, it might have been initialized 18435 # already. Go ahead and uninit it before we try to init it 18436 # again. 18437 memberName = self.makeMemberName(member.identifier.name) 18438 if member.type.nullable(): 18439 conversion += fill( 18440 """ 18441 if (!${memberName}.IsNull()) { 18442 ${memberName}.Value().Uninit(); 18443 } 18444 """, 18445 memberName=memberName, 18446 ) 18447 else: 18448 conversion += "%s.Uninit();\n" % memberName 18449 conversion += "${convert}" 18450 elif not conversionInfo.dealWithOptional: 18451 # We're required, but have no default value. Make sure 18452 # that we throw if we have no value provided. 18453 conversion += dedent( 18454 """ 18455 if (!isNull && !temp->isUndefined()) { 18456 ${convert} 18457 } else if (cx) { 18458 // Don't error out if we have no cx. In that 18459 // situation the caller is default-constructing us and we'll 18460 // just assume they know what they're doing. 18461 return cx.ThrowErrorMessage<MSG_MISSING_REQUIRED_DICTIONARY_MEMBER>("%s"); 18462 } 18463 """ 18464 % self.getMemberSourceDescription(member) 18465 ) 18466 conversionReplacements["convert"] = indent( 18467 conversionReplacements["convert"] 18468 ).rstrip() 18469 else: 18470 conversion += ( 18471 "if (!isNull && !temp->isUndefined()) {\n" 18472 " ${prop}.Construct();\n" 18473 "${convert}" 18474 "}\n" 18475 ) 18476 conversionReplacements["convert"] = indent( 18477 conversionReplacements["convert"] 18478 ) 18479 18480 return CGGeneric(string.Template(conversion).substitute(conversionReplacements)) 18481 18482 def getMemberDefinition(self, memberInfo): 18483 member = memberInfo[0] 18484 declType = memberInfo[1].declType 18485 memberLoc = self.makeMemberName(member.identifier.name) 18486 if not member.canHaveMissingValue(): 18487 memberData = memberLoc 18488 else: 18489 # The data is inside the Optional<> 18490 memberData = "%s.InternalValue()" % memberLoc 18491 18492 if member.identifier.name in JS_OBJECT_PROTOTYPE_PROPERTIES: 18493 raise TypeError( 18494 "'%s' member of %s dictionary shadows " 18495 "a property of Object.prototype, and Xrays to " 18496 "Object can't handle that.\n" 18497 "%s" 18498 % ( 18499 member.identifier.name, 18500 self.dictionary.identifier.name, 18501 member.location, 18502 ) 18503 ) 18504 18505 propDef = ( 18506 "JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, JSPROP_ENUMERATE)" 18507 % self.makeIdName(member.identifier.name) 18508 ) 18509 18510 innerTemplate = wrapForType( 18511 member.type, 18512 self.descriptorProvider, 18513 { 18514 "result": "currentValue", 18515 "successCode": ( 18516 "if (!%s) {\n" " return false;\n" "}\n" "break;\n" % propDef 18517 ), 18518 "jsvalRef": "temp", 18519 "jsvalHandle": "&temp", 18520 "returnsNewObject": False, 18521 # 'obj' can just be allowed to be the string "obj", since that 18522 # will be our dictionary object, which is presumably itself in 18523 # the right scope. 18524 "spiderMonkeyInterfacesAreStructs": True, 18525 }, 18526 ) 18527 conversion = CGGeneric(innerTemplate) 18528 conversion = CGWrapper( 18529 conversion, 18530 pre=( 18531 "JS::Rooted<JS::Value> temp(cx);\n" 18532 "%s const & currentValue = %s;\n" % (declType.define(), memberData) 18533 ), 18534 ) 18535 18536 # Now make sure that our successCode can actually break out of the 18537 # conversion. This incidentally gives us a scope for 'temp' and 18538 # 'currentValue'. 18539 conversion = CGWrapper( 18540 CGIndenter(conversion), 18541 pre=( 18542 "do {\n" 18543 " // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n" 18544 ), 18545 post="} while(false);\n", 18546 ) 18547 if member.canHaveMissingValue(): 18548 # Only do the conversion if we have a value 18549 conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc) 18550 conditions = getConditionList(member, "cx", "obj") 18551 if len(conditions) != 0: 18552 conversion = CGIfWrapper(conversion, conditions.define()) 18553 return conversion 18554 18555 def getMemberTrace(self, member): 18556 type = member.type 18557 assert typeNeedsRooting(type) 18558 memberLoc = self.makeMemberName(member.identifier.name) 18559 if not member.canHaveMissingValue(): 18560 memberData = memberLoc 18561 else: 18562 # The data is inside the Optional<> 18563 memberData = "%s.Value()" % memberLoc 18564 18565 memberName = "%s.%s" % (self.makeClassName(self.dictionary), memberLoc) 18566 18567 if type.isObject(): 18568 trace = CGGeneric( 18569 'JS::TraceRoot(trc, %s, "%s");\n' % ("&" + memberData, memberName) 18570 ) 18571 if type.nullable(): 18572 trace = CGIfWrapper(trace, memberData) 18573 elif type.isAny(): 18574 trace = CGGeneric( 18575 'JS::TraceRoot(trc, %s, "%s");\n' % ("&" + memberData, memberName) 18576 ) 18577 elif ( 18578 type.isSequence() 18579 or type.isDictionary() 18580 or type.isSpiderMonkeyInterface() 18581 or type.isUnion() 18582 or type.isRecord() 18583 ): 18584 if type.nullable(): 18585 memberNullable = memberData 18586 memberData = "%s.Value()" % memberData 18587 if type.isSequence(): 18588 trace = CGGeneric("DoTraceSequence(trc, %s);\n" % memberData) 18589 elif type.isDictionary(): 18590 trace = CGGeneric("%s.TraceDictionary(trc);\n" % memberData) 18591 elif type.isUnion(): 18592 trace = CGGeneric("%s.TraceUnion(trc);\n" % memberData) 18593 elif type.isRecord(): 18594 trace = CGGeneric("TraceRecord(trc, %s);\n" % memberData) 18595 else: 18596 assert type.isSpiderMonkeyInterface() 18597 trace = CGGeneric("%s.TraceSelf(trc);\n" % memberData) 18598 if type.nullable(): 18599 trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable) 18600 else: 18601 assert False # unknown type 18602 18603 if member.canHaveMissingValue(): 18604 trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc) 18605 18606 return trace.define() 18607 18608 def getMemberInitializer(self, memberInfo): 18609 """ 18610 Get the right initializer for the member. Most members don't need one, 18611 but we need to pre-initialize 'object' that have a default value or are 18612 required (and hence are not inside Optional), so they're safe to trace 18613 at all times. And we can optimize a bit for dictionary-typed members. 18614 """ 18615 member, _ = memberInfo 18616 if member.canHaveMissingValue(): 18617 # Allowed missing value means no need to set it up front, since it's 18618 # inside an Optional and won't get traced until it's actually set 18619 # up. 18620 return None 18621 type = member.type 18622 if type.isDictionary(): 18623 # When we construct ourselves, we don't want to init our member 18624 # dictionaries. Either we're being constructed-but-not-initialized 18625 # ourselves (and then we don't want to init them) or we're about to 18626 # init ourselves and then we'll init them anyway. 18627 return CGDictionary.getNonInitializingCtorArg() 18628 return initializerForType(type) 18629 18630 def getMemberSourceDescription(self, member): 18631 return "'%s' member of %s" % ( 18632 member.identifier.name, 18633 self.dictionary.identifier.name, 18634 ) 18635 18636 @staticmethod 18637 def makeIdName(name): 18638 return IDLToCIdentifier(name) + "_id" 18639 18640 @staticmethod 18641 def getNonInitializingCtorArg(): 18642 return "FastDictionaryInitializer()" 18643 18644 @staticmethod 18645 def isDictionaryCopyConstructible(dictionary): 18646 if dictionary.parent and not CGDictionary.isDictionaryCopyConstructible( 18647 dictionary.parent 18648 ): 18649 return False 18650 return all(isTypeCopyConstructible(m.type) for m in dictionary.members) 18651 18652 @staticmethod 18653 def typeSafeToJSONify(type): 18654 """ 18655 Determine whether the given type is safe to convert to JSON. The 18656 restriction is that this needs to be safe while in a global controlled 18657 by an adversary, and "safe" means no side-effects when the JS 18658 representation of this type is converted to JSON. That means that we 18659 have to be pretty restrictive about what things we can allow. For 18660 example, "object" is out, because it may have accessor properties on it. 18661 """ 18662 if type.nullable(): 18663 # Converting null to JSON is always OK. 18664 return CGDictionary.typeSafeToJSONify(type.inner) 18665 18666 if type.isSequence(): 18667 # Sequences are arrays we create ourselves, with no holes. They 18668 # should be safe if their contents are safe, as long as we suppress 18669 # invocation of .toJSON on objects. 18670 return CGDictionary.typeSafeToJSONify(type.inner) 18671 18672 if type.isUnion(): 18673 # OK if everything in it is ok. 18674 return all(CGDictionary.typeSafeToJSONify(t) for t in type.flatMemberTypes) 18675 18676 if type.isDictionary(): 18677 # OK if the dictionary is OK 18678 return CGDictionary.dictionarySafeToJSONify(type.inner) 18679 18680 if type.isUndefined() or type.isString() or type.isEnum(): 18681 # Strings are always OK. 18682 return True 18683 18684 if type.isPrimitive(): 18685 # Primitives (numbers and booleans) are ok, as long as 18686 # they're not unrestricted float/double. 18687 return not type.isFloat() or not type.isUnrestricted() 18688 18689 if type.isRecord(): 18690 # Records are okay, as long as the value type is. 18691 # Per spec, only strings are allowed as keys. 18692 return CGDictionary.typeSafeToJSONify(type.inner) 18693 18694 return False 18695 18696 @staticmethod 18697 def dictionarySafeToJSONify(dictionary): 18698 # The dictionary itself is OK, so we're good if all our types are. 18699 return all(CGDictionary.typeSafeToJSONify(m.type) for m in dictionary.members) 18700 18701 18702 def RegisterNonWindowBindings(descriptors): 18703 conditions = [] 18704 for desc in descriptors: 18705 bindingNS = toBindingNamespace(desc.name) 18706 condition = "!%s::CreateAndDefineOnGlobal(aCx)" % bindingNS 18707 if desc.isExposedConditionally(): 18708 condition = "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition 18709 conditions.append(condition) 18710 lines = [ 18711 CGIfWrapper(CGGeneric("return false;\n"), condition) for condition in conditions 18712 ] 18713 lines.append(CGGeneric("return true;\n")) 18714 return CGList(lines, "\n").define() 18715 18716 18717 class CGRegisterWorkerBindings(CGAbstractMethod): 18718 def __init__(self, config): 18719 CGAbstractMethod.__init__( 18720 self, 18721 None, 18722 "RegisterWorkerBindings", 18723 "bool", 18724 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")], 18725 ) 18726 self.config = config 18727 18728 def definition_body(self): 18729 return RegisterNonWindowBindings( 18730 self.config.getDescriptors( 18731 hasInterfaceObject=True, isExposedInAnyWorker=True, register=True 18732 ) 18733 ) 18734 18735 18736 class CGRegisterWorkerDebuggerBindings(CGAbstractMethod): 18737 def __init__(self, config): 18738 CGAbstractMethod.__init__( 18739 self, 18740 None, 18741 "RegisterWorkerDebuggerBindings", 18742 "bool", 18743 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")], 18744 ) 18745 self.config = config 18746 18747 def definition_body(self): 18748 return RegisterNonWindowBindings( 18749 self.config.getDescriptors( 18750 hasInterfaceObject=True, isExposedInWorkerDebugger=True, register=True 18751 ) 18752 ) 18753 18754 18755 class CGRegisterWorkletBindings(CGAbstractMethod): 18756 def __init__(self, config): 18757 CGAbstractMethod.__init__( 18758 self, 18759 None, 18760 "RegisterWorkletBindings", 18761 "bool", 18762 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")], 18763 ) 18764 self.config = config 18765 18766 def definition_body(self): 18767 return RegisterNonWindowBindings( 18768 self.config.getDescriptors( 18769 hasInterfaceObject=True, isExposedInAnyWorklet=True, register=True 18770 ) 18771 ) 18772 18773 18774 class CGRegisterShadowRealmBindings(CGAbstractMethod): 18775 def __init__(self, config): 18776 CGAbstractMethod.__init__( 18777 self, 18778 None, 18779 "RegisterShadowRealmBindings", 18780 "bool", 18781 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")], 18782 ) 18783 self.config = config 18784 18785 def definition_body(self): 18786 return RegisterNonWindowBindings( 18787 self.config.getDescriptors( 18788 hasInterfaceObject=True, isExposedInShadowRealms=True, register=True 18789 ) 18790 ) 18791 18792 18793 def BindingNamesOffsetEnum(name): 18794 return CppKeywords.checkMethodName(name.replace(" ", "_")) 18795 18796 18797 class CGGlobalNames(CGGeneric): 18798 def __init__(self, names): 18799 """ 18800 names is expected to be a list of tuples of the name and the descriptor it refers to. 18801 """ 18802 18803 strings = [] 18804 entries = [] 18805 for name, desc in names: 18806 # Generate the entry declaration 18807 # XXX(nika): mCreate & mEnabled require relocations. If we want to 18808 # reduce those, we could move them into separate tables. 18809 nativeEntry = fill( 18810 """ 18811 { 18812 /* mNameOffset */ BindingNamesOffset::${nameOffset}, 18813 /* mNameLength */ ${nameLength}, 18814 /* mConstructorId */ constructors::id::${realname}, 18815 /* mCreate */ ${realname}_Binding::CreateInterfaceObjects, 18816 /* mEnabled */ ${enabled} 18817 } 18818 """, 18819 nameOffset=BindingNamesOffsetEnum(name), 18820 nameLength=len(name), 18821 name=name, 18822 realname=desc.name, 18823 enabled=( 18824 "%s_Binding::ConstructorEnabled" % desc.name 18825 if desc.isExposedConditionally() 18826 else "nullptr" 18827 ), 18828 ) 18829 18830 entries.append((name.encode(), nativeEntry)) 18831 18832 # Unfortunately, when running tests, we may have no entries. 18833 # PerfectHash will assert if we give it an empty set of entries, so we 18834 # just generate a dummy value. 18835 if len(entries) == 0: 18836 CGGeneric.__init__( 18837 self, 18838 define=dedent( 18839 """ 18840 static_assert(false, "No WebIDL global name entries!"); 18841 """ 18842 ), 18843 ) 18844 return 18845 18846 # Build the perfect hash function. 18847 phf = PerfectHash(entries, GLOBAL_NAMES_PHF_SIZE) 18848 18849 # Generate code for the PHF 18850 phfCodegen = phf.codegen( 18851 "WebIDLGlobalNameHash::sEntries", "WebIDLNameTableEntry" 18852 ) 18853 entries = phfCodegen.gen_entries(lambda e: e[1]) 18854 getter = phfCodegen.gen_jslinearstr_getter( 18855 name="WebIDLGlobalNameHash::GetEntry", 18856 return_type="const WebIDLNameTableEntry*", 18857 return_entry=dedent( 18858 """ 18859 if (JS_LinearStringEqualsAscii(aKey, BindingName(entry.mNameOffset), entry.mNameLength)) { 18860 return &entry; 18861 } 18862 return nullptr; 18863 """ 18864 ), 18865 ) 18866 18867 define = fill( 18868 """ 18869 const uint32_t WebIDLGlobalNameHash::sCount = ${count}; 18870 18871 $*{entries} 18872 18873 $*{getter} 18874 """, 18875 count=len(phf.entries), 18876 strings="\n".join(strings) + ";\n", 18877 entries=entries, 18878 getter=getter, 18879 ) 18880 CGGeneric.__init__(self, define=define) 18881 18882 18883 def dependencySortObjects(objects, dependencyGetter, nameGetter): 18884 """ 18885 Sort IDL objects with dependencies on each other such that if A 18886 depends on B then B will come before A. This is needed for 18887 declaring C++ classes in the right order, for example. Objects 18888 that have no dependencies are just sorted by name. 18889 18890 objects should be something that can produce a set of objects 18891 (e.g. a set, iterator, list, etc). 18892 18893 dependencyGetter is something that, given an object, should return 18894 the set of objects it depends on. 18895 """ 18896 # XXXbz this will fail if we have two webidl files F1 and F2 such that F1 18897 # declares an object which depends on an object in F2, and F2 declares an 18898 # object (possibly a different one!) that depends on an object in F1. The 18899 # good news is that I expect this to never happen. 18900 sortedObjects = [] 18901 objects = set(objects) 18902 while len(objects) != 0: 18903 # Find the dictionaries that don't depend on anything else 18904 # anymore and move them over. 18905 toMove = [o for o in objects if len(dependencyGetter(o) & objects) == 0] 18906 if len(toMove) == 0: 18907 raise TypeError( 18908 "Loop in dependency graph\n" + "\n".join(o.location for o in objects) 18909 ) 18910 objects = objects - set(toMove) 18911 sortedObjects.extend(sorted(toMove, key=nameGetter)) 18912 return sortedObjects 18913 18914 18915 class ForwardDeclarationBuilder: 18916 """ 18917 Create a canonical representation of a set of namespaced forward 18918 declarations. 18919 """ 18920 18921 def __init__(self): 18922 """ 18923 The set of declarations is represented as a tree of nested namespaces. 18924 Each tree node has a set of declarations |decls| and a dict |children|. 18925 Each declaration is a pair consisting of the class name and a boolean 18926 that is true iff the class is really a struct. |children| maps the 18927 names of inner namespaces to the declarations in that namespace. 18928 """ 18929 self.decls = set() 18930 self.children = {} 18931 18932 def _ensureNonTemplateType(self, type): 18933 if "<" in type: 18934 # This is a templated type. We don't really know how to 18935 # forward-declare those, and trying to do it naively is not going to 18936 # go well (e.g. we may have :: characters inside the type we're 18937 # templated on!). Just bail out. 18938 raise TypeError( 18939 "Attempt to use ForwardDeclarationBuilder on " 18940 "templated type %s. We don't know how to do that " 18941 "yet." % type 18942 ) 18943 18944 def _listAdd(self, namespaces, name, isStruct=False): 18945 """ 18946 Add a forward declaration, where |namespaces| is a list of namespaces. 18947 |name| should not contain any other namespaces. 18948 """ 18949 if namespaces: 18950 child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder()) 18951 child._listAdd(namespaces[1:], name, isStruct) 18952 else: 18953 assert "::" not in name 18954 self.decls.add((name, isStruct)) 18955 18956 def addInMozillaDom(self, name, isStruct=False): 18957 """ 18958 Add a forward declaration to the mozilla::dom:: namespace. |name| should not 18959 contain any other namespaces. 18960 """ 18961 self._ensureNonTemplateType(name) 18962 self._listAdd(["mozilla", "dom"], name, isStruct) 18963 18964 def add(self, nativeType, isStruct=False): 18965 """ 18966 Add a forward declaration, where |nativeType| is a string containing 18967 the type and its namespaces, in the usual C++ way. 18968 """ 18969 self._ensureNonTemplateType(nativeType) 18970 components = nativeType.split("::") 18971 self._listAdd(components[:-1], components[-1], isStruct) 18972 18973 def _build(self, atTopLevel): 18974 """ 18975 Return a codegenerator for the forward declarations. 18976 """ 18977 decls = [] 18978 if self.decls: 18979 decls.append( 18980 CGList( 18981 [ 18982 CGClassForwardDeclare(cname, isStruct) 18983 for cname, isStruct in sorted(self.decls) 18984 ] 18985 ) 18986 ) 18987 for namespace, child in sorted(self.children.items()): 18988 decls.append(CGNamespace(namespace, child._build(atTopLevel=False))) 18989 18990 cg = CGList(decls, "\n") 18991 if not atTopLevel and len(decls) + len(self.decls) > 1: 18992 cg = CGWrapper(cg, pre="\n", post="\n") 18993 return cg 18994 18995 def build(self): 18996 return self._build(atTopLevel=True) 18997 18998 def forwardDeclareForType(self, t, config): 18999 t = t.unroll() 19000 if t.isGeckoInterface(): 19001 name = t.inner.identifier.name 19002 try: 19003 desc = config.getDescriptor(name) 19004 self.add(desc.nativeType) 19005 except NoSuchDescriptorError: 19006 pass 19007 19008 # Note: SpiderMonkey interfaces are typedefs, so can't be 19009 # forward-declared 19010 elif t.isPromise(): 19011 self.addInMozillaDom("Promise") 19012 elif t.isCallback(): 19013 self.addInMozillaDom(t.callback.identifier.name) 19014 elif t.isDictionary(): 19015 self.addInMozillaDom(t.inner.identifier.name, isStruct=True) 19016 elif t.isCallbackInterface(): 19017 self.addInMozillaDom(t.inner.identifier.name) 19018 elif t.isUnion(): 19019 # Forward declare both the owning and non-owning version, 19020 # since we don't know which one we might want 19021 self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False)) 19022 self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True)) 19023 elif t.isRecord(): 19024 self.forwardDeclareForType(t.inner, config) 19025 # Don't need to do anything for void, primitive, string, any or object. 19026 # There may be some other cases we are missing. 19027 19028 19029 class CGForwardDeclarations(CGWrapper): 19030 """ 19031 Code generate the forward declarations for a header file. 19032 additionalDeclarations is a list of tuples containing a classname and a 19033 boolean. If the boolean is true we will declare a struct, otherwise we'll 19034 declare a class. 19035 """ 19036 19037 def __init__( 19038 self, 19039 config, 19040 descriptors, 19041 callbacks, 19042 dictionaries, 19043 callbackInterfaces, 19044 additionalDeclarations=[], 19045 ): 19046 builder = ForwardDeclarationBuilder() 19047 19048 # Needed for at least Wrap. 19049 for d in descriptors: 19050 # If this is a generated iterator interface, we only create these 19051 # in the generated bindings, and don't need to forward declare. 19052 if ( 19053 d.interface.isIteratorInterface() 19054 or d.interface.isAsyncIteratorInterface() 19055 ): 19056 continue 19057 builder.add(d.nativeType) 19058 if d.interface.isSerializable(): 19059 builder.add("nsIGlobalObject") 19060 # If we're an interface and we have a maplike/setlike declaration, 19061 # we'll have helper functions exposed to the native side of our 19062 # bindings, which will need to show up in the header. If either of 19063 # our key/value types are interfaces, they'll be passed as 19064 # arguments to helper functions, and they'll need to be forward 19065 # declared in the header. 19066 if d.interface.maplikeOrSetlikeOrIterable: 19067 if d.interface.maplikeOrSetlikeOrIterable.hasKeyType(): 19068 builder.forwardDeclareForType( 19069 d.interface.maplikeOrSetlikeOrIterable.keyType, config 19070 ) 19071 if d.interface.maplikeOrSetlikeOrIterable.hasValueType(): 19072 builder.forwardDeclareForType( 19073 d.interface.maplikeOrSetlikeOrIterable.valueType, config 19074 ) 19075 19076 for m in d.interface.members: 19077 if m.isAttr() and m.type.isObservableArray(): 19078 builder.forwardDeclareForType(m.type, config) 19079 19080 # We just about always need NativePropertyHooks 19081 builder.addInMozillaDom("NativePropertyHooks", isStruct=True) 19082 builder.addInMozillaDom("ProtoAndIfaceCache") 19083 19084 for callback in callbacks: 19085 builder.addInMozillaDom(callback.identifier.name) 19086 for t in getTypesFromCallback(callback): 19087 builder.forwardDeclareForType(t, config) 19088 19089 for d in callbackInterfaces: 19090 builder.add(d.nativeType) 19091 builder.add(d.nativeType + "Atoms", isStruct=True) 19092 for t in getTypesFromDescriptor(d): 19093 builder.forwardDeclareForType(t, config) 19094 if d.hasCEReactions(): 19095 builder.addInMozillaDom("DocGroup") 19096 19097 for d in dictionaries: 19098 if len(d.members) > 0: 19099 builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True) 19100 for t in getTypesFromDictionary(d): 19101 builder.forwardDeclareForType(t, config) 19102 19103 for className, isStruct in additionalDeclarations: 19104 builder.add(className, isStruct=isStruct) 19105 19106 CGWrapper.__init__(self, builder.build()) 19107 19108 def forward_declare(self): 19109 return "" 19110 19111 19112 def dependencySortDictionariesAndUnionsAndCallbacks(types): 19113 def getDependenciesFromType(type): 19114 if type.isDictionary(): 19115 return set([type.unroll().inner]) 19116 if type.isSequence(): 19117 return getDependenciesFromType(type.unroll()) 19118 if type.isUnion(): 19119 return set([type.unroll()]) 19120 if type.isRecord(): 19121 return set([type.unroll().inner]) 19122 if type.isCallback(): 19123 return set([type.unroll()]) 19124 return set() 19125 19126 def getDependencies(unionTypeOrDictionaryOrCallback): 19127 if isinstance(unionTypeOrDictionaryOrCallback, IDLDictionary): 19128 deps = set() 19129 if unionTypeOrDictionaryOrCallback.parent: 19130 deps.add(unionTypeOrDictionaryOrCallback.parent) 19131 for member in unionTypeOrDictionaryOrCallback.members: 19132 deps |= getDependenciesFromType(member.type) 19133 return deps 19134 19135 if ( 19136 unionTypeOrDictionaryOrCallback.isType() 19137 and unionTypeOrDictionaryOrCallback.isUnion() 19138 ): 19139 deps = set() 19140 for member in unionTypeOrDictionaryOrCallback.flatMemberTypes: 19141 deps |= getDependenciesFromType(member) 19142 return deps 19143 19144 assert unionTypeOrDictionaryOrCallback.isCallback() 19145 return set() 19146 19147 def getName(unionTypeOrDictionaryOrCallback): 19148 if isinstance(unionTypeOrDictionaryOrCallback, IDLDictionary): 19149 return unionTypeOrDictionaryOrCallback.identifier.name 19150 19151 if ( 19152 unionTypeOrDictionaryOrCallback.isType() 19153 and unionTypeOrDictionaryOrCallback.isUnion() 19154 ): 19155 return unionTypeOrDictionaryOrCallback.name 19156 19157 assert unionTypeOrDictionaryOrCallback.isCallback() 19158 return unionTypeOrDictionaryOrCallback.identifier.name 19159 19160 return dependencySortObjects(types, getDependencies, getName) 19161 19162 19163 class CGBindingRoot(CGThing): 19164 """ 19165 Root codegen class for binding generation. Instantiate the class, and call 19166 declare or define to generate header or cpp code (respectively). 19167 """ 19168 19169 def __init__(self, config: Configuration, prefix, webIDLFile): 19170 bindingHeaders = dict.fromkeys( 19171 ("mozilla/dom/NonRefcountedDOMObject.h", "MainThreadUtils.h"), True 19172 ) 19173 bindingDeclareHeaders = dict.fromkeys( 19174 ( 19175 "mozilla/dom/BindingDeclarations.h", 19176 "mozilla/dom/Nullable.h", 19177 ), 19178 True, 19179 ) 19180 19181 descriptors = config.getDescriptors( 19182 webIDLFile=webIDLFile, hasInterfaceOrInterfacePrototypeObject=True 19183 ) 19184 descriptors.extend( 19185 config.getDescriptors( 19186 webIDLFile=webIDLFile, hasOrdinaryObjectPrototype=True 19187 ) 19188 ) 19189 19190 unionTypes = UnionsForFile(config, webIDLFile) 19191 19192 ( 19193 unionHeaders, 19194 unionImplheaders, 19195 unionDeclarations, 19196 traverseMethods, 19197 unlinkMethods, 19198 unionStructs, 19199 ) = UnionTypes(unionTypes, config) 19200 19201 bindingDeclareHeaders.update(dict.fromkeys(unionHeaders, True)) 19202 bindingHeaders.update(dict.fromkeys(unionImplheaders, True)) 19203 bindingDeclareHeaders["mozilla/dom/UnionMember.h"] = len(unionStructs) > 0 19204 bindingDeclareHeaders["mozilla/dom/FakeString.h"] = len(unionStructs) > 0 19205 # BindingUtils.h is only needed for SetToObject. 19206 # If it stops being inlined or stops calling CallerSubsumes 19207 # both this bit and the bit in UnionTypes can be removed. 19208 bindingDeclareHeaders["mozilla/dom/BindingUtils.h"] = any( 19209 d.isObject() for t in unionTypes for d in t.flatMemberTypes 19210 ) 19211 bindingDeclareHeaders["mozilla/dom/JSSlots.h"] = any( 19212 d.interface.reflectedHTMLAttributesReturningFrozenArray for d in descriptors 19213 ) 19214 bindingHeaders["mozilla/dom/IterableIterator.h"] = any( 19215 ( 19216 d.interface.isIteratorInterface() 19217 and d.interface.maplikeOrSetlikeOrIterable.isPairIterator() 19218 ) 19219 or d.interface.isAsyncIteratorInterface() 19220 or d.interface.isIterable() 19221 or d.interface.isAsyncIterable() 19222 for d in descriptors 19223 ) 19224 19225 def memberNeedsSubjectPrincipal(d, m): 19226 if m.isAttr(): 19227 return ( 19228 "needsSubjectPrincipal" in d.getExtendedAttributes(m, getter=True) 19229 ) or ( 19230 not m.readonly 19231 and "needsSubjectPrincipal" 19232 in d.getExtendedAttributes(m, setter=True) 19233 ) 19234 return m.isMethod() and "needsSubjectPrincipal" in d.getExtendedAttributes( 19235 m 19236 ) 19237 19238 if any( 19239 memberNeedsSubjectPrincipal(d, m) 19240 for d in descriptors 19241 for m in d.interface.members 19242 ): 19243 bindingHeaders["mozilla/BasePrincipal.h"] = True 19244 bindingHeaders["nsJSPrincipals.h"] = True 19245 19246 # The conditions for which we generate profiler labels are fairly 19247 # complicated. The check below is a little imprecise to make it simple. 19248 # It includes the profiler header in all cases where it is necessary and 19249 # generates only a few false positives. 19250 bindingHeaders["mozilla/ProfilerLabels.h"] = any( 19251 # constructor profiler label 19252 d.interface.legacyFactoryFunctions 19253 or (d.interface.hasInterfaceObject() and d.interface.ctor()) 19254 or any( 19255 # getter/setter profiler labels 19256 m.isAttr() 19257 # method profiler label 19258 or m.isMethod() 19259 for m in d.interface.members 19260 ) 19261 for d in descriptors 19262 ) 19263 19264 def descriptorHasCrossOriginProperties(desc): 19265 def hasCrossOriginProperty(m): 19266 props = memberProperties(m, desc) 19267 return ( 19268 props.isCrossOriginMethod 19269 or props.isCrossOriginGetter 19270 or props.isCrossOriginSetter 19271 ) 19272 19273 return any(hasCrossOriginProperty(m) for m in desc.interface.members) 19274 19275 def descriptorHasObservableArrayTypes(desc): 19276 def hasObservableArrayTypes(m): 19277 return m.isAttr() and m.type.isObservableArray() 19278 19279 return any(hasObservableArrayTypes(m) for m in desc.interface.members) 19280 19281 bindingDeclareHeaders["mozilla/dom/RemoteObjectProxy.h"] = any( 19282 descriptorHasCrossOriginProperties(d) for d in descriptors 19283 ) 19284 bindingDeclareHeaders["jsapi.h"] = any( 19285 descriptorHasCrossOriginProperties(d) 19286 or descriptorHasObservableArrayTypes(d) 19287 for d in descriptors 19288 ) 19289 bindingHeaders["mozilla/dom/ToJSValue.h"] = any( 19290 descriptorHasObservableArrayTypes(d) for d in descriptors 19291 ) 19292 bindingDeclareHeaders["js/TypeDecls.h"] = not bindingDeclareHeaders["jsapi.h"] 19293 bindingDeclareHeaders["js/RootingAPI.h"] = not bindingDeclareHeaders["jsapi.h"] 19294 19295 # JS::IsCallable 19296 bindingDeclareHeaders["js/CallAndConstruct.h"] = True 19297 19298 def descriptorHasIteratorAlias(desc): 19299 def hasIteratorAlias(m): 19300 return m.isMethod() and ( 19301 ("@@iterator" in m.aliases) or ("@@asyncIterator" in m.aliases) 19302 ) 19303 19304 return any(hasIteratorAlias(m) for m in desc.interface.members) 19305 19306 bindingHeaders["js/Symbol.h"] = any( 19307 descriptorHasIteratorAlias(d) for d in descriptors 19308 ) 19309 19310 bindingHeaders["js/shadow/Object.h"] = any( 19311 d.interface.hasMembersInSlots() for d in descriptors 19312 ) 19313 19314 # The symbols supplied by this header are used so ubiquitously it's not 19315 # worth the effort delineating the exact dependency, if it can't be done 19316 # *at* the places where their definitions are required. 19317 bindingHeaders["js/experimental/JitInfo.h"] = True 19318 19319 # JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, and 19320 # JS::SetReservedSlot are also used too many places to restate 19321 # dependency logic. 19322 bindingHeaders["js/Object.h"] = True 19323 19324 # JS::IsCallable, JS::Call, JS::Construct 19325 bindingHeaders["js/CallAndConstruct.h"] = True 19326 19327 # JS_IsExceptionPending 19328 bindingHeaders["js/Exception.h"] = True 19329 19330 # JS::Map{Clear, Delete, Has, Get, Set} 19331 bindingHeaders["js/MapAndSet.h"] = True 19332 19333 # JS_DefineElement, JS_DefineProperty, JS_DefinePropertyById, 19334 # JS_DefineUCProperty, JS_ForwardGetPropertyTo, JS_GetProperty, 19335 # JS_GetPropertyById, JS_HasPropertyById, JS_SetProperty, 19336 # JS_SetPropertyById 19337 bindingHeaders["js/PropertyAndElement.h"] = True 19338 19339 # JS_GetOwnPropertyDescriptorById 19340 bindingHeaders["js/PropertyDescriptor.h"] = True 19341 19342 def descriptorDeprecated(desc): 19343 iface = desc.interface 19344 return any( 19345 m.getExtendedAttribute("Deprecated") for m in iface.members + [iface] 19346 ) 19347 19348 bindingHeaders["mozilla/dom/Document.h"] = any( 19349 descriptorDeprecated(d) for d in descriptors 19350 ) 19351 19352 bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any( 19353 d.concrete and d.proxy for d in descriptors 19354 ) 19355 19356 bindingHeaders["mozilla/dom/ProxyHandlerUtils.h"] = any( 19357 d.concrete and d.proxy for d in descriptors 19358 ) 19359 19360 bindingHeaders["js/String.h"] = any( 19361 d.needsMissingPropUseCounters for d in descriptors 19362 ) 19363 19364 hasCrossOriginObjects = any( 19365 d.concrete and d.isMaybeCrossOriginObject() for d in descriptors 19366 ) 19367 bindingHeaders["mozilla/dom/MaybeCrossOriginObject.h"] = hasCrossOriginObjects 19368 bindingHeaders["AccessCheck.h"] = hasCrossOriginObjects 19369 hasCEReactions = any(d.hasCEReactions() for d in descriptors) 19370 bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = hasCEReactions 19371 bindingHeaders["mozilla/dom/DocGroup.h"] = hasCEReactions 19372 19373 def descriptorHasChromeOnly(desc): 19374 ctor = desc.interface.ctor() 19375 19376 return ( 19377 any( 19378 isChromeOnly(a) or needsCallerType(a) 19379 for a in desc.interface.members 19380 ) 19381 or desc.interface.getExtendedAttribute("ChromeOnly") is not None 19382 or 19383 # JS-implemented interfaces with an interface object get a 19384 # chromeonly _create method. And interfaces with an 19385 # interface object might have a ChromeOnly constructor. 19386 ( 19387 desc.interface.hasInterfaceObject() 19388 and ( 19389 desc.interface.isJSImplemented() 19390 or (ctor and isChromeOnly(ctor)) 19391 ) 19392 ) 19393 ) 19394 19395 # XXXkhuey ugly hack but this is going away soon. 19396 bindingHeaders["xpcprivate.h"] = webIDLFile.endswith("EventTarget.webidl") 19397 19398 hasThreadChecks = any(d.hasThreadChecks() for d in descriptors) 19399 bindingHeaders["nsThreadUtils.h"] = hasThreadChecks 19400 19401 dictionaries = config.getDictionaries(webIDLFile) 19402 19403 def dictionaryHasChromeOnly(dictionary): 19404 while dictionary: 19405 if any(isChromeOnly(m) for m in dictionary.members): 19406 return True 19407 dictionary = dictionary.parent 19408 return False 19409 19410 def needsNonSystemPrincipal(member): 19411 return ( 19412 member.getExtendedAttribute("NeedsSubjectPrincipal") == ["NonSystem"] 19413 or member.getExtendedAttribute("SetterNeedsSubjectPrincipal") 19414 == ["NonSystem"] 19415 or member.getExtendedAttribute("GetterNeedsSubjectPrincipal") 19416 == ["NonSystem"] 19417 ) 19418 19419 def descriptorNeedsNonSystemPrincipal(d): 19420 return any(needsNonSystemPrincipal(m) for m in d.interface.members) 19421 19422 def descriptorHasPrefDisabler(desc): 19423 iface = desc.interface 19424 return any( 19425 PropertyDefiner.getControllingCondition(m, desc).hasDisablers() 19426 for m in iface.members 19427 if (m.isMethod() or m.isAttr() or m.isConst()) 19428 ) 19429 19430 def addPrefHeaderForObject(bindingHeaders, obj): 19431 """ 19432 obj might be a dictionary member or an interface. 19433 """ 19434 if obj is not None: 19435 pref = PropertyDefiner.getStringAttr(obj, "Pref") 19436 if pref: 19437 bindingHeaders[prefHeader(pref)] = True 19438 19439 def addPrefHeadersForDictionary(bindingHeaders, dictionary): 19440 while dictionary: 19441 for m in dictionary.members: 19442 addPrefHeaderForObject(bindingHeaders, m) 19443 dictionary = dictionary.parent 19444 19445 for d in dictionaries: 19446 addPrefHeadersForDictionary(bindingHeaders, d) 19447 try: 19448 if CGDictionary.dictionaryNeedsCycleCollection(d): 19449 bindingDeclareHeaders["nsCycleCollectionParticipant.h"] = True 19450 except CycleCollectionUnsupported: 19451 # We have some member that we don't know how to CC. Don't output 19452 # our cycle collection overloads, so attempts to CC us will fail to 19453 # compile instead of misbehaving. 19454 pass 19455 19456 for d in descriptors: 19457 interface = d.interface 19458 addPrefHeaderForObject(bindingHeaders, interface) 19459 addPrefHeaderForObject(bindingHeaders, interface.ctor()) 19460 19461 bindingHeaders["mozilla/dom/WebIDLPrefs.h"] = any( 19462 descriptorHasPrefDisabler(d) for d in descriptors 19463 ) 19464 bindingHeaders["nsContentUtils.h"] = ( 19465 any(descriptorHasChromeOnly(d) for d in descriptors) 19466 or any(descriptorNeedsNonSystemPrincipal(d) for d in descriptors) 19467 or any(dictionaryHasChromeOnly(d) for d in dictionaries) 19468 ) 19469 hasNonEmptyDictionaries = any(len(dict.members) > 0 for dict in dictionaries) 19470 callbacks = config.getCallbacks(webIDLFile) 19471 callbackDescriptors = config.getDescriptors( 19472 webIDLFile=webIDLFile, isCallback=True 19473 ) 19474 jsImplemented = config.getDescriptors( 19475 webIDLFile=webIDLFile, isJSImplemented=True 19476 ) 19477 bindingDeclareHeaders["nsWeakReference.h"] = jsImplemented 19478 bindingDeclareHeaders["mozilla/dom/PrototypeList.h"] = descriptors 19479 bindingHeaders["nsIGlobalObject.h"] = jsImplemented 19480 bindingHeaders["AtomList.h"] = ( 19481 hasNonEmptyDictionaries or jsImplemented or callbackDescriptors 19482 ) 19483 19484 if callbackDescriptors: 19485 bindingDeclareHeaders["mozilla/ErrorResult.h"] = True 19486 19487 def descriptorClearsPropsInSlots(descriptor): 19488 if not descriptor.wrapperCache: 19489 return False 19490 return any( 19491 m.isAttr() and m.getExtendedAttribute("StoreInSlot") 19492 for m in descriptor.interface.members 19493 ) 19494 19495 bindingHeaders["nsJSUtils.h"] = any( 19496 descriptorClearsPropsInSlots(d) for d in descriptors 19497 ) 19498 19499 # Make sure we can sanely use binding_detail in generated code. 19500 cgthings = [ 19501 CGGeneric( 19502 dedent( 19503 """ 19504 namespace binding_detail {}; // Just to make sure it's known as a namespace 19505 using namespace mozilla::dom::binding_detail; 19506 """ 19507 ) 19508 ) 19509 ] 19510 19511 # Do codegen for all the enums 19512 enums = config.getEnums(webIDLFile) 19513 cgthings.extend(CGEnum(e) for e in enums) 19514 maxEnumValues = CGList([CGMaxContiguousEnumValue(e) for e in enums], "\n") 19515 19516 bindingDeclareHeaders["mozilla/Span.h"] = enums 19517 bindingDeclareHeaders["mozilla/ArrayUtils.h"] = enums 19518 bindingDeclareHeaders["mozilla/EnumTypeTraits.h"] = enums 19519 19520 hasCode = descriptors or callbackDescriptors or dictionaries or callbacks 19521 bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode 19522 bindingHeaders["mozilla/OwningNonNull.h"] = hasCode 19523 bindingHeaders["<type_traits>"] = hasCode 19524 bindingHeaders["mozilla/dom/BindingDeclarations.h"] = not hasCode and enums 19525 19526 bindingHeaders["WrapperFactory.h"] = descriptors 19527 bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors 19528 bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI 19529 # Ensure we see our enums in the generated .cpp file, for the ToJSValue 19530 # method body. Also ensure that we see jsapi.h. 19531 if enums: 19532 bindingHeaders[CGHeaders.getDeclarationFilename(enums[0])] = True 19533 bindingHeaders["jsapi.h"] = True 19534 19535 # For things that have [UseCounter] or [InstrumentedProps] or [Trial] 19536 for d in descriptors: 19537 if d.concrete: 19538 if d.instrumentedProps: 19539 bindingHeaders["mozilla/UseCounter.h"] = True 19540 if d.needsMissingPropUseCounters: 19541 bindingHeaders[prefHeader(MISSING_PROP_PREF)] = True 19542 if d.interface.isSerializable(): 19543 bindingHeaders["mozilla/dom/StructuredCloneTags.h"] = True 19544 if d.wantsXrays: 19545 bindingHeaders["mozilla/Atomics.h"] = True 19546 bindingHeaders["mozilla/dom/XrayExpandoClass.h"] = True 19547 if d.wantsXrayExpandoClass: 19548 bindingHeaders["XrayWrapper.h"] = True 19549 for m in d.interface.members: 19550 if m.getExtendedAttribute("UseCounter"): 19551 bindingHeaders["mozilla/UseCounter.h"] = True 19552 if m.getExtendedAttribute("Trial"): 19553 bindingHeaders["mozilla/OriginTrials.h"] = True 19554 19555 bindingHeaders["mozilla/dom/SimpleGlobalObject.h"] = any( 19556 CGDictionary.dictionarySafeToJSONify(d) for d in dictionaries 19557 ) 19558 19559 for ancestor in (findAncestorWithInstrumentedProps(d) for d in descriptors): 19560 if not ancestor: 19561 continue 19562 bindingHeaders[CGHeaders.getDeclarationFilename(ancestor)] = True 19563 19564 for d in descriptors: 19565 ancestor = d.interface.parent 19566 while ancestor: 19567 if ancestor.reflectedHTMLAttributesReturningFrozenArray: 19568 bindingHeaders[CGHeaders.getDeclarationFilename(ancestor)] = True 19569 break 19570 ancestor = ancestor.parent 19571 19572 cgthings.extend(traverseMethods) 19573 cgthings.extend(unlinkMethods) 19574 19575 # Do codegen for all the dictionaries. We have to be a bit careful 19576 # here, because we have to generate these in order from least derived 19577 # to most derived so that class inheritance works out. We also have to 19578 # generate members before the dictionary that contains them. 19579 19580 for t in dependencySortDictionariesAndUnionsAndCallbacks( 19581 dictionaries + unionStructs + callbacks 19582 ): 19583 if t.isDictionary(): 19584 cgthings.append(CGDictionary(t, config)) 19585 elif t.isUnion(): 19586 cgthings.append(CGUnionStruct(t, config)) 19587 cgthings.append(CGUnionStruct(t, config, True)) 19588 else: 19589 assert t.isCallback() 19590 cgthings.append(CGCallbackFunction(t, config)) 19591 cgthings.append(CGNamespace("binding_detail", CGFastCallback(t))) 19592 19593 # Do codegen for all the descriptors 19594 cgthings.extend( 19595 [CGDescriptor(x, config.attributeTemplates) for x in descriptors] 19596 ) 19597 19598 # Do codegen for all the callback interfaces. 19599 cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors]) 19600 19601 cgthings.extend( 19602 [ 19603 CGNamespace("binding_detail", CGFastCallback(x.interface)) 19604 for x in callbackDescriptors 19605 ] 19606 ) 19607 19608 # Do codegen for JS implemented classes 19609 def getParentDescriptor(desc): 19610 if not desc.interface.parent: 19611 return set() 19612 return {desc.getDescriptor(desc.interface.parent.identifier.name)} 19613 19614 for x in dependencySortObjects( 19615 jsImplemented, getParentDescriptor, lambda d: d.interface.identifier.name 19616 ): 19617 cgthings.append( 19618 CGCallbackInterface(x, spiderMonkeyInterfacesAreStructs=True) 19619 ) 19620 cgthings.append(CGJSImplClass(x)) 19621 19622 # And make sure we have the right number of newlines at the end 19623 curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n") 19624 19625 # Wrap all of that in our namespaces. 19626 19627 if len(maxEnumValues) > 0: 19628 curr = CGNamespace("dom", CGWrapper(curr, pre="\n")) 19629 curr = CGWrapper(CGList([curr, maxEnumValues], "\n\n"), post="\n\n") 19630 curr = CGNamespace("mozilla", CGWrapper(curr, pre="\n")) 19631 else: 19632 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, pre="\n")) 19633 19634 curr = CGList( 19635 [ 19636 CGForwardDeclarations( 19637 config, 19638 descriptors, 19639 callbacks, 19640 dictionaries, 19641 callbackDescriptors + jsImplemented, 19642 additionalDeclarations=unionDeclarations, 19643 ), 19644 curr, 19645 ], 19646 "\n", 19647 ) 19648 19649 unionTypedefs = config.getUnionTypedefs(webIDLFile) 19650 cgUnionTypedefs = CGList( 19651 [CGUnionTypedef(t, config) for t in unionTypedefs], joiner="\n" 19652 ) 19653 curr = CGList([cgUnionTypedefs, curr], joiner="\n") 19654 19655 # Add header includes. 19656 bindingHeaders = [ 19657 header for header, include in bindingHeaders.items() if include 19658 ] 19659 bindingDeclareHeaders = [ 19660 header for header, include in bindingDeclareHeaders.items() if include 19661 ] 19662 19663 curr = CGHeaders( 19664 descriptors, 19665 dictionaries, 19666 callbacks, 19667 callbackDescriptors, 19668 bindingDeclareHeaders, 19669 bindingHeaders, 19670 prefix, 19671 curr, 19672 config, 19673 jsImplemented, 19674 ) 19675 19676 # Add include guards. 19677 curr = CGIncludeGuard(prefix, curr) 19678 19679 # Add the auto-generated comment. 19680 curr = CGWrapper( 19681 curr, 19682 pre=( 19683 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT % os.path.basename(webIDLFile) 19684 ), 19685 ) 19686 19687 # Store the final result. 19688 self.root = curr 19689 19690 def declare(self): 19691 return stripTrailingWhitespace(self.root.declare()) 19692 19693 def define(self): 19694 return stripTrailingWhitespace(self.root.define()) 19695 19696 def forward_declare(self): 19697 return stripTrailingWhitespace(self.root.forward_declare()) 19698 19699 def deps(self): 19700 return self.root.deps() 19701 19702 19703 class CGNativeMember(ClassMethod): 19704 def __init__( 19705 self, 19706 descriptorProvider, 19707 member, 19708 name, 19709 signature, 19710 extendedAttrs, 19711 breakAfter=True, 19712 passJSBitsAsNeeded=True, 19713 visibility="public", 19714 spiderMonkeyInterfacesAreStructs=True, 19715 variadicIsSequence=False, 19716 resultNotAddRefed=False, 19717 virtual=False, 19718 override=False, 19719 canRunScript=False, 19720 ): 19721 """ 19722 If spiderMonkeyInterfacesAreStructs is false, SpiderMonkey interfaces 19723 will be passed as JS::Handle<JSObject*>. If it's true they will be 19724 passed as one of the dom::SpiderMonkeyInterfaceObjectStorage subclasses. 19725 19726 If passJSBitsAsNeeded is false, we don't automatically pass in a 19727 JSContext* or a JSObject* based on the return and argument types. We 19728 can still pass it based on 'implicitJSContext' annotations. 19729 """ 19730 self.descriptorProvider = descriptorProvider 19731 self.member = member 19732 self.extendedAttrs = extendedAttrs 19733 self.resultAlreadyAddRefed = not resultNotAddRefed 19734 self.passJSBitsAsNeeded = passJSBitsAsNeeded 19735 self.spiderMonkeyInterfacesAreStructs = spiderMonkeyInterfacesAreStructs 19736 self.variadicIsSequence = variadicIsSequence 19737 breakAfterSelf = "\n" if breakAfter else "" 19738 ClassMethod.__init__( 19739 self, 19740 name, 19741 self.getReturnType(signature[0], False), 19742 self.getArgs(signature[0], signature[1]), 19743 static=member.isStatic(), 19744 # Mark our getters, which are attrs that 19745 # have a non-void return type, as const. 19746 const=( 19747 not member.isStatic() 19748 and member.isAttr() 19749 and not signature[0].isUndefined() 19750 ), 19751 breakAfterReturnDecl=" ", 19752 breakAfterSelf=breakAfterSelf, 19753 visibility=visibility, 19754 virtual=virtual, 19755 override=override, 19756 canRunScript=canRunScript, 19757 ) 19758 19759 def getReturnType(self, type, isMember): 19760 return self.getRetvalInfo(type, isMember)[0] 19761 19762 def getRetvalInfo(self, type, isMember): 19763 """ 19764 Returns a tuple: 19765 19766 The first element is the type declaration for the retval 19767 19768 The second element is a default value that can be used on error returns. 19769 For cases whose behavior depends on isMember, the second element will be 19770 None if isMember is true. 19771 19772 The third element is a template for actually returning a value stored in 19773 "${declName}" and "${holderName}". This means actually returning it if 19774 we're not outparam, else assigning to the "retval" outparam. If 19775 isMember is true, this can be None, since in that case the caller will 19776 never examine this value. 19777 """ 19778 if type.isUndefined(): 19779 return "void", "", "" 19780 if type.isPrimitive() and type.tag() in builtinNames: 19781 result = CGGeneric(builtinNames[type.tag()]) 19782 defaultReturnArg = "0" 19783 if type.nullable(): 19784 result = CGTemplatedType("Nullable", result) 19785 defaultReturnArg = "" 19786 return ( 19787 result.define(), 19788 "%s(%s)" % (result.define(), defaultReturnArg), 19789 "return ${declName};\n", 19790 ) 19791 if type.isJSString(): 19792 if isMember: 19793 raise TypeError("JSString not supported as return type member") 19794 # Outparam 19795 return "void", "", "aRetVal.set(${declName});\n" 19796 if type.isDOMString() or type.isUSVString(): 19797 if isMember: 19798 # No need for a third element in the isMember case 19799 return "nsString", None, None 19800 # Outparam 19801 return "void", "", "aRetVal = ${declName};\n" 19802 if type.isByteString() or type.isUTF8String(): 19803 if isMember: 19804 # No need for a third element in the isMember case 19805 return "nsCString", None, None 19806 # Outparam 19807 return "void", "", "aRetVal = ${declName};\n" 19808 if type.isEnum(): 19809 enumName = type.unroll().inner.identifier.name 19810 if type.nullable(): 19811 enumName = CGTemplatedType("Nullable", CGGeneric(enumName)).define() 19812 defaultValue = "%s()" % enumName 19813 else: 19814 defaultValue = "%s(0)" % enumName 19815 return enumName, defaultValue, "return ${declName};\n" 19816 if type.isGeckoInterface() or type.isPromise(): 19817 if type.isGeckoInterface(): 19818 iface = type.unroll().inner 19819 result = CGGeneric( 19820 self.descriptorProvider.getDescriptor( 19821 iface.identifier.name 19822 ).prettyNativeType 19823 ) 19824 else: 19825 result = CGGeneric("Promise") 19826 if self.resultAlreadyAddRefed: 19827 if isMember: 19828 holder = "RefPtr" 19829 else: 19830 holder = "already_AddRefed" 19831 if memberReturnsNewObject(self.member) or isMember: 19832 warning = "" 19833 else: 19834 warning = "// Return a raw pointer here to avoid refcounting, but make sure it's safe (the object should be kept alive by the callee).\n" 19835 result = CGWrapper(result, pre=("%s%s<" % (warning, holder)), post=">") 19836 else: 19837 result = CGWrapper(result, post="*") 19838 # Since we always force an owning type for callback return values, 19839 # our ${declName} is an OwningNonNull or RefPtr. So we can just 19840 # .forget() to get our already_AddRefed. 19841 return result.define(), "nullptr", "return ${declName}.forget();\n" 19842 if type.isCallback(): 19843 return ( 19844 "already_AddRefed<%s>" % type.unroll().callback.identifier.name, 19845 "nullptr", 19846 "return ${declName}.forget();\n", 19847 ) 19848 if type.isAny(): 19849 if isMember: 19850 # No need for a third element in the isMember case 19851 return "JS::Value", None, None 19852 # Outparam 19853 return "void", "", "aRetVal.set(${declName});\n" 19854 19855 if type.isObject(): 19856 if isMember: 19857 # No need for a third element in the isMember case 19858 return "JSObject*", None, None 19859 return "void", "", "aRetVal.set(${declName});\n" 19860 if type.isSpiderMonkeyInterface(): 19861 if isMember: 19862 # No need for a third element in the isMember case 19863 return "JSObject*", None, None 19864 if type.nullable(): 19865 returnCode = ( 19866 "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj()" 19867 ) 19868 else: 19869 returnCode = "${declName}.Obj()" 19870 return "void", "", "aRetVal.set(%s);\n" % returnCode 19871 if type.isSequence(): 19872 # If we want to handle sequence-of-sequences return values, we're 19873 # going to need to fix example codegen to not produce nsTArray<void> 19874 # for the relevant argument... 19875 assert not isMember 19876 # Outparam. 19877 if type.nullable(): 19878 returnCode = dedent( 19879 """ 19880 if (${declName}.IsNull()) { 19881 aRetVal.SetNull(); 19882 } else { 19883 aRetVal.SetValue() = std::move(${declName}.Value()); 19884 } 19885 """ 19886 ) 19887 else: 19888 returnCode = "aRetVal = std::move(${declName});\n" 19889 return "void", "", returnCode 19890 if type.isRecord(): 19891 # If we want to handle record-of-record return values, we're 19892 # going to need to fix example codegen to not produce record<void> 19893 # for the relevant argument... 19894 assert not isMember 19895 # In this case we convert directly into our outparam to start with 19896 return "void", "", "" 19897 if type.isDictionary(): 19898 if isMember: 19899 # Only the first member of the tuple matters here, but return 19900 # bogus values for the others in case someone decides to use 19901 # them. 19902 return CGDictionary.makeDictionaryName(type.inner), None, None 19903 # In this case we convert directly into our outparam to start with 19904 return "void", "", "" 19905 if type.isUnion(): 19906 if isMember: 19907 # Only the first member of the tuple matters here, but return 19908 # bogus values for the others in case someone decides to use 19909 # them. 19910 return CGUnionStruct.unionTypeDecl(type, True), None, None 19911 # In this case we convert directly into our outparam to start with 19912 return "void", "", "" 19913 19914 raise TypeError("Don't know how to declare return value for %s" % type) 19915 19916 def getArgs(self, returnType, argList): 19917 args = [self.getArg(arg) for arg in argList] 19918 # Now the outparams 19919 if returnType.isJSString(): 19920 args.append(Argument("JS::MutableHandle<JSString*>", "aRetVal")) 19921 elif returnType.isDOMString() or returnType.isUSVString(): 19922 args.append(Argument("nsString&", "aRetVal")) 19923 elif returnType.isByteString() or returnType.isUTF8String(): 19924 args.append(Argument("nsCString&", "aRetVal")) 19925 elif returnType.isSequence(): 19926 nullable = returnType.nullable() 19927 if nullable: 19928 returnType = returnType.inner 19929 # And now the actual underlying type 19930 elementDecl = self.getReturnType(returnType.inner, True) 19931 type = CGTemplatedType("nsTArray", CGGeneric(elementDecl)) 19932 if nullable: 19933 type = CGTemplatedType("Nullable", type) 19934 args.append(Argument("%s&" % type.define(), "aRetVal")) 19935 elif returnType.isRecord(): 19936 nullable = returnType.nullable() 19937 if nullable: 19938 returnType = returnType.inner 19939 # And now the actual underlying type 19940 elementDecl = self.getReturnType(returnType.inner, True) 19941 type = CGTemplatedType( 19942 "Record", [recordKeyDeclType(returnType), CGGeneric(elementDecl)] 19943 ) 19944 if nullable: 19945 type = CGTemplatedType("Nullable", type) 19946 args.append(Argument("%s&" % type.define(), "aRetVal")) 19947 elif returnType.isDictionary(): 19948 nullable = returnType.nullable() 19949 if nullable: 19950 returnType = returnType.inner 19951 dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner)) 19952 if nullable: 19953 dictType = CGTemplatedType("Nullable", dictType) 19954 args.append(Argument("%s&" % dictType.define(), "aRetVal")) 19955 elif returnType.isUnion(): 19956 args.append( 19957 Argument( 19958 "%s&" % CGUnionStruct.unionTypeDecl(returnType, True), "aRetVal" 19959 ) 19960 ) 19961 elif returnType.isAny(): 19962 args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal")) 19963 elif returnType.isObject() or returnType.isSpiderMonkeyInterface(): 19964 args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal")) 19965 19966 # And the nsIPrincipal 19967 if "needsSubjectPrincipal" in self.extendedAttrs: 19968 if "needsNonSystemSubjectPrincipal" in self.extendedAttrs: 19969 args.append(Argument("nsIPrincipal*", "aPrincipal")) 19970 else: 19971 args.append(Argument("nsIPrincipal&", "aPrincipal")) 19972 # And the caller type, if desired. 19973 if needsCallerType(self.member): 19974 args.append(Argument("CallerType", "aCallerType")) 19975 # And the ErrorResult or OOMReporter 19976 if "needsErrorResult" in self.extendedAttrs: 19977 # Use aRv so it won't conflict with local vars named "rv" 19978 args.append(Argument("ErrorResult&", "aRv")) 19979 elif "canOOM" in self.extendedAttrs: 19980 args.append(Argument("OOMReporter&", "aRv")) 19981 19982 # The legacycaller thisval 19983 if self.member.isMethod() and self.member.isLegacycaller(): 19984 # If it has an identifier, we can't deal with it yet 19985 assert self.member.isIdentifierLess() 19986 args.insert(0, Argument("const JS::Value&", "aThisVal")) 19987 # And jscontext bits. 19988 if needCx( 19989 returnType, 19990 argList, 19991 self.extendedAttrs, 19992 self.passJSBitsAsNeeded, 19993 self.member.isStatic(), 19994 ): 19995 args.insert(0, Argument("JSContext*", "cx")) 19996 if needScopeObject( 19997 returnType, 19998 argList, 19999 self.extendedAttrs, 20000 self.descriptorProvider.wrapperCache, 20001 self.passJSBitsAsNeeded, 20002 self.member.getExtendedAttribute("StoreInSlot"), 20003 ): 20004 args.insert(1, Argument("JS::Handle<JSObject*>", "obj")) 20005 # And if we're static, a global 20006 if self.member.isStatic(): 20007 args.insert(0, Argument("const GlobalObject&", "global")) 20008 return args 20009 20010 def doGetArgType(self, type, optional, isMember): 20011 """ 20012 The main work of getArgType. Returns a string type decl, whether this 20013 is a const ref, as well as whether the type should be wrapped in 20014 Nullable as needed. 20015 20016 isMember can be false or one of the strings "Sequence", "Variadic", 20017 "Record" 20018 """ 20019 if type.isSequence(): 20020 nullable = type.nullable() 20021 if nullable: 20022 type = type.inner 20023 elementType = type.inner 20024 argType = self.getArgType(elementType, False, "Sequence")[0] 20025 decl = CGTemplatedType("Sequence", argType) 20026 return decl.define(), True, True 20027 20028 if type.isRecord(): 20029 nullable = type.nullable() 20030 if nullable: 20031 type = type.inner 20032 elementType = type.inner 20033 argType = self.getArgType(elementType, False, "Record")[0] 20034 decl = CGTemplatedType("Record", [recordKeyDeclType(type), argType]) 20035 return decl.define(), True, True 20036 20037 if type.isUnion(): 20038 # unionTypeDecl will handle nullable types, so return False for 20039 # auto-wrapping in Nullable 20040 return CGUnionStruct.unionTypeDecl(type, isMember), True, False 20041 20042 if type.isPromise(): 20043 assert not type.nullable() 20044 if optional or isMember: 20045 typeDecl = "OwningNonNull<Promise>" 20046 else: 20047 typeDecl = "Promise&" 20048 return (typeDecl, False, False) 20049 20050 if type.isGeckoInterface() and not type.isCallbackInterface(): 20051 iface = type.unroll().inner 20052 if iface.identifier.name == "WindowProxy": 20053 return "WindowProxyHolder", True, False 20054 20055 argIsPointer = type.nullable() or iface.isExternal() 20056 forceOwningType = iface.isCallback() or isMember 20057 if argIsPointer: 20058 if (optional or isMember) and forceOwningType: 20059 typeDecl = "RefPtr<%s>" 20060 else: 20061 typeDecl = "%s*" 20062 else: 20063 if optional or isMember: 20064 if forceOwningType: 20065 typeDecl = "OwningNonNull<%s>" 20066 else: 20067 typeDecl = "NonNull<%s>" 20068 else: 20069 typeDecl = "%s&" 20070 return ( 20071 ( 20072 typeDecl 20073 % self.descriptorProvider.getDescriptor( 20074 iface.identifier.name 20075 ).prettyNativeType 20076 ), 20077 False, 20078 False, 20079 ) 20080 20081 if type.isSpiderMonkeyInterface(): 20082 if not self.spiderMonkeyInterfacesAreStructs: 20083 return "JS::Handle<JSObject*>", False, False 20084 20085 # Unroll for the name, in case we're nullable. 20086 return type.unroll().name, True, True 20087 20088 if type.isJSString(): 20089 if isMember: 20090 raise TypeError("JSString not supported as member") 20091 return "JS::Handle<JSString*>", False, False 20092 20093 if type.isDOMString() or type.isUSVString(): 20094 if isMember: 20095 declType = "nsString" 20096 else: 20097 declType = "nsAString" 20098 return declType, True, False 20099 20100 if type.isByteString() or type.isUTF8String(): 20101 # TODO(emilio): Maybe bytestrings could benefit from nsAutoCString 20102 # or such too. 20103 if type.isUTF8String() and not isMember: 20104 declType = "nsACString" 20105 else: 20106 declType = "nsCString" 20107 return declType, True, False 20108 20109 if type.isEnum(): 20110 return type.unroll().inner.identifier.name, False, True 20111 20112 if type.isCallback() or type.isCallbackInterface(): 20113 forceOwningType = optional or isMember 20114 if type.nullable(): 20115 if forceOwningType: 20116 declType = "RefPtr<%s>" 20117 else: 20118 declType = "%s*" 20119 else: 20120 if forceOwningType: 20121 declType = "OwningNonNull<%s>" 20122 else: 20123 declType = "%s&" 20124 if type.isCallback(): 20125 name = type.unroll().callback.identifier.name 20126 else: 20127 name = type.unroll().inner.identifier.name 20128 return declType % name, False, False 20129 20130 if type.isAny(): 20131 # Don't do the rooting stuff for variadics for now 20132 if isMember: 20133 declType = "JS::Value" 20134 else: 20135 declType = "JS::Handle<JS::Value>" 20136 return declType, False, False 20137 20138 if type.isObject(): 20139 if isMember: 20140 declType = "JSObject*" 20141 else: 20142 declType = "JS::Handle<JSObject*>" 20143 return declType, False, False 20144 20145 if type.isDictionary(): 20146 typeName = CGDictionary.makeDictionaryName(type.inner) 20147 return typeName, True, True 20148 20149 assert type.isPrimitive() 20150 20151 return builtinNames[type.tag()], False, True 20152 20153 def getArgType(self, type, optional, isMember): 20154 """ 20155 Get the type of an argument declaration. Returns the type CGThing, and 20156 whether this should be a const ref. 20157 20158 isMember can be False, "Sequence", or "Variadic" 20159 """ 20160 decl, ref, handleNullable = self.doGetArgType(type, optional, isMember) 20161 decl = CGGeneric(decl) 20162 if handleNullable and type.nullable(): 20163 decl = CGTemplatedType("Nullable", decl) 20164 ref = True 20165 if isMember == "Variadic": 20166 arrayType = "Sequence" if self.variadicIsSequence else "nsTArray" 20167 decl = CGTemplatedType(arrayType, decl) 20168 ref = True 20169 elif optional: 20170 # Note: All variadic args claim to be optional, but we can just use 20171 # empty arrays to represent them not being present. 20172 decl = CGTemplatedType("Optional", decl) 20173 ref = True 20174 return (decl, ref) 20175 20176 def getArg(self, arg): 20177 """ 20178 Get the full argument declaration for an argument 20179 """ 20180 decl, ref = self.getArgType( 20181 arg.type, arg.canHaveMissingValue(), "Variadic" if arg.variadic else False 20182 ) 20183 if ref: 20184 decl = CGWrapper(decl, pre="const ", post="&") 20185 20186 return Argument(decl.define(), arg.identifier.name) 20187 20188 def arguments(self): 20189 return self.member.signatures()[0][1] 20190 20191 20192 class CGExampleMethod(CGNativeMember): 20193 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): 20194 CGNativeMember.__init__( 20195 self, 20196 descriptor, 20197 method, 20198 CGSpecializedMethod.makeNativeName(descriptor, method), 20199 signature, 20200 descriptor.getExtendedAttributes(method), 20201 breakAfter=breakAfter, 20202 variadicIsSequence=True, 20203 ) 20204 20205 def declare(self, cgClass): 20206 assert self.member.isMethod() 20207 # We skip declaring ourselves if this is a maplike/setlike/iterable 20208 # method, because those get implemented automatically by the binding 20209 # machinery, so the implementor of the interface doesn't have to worry 20210 # about it. 20211 if self.member.isMaplikeOrSetlikeOrIterableMethod(): 20212 return "" 20213 return CGNativeMember.declare(self, cgClass) 20214 20215 def define(self, cgClass): 20216 return "" 20217 20218 20219 class CGExampleGetter(CGNativeMember): 20220 def __init__(self, descriptor, attr): 20221 CGNativeMember.__init__( 20222 self, 20223 descriptor, 20224 attr, 20225 CGSpecializedGetterCommon.makeNativeName(descriptor, attr), 20226 (attr.type, []), 20227 descriptor.getExtendedAttributes(attr, getter=True), 20228 ) 20229 20230 def getArgs(self, returnType, argList): 20231 args = CGNativeMember.getArgs(self, returnType, argList) 20232 if self.member.getExtendedAttribute( 20233 "ReflectedHTMLAttributeReturningFrozenArray" 20234 ): 20235 args.insert(0, Argument("bool*", "aUseCachedValue")) 20236 return args 20237 20238 def declare(self, cgClass): 20239 assert self.member.isAttr() 20240 # We skip declaring ourselves if this is a maplike/setlike attr (in 20241 # practice, "size"), because those get implemented automatically by the 20242 # binding machinery, so the implementor of the interface doesn't have to 20243 # worry about it. 20244 if self.member.isMaplikeOrSetlikeAttr(): 20245 return "" 20246 return CGNativeMember.declare(self, cgClass) 20247 20248 def define(self, cgClass): 20249 return "" 20250 20251 20252 class CGExampleSetter(CGNativeMember): 20253 def __init__(self, descriptor, attr): 20254 CGNativeMember.__init__( 20255 self, 20256 descriptor, 20257 attr, 20258 CGSpecializedSetterCommon.makeNativeName(descriptor, attr), 20259 ( 20260 BuiltinTypes[IDLBuiltinType.Types.undefined], 20261 [FakeArgument(attr.type)], 20262 ), 20263 descriptor.getExtendedAttributes(attr, setter=True), 20264 ) 20265 20266 def define(self, cgClass): 20267 return "" 20268 20269 20270 class CGBindingImplClass(CGClass): 20271 """ 20272 Common codegen for generating a C++ implementation of a WebIDL interface 20273 """ 20274 20275 def __init__( 20276 self, 20277 descriptor, 20278 cgMethod, 20279 cgGetter, 20280 cgSetter, 20281 wantGetParent=True, 20282 wrapMethodName="WrapObject", 20283 skipStaticMethods=False, 20284 ): 20285 """ 20286 cgMethod, cgGetter and cgSetter are classes used to codegen methods, 20287 getters and setters. 20288 """ 20289 self.descriptor = descriptor 20290 self._deps = descriptor.interface.getDeps() 20291 20292 iface = descriptor.interface 20293 20294 self.methodDecls = [] 20295 20296 def appendMethod(m, isConstructor=False): 20297 sigs = m.signatures() 20298 for s in sigs[:-1]: 20299 # Don't put a blank line after overloads, until we 20300 # get to the last one. 20301 self.methodDecls.append( 20302 cgMethod(descriptor, m, s, isConstructor, breakAfter=False) 20303 ) 20304 self.methodDecls.append(cgMethod(descriptor, m, sigs[-1], isConstructor)) 20305 20306 if iface.ctor(): 20307 appendMethod(iface.ctor(), isConstructor=True) 20308 for n in iface.legacyFactoryFunctions: 20309 appendMethod(n, isConstructor=True) 20310 for m in iface.members: 20311 if m.isMethod(): 20312 if m.isIdentifierLess(): 20313 continue 20314 if m.isMaplikeOrSetlikeOrIterableMethod(): 20315 # Handled by generated code already 20316 continue 20317 if not m.isStatic() or not skipStaticMethods: 20318 appendMethod(m) 20319 elif m.isAttr(): 20320 if m.isMaplikeOrSetlikeAttr() or m.type.isObservableArray(): 20321 # Handled by generated code already 20322 continue 20323 self.methodDecls.append(cgGetter(descriptor, m)) 20324 if not m.readonly: 20325 self.methodDecls.append(cgSetter(descriptor, m)) 20326 20327 # Now do the special operations 20328 def appendSpecialOperation(name, op): 20329 if op is None: 20330 return 20331 assert len(op.signatures()) == 1 20332 returnType, args = op.signatures()[0] 20333 # Make a copy of the args, since we plan to modify them. 20334 args = list(args) 20335 if op.isGetter() or op.isDeleter(): 20336 # This is a total hack. The '&' belongs with the 20337 # type, not the name! But it works, and is simpler 20338 # than trying to somehow make this pretty. 20339 args.append( 20340 FakeArgument( 20341 BuiltinTypes[IDLBuiltinType.Types.boolean], name="&found" 20342 ) 20343 ) 20344 if name == "Stringifier": 20345 if op.isIdentifierLess(): 20346 # XXXbz I wish we were consistent about our renaming here. 20347 name = "Stringify" 20348 else: 20349 # We already added this method 20350 return 20351 if name == "LegacyCaller": 20352 if op.isIdentifierLess(): 20353 # XXXbz I wish we were consistent about our renaming here. 20354 name = "LegacyCall" 20355 else: 20356 # We already added this method 20357 return 20358 self.methodDecls.append( 20359 CGNativeMember( 20360 descriptor, 20361 op, 20362 name, 20363 (returnType, args), 20364 descriptor.getExtendedAttributes(op), 20365 ) 20366 ) 20367 20368 # Sort things by name so we get stable ordering in the output. 20369 ops = sorted(descriptor.operations.items(), key=lambda x: x[0]) 20370 for name, op in ops: 20371 appendSpecialOperation(name, op) 20372 # If we support indexed properties, then we need a Length() 20373 # method so we know which indices are supported. 20374 if descriptor.supportsIndexedProperties(): 20375 # But we don't need it if we already have an infallible 20376 # "length" attribute, which we often do. 20377 haveLengthAttr = any( 20378 m 20379 for m in iface.members 20380 if m.isAttr() 20381 and CGSpecializedGetterCommon.makeNativeName(descriptor, m) == "Length" 20382 ) 20383 if not haveLengthAttr: 20384 self.methodDecls.append( 20385 CGNativeMember( 20386 descriptor, 20387 FakeMember(), 20388 "Length", 20389 (BuiltinTypes[IDLBuiltinType.Types.unsigned_long], []), 20390 [], 20391 ), 20392 ) 20393 # And if we support named properties we need to be able to 20394 # enumerate the supported names. 20395 if descriptor.supportsNamedProperties(): 20396 self.methodDecls.append( 20397 CGNativeMember( 20398 descriptor, 20399 FakeMember(), 20400 "GetSupportedNames", 20401 ( 20402 IDLSequenceType( 20403 None, BuiltinTypes[IDLBuiltinType.Types.domstring] 20404 ), 20405 [], 20406 ), 20407 [], 20408 ) 20409 ) 20410 20411 if descriptor.concrete: 20412 wrapArgs = [ 20413 Argument("JSContext*", "aCx"), 20414 Argument("JS::Handle<JSObject*>", "aGivenProto"), 20415 ] 20416 if not descriptor.wrapperCache: 20417 wrapReturnType = "bool" 20418 wrapArgs.append(Argument("JS::MutableHandle<JSObject*>", "aReflector")) 20419 else: 20420 wrapReturnType = "JSObject*" 20421 self.methodDecls.insert( 20422 0, 20423 ClassMethod( 20424 wrapMethodName, 20425 wrapReturnType, 20426 wrapArgs, 20427 virtual=descriptor.wrapperCache, 20428 breakAfterReturnDecl=" ", 20429 override=descriptor.wrapperCache, 20430 body=self.getWrapObjectBody(), 20431 ), 20432 ) 20433 if descriptor.hasCEReactions(): 20434 self.methodDecls.insert( 20435 0, 20436 ClassMethod( 20437 "GetDocGroup", 20438 "DocGroup*", 20439 [], 20440 const=True, 20441 breakAfterReturnDecl=" ", 20442 body=self.getGetDocGroupBody(), 20443 ), 20444 ) 20445 if wantGetParent: 20446 self.methodDecls.insert( 20447 0, 20448 ClassMethod( 20449 "GetParentObject", 20450 self.getGetParentObjectReturnType(), 20451 [], 20452 const=True, 20453 breakAfterReturnDecl=" ", 20454 body=self.getGetParentObjectBody(), 20455 ), 20456 ) 20457 20458 # Invoke CGClass.__init__ in any subclasses afterwards to do the actual codegen. 20459 20460 def getWrapObjectBody(self): 20461 return None 20462 20463 def getGetParentObjectReturnType(self): 20464 # The lack of newline before the end of the string is on purpose. 20465 return dedent( 20466 """ 20467 // This should return something that eventually allows finding a 20468 // path to the global this object is associated with. Most simply, 20469 // returning an actual global works. 20470 nsIGlobalObject*""" 20471 ) 20472 20473 def getGetParentObjectBody(self): 20474 return None 20475 20476 def getGetDocGroupBody(self): 20477 return None 20478 20479 def deps(self): 20480 return self._deps 20481 20482 20483 class CGExampleObservableArrayCallback(CGNativeMember): 20484 def __init__(self, descriptor, attr, callbackName): 20485 assert attr.isAttr() 20486 assert attr.type.isObservableArray() 20487 CGNativeMember.__init__( 20488 self, 20489 descriptor, 20490 attr, 20491 self.makeNativeName(attr, callbackName), 20492 ( 20493 BuiltinTypes[IDLBuiltinType.Types.undefined], 20494 [ 20495 FakeArgument(attr.type.inner, "aValue"), 20496 FakeArgument( 20497 BuiltinTypes[IDLBuiltinType.Types.unsigned_long], "aIndex" 20498 ), 20499 ], 20500 ), 20501 ["needsErrorResult"], 20502 ) 20503 20504 def declare(self, cgClass): 20505 assert self.member.isAttr() 20506 assert self.member.type.isObservableArray() 20507 return CGNativeMember.declare(self, cgClass) 20508 20509 def define(self, cgClass): 20510 return "" 20511 20512 @staticmethod 20513 def makeNativeName(attr, callbackName): 20514 assert attr.isAttr() 20515 nativeName = MakeNativeName(attr.identifier.name) 20516 return "On" + callbackName + nativeName 20517 20518 20519 class CGExampleClass(CGBindingImplClass): 20520 """ 20521 Codegen for the actual example class implementation for this descriptor 20522 """ 20523 20524 def __init__(self, descriptor): 20525 CGBindingImplClass.__init__( 20526 self, 20527 descriptor, 20528 CGExampleMethod, 20529 CGExampleGetter, 20530 CGExampleSetter, 20531 wantGetParent=descriptor.wrapperCache, 20532 ) 20533 20534 self.parentIface = descriptor.interface.parent 20535 if self.parentIface: 20536 self.parentDesc = descriptor.getDescriptor(self.parentIface.identifier.name) 20537 bases = [ClassBase(self.nativeLeafName(self.parentDesc))] 20538 else: 20539 bases = [ 20540 ClassBase( 20541 "nsISupports /* or NonRefcountedDOMObject if this is a non-refcounted object */" 20542 ) 20543 ] 20544 if descriptor.wrapperCache: 20545 bases.append( 20546 ClassBase( 20547 "nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */" 20548 ) 20549 ) 20550 20551 destructorVisibility = "protected" 20552 if self.parentIface: 20553 extradeclarations = ( 20554 "public:\n" 20555 " NS_DECL_ISUPPORTS_INHERITED\n" 20556 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n" 20557 "\n" 20558 % ( 20559 self.nativeLeafName(descriptor), 20560 self.nativeLeafName(self.parentDesc), 20561 ) 20562 ) 20563 else: 20564 extradeclarations = ( 20565 "public:\n" 20566 " NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n" 20567 " NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(%s)\n" 20568 "\n" % self.nativeLeafName(descriptor) 20569 ) 20570 20571 if descriptor.interface.hasChildInterfaces(): 20572 decorators = "" 20573 else: 20574 decorators = "final" 20575 20576 for m in descriptor.interface.members: 20577 if m.isAttr() and m.type.isObservableArray(): 20578 self.methodDecls.append( 20579 CGExampleObservableArrayCallback(descriptor, m, "Set") 20580 ) 20581 self.methodDecls.append( 20582 CGExampleObservableArrayCallback(descriptor, m, "Delete") 20583 ) 20584 20585 CGClass.__init__( 20586 self, 20587 self.nativeLeafName(descriptor), 20588 bases=bases, 20589 constructors=[ClassConstructor([], visibility="public")], 20590 destructor=ClassDestructor(visibility=destructorVisibility), 20591 methods=self.methodDecls, 20592 decorators=decorators, 20593 extradeclarations=extradeclarations, 20594 ) 20595 20596 def define(self): 20597 # Just override CGClass and do our own thing 20598 nativeType = self.nativeLeafName(self.descriptor) 20599 20600 ctordtor = fill( 20601 """ 20602 ${nativeType}::${nativeType}() 20603 { 20604 // Add |MOZ_COUNT_CTOR(${nativeType});| for a non-refcounted object. 20605 } 20606 20607 ${nativeType}::~${nativeType}() 20608 { 20609 // Add |MOZ_COUNT_DTOR(${nativeType});| for a non-refcounted object. 20610 } 20611 """, 20612 nativeType=nativeType, 20613 ) 20614 20615 if self.parentIface: 20616 ccImpl = fill( 20617 """ 20618 20619 // Only needed for refcounted objects. 20620 # error "If you don't have members that need cycle collection, 20621 # then remove all the cycle collection bits from this 20622 # implementation and the corresponding header. If you do, you 20623 # want NS_IMPL_CYCLE_COLLECTION_INHERITED(${nativeType}, 20624 # ${parentType}, your, members, here)" 20625 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType}) 20626 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType}) 20627 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType}) 20628 NS_INTERFACE_MAP_END_INHERITING(${parentType}) 20629 20630 """, 20631 nativeType=nativeType, 20632 parentType=self.nativeLeafName(self.parentDesc), 20633 ) 20634 else: 20635 ccImpl = fill( 20636 """ 20637 20638 // Only needed for refcounted objects. 20639 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType}) 20640 NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType}) 20641 NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType}) 20642 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType}) 20643 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 20644 NS_INTERFACE_MAP_ENTRY(nsISupports) 20645 NS_INTERFACE_MAP_END 20646 20647 """, 20648 nativeType=nativeType, 20649 ) 20650 20651 classImpl = ccImpl + ctordtor + "\n" 20652 if self.descriptor.concrete: 20653 if self.descriptor.wrapperCache: 20654 reflectorArg = "" 20655 reflectorPassArg = "" 20656 returnType = "JSObject*" 20657 else: 20658 reflectorArg = ", JS::MutableHandle<JSObject*> aReflector" 20659 reflectorPassArg = ", aReflector" 20660 returnType = "bool" 20661 classImpl += fill( 20662 """ 20663 ${returnType} 20664 ${nativeType}::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto${reflectorArg}) 20665 { 20666 return ${ifaceName}_Binding::Wrap(aCx, this, aGivenProto${reflectorPassArg}); 20667 } 20668 20669 """, 20670 returnType=returnType, 20671 nativeType=nativeType, 20672 reflectorArg=reflectorArg, 20673 ifaceName=self.descriptor.name, 20674 reflectorPassArg=reflectorPassArg, 20675 ) 20676 20677 return classImpl 20678 20679 @staticmethod 20680 def nativeLeafName(descriptor): 20681 return descriptor.nativeType.split("::")[-1] 20682 20683 20684 class CGExampleRoot(CGThing): 20685 """ 20686 Root codegen class for example implementation generation. Instantiate the 20687 class and call declare or define to generate header or cpp code, 20688 respectively. 20689 """ 20690 20691 def __init__(self, config, interfaceName): 20692 descriptor = config.getDescriptor(interfaceName) 20693 20694 self.root = CGWrapper(CGExampleClass(descriptor), pre="\n", post="\n") 20695 20696 self.root = CGNamespace.build(["mozilla", "dom"], self.root) 20697 20698 builder = ForwardDeclarationBuilder() 20699 if descriptor.hasCEReactions(): 20700 builder.addInMozillaDom("DocGroup") 20701 for member in descriptor.interface.members: 20702 if not member.isAttr() and not member.isMethod(): 20703 continue 20704 if member.isStatic(): 20705 builder.addInMozillaDom("GlobalObject") 20706 if member.isAttr(): 20707 if not member.isMaplikeOrSetlikeAttr(): 20708 builder.forwardDeclareForType(member.type, config) 20709 else: 20710 assert member.isMethod() 20711 if not member.isMaplikeOrSetlikeOrIterableMethod(): 20712 for sig in member.signatures(): 20713 builder.forwardDeclareForType(sig[0], config) 20714 for arg in sig[1]: 20715 builder.forwardDeclareForType(arg.type, config) 20716 20717 self.root = CGList([builder.build(), self.root], "\n") 20718 20719 # Throw in our #includes 20720 self.root = CGHeaders( 20721 [], 20722 [], 20723 [], 20724 [], 20725 [ 20726 "nsWrapperCache.h", 20727 "nsCycleCollectionParticipant.h", 20728 "mozilla/Attributes.h", 20729 "mozilla/ErrorResult.h", 20730 "mozilla/dom/BindingDeclarations.h", 20731 "js/TypeDecls.h", 20732 ], 20733 [ 20734 "mozilla/dom/%s.h" % interfaceName, 20735 ( 20736 "mozilla/dom/%s" 20737 % CGHeaders.getDeclarationFilename(descriptor.interface) 20738 ), 20739 ], 20740 "", 20741 self.root, 20742 ) 20743 20744 # And now some include guards 20745 self.root = CGIncludeGuard(interfaceName, self.root) 20746 20747 # And our license block comes before everything else 20748 self.root = CGWrapper( 20749 self.root, 20750 pre=dedent( 20751 """ 20752 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 20753 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 20754 /* This Source Code Form is subject to the terms of the Mozilla Public 20755 * License, v. 2.0. If a copy of the MPL was not distributed with this 20756 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 20757 20758 """ 20759 ), 20760 ) 20761 20762 def declare(self): 20763 return self.root.declare() 20764 20765 def define(self): 20766 return self.root.define() 20767 20768 20769 def jsImplName(name): 20770 return name + "JSImpl" 20771 20772 20773 class CGJSImplMember(CGNativeMember): 20774 """ 20775 Base class for generating code for the members of the implementation class 20776 for a JS-implemented WebIDL interface. 20777 """ 20778 20779 def __init__( 20780 self, 20781 descriptorProvider, 20782 member, 20783 name, 20784 signature, 20785 extendedAttrs, 20786 breakAfter=True, 20787 passJSBitsAsNeeded=True, 20788 visibility="public", 20789 variadicIsSequence=False, 20790 virtual=False, 20791 override=False, 20792 ): 20793 CGNativeMember.__init__( 20794 self, 20795 descriptorProvider, 20796 member, 20797 name, 20798 signature, 20799 extendedAttrs, 20800 breakAfter=breakAfter, 20801 passJSBitsAsNeeded=passJSBitsAsNeeded, 20802 visibility=visibility, 20803 variadicIsSequence=variadicIsSequence, 20804 virtual=virtual, 20805 override=override, 20806 ) 20807 self.body = self.getImpl() 20808 20809 def getArgs(self, returnType, argList): 20810 args = CGNativeMember.getArgs(self, returnType, argList) 20811 args.append(Argument("JS::Realm*", "aRealm", "nullptr")) 20812 return args 20813 20814 20815 class CGJSImplMethod(CGJSImplMember): 20816 """ 20817 Class for generating code for the methods for a JS-implemented WebIDL 20818 interface. 20819 """ 20820 20821 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): 20822 self.signature = signature 20823 self.descriptor = descriptor 20824 self.isConstructor = isConstructor 20825 CGJSImplMember.__init__( 20826 self, 20827 descriptor, 20828 method, 20829 CGSpecializedMethod.makeNativeName(descriptor, method), 20830 signature, 20831 descriptor.getExtendedAttributes(method), 20832 breakAfter=breakAfter, 20833 variadicIsSequence=True, 20834 passJSBitsAsNeeded=False, 20835 ) 20836 20837 def getArgs(self, returnType, argList): 20838 if self.isConstructor: 20839 # Skip the JS::Compartment bits for constructors; it's handled 20840 # manually in getImpl. But we do need our aGivenProto argument. We 20841 # allow it to be omitted if the default proto is desired. 20842 return CGNativeMember.getArgs(self, returnType, argList) + [ 20843 Argument("JS::Handle<JSObject*>", "aGivenProto", "nullptr") 20844 ] 20845 return CGJSImplMember.getArgs(self, returnType, argList) 20846 20847 def getImpl(self): 20848 args = self.getArgs(self.signature[0], self.signature[1]) 20849 if not self.isConstructor: 20850 return "return mImpl->%s(%s);\n" % ( 20851 self.name, 20852 ", ".join(arg.name for arg in args), 20853 ) 20854 20855 assert self.descriptor.interface.isJSImplemented() 20856 if self.name != "Constructor": 20857 raise TypeError( 20858 "Legacy factory functions are not supported for JS implemented WebIDL." 20859 ) 20860 if len(self.signature[1]) != 0: 20861 # The first two arguments to the constructor implementation are not 20862 # arguments to the WebIDL constructor, so don't pass them to 20863 # __Init(). The last argument is the prototype we're supposed to 20864 # use, and shouldn't get passed to __Init() either. 20865 assert args[0].argType == "const GlobalObject&" 20866 assert args[1].argType == "JSContext*" 20867 assert args[-1].argType == "JS::Handle<JSObject*>" 20868 assert args[-1].name == "aGivenProto" 20869 constructorArgs = [arg.name for arg in args[2:-1]] 20870 constructorArgs.append("js::GetNonCCWObjectRealm(scopeObj)") 20871 initCall = fill( 20872 """ 20873 // Wrap the object before calling __Init so that __DOM_IMPL__ is available. 20874 JS::Rooted<JSObject*> scopeObj(cx, global.Get()); 20875 MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx)); 20876 JS::Rooted<JS::Value> wrappedVal(cx); 20877 if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal, aGivenProto)) { 20878 MOZ_ASSERT(JS_IsExceptionPending(cx)); 20879 aRv.Throw(NS_ERROR_UNEXPECTED); 20880 return nullptr; 20881 } 20882 // Initialize the object with the constructor arguments. 20883 impl->mImpl->__Init(${args}); 20884 if (aRv.Failed()) { 20885 return nullptr; 20886 } 20887 """, 20888 args=", ".join(constructorArgs), 20889 ) 20890 else: 20891 initCall = "" 20892 return fill( 20893 """ 20894 RefPtr<${implClass}> impl = 20895 ConstructJSImplementation<${implClass}>("${contractId}", global, aRv); 20896 if (aRv.Failed()) { 20897 return nullptr; 20898 } 20899 $*{initCall} 20900 return impl.forget(); 20901 """, 20902 contractId=self.descriptor.interface.getJSImplementation(), 20903 implClass=self.descriptor.name, 20904 initCall=initCall, 20905 ) 20906 20907 20908 # We're always fallible 20909 def callbackGetterName(attr, descriptor): 20910 return "Get" + MakeNativeName( 20911 descriptor.binaryNameFor(attr.identifier.name, attr.isStatic()) 20912 ) 20913 20914 20915 def callbackSetterName(attr, descriptor): 20916 return "Set" + MakeNativeName( 20917 descriptor.binaryNameFor(attr.identifier.name, attr.isStatic()) 20918 ) 20919 20920 20921 class CGJSImplGetter(CGJSImplMember): 20922 """ 20923 Class for generating code for the getters of attributes for a JS-implemented 20924 WebIDL interface. 20925 """ 20926 20927 def __init__(self, descriptor, attr): 20928 CGJSImplMember.__init__( 20929 self, 20930 descriptor, 20931 attr, 20932 CGSpecializedGetterCommon.makeNativeName(descriptor, attr), 20933 (attr.type, []), 20934 descriptor.getExtendedAttributes(attr, getter=True), 20935 passJSBitsAsNeeded=False, 20936 ) 20937 20938 def getImpl(self): 20939 callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])] 20940 return "return mImpl->%s(%s);\n" % ( 20941 callbackGetterName(self.member, self.descriptorProvider), 20942 ", ".join(callbackArgs), 20943 ) 20944 20945 20946 class CGJSImplSetter(CGJSImplMember): 20947 """ 20948 Class for generating code for the setters of attributes for a JS-implemented 20949 WebIDL interface. 20950 """ 20951 20952 def __init__(self, descriptor, attr): 20953 CGJSImplMember.__init__( 20954 self, 20955 descriptor, 20956 attr, 20957 CGSpecializedSetterCommon.makeNativeName(descriptor, attr), 20958 ( 20959 BuiltinTypes[IDLBuiltinType.Types.undefined], 20960 [FakeArgument(attr.type)], 20961 ), 20962 descriptor.getExtendedAttributes(attr, setter=True), 20963 passJSBitsAsNeeded=False, 20964 ) 20965 20966 def getImpl(self): 20967 callbackArgs = [ 20968 arg.name 20969 for arg in self.getArgs( 20970 BuiltinTypes[IDLBuiltinType.Types.undefined], 20971 [FakeArgument(self.member.type)], 20972 ) 20973 ] 20974 return "mImpl->%s(%s);\n" % ( 20975 callbackSetterName(self.member, self.descriptorProvider), 20976 ", ".join(callbackArgs), 20977 ) 20978 20979 20980 class CGJSImplClass(CGBindingImplClass): 20981 def __init__(self, descriptor): 20982 CGBindingImplClass.__init__( 20983 self, 20984 descriptor, 20985 CGJSImplMethod, 20986 CGJSImplGetter, 20987 CGJSImplSetter, 20988 skipStaticMethods=True, 20989 ) 20990 20991 if descriptor.interface.parent: 20992 parentClass = descriptor.getDescriptor( 20993 descriptor.interface.parent.identifier.name 20994 ).jsImplParent 20995 baseClasses = [ClassBase(parentClass)] 20996 isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n" 20997 ccDecl = "NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" % ( 20998 descriptor.name, 20999 parentClass, 21000 ) 21001 extradefinitions = fill( 21002 """ 21003 NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent) 21004 NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass}) 21005 NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass}) 21006 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName}) 21007 NS_INTERFACE_MAP_END_INHERITING(${parentClass}) 21008 """, 21009 ifaceName=self.descriptor.name, 21010 parentClass=parentClass, 21011 ) 21012 else: 21013 baseClasses = [ 21014 ClassBase("nsSupportsWeakReference"), 21015 ClassBase("nsWrapperCache"), 21016 ] 21017 isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n" 21018 ccDecl = ( 21019 "NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(%s)\n" % descriptor.name 21020 ) 21021 extradefinitions = fill( 21022 """ 21023 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(${ifaceName}) 21024 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(${ifaceName}) 21025 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl) 21026 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) 21027 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 21028 tmp->ClearWeakReferences(); 21029 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 21030 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName}) 21031 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl) 21032 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) 21033 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 21034 NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName}) 21035 NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName}) 21036 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName}) 21037 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 21038 NS_INTERFACE_MAP_ENTRY(nsISupports) 21039 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 21040 NS_INTERFACE_MAP_END 21041 """, 21042 ifaceName=self.descriptor.name, 21043 ) 21044 21045 extradeclarations = fill( 21046 """ 21047 public: 21048 $*{isupportsDecl} 21049 $*{ccDecl} 21050 21051 private: 21052 RefPtr<${jsImplName}> mImpl; 21053 nsCOMPtr<nsIGlobalObject> mParent; 21054 21055 """, 21056 isupportsDecl=isupportsDecl, 21057 ccDecl=ccDecl, 21058 jsImplName=jsImplName(descriptor.name), 21059 ) 21060 21061 if descriptor.interface.getExtendedAttribute("WantsEventListenerHooks"): 21062 # No need to do too much sanity checking here; the 21063 # generated code will fail to compile if the methods we 21064 # try to overrid aren't on a superclass. 21065 self.methodDecls.extend( 21066 self.getEventHookMethod(parentClass, "EventListenerAdded") 21067 ) 21068 self.methodDecls.extend( 21069 self.getEventHookMethod(parentClass, "EventListenerRemoved") 21070 ) 21071 21072 if descriptor.interface.hasChildInterfaces(): 21073 decorators = "" 21074 # We need a protected virtual destructor our subclasses can use 21075 destructor = ClassDestructor(virtual=True, visibility="protected") 21076 else: 21077 decorators = "final" 21078 destructor = ClassDestructor(virtual=False, visibility="private") 21079 21080 baseConstructors = [ 21081 ( 21082 "mImpl(new %s(nullptr, aJSImplObject, aJSImplGlobal, /* aIncumbentGlobal = */ nullptr))" 21083 % jsImplName(descriptor.name) 21084 ), 21085 "mParent(aParent)", 21086 ] 21087 parentInterface = descriptor.interface.parent 21088 while parentInterface: 21089 if parentInterface.isJSImplemented(): 21090 baseConstructors.insert( 21091 0, "%s(aJSImplObject, aJSImplGlobal, aParent)" % parentClass 21092 ) 21093 break 21094 parentInterface = parentInterface.parent 21095 if not parentInterface and descriptor.interface.parent: 21096 # We only have C++ ancestors, so only pass along the window 21097 baseConstructors.insert(0, "%s(aParent)" % parentClass) 21098 21099 constructor = ClassConstructor( 21100 [ 21101 Argument("JS::Handle<JSObject*>", "aJSImplObject"), 21102 Argument("JS::Handle<JSObject*>", "aJSImplGlobal"), 21103 Argument("nsIGlobalObject*", "aParent"), 21104 ], 21105 visibility="public", 21106 baseConstructors=baseConstructors, 21107 ) 21108 21109 self.methodDecls.append( 21110 ClassMethod( 21111 "_Create", 21112 "bool", 21113 JSNativeArguments(), 21114 static=True, 21115 body=self.getCreateFromExistingBody(), 21116 ) 21117 ) 21118 21119 CGClass.__init__( 21120 self, 21121 descriptor.name, 21122 bases=baseClasses, 21123 constructors=[constructor], 21124 destructor=destructor, 21125 methods=self.methodDecls, 21126 decorators=decorators, 21127 extradeclarations=extradeclarations, 21128 extradefinitions=extradefinitions, 21129 ) 21130 21131 def getWrapObjectBody(self): 21132 return fill( 21133 """ 21134 JS::Rooted<JSObject*> obj(aCx, ${name}_Binding::Wrap(aCx, this, aGivenProto)); 21135 if (!obj) { 21136 return nullptr; 21137 } 21138 21139 // Now define it on our chrome object 21140 JSAutoRealm ar(aCx, mImpl->CallbackGlobalOrNull()); 21141 if (!JS_WrapObject(aCx, &obj)) { 21142 return nullptr; 21143 } 21144 JS::Rooted<JSObject*> callback(aCx, mImpl->CallbackOrNull()); 21145 if (!JS_DefineProperty(aCx, callback, "__DOM_IMPL__", obj, 0)) { 21146 return nullptr; 21147 } 21148 return obj; 21149 """, 21150 name=self.descriptor.name, 21151 ) 21152 21153 def getGetParentObjectReturnType(self): 21154 return "nsISupports*" 21155 21156 def getGetParentObjectBody(self): 21157 return "return mParent;\n" 21158 21159 def getGetDocGroupBody(self): 21160 return dedent( 21161 """ 21162 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent); 21163 if (!window) { 21164 return nullptr; 21165 } 21166 return window->GetDocGroup(); 21167 """ 21168 ) 21169 21170 def getCreateFromExistingBody(self): 21171 # XXXbz we could try to get parts of this (e.g. the argument 21172 # conversions) auto-generated by somehow creating an IDLMethod and 21173 # adding it to our interface, but we'd still need to special-case the 21174 # implementation slightly to have it not try to forward to the JS 21175 # object... 21176 return fill( 21177 """ 21178 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 21179 if (!args.requireAtLeast(cx, "${ifaceName}._create", 2)) { 21180 return false; 21181 } 21182 BindingCallContext callCx(cx, "${ifaceName}._create"); 21183 if (!args[0].isObject()) { 21184 return callCx.ThrowErrorMessage<MSG_NOT_OBJECT>("Argument 1"); 21185 } 21186 if (!args[1].isObject()) { 21187 return callCx.ThrowErrorMessage<MSG_NOT_OBJECT>("Argument 2"); 21188 } 21189 21190 // GlobalObject will go through wrappers as needed for us, and 21191 // is simpler than the right UnwrapArg incantation. 21192 GlobalObject global(cx, &args[0].toObject()); 21193 if (global.Failed()) { 21194 return false; 21195 } 21196 nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(global.GetAsSupports()); 21197 MOZ_ASSERT(globalHolder); 21198 JS::Rooted<JSObject*> arg(cx, &args[1].toObject()); 21199 JS::Rooted<JSObject*> argGlobal(cx, JS::CurrentGlobalOrNull(cx)); 21200 RefPtr<${implName}> impl = new ${implName}(arg, argGlobal, globalHolder); 21201 MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx)); 21202 return GetOrCreateDOMReflector(cx, impl, args.rval()); 21203 """, 21204 ifaceName=self.descriptor.interface.identifier.name, 21205 implName=self.descriptor.name, 21206 ) 21207 21208 def getEventHookMethod(self, parentClass, methodName): 21209 body = fill( 21210 """ 21211 ${parentClass}::${methodName}(aType); 21212 mImpl->${methodName}(Substring(nsDependentAtomString(aType), 2), IgnoreErrors()); 21213 """, 21214 parentClass=parentClass, 21215 methodName=methodName, 21216 ) 21217 return [ 21218 ClassMethod( 21219 methodName, 21220 "void", 21221 [Argument("nsAtom*", "aType")], 21222 virtual=True, 21223 override=True, 21224 body=body, 21225 ), 21226 ClassUsingFromBaseDeclaration(parentClass, methodName), 21227 ] 21228 21229 21230 def isJSImplementedDescriptor(descriptorProvider): 21231 return ( 21232 isinstance(descriptorProvider, Descriptor) 21233 and descriptorProvider.interface.isJSImplemented() 21234 ) 21235 21236 21237 class CGCallback(CGClass): 21238 def __init__( 21239 self, idlObject, descriptorProvider, baseName, methods, getters=[], setters=[] 21240 ): 21241 self.baseName = baseName 21242 self._deps = idlObject.getDeps() 21243 self.idlObject = idlObject 21244 self.name = idlObject.identifier.name 21245 if isJSImplementedDescriptor(descriptorProvider): 21246 self.name = jsImplName(self.name) 21247 # For our public methods that needThisHandling we want most of the 21248 # same args and the same return type as what CallbackMember 21249 # generates. So we want to take advantage of all its 21250 # CGNativeMember infrastructure, but that infrastructure can't deal 21251 # with templates and most especially template arguments. So just 21252 # cheat and have CallbackMember compute all those things for us. 21253 realMethods = [] 21254 for method in methods: 21255 if not isinstance(method, CallbackMember) or not method.needThisHandling: 21256 realMethods.append(method) 21257 else: 21258 realMethods.extend(self.getMethodImpls(method)) 21259 realMethods.append( 21260 ClassMethod( 21261 "operator==", 21262 "bool", 21263 [Argument("const %s&" % self.name, "aOther")], 21264 inline=True, 21265 bodyInHeader=True, 21266 const=True, 21267 body=("return %s::operator==(aOther);\n" % baseName), 21268 ) 21269 ) 21270 CGClass.__init__( 21271 self, 21272 self.name, 21273 bases=[ClassBase(baseName)], 21274 constructors=self.getConstructors(), 21275 methods=realMethods + getters + setters, 21276 ) 21277 21278 def getConstructors(self): 21279 if ( 21280 not self.idlObject.isInterface() 21281 and not self.idlObject._treatNonObjectAsNull 21282 ): 21283 body = "MOZ_ASSERT(JS::IsCallable(mCallback));\n" 21284 else: 21285 # Not much we can assert about it, other than not being null, and 21286 # CallbackObject does that already. 21287 body = "" 21288 return [ 21289 ClassConstructor( 21290 [ 21291 Argument("JSContext*", "aCx"), 21292 Argument("JS::Handle<JSObject*>", "aCallback"), 21293 Argument("JS::Handle<JSObject*>", "aCallbackGlobal"), 21294 Argument("nsIGlobalObject*", "aIncumbentGlobal"), 21295 ], 21296 bodyInHeader=True, 21297 visibility="public", 21298 explicit=True, 21299 baseConstructors=[ 21300 "%s(aCx, aCallback, aCallbackGlobal, aIncumbentGlobal)" 21301 % self.baseName, 21302 ], 21303 body=body, 21304 ), 21305 ClassConstructor( 21306 [ 21307 Argument("JSObject*", "aCallback"), 21308 Argument("JSObject*", "aCallbackGlobal"), 21309 Argument("const FastCallbackConstructor&", ""), 21310 ], 21311 bodyInHeader=True, 21312 visibility="public", 21313 explicit=True, 21314 baseConstructors=[ 21315 "%s(aCallback, aCallbackGlobal, FastCallbackConstructor())" 21316 % self.baseName, 21317 ], 21318 body=body, 21319 ), 21320 ClassConstructor( 21321 [ 21322 Argument("JSObject*", "aCallback"), 21323 Argument("JSObject*", "aCallbackGlobal"), 21324 Argument("JSObject*", "aAsyncStack"), 21325 Argument("nsIGlobalObject*", "aIncumbentGlobal"), 21326 ], 21327 bodyInHeader=True, 21328 visibility="public", 21329 explicit=True, 21330 baseConstructors=[ 21331 "%s(aCallback, aCallbackGlobal, aAsyncStack, aIncumbentGlobal)" 21332 % self.baseName, 21333 ], 21334 body=body, 21335 ), 21336 ] 21337 21338 def getMethodImpls(self, method): 21339 assert method.needThisHandling 21340 args = list(method.args) 21341 # Strip out the BindingCallContext&/JSObject* args 21342 # that got added. 21343 assert args[0].name == "cx" and args[0].argType == "BindingCallContext&" 21344 assert args[1].name == "aThisVal" and args[1].argType == "JS::Handle<JS::Value>" 21345 args = args[2:] 21346 21347 # Now remember which index the ErrorResult argument is at; 21348 # we'll need this below. 21349 assert args[-1].name == "aRv" and args[-1].argType == "ErrorResult&" 21350 rvIndex = len(args) - 1 21351 assert rvIndex >= 0 21352 21353 # Record the names of all the arguments, so we can use them when we call 21354 # the private method. 21355 argnames = [arg.name for arg in args] 21356 argnamesWithThis = ["s.GetCallContext()", "thisValJS"] + argnames 21357 argnamesWithoutThis = [ 21358 "s.GetCallContext()", 21359 "JS::UndefinedHandleValue", 21360 ] + argnames 21361 # Now that we've recorded the argnames for our call to our private 21362 # method, insert our optional argument for the execution reason. 21363 args.append(Argument("const char*", "aExecutionReason", "nullptr")) 21364 21365 # Make copies of the arg list for the two "without rv" overloads. Note 21366 # that those don't need aExceptionHandling or aRealm arguments because 21367 # those would make not sense anyway: the only sane thing to do with 21368 # exceptions in the "without rv" cases is to report them. 21369 argsWithoutRv = list(args) 21370 argsWithoutRv.pop(rvIndex) 21371 argsWithoutThisAndRv = list(argsWithoutRv) 21372 21373 # Add the potional argument for deciding whether the CallSetup should 21374 # re-throw exceptions on aRv. 21375 args.append( 21376 Argument("ExceptionHandling", "aExceptionHandling", "eReportExceptions") 21377 ) 21378 # And the argument for communicating when exceptions should really be 21379 # rethrown. In particular, even when aExceptionHandling is 21380 # eRethrowExceptions they won't get rethrown if aRealm is provided 21381 # and its principal doesn't subsume either the callback or the 21382 # exception. 21383 args.append(Argument("JS::Realm*", "aRealm", "nullptr")) 21384 # And now insert our template argument. 21385 argsWithoutThis = list(args) 21386 args.insert(0, Argument("const T&", "thisVal")) 21387 argsWithoutRv.insert(0, Argument("const T&", "thisVal")) 21388 21389 argnamesWithoutThisAndRv = [arg.name for arg in argsWithoutThisAndRv] 21390 argnamesWithoutThisAndRv.insert(rvIndex, "IgnoreErrors()") 21391 # If we just leave things like that, and have no actual arguments in the 21392 # IDL, we will end up trying to call the templated "without rv" overload 21393 # with "rv" as the thisVal. That's no good. So explicitly append the 21394 # aExceptionHandling and aRealm values we need to end up matching the 21395 # signature of our non-templated "with rv" overload. 21396 argnamesWithoutThisAndRv.extend(["eReportExceptions", "nullptr"]) 21397 21398 argnamesWithoutRv = [arg.name for arg in argsWithoutRv] 21399 # Note that we need to insert at rvIndex + 1, since we inserted a 21400 # thisVal arg at the start. 21401 argnamesWithoutRv.insert(rvIndex + 1, "IgnoreErrors()") 21402 21403 errorReturn = method.getDefaultRetval() 21404 21405 setupCall = fill( 21406 """ 21407 MOZ_ASSERT(!aRv.Failed(), "Don't pass an already-failed ErrorResult to a callback!"); 21408 if (!aExecutionReason) { 21409 aExecutionReason = "${executionReason}"; 21410 } 21411 CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aRealm); 21412 if (!s.GetContext()) { 21413 MOZ_ASSERT(aRv.Failed()); 21414 return${errorReturn}; 21415 } 21416 """, 21417 errorReturn=errorReturn, 21418 executionReason=method.getPrettyName(), 21419 ) 21420 21421 bodyWithThis = fill( 21422 """ 21423 $*{setupCall} 21424 JS::Rooted<JS::Value> thisValJS(s.GetContext()); 21425 if (!ToJSValue(s.GetContext(), thisVal, &thisValJS)) { 21426 aRv.Throw(NS_ERROR_FAILURE); 21427 return${errorReturn}; 21428 } 21429 return ${methodName}(${callArgs}); 21430 """, 21431 setupCall=setupCall, 21432 errorReturn=errorReturn, 21433 methodName=method.name, 21434 callArgs=", ".join(argnamesWithThis), 21435 ) 21436 bodyWithoutThis = fill( 21437 """ 21438 $*{setupCall} 21439 return ${methodName}(${callArgs}); 21440 """, 21441 setupCall=setupCall, 21442 errorReturn=errorReturn, 21443 methodName=method.name, 21444 callArgs=", ".join(argnamesWithoutThis), 21445 ) 21446 bodyWithThisWithoutRv = fill( 21447 """ 21448 return ${methodName}(${callArgs}); 21449 """, 21450 methodName=method.name, 21451 callArgs=", ".join(argnamesWithoutRv), 21452 ) 21453 bodyWithoutThisAndRv = fill( 21454 """ 21455 return ${methodName}(${callArgs}); 21456 """, 21457 methodName=method.name, 21458 callArgs=", ".join(argnamesWithoutThisAndRv), 21459 ) 21460 21461 return [ 21462 ClassMethod( 21463 method.name, 21464 method.returnType, 21465 args, 21466 bodyInHeader=True, 21467 templateArgs=["typename T"], 21468 body=bodyWithThis, 21469 canRunScript=method.canRunScript, 21470 ), 21471 ClassMethod( 21472 method.name, 21473 method.returnType, 21474 argsWithoutThis, 21475 bodyInHeader=True, 21476 body=bodyWithoutThis, 21477 canRunScript=method.canRunScript, 21478 ), 21479 ClassMethod( 21480 method.name, 21481 method.returnType, 21482 argsWithoutRv, 21483 bodyInHeader=True, 21484 templateArgs=["typename T"], 21485 body=bodyWithThisWithoutRv, 21486 canRunScript=method.canRunScript, 21487 ), 21488 ClassMethod( 21489 method.name, 21490 method.returnType, 21491 argsWithoutThisAndRv, 21492 bodyInHeader=True, 21493 body=bodyWithoutThisAndRv, 21494 canRunScript=method.canRunScript, 21495 ), 21496 method, 21497 ] 21498 21499 def deps(self): 21500 return self._deps 21501 21502 21503 class CGCallbackFunction(CGCallback): 21504 def __init__(self, callback, descriptorProvider): 21505 self.callback = callback 21506 if callback.isConstructor(): 21507 methods = [ConstructCallback(callback, descriptorProvider)] 21508 else: 21509 methods = [CallCallback(callback, descriptorProvider)] 21510 CGCallback.__init__( 21511 self, callback, descriptorProvider, "CallbackFunction", methods 21512 ) 21513 21514 def getConstructors(self): 21515 return CGCallback.getConstructors(self) + [ 21516 ClassConstructor( 21517 [Argument("CallbackFunction*", "aOther")], 21518 bodyInHeader=True, 21519 visibility="public", 21520 explicit=True, 21521 baseConstructors=["CallbackFunction(aOther)"], 21522 ) 21523 ] 21524 21525 21526 class CGFastCallback(CGClass): 21527 def __init__(self, idlObject): 21528 self._deps = idlObject.getDeps() 21529 baseName = idlObject.identifier.name 21530 constructor = ClassConstructor( 21531 [ 21532 Argument("JSObject*", "aCallback"), 21533 Argument("JSObject*", "aCallbackGlobal"), 21534 ], 21535 bodyInHeader=True, 21536 visibility="public", 21537 explicit=True, 21538 baseConstructors=[ 21539 "%s(aCallback, aCallbackGlobal, FastCallbackConstructor())" % baseName, 21540 ], 21541 body="", 21542 ) 21543 21544 traceMethod = ClassMethod( 21545 "Trace", 21546 "void", 21547 [Argument("JSTracer*", "aTracer")], 21548 inline=True, 21549 bodyInHeader=True, 21550 visibility="public", 21551 body="%s::Trace(aTracer);\n" % baseName, 21552 ) 21553 holdMethod = ClassMethod( 21554 "FinishSlowJSInitIfMoreThanOneOwner", 21555 "void", 21556 [Argument("JSContext*", "aCx")], 21557 inline=True, 21558 bodyInHeader=True, 21559 visibility="public", 21560 body=("%s::FinishSlowJSInitIfMoreThanOneOwner(aCx);\n" % baseName), 21561 ) 21562 21563 CGClass.__init__( 21564 self, 21565 "Fast%s" % baseName, 21566 bases=[ClassBase(baseName)], 21567 constructors=[constructor], 21568 methods=[traceMethod, holdMethod], 21569 ) 21570 21571 def deps(self): 21572 return self._deps 21573 21574 21575 class CGCallbackInterface(CGCallback): 21576 def __init__(self, descriptor, spiderMonkeyInterfacesAreStructs=False): 21577 iface = descriptor.interface 21578 attrs = [ 21579 m 21580 for m in iface.members 21581 if ( 21582 m.isAttr() 21583 and not m.isStatic() 21584 and (not m.isMaplikeOrSetlikeAttr() or not iface.isJSImplemented()) 21585 ) 21586 ] 21587 getters = [ 21588 CallbackGetter(a, descriptor, spiderMonkeyInterfacesAreStructs) 21589 for a in attrs 21590 ] 21591 setters = [ 21592 CallbackSetter(a, descriptor, spiderMonkeyInterfacesAreStructs) 21593 for a in attrs 21594 if not a.readonly 21595 ] 21596 methods = [ 21597 m 21598 for m in iface.members 21599 if ( 21600 m.isMethod() 21601 and not m.isStatic() 21602 and not m.isIdentifierLess() 21603 and ( 21604 not m.isMaplikeOrSetlikeOrIterableMethod() 21605 or not iface.isJSImplemented() 21606 ) 21607 ) 21608 ] 21609 methods = [ 21610 CallbackOperation(m, sig, descriptor, spiderMonkeyInterfacesAreStructs) 21611 for m in methods 21612 for sig in m.signatures() 21613 ] 21614 21615 needInitId = False 21616 if iface.isJSImplemented() and iface.ctor(): 21617 sigs = descriptor.interface.ctor().signatures() 21618 if len(sigs) != 1: 21619 raise TypeError("We only handle one constructor. See bug 869268.") 21620 methods.append(CGJSImplInitOperation(sigs[0], descriptor)) 21621 needInitId = True 21622 21623 idlist = [ 21624 descriptor.binaryNameFor(m.identifier.name, m.isStatic()) 21625 for m in iface.members 21626 if m.isAttr() or m.isMethod() 21627 ] 21628 if needInitId: 21629 idlist.append("__init") 21630 21631 if iface.isJSImplemented() and iface.getExtendedAttribute( 21632 "WantsEventListenerHooks" 21633 ): 21634 methods.append(CGJSImplEventHookOperation(descriptor, "eventListenerAdded")) 21635 methods.append( 21636 CGJSImplEventHookOperation(descriptor, "eventListenerRemoved") 21637 ) 21638 idlist.append("eventListenerAdded") 21639 idlist.append("eventListenerRemoved") 21640 21641 if len(idlist) != 0: 21642 methods.append(initIdsClassMethod(idlist, iface.identifier.name + "Atoms")) 21643 CGCallback.__init__( 21644 self, 21645 iface, 21646 descriptor, 21647 "CallbackInterface", 21648 methods, 21649 getters=getters, 21650 setters=setters, 21651 ) 21652 21653 21654 class FakeMember: 21655 def __init__(self, name=None): 21656 if name is not None: 21657 self.identifier = FakeIdentifier(name) 21658 21659 def isStatic(self): 21660 return False 21661 21662 def isAttr(self): 21663 return False 21664 21665 def isMethod(self): 21666 return False 21667 21668 def getExtendedAttribute(self, name): 21669 # Claim to be a [NewObject] so we can avoid the "return a raw pointer" 21670 # comments CGNativeMember codegen would otherwise stick in. 21671 if name == "NewObject": 21672 return True 21673 return None 21674 21675 21676 class CallbackMember(CGNativeMember): 21677 # XXXbz It's OK to use CallbackKnownNotGray for wrapScope because 21678 # CallSetup already handled the unmark-gray bits for us. we don't have 21679 # anything better to use for 'obj', really... 21680 def __init__( 21681 self, 21682 sig, 21683 name, 21684 descriptorProvider, 21685 needThisHandling, 21686 rethrowContentException=False, 21687 spiderMonkeyInterfacesAreStructs=False, 21688 wrapScope=None, 21689 canRunScript=False, 21690 passJSBitsAsNeeded=False, 21691 ): 21692 """ 21693 needThisHandling is True if we need to be able to accept a specified 21694 thisObj, False otherwise. 21695 """ 21696 assert not rethrowContentException or not needThisHandling 21697 21698 self.retvalType = sig[0] 21699 self.originalSig = sig 21700 args = sig[1] 21701 self.argCount = len(args) 21702 if self.argCount > 0: 21703 # Check for variadic arguments 21704 lastArg = args[self.argCount - 1] 21705 if lastArg.variadic: 21706 self.argCountStr = "(%d - 1) + %s.Length()" % ( 21707 self.argCount, 21708 lastArg.identifier.name, 21709 ) 21710 else: 21711 self.argCountStr = "%d" % self.argCount 21712 self.needThisHandling = needThisHandling 21713 # If needThisHandling, we generate ourselves as private and the caller 21714 # will handle generating public versions that handle the "this" stuff. 21715 visibility = "private" if needThisHandling else "public" 21716 self.rethrowContentException = rethrowContentException 21717 21718 self.wrapScope = wrapScope 21719 # We don't care, for callback codegen, whether our original member was 21720 # a method or attribute or whatnot. Just always pass FakeMember() 21721 # here. 21722 CGNativeMember.__init__( 21723 self, 21724 descriptorProvider, 21725 FakeMember(), 21726 name, 21727 (self.retvalType, args), 21728 extendedAttrs=["needsErrorResult"], 21729 passJSBitsAsNeeded=passJSBitsAsNeeded, 21730 visibility=visibility, 21731 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs, 21732 canRunScript=canRunScript, 21733 ) 21734 # We have to do all the generation of our body now, because 21735 # the caller relies on us throwing if we can't manage it. 21736 self.body = self.getImpl() 21737 21738 def getImpl(self): 21739 setupCall = self.getCallSetup() 21740 declRval = self.getRvalDecl() 21741 if self.argCount > 0: 21742 argvDecl = fill( 21743 """ 21744 JS::RootedVector<JS::Value> argv(cx); 21745 if (!argv.resize(${argCount})) { 21746 $*{failureCode} 21747 return${errorReturn}; 21748 } 21749 """, 21750 argCount=self.argCountStr, 21751 failureCode=self.getArgvDeclFailureCode(), 21752 errorReturn=self.getDefaultRetval(), 21753 ) 21754 else: 21755 # Avoid weird 0-sized arrays 21756 argvDecl = "" 21757 convertArgs = self.getArgConversions() 21758 doCall = self.getCall() 21759 returnResult = self.getResultConversion() 21760 21761 body = declRval + argvDecl + convertArgs + doCall 21762 if self.needsScopeBody(): 21763 body = "{\n" + indent(body) + "}\n" 21764 return setupCall + body + returnResult 21765 21766 def needsScopeBody(self): 21767 return False 21768 21769 def getArgvDeclFailureCode(self): 21770 return dedent( 21771 """ 21772 // That threw an exception on the JSContext, and our CallSetup will do 21773 // the right thing with that. 21774 """ 21775 ) 21776 21777 def getExceptionCode(self, forResult): 21778 return fill( 21779 """ 21780 aRv.Throw(NS_ERROR_UNEXPECTED); 21781 return${defaultRetval}; 21782 """, 21783 defaultRetval=self.getDefaultRetval(), 21784 ) 21785 21786 def getResultConversion( 21787 self, val="rval", failureCode=None, isDefinitelyObject=False, exceptionCode=None 21788 ): 21789 replacements = { 21790 "val": val, 21791 "holderName": "rvalHolder", 21792 "declName": "rvalDecl", 21793 # We actually want to pass in a null scope object here, because 21794 # wrapping things into our current compartment (that of mCallback) 21795 # is what we want. 21796 "obj": "nullptr", 21797 "passedToJSImpl": "false", 21798 } 21799 21800 if isJSImplementedDescriptor(self.descriptorProvider): 21801 isCallbackReturnValue = "JSImpl" 21802 else: 21803 isCallbackReturnValue = "Callback" 21804 sourceDescription = "return value of %s" % self.getPrettyName() 21805 convertType = instantiateJSToNativeConversion( 21806 getJSToNativeConversionInfo( 21807 self.retvalType, 21808 self.descriptorProvider, 21809 failureCode=failureCode, 21810 isDefinitelyObject=isDefinitelyObject, 21811 exceptionCode=exceptionCode or self.getExceptionCode(forResult=True), 21812 isCallbackReturnValue=isCallbackReturnValue, 21813 # Allow returning a callback type that 21814 # allows non-callable objects. 21815 allowTreatNonCallableAsNull=True, 21816 sourceDescription=sourceDescription, 21817 ), 21818 replacements, 21819 ) 21820 assignRetval = string.Template( 21821 self.getRetvalInfo(self.retvalType, False)[2] 21822 ).substitute(replacements) 21823 type = convertType.define() 21824 return type + assignRetval 21825 21826 def getArgConversions(self): 21827 # Just reget the arglist from self.originalSig, because our superclasses 21828 # just have way to many members they like to clobber, so I can't find a 21829 # safe member name to store it in. 21830 argConversions = [ 21831 self.getArgConversion(i, arg) for i, arg in enumerate(self.originalSig[1]) 21832 ] 21833 if not argConversions: 21834 return "\n" 21835 21836 # Do them back to front, so our argc modifications will work 21837 # correctly, because we examine trailing arguments first. 21838 argConversions.reverse() 21839 # Wrap each one in a scope so that any locals it has don't leak out, and 21840 # also so that we can just "break;" for our successCode. 21841 argConversions = [ 21842 CGWrapper(CGIndenter(CGGeneric(c)), pre="do {\n", post="} while (false);\n") 21843 for c in argConversions 21844 ] 21845 if self.argCount > 0: 21846 argConversions.insert(0, self.getArgcDecl()) 21847 # And slap them together. 21848 return CGList(argConversions, "\n").define() + "\n" 21849 21850 def getArgConversion(self, i, arg): 21851 argval = arg.identifier.name 21852 21853 if arg.variadic: 21854 argval = argval + "[idx]" 21855 jsvalIndex = "%d + idx" % i 21856 else: 21857 jsvalIndex = "%d" % i 21858 if arg.canHaveMissingValue(): 21859 argval += ".Value()" 21860 21861 prepend = "" 21862 21863 wrapScope = self.wrapScope 21864 if arg.type.isUnion() and wrapScope is None: 21865 prepend += ( 21866 "JS::Rooted<JSObject*> callbackObj(cx, CallbackKnownNotGray());\n" 21867 ) 21868 wrapScope = "callbackObj" 21869 21870 conversion = prepend + wrapForType( 21871 arg.type, 21872 self.descriptorProvider, 21873 { 21874 "result": argval, 21875 "successCode": "continue;\n" if arg.variadic else "break;\n", 21876 "jsvalRef": "argv[%s]" % jsvalIndex, 21877 "jsvalHandle": "argv[%s]" % jsvalIndex, 21878 "obj": wrapScope, 21879 "returnsNewObject": False, 21880 "exceptionCode": self.getExceptionCode(forResult=False), 21881 "spiderMonkeyInterfacesAreStructs": self.spiderMonkeyInterfacesAreStructs, 21882 }, 21883 ) 21884 21885 if arg.variadic: 21886 conversion = fill( 21887 """ 21888 for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) { 21889 $*{conversion} 21890 } 21891 break; 21892 """, 21893 arg=arg.identifier.name, 21894 conversion=conversion, 21895 ) 21896 elif arg.canHaveMissingValue(): 21897 conversion = fill( 21898 """ 21899 if (${argName}.WasPassed()) { 21900 $*{conversion} 21901 } else if (argc == ${iPlus1}) { 21902 // This is our current trailing argument; reduce argc 21903 --argc; 21904 } else { 21905 argv[${i}].setUndefined(); 21906 } 21907 """, 21908 argName=arg.identifier.name, 21909 conversion=conversion, 21910 iPlus1=i + 1, 21911 i=i, 21912 ) 21913 return conversion 21914 21915 def getDefaultRetval(self): 21916 default = self.getRetvalInfo(self.retvalType, False)[1] 21917 if len(default) != 0: 21918 default = " " + default 21919 return default 21920 21921 def getArgs(self, returnType, argList): 21922 args = CGNativeMember.getArgs(self, returnType, argList) 21923 if not self.needThisHandling: 21924 # Since we don't need this handling, we're the actual method that 21925 # will be called, so we need an aRethrowExceptions argument. 21926 if not self.rethrowContentException: 21927 args.append(Argument("const char*", "aExecutionReason", "nullptr")) 21928 args.append( 21929 Argument( 21930 "ExceptionHandling", "aExceptionHandling", "eReportExceptions" 21931 ) 21932 ) 21933 args.append(Argument("JS::Realm*", "aRealm", "nullptr")) 21934 return args 21935 # We want to allow the caller to pass in a "this" value, as 21936 # well as a BindingCallContext. 21937 return [ 21938 Argument("BindingCallContext&", "cx"), 21939 Argument("JS::Handle<JS::Value>", "aThisVal"), 21940 ] + args 21941 21942 def getCallSetup(self): 21943 if self.needThisHandling: 21944 # It's been done for us already 21945 return "" 21946 callSetup = "CallSetup s(this, aRv" 21947 if self.rethrowContentException: 21948 # getArgs doesn't add the aExceptionHandling argument but does add 21949 # aRealm for us. 21950 callSetup += ( 21951 ', "%s", eRethrowContentExceptions, aRealm, /* aIsJSImplementedWebIDL = */ ' 21952 % self.getPrettyName() 21953 ) 21954 callSetup += toStringBool( 21955 isJSImplementedDescriptor(self.descriptorProvider) 21956 ) 21957 else: 21958 callSetup += ', "%s", aExceptionHandling, aRealm' % self.getPrettyName() 21959 callSetup += ");\n" 21960 return fill( 21961 """ 21962 $*{callSetup} 21963 if (aRv.Failed()) { 21964 return${errorReturn}; 21965 } 21966 MOZ_ASSERT(s.GetContext()); 21967 BindingCallContext& cx = s.GetCallContext(); 21968 21969 """, 21970 callSetup=callSetup, 21971 errorReturn=self.getDefaultRetval(), 21972 ) 21973 21974 def getArgcDecl(self): 21975 return CGGeneric("unsigned argc = %s;\n" % self.argCountStr) 21976 21977 @staticmethod 21978 def ensureASCIIName(idlObject): 21979 type = "attribute" if idlObject.isAttr() else "operation" 21980 if re.match("[^\x20-\x7E]", idlObject.identifier.name): 21981 raise SyntaxError( 21982 'Callback %s name "%s" contains non-ASCII ' 21983 "characters. We can't handle that. %s" 21984 % (type, idlObject.identifier.name, idlObject.location) 21985 ) 21986 if re.match('"', idlObject.identifier.name): 21987 raise SyntaxError( 21988 "Callback %s name '%s' contains " 21989 "double-quote character. We can't handle " 21990 "that. %s" % (type, idlObject.identifier.name, idlObject.location) 21991 ) 21992 21993 21994 class ConstructCallback(CallbackMember): 21995 def __init__(self, callback, descriptorProvider): 21996 self.callback = callback 21997 CallbackMember.__init__( 21998 self, 21999 callback.signatures()[0], 22000 "Construct", 22001 descriptorProvider, 22002 needThisHandling=False, 22003 canRunScript=True, 22004 ) 22005 22006 def getRvalDecl(self): 22007 # Box constructedObj for getJSToNativeConversionInfo(). 22008 return "JS::Rooted<JS::Value> rval(cx);\n" 22009 22010 def getCall(self): 22011 if self.argCount > 0: 22012 args = "JS::HandleValueArray::subarray(argv, 0, argc)" 22013 else: 22014 args = "JS::HandleValueArray::empty()" 22015 22016 return fill( 22017 """ 22018 JS::Rooted<JS::Value> constructor(cx, JS::ObjectValue(*mCallback)); 22019 JS::Rooted<JSObject*> constructedObj(cx); 22020 if (!JS::Construct(cx, constructor, 22021 ${args}, &constructedObj)) { 22022 aRv.NoteJSContextException(cx); 22023 return${errorReturn}; 22024 } 22025 rval.setObject(*constructedObj); 22026 """, 22027 args=args, 22028 errorReturn=self.getDefaultRetval(), 22029 ) 22030 22031 def getResultConversion(self): 22032 return CallbackMember.getResultConversion(self, isDefinitelyObject=True) 22033 22034 def getPrettyName(self): 22035 return self.callback.identifier.name 22036 22037 22038 class CallbackMethod(CallbackMember): 22039 def __init__( 22040 self, 22041 sig, 22042 name, 22043 descriptorProvider, 22044 needThisHandling, 22045 rethrowContentException=False, 22046 spiderMonkeyInterfacesAreStructs=False, 22047 canRunScript=False, 22048 ): 22049 CallbackMember.__init__( 22050 self, 22051 sig, 22052 name, 22053 descriptorProvider, 22054 needThisHandling, 22055 rethrowContentException, 22056 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs, 22057 canRunScript=canRunScript, 22058 ) 22059 22060 def getRvalDecl(self): 22061 return "JS::Rooted<JS::Value> rval(cx);\n" 22062 22063 def getNoteCallFailed(self): 22064 return fill( 22065 """ 22066 aRv.NoteJSContextException(cx); 22067 return${errorReturn}; 22068 """, 22069 errorReturn=self.getDefaultRetval(), 22070 ) 22071 22072 def getCall(self): 22073 if self.argCount > 0: 22074 args = "JS::HandleValueArray::subarray(argv, 0, argc)" 22075 else: 22076 args = "JS::HandleValueArray::empty()" 22077 22078 return fill( 22079 """ 22080 $*{declCallable} 22081 $*{declThis} 22082 if (${callGuard}!JS::Call(cx, ${thisVal}, callable, 22083 ${args}, &rval)) { 22084 $*{noteError} 22085 } 22086 """, 22087 declCallable=self.getCallableDecl(), 22088 declThis=self.getThisDecl(), 22089 callGuard=self.getCallGuard(), 22090 thisVal=self.getThisVal(), 22091 args=args, 22092 noteError=self.getNoteCallFailed(), 22093 ) 22094 22095 22096 class CallCallback(CallbackMethod): 22097 def __init__(self, callback, descriptorProvider): 22098 self.callback = callback 22099 CallbackMethod.__init__( 22100 self, 22101 callback.signatures()[0], 22102 "Call", 22103 descriptorProvider, 22104 needThisHandling=True, 22105 canRunScript=not callback.isRunScriptBoundary(), 22106 ) 22107 22108 def getNoteCallFailed(self): 22109 if self.retvalType.isPromise(): 22110 return dedent( 22111 """ 22112 // Convert exception to a rejected promise. 22113 // See https://heycam.github.io/webidl/#call-a-user-objects-operation 22114 // step 12 and step 15.5. 22115 return CreateRejectedPromiseFromThrownException(cx, aRv); 22116 """ 22117 ) 22118 return CallbackMethod.getNoteCallFailed(self) 22119 22120 def getExceptionCode(self, forResult): 22121 # If the result value is a promise, and conversion 22122 # to the promise throws an exception we shouldn't 22123 # try to convert that exception to a promise again. 22124 if self.retvalType.isPromise() and not forResult: 22125 return dedent( 22126 """ 22127 // Convert exception to a rejected promise. 22128 // See https://heycam.github.io/webidl/#call-a-user-objects-operation 22129 // step 10 and step 15.5. 22130 return CreateRejectedPromiseFromThrownException(cx, aRv); 22131 """ 22132 ) 22133 return CallbackMethod.getExceptionCode(self, forResult) 22134 22135 def getThisDecl(self): 22136 return "" 22137 22138 def getThisVal(self): 22139 return "aThisVal" 22140 22141 def getCallableDecl(self): 22142 return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n" 22143 22144 def getPrettyName(self): 22145 return self.callback.identifier.name 22146 22147 def getCallGuard(self): 22148 if self.callback._treatNonObjectAsNull: 22149 return "JS::IsCallable(mCallback) && " 22150 return "" 22151 22152 22153 class CallbackOperationBase(CallbackMethod): 22154 """ 22155 Common class for implementing various callback operations. 22156 """ 22157 22158 def __init__( 22159 self, 22160 signature, 22161 jsName, 22162 nativeName, 22163 descriptor, 22164 singleOperation, 22165 rethrowContentException=False, 22166 spiderMonkeyInterfacesAreStructs=False, 22167 ): 22168 self.singleOperation = singleOperation 22169 self.methodName = descriptor.binaryNameFor(jsName, False) 22170 CallbackMethod.__init__( 22171 self, 22172 signature, 22173 nativeName, 22174 descriptor, 22175 singleOperation, 22176 rethrowContentException, 22177 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs, 22178 ) 22179 22180 def getThisDecl(self): 22181 if not self.singleOperation: 22182 return "JS::Rooted<JS::Value> thisValue(cx, JS::ObjectValue(*mCallback));\n" 22183 # This relies on getCallableDecl declaring a boolean 22184 # isCallable in the case when we're a single-operation 22185 # interface. 22186 return dedent( 22187 """ 22188 JS::Rooted<JS::Value> thisValue(cx, isCallable ? aThisVal.get() 22189 : JS::ObjectValue(*mCallback)); 22190 """ 22191 ) 22192 22193 def getThisVal(self): 22194 return "thisValue" 22195 22196 def getCallableDecl(self): 22197 getCallableFromProp = fill( 22198 """ 22199 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx); 22200 if ((reinterpret_cast<jsid*>(atomsCache)->isVoid() && 22201 !InitIds(cx, atomsCache)) || 22202 !GetCallableProperty(cx, atomsCache->${methodAtomName}, &callable)) { 22203 aRv.Throw(NS_ERROR_UNEXPECTED); 22204 return${errorReturn}; 22205 } 22206 """, 22207 methodAtomName=CGDictionary.makeIdName(self.methodName), 22208 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms", 22209 errorReturn=self.getDefaultRetval(), 22210 ) 22211 if not self.singleOperation: 22212 return "JS::Rooted<JS::Value> callable(cx);\n" + getCallableFromProp 22213 return fill( 22214 """ 22215 bool isCallable = JS::IsCallable(mCallback); 22216 JS::Rooted<JS::Value> callable(cx); 22217 if (isCallable) { 22218 callable = JS::ObjectValue(*mCallback); 22219 } else { 22220 $*{getCallableFromProp} 22221 } 22222 """, 22223 getCallableFromProp=getCallableFromProp, 22224 ) 22225 22226 def getCallGuard(self): 22227 return "" 22228 22229 22230 class CallbackOperation(CallbackOperationBase): 22231 """ 22232 Codegen actual WebIDL operations on callback interfaces. 22233 """ 22234 22235 def __init__(self, method, signature, descriptor, spiderMonkeyInterfacesAreStructs): 22236 self.ensureASCIIName(method) 22237 self.method = method 22238 jsName = method.identifier.name 22239 CallbackOperationBase.__init__( 22240 self, 22241 signature, 22242 jsName, 22243 MakeNativeName(descriptor.binaryNameFor(jsName, False)), 22244 descriptor, 22245 descriptor.interface.isSingleOperationInterface(), 22246 rethrowContentException=descriptor.interface.isJSImplemented(), 22247 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs, 22248 ) 22249 22250 def getPrettyName(self): 22251 return "%s.%s" % ( 22252 self.descriptorProvider.interface.identifier.name, 22253 self.method.identifier.name, 22254 ) 22255 22256 22257 class CallbackAccessor(CallbackMember): 22258 """ 22259 Shared superclass for CallbackGetter and CallbackSetter. 22260 """ 22261 22262 def __init__(self, attr, sig, name, descriptor, spiderMonkeyInterfacesAreStructs): 22263 self.ensureASCIIName(attr) 22264 self.attrName = attr.identifier.name 22265 CallbackMember.__init__( 22266 self, 22267 sig, 22268 name, 22269 descriptor, 22270 needThisHandling=False, 22271 rethrowContentException=descriptor.interface.isJSImplemented(), 22272 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs, 22273 ) 22274 22275 def getPrettyName(self): 22276 return "%s.%s" % ( 22277 self.descriptorProvider.interface.identifier.name, 22278 self.attrName, 22279 ) 22280 22281 22282 class CallbackGetter(CallbackAccessor): 22283 def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs): 22284 CallbackAccessor.__init__( 22285 self, 22286 attr, 22287 (attr.type, []), 22288 callbackGetterName(attr, descriptor), 22289 descriptor, 22290 spiderMonkeyInterfacesAreStructs, 22291 ) 22292 22293 def getRvalDecl(self): 22294 return "JS::Rooted<JS::Value> rval(cx);\n" 22295 22296 def getCall(self): 22297 return fill( 22298 """ 22299 JS::Rooted<JSObject *> callback(cx, mCallback); 22300 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx); 22301 if ((reinterpret_cast<jsid*>(atomsCache)->isVoid() 22302 && !InitIds(cx, atomsCache)) || 22303 !JS_GetPropertyById(cx, callback, atomsCache->${attrAtomName}, &rval)) { 22304 aRv.Throw(NS_ERROR_UNEXPECTED); 22305 return${errorReturn}; 22306 } 22307 """, 22308 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms", 22309 attrAtomName=CGDictionary.makeIdName( 22310 self.descriptorProvider.binaryNameFor(self.attrName, False) 22311 ), 22312 errorReturn=self.getDefaultRetval(), 22313 ) 22314 22315 22316 class CallbackSetter(CallbackAccessor): 22317 def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs): 22318 CallbackAccessor.__init__( 22319 self, 22320 attr, 22321 ( 22322 BuiltinTypes[IDLBuiltinType.Types.undefined], 22323 [FakeArgument(attr.type)], 22324 ), 22325 callbackSetterName(attr, descriptor), 22326 descriptor, 22327 spiderMonkeyInterfacesAreStructs, 22328 ) 22329 22330 def getRvalDecl(self): 22331 # We don't need an rval 22332 return "" 22333 22334 def getCall(self): 22335 return fill( 22336 """ 22337 MOZ_ASSERT(argv.length() == 1); 22338 JS::Rooted<JSObject*> callback(cx, CallbackKnownNotGray()); 22339 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx); 22340 if ((reinterpret_cast<jsid*>(atomsCache)->isVoid() && 22341 !InitIds(cx, atomsCache)) || 22342 !JS_SetPropertyById(cx, callback, atomsCache->${attrAtomName}, argv[0])) { 22343 aRv.Throw(NS_ERROR_UNEXPECTED); 22344 return${errorReturn}; 22345 } 22346 """, 22347 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms", 22348 attrAtomName=CGDictionary.makeIdName( 22349 self.descriptorProvider.binaryNameFor(self.attrName, False) 22350 ), 22351 errorReturn=self.getDefaultRetval(), 22352 ) 22353 22354 def getArgcDecl(self): 22355 return None 22356 22357 22358 class CGJSImplInitOperation(CallbackOperationBase): 22359 """ 22360 Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL. 22361 """ 22362 22363 def __init__(self, sig, descriptor): 22364 assert sig in descriptor.interface.ctor().signatures() 22365 CallbackOperationBase.__init__( 22366 self, 22367 (BuiltinTypes[IDLBuiltinType.Types.undefined], sig[1]), 22368 "__init", 22369 "__Init", 22370 descriptor, 22371 singleOperation=False, 22372 rethrowContentException=True, 22373 spiderMonkeyInterfacesAreStructs=True, 22374 ) 22375 22376 def getPrettyName(self): 22377 return "__init" 22378 22379 22380 class CGJSImplEventHookOperation(CallbackOperationBase): 22381 """ 22382 Codegen the hooks on a JS impl for adding/removing event listeners. 22383 """ 22384 22385 def __init__(self, descriptor, name): 22386 self.name = name 22387 22388 CallbackOperationBase.__init__( 22389 self, 22390 ( 22391 BuiltinTypes[IDLBuiltinType.Types.undefined], 22392 [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.domstring], "aType")], 22393 ), 22394 name, 22395 MakeNativeName(name), 22396 descriptor, 22397 singleOperation=False, 22398 rethrowContentException=False, 22399 spiderMonkeyInterfacesAreStructs=True, 22400 ) 22401 22402 def getPrettyName(self): 22403 return self.name 22404 22405 22406 def getMaplikeOrSetlikeErrorReturn(helperImpl): 22407 """ 22408 Generate return values based on whether a maplike or setlike generated 22409 method is an interface method (which returns bool) or a helper function 22410 (which uses ErrorResult). 22411 """ 22412 if helperImpl: 22413 return dedent( 22414 """ 22415 aRv.Throw(NS_ERROR_UNEXPECTED); 22416 return%s; 22417 """ 22418 % helperImpl.getDefaultRetval() 22419 ) 22420 return "return false;\n" 22421 22422 22423 def getMaplikeOrSetlikeBackingObject(descriptor, maplikeOrSetlike, helperImpl=None): 22424 """ 22425 Generate code to get/create a JS backing object for a maplike/setlike 22426 declaration from the declaration slot. 22427 """ 22428 func_prefix = maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title() 22429 ret = fill( 22430 """ 22431 JS::Rooted<JSObject*> backingObj(cx); 22432 bool created = false; 22433 if (!Get${func_prefix}BackingObject(cx, obj, ${slot}, &backingObj, &created)) { 22434 $*{errorReturn} 22435 } 22436 if (created) { 22437 PreserveWrapper<${selfType}>(self); 22438 } 22439 """, 22440 slot=memberReservedSlot(maplikeOrSetlike, descriptor), 22441 func_prefix=func_prefix, 22442 errorReturn=getMaplikeOrSetlikeErrorReturn(helperImpl), 22443 selfType=descriptor.nativeType, 22444 ) 22445 return ret 22446 22447 22448 def getMaplikeOrSetlikeSizeGetterBody(descriptor, attr): 22449 """ 22450 Creates the body for the size getter method of maplike/setlike interfaces. 22451 """ 22452 # We should only have one declaration attribute currently 22453 assert attr.identifier.name == "size" 22454 assert attr.isMaplikeOrSetlikeAttr() 22455 return fill( 22456 """ 22457 $*{getBackingObj} 22458 uint32_t result = JS::${funcPrefix}Size(cx, backingObj); 22459 MOZ_ASSERT(!JS_IsExceptionPending(cx)); 22460 args.rval().setNumber(result); 22461 return true; 22462 """, 22463 getBackingObj=getMaplikeOrSetlikeBackingObject( 22464 descriptor, attr.maplikeOrSetlike 22465 ), 22466 funcPrefix=attr.maplikeOrSetlike.prefix, 22467 ) 22468 22469 22470 class CGMaplikeOrSetlikeMethodGenerator(CGThing): 22471 """ 22472 Creates methods for maplike/setlike interfaces. It is expected that all 22473 methods will be have a maplike/setlike object attached. Unwrapping/wrapping 22474 will be taken care of by the usual method generation machinery in 22475 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of 22476 using CGCallGenerator. 22477 """ 22478 22479 def __init__( 22480 self, 22481 descriptor, 22482 maplikeOrSetlike, 22483 methodName, 22484 needsValueTypeReturn=False, 22485 helperImpl=None, 22486 ): 22487 CGThing.__init__(self) 22488 # True if this will be the body of a C++ helper function. 22489 self.helperImpl = helperImpl 22490 self.descriptor = descriptor 22491 self.maplikeOrSetlike = maplikeOrSetlike 22492 self.cgRoot = CGList([]) 22493 impl_method_name = methodName 22494 if impl_method_name[0] == "_": 22495 # double underscore means this is a js-implemented chrome only rw 22496 # function. Truncate the double underscore so calling the right 22497 # underlying JSAPI function still works. 22498 impl_method_name = impl_method_name[2:] 22499 self.cgRoot.append( 22500 CGGeneric( 22501 getMaplikeOrSetlikeBackingObject( 22502 self.descriptor, self.maplikeOrSetlike, self.helperImpl 22503 ) 22504 ) 22505 ) 22506 self.returnStmt = getMaplikeOrSetlikeErrorReturn(self.helperImpl) 22507 22508 # Generates required code for the method. Method descriptions included 22509 # in definitions below. Throw if we don't have a method to fill in what 22510 # we're looking for. 22511 try: 22512 methodGenerator = getattr(self, impl_method_name) 22513 except AttributeError: 22514 raise TypeError( 22515 "Missing %s method definition '%s'" 22516 % (self.maplikeOrSetlike.maplikeOrSetlikeType, methodName) 22517 ) 22518 # Method generator returns tuple, containing: 22519 # 22520 # - a list of CGThings representing setup code for preparing to call 22521 # the JS API function 22522 # - a list of arguments needed for the JS API function we're calling 22523 # - list of code CGThings needed for return value conversion. 22524 (setupCode, arguments, setResult) = methodGenerator() 22525 22526 # Create the actual method call, and then wrap it with the code to 22527 # return the value if needed. 22528 funcName = self.maplikeOrSetlike.prefix + MakeNativeName(impl_method_name) 22529 # Append the list of setup code CGThings 22530 self.cgRoot.append(CGList(setupCode)) 22531 # Create the JS API call 22532 code = dedent( 22533 """ 22534 if (!JS::${funcName}(${args})) { 22535 $*{errorReturn} 22536 } 22537 """ 22538 ) 22539 22540 if needsValueTypeReturn: 22541 assert self.helperImpl and impl_method_name == "get" 22542 code += fill( 22543 """ 22544 if (result.isUndefined()) { 22545 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 22546 return${retval}; 22547 } 22548 """, 22549 retval=self.helperImpl.getDefaultRetval(), 22550 ) 22551 22552 self.cgRoot.append( 22553 CGWrapper( 22554 CGGeneric( 22555 fill( 22556 code, 22557 funcName=funcName, 22558 args=", ".join(["cx", "backingObj"] + arguments), 22559 errorReturn=self.returnStmt, 22560 ) 22561 ) 22562 ) 22563 ) 22564 # Append result conversion 22565 self.cgRoot.append(CGList(setResult)) 22566 22567 def mergeTuples(self, a, b): 22568 """ 22569 Expecting to take 2 tuples were all elements are lists, append the lists in 22570 the second tuple to the lists in the first. 22571 """ 22572 return tuple([x + y for x, y in zip(a, b)]) 22573 22574 def appendArgConversion(self, name): 22575 """ 22576 Generate code to convert arguments to JS::Values, so they can be 22577 passed into JSAPI functions. 22578 """ 22579 return CGGeneric( 22580 fill( 22581 """ 22582 JS::Rooted<JS::Value> ${name}Val(cx); 22583 if (!ToJSValue(cx, ${name}, &${name}Val)) { 22584 $*{errorReturn} 22585 } 22586 """, 22587 name=name, 22588 errorReturn=self.returnStmt, 22589 ) 22590 ) 22591 22592 def appendKeyArgConversion(self): 22593 """ 22594 Generates the key argument for methods. Helper functions will use 22595 a RootedVector<JS::Value>, while interface methods have separate JS::Values. 22596 """ 22597 if self.helperImpl: 22598 return ([], ["argv[0]"], []) 22599 return ([self.appendArgConversion("arg0")], ["arg0Val"], []) 22600 22601 def appendKeyAndValueArgConversion(self): 22602 """ 22603 Generates arguments for methods that require a key and value. Helper 22604 functions will use a RootedVector<JS::Value>, while interface methods have 22605 separate JS::Values. 22606 """ 22607 r = self.appendKeyArgConversion() 22608 if self.helperImpl: 22609 return self.mergeTuples(r, ([], ["argv[1]"], [])) 22610 return self.mergeTuples( 22611 r, ([self.appendArgConversion("arg1")], ["arg1Val"], []) 22612 ) 22613 22614 def appendIteratorResult(self): 22615 """ 22616 Generate code to output JSObject* return values, needed for functions that 22617 return iterators. Iterators cannot currently be wrapped via Xrays. If 22618 something that would return an iterator is called via Xray, fail early. 22619 """ 22620 # TODO: Bug 1173651 - Remove check once bug 1023984 is fixed. 22621 code = CGGeneric( 22622 dedent( 22623 """ 22624 // TODO (Bug 1173651): Xrays currently cannot wrap iterators. Change 22625 // after bug 1023984 is fixed. 22626 if (xpc::WrapperFactory::IsXrayWrapper(obj)) { 22627 JS_ReportErrorASCII(cx, "Xray wrapping of iterators not supported."); 22628 return false; 22629 } 22630 JS::Rooted<JSObject*> result(cx); 22631 JS::Rooted<JS::Value> v(cx); 22632 """ 22633 ) 22634 ) 22635 arguments = "&v" 22636 setResult = CGGeneric( 22637 dedent( 22638 """ 22639 result = &v.toObject(); 22640 """ 22641 ) 22642 ) 22643 return ([code], [arguments], [setResult]) 22644 22645 def appendSelfResult(self): 22646 """ 22647 Generate code to return the interface object itself. 22648 """ 22649 code = CGGeneric( 22650 dedent( 22651 """ 22652 JS::Rooted<JSObject*> result(cx); 22653 """ 22654 ) 22655 ) 22656 setResult = CGGeneric( 22657 dedent( 22658 """ 22659 result = obj; 22660 """ 22661 ) 22662 ) 22663 return ([code], [], [setResult]) 22664 22665 def appendBoolResult(self): 22666 if self.helperImpl: 22667 return ([CGGeneric("bool retVal;\n")], ["&retVal"], []) 22668 return ([CGGeneric("bool result;\n")], ["&result"], []) 22669 22670 def forEach(self): 22671 """ 22672 void forEach(callback c, any thisval); 22673 22674 ForEach takes a callback, and a possible value to use as 'this'. The 22675 callback needs to take value, key, and the interface object 22676 implementing maplike/setlike. In order to make sure that the third arg 22677 is our interface object instead of the map/set backing object, we 22678 create a js function with the callback and original object in its 22679 storage slots, then use a helper function in BindingUtils to make sure 22680 the callback is called correctly. 22681 """ 22682 assert not self.helperImpl 22683 code = [ 22684 CGGeneric( 22685 dedent( 22686 """ 22687 // Create a wrapper function. 22688 JSFunction* func = js::NewFunctionWithReserved(cx, ForEachHandler, 3, 0, nullptr); 22689 if (!func) { 22690 return false; 22691 } 22692 JS::Rooted<JSObject*> funcObj(cx, JS_GetFunctionObject(func)); 22693 JS::Rooted<JS::Value> funcVal(cx, JS::ObjectValue(*funcObj)); 22694 js::SetFunctionNativeReserved(funcObj, FOREACH_CALLBACK_SLOT, 22695 JS::ObjectValue(*arg0)); 22696 js::SetFunctionNativeReserved(funcObj, FOREACH_MAPLIKEORSETLIKEOBJ_SLOT, 22697 JS::ObjectValue(*obj)); 22698 """ 22699 ) 22700 ) 22701 ] 22702 arguments = ["funcVal", "arg1"] 22703 return (code, arguments, []) 22704 22705 def set(self): 22706 """ 22707 object set(key, value); 22708 22709 Maplike only function, takes key and sets value to it, returns 22710 interface object unless being called from a C++ helper. 22711 """ 22712 assert self.maplikeOrSetlike.isMaplike() 22713 r = self.appendKeyAndValueArgConversion() 22714 if self.helperImpl: 22715 return r 22716 return self.mergeTuples(r, self.appendSelfResult()) 22717 22718 def add(self): 22719 """ 22720 object add(value); 22721 22722 Setlike only function, adds value to set, returns interface object 22723 unless being called from a C++ helper 22724 """ 22725 assert self.maplikeOrSetlike.isSetlike() 22726 r = self.appendKeyArgConversion() 22727 if self.helperImpl: 22728 return r 22729 return self.mergeTuples(r, self.appendSelfResult()) 22730 22731 def get(self): 22732 """ 22733 type? get(key); 22734 22735 Retrieves a value from a backing object based on the key. Returns value 22736 if key is in backing object, undefined otherwise. 22737 """ 22738 assert self.maplikeOrSetlike.isMaplike() 22739 r = self.appendKeyArgConversion() 22740 22741 code = [] 22742 # We don't need to create the result variable because it'll be created elsewhere 22743 # for JSObject Get method 22744 if not self.helperImpl or not self.helperImpl.needsScopeBody(): 22745 code = [ 22746 CGGeneric( 22747 dedent( 22748 """ 22749 JS::Rooted<JS::Value> result(cx); 22750 """ 22751 ) 22752 ) 22753 ] 22754 22755 arguments = ["&result"] 22756 return self.mergeTuples(r, (code, arguments, [])) 22757 22758 def has(self): 22759 """ 22760 bool has(key); 22761 22762 Check if an entry exists in the backing object. Returns true if value 22763 exists in backing object, false otherwise. 22764 """ 22765 return self.mergeTuples(self.appendKeyArgConversion(), self.appendBoolResult()) 22766 22767 def keys(self): 22768 """ 22769 object keys(); 22770 22771 Returns new object iterator with all keys from backing object. 22772 """ 22773 return self.appendIteratorResult() 22774 22775 def values(self): 22776 """ 22777 object values(); 22778 22779 Returns new object iterator with all values from backing object. 22780 """ 22781 return self.appendIteratorResult() 22782 22783 def entries(self): 22784 """ 22785 object entries(); 22786 22787 Returns new object iterator with all keys and values from backing 22788 object. Keys will be null for set. 22789 """ 22790 return self.appendIteratorResult() 22791 22792 def clear(self): 22793 """ 22794 void clear(); 22795 22796 Removes all entries from map/set. 22797 """ 22798 return ([], [], []) 22799 22800 def delete(self): 22801 """ 22802 bool delete(key); 22803 22804 Deletes an entry from the backing object. Returns true if value existed 22805 in backing object, false otherwise. 22806 """ 22807 return self.mergeTuples(self.appendKeyArgConversion(), self.appendBoolResult()) 22808 22809 def define(self): 22810 return self.cgRoot.define() 22811 22812 22813 class CGHelperFunctionGenerator(CallbackMember): 22814 """ 22815 Generates code to allow C++ to perform operations. Gets a context from the 22816 binding wrapper, turns arguments into JS::Values (via 22817 CallbackMember/CGNativeMember argument conversion), then uses 22818 getCall to generate the body for getting result, and maybe convert the 22819 result into return type (via CallbackMember/CGNativeMember result 22820 conversion) 22821 """ 22822 22823 class HelperFunction(CGAbstractMethod): 22824 """ 22825 Generates context retrieval code and rooted JSObject for interface for 22826 method generator to use 22827 """ 22828 22829 def __init__(self, descriptor, name, args, code, returnType): 22830 self.code = code 22831 CGAbstractMethod.__init__(self, descriptor, name, returnType, args) 22832 22833 def definition_body(self): 22834 return self.code 22835 22836 def __init__( 22837 self, 22838 descriptor, 22839 name, 22840 args, 22841 returnType=BuiltinTypes[IDLBuiltinType.Types.undefined], 22842 needsResultConversion=True, 22843 ): 22844 assert returnType.isType() 22845 self.needsResultConversion = needsResultConversion 22846 22847 # Run CallbackMember init function to generate argument conversion code. 22848 # wrapScope is set to 'obj' when generating maplike or setlike helper 22849 # functions, as we don't have access to the CallbackPreserveColor 22850 # method. 22851 CallbackMember.__init__( 22852 self, 22853 [returnType, args], 22854 name, 22855 descriptor, 22856 False, 22857 wrapScope="obj", 22858 passJSBitsAsNeeded=typeNeedsCx(returnType), 22859 ) 22860 22861 # Wrap CallbackMember body code into a CGAbstractMethod to make 22862 # generation easier. 22863 self.implMethod = CGHelperFunctionGenerator.HelperFunction( 22864 descriptor, name, self.args, self.body, self.returnType 22865 ) 22866 22867 def getCallSetup(self): 22868 # If passJSBitsAsNeeded is true, it means the caller will provide a 22869 # JSContext, so we don't need to create JSContext and enter 22870 # UnprivilegedJunkScopeOrWorkerGlobal here. 22871 code = "MOZ_ASSERT(self);\n" 22872 if not self.passJSBitsAsNeeded: 22873 code += dedent( 22874 """ 22875 AutoJSAPI jsapi; 22876 jsapi.Init(); 22877 JSContext* cx = jsapi.cx(); 22878 // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here because 22879 // all we want is to wrap into _some_ scope and then unwrap to find 22880 // the reflector, and wrapping has no side-effects. 22881 JSObject* scope = UnprivilegedJunkScopeOrWorkerGlobal(fallible); 22882 if (!scope) { 22883 aRv.Throw(NS_ERROR_UNEXPECTED); 22884 return%s; 22885 } 22886 JSAutoRealm tempRealm(cx, scope); 22887 """ 22888 % self.getDefaultRetval() 22889 ) 22890 22891 code += dedent( 22892 """ 22893 JS::Rooted<JS::Value> v(cx); 22894 if(!ToJSValue(cx, self, &v)) { 22895 aRv.Throw(NS_ERROR_UNEXPECTED); 22896 return%s; 22897 } 22898 // This is a reflector, but due to trying to name things 22899 // similarly across method generators, it's called obj here. 22900 JS::Rooted<JSObject*> obj(cx); 22901 obj = js::UncheckedUnwrap(&v.toObject(), /* stopAtWindowProxy = */ false); 22902 """ 22903 % self.getDefaultRetval() 22904 ) 22905 22906 # We'd like wrap the inner code in a scope such that the code can use the 22907 # same realm. So here we are creating the result variable outside of the 22908 # scope. 22909 if self.needsScopeBody(): 22910 code += "JS::Rooted<JS::Value> result(cx);\n" 22911 22912 return code 22913 22914 def getArgs(self, returnType, argList): 22915 # We don't need the context or the value. We'll generate those instead. 22916 args = CGNativeMember.getArgs(self, returnType, argList) 22917 # Prepend a pointer to the binding object onto the arguments 22918 return [Argument(self.descriptorProvider.nativeType + "*", "self")] + args 22919 22920 def needsScopeBody(self): 22921 return self.passJSBitsAsNeeded 22922 22923 def getArgvDeclFailureCode(self): 22924 return "aRv.Throw(NS_ERROR_UNEXPECTED);\n" 22925 22926 def getResultConversion(self): 22927 if self.needsResultConversion: 22928 code = "" 22929 if self.needsScopeBody(): 22930 code = dedent( 22931 """ 22932 if (!JS_WrapValue(cx, &result)) { 22933 aRv.NoteJSContextException(cx); 22934 return; 22935 } 22936 """ 22937 ) 22938 22939 failureCode = dedent("aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n") 22940 22941 exceptionCode = None 22942 if self.retvalType.isPrimitive(): 22943 exceptionCode = dedent( 22944 "aRv.NoteJSContextException(cx);\nreturn%s;\n" 22945 % self.getDefaultRetval() 22946 ) 22947 22948 return code + CallbackMember.getResultConversion( 22949 self, 22950 "result", 22951 failureCode=failureCode, 22952 isDefinitelyObject=True, 22953 exceptionCode=exceptionCode, 22954 ) 22955 22956 assignRetval = string.Template( 22957 self.getRetvalInfo(self.retvalType, False)[2] 22958 ).substitute( 22959 { 22960 "declName": "retVal", 22961 } 22962 ) 22963 return assignRetval 22964 22965 def getRvalDecl(self): 22966 # hack to make sure we put JSAutoRealm inside the body scope 22967 return "JSAutoRealm reflectorRealm(cx, obj);\n" 22968 22969 def getArgcDecl(self): 22970 # Don't need argc for anything. 22971 return None 22972 22973 def getCall(self): 22974 assert False # Override me! 22975 22976 def getPrettyName(self): 22977 return self.name 22978 22979 def declare(self): 22980 return self.implMethod.declare() 22981 22982 def define(self): 22983 return self.implMethod.define() 22984 22985 22986 class CGMaplikeOrSetlikeHelperFunctionGenerator(CGHelperFunctionGenerator): 22987 """ 22988 Generates code to allow C++ to perform operations on backing objects. Gets 22989 a context from the binding wrapper, turns arguments into JS::Values (via 22990 CallbackMember/CGNativeMember argument conversion), then uses 22991 CGMaplikeOrSetlikeMethodGenerator to generate the body. 22992 """ 22993 22994 def __init__( 22995 self, 22996 descriptor, 22997 maplikeOrSetlike, 22998 name, 22999 needsKeyArg=False, 23000 needsValueArg=False, 23001 needsValueTypeReturn=False, 23002 needsBoolReturn=False, 23003 needsResultConversion=True, 23004 ): 23005 self.maplikeOrSetlike = maplikeOrSetlike 23006 self.needsValueTypeReturn = needsValueTypeReturn 23007 23008 args = [] 23009 if needsKeyArg: 23010 args.append(FakeArgument(maplikeOrSetlike.keyType, "aKey")) 23011 if needsValueArg: 23012 assert needsKeyArg 23013 assert not needsValueTypeReturn 23014 args.append(FakeArgument(maplikeOrSetlike.valueType, "aValue")) 23015 23016 returnType = BuiltinTypes[IDLBuiltinType.Types.undefined] 23017 if needsBoolReturn: 23018 returnType = BuiltinTypes[IDLBuiltinType.Types.boolean] 23019 elif needsValueTypeReturn: 23020 returnType = maplikeOrSetlike.valueType 23021 23022 CGHelperFunctionGenerator.__init__( 23023 self, 23024 descriptor, 23025 name, 23026 args, 23027 returnType, 23028 needsResultConversion, 23029 ) 23030 23031 def getCall(self): 23032 return CGMaplikeOrSetlikeMethodGenerator( 23033 self.descriptorProvider, 23034 self.maplikeOrSetlike, 23035 self.name.lower(), 23036 self.needsValueTypeReturn, 23037 helperImpl=self, 23038 ).define() 23039 23040 23041 class CGMaplikeOrSetlikeHelperGenerator(CGNamespace): 23042 """ 23043 Declares and defines convenience methods for accessing backing objects on 23044 setlike/maplike interface. Generates function signatures, un/packs 23045 backing objects from slot, etc. 23046 """ 23047 23048 def __init__(self, descriptor, maplikeOrSetlike): 23049 self.descriptor = descriptor 23050 # Since iterables are folded in with maplike/setlike, make sure we've 23051 # got the right type here. 23052 assert maplikeOrSetlike.isMaplike() or maplikeOrSetlike.isSetlike() 23053 self.maplikeOrSetlike = maplikeOrSetlike 23054 self.namespace = "%sHelpers" % ( 23055 self.maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title() 23056 ) 23057 self.helpers = [ 23058 CGMaplikeOrSetlikeHelperFunctionGenerator( 23059 descriptor, maplikeOrSetlike, "Clear" 23060 ), 23061 CGMaplikeOrSetlikeHelperFunctionGenerator( 23062 descriptor, 23063 maplikeOrSetlike, 23064 "Delete", 23065 needsKeyArg=True, 23066 needsBoolReturn=True, 23067 needsResultConversion=False, 23068 ), 23069 CGMaplikeOrSetlikeHelperFunctionGenerator( 23070 descriptor, 23071 maplikeOrSetlike, 23072 "Has", 23073 needsKeyArg=True, 23074 needsBoolReturn=True, 23075 needsResultConversion=False, 23076 ), 23077 ] 23078 if self.maplikeOrSetlike.isMaplike(): 23079 self.helpers.append( 23080 CGMaplikeOrSetlikeHelperFunctionGenerator( 23081 descriptor, 23082 maplikeOrSetlike, 23083 "Set", 23084 needsKeyArg=True, 23085 needsValueArg=True, 23086 ) 23087 ) 23088 self.helpers.append( 23089 CGMaplikeOrSetlikeHelperFunctionGenerator( 23090 descriptor, 23091 maplikeOrSetlike, 23092 "Get", 23093 needsKeyArg=True, 23094 needsValueTypeReturn=True, 23095 ) 23096 ) 23097 else: 23098 assert self.maplikeOrSetlike.isSetlike() 23099 self.helpers.append( 23100 CGMaplikeOrSetlikeHelperFunctionGenerator( 23101 descriptor, maplikeOrSetlike, "Add", needsKeyArg=True 23102 ) 23103 ) 23104 CGNamespace.__init__(self, self.namespace, CGList(self.helpers)) 23105 23106 23107 class CGIterableMethodGenerator(CGGeneric): 23108 """ 23109 Creates methods for iterable interfaces. Unwrapping/wrapping 23110 will be taken care of by the usual method generation machinery in 23111 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of 23112 using CGCallGenerator. 23113 """ 23114 23115 def __init__(self, descriptor, methodName, args): 23116 if methodName == "forEach": 23117 assert len(args) == 2 23118 23119 CGGeneric.__init__( 23120 self, 23121 fill( 23122 """ 23123 if (!JS::IsCallable(arg0)) { 23124 cx.ThrowErrorMessage<MSG_NOT_CALLABLE>("Argument 1"); 23125 return false; 23126 } 23127 JS::RootedValueArray<3> callArgs(cx); 23128 callArgs[2].setObject(*obj); 23129 JS::Rooted<JS::Value> ignoredReturnVal(cx); 23130 auto GetKeyAtIndex = &${selfType}::GetKeyAtIndex; 23131 auto GetValueAtIndex = &${selfType}::GetValueAtIndex; 23132 for (size_t i = 0; i < self->GetIterableLength(); ++i) { 23133 if (!CallIterableGetter(cx, GetValueAtIndex, self, i, 23134 callArgs[0])) { 23135 return false; 23136 } 23137 if (!CallIterableGetter(cx, GetKeyAtIndex, self, i, 23138 callArgs[1])) { 23139 return false; 23140 } 23141 if (!JS::Call(cx, arg1, arg0, JS::HandleValueArray(callArgs), 23142 &ignoredReturnVal)) { 23143 return false; 23144 } 23145 } 23146 """, 23147 ifaceName=descriptor.interface.identifier.name, 23148 selfType=descriptor.nativeType, 23149 ), 23150 ) 23151 return 23152 23153 if descriptor.interface.isIterable(): 23154 assert descriptor.interface.maplikeOrSetlikeOrIterable.isPairIterator() 23155 assert len(args) == 0 23156 23157 wrap = f"{descriptor.interface.identifier.name}Iterator_Binding::Wrap" 23158 iterClass = f"mozilla::dom::binding_detail::WrappableIterableIterator<{descriptor.nativeType}, &{wrap}>" 23159 else: 23160 needReturnMethod = toStringBool( 23161 descriptor.interface.maplikeOrSetlikeOrIterable.getExtendedAttribute( 23162 "GenerateReturnMethod" 23163 ) 23164 is not None 23165 ) 23166 wrap = f"{descriptor.interface.identifier.name}AsyncIterator_Binding::Wrap" 23167 iterClass = f"mozilla::dom::binding_detail::WrappableAsyncIterableIterator<{descriptor.nativeType}, {needReturnMethod}, &{wrap}>" 23168 23169 createIterator = fill( 23170 """ 23171 typedef ${iterClass} itrType; 23172 RefPtr<itrType> result(new itrType(self, 23173 itrType::IteratorType::${itrMethod})); 23174 """, 23175 iterClass=iterClass, 23176 itrMethod=methodName.title(), 23177 ) 23178 23179 if descriptor.interface.isAsyncIterable(): 23180 args.append("initError") 23181 createIterator = fill( 23182 """ 23183 $*{createIterator} 23184 { 23185 ErrorResult initError; 23186 self->InitAsyncIteratorData(result->Data(), itrType::IteratorType::${itrMethod}, ${args}); 23187 if (initError.MaybeSetPendingException(cx, "Asynchronous iterator initialization steps for ${ifaceName} failed")) { 23188 return false; 23189 } 23190 } 23191 """, 23192 createIterator=createIterator, 23193 itrMethod=methodName.title(), 23194 args=", ".join(args), 23195 ifaceName=descriptor.interface.identifier.name, 23196 ) 23197 23198 CGGeneric.__init__(self, createIterator) 23199 23200 23201 def getObservableArrayBackingObject(descriptor, attr, errorReturn="return false;\n"): 23202 """ 23203 Generate code to get/create a JS backing list for an observableArray attribute 23204 from the declaration slot. 23205 """ 23206 assert attr.isAttr() 23207 assert attr.type.isObservableArray() 23208 23209 # GetObservableArrayBackingObject may return a wrapped object for Xrays, so 23210 # when we create it we need to unwrap it to store the interface in the 23211 # reserved slot. 23212 return fill( 23213 """ 23214 JS::Rooted<JSObject*> backingObj(cx); 23215 bool created = false; 23216 if (!GetObservableArrayBackingObject(cx, obj, ${slot}, 23217 &backingObj, &created, ${namespace}::ObservableArrayProxyHandler::getInstance(), 23218 self)) { 23219 $*{errorReturn} 23220 } 23221 if (created) { 23222 PreserveWrapper(self); 23223 } 23224 """, 23225 namespace=toBindingNamespace(MakeNativeName(attr.identifier.name)), 23226 slot=memberReservedSlot(attr, descriptor), 23227 errorReturn=errorReturn, 23228 selfType=descriptor.nativeType, 23229 ) 23230 23231 23232 def getObservableArrayGetterBody(descriptor, attr): 23233 """ 23234 Creates the body for the getter method of an observableArray attribute. 23235 """ 23236 assert attr.type.isObservableArray() 23237 return fill( 23238 """ 23239 $*{getBackingObj} 23240 MOZ_ASSERT(!JS_IsExceptionPending(cx)); 23241 args.rval().setObject(*backingObj); 23242 return true; 23243 """, 23244 getBackingObj=getObservableArrayBackingObject(descriptor, attr), 23245 ) 23246 23247 23248 class CGObservableArrayProxyHandler_callback(ClassMethod): 23249 """ 23250 Base class for declaring and defining the hook methods for ObservableArrayProxyHandler 23251 subclasses to get the interface native object from backing object and calls 23252 its On{Set|Delete}* callback. 23253 23254 * 'callbackType': "Set" or "Delete". 23255 * 'invalidTypeFatal' (optional): If True, we don't expect the type 23256 conversion would fail, so generate the 23257 assertion code if type conversion fails. 23258 """ 23259 23260 def __init__( 23261 self, descriptor, attr, name, args, callbackType, invalidTypeFatal=False 23262 ): 23263 assert attr.isAttr() 23264 assert attr.type.isObservableArray() 23265 assert callbackType in ["Set", "Delete"] 23266 self.descriptor = descriptor 23267 self.attr = attr 23268 self.innertype = attr.type.inner 23269 self.callbackType = callbackType 23270 self.invalidTypeFatal = invalidTypeFatal 23271 ClassMethod.__init__( 23272 self, 23273 name, 23274 "bool", 23275 args, 23276 visibility="protected", 23277 virtual=True, 23278 override=True, 23279 const=True, 23280 ) 23281 23282 def preConversion(self): 23283 """ 23284 The code to run before the conversion steps. 23285 """ 23286 return "" 23287 23288 def preCallback(self): 23289 """ 23290 The code to run before calling the callback. 23291 """ 23292 return "" 23293 23294 def postCallback(self): 23295 """ 23296 The code to run after calling the callback, all subclasses should override 23297 this to generate the return values. 23298 """ 23299 assert False # Override me! 23300 23301 def getBody(self): 23302 exceptionCode = ( 23303 fill( 23304 """ 23305 MOZ_ASSERT_UNREACHABLE("The item in ObservableArray backing list is not ${innertype}?"); 23306 return false; 23307 """, 23308 innertype=self.innertype, 23309 ) 23310 if self.invalidTypeFatal 23311 else None 23312 ) 23313 convertType = instantiateJSToNativeConversion( 23314 getJSToNativeConversionInfo( 23315 self.innertype, 23316 self.descriptor, 23317 sourceDescription="Element in ObservableArray backing list", 23318 exceptionCode=exceptionCode, 23319 ), 23320 { 23321 "declName": "decl", 23322 "holderName": "holder", 23323 "val": "aValue", 23324 }, 23325 ) 23326 callbackArgs = ["decl", "aIndex", "rv"] 23327 if typeNeedsCx(self.innertype): 23328 callbackArgs.insert(0, "cx") 23329 return fill( 23330 """ 23331 MOZ_ASSERT(IsObservableArrayProxy(aProxy)); 23332 $*{preConversion} 23333 23334 BindingCallContext cx(aCx, "ObservableArray ${name}"); 23335 $*{convertType} 23336 23337 $*{preCallback} 23338 JS::Value val = js::GetProxyReservedSlot(aProxy, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT); 23339 auto* interface = static_cast<${ifaceType}*>(val.toPrivate()); 23340 MOZ_ASSERT(interface); 23341 23342 ErrorResult rv; 23343 MOZ_KnownLive(interface)->${methodName}(${callbackArgs}); 23344 $*{postCallback} 23345 """, 23346 preConversion=self.preConversion(), 23347 name=self.name, 23348 convertType=convertType.define(), 23349 preCallback=self.preCallback(), 23350 ifaceType=self.descriptor.nativeType, 23351 methodName="On%s%s" 23352 % (self.callbackType, MakeNativeName(self.attr.identifier.name)), 23353 callbackArgs=", ".join(callbackArgs), 23354 postCallback=self.postCallback(), 23355 ) 23356 23357 23358 class CGObservableArrayProxyHandler_OnDeleteItem( 23359 CGObservableArrayProxyHandler_callback 23360 ): 23361 """ 23362 Declares and defines the hook methods for ObservableArrayProxyHandler 23363 subclasses to get the interface native object from backing object and calls 23364 its OnDelete* callback. 23365 """ 23366 23367 def __init__(self, descriptor, attr): 23368 args = [ 23369 Argument("JSContext*", "aCx"), 23370 Argument("JS::Handle<JSObject*>", "aProxy"), 23371 Argument("JS::Handle<JS::Value>", "aValue"), 23372 Argument("uint32_t", "aIndex"), 23373 ] 23374 CGObservableArrayProxyHandler_callback.__init__( 23375 self, 23376 descriptor, 23377 attr, 23378 "OnDeleteItem", 23379 args, 23380 "Delete", 23381 True, 23382 ) 23383 23384 def postCallback(self): 23385 return dedent( 23386 """ 23387 return !rv.MaybeSetPendingException(cx); 23388 """ 23389 ) 23390 23391 23392 class CGObservableArrayProxyHandler_SetIndexedValue( 23393 CGObservableArrayProxyHandler_callback 23394 ): 23395 """ 23396 Declares and defines the hook methods for ObservableArrayProxyHandler 23397 subclasses to run the setting the indexed value steps. 23398 """ 23399 23400 def __init__(self, descriptor, attr): 23401 args = [ 23402 Argument("JSContext*", "aCx"), 23403 Argument("JS::Handle<JSObject*>", "aProxy"), 23404 Argument("JS::Handle<JSObject*>", "aBackingList"), 23405 Argument("uint32_t", "aIndex"), 23406 Argument("JS::Handle<JS::Value>", "aValue"), 23407 Argument("JS::ObjectOpResult&", "aResult"), 23408 ] 23409 CGObservableArrayProxyHandler_callback.__init__( 23410 self, 23411 descriptor, 23412 attr, 23413 "SetIndexedValue", 23414 args, 23415 "Set", 23416 ) 23417 23418 def preConversion(self): 23419 return dedent( 23420 """ 23421 uint32_t oldLen; 23422 if (!JS::GetArrayLength(aCx, aBackingList, &oldLen)) { 23423 return false; 23424 } 23425 23426 if (aIndex > oldLen) { 23427 return aResult.failBadIndex(); 23428 } 23429 """ 23430 ) 23431 23432 def preCallback(self): 23433 return dedent( 23434 """ 23435 if (aIndex < oldLen) { 23436 JS::Rooted<JS::Value> value(aCx); 23437 if (!JS_GetElement(aCx, aBackingList, aIndex, &value)) { 23438 return false; 23439 } 23440 23441 if (!OnDeleteItem(aCx, aProxy, value, aIndex)) { 23442 return false; 23443 } 23444 } 23445 23446 """ 23447 ) 23448 23449 def postCallback(self): 23450 return dedent( 23451 """ 23452 if (rv.MaybeSetPendingException(cx)) { 23453 return false; 23454 } 23455 23456 if (!JS_SetElement(aCx, aBackingList, aIndex, aValue)) { 23457 return false; 23458 } 23459 23460 return aResult.succeed(); 23461 """ 23462 ) 23463 23464 23465 class CGObservableArrayProxyHandler(CGThing): 23466 """ 23467 A class for declaring a ObservableArrayProxyHandler. 23468 """ 23469 23470 def __init__(self, descriptor, attr): 23471 assert attr.isAttr() 23472 assert attr.type.isObservableArray() 23473 methods = [ 23474 # The item stored in backing object should always be converted successfully. 23475 CGObservableArrayProxyHandler_OnDeleteItem(descriptor, attr), 23476 CGObservableArrayProxyHandler_SetIndexedValue(descriptor, attr), 23477 CGJSProxyHandler_getInstance("ObservableArrayProxyHandler"), 23478 ] 23479 parentClass = "mozilla::dom::ObservableArrayProxyHandler" 23480 self.proxyHandler = CGClass( 23481 "ObservableArrayProxyHandler", 23482 bases=[ClassBase(parentClass)], 23483 constructors=[], 23484 methods=methods, 23485 ) 23486 23487 def declare(self): 23488 # Our class declaration should happen when we're defining 23489 return "" 23490 23491 def define(self): 23492 return self.proxyHandler.declare() + "\n" + self.proxyHandler.define() 23493 23494 23495 class CGObservableArrayProxyHandlerGenerator(CGNamespace): 23496 """ 23497 Declares and defines convenience methods for accessing backing list objects 23498 for observable array attribute. Generates function signatures, un/packs 23499 backing list objects from slot, etc. 23500 """ 23501 23502 def __init__(self, descriptor, attr): 23503 assert attr.isAttr() 23504 assert attr.type.isObservableArray() 23505 namespace = toBindingNamespace(MakeNativeName(attr.identifier.name)) 23506 proxyHandler = CGObservableArrayProxyHandler(descriptor, attr) 23507 CGNamespace.__init__(self, namespace, proxyHandler) 23508 23509 23510 class CGObservableArraySetterGenerator(CGGeneric): 23511 """ 23512 Creates setter for an observableArray attributes. 23513 """ 23514 23515 def __init__(self, descriptor, attr): 23516 assert attr.isAttr() 23517 assert attr.type.isObservableArray() 23518 getBackingObject = getObservableArrayBackingObject(descriptor, attr) 23519 setElement = dedent( 23520 """ 23521 if (!JS_SetElement(cx, backingObj, i, val)) { 23522 return false; 23523 } 23524 """ 23525 ) 23526 conversion = wrapForType( 23527 attr.type.inner, 23528 descriptor, 23529 { 23530 "result": "arg0.ElementAt(i)", 23531 "successCode": setElement, 23532 "jsvalRef": "val", 23533 "jsvalHandle": "&val", 23534 }, 23535 ) 23536 CGGeneric.__init__( 23537 self, 23538 fill( 23539 """ 23540 if (xpc::WrapperFactory::IsXrayWrapper(obj)) { 23541 JS_ReportErrorASCII(cx, "Accessing from Xray wrapper is not supported."); 23542 return false; 23543 } 23544 23545 ${getBackingObject} 23546 const ObservableArrayProxyHandler* handler = GetObservableArrayProxyHandler(backingObj); 23547 if (!handler->SetLength(cx, backingObj, 0)) { 23548 return false; 23549 } 23550 23551 JS::Rooted<JS::Value> val(cx); 23552 for (size_t i = 0; i < arg0.Length(); i++) { 23553 $*{conversion} 23554 } 23555 """, 23556 conversion=conversion, 23557 getBackingObject=getBackingObject, 23558 ), 23559 ) 23560 23561 23562 class CGObservableArrayHelperFunctionGenerator(CGHelperFunctionGenerator): 23563 """ 23564 Generates code to allow C++ to perform operations on backing objects. Gets 23565 a context from the binding wrapper, turns arguments into JS::Values (via 23566 CallbackMember/CGNativeMember argument conversion), then uses 23567 MethodBodyGenerator to generate the body. 23568 """ 23569 23570 class MethodBodyGenerator(CGThing): 23571 """ 23572 Creates methods body for observable array attribute. It is expected that all 23573 methods will be have a maplike/setlike object attached. Unwrapping/wrapping 23574 will be taken care of by the usual method generation machinery in 23575 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of 23576 using CGCallGenerator. 23577 """ 23578 23579 def __init__( 23580 self, 23581 descriptor, 23582 attr, 23583 methodName, 23584 helperGenerator, 23585 needsIndexArg, 23586 ): 23587 assert attr.isAttr() 23588 assert attr.type.isObservableArray() 23589 23590 CGThing.__init__(self) 23591 self.helperGenerator = helperGenerator 23592 self.cgRoot = CGList([]) 23593 23594 self.cgRoot.append( 23595 CGGeneric( 23596 getObservableArrayBackingObject( 23597 descriptor, 23598 attr, 23599 dedent( 23600 """ 23601 aRv.Throw(NS_ERROR_UNEXPECTED); 23602 return%s; 23603 """ 23604 % helperGenerator.getDefaultRetval() 23605 ), 23606 ) 23607 ) 23608 ) 23609 23610 # Generates required code for the method. Method descriptions included 23611 # in definitions below. Throw if we don't have a method to fill in what 23612 # we're looking for. 23613 try: 23614 methodGenerator = getattr(self, methodName) 23615 except AttributeError: 23616 raise TypeError( 23617 "Missing observable array method definition '%s'" % methodName 23618 ) 23619 # Method generator returns tuple, containing: 23620 # 23621 # - a list of CGThings representing setup code for preparing to call 23622 # the JS API function 23623 # - JS API function name 23624 # - a list of arguments needed for the JS API function we're calling 23625 # - a list of CGThings representing code needed before return. 23626 (setupCode, funcName, arguments, returnCode) = methodGenerator() 23627 23628 # Append the list of setup code CGThings 23629 self.cgRoot.append(CGList(setupCode)) 23630 # Create the JS API call 23631 if needsIndexArg: 23632 arguments.insert(0, "aIndex") 23633 self.cgRoot.append( 23634 CGWrapper( 23635 CGGeneric( 23636 fill( 23637 """ 23638 aRv.MightThrowJSException(); 23639 if (!${funcName}(${args})) { 23640 aRv.StealExceptionFromJSContext(cx); 23641 return${retval}; 23642 } 23643 """, 23644 funcName=funcName, 23645 args=", ".join(["cx", "backingObj"] + arguments), 23646 retval=helperGenerator.getDefaultRetval(), 23647 ) 23648 ) 23649 ) 23650 ) 23651 # Append code before return 23652 self.cgRoot.append(CGList(returnCode)) 23653 23654 def elementat(self): 23655 setupCode = [] 23656 if not self.helperGenerator.needsScopeBody(): 23657 setupCode.append(CGGeneric("JS::Rooted<JS::Value> result(cx);\n")) 23658 returnCode = [ 23659 CGGeneric( 23660 fill( 23661 """ 23662 if (result.isUndefined()) { 23663 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 23664 return${retval}; 23665 } 23666 """, 23667 retval=self.helperGenerator.getDefaultRetval(), 23668 ) 23669 ) 23670 ] 23671 return (setupCode, "JS_GetElement", ["&result"], returnCode) 23672 23673 def replaceelementat(self): 23674 setupCode = [ 23675 CGGeneric( 23676 fill( 23677 """ 23678 uint32_t length; 23679 aRv.MightThrowJSException(); 23680 if (!JS::GetArrayLength(cx, backingObj, &length)) { 23681 aRv.StealExceptionFromJSContext(cx); 23682 return${retval}; 23683 } 23684 if (aIndex > length) { 23685 aRv.ThrowRangeError("Invalid index"); 23686 return${retval}; 23687 } 23688 """, 23689 retval=self.helperGenerator.getDefaultRetval(), 23690 ) 23691 ) 23692 ] 23693 return (setupCode, "JS_SetElement", ["argv[0]"], []) 23694 23695 def appendelement(self): 23696 setupCode = [ 23697 CGGeneric( 23698 fill( 23699 """ 23700 uint32_t length; 23701 aRv.MightThrowJSException(); 23702 if (!JS::GetArrayLength(cx, backingObj, &length)) { 23703 aRv.StealExceptionFromJSContext(cx); 23704 return${retval}; 23705 } 23706 """, 23707 retval=self.helperGenerator.getDefaultRetval(), 23708 ) 23709 ) 23710 ] 23711 return (setupCode, "JS_SetElement", ["length", "argv[0]"], []) 23712 23713 def removelastelement(self): 23714 setupCode = [ 23715 CGGeneric( 23716 fill( 23717 """ 23718 uint32_t length; 23719 aRv.MightThrowJSException(); 23720 if (!JS::GetArrayLength(cx, backingObj, &length)) { 23721 aRv.StealExceptionFromJSContext(cx); 23722 return${retval}; 23723 } 23724 if (length == 0) { 23725 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 23726 return${retval}; 23727 } 23728 """, 23729 retval=self.helperGenerator.getDefaultRetval(), 23730 ) 23731 ) 23732 ] 23733 return (setupCode, "JS::SetArrayLength", ["length - 1"], []) 23734 23735 def length(self): 23736 return ( 23737 [CGGeneric("uint32_t retVal;\n")], 23738 "JS::GetArrayLength", 23739 ["&retVal"], 23740 [], 23741 ) 23742 23743 def define(self): 23744 return self.cgRoot.define() 23745 23746 def __init__( 23747 self, 23748 descriptor, 23749 attr, 23750 name, 23751 returnType=BuiltinTypes[IDLBuiltinType.Types.undefined], 23752 needsResultConversion=True, 23753 needsIndexArg=False, 23754 needsValueArg=False, 23755 ): 23756 assert attr.isAttr() 23757 assert attr.type.isObservableArray() 23758 self.attr = attr 23759 self.needsIndexArg = needsIndexArg 23760 23761 args = [] 23762 if needsValueArg: 23763 args.append(FakeArgument(attr.type.inner, "aValue")) 23764 23765 CGHelperFunctionGenerator.__init__( 23766 self, 23767 descriptor, 23768 name, 23769 args, 23770 returnType, 23771 needsResultConversion, 23772 ) 23773 23774 def getArgs(self, returnType, argList): 23775 if self.needsIndexArg: 23776 argList = [ 23777 FakeArgument(BuiltinTypes[IDLBuiltinType.Types.unsigned_long], "aIndex") 23778 ] + argList 23779 return CGHelperFunctionGenerator.getArgs(self, returnType, argList) 23780 23781 def getCall(self): 23782 return CGObservableArrayHelperFunctionGenerator.MethodBodyGenerator( 23783 self.descriptorProvider, 23784 self.attr, 23785 self.name.lower(), 23786 self, 23787 self.needsIndexArg, 23788 ).define() 23789 23790 23791 class CGObservableArrayHelperGenerator(CGNamespace): 23792 """ 23793 Declares and defines convenience methods for accessing backing object for 23794 observable array type. Generates function signatures, un/packs 23795 backing objects from slot, etc. 23796 """ 23797 23798 def __init__(self, descriptor, attr): 23799 assert attr.isAttr() 23800 assert attr.type.isObservableArray() 23801 23802 namespace = "%sHelpers" % MakeNativeName(attr.identifier.name) 23803 helpers = [ 23804 CGObservableArrayHelperFunctionGenerator( 23805 descriptor, 23806 attr, 23807 "ElementAt", 23808 returnType=attr.type.inner, 23809 needsIndexArg=True, 23810 ), 23811 CGObservableArrayHelperFunctionGenerator( 23812 descriptor, 23813 attr, 23814 "ReplaceElementAt", 23815 needsIndexArg=True, 23816 needsValueArg=True, 23817 ), 23818 CGObservableArrayHelperFunctionGenerator( 23819 descriptor, 23820 attr, 23821 "AppendElement", 23822 needsValueArg=True, 23823 ), 23824 CGObservableArrayHelperFunctionGenerator( 23825 descriptor, 23826 attr, 23827 "RemoveLastElement", 23828 ), 23829 CGObservableArrayHelperFunctionGenerator( 23830 descriptor, 23831 attr, 23832 "Length", 23833 returnType=BuiltinTypes[IDLBuiltinType.Types.unsigned_long], 23834 needsResultConversion=False, 23835 ), 23836 ] 23837 CGNamespace.__init__(self, namespace, CGList(helpers, "\n")) 23838 23839 23840 class GlobalGenRoots: 23841 """ 23842 Roots for global codegen. 23843 23844 To generate code, call the method associated with the target, and then 23845 call the appropriate define/declare method. 23846 """ 23847 23848 @staticmethod 23849 def GeneratedAtomList(config): 23850 # Atom enum 23851 dictionaries = config.dictionaries 23852 23853 structs = [] 23854 23855 def memberToAtomCacheMember(binaryNameFor, m): 23856 binaryMemberName = binaryNameFor(m) 23857 return ClassMember( 23858 CGDictionary.makeIdName(binaryMemberName), 23859 "PinnedStringId", 23860 visibility="public", 23861 ) 23862 23863 def buildAtomCacheStructure(idlobj, binaryNameFor, members): 23864 classMembers = [memberToAtomCacheMember(binaryNameFor, m) for m in members] 23865 structName = idlobj.identifier.name + "Atoms" 23866 return ( 23867 structName, 23868 CGWrapper( 23869 CGClass( 23870 structName, bases=None, isStruct=True, members=classMembers 23871 ), 23872 post="\n", 23873 ), 23874 ) 23875 23876 for dict in dictionaries: 23877 if len(dict.members) == 0: 23878 continue 23879 23880 structs.append( 23881 buildAtomCacheStructure(dict, lambda m: m.identifier.name, dict.members) 23882 ) 23883 23884 for d in config.getDescriptors(isJSImplemented=True) + config.getDescriptors( 23885 isCallback=True 23886 ): 23887 members = [m for m in d.interface.members if m.isAttr() or m.isMethod()] 23888 if d.interface.isJSImplemented() and d.interface.ctor(): 23889 # We'll have an __init() method. 23890 members.append(FakeMember("__init")) 23891 if d.interface.isJSImplemented() and d.interface.getExtendedAttribute( 23892 "WantsEventListenerHooks" 23893 ): 23894 members.append(FakeMember("eventListenerAdded")) 23895 members.append(FakeMember("eventListenerRemoved")) 23896 if len(members) == 0: 23897 continue 23898 23899 structs.append( 23900 buildAtomCacheStructure( 23901 d.interface, 23902 lambda m: d.binaryNameFor(m.identifier.name, m.isStatic()), 23903 members, 23904 ) 23905 ) 23906 23907 structs.sort() 23908 generatedStructs = [struct for structName, struct in structs] 23909 structNames = [structName for structName, struct in structs] 23910 23911 mainStruct = CGWrapper( 23912 CGClass( 23913 "PerThreadAtomCache", 23914 bases=[ClassBase(structName) for structName in structNames], 23915 isStruct=True, 23916 ), 23917 post="\n", 23918 ) 23919 23920 structs = CGList(generatedStructs + [mainStruct]) 23921 23922 # Wrap all of that in our namespaces. 23923 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(structs, pre="\n")) 23924 curr = CGWrapper(curr, post="\n") 23925 23926 # Add include statement for PinnedStringId. 23927 declareIncludes = ["mozilla/dom/PinnedStringId.h"] 23928 curr = CGHeaders([], [], [], [], declareIncludes, [], "GeneratedAtomList", curr) 23929 23930 # Add include guards. 23931 curr = CGIncludeGuard("GeneratedAtomList", curr) 23932 23933 # Add the auto-generated comment. 23934 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) 23935 23936 # Done. 23937 return curr 23938 23939 @staticmethod 23940 def GeneratedEventList(config): 23941 eventList = CGList([]) 23942 for generatedEvent in config.generatedEvents: 23943 eventList.append( 23944 CGGeneric(declare=("GENERATED_EVENT(%s)\n" % generatedEvent)) 23945 ) 23946 return eventList 23947 23948 @staticmethod 23949 def PrototypeList(config): 23950 # Prototype ID enum. 23951 descriptorsWithPrototype = config.getDescriptors( 23952 hasInterfacePrototypeObject=True 23953 ) 23954 descriptorsWithPrototype.extend( 23955 config.getDescriptors(hasOrdinaryObjectPrototype=True) 23956 ) 23957 descriptorsWithPrototype.sort(key=attrgetter("name")) 23958 protos = [d.name for d in descriptorsWithPrototype] 23959 idEnum = CGNamespacedEnum("id", "ID", ["_ID_Start"] + protos, [0, "_ID_Start"]) 23960 idEnum = CGList([idEnum]) 23961 23962 def fieldSizeAssert(amount, jitInfoField, message): 23963 maxFieldValue = ( 23964 "(uint64_t(1) << (sizeof(std::declval<JSJitInfo>().%s) * 8))" 23965 % jitInfoField 23966 ) 23967 return CGGeneric( 23968 define='static_assert(%s < %s, "%s");\n\n' 23969 % (amount, maxFieldValue, message) 23970 ) 23971 23972 idEnum.append( 23973 fieldSizeAssert("id::_ID_Count", "protoID", "Too many prototypes!") 23974 ) 23975 23976 # Wrap all of that in our namespaces. 23977 idEnum = CGNamespace.build( 23978 ["mozilla", "dom", "prototypes"], CGWrapper(idEnum, pre="\n") 23979 ) 23980 idEnum = CGWrapper(idEnum, post="\n") 23981 23982 curr = CGList( 23983 [ 23984 CGGeneric(define="#include <stdint.h>\n"), 23985 CGGeneric(define="#include <type_traits>\n\n"), 23986 CGGeneric(define='#include "js/experimental/JitInfo.h"\n\n'), 23987 CGGeneric(define='#include "mozilla/dom/BindingNames.h"\n\n'), 23988 CGGeneric(define='#include "mozilla/dom/PrototypeList.h"\n\n'), 23989 idEnum, 23990 ] 23991 ) 23992 23993 # Let things know the maximum length of the prototype chain. 23994 maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH" 23995 maxMacro = CGGeneric( 23996 declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength) 23997 ) 23998 curr.append(CGWrapper(maxMacro, post="\n\n")) 23999 curr.append( 24000 fieldSizeAssert( 24001 maxMacroName, "depth", "Some inheritance chain is too long!" 24002 ) 24003 ) 24004 24005 # Constructor ID enum. 24006 constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)] 24007 idEnum = CGNamespacedEnum( 24008 "id", 24009 "ID", 24010 ["_ID_Start"] + constructors, 24011 ["prototypes::id::_ID_Count", "_ID_Start"], 24012 ) 24013 24014 # Wrap all of that in our namespaces. 24015 idEnum = CGNamespace.build( 24016 ["mozilla", "dom", "constructors"], CGWrapper(idEnum, pre="\n") 24017 ) 24018 idEnum = CGWrapper(idEnum, post="\n") 24019 24020 curr.append(idEnum) 24021 24022 # Named properties object enum. 24023 namedPropertiesObjects = [ 24024 d.name for d in config.getDescriptors(hasNamedPropertiesObject=True) 24025 ] 24026 idEnum = CGNamespacedEnum( 24027 "id", 24028 "ID", 24029 ["_ID_Start"] + namedPropertiesObjects, 24030 ["constructors::id::_ID_Count", "_ID_Start"], 24031 ) 24032 24033 # Wrap all of that in our namespaces. 24034 idEnum = CGNamespace.build( 24035 ["mozilla", "dom", "namedpropertiesobjects"], CGWrapper(idEnum, pre="\n") 24036 ) 24037 idEnum = CGWrapper(idEnum, post="\n") 24038 24039 curr.append(idEnum) 24040 24041 traitsDecls = [ 24042 CGGeneric( 24043 declare=dedent( 24044 """ 24045 template <prototypes::ID PrototypeID> 24046 struct PrototypeTraits; 24047 """ 24048 ) 24049 ) 24050 ] 24051 traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype) 24052 24053 ifaceNamesWithProto = [ 24054 d.interface.getClassName() for d in descriptorsWithPrototype 24055 ] 24056 traitsDecls.append( 24057 CGStringTable("NamesOfInterfacesWithProtos", ifaceNamesWithProto) 24058 ) 24059 24060 traitsDecl = CGNamespace.build(["mozilla", "dom"], CGList(traitsDecls)) 24061 24062 curr.append(traitsDecl) 24063 24064 # Add include guards. 24065 curr = CGIncludeGuard("PrototypeList", curr) 24066 24067 # Add the auto-generated comment. 24068 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) 24069 24070 # Done. 24071 return curr 24072 24073 @staticmethod 24074 def BindingNames(config): 24075 declare = fill( 24076 """ 24077 enum class BindingNamesOffset : uint16_t { 24078 $*{enumValues} 24079 }; 24080 24081 namespace binding_detail { 24082 extern const char sBindingNames[]; 24083 } // namespace binding_detail 24084 24085 MOZ_ALWAYS_INLINE const char* BindingName(BindingNamesOffset aOffset) { 24086 return binding_detail::sBindingNames + static_cast<size_t>(aOffset); 24087 } 24088 """, 24089 enumValues="".join( 24090 "%s = %i,\n" % (BindingNamesOffsetEnum(n), o) 24091 for (n, o) in config.namesStringOffsets 24092 ), 24093 ) 24094 define = fill( 24095 """ 24096 namespace binding_detail { 24097 24098 const char sBindingNames[] = { 24099 $*{namesString} 24100 }; 24101 24102 } // namespace binding_detail 24103 24104 // Making this enum bigger than a uint16_t has consequences on the size 24105 // of some structs (eg. WebIDLNameTableEntry) and tables. We should try 24106 // to avoid that. 24107 static_assert(EnumTypeFitsWithin<BindingNamesOffset, uint16_t>::value, 24108 "Size increase"); 24109 """, 24110 namesString=' "\\0"\n'.join( 24111 '/* %5i */ "%s"' % (o, n) for (n, o) in config.namesStringOffsets 24112 ) 24113 + "\n", 24114 ) 24115 24116 curr = CGGeneric(declare=declare, define=define) 24117 curr = CGWrapper(curr, pre="\n", post="\n") 24118 24119 curr = CGNamespace.build(["mozilla", "dom"], curr) 24120 curr = CGWrapper(curr, post="\n") 24121 24122 curr = CGHeaders( 24123 [], 24124 [], 24125 [], 24126 [], 24127 ["<stddef.h>", "<stdint.h>", "mozilla/Attributes.h"], 24128 ["mozilla/dom/BindingNames.h", "mozilla/EnumTypeTraits.h"], 24129 "BindingNames", 24130 curr, 24131 ) 24132 24133 # Add include guards. 24134 curr = CGIncludeGuard("BindingNames", curr) 24135 24136 # Done. 24137 return curr 24138 24139 @staticmethod 24140 def RegisterBindings(config): 24141 curr = CGNamespace.build( 24142 ["mozilla", "dom"], CGGlobalNames(config.windowGlobalNames) 24143 ) 24144 curr = CGWrapper(curr, post="\n") 24145 24146 # Add the includes 24147 defineIncludes = [ 24148 CGHeaders.getDeclarationFilename(desc.interface) 24149 for desc in config.getDescriptors( 24150 hasInterfaceObject=True, isExposedInWindow=True, register=True 24151 ) 24152 ] 24153 defineIncludes.append("mozilla/dom/BindingNames.h") 24154 defineIncludes.append("mozilla/dom/WebIDLGlobalNameHash.h") 24155 defineIncludes.append("mozilla/dom/PrototypeList.h") 24156 defineIncludes.append("mozilla/PerfectHash.h") 24157 defineIncludes.append("js/String.h") 24158 curr = CGHeaders([], [], [], [], [], defineIncludes, "RegisterBindings", curr) 24159 24160 # Add include guards. 24161 curr = CGIncludeGuard("RegisterBindings", curr) 24162 24163 # Done. 24164 return curr 24165 24166 @staticmethod 24167 def RegisterWorkerBindings(config): 24168 curr = CGRegisterWorkerBindings(config) 24169 24170 # Wrap all of that in our namespaces. 24171 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n")) 24172 curr = CGWrapper(curr, post="\n") 24173 24174 # Add the includes 24175 defineIncludes = [ 24176 CGHeaders.getDeclarationFilename(desc.interface) 24177 for desc in config.getDescriptors( 24178 hasInterfaceObject=True, register=True, isExposedInAnyWorker=True 24179 ) 24180 ] 24181 24182 curr = CGHeaders( 24183 [], [], [], [], [], defineIncludes, "RegisterWorkerBindings", curr 24184 ) 24185 24186 # Add include guards. 24187 curr = CGIncludeGuard("RegisterWorkerBindings", curr) 24188 24189 # Done. 24190 return curr 24191 24192 @staticmethod 24193 def RegisterWorkerDebuggerBindings(config): 24194 curr = CGRegisterWorkerDebuggerBindings(config) 24195 24196 # Wrap all of that in our namespaces. 24197 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n")) 24198 curr = CGWrapper(curr, post="\n") 24199 24200 # Add the includes 24201 defineIncludes = [ 24202 CGHeaders.getDeclarationFilename(desc.interface) 24203 for desc in config.getDescriptors( 24204 hasInterfaceObject=True, register=True, isExposedInWorkerDebugger=True 24205 ) 24206 ] 24207 24208 curr = CGHeaders( 24209 [], [], [], [], [], defineIncludes, "RegisterWorkerDebuggerBindings", curr 24210 ) 24211 24212 # Add include guards. 24213 curr = CGIncludeGuard("RegisterWorkerDebuggerBindings", curr) 24214 24215 # Done. 24216 return curr 24217 24218 @staticmethod 24219 def RegisterWorkletBindings(config): 24220 curr = CGRegisterWorkletBindings(config) 24221 24222 # Wrap all of that in our namespaces. 24223 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n")) 24224 curr = CGWrapper(curr, post="\n") 24225 24226 # Add the includes 24227 defineIncludes = [ 24228 CGHeaders.getDeclarationFilename(desc.interface) 24229 for desc in config.getDescriptors( 24230 hasInterfaceObject=True, register=True, isExposedInAnyWorklet=True 24231 ) 24232 ] 24233 24234 curr = CGHeaders( 24235 [], [], [], [], [], defineIncludes, "RegisterWorkletBindings", curr 24236 ) 24237 24238 # Add include guards. 24239 curr = CGIncludeGuard("RegisterWorkletBindings", curr) 24240 24241 # Done. 24242 return curr 24243 24244 @staticmethod 24245 def RegisterShadowRealmBindings(config): 24246 curr = CGRegisterShadowRealmBindings(config) 24247 24248 # Wrap all of that in our namespaces. 24249 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n")) 24250 curr = CGWrapper(curr, post="\n") 24251 24252 # Add the includes 24253 defineIncludes = [ 24254 CGHeaders.getDeclarationFilename(desc.interface) 24255 for desc in config.getDescriptors( 24256 hasInterfaceObject=True, register=True, isExposedInShadowRealms=True 24257 ) 24258 ] 24259 24260 curr = CGHeaders( 24261 [], [], [], [], [], defineIncludes, "RegisterShadowRealmBindings", curr 24262 ) 24263 24264 # Add include guards. 24265 curr = CGIncludeGuard("RegisterShadowRealmBindings", curr) 24266 24267 # Done. 24268 return curr 24269 24270 @staticmethod 24271 def UnionTypes(config): 24272 unionTypes = UnionsForFile(config, None) 24273 ( 24274 includes, 24275 implincludes, 24276 declarations, 24277 traverseMethods, 24278 unlinkMethods, 24279 unionStructs, 24280 ) = UnionTypes(unionTypes, config) 24281 24282 unionStructs = dependencySortDictionariesAndUnionsAndCallbacks(unionStructs) 24283 24284 unions = CGList( 24285 traverseMethods 24286 + unlinkMethods 24287 + [CGUnionStruct(t, config) for t in unionStructs] 24288 + [CGUnionStruct(t, config, True) for t in unionStructs], 24289 "\n", 24290 ) 24291 24292 includes.add("mozilla/OwningNonNull.h") 24293 includes.add("mozilla/dom/UnionMember.h") 24294 includes.add("mozilla/dom/BindingDeclarations.h") 24295 # BindingUtils.h is only needed for SetToObject. 24296 # If it stops being inlined or stops calling CallerSubsumes 24297 # both this bit and the bit in CGBindingRoot can be removed. 24298 includes.add("mozilla/dom/BindingUtils.h") 24299 24300 # Wrap all of that in our namespaces. 24301 curr = CGNamespace.build(["mozilla", "dom"], unions) 24302 24303 curr = CGWrapper(curr, post="\n") 24304 24305 builder = ForwardDeclarationBuilder() 24306 for className, isStruct in declarations: 24307 builder.add(className, isStruct=isStruct) 24308 24309 curr = CGList([builder.build(), curr], "\n") 24310 24311 curr = CGHeaders([], [], [], [], includes, implincludes, "UnionTypes", curr) 24312 24313 # Add include guards. 24314 curr = CGIncludeGuard("UnionTypes", curr) 24315 24316 # Done. 24317 return curr 24318 24319 @staticmethod 24320 def WebIDLPrefs(config): 24321 prefs = set() 24322 headers = set(["mozilla/dom/WebIDLPrefs.h"]) 24323 for d in config.getDescriptors(hasInterfaceOrInterfacePrototypeObject=True): 24324 for m in d.interface.members: 24325 pref = PropertyDefiner.getStringAttr(m, "Pref") 24326 if pref: 24327 headers.add(prefHeader(pref)) 24328 prefs.add((pref, prefIdentifier(pref))) 24329 prefs = sorted(prefs) 24330 declare = fill( 24331 """ 24332 enum class WebIDLPrefIndex : uint8_t { 24333 NoPref, 24334 $*{prefs} 24335 }; 24336 typedef bool (*WebIDLPrefFunc)(); 24337 extern const WebIDLPrefFunc sWebIDLPrefs[${len}]; 24338 """, 24339 prefs=",\n".join(map(lambda p: "// " + p[0] + "\n" + p[1], prefs)) + "\n", 24340 len=len(prefs) + 1, 24341 ) 24342 define = fill( 24343 """ 24344 const WebIDLPrefFunc sWebIDLPrefs[] = { 24345 nullptr, 24346 $*{prefs} 24347 }; 24348 """, 24349 prefs=",\n".join( 24350 map(lambda p: "// " + p[0] + "\nStaticPrefs::" + p[1], prefs) 24351 ) 24352 + "\n", 24353 ) 24354 prefFunctions = CGGeneric(declare=declare, define=define) 24355 24356 # Wrap all of that in our namespaces. 24357 curr = CGNamespace.build(["mozilla", "dom"], prefFunctions) 24358 24359 curr = CGWrapper(curr, post="\n") 24360 24361 curr = CGHeaders([], [], [], [], [], headers, "WebIDLPrefs", curr) 24362 24363 # Add include guards. 24364 curr = CGIncludeGuard("WebIDLPrefs", curr) 24365 24366 # Done. 24367 return curr 24368 24369 @staticmethod 24370 def WebIDLSerializable(config): 24371 # We need a declaration of StructuredCloneTags in the header. 24372 declareIncludes = set( 24373 [ 24374 "mozilla/dom/DOMJSClass.h", 24375 "mozilla/dom/StructuredCloneTags.h", 24376 "js/TypeDecls.h", 24377 ] 24378 ) 24379 defineIncludes = set( 24380 ["mozilla/dom/WebIDLSerializable.h", "mozilla/PerfectHash.h"] 24381 ) 24382 names = list() 24383 for d in config.getDescriptors(isSerializable=True): 24384 names.append(d.name) 24385 defineIncludes.add(CGHeaders.getDeclarationFilename(d.interface)) 24386 24387 if len(names) == 0: 24388 # We can't really create a PerfectHash out of this, but also there's 24389 # not much point to this file if we have no [Serializable] objects. 24390 # Just spit out an empty file. 24391 return CGIncludeGuard("WebIDLSerializable", CGGeneric("")) 24392 24393 # If we had a lot of serializable things, it might be worth it to use a 24394 # PerfectHash here, or an array ordered by sctag value and binary 24395 # search. But setting those up would require knowing in this python 24396 # code the values of the various SCTAG_DOM_*. We could hardcode them 24397 # here and add static asserts that the values are right, or switch to 24398 # code-generating StructuredCloneTags.h or something. But in practice, 24399 # there's a pretty small number of serializable interfaces, and just 24400 # doing a linear walk is fine. It's not obviously worse than the 24401 # if-cascade we used to have. Let's just make sure we notice if we do 24402 # end up with a lot of serializable things here. 24403 # 24404 # Also, in practice it looks like compilers compile this linear walk to 24405 # an out-of-bounds check followed by a direct index into an array, by 24406 # basically making a second copy of this array ordered by tag, with the 24407 # holes filled in. Again, worth checking whether this still happens if 24408 # we have too many serializable things. 24409 if len(names) > 20: 24410 raise TypeError( 24411 "We now have %s serializable interfaces. " 24412 "Double-check that the compiler is still " 24413 "generating a jump table." % len(names) 24414 ) 24415 24416 entries = list() 24417 # Make sure we have stable ordering. 24418 for name in sorted(names): 24419 exposedGlobals = computeGlobalNamesFromExposureSet(d.interface.exposureSet) 24420 # Strip off trailing newline to make our formatting look right. 24421 entries.append( 24422 fill( 24423 """ 24424 { 24425 /* mTag */ ${tag}, 24426 /* mDeserialize */ ${name}_Binding::Deserialize, 24427 /* mExposedGlobals */ ${exposedGlobals}, 24428 } 24429 """, 24430 tag=StructuredCloneTag(name), 24431 name=name, 24432 exposedGlobals=exposedGlobals, 24433 )[:-1] 24434 ) 24435 24436 declare = dedent( 24437 """ 24438 Maybe<std::pair<uint16_t, WebIDLDeserializer>> LookupDeserializer(StructuredCloneTags aTag); 24439 """ 24440 ) 24441 define = fill( 24442 """ 24443 struct WebIDLSerializableEntry { 24444 StructuredCloneTags mTag; 24445 WebIDLDeserializer mDeserialize; 24446 uint16_t mExposedGlobals; 24447 }; 24448 24449 static const WebIDLSerializableEntry sEntries[] = { 24450 $*{entries} 24451 }; 24452 24453 Maybe<std::pair<uint16_t, WebIDLDeserializer>> LookupDeserializer(StructuredCloneTags aTag) { 24454 for (auto& entry : sEntries) { 24455 if (entry.mTag == aTag) { 24456 return Some(std::pair(entry.mExposedGlobals, entry.mDeserialize)); 24457 } 24458 } 24459 return Nothing(); 24460 } 24461 """, 24462 entries=",\n".join(entries) + "\n", 24463 ) 24464 24465 code = CGGeneric(declare=declare, define=define) 24466 24467 # Wrap all of that in our namespaces. 24468 curr = CGNamespace.build(["mozilla", "dom"], code) 24469 24470 curr = CGWrapper(curr, post="\n") 24471 24472 curr = CGHeaders( 24473 [], [], [], [], declareIncludes, defineIncludes, "WebIDLSerializable", curr 24474 ) 24475 24476 # Add include guards. 24477 curr = CGIncludeGuard("WebIDLSerializable", curr) 24478 24479 # Done. 24480 return curr 24481 24482 24483 # Code generator for simple events 24484 class CGEventGetter(CGNativeMember): 24485 def __init__(self, descriptor, attr): 24486 ea = descriptor.getExtendedAttributes(attr, getter=True) 24487 CGNativeMember.__init__( 24488 self, 24489 descriptor, 24490 attr, 24491 CGSpecializedGetterCommon.makeNativeName(descriptor, attr), 24492 (attr.type, []), 24493 ea, 24494 resultNotAddRefed=not attr.type.isSequence(), 24495 ) 24496 self.body = self.getMethodBody() 24497 24498 def getArgs(self, returnType, argList): 24499 if "needsErrorResult" in self.extendedAttrs: 24500 raise TypeError("Event code generator does not support [Throws]!") 24501 if "canOOM" in self.extendedAttrs: 24502 raise TypeError("Event code generator does not support [CanOOM]!") 24503 if not self.member.isAttr(): 24504 raise TypeError("Event code generator does not support methods") 24505 if self.member.isStatic(): 24506 raise TypeError("Event code generators does not support static attributes") 24507 return CGNativeMember.getArgs(self, returnType, argList) 24508 24509 def getMethodBody(self): 24510 type = self.member.type 24511 memberName = CGDictionary.makeMemberName(self.member.identifier.name) 24512 if ( 24513 (type.isPrimitive() and type.tag() in builtinNames) 24514 or type.isEnum() 24515 or type.isPromise() 24516 or type.isGeckoInterface() 24517 ): 24518 return "return " + memberName + ";\n" 24519 if type.isJSString(): 24520 # https://bugzilla.mozilla.org/show_bug.cgi?id=1580167 24521 raise TypeError("JSString not supported as member of a generated event") 24522 if ( 24523 type.isDOMString() 24524 or type.isByteString() 24525 or type.isUSVString() 24526 or type.isUTF8String() 24527 ): 24528 return "aRetVal = " + memberName + ";\n" 24529 if type.isSpiderMonkeyInterface() or type.isObject(): 24530 return fill( 24531 """ 24532 if (${memberName}) { 24533 JS::ExposeObjectToActiveJS(${memberName}); 24534 } 24535 aRetVal.set(${memberName}); 24536 return; 24537 """, 24538 memberName=memberName, 24539 ) 24540 if type.isAny(): 24541 return fill( 24542 """ 24543 ${selfName}(aRetVal); 24544 """, 24545 selfName=self.name, 24546 ) 24547 if type.isUnion(): 24548 return "aRetVal = " + memberName + ";\n" 24549 if type.isSequence(): 24550 if type.nullable(): 24551 return ( 24552 "if (" 24553 + memberName 24554 + ".IsNull()) { aRetVal.SetNull(); } else { aRetVal.SetValue(" 24555 + memberName 24556 + ".Value().Clone()); }\n" 24557 ) 24558 else: 24559 return "aRetVal = " + memberName + ".Clone();\n" 24560 if type.isDictionary(): 24561 return "aRetVal = " + memberName + ";\n" 24562 raise TypeError("Event code generator does not support this type!") 24563 24564 def declare(self, cgClass): 24565 if ( 24566 getattr(self.member, "originatingInterface", cgClass.descriptor.interface) 24567 != cgClass.descriptor.interface 24568 ): 24569 return "" 24570 return CGNativeMember.declare(self, cgClass) 24571 24572 def define(self, cgClass): 24573 if ( 24574 getattr(self.member, "originatingInterface", cgClass.descriptor.interface) 24575 != cgClass.descriptor.interface 24576 ): 24577 return "" 24578 return CGNativeMember.define(self, cgClass) 24579 24580 24581 class CGEventSetter(CGNativeMember): 24582 def __init__(self): 24583 raise TypeError("Event code generator does not support setters!") 24584 24585 24586 class CGEventMethod(CGNativeMember): 24587 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): 24588 self.isInit = False 24589 24590 CGNativeMember.__init__( 24591 self, 24592 descriptor, 24593 method, 24594 CGSpecializedMethod.makeNativeName(descriptor, method), 24595 signature, 24596 descriptor.getExtendedAttributes(method), 24597 breakAfter=breakAfter, 24598 variadicIsSequence=True, 24599 ) 24600 self.originalArgs = list(self.args) 24601 24602 iface = descriptor.interface 24603 allowed = isConstructor 24604 if not allowed and iface.getExtendedAttribute("LegacyEventInit"): 24605 # Allow it, only if it fits the initFooEvent profile exactly 24606 # We could check the arg types but it's not worth the effort. 24607 if ( 24608 method.identifier.name == "init" + iface.identifier.name 24609 and signature[1][0].type.isDOMString() 24610 and signature[1][1].type.isBoolean() 24611 and signature[1][2].type.isBoolean() 24612 and 24613 # -3 on the left to ignore the type, bubbles, and cancelable parameters 24614 # -1 on the right to ignore the .trusted property which bleeds through 24615 # here because it is [Unforgeable]. 24616 len(signature[1]) - 3 24617 == len([x for x in iface.members if x.isAttr()]) - 1 24618 ): 24619 allowed = True 24620 self.isInit = True 24621 24622 if not allowed: 24623 raise TypeError("Event code generator does not support methods!") 24624 24625 def getArgs(self, returnType, argList): 24626 args = [self.getArg(arg) for arg in argList] 24627 return args 24628 24629 def getArg(self, arg): 24630 decl, ref = self.getArgType( 24631 arg.type, arg.canHaveMissingValue(), "Variadic" if arg.variadic else False 24632 ) 24633 if ref: 24634 decl = CGWrapper(decl, pre="const ", post="&") 24635 24636 name = arg.identifier.name 24637 name = "a" + name[0].upper() + name[1:] 24638 return Argument(decl.define(), name) 24639 24640 def declare(self, cgClass): 24641 if self.isInit: 24642 constructorForNativeCaller = "" 24643 else: 24644 self.args = list(self.originalArgs) 24645 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner")) 24646 constructorForNativeCaller = CGNativeMember.declare(self, cgClass) 24647 24648 self.args = list(self.originalArgs) 24649 if needCx(None, self.arguments(), [], considerTypes=True, static=True): 24650 self.args.insert(0, Argument("JSContext*", "aCx")) 24651 if not self.isInit: 24652 self.args.insert(0, Argument("const GlobalObject&", "aGlobal")) 24653 24654 return constructorForNativeCaller + CGNativeMember.declare(self, cgClass) 24655 24656 def defineInit(self, cgClass): 24657 iface = self.descriptorProvider.interface 24658 members = "" 24659 while iface.identifier.name != "Event": 24660 i = 3 # Skip the boilerplate args: type, bubble,s cancelable. 24661 for m in iface.members: 24662 if m.isAttr(): 24663 # We need to initialize all the member variables that do 24664 # not come from Event. 24665 if ( 24666 getattr(m, "originatingInterface", iface).identifier.name 24667 == "Event" 24668 ): 24669 continue 24670 name = CGDictionary.makeMemberName(m.identifier.name) 24671 members += "%s = %s;\n" % (name, self.args[i].name) 24672 i += 1 24673 iface = iface.parent 24674 24675 self.body = fill( 24676 """ 24677 InitEvent(${typeArg}, ${bubblesArg}, ${cancelableArg}); 24678 ${members} 24679 """, 24680 typeArg=self.args[0].name, 24681 bubblesArg=self.args[1].name, 24682 cancelableArg=self.args[2].name, 24683 members=members, 24684 ) 24685 24686 return CGNativeMember.define(self, cgClass) 24687 24688 def define(self, cgClass): 24689 self.args = list(self.originalArgs) 24690 if self.isInit: 24691 return self.defineInit(cgClass) 24692 members = "" 24693 holdJS = "" 24694 iface = self.descriptorProvider.interface 24695 while iface.identifier.name != "Event": 24696 for m in self.descriptorProvider.getDescriptor( 24697 iface.identifier.name 24698 ).interface.members: 24699 if m.isAttr(): 24700 # We initialize all the other member variables in the 24701 # Constructor except those ones coming from the Event. 24702 if ( 24703 getattr( 24704 m, "originatingInterface", cgClass.descriptor.interface 24705 ).identifier.name 24706 == "Event" 24707 ): 24708 continue 24709 name = CGDictionary.makeMemberName(m.identifier.name) 24710 if m.type.isSequence(): 24711 # For sequences we may not be able to do a simple 24712 # assignment because the underlying types may not match. 24713 # For example, the argument can be a 24714 # Sequence<OwningNonNull<SomeInterface>> while our 24715 # member is an nsTArray<RefPtr<SomeInterface>>. So 24716 # use AppendElements, which is actually a template on 24717 # the incoming type on nsTArray and does the right thing 24718 # for this case. 24719 target = name 24720 source = "%s.%s" % (self.args[1].name, name) 24721 sequenceCopy = "e->%s.AppendElements(%s);\n" 24722 if m.type.nullable(): 24723 sequenceCopy = CGIfWrapper( 24724 CGGeneric(sequenceCopy), "!%s.IsNull()" % source 24725 ).define() 24726 target += ".SetValue()" 24727 source += ".Value()" 24728 members += sequenceCopy % (target, source) 24729 elif m.type.isSpiderMonkeyInterface(): 24730 srcname = "%s.%s" % (self.args[1].name, name) 24731 if m.type.nullable(): 24732 members += fill( 24733 """ 24734 if (${srcname}.IsNull()) { 24735 e->${varname} = nullptr; 24736 } else { 24737 e->${varname} = ${srcname}.Value().Obj(); 24738 } 24739 """, 24740 varname=name, 24741 srcname=srcname, 24742 ) 24743 else: 24744 members += fill( 24745 """ 24746 e->${varname}.set(${srcname}.Obj()); 24747 """, 24748 varname=name, 24749 srcname=srcname, 24750 ) 24751 else: 24752 members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name) 24753 if ( 24754 m.type.isAny() 24755 or m.type.isObject() 24756 or m.type.isSpiderMonkeyInterface() 24757 ): 24758 holdJS = "mozilla::HoldJSObjects(e.get());\n" 24759 iface = iface.parent 24760 24761 self.body = fill( 24762 """ 24763 RefPtr<${nativeType}> e = new ${nativeType}(aOwner); 24764 bool trusted = e->Init(aOwner); 24765 e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable); 24766 $*{members} 24767 e->SetTrusted(trusted); 24768 e->SetComposed(${eventInit}.mComposed); 24769 $*{holdJS} 24770 return e.forget(); 24771 """, 24772 nativeType=self.descriptorProvider.nativeType.split("::")[-1], 24773 eventType=self.args[0].name, 24774 eventInit=self.args[1].name, 24775 members=members, 24776 holdJS=holdJS, 24777 ) 24778 24779 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner")) 24780 constructorForNativeCaller = CGNativeMember.define(self, cgClass) + "\n" 24781 self.args = list(self.originalArgs) 24782 self.body = fill( 24783 """ 24784 nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports()); 24785 return Constructor(owner, ${arg0}, ${arg1}); 24786 """, 24787 arg0=self.args[0].name, 24788 arg1=self.args[1].name, 24789 ) 24790 if needCx(None, self.arguments(), [], considerTypes=True, static=True): 24791 self.args.insert(0, Argument("JSContext*", "aCx")) 24792 self.args.insert(0, Argument("const GlobalObject&", "aGlobal")) 24793 return constructorForNativeCaller + CGNativeMember.define(self, cgClass) 24794 24795 24796 class CGEventClass(CGBindingImplClass): 24797 """ 24798 Codegen for the actual Event class implementation for this descriptor 24799 """ 24800 24801 def __init__(self, descriptor): 24802 CGBindingImplClass.__init__( 24803 self, 24804 descriptor, 24805 CGEventMethod, 24806 CGEventGetter, 24807 CGEventSetter, 24808 False, 24809 "WrapObjectInternal", 24810 ) 24811 members = [] 24812 extraMethods = [] 24813 self.membersNeedingCC = [] 24814 self.membersNeedingTrace = [] 24815 24816 for m in descriptor.interface.members: 24817 if ( 24818 getattr(m, "originatingInterface", descriptor.interface) 24819 != descriptor.interface 24820 ): 24821 continue 24822 24823 if m.isAttr(): 24824 if m.type.isAny(): 24825 self.membersNeedingTrace.append(m) 24826 # Add a getter that doesn't need a JSContext. Note that we 24827 # don't need to do this if our originating interface is not 24828 # the descriptor's interface, because in that case we 24829 # wouldn't generate the getter that _does_ need a JSContext 24830 # either. 24831 extraMethods.append( 24832 ClassMethod( 24833 CGSpecializedGetterCommon.makeNativeName(descriptor, m), 24834 "void", 24835 [Argument("JS::MutableHandle<JS::Value>", "aRetVal")], 24836 const=True, 24837 body=fill( 24838 """ 24839 JS::ExposeValueToActiveJS(${memberName}); 24840 aRetVal.set(${memberName}); 24841 """, 24842 memberName=CGDictionary.makeMemberName( 24843 m.identifier.name 24844 ), 24845 ), 24846 ) 24847 ) 24848 elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): 24849 self.membersNeedingTrace.append(m) 24850 elif typeNeedsRooting(m.type): 24851 raise TypeError( 24852 "Need to implement tracing for event member of type %s" % m.type 24853 ) 24854 elif idlTypeNeedsCycleCollection(m.type): 24855 self.membersNeedingCC.append(m) 24856 24857 nativeType = self.getNativeTypeForIDLType(m.type).define() 24858 members.append( 24859 ClassMember( 24860 CGDictionary.makeMemberName(m.identifier.name), 24861 nativeType, 24862 visibility="private", 24863 body="body", 24864 ) 24865 ) 24866 24867 parent = self.descriptor.interface.parent 24868 self.parentType = self.descriptor.getDescriptor( 24869 parent.identifier.name 24870 ).nativeType.split("::")[-1] 24871 self.nativeType = self.descriptor.nativeType.split("::")[-1] 24872 24873 if self.needCC(): 24874 isupportsDecl = fill( 24875 """ 24876 NS_DECL_ISUPPORTS_INHERITED 24877 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(${nativeType}, ${parentType}) 24878 """, 24879 nativeType=self.nativeType, 24880 parentType=self.parentType, 24881 ) 24882 else: 24883 isupportsDecl = fill( 24884 """ 24885 NS_INLINE_DECL_REFCOUNTING_INHERITED(${nativeType}, ${parentType}) 24886 """, 24887 nativeType=self.nativeType, 24888 parentType=self.parentType, 24889 ) 24890 24891 baseDeclarations = fill( 24892 """ 24893 public: 24894 $*{isupportsDecl} 24895 24896 protected: 24897 virtual ~${nativeType}(); 24898 explicit ${nativeType}(mozilla::dom::EventTarget* aOwner); 24899 24900 """, 24901 isupportsDecl=isupportsDecl, 24902 nativeType=self.nativeType, 24903 parentType=self.parentType, 24904 ) 24905 24906 className = self.nativeType 24907 asConcreteTypeMethod = ClassMethod( 24908 "As%s" % className, 24909 "%s*" % className, 24910 [], 24911 virtual=True, 24912 body="return this;\n", 24913 breakAfterReturnDecl=" ", 24914 override=True, 24915 ) 24916 extraMethods.append(asConcreteTypeMethod) 24917 24918 CGClass.__init__( 24919 self, 24920 className, 24921 bases=[ClassBase(self.parentType)], 24922 methods=extraMethods + self.methodDecls, 24923 members=members, 24924 extradeclarations=baseDeclarations, 24925 ) 24926 24927 def getWrapObjectBody(self): 24928 return ( 24929 "return %s_Binding::Wrap(aCx, this, aGivenProto);\n" % self.descriptor.name 24930 ) 24931 24932 def needCC(self): 24933 return len(self.membersNeedingCC) != 0 or len(self.membersNeedingTrace) != 0 24934 24935 def implTraverse(self): 24936 retVal = "" 24937 for m in self.membersNeedingCC: 24938 retVal += ( 24939 " NS_IMPL_CYCLE_COLLECTION_TRAVERSE(%s)\n" 24940 % CGDictionary.makeMemberName(m.identifier.name) 24941 ) 24942 return retVal 24943 24944 def implUnlink(self): 24945 retVal = "" 24946 for m in self.membersNeedingCC: 24947 retVal += ( 24948 " NS_IMPL_CYCLE_COLLECTION_UNLINK(%s)\n" 24949 % CGDictionary.makeMemberName(m.identifier.name) 24950 ) 24951 for m in self.membersNeedingTrace: 24952 name = CGDictionary.makeMemberName(m.identifier.name) 24953 if m.type.isAny(): 24954 retVal += " tmp->" + name + ".setUndefined();\n" 24955 elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): 24956 retVal += " tmp->" + name + " = nullptr;\n" 24957 else: 24958 raise TypeError("Unknown traceable member type %s" % m.type) 24959 return retVal 24960 24961 def implTrace(self): 24962 retVal = "" 24963 for m in self.membersNeedingTrace: 24964 retVal += ( 24965 " NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(%s)\n" 24966 % CGDictionary.makeMemberName(m.identifier.name) 24967 ) 24968 return retVal 24969 24970 def define(self): 24971 for m in self.membersNeedingTrace: 24972 if not ( 24973 m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface() 24974 ): 24975 raise TypeError("Unknown traceable member type %s" % m.type) 24976 24977 if len(self.membersNeedingTrace) > 0: 24978 dropJS = "mozilla::DropJSObjects(this);\n" 24979 else: 24980 dropJS = "" 24981 # Just override CGClass and do our own thing 24982 ctorParams = ( 24983 "aOwner, nullptr, nullptr" if self.parentType == "Event" else "aOwner" 24984 ) 24985 24986 if self.needCC(): 24987 classImpl = fill( 24988 """ 24989 24990 NS_IMPL_CYCLE_COLLECTION_CLASS(${nativeType}) 24991 24992 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType}) 24993 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType}) 24994 24995 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(${nativeType}, ${parentType}) 24996 $*{traverse} 24997 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 24998 24999 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(${nativeType}, ${parentType}) 25000 $*{trace} 25001 NS_IMPL_CYCLE_COLLECTION_TRACE_END 25002 25003 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(${nativeType}, ${parentType}) 25004 $*{unlink} 25005 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 25006 25007 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType}) 25008 NS_INTERFACE_MAP_END_INHERITING(${parentType}) 25009 """, 25010 nativeType=self.nativeType, 25011 parentType=self.parentType, 25012 traverse=self.implTraverse(), 25013 unlink=self.implUnlink(), 25014 trace=self.implTrace(), 25015 ) 25016 else: 25017 classImpl = "" 25018 25019 classImpl += fill( 25020 """ 25021 25022 ${nativeType}::${nativeType}(mozilla::dom::EventTarget* aOwner) 25023 : ${parentType}(${ctorParams}) 25024 { 25025 } 25026 25027 ${nativeType}::~${nativeType}() 25028 { 25029 $*{dropJS} 25030 } 25031 25032 """, 25033 nativeType=self.nativeType, 25034 ctorParams=ctorParams, 25035 parentType=self.parentType, 25036 dropJS=dropJS, 25037 ) 25038 25039 return classImpl + CGBindingImplClass.define(self) 25040 25041 def getNativeTypeForIDLType(self, type): 25042 if type.isPrimitive() and type.tag() in builtinNames: 25043 nativeType = CGGeneric(builtinNames[type.tag()]) 25044 if type.nullable(): 25045 nativeType = CGTemplatedType("Nullable", nativeType) 25046 elif type.isEnum(): 25047 nativeType = CGGeneric(type.unroll().inner.identifier.name) 25048 if type.nullable(): 25049 nativeType = CGTemplatedType("Nullable", nativeType) 25050 elif type.isJSString(): 25051 nativeType = CGGeneric("JS::Heap<JSString*>") 25052 elif type.isDOMString() or type.isUSVString(): 25053 nativeType = CGGeneric("nsString") 25054 elif type.isByteString() or type.isUTF8String(): 25055 nativeType = CGGeneric("nsCString") 25056 elif type.isPromise(): 25057 nativeType = CGGeneric("RefPtr<Promise>") 25058 elif type.isDictionary(): 25059 if typeNeedsRooting(type): 25060 raise TypeError( 25061 "We don't support event members that are dictionary types " 25062 "that need rooting (%s)" % type 25063 ) 25064 nativeType = CGGeneric(CGDictionary.makeDictionaryName(type.unroll().inner)) 25065 if type.nullable(): 25066 nativeType = CGTemplatedType("Nullable", nativeType) 25067 elif type.isGeckoInterface(): 25068 iface = type.unroll().inner 25069 nativeType = self.descriptor.getDescriptor(iface.identifier.name).nativeType 25070 # Now trim off unnecessary namespaces 25071 nativeType = nativeType.split("::") 25072 if nativeType[0] == "mozilla": 25073 nativeType.pop(0) 25074 if nativeType[0] == "dom": 25075 nativeType.pop(0) 25076 nativeType = CGWrapper( 25077 CGGeneric("::".join(nativeType)), pre="RefPtr<", post=">" 25078 ) 25079 elif type.isAny(): 25080 nativeType = CGGeneric("JS::Heap<JS::Value>") 25081 elif type.isObject() or type.isSpiderMonkeyInterface(): 25082 nativeType = CGGeneric("JS::Heap<JSObject*>") 25083 elif type.isUnion(): 25084 nativeType = CGGeneric(CGUnionStruct.unionTypeDecl(type, True)) 25085 elif type.isSequence(): 25086 if type.nullable(): 25087 innerType = type.inner.inner 25088 else: 25089 innerType = type.inner 25090 if ( 25091 not innerType.isPrimitive() 25092 and not innerType.isEnum() 25093 and not innerType.isDOMString() 25094 and not innerType.isByteString() 25095 and not innerType.isUTF8String() 25096 and not innerType.isPromise() 25097 and not innerType.isGeckoInterface() 25098 ): 25099 raise TypeError( 25100 "Don't know how to properly manage GC/CC for " 25101 "event member of type %s" % type 25102 ) 25103 nativeType = CGTemplatedType( 25104 "nsTArray", self.getNativeTypeForIDLType(innerType) 25105 ) 25106 if type.nullable(): 25107 nativeType = CGTemplatedType("Nullable", nativeType) 25108 else: 25109 raise TypeError("Don't know how to declare event member of type %s" % type) 25110 return nativeType 25111 25112 25113 class CGEventRoot(CGThing): 25114 def __init__(self, config, interfaceName): 25115 descriptor = config.getDescriptor(interfaceName) 25116 25117 self.root = CGWrapper(CGEventClass(descriptor), pre="\n", post="\n") 25118 25119 self.root = CGNamespace.build(["mozilla", "dom"], self.root) 25120 25121 self.root = CGList( 25122 [CGClassForwardDeclare("JSContext", isStruct=True), self.root] 25123 ) 25124 25125 parent = descriptor.interface.parent.identifier.name 25126 25127 # Throw in our #includes 25128 self.root = CGHeaders( 25129 [descriptor], 25130 [], 25131 [], 25132 [], 25133 [ 25134 config.getDescriptor(parent).headerFile, 25135 "mozilla/Attributes.h", 25136 "mozilla/dom/%sBinding.h" % interfaceName, 25137 "mozilla/dom/BindingUtils.h", 25138 ], 25139 [ 25140 "%s.h" % interfaceName, 25141 "js/GCAPI.h", 25142 "mozilla/HoldDropJSObjects.h", 25143 "mozilla/dom/Nullable.h", 25144 ], 25145 "", 25146 self.root, 25147 config, 25148 ) 25149 25150 # And now some include guards 25151 self.root = CGIncludeGuard(interfaceName, self.root) 25152 25153 self.root = CGWrapper( 25154 self.root, 25155 pre=( 25156 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT 25157 % os.path.basename(descriptor.interface.filename) 25158 ), 25159 ) 25160 25161 self.root = CGWrapper( 25162 self.root, 25163 pre=dedent( 25164 """ 25165 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 25166 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 25167 /* This Source Code Form is subject to the terms of the Mozilla Public 25168 * License, v. 2.0. If a copy of the MPL was not distributed with this 25169 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 25170 25171 """ 25172 ), 25173 ) 25174 25175 def declare(self): 25176 return self.root.declare() 25177 25178 def define(self): 25179 return self.root.define()