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 7import os 8import re 9import string 10import math 11import textwrap 12import functools 13 14from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute, IDLInterfaceMember, IDLUndefinedValue, IDLEmptySequenceValue, IDLDictionary 15from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, getAllTypes, Descriptor, MemberIsUnforgeable, iteratorNativeType 16 17AUTOGENERATED_WARNING_COMMENT = \ 18 "/* THIS FILE IS AUTOGENERATED BY Codegen.py - DO NOT EDIT */\n\n" 19AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT = \ 20 "/* THIS FILE IS AUTOGENERATED FROM %s BY Codegen.py - DO NOT EDIT */\n\n" 21ADDPROPERTY_HOOK_NAME = '_addProperty' 22FINALIZE_HOOK_NAME = '_finalize' 23OBJECT_MOVED_HOOK_NAME = '_objectMoved' 24CONSTRUCT_HOOK_NAME = '_constructor' 25LEGACYCALLER_HOOK_NAME = '_legacycaller' 26HASINSTANCE_HOOK_NAME = '_hasInstance' 27RESOLVE_HOOK_NAME = '_resolve' 28MAY_RESOLVE_HOOK_NAME = '_mayResolve' 29ENUMERATE_HOOK_NAME = '_enumerate' 30ENUM_ENTRY_VARIABLE_NAME = 'strings' 31INSTANCE_RESERVED_SLOTS = 1 32 33 34def memberReservedSlot(member, descriptor): 35 return ("(DOM_INSTANCE_RESERVED_SLOTS + %d)" % 36 member.slotIndices[descriptor.interface.identifier.name]) 37 38 39def memberXrayExpandoReservedSlot(member, descriptor): 40 return ("(xpc::JSSLOT_EXPANDO_COUNT + %d)" % 41 member.slotIndices[descriptor.interface.identifier.name]) 42 43 44def mayUseXrayExpandoSlots(descriptor, attr): 45 assert not attr.getExtendedAttribute("NewObject") 46 # For attributes whose type is a Gecko interface we always use 47 # slots on the reflector for caching. Also, for interfaces that 48 # don't want Xrays we obviously never use the Xray expando slot. 49 return descriptor.wantsXrays and not attr.type.isGeckoInterface() 50 51 52def toStringBool(arg): 53 return str(not not arg).lower() 54 55 56def toBindingNamespace(arg): 57 return arg + "Binding" 58 59 60def isTypeCopyConstructible(type): 61 # Nullable and sequence stuff doesn't affect copy-constructibility 62 type = type.unroll() 63 return (type.isPrimitive() or type.isString() or type.isEnum() or 64 (type.isUnion() and 65 CGUnionStruct.isUnionCopyConstructible(type)) or 66 (type.isDictionary() and 67 CGDictionary.isDictionaryCopyConstructible(type.inner)) or 68 # Interface types are only copy-constructible if they're Gecko 69 # interfaces. SpiderMonkey interfaces are not copy-constructible 70 # because of rooting issues. 71 (type.isInterface() and type.isGeckoInterface())) 72 73 74def idlTypeNeedsCycleCollection(type): 75 type = type.unroll() # Takes care of sequences and nullables 76 if ((type.isPrimitive() and type.tag() in builtinNames) or 77 type.isEnum() or 78 type.isString() or 79 type.isAny() or 80 type.isObject() or 81 type.isSpiderMonkeyInterface()): 82 return False 83 elif type.isCallback() or type.isGeckoInterface(): 84 return True 85 elif type.isUnion(): 86 return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes) 87 elif type.isMozMap(): 88 if idlTypeNeedsCycleCollection(type.inner): 89 raise TypeError("Cycle collection for type %s is not supported" % type) 90 return False 91 elif type.isDictionary(): 92 if any(idlTypeNeedsCycleCollection(m.type) for m in type.inner.members): 93 raise TypeError("Cycle collection for type %s is not supported" % type) 94 return False 95 else: 96 raise TypeError("Don't know whether to cycle-collect type %s" % type) 97 98 99def wantsAddProperty(desc): 100 return (desc.concrete and desc.wrapperCache and not desc.isGlobal()) 101 102 103# We'll want to insert the indent at the beginnings of lines, but we 104# don't want to indent empty lines. So only indent lines that have a 105# non-newline character on them. 106lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE) 107 108 109def indent(s, indentLevel=2): 110 """ 111 Indent C++ code. 112 113 Weird secret feature: this doesn't indent lines that start with # (such as 114 #include lines or #ifdef/#endif). 115 """ 116 if s == "": 117 return s 118 return re.sub(lineStartDetector, indentLevel * " ", s) 119 120 121# dedent() and fill() are often called on the same string multiple 122# times. We want to memoize their return values so we don't keep 123# recomputing them all the time. 124def memoize(fn): 125 """ 126 Decorator to memoize a function of one argument. The cache just 127 grows without bound. 128 """ 129 cache = {} 130 131 @functools.wraps(fn) 132 def wrapper(arg): 133 retval = cache.get(arg) 134 if retval is None: 135 retval = cache[arg] = fn(arg) 136 return retval 137 return wrapper 138 139 140@memoize 141def dedent(s): 142 """ 143 Remove all leading whitespace from s, and remove a blank line 144 at the beginning. 145 """ 146 if s.startswith('\n'): 147 s = s[1:] 148 return textwrap.dedent(s) 149 150 151# This works by transforming the fill()-template to an equivalent 152# string.Template. 153fill_multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?") 154 155 156find_substitutions = re.compile(r"\${") 157 158 159@memoize 160def compile_fill_template(template): 161 """ 162 Helper function for fill(). Given the template string passed to fill(), 163 do the reusable part of template processing and return a pair (t, 164 argModList) that can be used every time fill() is called with that 165 template argument. 166 167 argsModList is list of tuples that represent modifications to be 168 made to args. Each modification has, in order: i) the arg name, 169 ii) the modified name, iii) the indent depth. 170 """ 171 t = dedent(template) 172 assert t.endswith("\n") or "\n" not in t 173 argModList = [] 174 175 def replace(match): 176 """ 177 Replaces a line like ' $*{xyz}\n' with '${xyz_n}', 178 where n is the indent depth, and add a corresponding entry to 179 argModList. 180 181 Note that this needs to close over argModList, so it has to be 182 defined inside compile_fill_template(). 183 """ 184 indentation, name, nl = match.groups() 185 depth = len(indentation) 186 187 # Check that $*{xyz} appears by itself on a line. 188 prev = match.string[:match.start()] 189 if (prev and not prev.endswith("\n")) or nl is None: 190 raise ValueError("Invalid fill() template: $*{%s} must appear by itself on a line" % name) 191 192 # Now replace this whole line of template with the indented equivalent. 193 modified_name = name + "_" + str(depth) 194 argModList.append((name, modified_name, depth)) 195 return "${" + modified_name + "}" 196 197 t = re.sub(fill_multiline_substitution_re, replace, t) 198 if not re.search(find_substitutions, t): 199 raise TypeError("Using fill() when dedent() would do.") 200 return (string.Template(t), argModList) 201 202 203def fill(template, **args): 204 """ 205 Convenience function for filling in a multiline template. 206 207 `fill(template, name1=v1, name2=v2)` is a lot like 208 `string.Template(template).substitute({"name1": v1, "name2": v2})`. 209 210 However, it's shorter, and has a few nice features: 211 212 * If `template` is indented, fill() automatically dedents it! 213 This makes code using fill() with Python's multiline strings 214 much nicer to look at. 215 216 * If `template` starts with a blank line, fill() strips it off. 217 (Again, convenient with multiline strings.) 218 219 * fill() recognizes a special kind of substitution 220 of the form `$*{name}`. 221 222 Use this to paste in, and automatically indent, multiple lines. 223 (Mnemonic: The `*` is for "multiple lines"). 224 225 A `$*` substitution must appear by itself on a line, with optional 226 preceding indentation (spaces only). The whole line is replaced by the 227 corresponding keyword argument, indented appropriately. If the 228 argument is an empty string, no output is generated, not even a blank 229 line. 230 """ 231 232 t, argModList = compile_fill_template(template) 233 # Now apply argModList to args 234 for (name, modified_name, depth) in argModList: 235 if not (args[name] == "" or args[name].endswith("\n")): 236 raise ValueError("Argument %s with value %r is missing a newline" % (name, args[name])) 237 args[modified_name] = indent(args[name], depth) 238 239 return t.substitute(args) 240 241 242class CGThing(): 243 """ 244 Abstract base class for things that spit out code. 245 """ 246 def __init__(self): 247 pass # Nothing for now 248 249 def declare(self): 250 """Produce code for a header file.""" 251 assert False # Override me! 252 253 def define(self): 254 """Produce code for a cpp file.""" 255 assert False # Override me! 256 257 def deps(self): 258 """Produce the deps for a pp file""" 259 assert False # Override me! 260 261 262class CGStringTable(CGThing): 263 """ 264 Generate a string table for the given strings with a function accessor: 265 266 const char *accessorName(unsigned int index) { 267 static const char table[] = "..."; 268 static const uint16_t indices = { ... }; 269 return &table[indices[index]]; 270 } 271 272 This is more efficient than the more natural: 273 274 const char *table[] = { 275 ... 276 }; 277 278 The uint16_t indices are smaller than the pointer equivalents, and the 279 string table requires no runtime relocations. 280 """ 281 def __init__(self, accessorName, strings): 282 CGThing.__init__(self) 283 self.accessorName = accessorName 284 self.strings = strings 285 286 def declare(self): 287 return "extern const char *%s(unsigned int aIndex);\n" % self.accessorName 288 289 def define(self): 290 table = ' "\\0" '.join('"%s"' % s for s in self.strings) 291 indices = [] 292 currentIndex = 0 293 for s in self.strings: 294 indices.append(currentIndex) 295 currentIndex += len(s) + 1 # for the null terminator 296 return fill( 297 """ 298 const char *${name}(unsigned int aIndex) 299 { 300 static const char table[] = ${table}; 301 static const uint16_t indices[] = { ${indices} }; 302 static_assert(${currentIndex} <= UINT16_MAX, "string table overflow!"); 303 return &table[indices[aIndex]]; 304 } 305 """, 306 name=self.accessorName, 307 table=table, 308 indices=", ".join("%d" % index for index in indices), 309 currentIndex=currentIndex) 310 311 312class CGNativePropertyHooks(CGThing): 313 """ 314 Generate a NativePropertyHooks for a given descriptor 315 """ 316 def __init__(self, descriptor, properties): 317 CGThing.__init__(self) 318 self.descriptor = descriptor 319 self.properties = properties 320 321 def declare(self): 322 if not self.descriptor.wantsXrays: 323 return "" 324 return dedent(""" 325 // We declare this as an array so that retrieving a pointer to this 326 // binding's property hooks only requires compile/link-time resolvable 327 // address arithmetic. Declaring it as a pointer instead would require 328 // doing a run-time load to fetch a pointer to this binding's property 329 // hooks. And then structures which embedded a pointer to this structure 330 // would require a run-time load for proper initialization, which would 331 // then induce static constructors. Lots of static constructors. 332 extern const NativePropertyHooks sNativePropertyHooks[]; 333 """) 334 335 def define(self): 336 if not self.descriptor.wantsXrays: 337 return "" 338 deleteNamedProperty = "nullptr" 339 if self.descriptor.concrete and self.descriptor.proxy: 340 resolveOwnProperty = "ResolveOwnProperty" 341 enumerateOwnProperties = "EnumerateOwnProperties" 342 if self.descriptor.needsXrayNamedDeleterHook(): 343 deleteNamedProperty = "DeleteNamedProperty" 344 elif self.descriptor.needsXrayResolveHooks(): 345 resolveOwnProperty = "ResolveOwnPropertyViaResolve" 346 enumerateOwnProperties = "EnumerateOwnPropertiesViaGetOwnPropertyNames" 347 else: 348 resolveOwnProperty = "nullptr" 349 enumerateOwnProperties = "nullptr" 350 if self.properties.hasNonChromeOnly(): 351 regular = "sNativeProperties.Upcast()" 352 else: 353 regular = "nullptr" 354 if self.properties.hasChromeOnly(): 355 chrome = "sChromeOnlyNativeProperties.Upcast()" 356 else: 357 chrome = "nullptr" 358 constructorID = "constructors::id::" 359 if self.descriptor.interface.hasInterfaceObject(): 360 constructorID += self.descriptor.name 361 else: 362 constructorID += "_ID_Count" 363 prototypeID = "prototypes::id::" 364 if self.descriptor.interface.hasInterfacePrototypeObject(): 365 prototypeID += self.descriptor.name 366 else: 367 prototypeID += "_ID_Count" 368 parentProtoName = self.descriptor.parentPrototypeName 369 parentHooks = (toBindingNamespace(parentProtoName) + "::sNativePropertyHooks" 370 if parentProtoName else 'nullptr') 371 372 if self.descriptor.wantsXrayExpandoClass: 373 expandoClass = "&sXrayExpandoObjectClass" 374 else: 375 expandoClass = "&DefaultXrayExpandoObjectClass" 376 377 return fill( 378 """ 379 const NativePropertyHooks sNativePropertyHooks[] = { { 380 ${resolveOwnProperty}, 381 ${enumerateOwnProperties}, 382 ${deleteNamedProperty}, 383 { ${regular}, ${chrome} }, 384 ${prototypeID}, 385 ${constructorID}, 386 ${parentHooks}, 387 ${expandoClass} 388 } }; 389 """, 390 resolveOwnProperty=resolveOwnProperty, 391 enumerateOwnProperties=enumerateOwnProperties, 392 deleteNamedProperty=deleteNamedProperty, 393 regular=regular, 394 chrome=chrome, 395 prototypeID=prototypeID, 396 constructorID=constructorID, 397 parentHooks=parentHooks, 398 expandoClass=expandoClass) 399 400 401def NativePropertyHooks(descriptor): 402 return "&sEmptyNativePropertyHooks" if not descriptor.wantsXrays else "sNativePropertyHooks" 403 404 405def DOMClass(descriptor): 406 protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeNameChain] 407 # Pad out the list to the right length with _ID_Count so we 408 # guarantee that all the lists are the same length. _ID_Count 409 # is never the ID of any prototype, so it's safe to use as 410 # padding. 411 protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList))) 412 413 return fill( 414 """ 415 { ${protoChain} }, 416 IsBaseOf<nsISupports, ${nativeType} >::value, 417 ${hooks}, 418 FindAssociatedGlobalForNative<${nativeType}>::Get, 419 GetProtoObjectHandle, 420 GetCCParticipant<${nativeType}>::Get() 421 """, 422 protoChain=', '.join(protoList), 423 nativeType=descriptor.nativeType, 424 hooks=NativePropertyHooks(descriptor)) 425 426 427class CGDOMJSClass(CGThing): 428 """ 429 Generate a DOMJSClass for a given descriptor 430 """ 431 def __init__(self, descriptor): 432 CGThing.__init__(self) 433 self.descriptor = descriptor 434 435 def declare(self): 436 return "" 437 438 def define(self): 439 callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr' 440 objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr' 441 slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots 442 classFlags = "JSCLASS_IS_DOMJSCLASS | JSCLASS_FOREGROUND_FINALIZE | " 443 if self.descriptor.isGlobal(): 444 classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)" 445 traceHook = "JS_GlobalObjectTraceHook" 446 reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS" 447 else: 448 classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount 449 traceHook = 'nullptr' 450 reservedSlots = slotCount 451 if self.descriptor.interface.isProbablyShortLivingObject(): 452 classFlags += " | JSCLASS_SKIP_NURSERY_FINALIZE" 453 if self.descriptor.interface.getExtendedAttribute("NeedResolve"): 454 resolveHook = RESOLVE_HOOK_NAME 455 mayResolveHook = MAY_RESOLVE_HOOK_NAME 456 enumerateHook = ENUMERATE_HOOK_NAME 457 elif self.descriptor.isGlobal(): 458 resolveHook = "mozilla::dom::ResolveGlobal" 459 mayResolveHook = "mozilla::dom::MayResolveGlobal" 460 enumerateHook = "mozilla::dom::EnumerateGlobal" 461 else: 462 resolveHook = "nullptr" 463 mayResolveHook = "nullptr" 464 enumerateHook = "nullptr" 465 466 return fill( 467 """ 468 static const js::ClassOps sClassOps = { 469 ${addProperty}, /* addProperty */ 470 nullptr, /* delProperty */ 471 nullptr, /* getProperty */ 472 nullptr, /* setProperty */ 473 ${enumerate}, /* enumerate */ 474 ${resolve}, /* resolve */ 475 ${mayResolve}, /* mayResolve */ 476 ${finalize}, /* finalize */ 477 ${call}, /* call */ 478 nullptr, /* hasInstance */ 479 nullptr, /* construct */ 480 ${trace}, /* trace */ 481 }; 482 483 static const js::ClassExtension sClassExtension = { 484 nullptr, /* weakmapKeyDelegateOp */ 485 ${objectMoved} /* objectMovedOp */ 486 }; 487 488 static const DOMJSClass sClass = { 489 { "${name}", 490 ${flags}, 491 &sClassOps, 492 JS_NULL_CLASS_SPEC, 493 &sClassExtension, 494 JS_NULL_OBJECT_OPS 495 }, 496 $*{descriptor} 497 }; 498 static_assert(${instanceReservedSlots} == DOM_INSTANCE_RESERVED_SLOTS, 499 "Must have the right minimal number of reserved slots."); 500 static_assert(${reservedSlots} >= ${slotCount}, 501 "Must have enough reserved slots."); 502 """, 503 name=self.descriptor.interface.identifier.name, 504 flags=classFlags, 505 addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'nullptr', 506 enumerate=enumerateHook, 507 resolve=resolveHook, 508 mayResolve=mayResolveHook, 509 finalize=FINALIZE_HOOK_NAME, 510 call=callHook, 511 trace=traceHook, 512 objectMoved=objectMovedHook, 513 descriptor=DOMClass(self.descriptor), 514 instanceReservedSlots=INSTANCE_RESERVED_SLOTS, 515 reservedSlots=reservedSlots, 516 slotCount=slotCount) 517 518 519class CGDOMProxyJSClass(CGThing): 520 """ 521 Generate a DOMJSClass for a given proxy descriptor 522 """ 523 def __init__(self, descriptor): 524 CGThing.__init__(self) 525 self.descriptor = descriptor 526 527 def declare(self): 528 return "" 529 530 def define(self): 531 flags = ["JSCLASS_IS_DOMJSCLASS"] 532 # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because 533 # we don't want people ever adding that to any interface other than 534 # HTMLAllCollection. So just hardcode it here. 535 if self.descriptor.interface.identifier.name == "HTMLAllCollection": 536 flags.append("JSCLASS_EMULATES_UNDEFINED") 537 objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr' 538 return fill( 539 """ 540 static const js::ClassExtension sClassExtension = PROXY_MAKE_EXT( 541 ${objectMoved} 542 ); 543 544 static const DOMJSClass sClass = { 545 PROXY_CLASS_WITH_EXT("${name}", 546 ${flags}, 547 &sClassExtension), 548 $*{descriptor} 549 }; 550 """, 551 name=self.descriptor.interface.identifier.name, 552 flags=" | ".join(flags), 553 objectMoved=objectMovedHook, 554 descriptor=DOMClass(self.descriptor)) 555 556 557class CGXrayExpandoJSClass(CGThing): 558 """ 559 Generate a JSClass for an Xray expando object. This is only 560 needed if we have members in slots (for [Cached] or [StoreInSlot] 561 stuff). 562 """ 563 def __init__(self, descriptor): 564 assert descriptor.interface.totalMembersInSlots != 0 565 assert descriptor.wantsXrays 566 assert descriptor.wantsXrayExpandoClass 567 CGThing.__init__(self) 568 self.descriptor = descriptor; 569 570 def declare(self): 571 return "" 572 573 def define(self): 574 return fill( 575 """ 576 // This may allocate too many slots, because we only really need 577 // slots for our non-interface-typed members that we cache. But 578 // allocating slots only for those would make the slot index 579 // computations much more complicated, so let's do this the simple 580 // way for now. 581 DEFINE_XRAY_EXPANDO_CLASS(static, sXrayExpandoObjectClass, ${memberSlots}); 582 """, 583 memberSlots=self.descriptor.interface.totalMembersInSlots) 584 585 586def PrototypeIDAndDepth(descriptor): 587 prototypeID = "prototypes::id::" 588 if descriptor.interface.hasInterfacePrototypeObject(): 589 prototypeID += descriptor.interface.identifier.name 590 depth = "PrototypeTraits<%s>::Depth" % prototypeID 591 else: 592 prototypeID += "_ID_Count" 593 depth = "0" 594 return (prototypeID, depth) 595 596 597def InterfacePrototypeObjectProtoGetter(descriptor): 598 """ 599 Returns a tuple with two elements: 600 601 1) The name of the function to call to get the prototype to use for the 602 interface prototype object as a JSObject*. 603 604 2) The name of the function to call to get the prototype to use for the 605 interface prototype object as a JS::Handle<JSObject*> or None if no 606 such function exists. 607 """ 608 parentProtoName = descriptor.parentPrototypeName 609 if descriptor.hasNamedPropertiesObject: 610 protoGetter = "GetNamedPropertiesObject" 611 protoHandleGetter = None 612 elif parentProtoName is None: 613 if descriptor.interface.getExtendedAttribute("ArrayClass"): 614 protoGetter = "JS::GetRealmArrayPrototype" 615 elif descriptor.interface.getExtendedAttribute("ExceptionClass"): 616 protoGetter = "JS::GetRealmErrorPrototype" 617 elif descriptor.interface.isIteratorInterface(): 618 protoGetter = "JS::GetRealmIteratorPrototype" 619 else: 620 protoGetter = "JS::GetRealmObjectPrototype" 621 protoHandleGetter = None 622 else: 623 prefix = toBindingNamespace(parentProtoName) 624 protoGetter = prefix + "::GetProtoObject" 625 protoHandleGetter = prefix + "::GetProtoObjectHandle" 626 627 return (protoGetter, protoHandleGetter) 628 629 630class CGPrototypeJSClass(CGThing): 631 def __init__(self, descriptor, properties): 632 CGThing.__init__(self) 633 self.descriptor = descriptor 634 self.properties = properties 635 636 def declare(self): 637 # We're purely for internal consumption 638 return "" 639 640 def define(self): 641 prototypeID, depth = PrototypeIDAndDepth(self.descriptor) 642 slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE" 643 # Globals handle unforgeables directly in Wrap() instead of 644 # via a holder. 645 if (self.descriptor.hasUnforgeableMembers and 646 not self.descriptor.isGlobal()): 647 slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */" 648 (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor) 649 type = "eGlobalInterfacePrototype" if self.descriptor.isGlobal() else "eInterfacePrototype" 650 return fill( 651 """ 652 static const DOMIfaceAndProtoJSClass sPrototypeClass = { 653 { 654 "${name}Prototype", 655 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}), 656 JS_NULL_CLASS_OPS, 657 JS_NULL_CLASS_SPEC, 658 JS_NULL_CLASS_EXT, 659 JS_NULL_OBJECT_OPS 660 }, 661 ${type}, 662 false, 663 ${prototypeID}, 664 ${depth}, 665 ${hooks}, 666 "[object ${name}Prototype]", 667 ${protoGetter} 668 }; 669 """, 670 name=self.descriptor.interface.identifier.name, 671 slotCount=slotCount, 672 type=type, 673 hooks=NativePropertyHooks(self.descriptor), 674 prototypeID=prototypeID, 675 depth=depth, 676 protoGetter=protoGetter) 677 678 679def NeedsGeneratedHasInstance(descriptor): 680 assert descriptor.interface.hasInterfaceObject() 681 return descriptor.hasXPConnectImpls or descriptor.interface.isConsequential() 682 683 684def InterfaceObjectProtoGetter(descriptor, forXrays=False): 685 """ 686 Returns a tuple with two elements: 687 688 1) The name of the function to call to get the prototype to use for the 689 interface object as a JSObject*. 690 691 2) The name of the function to call to get the prototype to use for the 692 interface prototype as a JS::Handle<JSObject*> or None if no such 693 function exists. 694 """ 695 parentInterface = descriptor.interface.parent 696 if parentInterface: 697 assert not descriptor.interface.isNamespace() 698 parentIfaceName = parentInterface.identifier.name 699 parentDesc = descriptor.getDescriptor(parentIfaceName) 700 prefix = toBindingNamespace(parentDesc.name) 701 protoGetter = prefix + "::GetConstructorObject" 702 protoHandleGetter = prefix + "::GetConstructorObjectHandle" 703 elif descriptor.interface.isNamespace(): 704 if (forXrays or 705 not descriptor.interface.getExtendedAttribute("ProtoObjectHack")): 706 protoGetter = "JS::GetRealmObjectPrototype" 707 else: 708 protoGetter = "binding_detail::GetHackedNamespaceProtoObject" 709 protoHandleGetter = None 710 else: 711 protoGetter = "JS::GetRealmFunctionPrototype" 712 protoHandleGetter = None 713 return (protoGetter, protoHandleGetter) 714 715 716class CGInterfaceObjectJSClass(CGThing): 717 def __init__(self, descriptor, properties): 718 CGThing.__init__(self) 719 self.descriptor = descriptor 720 self.properties = properties 721 722 def declare(self): 723 # We're purely for internal consumption 724 return "" 725 726 def define(self): 727 if self.descriptor.interface.ctor(): 728 assert not self.descriptor.interface.isNamespace() 729 ctorname = CONSTRUCT_HOOK_NAME 730 elif self.descriptor.interface.isNamespace(): 731 ctorname = "nullptr" 732 else: 733 ctorname = "ThrowingConstructor" 734 needsHasInstance = ( 735 not NeedsGeneratedHasInstance(self.descriptor) and 736 self.descriptor.interface.hasInterfacePrototypeObject()) 737 738 prototypeID, depth = PrototypeIDAndDepth(self.descriptor) 739 slotCount = "DOM_INTERFACE_SLOTS_BASE" 740 if len(self.descriptor.interface.namedConstructors) > 0: 741 slotCount += (" + %i /* slots for the named constructors */" % 742 len(self.descriptor.interface.namedConstructors)) 743 (protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor, 744 forXrays=True) 745 746 if ctorname == "ThrowingConstructor": 747 ret = "" 748 classOpsPtr = "&sBoringInterfaceObjectClassClassOps" 749 elif ctorname == "nullptr": 750 ret = "" 751 classOpsPtr = "JS_NULL_CLASS_OPS" 752 else: 753 ret = fill( 754 """ 755 static const js::ClassOps sInterfaceObjectClassOps = { 756 nullptr, /* addProperty */ 757 nullptr, /* delProperty */ 758 nullptr, /* getProperty */ 759 nullptr, /* setProperty */ 760 nullptr, /* enumerate */ 761 nullptr, /* resolve */ 762 nullptr, /* mayResolve */ 763 nullptr, /* finalize */ 764 ${ctorname}, /* call */ 765 nullptr, /* hasInstance */ 766 ${ctorname}, /* construct */ 767 nullptr, /* trace */ 768 }; 769 770 """, 771 ctorname=ctorname) 772 classOpsPtr = "&sInterfaceObjectClassOps" 773 774 if self.descriptor.interface.isNamespace(): 775 classString = self.descriptor.interface.getExtendedAttribute("ClassString") 776 if classString is None: 777 classString = "Object" 778 else: 779 classString = classString[0] 780 toStringResult = "[object %s]" % classString 781 objectOps = "JS_NULL_OBJECT_OPS" 782 else: 783 classString = "Function" 784 toStringResult = ("function %s() {\\n [native code]\\n}" % 785 self.descriptor.interface.identifier.name) 786 # We need non-default ObjectOps so we can actually make 787 # use of our toStringResult. 788 objectOps = "&sInterfaceObjectClassObjectOps" 789 790 ret = ret + fill( 791 """ 792 static const DOMIfaceAndProtoJSClass sInterfaceObjectClass = { 793 { 794 "${classString}", 795 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}), 796 ${classOpsPtr}, 797 JS_NULL_CLASS_SPEC, 798 JS_NULL_CLASS_EXT, 799 ${objectOps} 800 }, 801 eInterface, 802 ${needsHasInstance}, 803 ${prototypeID}, 804 ${depth}, 805 ${hooks}, 806 "${toStringResult}", 807 ${protoGetter} 808 }; 809 """, 810 classString=classString, 811 slotCount=slotCount, 812 classOpsPtr=classOpsPtr, 813 hooks=NativePropertyHooks(self.descriptor), 814 objectOps=objectOps, 815 needsHasInstance=toStringBool(needsHasInstance), 816 prototypeID=prototypeID, 817 depth=depth, 818 toStringResult=toStringResult, 819 protoGetter=protoGetter) 820 return ret 821 822class CGList(CGThing): 823 """ 824 Generate code for a list of GCThings. Just concatenates them together, with 825 an optional joiner string. "\n" is a common joiner. 826 """ 827 def __init__(self, children, joiner=""): 828 CGThing.__init__(self) 829 # Make a copy of the kids into a list, because if someone passes in a 830 # generator we won't be able to both declare and define ourselves, or 831 # define ourselves more than once! 832 self.children = list(children) 833 self.joiner = joiner 834 835 def append(self, child): 836 self.children.append(child) 837 838 def prepend(self, child): 839 self.children.insert(0, child) 840 841 def extend(self, kids): 842 self.children.extend(kids) 843 844 def join(self, iterable): 845 return self.joiner.join(s for s in iterable if len(s) > 0) 846 847 def declare(self): 848 return self.join(child.declare() for child in self.children if child is not None) 849 850 def define(self): 851 return self.join(child.define() for child in self.children if child is not None) 852 853 def deps(self): 854 deps = set() 855 for child in self.children: 856 if child is None: 857 continue 858 deps = deps.union(child.deps()) 859 return deps 860 861 def __len__(self): 862 return len(self.children) 863 864 865class CGGeneric(CGThing): 866 """ 867 A class that spits out a fixed string into the codegen. Can spit out a 868 separate string for the declaration too. 869 """ 870 def __init__(self, define="", declare=""): 871 self.declareText = declare 872 self.defineText = define 873 874 def declare(self): 875 return self.declareText 876 877 def define(self): 878 return self.defineText 879 880 def deps(self): 881 return set() 882 883 884class CGIndenter(CGThing): 885 """ 886 A class that takes another CGThing and generates code that indents that 887 CGThing by some number of spaces. The default indent is two spaces. 888 """ 889 def __init__(self, child, indentLevel=2, declareOnly=False): 890 assert isinstance(child, CGThing) 891 CGThing.__init__(self) 892 self.child = child 893 self.indentLevel = indentLevel 894 self.declareOnly = declareOnly 895 896 def declare(self): 897 return indent(self.child.declare(), self.indentLevel) 898 899 def define(self): 900 defn = self.child.define() 901 if self.declareOnly: 902 return defn 903 else: 904 return indent(defn, self.indentLevel) 905 906 907class CGWrapper(CGThing): 908 """ 909 Generic CGThing that wraps other CGThings with pre and post text. 910 """ 911 def __init__(self, child, pre="", post="", declarePre=None, 912 declarePost=None, definePre=None, definePost=None, 913 declareOnly=False, defineOnly=False, reindent=False): 914 CGThing.__init__(self) 915 self.child = child 916 self.declarePre = declarePre or pre 917 self.declarePost = declarePost or post 918 self.definePre = definePre or pre 919 self.definePost = definePost or post 920 self.declareOnly = declareOnly 921 self.defineOnly = defineOnly 922 self.reindent = reindent 923 924 def declare(self): 925 if self.defineOnly: 926 return '' 927 decl = self.child.declare() 928 if self.reindent: 929 decl = self.reindentString(decl, self.declarePre) 930 return self.declarePre + decl + self.declarePost 931 932 def define(self): 933 if self.declareOnly: 934 return '' 935 defn = self.child.define() 936 if self.reindent: 937 defn = self.reindentString(defn, self.definePre) 938 return self.definePre + defn + self.definePost 939 940 @staticmethod 941 def reindentString(stringToIndent, widthString): 942 # We don't use lineStartDetector because we don't want to 943 # insert whitespace at the beginning of our _first_ line. 944 # Use the length of the last line of width string, in case 945 # it is a multiline string. 946 lastLineWidth = len(widthString.splitlines()[-1]) 947 return stripTrailingWhitespace( 948 stringToIndent.replace("\n", "\n" + (" " * lastLineWidth))) 949 950 def deps(self): 951 return self.child.deps() 952 953 954class CGIfWrapper(CGList): 955 def __init__(self, child, condition): 956 CGList.__init__(self, [ 957 CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True), 958 CGIndenter(child), 959 CGGeneric("}\n") 960 ]) 961 962 963class CGIfElseWrapper(CGList): 964 def __init__(self, condition, ifTrue, ifFalse): 965 CGList.__init__(self, [ 966 CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True), 967 CGIndenter(ifTrue), 968 CGGeneric("} else {\n"), 969 CGIndenter(ifFalse), 970 CGGeneric("}\n") 971 ]) 972 973 974class CGElseChain(CGThing): 975 """ 976 Concatenate if statements in an if-else-if-else chain. 977 """ 978 def __init__(self, children): 979 self.children = [c for c in children if c is not None] 980 981 def declare(self): 982 assert False 983 984 def define(self): 985 if not self.children: 986 return "" 987 s = self.children[0].define() 988 assert s.endswith("\n") 989 for child in self.children[1:]: 990 code = child.define() 991 assert code.startswith("if") or code.startswith("{") 992 assert code.endswith("\n") 993 s = s.rstrip() + " else " + code 994 return s 995 996 997class CGTemplatedType(CGWrapper): 998 def __init__(self, templateName, child, isConst=False, isReference=False): 999 const = "const " if isConst else "" 1000 pre = "%s%s<" % (const, templateName) 1001 ref = "&" if isReference else "" 1002 post = ">%s" % ref 1003 CGWrapper.__init__(self, child, pre=pre, post=post) 1004 1005 1006class CGNamespace(CGWrapper): 1007 def __init__(self, namespace, child, declareOnly=False): 1008 pre = "namespace %s {\n" % namespace 1009 post = "} // namespace %s\n" % namespace 1010 CGWrapper.__init__(self, child, pre=pre, post=post, 1011 declareOnly=declareOnly) 1012 1013 @staticmethod 1014 def build(namespaces, child, declareOnly=False): 1015 """ 1016 Static helper method to build multiple wrapped namespaces. 1017 """ 1018 if not namespaces: 1019 return CGWrapper(child, declareOnly=declareOnly) 1020 inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly) 1021 return CGNamespace(namespaces[0], inner, declareOnly=declareOnly) 1022 1023 1024class CGIncludeGuard(CGWrapper): 1025 """ 1026 Generates include guards for a header. 1027 """ 1028 def __init__(self, prefix, child): 1029 """|prefix| is the filename without the extension.""" 1030 define = 'mozilla_dom_%s_h' % prefix 1031 CGWrapper.__init__(self, child, 1032 declarePre='#ifndef %s\n#define %s\n\n' % (define, define), 1033 declarePost='\n#endif // %s\n' % define) 1034 1035 1036class CGHeaders(CGWrapper): 1037 """ 1038 Generates the appropriate include statements. 1039 """ 1040 def __init__(self, descriptors, dictionaries, callbacks, 1041 callbackDescriptors, 1042 declareIncludes, defineIncludes, prefix, child, 1043 config=None, jsImplementedDescriptors=[]): 1044 """ 1045 Builds a set of includes to cover |descriptors|. 1046 1047 Also includes the files in |declareIncludes| in the header 1048 file and the files in |defineIncludes| in the .cpp. 1049 1050 |prefix| contains the basename of the file that we generate include 1051 statements for. 1052 """ 1053 1054 # Determine the filenames for which we need headers. 1055 interfaceDeps = [d.interface for d in descriptors] 1056 ancestors = [] 1057 for iface in interfaceDeps: 1058 if iface.parent: 1059 # We're going to need our parent's prototype, to use as the 1060 # prototype of our prototype object. 1061 ancestors.append(iface.parent) 1062 # And if we have an interface object, we'll need the nearest 1063 # ancestor with an interface object too, so we can use its 1064 # interface object as the proto of our interface object. 1065 if iface.hasInterfaceObject(): 1066 parent = iface.parent 1067 while parent and not parent.hasInterfaceObject(): 1068 parent = parent.parent 1069 if parent: 1070 ancestors.append(parent) 1071 interfaceDeps.extend(ancestors) 1072 bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps) 1073 1074 # Grab all the implementation declaration files we need. 1075 implementationIncludes = set(d.headerFile for d in descriptors if d.needsHeaderInclude()) 1076 1077 # Grab the includes for checking hasInstance 1078 interfacesImplementingSelf = set() 1079 for d in descriptors: 1080 interfacesImplementingSelf |= d.interface.interfacesImplementingSelf 1081 implementationIncludes |= set(self.getDeclarationFilename(i) for i in 1082 interfacesImplementingSelf) 1083 1084 # Grab the includes for the things that involve XPCOM interfaces 1085 hasInstanceIncludes = set("nsIDOM" + d.interface.identifier.name + ".h" for d 1086 in descriptors if 1087 d.interface.hasInterfaceObject() and 1088 NeedsGeneratedHasInstance(d) and 1089 d.interface.hasInterfacePrototypeObject()) 1090 if len(hasInstanceIncludes) > 0: 1091 hasInstanceIncludes.add("nsContentUtils.h") 1092 1093 # Now find all the things we'll need as arguments because we 1094 # need to wrap or unwrap them. 1095 bindingHeaders = set() 1096 declareIncludes = set(declareIncludes) 1097 1098 def addHeadersForType((t, dictionary)): 1099 """ 1100 Add the relevant headers for this type. We use dictionary, if 1101 passed, to decide what to do with interface types. 1102 """ 1103 # Dictionaries have members that need to be actually 1104 # declared, not just forward-declared. 1105 if dictionary: 1106 headerSet = declareIncludes 1107 else: 1108 headerSet = bindingHeaders 1109 if t.nullable(): 1110 # Need to make sure that Nullable as a dictionary 1111 # member works. 1112 headerSet.add("mozilla/dom/Nullable.h") 1113 unrolled = t.unroll() 1114 if unrolled.isUnion(): 1115 headerSet.add(self.getUnionDeclarationFilename(config, unrolled)) 1116 bindingHeaders.add("mozilla/dom/UnionConversions.h") 1117 elif unrolled.isDate(): 1118 if dictionary or jsImplementedDescriptors: 1119 declareIncludes.add("mozilla/dom/Date.h") 1120 else: 1121 bindingHeaders.add("mozilla/dom/Date.h") 1122 elif unrolled.isInterface(): 1123 if unrolled.isSpiderMonkeyInterface(): 1124 bindingHeaders.add("jsfriendapi.h") 1125 if jsImplementedDescriptors: 1126 # Since we can't forward-declare typed array types 1127 # (because they're typedefs), we have to go ahead and 1128 # just include their header if we need to have functions 1129 # taking references to them declared in that header. 1130 headerSet = declareIncludes 1131 headerSet.add("mozilla/dom/TypedArray.h") 1132 else: 1133 try: 1134 typeDesc = config.getDescriptor(unrolled.inner.identifier.name) 1135 except NoSuchDescriptorError: 1136 return 1137 # Dictionaries with interface members rely on the 1138 # actual class definition of that interface member 1139 # being visible in the binding header, because they 1140 # store them in RefPtr and have inline 1141 # constructors/destructors. 1142 # 1143 # XXXbz maybe dictionaries with interface members 1144 # should just have out-of-line constructors and 1145 # destructors? 1146 headerSet.add(typeDesc.headerFile) 1147 elif unrolled.isDictionary(): 1148 headerSet.add(self.getDeclarationFilename(unrolled.inner)) 1149 elif unrolled.isCallback(): 1150 headerSet.add(self.getDeclarationFilename(unrolled.callback)) 1151 elif unrolled.isFloat() and not unrolled.isUnrestricted(): 1152 # Restricted floats are tested for finiteness 1153 bindingHeaders.add("mozilla/FloatingPoint.h") 1154 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h") 1155 elif unrolled.isEnum(): 1156 filename = self.getDeclarationFilename(unrolled.inner) 1157 declareIncludes.add(filename) 1158 elif unrolled.isPrimitive(): 1159 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h") 1160 elif unrolled.isMozMap(): 1161 if dictionary or jsImplementedDescriptors: 1162 declareIncludes.add("mozilla/dom/MozMap.h") 1163 else: 1164 bindingHeaders.add("mozilla/dom/MozMap.h") 1165 # Also add headers for the type the MozMap is 1166 # parametrized over, if needed. 1167 addHeadersForType((t.inner, dictionary)) 1168 1169 map(addHeadersForType, 1170 getAllTypes(descriptors + callbackDescriptors, dictionaries, 1171 callbacks)) 1172 1173 # Now make sure we're not trying to include the header from inside itself 1174 declareIncludes.discard(prefix + ".h") 1175 1176 def addHeaderForFunc(func, desc): 1177 if func is None: 1178 return 1179 # Include the right class header, which we can only do 1180 # if this is a class member function. 1181 if desc is not None and not desc.headerIsDefault: 1182 # An explicit header file was provided, assume that we know 1183 # what we're doing. 1184 return 1185 1186 if "::" in func: 1187 # Strip out the function name and convert "::" to "/" 1188 bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h") 1189 1190 # Now for non-callback descriptors make sure we include any 1191 # headers needed by Func declarations and other things like that. 1192 for desc in descriptors: 1193 # If this is an iterator interface generated for a seperate 1194 # iterable interface, skip generating type includes, as we have 1195 # what we need in IterableIterator.h 1196 if desc.interface.isExternal() or desc.interface.isIteratorInterface(): 1197 continue 1198 1199 for m in desc.interface.members: 1200 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), desc) 1201 staticTypeOverride = PropertyDefiner.getStringAttr(m, "StaticClassOverride") 1202 if staticTypeOverride: 1203 bindingHeaders.add("/".join(staticTypeOverride.split("::")) + ".h") 1204 # getExtendedAttribute() returns a list, extract the entry. 1205 funcList = desc.interface.getExtendedAttribute("Func") 1206 if funcList is not None: 1207 addHeaderForFunc(funcList[0], desc) 1208 1209 if desc.interface.maplikeOrSetlikeOrIterable: 1210 # We need ToJSValue.h for maplike/setlike type conversions 1211 bindingHeaders.add("mozilla/dom/ToJSValue.h") 1212 # Add headers for the key and value types of the 1213 # maplike/setlike/iterable, since they'll be needed for 1214 # convenience functions 1215 if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType(): 1216 addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType, 1217 None)) 1218 if desc.interface.maplikeOrSetlikeOrIterable.hasValueType(): 1219 addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.valueType, 1220 None)) 1221 1222 for d in dictionaries: 1223 if d.parent: 1224 declareIncludes.add(self.getDeclarationFilename(d.parent)) 1225 bindingHeaders.add(self.getDeclarationFilename(d)) 1226 for m in d.members: 1227 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), 1228 None) 1229 # No need to worry about Func on members of ancestors, because that 1230 # will happen automatically in whatever files those ancestors live 1231 # in. 1232 1233 for c in callbacks: 1234 bindingHeaders.add(self.getDeclarationFilename(c)) 1235 1236 for c in callbackDescriptors: 1237 bindingHeaders.add(self.getDeclarationFilename(c.interface)) 1238 1239 if len(callbacks) != 0: 1240 # We need CallbackFunction to serve as our parent class 1241 declareIncludes.add("mozilla/dom/CallbackFunction.h") 1242 # And we need ToJSValue.h so we can wrap "this" objects 1243 declareIncludes.add("mozilla/dom/ToJSValue.h") 1244 1245 if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0: 1246 # We need CallbackInterface to serve as our parent class 1247 declareIncludes.add("mozilla/dom/CallbackInterface.h") 1248 # And we need ToJSValue.h so we can wrap "this" objects 1249 declareIncludes.add("mozilla/dom/ToJSValue.h") 1250 1251 # Also need to include the headers for ancestors of 1252 # JS-implemented interfaces. 1253 for jsImplemented in jsImplementedDescriptors: 1254 jsParent = jsImplemented.interface.parent 1255 if jsParent: 1256 parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name) 1257 declareIncludes.add(parentDesc.jsImplParentHeader) 1258 1259 # Let the machinery do its thing. 1260 def _includeString(includes): 1261 return ''.join(['#include "%s"\n' % i for i in includes]) + '\n' 1262 CGWrapper.__init__(self, child, 1263 declarePre=_includeString(sorted(declareIncludes)), 1264 definePre=_includeString(sorted(set(defineIncludes) | 1265 bindingIncludes | 1266 bindingHeaders | 1267 hasInstanceIncludes | 1268 implementationIncludes))) 1269 1270 @staticmethod 1271 def getDeclarationFilename(decl): 1272 # Use our local version of the header, not the exported one, so that 1273 # test bindings, which don't export, will work correctly. 1274 basename = os.path.basename(decl.filename()) 1275 return basename.replace('.webidl', 'Binding.h') 1276 1277 @staticmethod 1278 def getUnionDeclarationFilename(config, unionType): 1279 assert unionType.isUnion() 1280 assert unionType.unroll() == unionType 1281 # If a union is "defined" in multiple files, it goes in UnionTypes.h. 1282 if len(config.filenamesPerUnion[unionType.name]) > 1: 1283 return "mozilla/dom/UnionTypes.h" 1284 # If a union is defined by a built-in typedef, it also goes in 1285 # UnionTypes.h. 1286 assert len(config.filenamesPerUnion[unionType.name]) == 1 1287 if "<unknown>" in config.filenamesPerUnion[unionType.name]: 1288 return "mozilla/dom/UnionTypes.h" 1289 return CGHeaders.getDeclarationFilename(unionType) 1290 1291 1292def SortedDictValues(d): 1293 """ 1294 Returns a list of values from the dict sorted by key. 1295 """ 1296 return [v for k, v in sorted(d.items())] 1297 1298 1299def UnionsForFile(config, webIDLFile): 1300 """ 1301 Returns a list of union types for all union types that are only used in 1302 webIDLFile. If webIDLFile is None this will return the list of tuples for 1303 union types that are used in more than one WebIDL file. 1304 """ 1305 return config.unionsPerFilename.get(webIDLFile, []) 1306 1307 1308def UnionTypes(unionTypes, config): 1309 """ 1310 The unionTypes argument should be a list of union types. This is typically 1311 the list generated by UnionsForFile. 1312 1313 Returns a tuple containing a set of header filenames to include in 1314 the header for the types in unionTypes, a set of header filenames to 1315 include in the implementation file for the types in unionTypes, a set 1316 of tuples containing a type declaration and a boolean if the type is a 1317 struct for member types of the union, a list of traverse methods, 1318 unlink methods and a list of union types. These last three lists only 1319 contain unique union types. 1320 """ 1321 1322 headers = set() 1323 implheaders = set() 1324 declarations = set() 1325 unionStructs = dict() 1326 traverseMethods = dict() 1327 unlinkMethods = dict() 1328 1329 for t in unionTypes: 1330 name = str(t) 1331 if name not in unionStructs: 1332 unionStructs[name] = t 1333 1334 def addHeadersForType(f): 1335 if f.nullable(): 1336 headers.add("mozilla/dom/Nullable.h") 1337 isSequence = f.isSequence() 1338 f = f.unroll() 1339 if f.isInterface(): 1340 if f.isSpiderMonkeyInterface(): 1341 headers.add("jsfriendapi.h") 1342 headers.add("mozilla/dom/TypedArray.h") 1343 else: 1344 try: 1345 typeDesc = config.getDescriptor(f.inner.identifier.name) 1346 except NoSuchDescriptorError: 1347 return 1348 if typeDesc.interface.isCallback() or isSequence: 1349 # Callback interfaces always use strong refs, so 1350 # we need to include the right header to be able 1351 # to Release() in our inlined code. 1352 # 1353 # Similarly, sequences always contain strong 1354 # refs, so we'll need the header to handler 1355 # those. 1356 headers.add(typeDesc.headerFile) 1357 else: 1358 declarations.add((typeDesc.nativeType, False)) 1359 implheaders.add(typeDesc.headerFile) 1360 elif f.isDictionary(): 1361 # For a dictionary, we need to see its declaration in 1362 # UnionTypes.h so we have its sizeof and know how big to 1363 # make our union. 1364 headers.add(CGHeaders.getDeclarationFilename(f.inner)) 1365 # And if it needs rooting, we need RootedDictionary too 1366 if typeNeedsRooting(f): 1367 headers.add("mozilla/dom/RootedDictionary.h") 1368 elif f.isEnum(): 1369 # Need to see the actual definition of the enum, 1370 # unfortunately. 1371 headers.add(CGHeaders.getDeclarationFilename(f.inner)) 1372 elif f.isCallback(): 1373 # Callbacks always use strong refs, so we need to include 1374 # the right header to be able to Release() in our inlined 1375 # code. 1376 headers.add(CGHeaders.getDeclarationFilename(f.callback)) 1377 elif f.isMozMap(): 1378 headers.add("mozilla/dom/MozMap.h") 1379 # And add headers for the type we're parametrized over 1380 addHeadersForType(f.inner) 1381 1382 implheaders.add(CGHeaders.getUnionDeclarationFilename(config, t)) 1383 for f in t.flatMemberTypes: 1384 assert not f.nullable() 1385 addHeadersForType(f) 1386 1387 if idlTypeNeedsCycleCollection(t): 1388 declarations.add(("mozilla::dom::%s" % CGUnionStruct.unionTypeName(t, True), False)) 1389 traverseMethods[name] = CGCycleCollectionTraverseForOwningUnionMethod(t) 1390 unlinkMethods[name] = CGCycleCollectionUnlinkForOwningUnionMethod(t) 1391 1392 # The order of items in CGList is important. 1393 # Since the union structs friend the unlinkMethods, the forward-declaration 1394 # for these methods should come before the class declaration. Otherwise 1395 # some compilers treat the friend declaration as a forward-declaration in 1396 # the class scope. 1397 return (headers, implheaders, declarations, 1398 SortedDictValues(traverseMethods), SortedDictValues(unlinkMethods), 1399 SortedDictValues(unionStructs)) 1400 1401 1402def UnionConversions(unionTypes, config): 1403 """ 1404 The unionTypes argument should be a list of tuples, each containing two 1405 elements: a union type and a descriptor. This is typically the list 1406 generated by UnionsForFile. 1407 1408 Returns a tuple containing a list of headers and a CGThing to declare all 1409 union argument conversion helper structs. 1410 """ 1411 headers = set() 1412 unionConversions = dict() 1413 1414 for t in unionTypes: 1415 name = str(t) 1416 if name not in unionConversions: 1417 unionConversions[name] = CGUnionConversionStruct(t, config) 1418 1419 def addHeadersForType(f): 1420 f = f.unroll() 1421 if f.isInterface(): 1422 if f.isSpiderMonkeyInterface(): 1423 headers.add("jsfriendapi.h") 1424 headers.add("mozilla/dom/TypedArray.h") 1425 elif f.inner.isExternal(): 1426 try: 1427 typeDesc = config.getDescriptor(f.inner.identifier.name) 1428 except NoSuchDescriptorError: 1429 return 1430 headers.add(typeDesc.headerFile) 1431 else: 1432 headers.add(CGHeaders.getDeclarationFilename(f.inner)) 1433 elif f.isDictionary(): 1434 headers.add(CGHeaders.getDeclarationFilename(f.inner)) 1435 elif f.isPrimitive(): 1436 headers.add("mozilla/dom/PrimitiveConversions.h") 1437 elif f.isMozMap(): 1438 headers.add("mozilla/dom/MozMap.h") 1439 # And the internal type of the MozMap 1440 addHeadersForType(f.inner) 1441 1442 # We plan to include UnionTypes.h no matter what, so it's 1443 # OK if we throw it into the set here. 1444 headers.add(CGHeaders.getUnionDeclarationFilename(config, t)) 1445 1446 for f in t.flatMemberTypes: 1447 addHeadersForType(f) 1448 1449 return (headers, 1450 CGWrapper(CGList(SortedDictValues(unionConversions), "\n"), 1451 post="\n\n")) 1452 1453 1454class Argument(): 1455 """ 1456 A class for outputting the type and name of an argument 1457 """ 1458 def __init__(self, argType, name, default=None): 1459 self.argType = argType 1460 self.name = name 1461 self.default = default 1462 1463 def declare(self): 1464 string = self.argType + ' ' + self.name 1465 if self.default is not None: 1466 string += " = " + self.default 1467 return string 1468 1469 def define(self): 1470 return self.argType + ' ' + self.name 1471 1472 1473class CGAbstractMethod(CGThing): 1474 """ 1475 An abstract class for generating code for a method. Subclasses 1476 should override definition_body to create the actual code. 1477 1478 descriptor is the descriptor for the interface the method is associated with 1479 1480 name is the name of the method as a string 1481 1482 returnType is the IDLType of the return value 1483 1484 args is a list of Argument objects 1485 1486 inline should be True to generate an inline method, whose body is 1487 part of the declaration. 1488 1489 alwaysInline should be True to generate an inline method annotated with 1490 MOZ_ALWAYS_INLINE. 1491 1492 static should be True to generate a static method, which only has 1493 a definition. 1494 1495 If templateArgs is not None it should be a list of strings containing 1496 template arguments, and the function will be templatized using those 1497 arguments. 1498 """ 1499 def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, static=False, templateArgs=None): 1500 CGThing.__init__(self) 1501 self.descriptor = descriptor 1502 self.name = name 1503 self.returnType = returnType 1504 self.args = args 1505 self.inline = inline 1506 self.alwaysInline = alwaysInline 1507 self.static = static 1508 self.templateArgs = templateArgs 1509 1510 def _argstring(self, declare): 1511 return ', '.join([a.declare() if declare else a.define() for a in self.args]) 1512 1513 def _template(self): 1514 if self.templateArgs is None: 1515 return '' 1516 return 'template <%s>\n' % ', '.join(self.templateArgs) 1517 1518 def _decorators(self): 1519 decorators = [] 1520 if self.alwaysInline: 1521 decorators.append('MOZ_ALWAYS_INLINE') 1522 elif self.inline: 1523 decorators.append('inline') 1524 if self.static: 1525 decorators.append('static') 1526 decorators.append(self.returnType) 1527 maybeNewline = " " if self.inline else "\n" 1528 return ' '.join(decorators) + maybeNewline 1529 1530 def declare(self): 1531 if self.inline: 1532 return self._define(True) 1533 return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring(True)) 1534 1535 def indent_body(self, body): 1536 """ 1537 Indent the code returned by self.definition_body(). Most classes 1538 simply indent everything two spaces. This is here for 1539 CGRegisterProtos, which needs custom indentation. 1540 """ 1541 return indent(body) 1542 1543 def _define(self, fromDeclare=False): 1544 return (self.definition_prologue(fromDeclare) + 1545 self.indent_body(self.definition_body()) + 1546 self.definition_epilogue()) 1547 1548 def define(self): 1549 return "" if self.inline else self._define() 1550 1551 def definition_prologue(self, fromDeclare): 1552 return "%s%s%s(%s)\n{\n" % (self._template(), self._decorators(), 1553 self.name, self._argstring(fromDeclare)) 1554 1555 def definition_epilogue(self): 1556 return "}\n" 1557 1558 def definition_body(self): 1559 assert False # Override me! 1560 1561 1562class CGAbstractStaticMethod(CGAbstractMethod): 1563 """ 1564 Abstract base class for codegen of implementation-only (no 1565 declaration) static methods. 1566 """ 1567 def __init__(self, descriptor, name, returnType, args): 1568 CGAbstractMethod.__init__(self, descriptor, name, returnType, args, 1569 inline=False, static=True) 1570 1571 def declare(self): 1572 # We only have implementation 1573 return "" 1574 1575 1576class CGAbstractClassHook(CGAbstractStaticMethod): 1577 """ 1578 Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw 1579 'this' unwrapping as it assumes that the unwrapped type is always known. 1580 """ 1581 def __init__(self, descriptor, name, returnType, args): 1582 CGAbstractStaticMethod.__init__(self, descriptor, name, returnType, 1583 args) 1584 1585 def definition_body_prologue(self): 1586 return ("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" % 1587 (self.descriptor.nativeType, self.descriptor.nativeType)) 1588 1589 def definition_body(self): 1590 return self.definition_body_prologue() + self.generate_code() 1591 1592 def generate_code(self): 1593 assert False # Override me! 1594 1595 1596class CGGetJSClassMethod(CGAbstractMethod): 1597 def __init__(self, descriptor): 1598 CGAbstractMethod.__init__(self, descriptor, 'GetJSClass', 'const JSClass*', 1599 []) 1600 1601 def definition_body(self): 1602 return "return sClass.ToJSClass();\n" 1603 1604 1605class CGAddPropertyHook(CGAbstractClassHook): 1606 """ 1607 A hook for addProperty, used to preserve our wrapper from GC. 1608 """ 1609 def __init__(self, descriptor): 1610 args = [Argument('JSContext*', 'cx'), 1611 Argument('JS::Handle<JSObject*>', 'obj'), 1612 Argument('JS::Handle<jsid>', 'id'), 1613 Argument('JS::Handle<JS::Value>', 'val')] 1614 CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME, 1615 'bool', args) 1616 1617 def generate_code(self): 1618 assert self.descriptor.wrapperCache 1619 return dedent(""" 1620 // We don't want to preserve if we don't have a wrapper, and we 1621 // obviously can't preserve if we're not initialized. 1622 if (self && self->GetWrapperPreserveColor()) { 1623 PreserveWrapper(self); 1624 } 1625 return true; 1626 """) 1627 1628 1629def finalizeHook(descriptor, hookName, freeOp): 1630 finalize = "" 1631 if descriptor.wrapperCache: 1632 finalize += "ClearWrapper(self, self);\n" 1633 if descriptor.interface.getExtendedAttribute('OverrideBuiltins'): 1634 finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n" 1635 if descriptor.isGlobal(): 1636 finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), obj);\n" % freeOp 1637 finalize += ("AddForDeferredFinalization<%s>(self);\n" % 1638 descriptor.nativeType) 1639 return CGIfWrapper(CGGeneric(finalize), "self") 1640 1641 1642class CGClassFinalizeHook(CGAbstractClassHook): 1643 """ 1644 A hook for finalize, used to release our native object. 1645 """ 1646 def __init__(self, descriptor): 1647 args = [Argument('js::FreeOp*', 'fop'), Argument('JSObject*', 'obj')] 1648 CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, 1649 'void', args) 1650 1651 def generate_code(self): 1652 return finalizeHook(self.descriptor, self.name, self.args[0].name).define() 1653 1654 1655class CGClassObjectMovedHook(CGAbstractClassHook): 1656 """ 1657 A hook for objectMovedOp, used to update the wrapper cache when an object it 1658 is holding moves. 1659 """ 1660 def __init__(self, descriptor): 1661 args = [Argument('JSObject*', 'obj'), Argument('const JSObject*', 'old')] 1662 CGAbstractClassHook.__init__(self, descriptor, OBJECT_MOVED_HOOK_NAME, 1663 'void', args) 1664 1665 def generate_code(self): 1666 assert self.descriptor.wrapperCache 1667 return CGIfWrapper(CGGeneric("UpdateWrapper(self, self, obj, old);\n"), 1668 "self").define() 1669 1670 1671def JSNativeArguments(): 1672 return [Argument('JSContext*', 'cx'), 1673 Argument('unsigned', 'argc'), 1674 Argument('JS::Value*', 'vp')] 1675 1676 1677class CGClassConstructor(CGAbstractStaticMethod): 1678 """ 1679 JS-visible constructor for our objects 1680 """ 1681 def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME): 1682 CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', 1683 JSNativeArguments()) 1684 self._ctor = ctor 1685 1686 def define(self): 1687 if not self._ctor: 1688 return "" 1689 return CGAbstractStaticMethod.define(self) 1690 1691 def definition_body(self): 1692 return self.generate_code() 1693 1694 def generate_code(self): 1695 # [ChromeOnly] interfaces may only be constructed by chrome. 1696 chromeOnlyCheck = "" 1697 if isChromeOnly(self._ctor): 1698 chromeOnlyCheck = dedent(""" 1699 if (!nsContentUtils::ThreadsafeIsCallerChrome()) { 1700 return ThrowingConstructor(cx, argc, vp); 1701 } 1702 1703 """) 1704 1705 # Additionally, we want to throw if a caller does a bareword invocation 1706 # of a constructor without |new|. We don't enforce this for chrome in 1707 # realease builds to avoid the addon compat fallout of making that 1708 # change. See bug 916644. 1709 # 1710 # Figure out the name of our constructor for error reporting purposes. 1711 # For unnamed webidl constructors, identifier.name is "constructor" but 1712 # the name JS sees is the interface name; for named constructors 1713 # identifier.name is the actual name. 1714 name = self._ctor.identifier.name 1715 if name != "constructor": 1716 ctorName = name 1717 else: 1718 ctorName = self.descriptor.interface.identifier.name 1719 1720 preamble = fill( 1721 """ 1722 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1723 JS::Rooted<JSObject*> obj(cx, &args.callee()); 1724 $*{chromeOnlyCheck} 1725 if (!args.isConstructing()) { 1726 // XXXbz wish I could get the name from the callee instead of 1727 // Adding more relocations 1728 return ThrowConstructorWithoutNew(cx, "${ctorName}"); 1729 } 1730 JS::Rooted<JSObject*> desiredProto(cx); 1731 if (!GetDesiredProto(cx, args, &desiredProto)) { 1732 return false; 1733 } 1734 """, 1735 chromeOnlyCheck=chromeOnlyCheck, 1736 ctorName=ctorName) 1737 1738 name = self._ctor.identifier.name 1739 nativeName = MakeNativeName(self.descriptor.binaryNameFor(name)) 1740 callGenerator = CGMethodCall(nativeName, True, self.descriptor, 1741 self._ctor, isConstructor=True, 1742 constructorName=ctorName) 1743 return preamble + "\n" + callGenerator.define() 1744 1745 1746# Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod. 1747class CGConstructNavigatorObject(CGAbstractMethod): 1748 """ 1749 Construct a new JS-implemented WebIDL DOM object, for use on navigator. 1750 """ 1751 def __init__(self, descriptor): 1752 args = [Argument('JSContext*', 'cx'), 1753 Argument('JS::Handle<JSObject*>', 'obj'), 1754 Argument('ErrorResult&', 'aRv')] 1755 rtype = 'already_AddRefed<%s>' % descriptor.name 1756 CGAbstractMethod.__init__(self, descriptor, "ConstructNavigatorObject", 1757 rtype, args) 1758 1759 def definition_body(self): 1760 if not self.descriptor.interface.isJSImplemented(): 1761 raise TypeError("Only JS-implemented classes are currently supported " 1762 "on navigator. See bug 856820.") 1763 1764 return dedent( 1765 """ 1766 GlobalObject global(cx, obj); 1767 if (global.Failed()) { 1768 aRv.Throw(NS_ERROR_FAILURE); 1769 return nullptr; 1770 } 1771 """) + genConstructorBody(self.descriptor) 1772 1773 1774def NamedConstructorName(m): 1775 return '_' + m.identifier.name 1776 1777 1778class CGNamedConstructors(CGThing): 1779 def __init__(self, descriptor): 1780 self.descriptor = descriptor 1781 CGThing.__init__(self) 1782 1783 def declare(self): 1784 return "" 1785 1786 def define(self): 1787 if len(self.descriptor.interface.namedConstructors) == 0: 1788 return "" 1789 1790 constructorID = "constructors::id::" 1791 if self.descriptor.interface.hasInterfaceObject(): 1792 constructorID += self.descriptor.name 1793 else: 1794 constructorID += "_ID_Count" 1795 1796 namedConstructors = "" 1797 for n in self.descriptor.interface.namedConstructors: 1798 namedConstructors += ( 1799 "{ \"%s\", { %s, &sNamedConstructorNativePropertyHooks }, %i },\n" % 1800 (n.identifier.name, NamedConstructorName(n), methodLength(n))) 1801 1802 return fill( 1803 """ 1804 const NativePropertyHooks sNamedConstructorNativePropertyHooks = { 1805 nullptr, 1806 nullptr, 1807 nullptr, 1808 { nullptr, nullptr }, 1809 prototypes::id::${name}, 1810 ${constructorID}, 1811 nullptr 1812 }; 1813 1814 static const NamedConstructor namedConstructors[] = { 1815 $*{namedConstructors} 1816 { nullptr, { nullptr, nullptr }, 0 } 1817 }; 1818 """, 1819 name=self.descriptor.name, 1820 constructorID=constructorID, 1821 namedConstructors=namedConstructors) 1822 1823 1824class CGHasInstanceHook(CGAbstractStaticMethod): 1825 def __init__(self, descriptor): 1826 args = [Argument('JSContext*', 'cx'), 1827 Argument('unsigned', 'argc'), 1828 Argument('JS::Value*', 'vp')] 1829 assert descriptor.interface.hasInterfaceObject() 1830 assert NeedsGeneratedHasInstance(descriptor) 1831 CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME, 1832 'bool', args) 1833 1834 def define(self): 1835 return CGAbstractStaticMethod.define(self) 1836 1837 def definition_body(self): 1838 return self.generate_code() 1839 1840 def generate_code(self): 1841 header = dedent(""" 1842 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1843 if (!args.get(0).isObject()) { 1844 args.rval().setBoolean(false); 1845 return true; 1846 } 1847 1848 JS::Rooted<JSObject*> instance(cx, &args[0].toObject()); 1849 """) 1850 if self.descriptor.interface.hasInterfacePrototypeObject(): 1851 return ( 1852 header + 1853 fill( 1854 """ 1855 1856 static_assert(IsBaseOf<nsISupports, ${nativeType}>::value, 1857 "HasInstance only works for nsISupports-based classes."); 1858 1859 bool ok = InterfaceHasInstance(cx, argc, vp); 1860 if (!ok || args.rval().toBoolean()) { 1861 return ok; 1862 } 1863 1864 // FIXME Limit this to chrome by checking xpc::AccessCheck::isChrome(obj). 1865 nsCOMPtr<nsISupports> native = 1866 xpc::UnwrapReflectorToISupports(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false)); 1867 nsCOMPtr<nsIDOM${name}> qiResult = do_QueryInterface(native); 1868 args.rval().setBoolean(!!qiResult); 1869 return true; 1870 """, 1871 nativeType=self.descriptor.nativeType, 1872 name=self.descriptor.interface.identifier.name)) 1873 1874 hasInstanceCode = dedent(""" 1875 1876 const DOMJSClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false)); 1877 if (!domClass) { 1878 // Not a DOM object, so certainly not an instance of this interface 1879 args.rval().setBoolean(false); 1880 return true; 1881 } 1882 """) 1883 if self.descriptor.interface.identifier.name == "ChromeWindow": 1884 setRval = "args.rval().setBoolean(UnwrapDOMObject<nsGlobalWindow>(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false))->IsChromeWindow())" 1885 else: 1886 setRval = "args.rval().setBoolean(true)" 1887 # Sort interaces implementing self by name so we get stable output. 1888 for iface in sorted(self.descriptor.interface.interfacesImplementingSelf, 1889 key=lambda iface: iface.identifier.name): 1890 hasInstanceCode += fill( 1891 """ 1892 1893 if (domClass->mInterfaceChain[PrototypeTraits<prototypes::id::${name}>::Depth] == prototypes::id::${name}) { 1894 ${setRval}; 1895 return true; 1896 } 1897 """, 1898 name=iface.identifier.name, 1899 setRval=setRval) 1900 hasInstanceCode += ("args.rval().setBoolean(false);\n" 1901 "return true;\n") 1902 return header + hasInstanceCode 1903 1904 1905def isChromeOnly(m): 1906 return m.getExtendedAttribute("ChromeOnly") 1907 1908 1909class MemberCondition: 1910 """ 1911 An object representing the condition for a member to actually be 1912 exposed. Any of the arguments can be None. If not 1913 None, they should have the following types: 1914 1915 pref: The name of the preference. 1916 func: The name of the function. 1917 secureContext: A bool indicating whether a secure context is required. 1918 nonExposedGlobals: A set of names of globals. Can be empty, in which case 1919 it's treated the same way as None. 1920 """ 1921 def __init__(self, pref=None, func=None, secureContext=False, 1922 nonExposedGlobals=None): 1923 assert pref is None or isinstance(pref, str) 1924 assert func is None or isinstance(func, str) 1925 assert isinstance(secureContext, bool) 1926 assert nonExposedGlobals is None or isinstance(nonExposedGlobals, set) 1927 self.pref = pref 1928 self.secureContext = secureContext 1929 1930 def toFuncPtr(val): 1931 if val is None: 1932 return "nullptr" 1933 return "&" + val 1934 self.func = toFuncPtr(func) 1935 1936 if nonExposedGlobals: 1937 # Nonempty set 1938 self.nonExposedGlobals = " | ".join( 1939 map(lambda g: "GlobalNames::%s" % g, 1940 sorted(nonExposedGlobals))) 1941 else: 1942 self.nonExposedGlobals = "0" 1943 1944 def __eq__(self, other): 1945 return (self.pref == other.pref and self.func == other.func and 1946 self.secureContext == other.secureContext and 1947 self.nonExposedGlobals == other.nonExposedGlobals) 1948 1949 def __ne__(self, other): 1950 return not self.__eq__(other) 1951 1952 def hasDisablers(self): 1953 return (self.pref is not None or 1954 self.secureContext or 1955 self.func != "nullptr" or 1956 self.nonExposedGlobals != "0") 1957 1958 1959class PropertyDefiner: 1960 """ 1961 A common superclass for defining things on prototype objects. 1962 1963 Subclasses should implement generateArray to generate the actual arrays of 1964 things we're defining. They should also set self.chrome to the list of 1965 things only exposed to chrome and self.regular to the list of things exposed 1966 to both chrome and web pages. 1967 """ 1968 def __init__(self, descriptor, name): 1969 self.descriptor = descriptor 1970 self.name = name 1971 # self.prefCacheData will store an array of (prefname, bool*) 1972 # pairs for our bool var caches. generateArray will fill it 1973 # in as needed. 1974 self.prefCacheData = [] 1975 1976 def hasChromeOnly(self): 1977 return len(self.chrome) > 0 1978 1979 def hasNonChromeOnly(self): 1980 return len(self.regular) > 0 1981 1982 def variableName(self, chrome): 1983 if chrome: 1984 if self.hasChromeOnly(): 1985 return "sChrome" + self.name 1986 else: 1987 if self.hasNonChromeOnly(): 1988 return "s" + self.name 1989 return "nullptr" 1990 1991 def usedForXrays(self): 1992 return self.descriptor.wantsXrays 1993 1994 def __str__(self): 1995 # We only need to generate id arrays for things that will end 1996 # up used via ResolveProperty or EnumerateProperties. 1997 str = self.generateArray(self.regular, self.variableName(False), 1998 self.usedForXrays()) 1999 if self.hasChromeOnly(): 2000 str += self.generateArray(self.chrome, self.variableName(True), 2001 self.usedForXrays()) 2002 return str 2003 2004 @staticmethod 2005 def getStringAttr(member, name): 2006 attr = member.getExtendedAttribute(name) 2007 if attr is None: 2008 return None 2009 # It's a list of strings 2010 assert len(attr) == 1 2011 assert attr[0] is not None 2012 return attr[0] 2013 2014 @staticmethod 2015 def getControllingCondition(interfaceMember, descriptor): 2016 interface = descriptor.interface 2017 nonExposureSet = interface.exposureSet - interfaceMember.exposureSet 2018 2019 return MemberCondition( 2020 PropertyDefiner.getStringAttr(interfaceMember, 2021 "Pref"), 2022 PropertyDefiner.getStringAttr(interfaceMember, 2023 "Func"), 2024 interfaceMember.getExtendedAttribute("SecureContext") is not None, 2025 nonExposureSet) 2026 2027 def generatePrefableArray(self, array, name, specFormatter, specTerminator, 2028 specType, getCondition, getDataTuple, doIdArrays): 2029 """ 2030 This method generates our various arrays. 2031 2032 array is an array of interface members as passed to generateArray 2033 2034 name is the name as passed to generateArray 2035 2036 specFormatter is a function that takes a single argument, a tuple, 2037 and returns a string, a spec array entry 2038 2039 specTerminator is a terminator for the spec array (inserted every time 2040 our controlling pref changes and at the end of the array) 2041 2042 specType is the actual typename of our spec 2043 2044 getCondition is a callback function that takes an array entry and 2045 returns the corresponding MemberCondition. 2046 2047 getDataTuple is a callback function that takes an array entry and 2048 returns a tuple suitable to be passed to specFormatter. 2049 """ 2050 2051 # We want to generate a single list of specs, but with specTerminator 2052 # inserted at every point where the pref name controlling the member 2053 # changes. That will make sure the order of the properties as exposed 2054 # on the interface and interface prototype objects does not change when 2055 # pref control is added to members while still allowing us to define all 2056 # the members in the smallest number of JSAPI calls. 2057 assert len(array) != 0 2058 # So we won't put a specTerminator at the very front of the list: 2059 lastCondition = getCondition(array[0], self.descriptor) 2060 2061 specs = [] 2062 disablers = [] 2063 prefableSpecs = [] 2064 2065 disablersTemplate = dedent( 2066 """ 2067 static PrefableDisablers %s_disablers%d = { 2068 true, %s, %s, %s 2069 }; 2070 """) 2071 prefableWithDisablersTemplate = ' { &%s_disablers%d, &%s_specs[%d] }' 2072 prefableWithoutDisablersTemplate = ' { nullptr, &%s_specs[%d] }' 2073 prefCacheTemplate = '&%s[%d].disablers->enabled' 2074 2075 def switchToCondition(props, condition): 2076 # Remember the info about where our pref-controlled 2077 # booleans live. 2078 if condition.pref is not None: 2079 props.prefCacheData.append( 2080 (condition.pref, 2081 prefCacheTemplate % (name, len(prefableSpecs)))) 2082 # Set up pointers to the new sets of specs inside prefableSpecs 2083 if condition.hasDisablers(): 2084 prefableSpecs.append(prefableWithDisablersTemplate % 2085 (name, len(specs), name, len(specs))) 2086 disablers.append(disablersTemplate % 2087 (name, len(specs), 2088 toStringBool(condition.secureContext), 2089 condition.nonExposedGlobals, 2090 condition.func)) 2091 else: 2092 prefableSpecs.append(prefableWithoutDisablersTemplate % 2093 (name, len(specs))) 2094 2095 switchToCondition(self, lastCondition) 2096 2097 for member in array: 2098 curCondition = getCondition(member, self.descriptor) 2099 if lastCondition != curCondition: 2100 # Terminate previous list 2101 specs.append(specTerminator) 2102 # And switch to our new condition 2103 switchToCondition(self, curCondition) 2104 lastCondition = curCondition 2105 # And the actual spec 2106 specs.append(specFormatter(getDataTuple(member))) 2107 specs.append(specTerminator) 2108 prefableSpecs.append(" { nullptr, nullptr }") 2109 2110 specType = "const " + specType 2111 arrays = fill( 2112 """ 2113 static ${specType} ${name}_specs[] = { 2114 ${specs} 2115 }; 2116 2117 ${disablers} 2118 // Can't be const because the pref-enabled boolean needs to be writable 2119 static Prefable<${specType}> ${name}[] = { 2120 ${prefableSpecs} 2121 }; 2122 2123 """, 2124 specType=specType, 2125 name=name, 2126 disablers='\n'.join(disablers), 2127 specs=',\n'.join(specs), 2128 prefableSpecs=',\n'.join(prefableSpecs)) 2129 if doIdArrays: 2130 arrays += "static jsid %s_ids[%i];\n\n" % (name, len(specs)) 2131 return arrays 2132 2133 2134# The length of a method is the minimum of the lengths of the 2135# argument lists of all its overloads. 2136def overloadLength(arguments): 2137 i = len(arguments) 2138 while i > 0 and arguments[i - 1].optional: 2139 i -= 1 2140 return i 2141 2142 2143def methodLength(method): 2144 signatures = method.signatures() 2145 return min(overloadLength(arguments) for retType, arguments in signatures) 2146 2147 2148def clearableCachedAttrs(descriptor): 2149 return (m for m in descriptor.interface.members if 2150 m.isAttr() and 2151 # Constants should never need clearing! 2152 m.dependsOn != "Nothing" and 2153 m.slotIndices is not None) 2154 2155 2156def MakeClearCachedValueNativeName(member): 2157 return "ClearCached%sValue" % MakeNativeName(member.identifier.name) 2158 2159 2160def MakeJSImplClearCachedValueNativeName(member): 2161 return "_" + MakeClearCachedValueNativeName(member) 2162 2163 2164def IDLToCIdentifier(name): 2165 return name.replace("-", "_") 2166 2167 2168class MethodDefiner(PropertyDefiner): 2169 """ 2170 A class for defining methods on a prototype object. 2171 """ 2172 def __init__(self, descriptor, name, static, unforgeable=False): 2173 assert not (static and unforgeable) 2174 PropertyDefiner.__init__(self, descriptor, name) 2175 2176 # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822 2177 # We should be able to check for special operations without an 2178 # identifier. For now we check if the name starts with __ 2179 2180 # Ignore non-static methods for interfaces without a proto object 2181 if descriptor.interface.hasInterfacePrototypeObject() or static: 2182 methods = [m for m in descriptor.interface.members if 2183 m.isMethod() and m.isStatic() == static and 2184 MemberIsUnforgeable(m, descriptor) == unforgeable and 2185 not m.isIdentifierLess()] 2186 else: 2187 methods = [] 2188 self.chrome = [] 2189 self.regular = [] 2190 for m in methods: 2191 if m.identifier.name == 'queryInterface': 2192 if m.isStatic(): 2193 raise TypeError("Legacy queryInterface member shouldn't be static") 2194 signatures = m.signatures() 2195 2196 def argTypeIsIID(arg): 2197 return arg.type.inner.isExternal() and arg.type.inner.identifier.name == 'IID' 2198 if len(signatures) > 1 or len(signatures[0][1]) > 1 or not argTypeIsIID(signatures[0][1][0]): 2199 raise TypeError("There should be only one queryInterface method with 1 argument of type IID") 2200 2201 # Make sure to not stick QueryInterface on abstract interfaces that 2202 # have hasXPConnectImpls (like EventTarget). So only put it on 2203 # interfaces that are concrete and all of whose ancestors are abstract. 2204 def allAncestorsAbstract(iface): 2205 if not iface.parent: 2206 return True 2207 desc = self.descriptor.getDescriptor(iface.parent.identifier.name) 2208 if desc.concrete: 2209 return False 2210 return allAncestorsAbstract(iface.parent) 2211 if (not self.descriptor.interface.hasInterfacePrototypeObject() or 2212 not self.descriptor.concrete or 2213 not allAncestorsAbstract(self.descriptor.interface)): 2214 raise TypeError("QueryInterface is only supported on " 2215 "interfaces that are concrete and all " 2216 "of whose ancestors are abstract: " + 2217 self.descriptor.name) 2218 condition = "WantsQueryInterface<%s>::Enabled" % descriptor.nativeType 2219 self.regular.append({ 2220 "name": 'QueryInterface', 2221 "methodInfo": False, 2222 "length": 1, 2223 "flags": "0", 2224 "condition": MemberCondition(func=condition) 2225 }) 2226 continue 2227 2228 # Iterable methods should be enumerable, maplike/setlike methods 2229 # should not. 2230 isMaplikeOrSetlikeMethod = (m.isMaplikeOrSetlikeOrIterableMethod() and 2231 (m.maplikeOrSetlikeOrIterable.isMaplike() or 2232 m.maplikeOrSetlikeOrIterable.isSetlike())) 2233 method = { 2234 "name": m.identifier.name, 2235 "methodInfo": not m.isStatic(), 2236 "length": methodLength(m), 2237 # Methods generated for a maplike/setlike declaration are not 2238 # enumerable. 2239 "flags": "JSPROP_ENUMERATE" if not isMaplikeOrSetlikeMethod else "0", 2240 "condition": PropertyDefiner.getControllingCondition(m, descriptor), 2241 "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"), 2242 "returnsPromise": m.returnsPromise(), 2243 "hasIteratorAlias": "@@iterator" in m.aliases 2244 } 2245 2246 if m.isStatic(): 2247 method["nativeName"] = CppKeywords.checkMethodName(IDLToCIdentifier(m.identifier.name)) 2248 2249 if isChromeOnly(m): 2250 self.chrome.append(method) 2251 else: 2252 self.regular.append(method) 2253 2254 # TODO: Once iterable is implemented, use tiebreak rules instead of 2255 # failing. Also, may be more tiebreak rules to implement once spec bug 2256 # is resolved. 2257 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592 2258 def hasIterator(methods, regular): 2259 return (any("@@iterator" in m.aliases for m in methods) or 2260 any("@@iterator" == r["name"] for r in regular)) 2261 2262 # Check whether we need to output an @@iterator due to having an indexed 2263 # getter. We only do this while outputting non-static and 2264 # non-unforgeable methods, since the @@iterator function will be 2265 # neither. 2266 if (not static and 2267 not unforgeable and 2268 descriptor.supportsIndexedProperties()): 2269 if hasIterator(methods, self.regular): 2270 raise TypeError("Cannot have indexed getter/attr on " 2271 "interface %s with other members " 2272 "that generate @@iterator, such as " 2273 "maplike/setlike or aliased functions." % 2274 self.descriptor.interface.identifier.name) 2275 self.regular.append({ 2276 "name": "@@iterator", 2277 "methodInfo": False, 2278 "selfHostedName": "ArrayValues", 2279 "length": 0, 2280 "flags": "JSPROP_ENUMERATE", 2281 "condition": MemberCondition() 2282 }) 2283 2284 if (static and 2285 not unforgeable and 2286 descriptor.interface.hasInterfaceObject() and 2287 NeedsGeneratedHasInstance(descriptor)): 2288 self.regular.append({ 2289 "name": "@@hasInstance", 2290 "methodInfo": False, 2291 "nativeName": HASINSTANCE_HOOK_NAME, 2292 "length": 1, 2293 # Flags match those of Function[Symbol.hasInstance] 2294 "flags": "JSPROP_READONLY | JSPROP_PERMANENT", 2295 "condition": MemberCondition() 2296 }) 2297 2298 # Generate the keys/values/entries aliases for value iterables. 2299 maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable 2300 if (not static and 2301 not unforgeable and 2302 maplikeOrSetlikeOrIterable and 2303 maplikeOrSetlikeOrIterable.isIterable() and 2304 maplikeOrSetlikeOrIterable.isValueIterator()): 2305 # Add our keys/values/entries/forEach 2306 self.regular.append({ 2307 "name": "keys", 2308 "methodInfo": False, 2309 "selfHostedName": "ArrayKeys", 2310 "length": 0, 2311 "flags": "JSPROP_ENUMERATE", 2312 "condition": PropertyDefiner.getControllingCondition(m, 2313 descriptor) 2314 }) 2315 self.regular.append({ 2316 "name": "values", 2317 "methodInfo": False, 2318 "selfHostedName": "ArrayValues", 2319 "length": 0, 2320 "flags": "JSPROP_ENUMERATE", 2321 "condition": PropertyDefiner.getControllingCondition(m, 2322 descriptor) 2323 }) 2324 self.regular.append({ 2325 "name": "entries", 2326 "methodInfo": False, 2327 "selfHostedName": "ArrayEntries", 2328 "length": 0, 2329 "flags": "JSPROP_ENUMERATE", 2330 "condition": PropertyDefiner.getControllingCondition(m, 2331 descriptor) 2332 }) 2333 self.regular.append({ 2334 "name": "forEach", 2335 "methodInfo": False, 2336 "selfHostedName": "ArrayForEach", 2337 "length": 1, 2338 "flags": "JSPROP_ENUMERATE", 2339 "condition": PropertyDefiner.getControllingCondition(m, 2340 descriptor) 2341 }) 2342 2343 if not static: 2344 stringifier = descriptor.operations['Stringifier'] 2345 if (stringifier and 2346 unforgeable == MemberIsUnforgeable(stringifier, descriptor)): 2347 toStringDesc = { 2348 "name": "toString", 2349 "nativeName": stringifier.identifier.name, 2350 "length": 0, 2351 "flags": "JSPROP_ENUMERATE", 2352 "condition": PropertyDefiner.getControllingCondition(stringifier, descriptor) 2353 } 2354 if isChromeOnly(stringifier): 2355 self.chrome.append(toStringDesc) 2356 else: 2357 self.regular.append(toStringDesc) 2358 jsonifier = descriptor.operations['Jsonifier'] 2359 if (jsonifier and 2360 unforgeable == MemberIsUnforgeable(jsonifier, descriptor)): 2361 toJSONDesc = { 2362 "name": "toJSON", 2363 "nativeName": jsonifier.identifier.name, 2364 "length": 0, 2365 "flags": "JSPROP_ENUMERATE", 2366 "condition": PropertyDefiner.getControllingCondition(jsonifier, descriptor) 2367 } 2368 if isChromeOnly(jsonifier): 2369 self.chrome.append(toJSONDesc) 2370 else: 2371 self.regular.append(toJSONDesc) 2372 if (unforgeable and 2373 descriptor.interface.getExtendedAttribute("Unforgeable")): 2374 # Synthesize our valueOf method 2375 self.regular.append({ 2376 "name": 'valueOf', 2377 "nativeName": "UnforgeableValueOf", 2378 "methodInfo": False, 2379 "length": 0, 2380 "flags": "JSPROP_ENUMERATE", # readonly/permanent added 2381 # automatically. 2382 "condition": MemberCondition() 2383 }) 2384 2385 if descriptor.interface.isJSImplemented(): 2386 if static: 2387 if descriptor.interface.hasInterfaceObject(): 2388 self.chrome.append({ 2389 "name": '_create', 2390 "nativeName": ("%s::_Create" % descriptor.name), 2391 "methodInfo": False, 2392 "length": 2, 2393 "flags": "0", 2394 "condition": MemberCondition() 2395 }) 2396 else: 2397 for m in clearableCachedAttrs(descriptor): 2398 attrName = MakeNativeName(m.identifier.name) 2399 self.chrome.append({ 2400 "name": "_clearCached%sValue" % attrName, 2401 "nativeName": MakeJSImplClearCachedValueNativeName(m), 2402 "methodInfo": False, 2403 "length": "0", 2404 "flags": "0", 2405 "condition": MemberCondition() 2406 }) 2407 2408 self.unforgeable = unforgeable 2409 2410 if static: 2411 if not descriptor.interface.hasInterfaceObject(): 2412 # static methods go on the interface object 2413 assert not self.hasChromeOnly() and not self.hasNonChromeOnly() 2414 else: 2415 if not descriptor.interface.hasInterfacePrototypeObject(): 2416 # non-static methods go on the interface prototype object 2417 assert not self.hasChromeOnly() and not self.hasNonChromeOnly() 2418 2419 def generateArray(self, array, name, doIdArrays): 2420 if len(array) == 0: 2421 return "" 2422 2423 def condition(m, d): 2424 return m["condition"] 2425 2426 def flags(m): 2427 unforgeable = " | JSPROP_PERMANENT | JSPROP_READONLY" if self.unforgeable else "" 2428 return m["flags"] + unforgeable 2429 2430 def specData(m): 2431 if "selfHostedName" in m: 2432 selfHostedName = '"%s"' % m["selfHostedName"] 2433 assert not m.get("methodInfo", True) 2434 accessor = "nullptr" 2435 jitinfo = "nullptr" 2436 else: 2437 selfHostedName = "nullptr" 2438 # When defining symbols, function name may not match symbol name 2439 methodName = m.get("methodName", m["name"]) 2440 accessor = m.get("nativeName", IDLToCIdentifier(methodName)) 2441 if m.get("methodInfo", True): 2442 # Cast this in case the methodInfo is a 2443 # JSTypedMethodJitInfo. 2444 jitinfo = ("reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor) 2445 if m.get("allowCrossOriginThis", False): 2446 if m.get("returnsPromise", False): 2447 raise TypeError("%s returns a Promise but should " 2448 "be allowed cross-origin?" % 2449 accessor) 2450 accessor = "genericCrossOriginMethod" 2451 elif self.descriptor.needsSpecialGenericOps(): 2452 if m.get("returnsPromise", False): 2453 accessor = "genericPromiseReturningMethod" 2454 else: 2455 accessor = "genericMethod" 2456 elif m.get("returnsPromise", False): 2457 accessor = "GenericPromiseReturningBindingMethod" 2458 else: 2459 accessor = "GenericBindingMethod" 2460 else: 2461 if m.get("returnsPromise", False): 2462 jitinfo = "&%s_methodinfo" % accessor 2463 accessor = "StaticMethodPromiseWrapper" 2464 else: 2465 jitinfo = "nullptr" 2466 2467 return (m["name"], accessor, jitinfo, m["length"], flags(m), selfHostedName) 2468 2469 def formatSpec(fields): 2470 if fields[0].startswith("@@"): 2471 fields = (fields[0][2:],) + fields[1:] 2472 return ' JS_SYM_FNSPEC(%s, %s, %s, %s, %s, %s)' % fields 2473 return ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)' % fields 2474 2475 return self.generatePrefableArray( 2476 array, name, 2477 formatSpec, 2478 ' JS_FS_END', 2479 'JSFunctionSpec', 2480 condition, specData, doIdArrays) 2481 2482 2483def IsCrossOriginWritable(attr, descriptor): 2484 """ 2485 Return whether the IDLAttribute in question is cross-origin writable on the 2486 interface represented by descriptor. This is needed to handle the fact that 2487 some, but not all, interfaces implementing URLUtils want a cross-origin 2488 writable .href. 2489 """ 2490 crossOriginWritable = attr.getExtendedAttribute("CrossOriginWritable") 2491 if not crossOriginWritable: 2492 return False 2493 if crossOriginWritable is True: 2494 return True 2495 assert (isinstance(crossOriginWritable, list) and 2496 len(crossOriginWritable) == 1) 2497 return crossOriginWritable[0] == descriptor.interface.identifier.name 2498 2499def isNonExposedNavigatorObjectGetter(attr, descriptor): 2500 return (attr.navigatorObjectGetter and 2501 not descriptor.getDescriptor(attr.type.inner.identifier.name).register) 2502 2503class AttrDefiner(PropertyDefiner): 2504 def __init__(self, descriptor, name, static, unforgeable=False): 2505 assert not (static and unforgeable) 2506 PropertyDefiner.__init__(self, descriptor, name) 2507 self.name = name 2508 # Ignore non-static attributes for interfaces without a proto object 2509 if descriptor.interface.hasInterfacePrototypeObject() or static: 2510 attributes = [m for m in descriptor.interface.members if 2511 m.isAttr() and m.isStatic() == static and 2512 MemberIsUnforgeable(m, descriptor) == unforgeable and 2513 not isNonExposedNavigatorObjectGetter(m, descriptor)] 2514 else: 2515 attributes = [] 2516 self.chrome = [m for m in attributes if isChromeOnly(m)] 2517 self.regular = [m for m in attributes if not isChromeOnly(m)] 2518 self.static = static 2519 self.unforgeable = unforgeable 2520 2521 if static: 2522 if not descriptor.interface.hasInterfaceObject(): 2523 # static attributes go on the interface object 2524 assert not self.hasChromeOnly() and not self.hasNonChromeOnly() 2525 else: 2526 if not descriptor.interface.hasInterfacePrototypeObject(): 2527 # non-static attributes go on the interface prototype object 2528 assert not self.hasChromeOnly() and not self.hasNonChromeOnly() 2529 2530 def generateArray(self, array, name, doIdArrays): 2531 if len(array) == 0: 2532 return "" 2533 2534 def flags(attr): 2535 unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else "" 2536 # Attributes generated as part of a maplike/setlike declaration are 2537 # not enumerable. 2538 enumerable = " | JSPROP_ENUMERATE" if not attr.isMaplikeOrSetlikeAttr() else "" 2539 return ("JSPROP_SHARED" + enumerable + unforgeable) 2540 2541 def getter(attr): 2542 if self.static: 2543 accessor = 'get_' + IDLToCIdentifier(attr.identifier.name) 2544 jitinfo = "nullptr" 2545 else: 2546 if attr.hasLenientThis(): 2547 accessor = "genericLenientGetter" 2548 elif attr.getExtendedAttribute("CrossOriginReadable"): 2549 accessor = "genericCrossOriginGetter" 2550 elif self.descriptor.needsSpecialGenericOps(): 2551 accessor = "genericGetter" 2552 else: 2553 accessor = "GenericBindingGetter" 2554 jitinfo = ("&%s_getterinfo" % 2555 IDLToCIdentifier(attr.identifier.name)) 2556 return "{ { %s, %s } }" % \ 2557 (accessor, jitinfo) 2558 2559 def setter(attr): 2560 if (attr.readonly and 2561 attr.getExtendedAttribute("PutForwards") is None and 2562 attr.getExtendedAttribute("Replaceable") is None and 2563 attr.getExtendedAttribute("LenientSetter") is None): 2564 return "JSNATIVE_WRAPPER(nullptr)" 2565 if self.static: 2566 accessor = 'set_' + IDLToCIdentifier(attr.identifier.name) 2567 jitinfo = "nullptr" 2568 else: 2569 if attr.hasLenientThis(): 2570 accessor = "genericLenientSetter" 2571 elif IsCrossOriginWritable(attr, self.descriptor): 2572 accessor = "genericCrossOriginSetter" 2573 elif self.descriptor.needsSpecialGenericOps(): 2574 accessor = "genericSetter" 2575 else: 2576 accessor = "GenericBindingSetter" 2577 jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name) 2578 return "{ { %s, %s } }" % \ 2579 (accessor, jitinfo) 2580 2581 def specData(attr): 2582 return (attr.identifier.name, flags(attr), getter(attr), 2583 setter(attr)) 2584 2585 return self.generatePrefableArray( 2586 array, name, 2587 lambda fields: ' { "%s", %s, { { %s, %s } } }' % fields, 2588 ' JS_PS_END', 2589 'JSPropertySpec', 2590 PropertyDefiner.getControllingCondition, specData, doIdArrays) 2591 2592 2593class ConstDefiner(PropertyDefiner): 2594 """ 2595 A class for definining constants on the interface object 2596 """ 2597 def __init__(self, descriptor, name): 2598 PropertyDefiner.__init__(self, descriptor, name) 2599 self.name = name 2600 constants = [m for m in descriptor.interface.members if m.isConst()] 2601 self.chrome = [m for m in constants if isChromeOnly(m)] 2602 self.regular = [m for m in constants if not isChromeOnly(m)] 2603 2604 def generateArray(self, array, name, doIdArrays): 2605 if len(array) == 0: 2606 return "" 2607 2608 def specData(const): 2609 return (const.identifier.name, 2610 convertConstIDLValueToJSVal(const.value)) 2611 2612 return self.generatePrefableArray( 2613 array, name, 2614 lambda fields: ' { "%s", %s }' % fields, 2615 ' { 0, JS::UndefinedValue() }', 2616 'ConstantSpec', 2617 PropertyDefiner.getControllingCondition, specData, doIdArrays) 2618 2619 2620class PropertyArrays(): 2621 def __init__(self, descriptor): 2622 self.staticMethods = MethodDefiner(descriptor, "StaticMethods", 2623 static=True) 2624 self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes", 2625 static=True) 2626 self.methods = MethodDefiner(descriptor, "Methods", static=False) 2627 self.attrs = AttrDefiner(descriptor, "Attributes", static=False) 2628 self.unforgeableMethods = MethodDefiner(descriptor, "UnforgeableMethods", 2629 static=False, unforgeable=True) 2630 self.unforgeableAttrs = AttrDefiner(descriptor, "UnforgeableAttributes", 2631 static=False, unforgeable=True) 2632 self.consts = ConstDefiner(descriptor, "Constants") 2633 2634 @staticmethod 2635 def arrayNames(): 2636 return ["staticMethods", "staticAttrs", "methods", "attrs", 2637 "unforgeableMethods", "unforgeableAttrs", "consts"] 2638 2639 def hasChromeOnly(self): 2640 return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames()) 2641 2642 def hasNonChromeOnly(self): 2643 return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames()) 2644 2645 def __str__(self): 2646 define = "" 2647 for array in self.arrayNames(): 2648 define += str(getattr(self, array)) 2649 return define 2650 2651 2652class CGNativeProperties(CGList): 2653 def __init__(self, descriptor, properties): 2654 def generateNativeProperties(name, chrome): 2655 def check(p): 2656 return p.hasChromeOnly() if chrome else p.hasNonChromeOnly() 2657 2658 nativePropsInts = [] 2659 nativePropsTrios = [] 2660 2661 iteratorAliasIndex = -1 2662 for index, item in enumerate(properties.methods.regular): 2663 if item.get("hasIteratorAlias"): 2664 iteratorAliasIndex = index 2665 break 2666 nativePropsInts.append(CGGeneric(str(iteratorAliasIndex))) 2667 2668 offset = 0 2669 for array in properties.arrayNames(): 2670 propertyArray = getattr(properties, array) 2671 if check(propertyArray): 2672 varName = propertyArray.variableName(chrome) 2673 bitfields = "true, %d /* %s */" % (offset, varName) 2674 offset += 1 2675 nativePropsInts.append(CGGeneric(bitfields)) 2676 2677 if propertyArray.usedForXrays(): 2678 ids = "%(name)s_ids" 2679 else: 2680 ids = "nullptr" 2681 trio = "{ %(name)s, " + ids + ", %(name)s_specs }" 2682 trio = trio % {'name': varName} 2683 nativePropsTrios.append(CGGeneric(trio)) 2684 else: 2685 bitfields = "false, 0" 2686 nativePropsInts.append(CGGeneric(bitfields)) 2687 2688 nativePropsTrios = \ 2689 [CGWrapper(CGIndenter(CGList(nativePropsTrios, ",\n")), 2690 pre='{\n', post='\n}')] 2691 nativeProps = nativePropsInts + nativePropsTrios 2692 pre = ("static const NativePropertiesN<%d> %s = {\n" % 2693 (offset, name)) 2694 return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")), 2695 pre=pre, post="\n};\n") 2696 2697 nativeProperties = [] 2698 if properties.hasNonChromeOnly(): 2699 nativeProperties.append( 2700 generateNativeProperties("sNativeProperties", False)) 2701 if properties.hasChromeOnly(): 2702 nativeProperties.append( 2703 generateNativeProperties("sChromeOnlyNativeProperties", True)) 2704 2705 CGList.__init__(self, nativeProperties, "\n") 2706 2707 def declare(self): 2708 return "" 2709 2710 def define(self): 2711 return CGList.define(self) 2712 2713 2714class CGJsonifyAttributesMethod(CGAbstractMethod): 2715 """ 2716 Generate the JsonifyAttributes method for an interface descriptor 2717 """ 2718 def __init__(self, descriptor): 2719 args = [Argument('JSContext*', 'aCx'), 2720 Argument('JS::Handle<JSObject*>', 'obj'), 2721 Argument('%s*' % descriptor.nativeType, 'self'), 2722 Argument('JS::Rooted<JSObject*>&', 'aResult')] 2723 CGAbstractMethod.__init__(self, descriptor, 'JsonifyAttributes', 'bool', args) 2724 2725 def definition_body(self): 2726 ret = '' 2727 interface = self.descriptor.interface 2728 for m in interface.members: 2729 if m.isAttr() and not m.isStatic() and m.type.isSerializable(): 2730 ret += fill( 2731 """ 2732 { // scope for "temp" 2733 JS::Rooted<JS::Value> temp(aCx); 2734 if (!get_${name}(aCx, obj, self, JSJitGetterCallArgs(&temp))) { 2735 return false; 2736 } 2737 if (!JS_DefineProperty(aCx, aResult, "${name}", temp, JSPROP_ENUMERATE)) { 2738 return false; 2739 } 2740 } 2741 """, 2742 name=IDLToCIdentifier(m.identifier.name)) 2743 ret += 'return true;\n' 2744 return ret 2745 2746 2747class CGCreateInterfaceObjectsMethod(CGAbstractMethod): 2748 """ 2749 Generate the CreateInterfaceObjects method for an interface descriptor. 2750 2751 properties should be a PropertyArrays instance. 2752 """ 2753 def __init__(self, descriptor, properties, haveUnscopables): 2754 args = [Argument('JSContext*', 'aCx'), 2755 Argument('JS::Handle<JSObject*>', 'aGlobal'), 2756 Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'), 2757 Argument('bool', 'aDefineOnGlobal')] 2758 CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args) 2759 self.properties = properties 2760 self.haveUnscopables = haveUnscopables 2761 2762 def definition_body(self): 2763 (protoGetter, protoHandleGetter) = InterfacePrototypeObjectProtoGetter(self.descriptor) 2764 if protoHandleGetter is None: 2765 parentProtoType = "Rooted" 2766 getParentProto = "aCx, " + protoGetter 2767 else: 2768 parentProtoType = "Handle" 2769 getParentProto = protoHandleGetter 2770 getParentProto = getParentProto + "(aCx)" 2771 2772 (protoGetter, protoHandleGetter) = InterfaceObjectProtoGetter(self.descriptor) 2773 if protoHandleGetter is None: 2774 getConstructorProto = "aCx, " + protoGetter 2775 constructorProtoType = "Rooted" 2776 else: 2777 getConstructorProto = protoHandleGetter 2778 constructorProtoType = "Handle" 2779 getConstructorProto += "(aCx)" 2780 2781 needInterfaceObject = self.descriptor.interface.hasInterfaceObject() 2782 needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject() 2783 2784 # if we don't need to create anything, why are we generating this? 2785 assert needInterfaceObject or needInterfacePrototypeObject 2786 2787 getParentProto = fill( 2788 """ 2789 JS::${type}<JSObject*> parentProto(${getParentProto}); 2790 if (!parentProto) { 2791 return; 2792 } 2793 """, 2794 type=parentProtoType, 2795 getParentProto=getParentProto) 2796 2797 getConstructorProto = fill( 2798 """ 2799 JS::${type}<JSObject*> constructorProto(${getConstructorProto}); 2800 if (!constructorProto) { 2801 return; 2802 } 2803 """, 2804 type=constructorProtoType, 2805 getConstructorProto=getConstructorProto) 2806 2807 idsToInit = [] 2808 # There is no need to init any IDs in bindings that don't want Xrays. 2809 if self.descriptor.wantsXrays: 2810 for var in self.properties.arrayNames(): 2811 props = getattr(self.properties, var) 2812 # We only have non-chrome ids to init if we have no chrome ids. 2813 if props.hasChromeOnly(): 2814 idsToInit.append(props.variableName(True)) 2815 if props.hasNonChromeOnly(): 2816 idsToInit.append(props.variableName(False)) 2817 if len(idsToInit) > 0: 2818 initIdCalls = ["!InitIds(aCx, %s, %s_ids)" % (varname, varname) 2819 for varname in idsToInit] 2820 idsInitedFlag = CGGeneric("static bool sIdsInited = false;\n") 2821 setFlag = CGGeneric("sIdsInited = true;\n") 2822 initIdConditionals = [CGIfWrapper(CGGeneric("return;\n"), call) 2823 for call in initIdCalls] 2824 initIds = CGList([idsInitedFlag, 2825 CGIfWrapper(CGList(initIdConditionals + [setFlag]), 2826 "!sIdsInited && NS_IsMainThread()")]) 2827 else: 2828 initIds = None 2829 2830 prefCacheData = [] 2831 for var in self.properties.arrayNames(): 2832 props = getattr(self.properties, var) 2833 prefCacheData.extend(props.prefCacheData) 2834 if len(prefCacheData) != 0: 2835 prefCacheData = [ 2836 CGGeneric('Preferences::AddBoolVarCache(%s, "%s");\n' % (ptr, pref)) 2837 for pref, ptr in prefCacheData] 2838 prefCache = CGWrapper(CGIndenter(CGList(prefCacheData)), 2839 pre=("static bool sPrefCachesInited = false;\n" 2840 "if (!sPrefCachesInited && NS_IsMainThread()) {\n" 2841 " sPrefCachesInited = true;\n"), 2842 post="}\n") 2843 else: 2844 prefCache = None 2845 2846 if self.descriptor.interface.ctor(): 2847 constructArgs = methodLength(self.descriptor.interface.ctor()) 2848 else: 2849 constructArgs = 0 2850 if len(self.descriptor.interface.namedConstructors) > 0: 2851 namedConstructors = "namedConstructors" 2852 else: 2853 namedConstructors = "nullptr" 2854 2855 if needInterfacePrototypeObject: 2856 protoClass = "&sPrototypeClass.mBase" 2857 protoCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)" % self.descriptor.name 2858 parentProto = "parentProto" 2859 getParentProto = CGGeneric(getParentProto) 2860 else: 2861 protoClass = "nullptr" 2862 protoCache = "nullptr" 2863 parentProto = "nullptr" 2864 getParentProto = None 2865 2866 if needInterfaceObject: 2867 interfaceClass = "&sInterfaceObjectClass.mBase" 2868 interfaceCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)" % self.descriptor.name 2869 getConstructorProto = CGGeneric(getConstructorProto) 2870 constructorProto = "constructorProto" 2871 else: 2872 # We don't have slots to store the named constructors. 2873 assert len(self.descriptor.interface.namedConstructors) == 0 2874 interfaceClass = "nullptr" 2875 interfaceCache = "nullptr" 2876 getConstructorProto = None 2877 constructorProto = "nullptr" 2878 2879 isGlobal = self.descriptor.isGlobal() is not None 2880 if self.properties.hasNonChromeOnly(): 2881 properties = "sNativeProperties.Upcast()" 2882 else: 2883 properties = "nullptr" 2884 if self.properties.hasChromeOnly(): 2885 chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? sChromeOnlyNativeProperties.Upcast() : nullptr" 2886 else: 2887 chromeProperties = "nullptr" 2888 2889 call = fill( 2890 """ 2891 JS::Heap<JSObject*>* protoCache = ${protoCache}; 2892 JS::Heap<JSObject*>* interfaceCache = ${interfaceCache}; 2893 dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto}, 2894 ${protoClass}, protoCache, 2895 ${constructorProto}, ${interfaceClass}, ${constructArgs}, ${namedConstructors}, 2896 interfaceCache, 2897 ${properties}, 2898 ${chromeProperties}, 2899 ${name}, aDefineOnGlobal, 2900 ${unscopableNames}, 2901 ${isGlobal}); 2902 """, 2903 protoClass=protoClass, 2904 parentProto=parentProto, 2905 protoCache=protoCache, 2906 constructorProto=constructorProto, 2907 interfaceClass=interfaceClass, 2908 constructArgs=constructArgs, 2909 namedConstructors=namedConstructors, 2910 interfaceCache=interfaceCache, 2911 properties=properties, 2912 chromeProperties=chromeProperties, 2913 name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr", 2914 unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr", 2915 isGlobal=toStringBool(isGlobal)) 2916 2917 # If we fail after here, we must clear interface and prototype caches 2918 # using this code: intermediate failure must not expose the interface in 2919 # partially-constructed state. Note that every case after here needs an 2920 # interface prototype object. 2921 failureCode = dedent( 2922 """ 2923 *protoCache = nullptr; 2924 if (interfaceCache) { 2925 *interfaceCache = nullptr; 2926 } 2927 return; 2928 """) 2929 2930 aliasedMembers = [m for m in self.descriptor.interface.members if m.isMethod() and m.aliases] 2931 if aliasedMembers: 2932 assert needInterfacePrototypeObject 2933 2934 def defineAlias(alias): 2935 if alias == "@@iterator": 2936 symbolJSID = "SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::iterator))" 2937 getSymbolJSID = CGGeneric(fill("JS::Rooted<jsid> iteratorId(aCx, ${symbolJSID});", 2938 symbolJSID=symbolJSID)) 2939 defineFn = "JS_DefinePropertyById" 2940 prop = "iteratorId" 2941 elif alias.startswith("@@"): 2942 raise TypeError("Can't handle any well-known Symbol other than @@iterator") 2943 else: 2944 getSymbolJSID = None 2945 defineFn = "JS_DefineProperty" 2946 prop = '"%s"' % alias 2947 return CGList([ 2948 getSymbolJSID, 2949 # XXX If we ever create non-enumerable properties that can 2950 # be aliased, we should consider making the aliases 2951 # match the enumerability of the property being aliased. 2952 CGGeneric(fill( 2953 """ 2954 if (!${defineFn}(aCx, proto, ${prop}, aliasedVal, JSPROP_ENUMERATE)) { 2955 $*{failureCode} 2956 } 2957 """, 2958 defineFn=defineFn, 2959 prop=prop, 2960 failureCode=failureCode)) 2961 ], "\n") 2962 2963 def defineAliasesFor(m): 2964 return CGList([ 2965 CGGeneric(fill( 2966 """ 2967 if (!JS_GetProperty(aCx, proto, \"${prop}\", &aliasedVal)) { 2968 $*{failureCode} 2969 } 2970 """, 2971 failureCode=failureCode, 2972 prop=m.identifier.name)) 2973 ] + [defineAlias(alias) for alias in sorted(m.aliases)]) 2974 2975 defineAliases = CGList([ 2976 CGGeneric(fill(""" 2977 // Set up aliases on the interface prototype object we just created. 2978 JS::Handle<JSObject*> proto = GetProtoObjectHandle(aCx); 2979 if (!proto) { 2980 $*{failureCode} 2981 } 2982 2983 """, 2984 failureCode=failureCode)), 2985 CGGeneric("JS::Rooted<JS::Value> aliasedVal(aCx);\n\n") 2986 ] + [defineAliasesFor(m) for m in sorted(aliasedMembers)]) 2987 else: 2988 defineAliases = None 2989 2990 # Globals handle unforgeables directly in Wrap() instead of 2991 # via a holder. 2992 if self.descriptor.hasUnforgeableMembers and not self.descriptor.isGlobal(): 2993 assert needInterfacePrototypeObject 2994 2995 # We want to use the same JSClass and prototype as the object we'll 2996 # end up defining the unforgeable properties on in the end, so that 2997 # we can use JS_InitializePropertiesFromCompatibleNativeObject to do 2998 # a fast copy. In the case of proxies that's null, because the 2999 # expando object is a vanilla object, but in the case of other DOM 3000 # objects it's whatever our class is. 3001 if self.descriptor.proxy: 3002 holderClass = "nullptr" 3003 holderProto = "nullptr" 3004 else: 3005 holderClass = "sClass.ToJSClass()" 3006 holderProto = "*protoCache" 3007 createUnforgeableHolder = CGGeneric(fill( 3008 """ 3009 JS::Rooted<JSObject*> unforgeableHolder(aCx); 3010 { 3011 JS::Rooted<JSObject*> holderProto(aCx, ${holderProto}); 3012 unforgeableHolder = JS_NewObjectWithoutMetadata(aCx, ${holderClass}, holderProto); 3013 if (!unforgeableHolder) { 3014 $*{failureCode} 3015 } 3016 } 3017 """, 3018 holderProto=holderProto, 3019 holderClass=holderClass, 3020 failureCode=failureCode)) 3021 defineUnforgeables = InitUnforgeablePropertiesOnHolder(self.descriptor, 3022 self.properties, 3023 failureCode) 3024 createUnforgeableHolder = CGList( 3025 [createUnforgeableHolder, defineUnforgeables]) 3026 3027 installUnforgeableHolder = CGGeneric(dedent( 3028 """ 3029 if (*protoCache) { 3030 js::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE, 3031 JS::ObjectValue(*unforgeableHolder)); 3032 } 3033 """)) 3034 3035 unforgeableHolderSetup = CGList( 3036 [createUnforgeableHolder, installUnforgeableHolder], "\n") 3037 else: 3038 unforgeableHolderSetup = None 3039 3040 if self.descriptor.name == "Promise": 3041 speciesSetup = CGGeneric(fill( 3042 """ 3043 #ifndef SPIDERMONKEY_PROMISE 3044 JS::Rooted<JSObject*> promiseConstructor(aCx, *interfaceCache); 3045 JS::Rooted<jsid> species(aCx, 3046 SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::species))); 3047 if (!JS_DefinePropertyById(aCx, promiseConstructor, species, JS::UndefinedHandleValue, 3048 JSPROP_SHARED, Promise::PromiseSpecies, nullptr)) { 3049 $*{failureCode} 3050 } 3051 #endif // SPIDERMONKEY_PROMISE 3052 """, 3053 failureCode=failureCode)) 3054 else: 3055 speciesSetup = None 3056 3057 if (self.descriptor.interface.isOnGlobalProtoChain() and 3058 needInterfacePrototypeObject): 3059 makeProtoPrototypeImmutable = CGGeneric(fill( 3060 """ 3061 if (*${protoCache}) { 3062 bool succeeded; 3063 JS::Handle<JSObject*> prot = GetProtoObjectHandle(aCx); 3064 if (!JS_SetImmutablePrototype(aCx, prot, &succeeded)) { 3065 $*{failureCode} 3066 } 3067 3068 MOZ_ASSERT(succeeded, 3069 "making a fresh prototype object's [[Prototype]] " 3070 "immutable can internally fail, but it should " 3071 "never be unsuccessful"); 3072 } 3073 """, 3074 protoCache=protoCache, 3075 failureCode=failureCode)) 3076 else: 3077 makeProtoPrototypeImmutable = None 3078 3079 return CGList( 3080 [getParentProto, getConstructorProto, initIds, 3081 prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup, 3082 speciesSetup, makeProtoPrototypeImmutable], 3083 "\n").define() 3084 3085 3086class CGGetPerInterfaceObject(CGAbstractMethod): 3087 """ 3088 A method for getting a per-interface object (a prototype object or interface 3089 constructor object). 3090 """ 3091 def __init__(self, descriptor, name, idPrefix="", extraArgs=[]): 3092 args = [Argument('JSContext*', 'aCx')] + extraArgs 3093 CGAbstractMethod.__init__(self, descriptor, name, 3094 'JS::Handle<JSObject*>', args) 3095 self.id = idPrefix + "id::" + self.descriptor.name 3096 3097 def definition_body(self): 3098 return fill( 3099 """ 3100 /* Make sure our global is sane. Hopefully we can remove this sometime */ 3101 JSObject* global = JS::CurrentGlobalOrNull(aCx); 3102 if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) { 3103 return nullptr; 3104 } 3105 3106 /* Check to see whether the interface objects are already installed */ 3107 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global); 3108 if (!protoAndIfaceCache.EntrySlotIfExists(${id})) { 3109 JS::Rooted<JSObject*> rootedGlobal(aCx, global); 3110 CreateInterfaceObjects(aCx, rootedGlobal, protoAndIfaceCache, aDefineOnGlobal); 3111 } 3112 3113 /* 3114 * The object might _still_ be null, but that's OK. 3115 * 3116 * Calling fromMarkedLocation() is safe because protoAndIfaceCache is 3117 * traced by TraceProtoAndIfaceCache() and its contents are never 3118 * changed after they have been set. 3119 * 3120 * Calling address() avoids the read read barrier that does gray 3121 * unmarking, but it's not possible for the object to be gray here. 3122 */ 3123 3124 const JS::Heap<JSObject*>& entrySlot = protoAndIfaceCache.EntrySlotMustExist(${id}); 3125 MOZ_ASSERT_IF(entrySlot, !JS::ObjectIsMarkedGray(entrySlot)); 3126 return JS::Handle<JSObject*>::fromMarkedLocation(entrySlot.address()); 3127 """, 3128 id=self.id) 3129 3130 3131class CGGetProtoObjectHandleMethod(CGGetPerInterfaceObject): 3132 """ 3133 A method for getting the interface prototype object. 3134 """ 3135 def __init__(self, descriptor): 3136 CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObjectHandle", 3137 "prototypes::") 3138 3139 def definition_body(self): 3140 return dedent(""" 3141 /* Get the interface prototype object for this class. This will create the 3142 object as needed. */ 3143 bool aDefineOnGlobal = true; 3144 3145 """) + CGGetPerInterfaceObject.definition_body(self) 3146 3147 3148class CGGetProtoObjectMethod(CGAbstractMethod): 3149 """ 3150 A method for getting the interface prototype object. 3151 """ 3152 def __init__(self, descriptor): 3153 CGAbstractMethod.__init__( 3154 self, descriptor, "GetProtoObject", "JSObject*", 3155 [Argument('JSContext*', 'aCx')]) 3156 3157 def definition_body(self): 3158 return "return GetProtoObjectHandle(aCx);\n" 3159 3160 3161class CGGetConstructorObjectHandleMethod(CGGetPerInterfaceObject): 3162 """ 3163 A method for getting the interface constructor object. 3164 """ 3165 def __init__(self, descriptor): 3166 CGGetPerInterfaceObject.__init__( 3167 self, descriptor, "GetConstructorObjectHandle", 3168 "constructors::", 3169 extraArgs=[Argument("bool", "aDefineOnGlobal", "true")]) 3170 3171 def definition_body(self): 3172 return dedent(""" 3173 /* Get the interface object for this class. This will create the object as 3174 needed. */ 3175 3176 """) + CGGetPerInterfaceObject.definition_body(self) 3177 3178 3179class CGGetConstructorObjectMethod(CGAbstractMethod): 3180 """ 3181 A method for getting the interface constructor object. 3182 """ 3183 def __init__(self, descriptor): 3184 CGAbstractMethod.__init__( 3185 self, descriptor, "GetConstructorObject", "JSObject*", 3186 [Argument('JSContext*', 'aCx')]) 3187 3188 def definition_body(self): 3189 return "return GetConstructorObjectHandle(aCx);\n" 3190 3191 3192class CGGetNamedPropertiesObjectMethod(CGAbstractStaticMethod): 3193 def __init__(self, descriptor): 3194 args = [Argument('JSContext*', 'aCx')] 3195 CGAbstractStaticMethod.__init__(self, descriptor, 3196 'GetNamedPropertiesObject', 3197 'JSObject*', args) 3198 3199 def definition_body(self): 3200 parentProtoName = self.descriptor.parentPrototypeName 3201 if parentProtoName is None: 3202 getParentProto = "" 3203 parentProto = "nullptr" 3204 else: 3205 getParentProto = fill( 3206 """ 3207 JS::Rooted<JSObject*> parentProto(aCx, ${parent}::GetProtoObjectHandle(aCx)); 3208 if (!parentProto) { 3209 return nullptr; 3210 } 3211 """, 3212 parent=toBindingNamespace(parentProtoName)) 3213 parentProto = "parentProto" 3214 return fill( 3215 """ 3216 /* Make sure our global is sane. Hopefully we can remove this sometime */ 3217 JSObject* global = JS::CurrentGlobalOrNull(aCx); 3218 if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) { 3219 return nullptr; 3220 } 3221 3222 /* Check to see whether the named properties object has already been created */ 3223 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global); 3224 3225 JS::Heap<JSObject*>& namedPropertiesObject = protoAndIfaceCache.EntrySlotOrCreate(namedpropertiesobjects::id::${ifaceName}); 3226 if (!namedPropertiesObject) { 3227 $*{getParentProto} 3228 namedPropertiesObject = ${nativeType}::CreateNamedPropertiesObject(aCx, ${parentProto}); 3229 DebugOnly<const DOMIfaceAndProtoJSClass*> clasp = 3230 DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(namedPropertiesObject)); 3231 MOZ_ASSERT(clasp->mType == eNamedPropertiesObject, 3232 "Expected ${nativeType}::CreateNamedPropertiesObject to return a named properties object"); 3233 MOZ_ASSERT(clasp->mNativeHooks, 3234 "The named properties object for ${nativeType} should have NativePropertyHooks."); 3235 MOZ_ASSERT(clasp->mNativeHooks->mResolveOwnProperty, 3236 "Don't know how to resolve the properties of the named properties object for ${nativeType}."); 3237 MOZ_ASSERT(clasp->mNativeHooks->mEnumerateOwnProperties, 3238 "Don't know how to enumerate the properties of the named properties object for ${nativeType}."); 3239 } 3240 return namedPropertiesObject.get(); 3241 """, 3242 getParentProto=getParentProto, 3243 ifaceName=self.descriptor.name, 3244 parentProto=parentProto, 3245 nativeType=self.descriptor.nativeType) 3246 3247 3248class CGDefineDOMInterfaceMethod(CGAbstractMethod): 3249 """ 3250 A method for resolve hooks to try to lazily define the interface object for 3251 a given interface. 3252 """ 3253 def __init__(self, descriptor): 3254 args = [Argument('JSContext*', 'aCx'), 3255 Argument('JS::Handle<JSObject*>', 'aGlobal'), 3256 Argument('JS::Handle<jsid>', 'id'), 3257 Argument('bool', 'aDefineOnGlobal')] 3258 CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'JSObject*', args) 3259 3260 def definition_body(self): 3261 if len(self.descriptor.interface.namedConstructors) > 0: 3262 getConstructor = dedent(""" 3263 JSObject* interfaceObject = GetConstructorObjectHandle(aCx, aDefineOnGlobal); 3264 if (!interfaceObject) { 3265 return nullptr; 3266 } 3267 for (unsigned slot = DOM_INTERFACE_SLOTS_BASE; slot < JSCLASS_RESERVED_SLOTS(&sInterfaceObjectClass.mBase); ++slot) { 3268 JSObject* constructor = &js::GetReservedSlot(interfaceObject, slot).toObject(); 3269 if (JS_GetFunctionId(JS_GetObjectFunction(constructor)) == JSID_TO_STRING(id)) { 3270 return constructor; 3271 } 3272 } 3273 return interfaceObject; 3274 """) 3275 else: 3276 getConstructor = "return GetConstructorObjectHandle(aCx, aDefineOnGlobal);\n" 3277 return getConstructor 3278 3279 3280def getConditionList(idlobj, cxName, objName): 3281 """ 3282 Get the list of conditions for idlobj (to be used in "is this enabled" 3283 checks). This will be returned as a CGList with " &&\n" as the separator, 3284 for readability. 3285 3286 objName is the name of the object that we're working with, because some of 3287 our test functions want that. 3288 """ 3289 conditions = [] 3290 pref = idlobj.getExtendedAttribute("Pref") 3291 if pref: 3292 assert isinstance(pref, list) and len(pref) == 1 3293 conditions.append('Preferences::GetBool("%s")' % pref[0]) 3294 if idlobj.getExtendedAttribute("ChromeOnly"): 3295 conditions.append("nsContentUtils::ThreadsafeIsCallerChrome()") 3296 func = idlobj.getExtendedAttribute("Func") 3297 if func: 3298 assert isinstance(func, list) and len(func) == 1 3299 conditions.append("%s(%s, %s)" % (func[0], cxName, objName)) 3300 if idlobj.getExtendedAttribute("SecureContext"): 3301 conditions.append("mozilla::dom::IsSecureContextOrObjectIsFromSecureContext(%s, %s)" % (cxName, objName)) 3302 3303 return CGList((CGGeneric(cond) for cond in conditions), " &&\n") 3304 3305 3306class CGConstructorEnabled(CGAbstractMethod): 3307 """ 3308 A method for testing whether we should be exposing this interface 3309 object or navigator property. This can perform various tests 3310 depending on what conditions are specified on the interface. 3311 """ 3312 def __init__(self, descriptor): 3313 CGAbstractMethod.__init__(self, descriptor, 3314 'ConstructorEnabled', 'bool', 3315 [Argument("JSContext*", "aCx"), 3316 Argument("JS::Handle<JSObject*>", "aObj")]) 3317 3318 def definition_body(self): 3319 body = CGList([], "\n") 3320 3321 iface = self.descriptor.interface 3322 3323 if not iface.isExposedOnMainThread(): 3324 exposedInWindowCheck = dedent( 3325 """ 3326 MOZ_ASSERT(!NS_IsMainThread(), "Why did we even get called?"); 3327 """) 3328 body.append(CGGeneric(exposedInWindowCheck)) 3329 3330 if iface.isExposedInSomeButNotAllWorkers(): 3331 workerGlobals = sorted(iface.getWorkerExposureSet()) 3332 workerCondition = CGList((CGGeneric('strcmp(name, "%s")' % workerGlobal) 3333 for workerGlobal in workerGlobals), " && ") 3334 exposedInWorkerCheck = fill( 3335 """ 3336 const char* name = js::GetObjectClass(aObj)->name; 3337 if (${workerCondition}) { 3338 return false; 3339 } 3340 """, workerCondition=workerCondition.define()) 3341 exposedInWorkerCheck = CGGeneric(exposedInWorkerCheck) 3342 if iface.isExposedOnMainThread(): 3343 exposedInWorkerCheck = CGIfWrapper(exposedInWorkerCheck, 3344 "!NS_IsMainThread()") 3345 body.append(exposedInWorkerCheck) 3346 3347 conditions = getConditionList(iface, "aCx", "aObj") 3348 3349 # We should really have some conditions 3350 assert len(body) or len(conditions) 3351 3352 conditionsWrapper = "" 3353 if len(conditions): 3354 conditionsWrapper = CGWrapper(conditions, 3355 pre="return ", 3356 post=";\n", 3357 reindent=True) 3358 else: 3359 conditionsWrapper = CGGeneric("return true;\n") 3360 3361 body.append(conditionsWrapper) 3362 return body.define() 3363 3364 3365def CreateBindingJSObject(descriptor, properties): 3366 objDecl = "BindingJSObjectCreator<%s> creator(aCx);\n" % descriptor.nativeType 3367 3368 # We don't always need to root obj, but there are a variety 3369 # of cases where we do, so for simplicity, just always root it. 3370 if descriptor.proxy: 3371 create = dedent( 3372 """ 3373 creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(), 3374 proto, aObject, aReflector); 3375 if (!aReflector) { 3376 return false; 3377 } 3378 3379 """) 3380 if descriptor.interface.getExtendedAttribute('OverrideBuiltins'): 3381 create += dedent(""" 3382 js::SetProxyExtra(aReflector, JSPROXYSLOT_EXPANDO, 3383 JS::PrivateValue(&aObject->mExpandoAndGeneration)); 3384 3385 """) 3386 else: 3387 create = dedent( 3388 """ 3389 creator.CreateObject(aCx, sClass.ToJSClass(), proto, aObject, aReflector); 3390 if (!aReflector) { 3391 return false; 3392 } 3393 """) 3394 return objDecl + create 3395 3396 3397def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode, 3398 holderName="unforgeableHolder"): 3399 """ 3400 Define the unforgeable properties on the unforgeable holder for 3401 the interface represented by descriptor. 3402 3403 properties is a PropertyArrays instance. 3404 3405 """ 3406 assert (properties.unforgeableAttrs.hasNonChromeOnly() or 3407 properties.unforgeableAttrs.hasChromeOnly() or 3408 properties.unforgeableMethods.hasNonChromeOnly() or 3409 properties.unforgeableMethods.hasChromeOnly()) 3410 3411 unforgeables = [] 3412 3413 defineUnforgeableAttrs = fill( 3414 """ 3415 if (!DefineUnforgeableAttributes(aCx, ${holderName}, %s)) { 3416 $*{failureCode} 3417 } 3418 """, 3419 failureCode=failureCode, 3420 holderName=holderName) 3421 defineUnforgeableMethods = fill( 3422 """ 3423 if (!DefineUnforgeableMethods(aCx, ${holderName}, %s)) { 3424 $*{failureCode} 3425 } 3426 """, 3427 failureCode=failureCode, 3428 holderName=holderName) 3429 3430 unforgeableMembers = [ 3431 (defineUnforgeableAttrs, properties.unforgeableAttrs), 3432 (defineUnforgeableMethods, properties.unforgeableMethods) 3433 ] 3434 for (template, array) in unforgeableMembers: 3435 if array.hasNonChromeOnly(): 3436 unforgeables.append(CGGeneric(template % array.variableName(False))) 3437 if array.hasChromeOnly(): 3438 unforgeables.append( 3439 CGIfWrapper(CGGeneric(template % array.variableName(True)), 3440 "nsContentUtils::ThreadsafeIsCallerChrome()")) 3441 3442 if descriptor.interface.getExtendedAttribute("Unforgeable"): 3443 # We do our undefined toJSON and toPrimitive here, not as a regular 3444 # property because we don't have a concept of value props anywhere in 3445 # IDL. 3446 unforgeables.append(CGGeneric(fill( 3447 """ 3448 JS::RootedId toPrimitive(aCx, 3449 SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::toPrimitive))); 3450 if (!JS_DefinePropertyById(aCx, ${holderName}, toPrimitive, 3451 JS::UndefinedHandleValue, 3452 JSPROP_READONLY | JSPROP_PERMANENT) || 3453 !JS_DefineProperty(aCx, ${holderName}, "toJSON", 3454 JS::UndefinedHandleValue, 3455 JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) { 3456 $*{failureCode} 3457 } 3458 """, 3459 failureCode=failureCode, 3460 holderName=holderName))) 3461 3462 return CGWrapper(CGList(unforgeables), pre="\n") 3463 3464 3465def CopyUnforgeablePropertiesToInstance(descriptor, failureCode): 3466 """ 3467 Copy the unforgeable properties from the unforgeable holder for 3468 this interface to the instance object we have. 3469 """ 3470 assert not descriptor.isGlobal(); 3471 3472 if not descriptor.hasUnforgeableMembers: 3473 return "" 3474 3475 copyCode = [ 3476 CGGeneric(dedent( 3477 """ 3478 // Important: do unforgeable property setup after we have handed 3479 // over ownership of the C++ object to obj as needed, so that if 3480 // we fail and it ends up GCed it won't have problems in the 3481 // finalizer trying to drop its ownership of the C++ object. 3482 """)) 3483 ] 3484 3485 # For proxies, we want to define on the expando object, not directly on the 3486 # reflector, so we can make sure we don't get confused by named getters. 3487 if descriptor.proxy: 3488 copyCode.append(CGGeneric(fill( 3489 """ 3490 JS::Rooted<JSObject*> expando(aCx, 3491 DOMProxyHandler::EnsureExpandoObject(aCx, aReflector)); 3492 if (!expando) { 3493 $*{failureCode} 3494 } 3495 """, 3496 failureCode=failureCode))) 3497 obj = "expando" 3498 else: 3499 obj = "aReflector" 3500 3501 copyCode.append(CGGeneric(fill( 3502 """ 3503 JS::Rooted<JSObject*> unforgeableHolder(aCx, 3504 &js::GetReservedSlot(canonicalProto, DOM_INTERFACE_PROTO_SLOTS_BASE).toObject()); 3505 if (!JS_InitializePropertiesFromCompatibleNativeObject(aCx, ${obj}, unforgeableHolder)) { 3506 $*{failureCode} 3507 } 3508 """, 3509 obj=obj, 3510 failureCode=failureCode))) 3511 3512 return CGWrapper(CGList(copyCode), pre="\n").define() 3513 3514 3515def AssertInheritanceChain(descriptor): 3516 asserts = "" 3517 iface = descriptor.interface 3518 while iface: 3519 desc = descriptor.getDescriptor(iface.identifier.name) 3520 asserts += ( 3521 "MOZ_ASSERT(static_cast<%s*>(aObject) == \n" 3522 " reinterpret_cast<%s*>(aObject),\n" 3523 " \"Multiple inheritance for %s is broken.\");\n" % 3524 (desc.nativeType, desc.nativeType, desc.nativeType)) 3525 iface = iface.parent 3526 asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n" 3527 return asserts 3528 3529 3530def InitMemberSlots(descriptor, failureCode): 3531 """ 3532 Initialize member slots on our JS object if we're supposed to have some. 3533 3534 Note that this is called after the SetWrapper() call in the 3535 wrapperCache case, since that can affect how our getters behave 3536 and we plan to invoke them here. So if we fail, we need to 3537 ClearWrapper. 3538 """ 3539 if not descriptor.interface.hasMembersInSlots(): 3540 return "" 3541 return fill( 3542 """ 3543 if (!UpdateMemberSlots(aCx, aReflector, aObject)) { 3544 $*{failureCode} 3545 } 3546 """, 3547 failureCode=failureCode) 3548 3549 3550def SetImmutablePrototype(descriptor, failureCode): 3551 if not descriptor.hasNonOrdinaryGetPrototypeOf(): 3552 return "" 3553 3554 return fill( 3555 """ 3556 bool succeeded; 3557 if (!JS_SetImmutablePrototype(aCx, aReflector, &succeeded)) { 3558 ${failureCode} 3559 } 3560 MOZ_ASSERT(succeeded, 3561 "Making a fresh reflector instance have an immutable " 3562 "prototype can internally fail, but it should never be " 3563 "unsuccessful"); 3564 """, 3565 failureCode=failureCode) 3566 3567 3568def DeclareProto(): 3569 """ 3570 Declare the canonicalProto and proto we have for our wrapping operation. 3571 """ 3572 return dedent( 3573 """ 3574 JS::Handle<JSObject*> canonicalProto = GetProtoObjectHandle(aCx); 3575 if (!canonicalProto) { 3576 return false; 3577 } 3578 JS::Rooted<JSObject*> proto(aCx); 3579 if (aGivenProto) { 3580 proto = aGivenProto; 3581 // Unfortunately, while aGivenProto was in the compartment of aCx 3582 // coming in, we changed compartments to that of "parent" so may need 3583 // to wrap the proto here. 3584 if (js::GetContextCompartment(aCx) != js::GetObjectCompartment(proto)) { 3585 if (!JS_WrapObject(aCx, &proto)) { 3586 return false; 3587 } 3588 } 3589 } else { 3590 proto = canonicalProto; 3591 } 3592 """) 3593 3594 3595class CGWrapWithCacheMethod(CGAbstractMethod): 3596 """ 3597 Create a wrapper JSObject for a given native that implements nsWrapperCache. 3598 3599 properties should be a PropertyArrays instance. 3600 """ 3601 def __init__(self, descriptor, properties): 3602 assert descriptor.interface.hasInterfacePrototypeObject() 3603 args = [Argument('JSContext*', 'aCx'), 3604 Argument(descriptor.nativeType + '*', 'aObject'), 3605 Argument('nsWrapperCache*', 'aCache'), 3606 Argument('JS::Handle<JSObject*>', 'aGivenProto'), 3607 Argument('JS::MutableHandle<JSObject*>', 'aReflector')] 3608 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args) 3609 self.properties = properties 3610 3611 def definition_body(self): 3612 if self.descriptor.proxy: 3613 preserveWrapper = dedent( 3614 """ 3615 // For DOM proxies, the only reliable way to preserve the wrapper 3616 // is to force creation of the expando object. 3617 JS::Rooted<JSObject*> unused(aCx, 3618 DOMProxyHandler::EnsureExpandoObject(aCx, aReflector)); 3619 """) 3620 else: 3621 preserveWrapper = "PreserveWrapper(aObject);\n" 3622 3623 failureCode = dedent( 3624 """ 3625 aCache->ReleaseWrapper(aObject); 3626 aCache->ClearWrapper(); 3627 return false; 3628 """) 3629 3630 return fill( 3631 """ 3632 $*{assertInheritance} 3633 MOZ_ASSERT(!aCache->GetWrapper(), 3634 "You should probably not be using Wrap() directly; use " 3635 "GetOrCreateDOMReflector instead"); 3636 3637 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache), 3638 "nsISupports must be on our primary inheritance chain"); 3639 3640 JS::Rooted<JSObject*> global(aCx, FindAssociatedGlobal(aCx, aObject->GetParentObject())); 3641 if (!global) { 3642 return false; 3643 } 3644 MOZ_ASSERT(JS_IsGlobalObject(global)); 3645 MOZ_ASSERT(!JS::ObjectIsMarkedGray(global)); 3646 3647 // That might have ended up wrapping us already, due to the wonders 3648 // of XBL. Check for that, and bail out as needed. 3649 aReflector.set(aCache->GetWrapper()); 3650 if (aReflector) { 3651 #ifdef DEBUG 3652 binding_detail::AssertReflectorHasGivenProto(aCx, aReflector, aGivenProto); 3653 #endif // DEBUG 3654 return true; 3655 } 3656 3657 JSAutoCompartment ac(aCx, global); 3658 $*{declareProto} 3659 3660 $*{createObject} 3661 3662 aCache->SetWrapper(aReflector); 3663 $*{unforgeable} 3664 $*{slots} 3665 $*{setImmutablePrototype} 3666 creator.InitializationSucceeded(); 3667 3668 MOZ_ASSERT(aCache->GetWrapperPreserveColor() && 3669 aCache->GetWrapperPreserveColor() == aReflector); 3670 // If proto != canonicalProto, we have to preserve our wrapper; 3671 // otherwise we won't be able to properly recreate it later, since 3672 // we won't know what proto to use. Note that we don't check 3673 // aGivenProto here, since it's entirely possible (and even 3674 // somewhat common) to have a non-null aGivenProto which is the 3675 // same as canonicalProto. 3676 if (proto != canonicalProto) { 3677 $*{preserveWrapper} 3678 } 3679 3680 return true; 3681 """, 3682 assertInheritance=AssertInheritanceChain(self.descriptor), 3683 declareProto=DeclareProto(), 3684 createObject=CreateBindingJSObject(self.descriptor, self.properties), 3685 unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor, 3686 failureCode), 3687 slots=InitMemberSlots(self.descriptor, failureCode), 3688 setImmutablePrototype=SetImmutablePrototype(self.descriptor, 3689 failureCode), 3690 preserveWrapper=preserveWrapper) 3691 3692 3693class CGWrapMethod(CGAbstractMethod): 3694 def __init__(self, descriptor): 3695 # XXX can we wrap if we don't have an interface prototype object? 3696 assert descriptor.interface.hasInterfacePrototypeObject() 3697 args = [Argument('JSContext*', 'aCx'), 3698 Argument('T*', 'aObject'), 3699 Argument('JS::Handle<JSObject*>', 'aGivenProto')] 3700 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args, 3701 inline=True, templateArgs=["class T"]) 3702 3703 def definition_body(self): 3704 return dedent(""" 3705 JS::Rooted<JSObject*> reflector(aCx); 3706 return Wrap(aCx, aObject, aObject, aGivenProto, &reflector) ? reflector.get() : nullptr; 3707 """) 3708 3709 3710class CGWrapNonWrapperCacheMethod(CGAbstractMethod): 3711 """ 3712 Create a wrapper JSObject for a given native that does not implement 3713 nsWrapperCache. 3714 3715 properties should be a PropertyArrays instance. 3716 """ 3717 def __init__(self, descriptor, properties): 3718 # XXX can we wrap if we don't have an interface prototype object? 3719 assert descriptor.interface.hasInterfacePrototypeObject() 3720 args = [Argument('JSContext*', 'aCx'), 3721 Argument(descriptor.nativeType + '*', 'aObject'), 3722 Argument('JS::Handle<JSObject*>', 'aGivenProto'), 3723 Argument('JS::MutableHandle<JSObject*>', 'aReflector')] 3724 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args) 3725 self.properties = properties 3726 3727 def definition_body(self): 3728 failureCode = "return false;\n" 3729 3730 return fill( 3731 """ 3732 $*{assertions} 3733 3734 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx)); 3735 $*{declareProto} 3736 3737 $*{createObject} 3738 3739 $*{unforgeable} 3740 3741 $*{slots} 3742 3743 $*{setImmutablePrototype} 3744 creator.InitializationSucceeded(); 3745 return true; 3746 """, 3747 assertions=AssertInheritanceChain(self.descriptor), 3748 declareProto=DeclareProto(), 3749 createObject=CreateBindingJSObject(self.descriptor, self.properties), 3750 unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor, 3751 failureCode), 3752 slots=InitMemberSlots(self.descriptor, failureCode), 3753 setImmutablePrototype=SetImmutablePrototype(self.descriptor, 3754 failureCode)) 3755 3756 3757class CGWrapGlobalMethod(CGAbstractMethod): 3758 """ 3759 Create a wrapper JSObject for a global. The global must implement 3760 nsWrapperCache. 3761 3762 properties should be a PropertyArrays instance. 3763 """ 3764 def __init__(self, descriptor, properties): 3765 assert descriptor.interface.hasInterfacePrototypeObject() 3766 args = [Argument('JSContext*', 'aCx'), 3767 Argument(descriptor.nativeType + '*', 'aObject'), 3768 Argument('nsWrapperCache*', 'aCache'), 3769 Argument('JS::CompartmentOptions&', 'aOptions'), 3770 Argument('JSPrincipals*', 'aPrincipal'), 3771 Argument('bool', 'aInitStandardClasses'), 3772 Argument('JS::MutableHandle<JSObject*>', 'aReflector')] 3773 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args) 3774 self.descriptor = descriptor 3775 self.properties = properties 3776 3777 def definition_body(self): 3778 if self.properties.hasNonChromeOnly(): 3779 properties = "sNativeProperties.Upcast()" 3780 else: 3781 properties = "nullptr" 3782 if self.properties.hasChromeOnly(): 3783 chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? sChromeOnlyNativeProperties.Upcast() : nullptr" 3784 else: 3785 chromeProperties = "nullptr" 3786 3787 failureCode = dedent( 3788 """ 3789 aCache->ReleaseWrapper(aObject); 3790 aCache->ClearWrapper(); 3791 return false; 3792 """); 3793 3794 if self.descriptor.hasUnforgeableMembers: 3795 unforgeable = InitUnforgeablePropertiesOnHolder( 3796 self.descriptor, self.properties, failureCode, 3797 "aReflector").define(); 3798 else: 3799 unforgeable = "" 3800 3801 return fill( 3802 """ 3803 $*{assertions} 3804 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache), 3805 "nsISupports must be on our primary inheritance chain"); 3806 3807 if (!CreateGlobal<${nativeType}, GetProtoObjectHandle>(aCx, 3808 aObject, 3809 aCache, 3810 sClass.ToJSClass(), 3811 aOptions, 3812 aPrincipal, 3813 aInitStandardClasses, 3814 aReflector)) { 3815 $*{failureCode} 3816 } 3817 3818 // aReflector is a new global, so has a new compartment. Enter it 3819 // before doing anything with it. 3820 JSAutoCompartment ac(aCx, aReflector); 3821 3822 if (!DefineProperties(aCx, aReflector, ${properties}, ${chromeProperties})) { 3823 $*{failureCode} 3824 } 3825 $*{unforgeable} 3826 3827 $*{slots} 3828 3829 return true; 3830 """, 3831 assertions=AssertInheritanceChain(self.descriptor), 3832 nativeType=self.descriptor.nativeType, 3833 properties=properties, 3834 chromeProperties=chromeProperties, 3835 failureCode=failureCode, 3836 unforgeable=unforgeable, 3837 slots=InitMemberSlots(self.descriptor, failureCode)) 3838 3839 3840class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod): 3841 def __init__(self, descriptor): 3842 args = [Argument('JSContext*', 'aCx'), 3843 Argument('JS::Handle<JSObject*>', 'aWrapper'), 3844 Argument(descriptor.nativeType + '*', 'aObject')] 3845 CGAbstractStaticMethod.__init__(self, descriptor, 'UpdateMemberSlots', 'bool', args) 3846 3847 def definition_body(self): 3848 body = ("JS::Rooted<JS::Value> temp(aCx);\n" 3849 "JSJitGetterCallArgs args(&temp);\n") 3850 for m in self.descriptor.interface.members: 3851 if m.isAttr() and m.getExtendedAttribute("StoreInSlot"): 3852 # Skip doing this for the "window" and "self" attributes on the 3853 # Window interface, because those can't be gotten safely until 3854 # we have hooked it up correctly to the outer window. The 3855 # window code handles doing the get itself. 3856 if (self.descriptor.interface.identifier.name == "Window" and 3857 (m.identifier.name == "window" or m.identifier.name == "self")): 3858 continue 3859 body += fill( 3860 """ 3861 3862 if (!get_${member}(aCx, aWrapper, aObject, args)) { 3863 return false; 3864 } 3865 // Getter handled setting our reserved slots 3866 """, 3867 member=m.identifier.name) 3868 3869 body += "\nreturn true;\n" 3870 return body 3871 3872 3873class CGClearCachedValueMethod(CGAbstractMethod): 3874 def __init__(self, descriptor, member): 3875 self.member = member 3876 # If we're StoreInSlot, we'll need to call the getter 3877 if member.getExtendedAttribute("StoreInSlot"): 3878 args = [Argument('JSContext*', 'aCx')] 3879 returnType = 'bool' 3880 else: 3881 args = [] 3882 returnType = 'void' 3883 args.append(Argument(descriptor.nativeType + '*', 'aObject')) 3884 name = MakeClearCachedValueNativeName(member) 3885 CGAbstractMethod.__init__(self, descriptor, name, returnType, args) 3886 3887 def definition_body(self): 3888 slotIndex = memberReservedSlot(self.member, self.descriptor) 3889 if self.member.getExtendedAttribute("StoreInSlot"): 3890 # We have to root things and save the old value in case 3891 # regetting fails, so we can restore it. 3892 declObj = "JS::Rooted<JSObject*> obj(aCx);\n" 3893 noopRetval = " true" 3894 saveMember = ( 3895 "JS::Rooted<JS::Value> oldValue(aCx, js::GetReservedSlot(obj, %s));\n" % 3896 slotIndex) 3897 regetMember = fill( 3898 """ 3899 JS::Rooted<JS::Value> temp(aCx); 3900 JSJitGetterCallArgs args(&temp); 3901 JSAutoCompartment ac(aCx, obj); 3902 if (!get_${name}(aCx, obj, aObject, args)) { 3903 js::SetReservedSlot(obj, ${slotIndex}, oldValue); 3904 return false; 3905 } 3906 return true; 3907 """, 3908 name=self.member.identifier.name, 3909 slotIndex=slotIndex) 3910 else: 3911 declObj = "JSObject* obj;\n" 3912 noopRetval = "" 3913 saveMember = "" 3914 regetMember = "" 3915 3916 if self.descriptor.wantsXrays: 3917 clearXrayExpandoSlots = fill( 3918 """ 3919 xpc::ClearXrayExpandoSlots(obj, ${xraySlotIndex}); 3920 """, 3921 xraySlotIndex=memberXrayExpandoReservedSlot(self.member, 3922 self.descriptor)) 3923 else : 3924 clearXrayExpandoSlots = "" 3925 3926 return fill( 3927 """ 3928 $*{declObj} 3929 obj = aObject->GetWrapper(); 3930 if (!obj) { 3931 return${noopRetval}; 3932 } 3933 $*{saveMember} 3934 js::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue()); 3935 $*{clearXrayExpandoSlots} 3936 $*{regetMember} 3937 """, 3938 declObj=declObj, 3939 noopRetval=noopRetval, 3940 saveMember=saveMember, 3941 slotIndex=slotIndex, 3942 clearXrayExpandoSlots=clearXrayExpandoSlots, 3943 regetMember=regetMember) 3944 3945 3946class CGIsPermittedMethod(CGAbstractMethod): 3947 """ 3948 crossOriginGetters/Setters/Methods are sets of names of the relevant members. 3949 """ 3950 def __init__(self, descriptor, crossOriginGetters, crossOriginSetters, 3951 crossOriginMethods): 3952 self.crossOriginGetters = crossOriginGetters 3953 self.crossOriginSetters = crossOriginSetters 3954 self.crossOriginMethods = crossOriginMethods 3955 args = [Argument("JSFlatString*", "prop"), 3956 Argument("char16_t", "propFirstChar"), 3957 Argument("bool", "set")] 3958 CGAbstractMethod.__init__(self, descriptor, "IsPermitted", "bool", args, 3959 inline=True) 3960 3961 def definition_body(self): 3962 allNames = self.crossOriginGetters | self.crossOriginSetters | self.crossOriginMethods 3963 readwrite = self.crossOriginGetters & self.crossOriginSetters 3964 readonly = (self.crossOriginGetters - self.crossOriginSetters) | self.crossOriginMethods 3965 writeonly = self.crossOriginSetters - self.crossOriginGetters 3966 cases = {} 3967 for name in sorted(allNames): 3968 cond = 'JS_FlatStringEqualsAscii(prop, "%s")' % name 3969 if name in readonly: 3970 cond = "!set && %s" % cond 3971 elif name in writeonly: 3972 cond = "set && %s" % cond 3973 else: 3974 assert name in readwrite 3975 firstLetter = name[0] 3976 case = cases.get(firstLetter, CGList([])) 3977 case.append(CGGeneric("if (%s) {\n" 3978 " return true;\n" 3979 "}\n" % cond)) 3980 cases[firstLetter] = case 3981 caseList = [] 3982 for firstLetter in sorted(cases.keys()): 3983 caseList.append(CGCase("'%s'" % firstLetter, cases[firstLetter])) 3984 switch = CGSwitch("propFirstChar", caseList) 3985 return switch.define() + "\nreturn false;\n" 3986 3987 3988class CGCycleCollectionTraverseForOwningUnionMethod(CGAbstractMethod): 3989 """ 3990 ImplCycleCollectionUnlink for owning union type. 3991 """ 3992 def __init__(self, type): 3993 self.type = type 3994 args = [Argument("nsCycleCollectionTraversalCallback&", "aCallback"), 3995 Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion"), 3996 Argument("const char*", "aName"), 3997 Argument("uint32_t", "aFlags", "0")] 3998 CGAbstractMethod.__init__(self, None, "ImplCycleCollectionTraverse", "void", args) 3999 4000 def deps(self): 4001 return self.type.getDeps() 4002 4003 def definition_body(self): 4004 memberNames = [getUnionMemberName(t) 4005 for t in self.type.flatMemberTypes 4006 if idlTypeNeedsCycleCollection(t)] 4007 assert memberNames 4008 4009 conditionTemplate = 'aUnion.Is%s()' 4010 functionCallTemplate = 'ImplCycleCollectionTraverse(aCallback, aUnion.GetAs%s(), "m%s", aFlags);\n' 4011 4012 ifStaments = (CGIfWrapper(CGGeneric(functionCallTemplate % (m, m)), 4013 conditionTemplate % m) 4014 for m in memberNames) 4015 4016 return CGElseChain(ifStaments).define() 4017 4018 4019class CGCycleCollectionUnlinkForOwningUnionMethod(CGAbstractMethod): 4020 """ 4021 ImplCycleCollectionUnlink for owning union type. 4022 """ 4023 def __init__(self, type): 4024 self.type = type 4025 args = [Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion")] 4026 CGAbstractMethod.__init__(self, None, "ImplCycleCollectionUnlink", "void", args) 4027 4028 def deps(self): 4029 return self.type.getDeps() 4030 4031 def definition_body(self): 4032 return "aUnion.Uninit();\n" 4033 4034 4035builtinNames = { 4036 IDLType.Tags.bool: 'bool', 4037 IDLType.Tags.int8: 'int8_t', 4038 IDLType.Tags.int16: 'int16_t', 4039 IDLType.Tags.int32: 'int32_t', 4040 IDLType.Tags.int64: 'int64_t', 4041 IDLType.Tags.uint8: 'uint8_t', 4042 IDLType.Tags.uint16: 'uint16_t', 4043 IDLType.Tags.uint32: 'uint32_t', 4044 IDLType.Tags.uint64: 'uint64_t', 4045 IDLType.Tags.unrestricted_float: 'float', 4046 IDLType.Tags.float: 'float', 4047 IDLType.Tags.unrestricted_double: 'double', 4048 IDLType.Tags.double: 'double' 4049} 4050 4051numericSuffixes = { 4052 IDLType.Tags.int8: '', 4053 IDLType.Tags.uint8: '', 4054 IDLType.Tags.int16: '', 4055 IDLType.Tags.uint16: '', 4056 IDLType.Tags.int32: '', 4057 IDLType.Tags.uint32: 'U', 4058 IDLType.Tags.int64: 'LL', 4059 IDLType.Tags.uint64: 'ULL', 4060 IDLType.Tags.unrestricted_float: 'F', 4061 IDLType.Tags.float: 'F', 4062 IDLType.Tags.unrestricted_double: '', 4063 IDLType.Tags.double: '' 4064} 4065 4066 4067def numericValue(t, v): 4068 if (t == IDLType.Tags.unrestricted_double or 4069 t == IDLType.Tags.unrestricted_float): 4070 typeName = builtinNames[t] 4071 if v == float("inf"): 4072 return "mozilla::PositiveInfinity<%s>()" % typeName 4073 if v == float("-inf"): 4074 return "mozilla::NegativeInfinity<%s>()" % typeName 4075 if math.isnan(v): 4076 return "mozilla::UnspecifiedNaN<%s>()" % typeName 4077 return "%s%s" % (v, numericSuffixes[t]) 4078 4079 4080class CastableObjectUnwrapper(): 4081 """ 4082 A class for unwrapping an object stored in a JS Value (or 4083 MutableHandle<Value> or Handle<Value>) named by the "source" and 4084 "mutableSource" arguments based on the passed-in descriptor and storing it 4085 in a variable called by the name in the "target" argument. The "source" 4086 argument should be able to produce a Value or Handle<Value>; the 4087 "mutableSource" argument should be able to produce a MutableHandle<Value> 4088 4089 codeOnFailure is the code to run if unwrapping fails. 4090 4091 If isCallbackReturnValue is "JSImpl" and our descriptor is also 4092 JS-implemented, fall back to just creating the right object if what we 4093 have isn't one already. 4094 4095 If allowCrossOriginObj is True, then we'll first do an 4096 UncheckedUnwrap and then operate on the result. 4097 """ 4098 def __init__(self, descriptor, source, mutableSource, target, codeOnFailure, 4099 exceptionCode=None, isCallbackReturnValue=False, 4100 allowCrossOriginObj=False): 4101 self.substitution = { 4102 "type": descriptor.nativeType, 4103 "protoID": "prototypes::id::" + descriptor.name, 4104 "target": target, 4105 "codeOnFailure": codeOnFailure, 4106 } 4107 # Supporting both the "cross origin object" case and the "has 4108 # XPConnect impls" case at the same time is a pain, so let's 4109 # not do that. That allows us to assume that our source is 4110 # always a Handle or MutableHandle. 4111 if allowCrossOriginObj and descriptor.hasXPConnectImpls: 4112 raise TypeError("Interface %s both allows a cross-origin 'this' " 4113 "and has XPConnect impls. We don't support that" % 4114 descriptor.name) 4115 if allowCrossOriginObj: 4116 self.substitution["uncheckedObjDecl"] = fill( 4117 """ 4118 JS::Rooted<JSObject*> maybeUncheckedObj(cx, &${source}.toObject()); 4119 """, 4120 source=source) 4121 self.substitution["uncheckedObjGet"] = fill( 4122 """ 4123 if (xpc::WrapperFactory::IsXrayWrapper(maybeUncheckedObj)) { 4124 maybeUncheckedObj = js::UncheckedUnwrap(maybeUncheckedObj); 4125 } else { 4126 maybeUncheckedObj = js::CheckedUnwrap(maybeUncheckedObj); 4127 if (!maybeUncheckedObj) { 4128 $*{codeOnFailure} 4129 } 4130 } 4131 """, 4132 codeOnFailure=(codeOnFailure % { 'securityError': 'true'})) 4133 self.substitution["source"] = "maybeUncheckedObj" 4134 self.substitution["mutableSource"] = "&maybeUncheckedObj" 4135 # No need to set up xpconnectUnwrap, since it won't be 4136 # used in the allowCrossOriginObj case. 4137 else: 4138 self.substitution["uncheckedObjDecl"] = "" 4139 self.substitution["uncheckedObjGet"] = "" 4140 self.substitution["source"] = source 4141 self.substitution["mutableSource"] = mutableSource 4142 xpconnectUnwrap = ( 4143 "nsresult rv = UnwrapXPConnect<${type}>(cx, ${mutableSource}, getter_AddRefs(objPtr));\n") 4144 4145 if descriptor.hasXPConnectImpls: 4146 self.substitution["codeOnFailure"] = string.Template( 4147 "RefPtr<${type}> objPtr;\n" + 4148 xpconnectUnwrap + 4149 "if (NS_FAILED(rv)) {\n" 4150 "${indentedCodeOnFailure}" 4151 "}\n" 4152 "// We should have an object\n" 4153 "MOZ_ASSERT(objPtr);\n" 4154 "${target} = objPtr;\n" 4155 ).substitute(self.substitution, 4156 indentedCodeOnFailure=indent(codeOnFailure)) 4157 elif (isCallbackReturnValue == "JSImpl" and 4158 descriptor.interface.isJSImplemented()): 4159 exceptionCode = exceptionCode or codeOnFailure 4160 self.substitution["codeOnFailure"] = fill( 4161 """ 4162 // Be careful to not wrap random DOM objects here, even if 4163 // they're wrapped in opaque security wrappers for some reason. 4164 // XXXbz Wish we could check for a JS-implemented object 4165 // that already has a content reflection... 4166 if (!IsDOMObject(js::UncheckedUnwrap(&${source}.toObject()))) { 4167 nsCOMPtr<nsIGlobalObject> contentGlobal; 4168 if (!GetContentGlobalForJSImplementedObject(cx, Callback(), getter_AddRefs(contentGlobal))) { 4169 $*{exceptionCode} 4170 } 4171 JS::Rooted<JSObject*> jsImplSourceObj(cx, &${source}.toObject()); 4172 ${target} = new ${type}(jsImplSourceObj, contentGlobal); 4173 } else { 4174 $*{codeOnFailure} 4175 } 4176 """, 4177 exceptionCode=exceptionCode, 4178 **self.substitution) 4179 else: 4180 self.substitution["codeOnFailure"] = codeOnFailure 4181 4182 def __str__(self): 4183 substitution = self.substitution.copy() 4184 substitution["codeOnFailure"] %= { 4185 'securityError': 'rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO' 4186 } 4187 return fill( 4188 """ 4189 $*{uncheckedObjDecl} 4190 { 4191 $*{uncheckedObjGet} 4192 nsresult rv = UnwrapObject<${protoID}, ${type}>(${mutableSource}, ${target}); 4193 if (NS_FAILED(rv)) { 4194 $*{codeOnFailure} 4195 } 4196 } 4197 """, 4198 **substitution) 4199 4200 4201class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper): 4202 """ 4203 As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails 4204 """ 4205 def __init__(self, descriptor, source, mutableSource, target, exceptionCode, 4206 isCallbackReturnValue, sourceDescription): 4207 CastableObjectUnwrapper.__init__( 4208 self, descriptor, source, mutableSource, target, 4209 'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n' 4210 '%s' % (sourceDescription, descriptor.interface.identifier.name, 4211 exceptionCode), 4212 exceptionCode, 4213 isCallbackReturnValue) 4214 4215 4216class CGCallbackTempRoot(CGGeneric): 4217 def __init__(self, name): 4218 define = dedent(""" 4219 { // Scope for tempRoot 4220 JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject()); 4221 ${declName} = new %s(cx, tempRoot, mozilla::dom::GetIncumbentGlobal()); 4222 } 4223 """) % name 4224 CGGeneric.__init__(self, define=define) 4225 4226 4227def getCallbackConversionInfo(type, idlObject, isMember, isCallbackReturnValue, 4228 isOptional): 4229 """ 4230 Returns a tuple containing the declType, declArgs, and basic 4231 conversion for the given callback type, with the given callback 4232 idl object in the given context (isMember/isCallbackReturnValue/isOptional). 4233 """ 4234 name = idlObject.identifier.name 4235 4236 # We can't use fast callbacks if isOptional because then we get an 4237 # Optional<RootedCallback> thing, which is not transparent to consumers. 4238 useFastCallback = (not isMember and not isCallbackReturnValue and 4239 not isOptional) 4240 if useFastCallback: 4241 name = "binding_detail::Fast%s" % name 4242 4243 if type.nullable() or isCallbackReturnValue: 4244 declType = CGGeneric("RefPtr<%s>" % name) 4245 else: 4246 declType = CGGeneric("OwningNonNull<%s>" % name) 4247 4248 if useFastCallback: 4249 declType = CGTemplatedType("RootedCallback", declType) 4250 declArgs = "cx" 4251 else: 4252 declArgs = None 4253 4254 conversion = indent(CGCallbackTempRoot(name).define()) 4255 return (declType, declArgs, conversion) 4256 4257 4258class JSToNativeConversionInfo(): 4259 """ 4260 An object representing information about a JS-to-native conversion. 4261 """ 4262 def __init__(self, template, declType=None, holderType=None, 4263 dealWithOptional=False, declArgs=None, 4264 holderArgs=None): 4265 """ 4266 template: A string representing the conversion code. This will have 4267 template substitution performed on it as follows: 4268 4269 ${val} is a handle to the JS::Value in question 4270 ${maybeMutableVal} May be a mutable handle to the JS::Value in 4271 question. This is only OK to use if ${val} is 4272 known to not be undefined. 4273 ${holderName} replaced by the holder's name, if any 4274 ${declName} replaced by the declaration's name 4275 ${haveValue} replaced by an expression that evaluates to a boolean 4276 for whether we have a JS::Value. Only used when 4277 defaultValue is not None or when True is passed for 4278 checkForValue to instantiateJSToNativeConversion. 4279 ${passedToJSImpl} replaced by an expression that evaluates to a boolean 4280 for whether this value is being passed to a JS- 4281 implemented interface. 4282 4283 declType: A CGThing representing the native C++ type we're converting 4284 to. This is allowed to be None if the conversion code is 4285 supposed to be used as-is. 4286 4287 holderType: A CGThing representing the type of a "holder" which will 4288 hold a possible reference to the C++ thing whose type we 4289 returned in declType, or None if no such holder is needed. 4290 4291 dealWithOptional: A boolean indicating whether the caller has to do 4292 optional-argument handling. This should only be set 4293 to true if the JS-to-native conversion is being done 4294 for an optional argument or dictionary member with no 4295 default value and if the returned template expects 4296 both declType and holderType to be wrapped in 4297 Optional<>, with ${declName} and ${holderName} 4298 adjusted to point to the Value() of the Optional, and 4299 Construct() calls to be made on the Optional<>s as 4300 needed. 4301 4302 declArgs: If not None, the arguments to pass to the ${declName} 4303 constructor. These will have template substitution performed 4304 on them so you can use things like ${val}. This is a 4305 single string, not a list of strings. 4306 4307 holderArgs: If not None, the arguments to pass to the ${holderName} 4308 constructor. These will have template substitution 4309 performed on them so you can use things like ${val}. 4310 This is a single string, not a list of strings. 4311 4312 ${declName} must be in scope before the code from 'template' is entered. 4313 4314 If holderType is not None then ${holderName} must be in scope before 4315 the code from 'template' is entered. 4316 """ 4317 assert isinstance(template, str) 4318 assert declType is None or isinstance(declType, CGThing) 4319 assert holderType is None or isinstance(holderType, CGThing) 4320 self.template = template 4321 self.declType = declType 4322 self.holderType = holderType 4323 self.dealWithOptional = dealWithOptional 4324 self.declArgs = declArgs 4325 self.holderArgs = holderArgs 4326 4327 4328def getHandleDefault(defaultValue): 4329 tag = defaultValue.type.tag() 4330 if tag in numericSuffixes: 4331 # Some numeric literals require a suffix to compile without warnings 4332 return numericValue(tag, defaultValue.value) 4333 assert tag == IDLType.Tags.bool 4334 return toStringBool(defaultValue.value) 4335 4336 4337def handleDefaultStringValue(defaultValue, method): 4338 """ 4339 Returns a string which ends up calling 'method' with a (char_t*, length) 4340 pair that sets this string default value. This string is suitable for 4341 passing as the second argument of handleDefault; in particular it does not 4342 end with a ';' 4343 """ 4344 assert defaultValue.type.isDOMString() or defaultValue.type.isByteString() 4345 return ("static const %(char_t)s data[] = { %(data)s };\n" 4346 "%(method)s(data, ArrayLength(data) - 1)") % { 4347 'char_t': "char" if defaultValue.type.isByteString() else "char16_t", 4348 'method': method, 4349 'data': ", ".join(["'" + char + "'" for char in 4350 defaultValue.value] + ["0"]) 4351 } 4352 4353 4354# If this function is modified, modify CGNativeMember.getArg and 4355# CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype 4356# and holdertype we end up using, because it needs to be able to return the code 4357# that will convert those to the actual return value of the callback function. 4358def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, 4359 isDefinitelyObject=False, 4360 isMember=False, 4361 isOptional=False, 4362 invalidEnumValueFatal=True, 4363 defaultValue=None, 4364 treatNullAs="Default", 4365 isEnforceRange=False, 4366 isClamp=False, 4367 isNullOrUndefined=False, 4368 exceptionCode=None, 4369 lenientFloatCode=None, 4370 allowTreatNonCallableAsNull=False, 4371 isCallbackReturnValue=False, 4372 sourceDescription="value", 4373 nestingLevel=""): 4374 """ 4375 Get a template for converting a JS value to a native object based on the 4376 given type and descriptor. If failureCode is given, then we're actually 4377 testing whether we can convert the argument to the desired type. That 4378 means that failures to convert due to the JS value being the wrong type of 4379 value need to use failureCode instead of throwing exceptions. Failures to 4380 convert that are due to JS exceptions (from toString or valueOf methods) or 4381 out of memory conditions need to throw exceptions no matter what 4382 failureCode is. However what actually happens when throwing an exception 4383 can be controlled by exceptionCode. The only requirement on that is that 4384 exceptionCode must end up doing a return, and every return from this 4385 function must happen via exceptionCode if exceptionCode is not None. 4386 4387 If isDefinitelyObject is True, that means we know the value 4388 isObject() and we have no need to recheck that. 4389 4390 if isMember is not False, we're being converted from a property of some JS 4391 object, not from an actual method argument, so we can't rely on our jsval 4392 being rooted or outliving us in any way. Callers can pass "Dictionary", 4393 "Variadic", "Sequence", or "OwningUnion" to indicate that the conversion is 4394 for something that is a dictionary member, a variadic argument, a sequence, 4395 or an owning union respectively. 4396 4397 If isOptional is true, then we are doing conversion of an optional 4398 argument with no default value. 4399 4400 invalidEnumValueFatal controls whether an invalid enum value conversion 4401 attempt will throw (if true) or simply return without doing anything (if 4402 false). 4403 4404 If defaultValue is not None, it's the IDL default value for this conversion 4405 4406 If isEnforceRange is true, we're converting an integer and throwing if the 4407 value is out of range. 4408 4409 If isClamp is true, we're converting an integer and clamping if the 4410 value is out of range. 4411 4412 If lenientFloatCode is not None, it should be used in cases when 4413 we're a non-finite float that's not unrestricted. 4414 4415 If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and 4416 [TreatNonObjectAsNull] extended attributes on nullable callback functions 4417 will be honored. 4418 4419 If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be 4420 adjusted to make it easier to return from a callback. Since that type is 4421 never directly observable by any consumers of the callback code, this is OK. 4422 Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior 4423 of the FailureFatalCastableObjectUnwrapper conversion; this is used for 4424 implementing auto-wrapping of JS-implemented return values from a 4425 JS-implemented interface. 4426 4427 sourceDescription is a description of what this JS value represents, to be 4428 used in error reporting. Callers should assume that it might get placed in 4429 the middle of a sentence. If it ends up at the beginning of a sentence, its 4430 first character will be automatically uppercased. 4431 4432 The return value from this function is a JSToNativeConversionInfo. 4433 """ 4434 # If we have a defaultValue then we're not actually optional for 4435 # purposes of what we need to be declared as. 4436 assert defaultValue is None or not isOptional 4437 4438 # Also, we should not have a defaultValue if we know we're an object 4439 assert not isDefinitelyObject or defaultValue is None 4440 4441 # And we can't both be an object and be null or undefined 4442 assert not isDefinitelyObject or not isNullOrUndefined 4443 4444 # If exceptionCode is not set, we'll just rethrow the exception we got. 4445 # Note that we can't just set failureCode to exceptionCode, because setting 4446 # failureCode will prevent pending exceptions from being set in cases when 4447 # they really should be! 4448 if exceptionCode is None: 4449 exceptionCode = "return false;\n" 4450 4451 # Unfortunately, .capitalize() on a string will lowercase things inside the 4452 # string, which we do not want. 4453 def firstCap(string): 4454 return string[0].upper() + string[1:] 4455 4456 # Helper functions for dealing with failures due to the JS value being the 4457 # wrong type of value 4458 def onFailureNotAnObject(failureCode): 4459 return CGGeneric( 4460 failureCode or 4461 ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n' 4462 '%s' % (firstCap(sourceDescription), exceptionCode))) 4463 4464 def onFailureBadType(failureCode, typeName): 4465 return CGGeneric( 4466 failureCode or 4467 ('ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n' 4468 '%s' % (firstCap(sourceDescription), typeName, exceptionCode))) 4469 4470 def onFailureNotCallable(failureCode): 4471 return CGGeneric( 4472 failureCode or 4473 ('ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "%s");\n' 4474 '%s' % (firstCap(sourceDescription), exceptionCode))) 4475 4476 # A helper function for handling default values. Takes a template 4477 # body and the C++ code to set the default value and wraps the 4478 # given template body in handling for the default value. 4479 def handleDefault(template, setDefault): 4480 if defaultValue is None: 4481 return template 4482 return ( 4483 "if (${haveValue}) {\n" + 4484 indent(template) + 4485 "} else {\n" + 4486 indent(setDefault) + 4487 "}\n") 4488 4489 # A helper function for wrapping up the template body for 4490 # possibly-nullable objecty stuff 4491 def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None): 4492 if isNullOrUndefined and type.nullable(): 4493 # Just ignore templateBody and set ourselves to null. 4494 # Note that we don't have to worry about default values 4495 # here either, since we already examined this value. 4496 return codeToSetNull 4497 4498 if not isDefinitelyObject: 4499 # Handle the non-object cases by wrapping up the whole 4500 # thing in an if cascade. 4501 if type.nullable(): 4502 elifLine = "} else if (${val}.isNullOrUndefined()) {\n" 4503 elifBody = codeToSetNull 4504 else: 4505 elifLine = "" 4506 elifBody = "" 4507 4508 # Note that $${val} below expands to ${val}. This string is 4509 # used as a template later, and val will be filled in then. 4510 templateBody = fill( 4511 """ 4512 if ($${val}.isObject()) { 4513 $*{templateBody} 4514 $*{elifLine} 4515 $*{elifBody} 4516 } else { 4517 $*{failureBody} 4518 } 4519 """, 4520 templateBody=templateBody, 4521 elifLine=elifLine, 4522 elifBody=elifBody, 4523 failureBody=onFailureNotAnObject(failureCode).define()) 4524 4525 if isinstance(defaultValue, IDLNullValue): 4526 assert type.nullable() # Parser should enforce this 4527 templateBody = handleDefault(templateBody, codeToSetNull) 4528 elif isinstance(defaultValue, IDLEmptySequenceValue): 4529 # Our caller will handle it 4530 pass 4531 else: 4532 assert defaultValue is None 4533 4534 return templateBody 4535 4536 # A helper function for converting things that look like a JSObject*. 4537 def handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription): 4538 if not isMember: 4539 if isOptional: 4540 # We have a specialization of Optional that will use a 4541 # Rooted for the storage here. 4542 declType = CGGeneric("JS::Handle<JSObject*>") 4543 else: 4544 declType = CGGeneric("JS::Rooted<JSObject*>") 4545 declArgs = "cx" 4546 else: 4547 assert (isMember in 4548 ("Sequence", "Variadic", "Dictionary", "OwningUnion", "MozMap")) 4549 # We'll get traced by the sequence or dictionary or union tracer 4550 declType = CGGeneric("JSObject*") 4551 declArgs = None 4552 templateBody = "${declName} = &${val}.toObject();\n" 4553 4554 # For JS-implemented APIs, we refuse to allow passing objects that the 4555 # API consumer does not subsume. The extra parens around 4556 # ($${passedToJSImpl}) suppress unreachable code warnings when 4557 # $${passedToJSImpl} is the literal `false`. 4558 if not isinstance(descriptorProvider, Descriptor) or descriptorProvider.interface.isJSImplemented(): 4559 templateBody = fill( 4560 """ 4561 if (($${passedToJSImpl}) && !CallerSubsumes($${val})) { 4562 ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "${sourceDescription}"); 4563 $*{exceptionCode} 4564 } 4565 """, 4566 sourceDescription=sourceDescription, 4567 exceptionCode=exceptionCode) + templateBody 4568 4569 setToNullCode = "${declName} = nullptr;\n" 4570 template = wrapObjectTemplate(templateBody, type, setToNullCode, 4571 failureCode) 4572 return JSToNativeConversionInfo(template, declType=declType, 4573 dealWithOptional=isOptional, 4574 declArgs=declArgs) 4575 4576 def incrementNestingLevel(): 4577 if nestingLevel is "": 4578 return 1 4579 return nestingLevel + 1 4580 4581 assert not (isEnforceRange and isClamp) # These are mutually exclusive 4582 4583 if type.isSequence(): 4584 assert not isEnforceRange and not isClamp 4585 4586 if failureCode is None: 4587 notSequence = ('ThrowErrorMessage(cx, MSG_NOT_SEQUENCE, "%s");\n' 4588 "%s" % (firstCap(sourceDescription), exceptionCode)) 4589 else: 4590 notSequence = failureCode 4591 4592 nullable = type.nullable() 4593 # Be very careful not to change "type": we need it later 4594 if nullable: 4595 elementType = type.inner.inner 4596 else: 4597 elementType = type.inner 4598 4599 # We want to use auto arrays if we can, but we have to be careful with 4600 # reallocation behavior for arrays. In particular, if we use auto 4601 # arrays for sequences and have a sequence of elements which are 4602 # themselves sequences or have sequences as members, we have a problem. 4603 # In that case, resizing the outermost AutoTArray to the right size 4604 # will memmove its elements, but AutoTArrays are not memmovable and 4605 # hence will end up with pointers to bogus memory, which is bad. To 4606 # deal with this, we typically map WebIDL sequences to our Sequence 4607 # type, which is in fact memmovable. The one exception is when we're 4608 # passing in a sequence directly as an argument without any sort of 4609 # optional or nullable complexity going on. In that situation, we can 4610 # use an AutoSequence instead. We have to keep using Sequence in the 4611 # nullable and optional cases because we don't want to leak the 4612 # AutoSequence type to consumers, which would be unavoidable with 4613 # Nullable<AutoSequence> or Optional<AutoSequence>. 4614 if isMember or isOptional or nullable or isCallbackReturnValue: 4615 sequenceClass = "Sequence" 4616 else: 4617 sequenceClass = "binding_detail::AutoSequence" 4618 4619 # XXXbz we can't include the index in the sourceDescription, because 4620 # we don't really have a way to pass one in dynamically at runtime... 4621 elementInfo = getJSToNativeConversionInfo( 4622 elementType, descriptorProvider, isMember="Sequence", 4623 exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode, 4624 isCallbackReturnValue=isCallbackReturnValue, 4625 sourceDescription="element of %s" % sourceDescription, 4626 nestingLevel=incrementNestingLevel()) 4627 if elementInfo.dealWithOptional: 4628 raise TypeError("Shouldn't have optional things in sequences") 4629 if elementInfo.holderType is not None: 4630 raise TypeError("Shouldn't need holders for sequences") 4631 4632 typeName = CGTemplatedType(sequenceClass, elementInfo.declType) 4633 sequenceType = typeName.define() 4634 if nullable: 4635 typeName = CGTemplatedType("Nullable", typeName) 4636 arrayRef = "${declName}.SetValue()" 4637 else: 4638 arrayRef = "${declName}" 4639 4640 elementConversion = string.Template(elementInfo.template).substitute({ 4641 "val": "temp" + str(nestingLevel), 4642 "maybeMutableVal": "&temp" + str(nestingLevel), 4643 "declName": "slot" + str(nestingLevel), 4644 # We only need holderName here to handle isExternal() 4645 # interfaces, which use an internal holder for the 4646 # conversion even when forceOwningType ends up true. 4647 "holderName": "tempHolder" + str(nestingLevel), 4648 "passedToJSImpl": "${passedToJSImpl}" 4649 }) 4650 4651 # NOTE: Keep this in sync with variadic conversions as needed 4652 templateBody = fill( 4653 """ 4654 JS::ForOfIterator iter${nestingLevel}(cx); 4655 if (!iter${nestingLevel}.init($${val}, JS::ForOfIterator::AllowNonIterable)) { 4656 $*{exceptionCode} 4657 } 4658 if (!iter${nestingLevel}.valueIsIterable()) { 4659 $*{notSequence} 4660 } 4661 ${sequenceType} &arr${nestingLevel} = ${arrayRef}; 4662 JS::Rooted<JS::Value> temp${nestingLevel}(cx); 4663 while (true) { 4664 bool done${nestingLevel}; 4665 if (!iter${nestingLevel}.next(&temp${nestingLevel}, &done${nestingLevel})) { 4666 $*{exceptionCode} 4667 } 4668 if (done${nestingLevel}) { 4669 break; 4670 } 4671 ${elementType}* slotPtr${nestingLevel} = arr${nestingLevel}.AppendElement(mozilla::fallible); 4672 if (!slotPtr${nestingLevel}) { 4673 JS_ReportOutOfMemory(cx); 4674 $*{exceptionCode} 4675 } 4676 ${elementType}& slot${nestingLevel} = *slotPtr${nestingLevel}; 4677 $*{elementConversion} 4678 } 4679 """, 4680 exceptionCode=exceptionCode, 4681 notSequence=notSequence, 4682 sequenceType=sequenceType, 4683 arrayRef=arrayRef, 4684 elementType=elementInfo.declType.define(), 4685 elementConversion=elementConversion, 4686 nestingLevel=str(nestingLevel)) 4687 4688 templateBody = wrapObjectTemplate(templateBody, type, 4689 "${declName}.SetNull();\n", notSequence) 4690 if isinstance(defaultValue, IDLEmptySequenceValue): 4691 if type.nullable(): 4692 codeToSetEmpty = "${declName}.SetValue();\n" 4693 else: 4694 codeToSetEmpty = "/* Array is already empty; nothing to do */\n" 4695 templateBody = handleDefault(templateBody, codeToSetEmpty) 4696 4697 # Sequence arguments that might contain traceable things need 4698 # to get traced 4699 if not isMember and typeNeedsRooting(elementType): 4700 holderType = CGTemplatedType("SequenceRooter", elementInfo.declType) 4701 # If our sequence is nullable, this will set the Nullable to be 4702 # not-null, but that's ok because we make an explicit SetNull() call 4703 # on it as needed if our JS value is actually null. 4704 holderArgs = "cx, &%s" % arrayRef 4705 else: 4706 holderType = None 4707 holderArgs = None 4708 4709 return JSToNativeConversionInfo(templateBody, declType=typeName, 4710 holderType=holderType, 4711 dealWithOptional=isOptional, 4712 holderArgs=holderArgs) 4713 4714 if type.isMozMap(): 4715 assert not isEnforceRange and not isClamp 4716 if failureCode is None: 4717 notMozMap = ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n' 4718 "%s" % (firstCap(sourceDescription), exceptionCode)) 4719 else: 4720 notMozMap = failureCode 4721 4722 nullable = type.nullable() 4723 # Be very careful not to change "type": we need it later 4724 if nullable: 4725 valueType = type.inner.inner 4726 else: 4727 valueType = type.inner 4728 4729 valueInfo = getJSToNativeConversionInfo( 4730 valueType, descriptorProvider, isMember="MozMap", 4731 exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode, 4732 isCallbackReturnValue=isCallbackReturnValue, 4733 sourceDescription="value in %s" % sourceDescription, 4734 nestingLevel=incrementNestingLevel()) 4735 if valueInfo.dealWithOptional: 4736 raise TypeError("Shouldn't have optional things in MozMap") 4737 if valueInfo.holderType is not None: 4738 raise TypeError("Shouldn't need holders for MozMap") 4739 4740 typeName = CGTemplatedType("MozMap", valueInfo.declType) 4741 mozMapType = typeName.define() 4742 if nullable: 4743 typeName = CGTemplatedType("Nullable", typeName) 4744 mozMapRef = "${declName}.SetValue()" 4745 else: 4746 mozMapRef = "${declName}" 4747 4748 valueConversion = string.Template(valueInfo.template).substitute({ 4749 "val": "temp", 4750 "maybeMutableVal": "&temp", 4751 "declName": "slot", 4752 # We only need holderName here to handle isExternal() 4753 # interfaces, which use an internal holder for the 4754 # conversion even when forceOwningType ends up true. 4755 "holderName": "tempHolder", 4756 "passedToJSImpl": "${passedToJSImpl}" 4757 }) 4758 4759 templateBody = fill( 4760 """ 4761 ${mozMapType} &mozMap = ${mozMapRef}; 4762 4763 JS::Rooted<JSObject*> mozMapObj(cx, &$${val}.toObject()); 4764 JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx)); 4765 if (!JS_Enumerate(cx, mozMapObj, &ids)) { 4766 $*{exceptionCode} 4767 } 4768 JS::Rooted<JS::Value> propNameValue(cx); 4769 JS::Rooted<JS::Value> temp(cx); 4770 JS::Rooted<jsid> curId(cx); 4771 for (size_t i = 0; i < ids.length(); ++i) { 4772 // Make sure we get the value before converting the name, since 4773 // getting the value can trigger GC but our name is a dependent 4774 // string. 4775 curId = ids[i]; 4776 binding_detail::FakeString propName; 4777 bool isSymbol; 4778 if (!ConvertIdToString(cx, curId, propName, isSymbol) || 4779 (!isSymbol && !JS_GetPropertyById(cx, mozMapObj, curId, &temp))) { 4780 $*{exceptionCode} 4781 } 4782 if (isSymbol) { 4783 continue; 4784 } 4785 4786 ${valueType}* slotPtr = mozMap.AddEntry(propName); 4787 if (!slotPtr) { 4788 JS_ReportOutOfMemory(cx); 4789 $*{exceptionCode} 4790 } 4791 ${valueType}& slot = *slotPtr; 4792 $*{valueConversion} 4793 } 4794 """, 4795 exceptionCode=exceptionCode, 4796 mozMapType=mozMapType, 4797 mozMapRef=mozMapRef, 4798 valueType=valueInfo.declType.define(), 4799 valueConversion=valueConversion) 4800 4801 templateBody = wrapObjectTemplate(templateBody, type, 4802 "${declName}.SetNull();\n", 4803 notMozMap) 4804 4805 declType = typeName 4806 declArgs = None 4807 holderType = None 4808 holderArgs = None 4809 # MozMap arguments that might contain traceable things need 4810 # to get traced 4811 if not isMember and isCallbackReturnValue: 4812 # Go ahead and just convert directly into our actual return value 4813 declType = CGWrapper(declType, post="&") 4814 declArgs = "aRetVal" 4815 elif not isMember and typeNeedsRooting(valueType): 4816 holderType = CGTemplatedType("MozMapRooter", valueInfo.declType) 4817 # If our MozMap is nullable, this will set the Nullable to be 4818 # not-null, but that's ok because we make an explicit SetNull() call 4819 # on it as needed if our JS value is actually null. 4820 holderArgs = "cx, &%s" % mozMapRef 4821 4822 return JSToNativeConversionInfo(templateBody, declType=declType, 4823 declArgs=declArgs, 4824 holderType=holderType, 4825 dealWithOptional=isOptional, 4826 holderArgs=holderArgs) 4827 4828 if type.isUnion(): 4829 nullable = type.nullable() 4830 if nullable: 4831 type = type.inner 4832 4833 isOwningUnion = isMember or isCallbackReturnValue 4834 unionArgumentObj = "${declName}" if isOwningUnion else "${holderName}" 4835 if nullable: 4836 # If we're owning, we're a Nullable, which hasn't been told it has 4837 # a value. Otherwise we're an already-constructed Maybe. 4838 unionArgumentObj += ".SetValue()" if isOwningUnion else ".ref()" 4839 4840 memberTypes = type.flatMemberTypes 4841 names = [] 4842 4843 interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes) 4844 if len(interfaceMemberTypes) > 0: 4845 interfaceObject = [] 4846 for memberType in interfaceMemberTypes: 4847 name = getUnionMemberName(memberType) 4848 interfaceObject.append( 4849 CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext" % 4850 (unionArgumentObj, name))) 4851 names.append(name) 4852 interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), 4853 pre="done = ", post=";\n\n", reindent=True) 4854 else: 4855 interfaceObject = None 4856 4857 sequenceObjectMemberTypes = filter(lambda t: t.isSequence(), memberTypes) 4858 if len(sequenceObjectMemberTypes) > 0: 4859 assert len(sequenceObjectMemberTypes) == 1 4860 name = getUnionMemberName(sequenceObjectMemberTypes[0]) 4861 sequenceObject = CGGeneric( 4862 "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" % 4863 (unionArgumentObj, name)) 4864 names.append(name) 4865 else: 4866 sequenceObject = None 4867 4868 dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes) 4869 if len(dateObjectMemberTypes) > 0: 4870 assert len(dateObjectMemberTypes) == 1 4871 memberType = dateObjectMemberTypes[0] 4872 name = getUnionMemberName(memberType) 4873 dateObject = CGGeneric("%s.SetTo%s(cx, ${val});\n" 4874 "done = true;\n" % (unionArgumentObj, name)) 4875 dateObject = CGIfWrapper(dateObject, "JS_ObjectIsDate(cx, argObj)") 4876 names.append(name) 4877 else: 4878 dateObject = None 4879 4880 callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes) 4881 if len(callbackMemberTypes) > 0: 4882 assert len(callbackMemberTypes) == 1 4883 memberType = callbackMemberTypes[0] 4884 name = getUnionMemberName(memberType) 4885 callbackObject = CGGeneric( 4886 "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" % 4887 (unionArgumentObj, name)) 4888 names.append(name) 4889 else: 4890 callbackObject = None 4891 4892 dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes) 4893 if len(dictionaryMemberTypes) > 0: 4894 assert len(dictionaryMemberTypes) == 1 4895 name = getUnionMemberName(dictionaryMemberTypes[0]) 4896 setDictionary = CGGeneric( 4897 "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" % 4898 (unionArgumentObj, name)) 4899 names.append(name) 4900 else: 4901 setDictionary = None 4902 4903 mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes) 4904 if len(mozMapMemberTypes) > 0: 4905 assert len(mozMapMemberTypes) == 1 4906 name = getUnionMemberName(mozMapMemberTypes[0]) 4907 mozMapObject = CGGeneric( 4908 "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" % 4909 (unionArgumentObj, name)) 4910 names.append(name) 4911 else: 4912 mozMapObject = None 4913 4914 objectMemberTypes = filter(lambda t: t.isObject(), memberTypes) 4915 if len(objectMemberTypes) > 0: 4916 assert len(objectMemberTypes) == 1 4917 # Very important to NOT construct a temporary Rooted here, since the 4918 # SetToObject call can call a Rooted constructor and we need to keep 4919 # stack discipline for Rooted. 4920 object = CGGeneric("if (!%s.SetToObject(cx, &${val}.toObject(), ${passedToJSImpl})) {\n" 4921 "%s" 4922 "}\n" 4923 "done = true;\n" % (unionArgumentObj, indent(exceptionCode))) 4924 names.append(objectMemberTypes[0].name) 4925 else: 4926 object = None 4927 4928 hasObjectTypes = interfaceObject or sequenceObject or dateObject or callbackObject or object or mozMapObject 4929 if hasObjectTypes: 4930 # "object" is not distinguishable from other types 4931 assert not object or not (interfaceObject or sequenceObject or dateObject or callbackObject or mozMapObject) 4932 if sequenceObject or dateObject or callbackObject: 4933 # An object can be both an sequence object and a callback or 4934 # dictionary, but we shouldn't have both in the union's members 4935 # because they are not distinguishable. 4936 assert not (sequenceObject and callbackObject) 4937 templateBody = CGElseChain([sequenceObject, dateObject, callbackObject]) 4938 else: 4939 templateBody = None 4940 if interfaceObject: 4941 assert not object 4942 if templateBody: 4943 templateBody = CGIfWrapper(templateBody, "!done") 4944 templateBody = CGList([interfaceObject, templateBody]) 4945 else: 4946 templateBody = CGList([templateBody, object]) 4947 4948 if dateObject: 4949 templateBody.prepend(CGGeneric("JS::Rooted<JSObject*> argObj(cx, &${val}.toObject());\n")) 4950 4951 if mozMapObject: 4952 templateBody = CGList([templateBody, 4953 CGIfWrapper(mozMapObject, "!done")]) 4954 4955 templateBody = CGIfWrapper(templateBody, "${val}.isObject()") 4956 else: 4957 templateBody = CGGeneric() 4958 4959 if setDictionary: 4960 assert not object 4961 templateBody = CGList([templateBody, 4962 CGIfWrapper(setDictionary, "!done")]) 4963 4964 stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()] 4965 numericTypes = [t for t in memberTypes if t.isNumeric()] 4966 booleanTypes = [t for t in memberTypes if t.isBoolean()] 4967 if stringTypes or numericTypes or booleanTypes: 4968 assert len(stringTypes) <= 1 4969 assert len(numericTypes) <= 1 4970 assert len(booleanTypes) <= 1 4971 4972 # We will wrap all this stuff in a do { } while (0); so we 4973 # can use "break" for flow control. 4974 def getStringOrPrimitiveConversion(memberType): 4975 name = getUnionMemberName(memberType) 4976 return CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n" 4977 "break;\n" % (unionArgumentObj, name)) 4978 other = CGList([]) 4979 stringConversion = map(getStringOrPrimitiveConversion, stringTypes) 4980 numericConversion = map(getStringOrPrimitiveConversion, numericTypes) 4981 booleanConversion = map(getStringOrPrimitiveConversion, booleanTypes) 4982 if stringConversion: 4983 if booleanConversion: 4984 other.append(CGIfWrapper(booleanConversion[0], 4985 "${val}.isBoolean()")) 4986 if numericConversion: 4987 other.append(CGIfWrapper(numericConversion[0], 4988 "${val}.isNumber()")) 4989 other.append(stringConversion[0]) 4990 elif numericConversion: 4991 if booleanConversion: 4992 other.append(CGIfWrapper(booleanConversion[0], 4993 "${val}.isBoolean()")) 4994 other.append(numericConversion[0]) 4995 else: 4996 assert booleanConversion 4997 other.append(booleanConversion[0]) 4998 4999 other = CGWrapper(CGIndenter(other), pre="do {\n", post="} while (0);\n") 5000 if hasObjectTypes or setDictionary: 5001 other = CGWrapper(CGIndenter(other), "{\n", post="}\n") 5002 if object: 5003 templateBody = CGElseChain([templateBody, other]) 5004 else: 5005 other = CGWrapper(other, pre="if (!done) ") 5006 templateBody = CGList([templateBody, other]) 5007 else: 5008 assert templateBody.define() == "" 5009 templateBody = other 5010 else: 5011 other = None 5012 5013 templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n") 5014 throw = CGGeneric(fill( 5015 """ 5016 if (failed) { 5017 $*{exceptionCode} 5018 } 5019 if (!done) { 5020 ThrowErrorMessage(cx, MSG_NOT_IN_UNION, "${desc}", "${names}"); 5021 $*{exceptionCode} 5022 } 5023 """, 5024 exceptionCode=exceptionCode, 5025 desc=firstCap(sourceDescription), 5026 names=", ".join(names))) 5027 5028 templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw])), pre="{\n", post="}\n") 5029 5030 typeName = CGUnionStruct.unionTypeDecl(type, isOwningUnion) 5031 argumentTypeName = typeName + "Argument" 5032 if nullable: 5033 typeName = "Nullable<" + typeName + " >" 5034 5035 def handleNull(templateBody, setToNullVar, extraConditionForNull=""): 5036 nullTest = "%s${val}.isNullOrUndefined()" % extraConditionForNull 5037 return CGIfElseWrapper(nullTest, 5038 CGGeneric("%s.SetNull();\n" % setToNullVar), 5039 templateBody) 5040 5041 if type.hasNullableType: 5042 assert not nullable 5043 # Make sure to handle a null default value here 5044 if defaultValue and isinstance(defaultValue, IDLNullValue): 5045 assert defaultValue.type == type 5046 extraConditionForNull = "!(${haveValue}) || " 5047 else: 5048 extraConditionForNull = "" 5049 templateBody = handleNull(templateBody, unionArgumentObj, 5050 extraConditionForNull=extraConditionForNull) 5051 5052 declType = CGGeneric(typeName) 5053 if isOwningUnion: 5054 holderType = None 5055 else: 5056 holderType = CGGeneric(argumentTypeName) 5057 if nullable: 5058 holderType = CGTemplatedType("Maybe", holderType) 5059 5060 # If we're isOptional and not nullable the normal optional handling will 5061 # handle lazy construction of our holder. If we're nullable and not 5062 # owning we do it all by hand because we do not want our holder 5063 # constructed if we're null. But if we're owning we don't have a 5064 # holder anyway, so we can do the normal Optional codepath. 5065 declLoc = "${declName}" 5066 constructDecl = None 5067 if nullable: 5068 if isOptional and not isOwningUnion: 5069 holderArgs = "${declName}.Value().SetValue()" 5070 declType = CGTemplatedType("Optional", declType) 5071 constructDecl = CGGeneric("${declName}.Construct();\n") 5072 declLoc = "${declName}.Value()" 5073 else: 5074 holderArgs = "${declName}.SetValue()" 5075 if holderType is not None: 5076 constructHolder = CGGeneric("${holderName}.emplace(%s);\n" % holderArgs) 5077 else: 5078 constructHolder = None 5079 # Don't need to pass those args when the holder is being constructed 5080 holderArgs = None 5081 else: 5082 holderArgs = "${declName}" 5083 constructHolder = None 5084 5085 if not isMember and isCallbackReturnValue: 5086 declType = CGWrapper(declType, post="&") 5087 declArgs = "aRetVal" 5088 else: 5089 declArgs = None 5090 5091 if defaultValue and not isinstance(defaultValue, IDLNullValue): 5092 tag = defaultValue.type.tag() 5093 5094 if tag in numericSuffixes or tag is IDLType.Tags.bool: 5095 defaultStr = getHandleDefault(defaultValue) 5096 # Make sure we actually construct the thing inside the nullable. 5097 value = declLoc + (".SetValue()" if nullable else "") 5098 name = getUnionMemberName(defaultValue.type) 5099 default = CGGeneric("%s.RawSetAs%s() = %s;\n" % 5100 (value, name, defaultStr)) 5101 elif isinstance(defaultValue, IDLEmptySequenceValue): 5102 name = getUnionMemberName(defaultValue.type) 5103 # Make sure we actually construct the thing inside the nullable. 5104 value = declLoc + (".SetValue()" if nullable else "") 5105 # It's enough to set us to the right type; that will 5106 # create an empty array, which is all we need here. 5107 default = CGGeneric("%s.RawSetAs%s();\n" % 5108 (value, name)) 5109 elif defaultValue.type.isEnum(): 5110 name = getUnionMemberName(defaultValue.type) 5111 # Make sure we actually construct the thing inside the nullable. 5112 value = declLoc + (".SetValue()" if nullable else "") 5113 default = CGGeneric( 5114 "%s.RawSetAs%s() = %s::%s;\n" % 5115 (value, name, 5116 defaultValue.type.inner.identifier.name, 5117 getEnumValueName(defaultValue.value))) 5118 else: 5119 default = CGGeneric( 5120 handleDefaultStringValue( 5121 defaultValue, "%s.SetStringData" % unionArgumentObj) + 5122 ";\n") 5123 5124 templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody) 5125 5126 templateBody = CGList([constructHolder, templateBody]) 5127 5128 if nullable: 5129 if defaultValue: 5130 if isinstance(defaultValue, IDLNullValue): 5131 extraConditionForNull = "!(${haveValue}) || " 5132 else: 5133 extraConditionForNull = "${haveValue} && " 5134 else: 5135 extraConditionForNull = "" 5136 templateBody = handleNull(templateBody, declLoc, 5137 extraConditionForNull=extraConditionForNull) 5138 elif (not type.hasNullableType and defaultValue and 5139 isinstance(defaultValue, IDLNullValue)): 5140 assert type.hasDictionaryType() 5141 assert defaultValue.type.isDictionary() 5142 if not isOwningUnion and typeNeedsRooting(defaultValue.type): 5143 ctorArgs = "cx" 5144 else: 5145 ctorArgs = "" 5146 initDictionaryWithNull = CGIfWrapper( 5147 CGGeneric("return false;\n"), 5148 ('!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")' 5149 % (declLoc, getUnionMemberName(defaultValue.type), 5150 ctorArgs, type))) 5151 templateBody = CGIfElseWrapper("!(${haveValue})", 5152 initDictionaryWithNull, 5153 templateBody) 5154 5155 templateBody = CGList([constructDecl, templateBody]) 5156 5157 return JSToNativeConversionInfo(templateBody.define(), 5158 declType=declType, 5159 declArgs=declArgs, 5160 holderType=holderType, 5161 holderArgs=holderArgs, 5162 dealWithOptional=isOptional and (not nullable or isOwningUnion)) 5163 5164 if type.isGeckoInterface(): 5165 assert not isEnforceRange and not isClamp 5166 5167 descriptor = descriptorProvider.getDescriptor( 5168 type.unroll().inner.identifier.name) 5169 5170 assert descriptor.nativeType != 'JSObject' 5171 5172 if descriptor.interface.isCallback(): 5173 (declType, declArgs, 5174 conversion) = getCallbackConversionInfo(type, descriptor.interface, 5175 isMember, 5176 isCallbackReturnValue, 5177 isOptional) 5178 template = wrapObjectTemplate(conversion, type, 5179 "${declName} = nullptr;\n", 5180 failureCode) 5181 return JSToNativeConversionInfo(template, declType=declType, 5182 declArgs=declArgs, 5183 dealWithOptional=isOptional) 5184 5185 # This is an interface that we implement as a concrete class 5186 # or an XPCOM interface. 5187 5188 # Allow null pointers for nullable types and old-binding classes, and 5189 # use an RefPtr or raw pointer for callback return values to make 5190 # them easier to return. 5191 argIsPointer = (type.nullable() or type.unroll().inner.isExternal() or 5192 isCallbackReturnValue) 5193 5194 # Sequence and dictionary members, as well as owning unions (which can 5195 # appear here as return values in JS-implemented interfaces) have to 5196 # hold a strong ref to the thing being passed down. Those all set 5197 # isMember. 5198 # 5199 # Also, callback return values always end up addrefing anyway, so there 5200 # is no point trying to avoid it here and it makes other things simpler 5201 # since we can assume the return value is a strong ref. 5202 # 5203 # Finally, promises need to hold a strong ref because that's what 5204 # Promise.resolve returns. 5205 assert not descriptor.interface.isCallback() 5206 isPromise = descriptor.interface.identifier.name == "Promise" 5207 forceOwningType = isMember or isCallbackReturnValue or isPromise 5208 5209 typeName = descriptor.nativeType 5210 typePtr = typeName + "*" 5211 5212 # Compute a few things: 5213 # - declType is the type we want to return as the first element of our 5214 # tuple. 5215 # - holderType is the type we want to return as the third element 5216 # of our tuple. 5217 5218 # Set up some sensible defaults for these things insofar as we can. 5219 holderType = None 5220 if argIsPointer: 5221 if forceOwningType: 5222 declType = "RefPtr<" + typeName + ">" 5223 else: 5224 declType = typePtr 5225 else: 5226 if forceOwningType: 5227 declType = "OwningNonNull<" + typeName + ">" 5228 else: 5229 declType = "NonNull<" + typeName + ">" 5230 5231 templateBody = "" 5232 if forceOwningType: 5233 templateBody += 'static_assert(IsRefcounted<%s>::value, "We can only store refcounted classes.");' % typeName 5234 5235 if isPromise: 5236 # Per spec, what we're supposed to do is take the original 5237 # Promise.resolve and call it with the original Promise as this 5238 # value to make a Promise out of whatever value we actually have 5239 # here. The question is which global we should use. There are 5240 # several cases to consider: 5241 # 5242 # 1) Normal call to API with a Promise argument. This is a case the 5243 # spec covers, and we should be using the current Realm's 5244 # Promise. That means the current compartment. 5245 # 2) Call to API with a Promise argument over Xrays. In practice, 5246 # this sort of thing seems to be used for giving an API 5247 # implementation a way to wait for conclusion of an asyc 5248 # operation, _not_ to expose the Promise to content code. So we 5249 # probably want to allow callers to use such an API in a 5250 # "natural" way, by passing chrome-side promises; indeed, that 5251 # may be all that the caller has to represent their async 5252 # operation. That means we really need to do the 5253 # Promise.resolve() in the caller (chrome) compartment: if we do 5254 # it in the content compartment, we will try to call .then() on 5255 # the chrome promise while in the content compartment, which will 5256 # throw and we'll just get a rejected Promise. Note that this is 5257 # also the reason why a caller who has a chrome Promise 5258 # representing an async operation can't itself convert it to a 5259 # content-side Promise (at least not without some serious 5260 # gyrations). 5261 # 3) Promise return value from a callback or callback interface. 5262 # This is in theory a case the spec covers but in practice it 5263 # really doesn't define behavior here because it doesn't define 5264 # what Realm we're in after the callback returns, which is when 5265 # the argument conversion happens. We will use the current 5266 # compartment, which is the compartment of the callable (which 5267 # may itself be a cross-compartment wrapper itself), which makes 5268 # as much sense as anything else. In practice, such an API would 5269 # once again be providing a Promise to signal completion of an 5270 # operation, which would then not be exposed to anyone other than 5271 # our own implementation code. 5272 # 4) Return value from a JS-implemented interface. In this case we 5273 # have a problem. Our current compartment is the compartment of 5274 # the JS implementation. But if the JS implementation returned 5275 # a page-side Promise (which is a totally sane thing to do, and 5276 # in fact the right thing to do given that this return value is 5277 # going right to content script) then we don't want to 5278 # Promise.resolve with our current compartment Promise, because 5279 # that will wrap it up in a chrome-side Promise, which is 5280 # decidedly _not_ what's desired here. So in that case we 5281 # should really unwrap the return value and use the global of 5282 # the result. CheckedUnwrap should be good enough for that; if 5283 # it fails, then we're failing unwrap while in a 5284 # system-privileged compartment, so presumably we have a dead 5285 # object wrapper. Just error out. Do NOT fall back to using 5286 # the current compartment instead: that will return a 5287 # system-privileged rejected (because getting .then inside 5288 # resolve() failed) Promise to the caller, which they won't be 5289 # able to touch. That's not helpful. If we error out, on the 5290 # other hand, they will get a content-side rejected promise. 5291 # Same thing if the value returned is not even an object. 5292 if isCallbackReturnValue == "JSImpl": 5293 # Case 4 above. Note that globalObj defaults to the current 5294 # compartment global. Note that we don't use $*{exceptionCode} 5295 # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED) 5296 # which we don't really want here. 5297 assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n" 5298 getPromiseGlobal = fill( 5299 """ 5300 if (!$${val}.isObject()) { 5301 aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}")); 5302 return nullptr; 5303 } 5304 JSObject* unwrappedVal = js::CheckedUnwrap(&$${val}.toObject()); 5305 if (!unwrappedVal) { 5306 // A slight lie, but not much of one, for a dead object wrapper. 5307 aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}")); 5308 return nullptr; 5309 } 5310 globalObj = js::GetGlobalForObjectCrossCompartment(unwrappedVal); 5311 """, 5312 sourceDescription=sourceDescription) 5313 else: 5314 getPromiseGlobal = "" 5315 5316 templateBody = fill( 5317 """ 5318 { // Scope for our GlobalObject, FastErrorResult, JSAutoCompartment, 5319 // etc. 5320 5321 JS::Rooted<JSObject*> globalObj(cx, JS::CurrentGlobalOrNull(cx)); 5322 $*{getPromiseGlobal} 5323 JSAutoCompartment ac(cx, globalObj); 5324 GlobalObject promiseGlobal(cx, globalObj); 5325 if (promiseGlobal.Failed()) { 5326 $*{exceptionCode} 5327 } 5328 5329 JS::Rooted<JS::Value> valueToResolve(cx, $${val}); 5330 if (!JS_WrapValue(cx, &valueToResolve)) { 5331 $*{exceptionCode} 5332 } 5333 binding_detail::FastErrorResult promiseRv; 5334 #ifdef SPIDERMONKEY_PROMISE 5335 nsCOMPtr<nsIGlobalObject> global = 5336 do_QueryInterface(promiseGlobal.GetAsSupports()); 5337 if (!global) { 5338 promiseRv.Throw(NS_ERROR_UNEXPECTED); 5339 promiseRv.MaybeSetPendingException(cx); 5340 $*{exceptionCode} 5341 } 5342 $${declName} = Promise::Resolve(global, cx, valueToResolve, 5343 promiseRv); 5344 if (promiseRv.MaybeSetPendingException(cx)) { 5345 $*{exceptionCode} 5346 } 5347 #else 5348 JS::Handle<JSObject*> promiseCtor = 5349 PromiseBinding::GetConstructorObjectHandle(cx); 5350 if (!promiseCtor) { 5351 $*{exceptionCode} 5352 } 5353 JS::Rooted<JS::Value> resolveThisv(cx, JS::ObjectValue(*promiseCtor)); 5354 JS::Rooted<JS::Value> resolveResult(cx); 5355 Promise::Resolve(promiseGlobal, resolveThisv, valueToResolve, 5356 &resolveResult, promiseRv); 5357 if (promiseRv.MaybeSetPendingException(cx)) { 5358 $*{exceptionCode} 5359 } 5360 nsresult unwrapRv = UNWRAP_OBJECT(Promise, &resolveResult.toObject(), $${declName}); 5361 if (NS_FAILED(unwrapRv)) { // Quite odd 5362 promiseRv.Throw(unwrapRv); 5363 promiseRv.MaybeSetPendingException(cx); 5364 $*{exceptionCode} 5365 } 5366 #endif // SPIDERMONKEY_PROMISE 5367 } 5368 """, 5369 getPromiseGlobal=getPromiseGlobal, 5370 exceptionCode=exceptionCode) 5371 elif not descriptor.interface.isConsequential() and not descriptor.interface.isExternal(): 5372 if failureCode is not None: 5373 templateBody += str(CastableObjectUnwrapper( 5374 descriptor, 5375 "${val}", 5376 "${maybeMutableVal}", 5377 "${declName}", 5378 failureCode)) 5379 else: 5380 templateBody += str(FailureFatalCastableObjectUnwrapper( 5381 descriptor, 5382 "${val}", 5383 "${maybeMutableVal}", 5384 "${declName}", 5385 exceptionCode, 5386 isCallbackReturnValue, 5387 firstCap(sourceDescription))) 5388 else: 5389 # Either external, or new-binding non-castable. We always have a 5390 # holder for these, because we don't actually know whether we have 5391 # to addref when unwrapping or not. So we just pass an 5392 # getter_AddRefs(RefPtr) to XPConnect and if we'll need a release 5393 # it'll put a non-null pointer in there. 5394 if forceOwningType: 5395 # Don't return a holderType in this case; our declName 5396 # will just own stuff. 5397 templateBody += "RefPtr<" + typeName + "> ${holderName};\n" 5398 else: 5399 holderType = "RefPtr<" + typeName + ">" 5400 templateBody += ( 5401 "JS::Rooted<JSObject*> source(cx, &${val}.toObject());\n" + 5402 "if (NS_FAILED(UnwrapArg<" + typeName + ">(source, getter_AddRefs(${holderName})))) {\n") 5403 templateBody += CGIndenter(onFailureBadType(failureCode, 5404 descriptor.interface.identifier.name)).define() 5405 templateBody += ("}\n" 5406 "MOZ_ASSERT(${holderName});\n") 5407 5408 # And store our value in ${declName} 5409 templateBody += "${declName} = ${holderName};\n" 5410 5411 if isPromise: 5412 if type.nullable(): 5413 codeToSetNull = "${declName} = nullptr;\n" 5414 templateBody = CGIfElseWrapper( 5415 "${val}.isNullOrUndefined()", 5416 CGGeneric(codeToSetNull), 5417 CGGeneric(templateBody)).define() 5418 if isinstance(defaultValue, IDLNullValue): 5419 templateBody = handleDefault(templateBody, codeToSetNull) 5420 else: 5421 assert defaultValue is None 5422 else: 5423 # Just pass failureCode, not onFailureBadType, here, so we'll report 5424 # the thing as not an object as opposed to not implementing whatever 5425 # our interface is. 5426 templateBody = wrapObjectTemplate(templateBody, type, 5427 "${declName} = nullptr;\n", 5428 failureCode) 5429 5430 declType = CGGeneric(declType) 5431 if holderType is not None: 5432 holderType = CGGeneric(holderType) 5433 return JSToNativeConversionInfo(templateBody, 5434 declType=declType, 5435 holderType=holderType, 5436 dealWithOptional=isOptional) 5437 5438 if type.isSpiderMonkeyInterface(): 5439 assert not isEnforceRange and not isClamp 5440 name = type.unroll().name # unroll() because it may be nullable 5441 arrayType = CGGeneric(name) 5442 declType = arrayType 5443 if type.nullable(): 5444 declType = CGTemplatedType("Nullable", declType) 5445 objRef = "${declName}.SetValue()" 5446 else: 5447 objRef = "${declName}" 5448 5449 # Again, this is a bit strange since we are actually building a 5450 # template string here. ${objRef} and $*{badType} below are filled in 5451 # right now; $${val} expands to ${val}, to be filled in later. 5452 template = fill( 5453 """ 5454 if (!${objRef}.Init(&$${val}.toObject())) { 5455 $*{badType} 5456 } 5457 """, 5458 objRef=objRef, 5459 badType=onFailureBadType(failureCode, type.name).define()) 5460 template = wrapObjectTemplate(template, type, "${declName}.SetNull();\n", 5461 failureCode) 5462 if not isMember: 5463 # This is a bit annoying. In a union we don't want to have a 5464 # holder, since unions don't support that. But if we're optional we 5465 # want to have a holder, so that the callee doesn't see 5466 # Optional<RootedTypedArray<ArrayType> >. So do a holder if we're 5467 # optional and use a RootedTypedArray otherwise. 5468 if isOptional: 5469 holderType = CGTemplatedType("TypedArrayRooter", arrayType) 5470 # If our typed array is nullable, this will set the Nullable to 5471 # be not-null, but that's ok because we make an explicit 5472 # SetNull() call on it as needed if our JS value is actually 5473 # null. XXXbz Because "Maybe" takes const refs for constructor 5474 # arguments, we can't pass a reference here; have to pass a 5475 # pointer. 5476 holderArgs = "cx, &%s" % objRef 5477 declArgs = None 5478 else: 5479 holderType = None 5480 holderArgs = None 5481 declType = CGTemplatedType("RootedTypedArray", declType) 5482 declArgs = "cx" 5483 else: 5484 holderType = None 5485 holderArgs = None 5486 declArgs = None 5487 return JSToNativeConversionInfo(template, 5488 declType=declType, 5489 holderType=holderType, 5490 dealWithOptional=isOptional, 5491 declArgs=declArgs, 5492 holderArgs=holderArgs) 5493 5494 if type.isDOMString() or type.isUSVString(): 5495 assert not isEnforceRange and not isClamp 5496 5497 treatAs = { 5498 "Default": "eStringify", 5499 "EmptyString": "eEmpty", 5500 "Null": "eNull", 5501 } 5502 if type.nullable(): 5503 # For nullable strings null becomes a null string. 5504 treatNullAs = "Null" 5505 # For nullable strings undefined also becomes a null string. 5506 undefinedBehavior = "eNull" 5507 else: 5508 undefinedBehavior = "eStringify" 5509 nullBehavior = treatAs[treatNullAs] 5510 5511 def getConversionCode(varName): 5512 normalizeCode = "" 5513 if type.isUSVString(): 5514 normalizeCode = "NormalizeUSVString(cx, %s);\n" % varName 5515 5516 conversionCode = fill(""" 5517 if (!ConvertJSValueToString(cx, $${val}, ${nullBehavior}, ${undefinedBehavior}, ${varName})) { 5518 $*{exceptionCode} 5519 } 5520 $*{normalizeCode} 5521 """ 5522 , 5523 nullBehavior=nullBehavior, 5524 undefinedBehavior=undefinedBehavior, 5525 varName=varName, 5526 exceptionCode=exceptionCode, 5527 normalizeCode=normalizeCode) 5528 5529 if defaultValue is None: 5530 return conversionCode 5531 5532 if isinstance(defaultValue, IDLNullValue): 5533 assert(type.nullable()) 5534 defaultCode = "%s.SetIsVoid(true)" % varName 5535 else: 5536 defaultCode = handleDefaultStringValue(defaultValue, 5537 "%s.Rebind" % varName) 5538 return handleDefault(conversionCode, defaultCode + ";\n") 5539 5540 if isMember: 5541 # Convert directly into the nsString member we have. 5542 declType = CGGeneric("nsString") 5543 return JSToNativeConversionInfo( 5544 getConversionCode("${declName}"), 5545 declType=declType, 5546 dealWithOptional=isOptional) 5547 5548 if isOptional: 5549 declType = "Optional<nsAString>" 5550 holderType = CGGeneric("binding_detail::FakeString") 5551 conversionCode = ("%s" 5552 "${declName} = &${holderName};\n" % 5553 getConversionCode("${holderName}")) 5554 else: 5555 declType = "binding_detail::FakeString" 5556 holderType = None 5557 conversionCode = getConversionCode("${declName}") 5558 5559 # No need to deal with optional here; we handled it already 5560 return JSToNativeConversionInfo( 5561 conversionCode, 5562 declType=CGGeneric(declType), 5563 holderType=holderType) 5564 5565 if type.isByteString(): 5566 assert not isEnforceRange and not isClamp 5567 5568 nullable = toStringBool(type.nullable()) 5569 5570 conversionCode = fill(""" 5571 if (!ConvertJSValueToByteString(cx, $${val}, ${nullable}, $${declName})) { 5572 $*{exceptionCode} 5573 } 5574 """, 5575 nullable=nullable, 5576 exceptionCode=exceptionCode) 5577 5578 if defaultValue is not None: 5579 if isinstance(defaultValue, IDLNullValue): 5580 assert(type.nullable()) 5581 defaultCode = "${declName}.SetIsVoid(true)" 5582 else: 5583 defaultCode = handleDefaultStringValue(defaultValue, 5584 "${declName}.Rebind") 5585 conversionCode = handleDefault(conversionCode, defaultCode + ";\n") 5586 5587 return JSToNativeConversionInfo( 5588 conversionCode, 5589 declType=CGGeneric("nsCString"), 5590 dealWithOptional=isOptional) 5591 5592 if type.isEnum(): 5593 assert not isEnforceRange and not isClamp 5594 5595 enumName = type.unroll().inner.identifier.name 5596 declType = CGGeneric(enumName) 5597 if type.nullable(): 5598 declType = CGTemplatedType("Nullable", declType) 5599 declType = declType.define() 5600 enumLoc = "${declName}.SetValue()" 5601 else: 5602 enumLoc = "${declName}" 5603 declType = declType.define() 5604 5605 if invalidEnumValueFatal: 5606 handleInvalidEnumValueCode = "MOZ_ASSERT(index >= 0);\n" 5607 else: 5608 # invalidEnumValueFatal is false only for attributes. So we won't 5609 # have a non-default exceptionCode here unless attribute "arg 5610 # conversion" code starts passing in an exceptionCode. At which 5611 # point we'll need to figure out what that even means. 5612 assert exceptionCode == "return false;\n" 5613 handleInvalidEnumValueCode = dedent(""" 5614 if (index < 0) { 5615 return true; 5616 } 5617 """) 5618 5619 template = fill( 5620 """ 5621 { 5622 int index; 5623 if (!FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val}, ${values}, "${enumtype}", "${sourceDescription}", &index)) { 5624 $*{exceptionCode} 5625 } 5626 $*{handleInvalidEnumValueCode} 5627 ${enumLoc} = static_cast<${enumtype}>(index); 5628 } 5629 """, 5630 enumtype=enumName, 5631 values=enumName + "Values::" + ENUM_ENTRY_VARIABLE_NAME, 5632 invalidEnumValueFatal=toStringBool(invalidEnumValueFatal), 5633 handleInvalidEnumValueCode=handleInvalidEnumValueCode, 5634 exceptionCode=exceptionCode, 5635 enumLoc=enumLoc, 5636 sourceDescription=firstCap(sourceDescription)) 5637 5638 setNull = "${declName}.SetNull();\n" 5639 5640 if type.nullable(): 5641 template = CGIfElseWrapper("${val}.isNullOrUndefined()", 5642 CGGeneric(setNull), 5643 CGGeneric(template)).define() 5644 5645 if defaultValue is not None: 5646 if isinstance(defaultValue, IDLNullValue): 5647 assert type.nullable() 5648 template = handleDefault(template, setNull) 5649 else: 5650 assert(defaultValue.type.tag() == IDLType.Tags.domstring) 5651 template = handleDefault(template, 5652 ("%s = %s::%s;\n" % 5653 (enumLoc, enumName, 5654 getEnumValueName(defaultValue.value)))) 5655 return JSToNativeConversionInfo(template, declType=CGGeneric(declType), 5656 dealWithOptional=isOptional) 5657 5658 if type.isCallback(): 5659 assert not isEnforceRange and not isClamp 5660 assert not type.treatNonCallableAsNull() or type.nullable() 5661 assert not type.treatNonObjectAsNull() or type.nullable() 5662 assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull() 5663 5664 callback = type.unroll().callback 5665 name = callback.identifier.name 5666 (declType, declArgs, 5667 conversion) = getCallbackConversionInfo(type, callback, isMember, 5668 isCallbackReturnValue, 5669 isOptional) 5670 5671 if allowTreatNonCallableAsNull and type.treatNonCallableAsNull(): 5672 haveCallable = "JS::IsCallable(&${val}.toObject())" 5673 if not isDefinitelyObject: 5674 haveCallable = "${val}.isObject() && " + haveCallable 5675 if defaultValue is not None: 5676 assert(isinstance(defaultValue, IDLNullValue)) 5677 haveCallable = "${haveValue} && " + haveCallable 5678 template = ( 5679 ("if (%s) {\n" % haveCallable) + 5680 conversion + 5681 "} else {\n" 5682 " ${declName} = nullptr;\n" 5683 "}\n") 5684 elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull(): 5685 if not isDefinitelyObject: 5686 haveObject = "${val}.isObject()" 5687 if defaultValue is not None: 5688 assert(isinstance(defaultValue, IDLNullValue)) 5689 haveObject = "${haveValue} && " + haveObject 5690 template = CGIfElseWrapper(haveObject, 5691 CGGeneric(conversion), 5692 CGGeneric("${declName} = nullptr;\n")).define() 5693 else: 5694 template = conversion 5695 else: 5696 template = wrapObjectTemplate( 5697 "if (JS::IsCallable(&${val}.toObject())) {\n" + 5698 conversion + 5699 "} else {\n" + 5700 indent(onFailureNotCallable(failureCode).define()) + 5701 "}\n", 5702 type, 5703 "${declName} = nullptr;\n", 5704 failureCode) 5705 return JSToNativeConversionInfo(template, declType=declType, 5706 declArgs=declArgs, 5707 dealWithOptional=isOptional) 5708 5709 if type.isAny(): 5710 assert not isEnforceRange and not isClamp 5711 5712 declArgs = None 5713 if isMember in ("Variadic", "Sequence", "Dictionary", "MozMap"): 5714 # Rooting is handled by the sequence and dictionary tracers. 5715 declType = "JS::Value" 5716 else: 5717 assert not isMember 5718 declType = "JS::Rooted<JS::Value>" 5719 declArgs = "cx" 5720 5721 assert not isOptional 5722 templateBody = "${declName} = ${val};\n" 5723 5724 # For JS-implemented APIs, we refuse to allow passing objects that the 5725 # API consumer does not subsume. The extra parens around 5726 # ($${passedToJSImpl}) suppress unreachable code warnings when 5727 # $${passedToJSImpl} is the literal `false`. 5728 if not isinstance(descriptorProvider, Descriptor) or descriptorProvider.interface.isJSImplemented(): 5729 templateBody = fill( 5730 """ 5731 if (($${passedToJSImpl}) && !CallerSubsumes($${val})) { 5732 ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "${sourceDescription}"); 5733 $*{exceptionCode} 5734 } 5735 """, 5736 sourceDescription=sourceDescription, 5737 exceptionCode=exceptionCode) + templateBody 5738 5739 # We may not have a default value if we're being converted for 5740 # a setter, say. 5741 if defaultValue: 5742 if isinstance(defaultValue, IDLNullValue): 5743 defaultHandling = "${declName} = JS::NullValue();\n" 5744 else: 5745 assert isinstance(defaultValue, IDLUndefinedValue) 5746 defaultHandling = "${declName} = JS::UndefinedValue();\n" 5747 templateBody = handleDefault(templateBody, defaultHandling) 5748 return JSToNativeConversionInfo(templateBody, 5749 declType=CGGeneric(declType), 5750 declArgs=declArgs) 5751 5752 if type.isObject(): 5753 assert not isEnforceRange and not isClamp 5754 return handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription) 5755 5756 if type.isDictionary(): 5757 # There are no nullable dictionaries 5758 assert not type.nullable() or isCallbackReturnValue 5759 # All optional dictionaries always have default values, so we 5760 # should be able to assume not isOptional here. 5761 assert not isOptional 5762 # In the callback return value case we never have to worry 5763 # about a default value; we always have a value. 5764 assert not isCallbackReturnValue or defaultValue is None 5765 5766 typeName = CGDictionary.makeDictionaryName(type.unroll().inner) 5767 if not isMember and not isCallbackReturnValue: 5768 # Since we're not a member and not nullable or optional, no one will 5769 # see our real type, so we can do the fast version of the dictionary 5770 # that doesn't pre-initialize members. 5771 typeName = "binding_detail::Fast" + typeName 5772 5773 declType = CGGeneric(typeName) 5774 5775 # We do manual default value handling here, because we 5776 # actually do want a jsval, and we only handle null anyway 5777 # NOTE: if isNullOrUndefined or isDefinitelyObject are true, 5778 # we know we have a value, so we don't have to worry about the 5779 # default value. 5780 if (not isNullOrUndefined and not isDefinitelyObject and 5781 defaultValue is not None): 5782 assert(isinstance(defaultValue, IDLNullValue)) 5783 val = "(${haveValue}) ? ${val} : JS::NullHandleValue" 5784 else: 5785 val = "${val}" 5786 5787 dictLoc = "${declName}" 5788 if type.nullable(): 5789 dictLoc += ".SetValue()" 5790 5791 conversionCode = fill(""" 5792 if (!${dictLoc}.Init(cx, ${val}, "${desc}", $${passedToJSImpl})) { 5793 $*{exceptionCode} 5794 } 5795 """, 5796 dictLoc=dictLoc, 5797 val=val, 5798 desc=firstCap(sourceDescription), 5799 exceptionCode=exceptionCode) 5800 5801 if failureCode is not None: 5802 if isDefinitelyObject: 5803 dictionaryTest = "IsObjectValueConvertibleToDictionary" 5804 else: 5805 dictionaryTest = "IsConvertibleToDictionary" 5806 5807 template = fill(""" 5808 { // scope for isConvertible 5809 bool isConvertible; 5810 if (!${testConvertible}(cx, ${val}, &isConvertible)) { 5811 $*{exceptionCode} 5812 } 5813 if (!isConvertible) { 5814 $*{failureCode} 5815 } 5816 5817 $*{conversionCode} 5818 } 5819 5820 """, 5821 testConvertible=dictionaryTest, 5822 val=val, 5823 exceptionCode=exceptionCode, 5824 failureCode=failureCode, 5825 conversionCode=conversionCode) 5826 else: 5827 template = conversionCode 5828 5829 if type.nullable(): 5830 declType = CGTemplatedType("Nullable", declType) 5831 template = CGIfElseWrapper("${val}.isNullOrUndefined()", 5832 CGGeneric("${declName}.SetNull();\n"), 5833 CGGeneric(template)).define() 5834 5835 # Dictionary arguments that might contain traceable things need to get 5836 # traced 5837 if not isMember and isCallbackReturnValue: 5838 # Go ahead and just convert directly into our actual return value 5839 declType = CGWrapper(declType, post="&") 5840 declArgs = "aRetVal" 5841 elif not isMember and typeNeedsRooting(type): 5842 declType = CGTemplatedType("RootedDictionary", declType) 5843 declArgs = "cx" 5844 else: 5845 declArgs = None 5846 5847 return JSToNativeConversionInfo(template, declType=declType, 5848 declArgs=declArgs) 5849 5850 if type.isVoid(): 5851 assert not isOptional 5852 # This one only happens for return values, and its easy: Just 5853 # ignore the jsval. 5854 return JSToNativeConversionInfo("") 5855 5856 if type.isDate(): 5857 assert not isEnforceRange and not isClamp 5858 5859 declType = CGGeneric("Date") 5860 if type.nullable(): 5861 declType = CGTemplatedType("Nullable", declType) 5862 dateVal = "${declName}.SetValue()" 5863 else: 5864 dateVal = "${declName}" 5865 5866 if failureCode is None: 5867 notDate = ('ThrowErrorMessage(cx, MSG_NOT_DATE, "%s");\n' 5868 "%s" % (firstCap(sourceDescription), exceptionCode)) 5869 else: 5870 notDate = failureCode 5871 5872 conversion = fill( 5873 """ 5874 JS::Rooted<JSObject*> possibleDateObject(cx, &$${val}.toObject()); 5875 { // scope for isDate 5876 bool isDate; 5877 if (!JS_ObjectIsDate(cx, possibleDateObject, &isDate)) { 5878 $*{exceptionCode} 5879 } 5880 if (!isDate) { 5881 $*{notDate} 5882 } 5883 if (!${dateVal}.SetTimeStamp(cx, possibleDateObject)) { 5884 $*{exceptionCode} 5885 } 5886 } 5887 """, 5888 exceptionCode=exceptionCode, 5889 dateVal=dateVal, 5890 notDate=notDate) 5891 5892 conversion = wrapObjectTemplate(conversion, type, 5893 "${declName}.SetNull();\n", notDate) 5894 return JSToNativeConversionInfo(conversion, 5895 declType=declType, 5896 dealWithOptional=isOptional) 5897 5898 if not type.isPrimitive(): 5899 raise TypeError("Need conversion for argument type '%s'" % str(type)) 5900 5901 typeName = builtinNames[type.tag()] 5902 5903 conversionBehavior = "eDefault" 5904 if isEnforceRange: 5905 assert type.isInteger() 5906 conversionBehavior = "eEnforceRange" 5907 elif isClamp: 5908 assert type.isInteger() 5909 conversionBehavior = "eClamp" 5910 5911 if type.nullable(): 5912 declType = CGGeneric("Nullable<" + typeName + ">") 5913 writeLoc = "${declName}.SetValue()" 5914 readLoc = "${declName}.Value()" 5915 nullCondition = "${val}.isNullOrUndefined()" 5916 if defaultValue is not None and isinstance(defaultValue, IDLNullValue): 5917 nullCondition = "!(${haveValue}) || " + nullCondition 5918 template = fill(""" 5919 if (${nullCondition}) { 5920 $${declName}.SetNull(); 5921 } else if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, &${writeLoc})) { 5922 $*{exceptionCode} 5923 } 5924 """, 5925 nullCondition=nullCondition, 5926 typeName=typeName, 5927 conversionBehavior=conversionBehavior, 5928 writeLoc=writeLoc, 5929 exceptionCode=exceptionCode) 5930 else: 5931 assert(defaultValue is None or 5932 not isinstance(defaultValue, IDLNullValue)) 5933 writeLoc = "${declName}" 5934 readLoc = writeLoc 5935 template = fill(""" 5936 if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, &${writeLoc})) { 5937 $*{exceptionCode} 5938 } 5939 """, 5940 typeName=typeName, 5941 conversionBehavior=conversionBehavior, 5942 writeLoc=writeLoc, 5943 exceptionCode=exceptionCode) 5944 declType = CGGeneric(typeName) 5945 5946 if type.isFloat() and not type.isUnrestricted(): 5947 if lenientFloatCode is not None: 5948 nonFiniteCode = lenientFloatCode 5949 else: 5950 nonFiniteCode = ('ThrowErrorMessage(cx, MSG_NOT_FINITE, "%s");\n' 5951 "%s" % (firstCap(sourceDescription), exceptionCode)) 5952 5953 # We're appending to an if-block brace, so strip trailing whitespace 5954 # and add an extra space before the else. 5955 template = template.rstrip() 5956 template += fill(""" 5957 else if (!mozilla::IsFinite(${readLoc})) { 5958 $*{nonFiniteCode} 5959 } 5960 """, 5961 readLoc=readLoc, 5962 nonFiniteCode=nonFiniteCode) 5963 5964 if (defaultValue is not None and 5965 # We already handled IDLNullValue, so just deal with the other ones 5966 not isinstance(defaultValue, IDLNullValue)): 5967 tag = defaultValue.type.tag() 5968 defaultStr = getHandleDefault(defaultValue) 5969 template = CGIfElseWrapper( 5970 "${haveValue}", 5971 CGGeneric(template), 5972 CGGeneric("%s = %s;\n" % (writeLoc, defaultStr))).define() 5973 5974 return JSToNativeConversionInfo(template, declType=declType, 5975 dealWithOptional=isOptional) 5976 5977 5978def instantiateJSToNativeConversion(info, replacements, checkForValue=False): 5979 """ 5980 Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo 5981 and a set of replacements as required by the strings in such an object, and 5982 generate code to convert into stack C++ types. 5983 5984 If checkForValue is True, then the conversion will get wrapped in 5985 a check for ${haveValue}. 5986 """ 5987 templateBody, declType, holderType, dealWithOptional = ( 5988 info.template, info.declType, info.holderType, info.dealWithOptional) 5989 5990 if dealWithOptional and not checkForValue: 5991 raise TypeError("Have to deal with optional things, but don't know how") 5992 if checkForValue and declType is None: 5993 raise TypeError("Need to predeclare optional things, so they will be " 5994 "outside the check for big enough arg count!") 5995 5996 # We can't precompute our holder constructor arguments, since 5997 # those might depend on ${declName}, which we change below. Just 5998 # compute arguments at the point when we need them as we go. 5999 def getArgsCGThing(args): 6000 return CGGeneric(string.Template(args).substitute(replacements)) 6001 6002 result = CGList([]) 6003 # Make a copy of "replacements" since we may be about to start modifying it 6004 replacements = dict(replacements) 6005 originalDeclName = replacements["declName"] 6006 if declType is not None: 6007 if dealWithOptional: 6008 replacements["declName"] = "%s.Value()" % originalDeclName 6009 declType = CGTemplatedType("Optional", declType) 6010 declCtorArgs = None 6011 elif info.declArgs is not None: 6012 declCtorArgs = CGWrapper(getArgsCGThing(info.declArgs), 6013 pre="(", post=")") 6014 else: 6015 declCtorArgs = None 6016 result.append( 6017 CGList([declType, CGGeneric(" "), 6018 CGGeneric(originalDeclName), 6019 declCtorArgs, CGGeneric(";\n")])) 6020 6021 originalHolderName = replacements["holderName"] 6022 if holderType is not None: 6023 if dealWithOptional: 6024 replacements["holderName"] = "%s.ref()" % originalHolderName 6025 holderType = CGTemplatedType("Maybe", holderType) 6026 holderCtorArgs = None 6027 elif info.holderArgs is not None: 6028 holderCtorArgs = CGWrapper(getArgsCGThing(info.holderArgs), 6029 pre="(", post=")") 6030 else: 6031 holderCtorArgs = None 6032 result.append( 6033 CGList([holderType, CGGeneric(" "), 6034 CGGeneric(originalHolderName), 6035 holderCtorArgs, CGGeneric(";\n")])) 6036 6037 if "maybeMutableVal" not in replacements: 6038 replacements["maybeMutableVal"] = replacements["val"] 6039 6040 conversion = CGGeneric( 6041 string.Template(templateBody).substitute(replacements)) 6042 6043 if checkForValue: 6044 if dealWithOptional: 6045 declConstruct = CGIndenter( 6046 CGGeneric("%s.Construct(%s);\n" % 6047 (originalDeclName, 6048 getArgsCGThing(info.declArgs).define() if 6049 info.declArgs else ""))) 6050 if holderType is not None: 6051 holderConstruct = CGIndenter( 6052 CGGeneric("%s.emplace(%s);\n" % 6053 (originalHolderName, 6054 getArgsCGThing(info.holderArgs).define() if 6055 info.holderArgs else ""))) 6056 else: 6057 holderConstruct = None 6058 else: 6059 declConstruct = None 6060 holderConstruct = None 6061 6062 conversion = CGList([ 6063 CGGeneric( 6064 string.Template("if (${haveValue}) {\n").substitute(replacements)), 6065 declConstruct, 6066 holderConstruct, 6067 CGIndenter(conversion), 6068 CGGeneric("}\n") 6069 ]) 6070 6071 result.append(conversion) 6072 return result 6073 6074 6075def convertConstIDLValueToJSVal(value): 6076 if isinstance(value, IDLNullValue): 6077 return "JS::NullValue()" 6078 if isinstance(value, IDLUndefinedValue): 6079 return "JS::UndefinedValue()" 6080 tag = value.type.tag() 6081 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16, 6082 IDLType.Tags.uint16, IDLType.Tags.int32]: 6083 return "JS::Int32Value(%s)" % (value.value) 6084 if tag == IDLType.Tags.uint32: 6085 return "JS::NumberValue(%sU)" % (value.value) 6086 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]: 6087 return "JS::CanonicalizedDoubleValue(%s)" % numericValue(tag, value.value) 6088 if tag == IDLType.Tags.bool: 6089 return "JS::BooleanValue(true)" if value.value else "JS::BooleanValue(false)" 6090 if tag in [IDLType.Tags.float, IDLType.Tags.double]: 6091 return "JS::CanonicalizedDoubleValue(%s)" % (value.value) 6092 raise TypeError("Const value of unhandled type: %s" % value.type) 6093 6094 6095class CGArgumentConverter(CGThing): 6096 """ 6097 A class that takes an IDL argument object and its index in the 6098 argument list and generates code to unwrap the argument to the 6099 right native type. 6100 6101 argDescription is a description of the argument for error-reporting 6102 purposes. Callers should assume that it might get placed in the middle of a 6103 sentence. If it ends up at the beginning of a sentence, its first character 6104 will be automatically uppercased. 6105 """ 6106 def __init__(self, argument, index, descriptorProvider, 6107 argDescription, member, 6108 invalidEnumValueFatal=True, lenientFloatCode=None): 6109 CGThing.__init__(self) 6110 self.argument = argument 6111 self.argDescription = argDescription 6112 assert(not argument.defaultValue or argument.optional) 6113 6114 replacer = { 6115 "index": index, 6116 "argc": "args.length()" 6117 } 6118 self.replacementVariables = { 6119 "declName": "arg%d" % index, 6120 "holderName": ("arg%d" % index) + "_holder", 6121 "obj": "obj", 6122 "passedToJSImpl": toStringBool(isJSImplementedDescriptor(descriptorProvider)) 6123 } 6124 # If we have a method generated by the maplike/setlike portion of an 6125 # interface, arguments can possibly be undefined, but will need to be 6126 # converted to the key/value type of the backing object. In this case, 6127 # use .get() instead of direct access to the argument. This won't 6128 # matter for iterable since generated functions for those interface 6129 # don't take arguments. 6130 if member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod(): 6131 self.replacementVariables["val"] = string.Template( 6132 "args.get(${index})").substitute(replacer) 6133 self.replacementVariables["maybeMutableVal"] = string.Template( 6134 "args[${index}]").substitute(replacer) 6135 else: 6136 self.replacementVariables["val"] = string.Template( 6137 "args[${index}]").substitute(replacer) 6138 haveValueCheck = string.Template( 6139 "args.hasDefined(${index})").substitute(replacer) 6140 self.replacementVariables["haveValue"] = haveValueCheck 6141 self.descriptorProvider = descriptorProvider 6142 if self.argument.canHaveMissingValue(): 6143 self.argcAndIndex = replacer 6144 else: 6145 self.argcAndIndex = None 6146 self.invalidEnumValueFatal = invalidEnumValueFatal 6147 self.lenientFloatCode = lenientFloatCode 6148 6149 def define(self): 6150 typeConversion = getJSToNativeConversionInfo( 6151 self.argument.type, 6152 self.descriptorProvider, 6153 isOptional=(self.argcAndIndex is not None and 6154 not self.argument.variadic), 6155 invalidEnumValueFatal=self.invalidEnumValueFatal, 6156 defaultValue=self.argument.defaultValue, 6157 treatNullAs=self.argument.treatNullAs, 6158 isEnforceRange=self.argument.enforceRange, 6159 isClamp=self.argument.clamp, 6160 lenientFloatCode=self.lenientFloatCode, 6161 isMember="Variadic" if self.argument.variadic else False, 6162 allowTreatNonCallableAsNull=self.argument.allowTreatNonCallableAsNull(), 6163 sourceDescription=self.argDescription) 6164 6165 if not self.argument.variadic: 6166 return instantiateJSToNativeConversion( 6167 typeConversion, 6168 self.replacementVariables, 6169 self.argcAndIndex is not None).define() 6170 6171 # Variadic arguments get turned into a sequence. 6172 if typeConversion.dealWithOptional: 6173 raise TypeError("Shouldn't have optional things in variadics") 6174 if typeConversion.holderType is not None: 6175 raise TypeError("Shouldn't need holders for variadics") 6176 6177 replacer = dict(self.argcAndIndex, **self.replacementVariables) 6178 replacer["seqType"] = CGTemplatedType("binding_detail::AutoSequence", 6179 typeConversion.declType).define() 6180 if typeNeedsRooting(self.argument.type): 6181 rooterDecl = ("SequenceRooter<%s> ${holderName}(cx, &${declName});\n" % 6182 typeConversion.declType.define()) 6183 else: 6184 rooterDecl = "" 6185 replacer["elemType"] = typeConversion.declType.define() 6186 6187 # NOTE: Keep this in sync with sequence conversions as needed 6188 variadicConversion = string.Template( 6189 "${seqType} ${declName};\n" + 6190 rooterDecl + 6191 dedent(""" 6192 if (${argc} > ${index}) { 6193 if (!${declName}.SetCapacity(${argc} - ${index}, mozilla::fallible)) { 6194 JS_ReportOutOfMemory(cx); 6195 return false; 6196 } 6197 for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) { 6198 ${elemType}& slot = *${declName}.AppendElement(mozilla::fallible); 6199 """) 6200 ).substitute(replacer) 6201 6202 val = string.Template("args[variadicArg]").substitute(replacer) 6203 variadicConversion += indent( 6204 string.Template(typeConversion.template).substitute({ 6205 "val": val, 6206 "maybeMutableVal": val, 6207 "declName": "slot", 6208 # We only need holderName here to handle isExternal() 6209 # interfaces, which use an internal holder for the 6210 # conversion even when forceOwningType ends up true. 6211 "holderName": "tempHolder", 6212 # Use the same ${obj} as for the variadic arg itself 6213 "obj": replacer["obj"], 6214 "passedToJSImpl": toStringBool(isJSImplementedDescriptor(self.descriptorProvider)) 6215 }), 4) 6216 6217 variadicConversion += (" }\n" 6218 "}\n") 6219 return variadicConversion 6220 6221 6222def getMaybeWrapValueFuncForType(type): 6223 # Callbacks might actually be DOM objects; nothing prevents a page from 6224 # doing that. 6225 if type.isCallback() or type.isCallbackInterface() or type.isObject(): 6226 if type.nullable(): 6227 return "MaybeWrapObjectOrNullValue" 6228 return "MaybeWrapObjectValue" 6229 # Spidermonkey interfaces are never DOM objects. Neither are sequences or 6230 # dictionaries, since those are always plain JS objects. 6231 if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence(): 6232 if type.nullable(): 6233 return "MaybeWrapNonDOMObjectOrNullValue" 6234 return "MaybeWrapNonDOMObjectValue" 6235 if type.isAny(): 6236 return "MaybeWrapValue" 6237 6238 # For other types, just go ahead an fall back on MaybeWrapValue for now: 6239 # it's always safe to do, and shouldn't be particularly slow for any of 6240 # them 6241 return "MaybeWrapValue" 6242 6243 6244sequenceWrapLevel = 0 6245mozMapWrapLevel = 0 6246 6247 6248def getWrapTemplateForType(type, descriptorProvider, result, successCode, 6249 returnsNewObject, exceptionCode, typedArraysAreStructs, 6250 isConstructorRetval=False): 6251 """ 6252 Reflect a C++ value stored in "result", of IDL type "type" into JS. The 6253 "successCode" is the code to run once we have successfully done the 6254 conversion and must guarantee that execution of the conversion template 6255 stops once the successCode has executed (e.g. by doing a 'return', or by 6256 doing a 'break' if the entire conversion template is inside a block that 6257 the 'break' will exit). 6258 6259 If typedArraysAreStructs is true, then if the type is a typed array, 6260 "result" is one of the dom::TypedArray subclasses, not a JSObject*. 6261 6262 The resulting string should be used with string.Template. It 6263 needs the following keys when substituting: 6264 6265 jsvalHandle: something that can be passed to methods taking a 6266 JS::MutableHandle<JS::Value>. This can be a 6267 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*. 6268 jsvalRef: something that can have .address() called on it to get a 6269 JS::Value* and .set() called on it to set it to a JS::Value. 6270 This can be a JS::MutableHandle<JS::Value> or a 6271 JS::Rooted<JS::Value>. 6272 obj: a JS::Handle<JSObject*>. 6273 6274 Returns (templateString, infallibility of conversion template) 6275 """ 6276 if successCode is None: 6277 successCode = "return true;\n" 6278 6279 def setUndefined(): 6280 return _setValue("", setter="setUndefined") 6281 6282 def setNull(): 6283 return _setValue("", setter="setNull") 6284 6285 def setInt32(value): 6286 return _setValue(value, setter="setInt32") 6287 6288 def setString(value): 6289 return _setValue(value, setter="setString") 6290 6291 def setObject(value, wrapAsType=None): 6292 return _setValue(value, wrapAsType=wrapAsType, setter="setObject") 6293 6294 def setObjectOrNull(value, wrapAsType=None): 6295 return _setValue(value, wrapAsType=wrapAsType, setter="setObjectOrNull") 6296 6297 def setUint32(value): 6298 return _setValue(value, setter="setNumber") 6299 6300 def setDouble(value): 6301 return _setValue("JS_NumberValue(%s)" % value) 6302 6303 def setBoolean(value): 6304 return _setValue(value, setter="setBoolean") 6305 6306 def _setValue(value, wrapAsType=None, setter="set"): 6307 """ 6308 Returns the code to set the jsval to value. 6309 6310 If wrapAsType is not None, then will wrap the resulting value using the 6311 function that getMaybeWrapValueFuncForType(wrapAsType) returns. 6312 Otherwise, no wrapping will be done. 6313 """ 6314 if wrapAsType is None: 6315 tail = successCode 6316 else: 6317 tail = fill( 6318 """ 6319 if (!${maybeWrap}(cx, $${jsvalHandle})) { 6320 $*{exceptionCode} 6321 } 6322 $*{successCode} 6323 """, 6324 maybeWrap=getMaybeWrapValueFuncForType(wrapAsType), 6325 exceptionCode=exceptionCode, 6326 successCode=successCode) 6327 return ("${jsvalRef}.%s(%s);\n" % (setter, value)) + tail 6328 6329 def wrapAndSetPtr(wrapCall, failureCode=None): 6330 """ 6331 Returns the code to set the jsval by calling "wrapCall". "failureCode" 6332 is the code to run if calling "wrapCall" fails 6333 """ 6334 if failureCode is None: 6335 failureCode = exceptionCode 6336 return fill( 6337 """ 6338 if (!${wrapCall}) { 6339 $*{failureCode} 6340 } 6341 $*{successCode} 6342 """, 6343 wrapCall=wrapCall, 6344 failureCode=failureCode, 6345 successCode=successCode) 6346 6347 if type is None or type.isVoid(): 6348 return (setUndefined(), True) 6349 6350 if (type.isSequence() or type.isMozMap()) and type.nullable(): 6351 # These are both wrapped in Nullable<> 6352 recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider, 6353 "%s.Value()" % result, successCode, 6354 returnsNewObject, exceptionCode, 6355 typedArraysAreStructs) 6356 code = fill( 6357 """ 6358 6359 if (${result}.IsNull()) { 6360 $*{setNull} 6361 } 6362 $*{recTemplate} 6363 """, 6364 result=result, 6365 setNull=setNull(), 6366 recTemplate=recTemplate) 6367 return code, recInfall 6368 6369 if type.isSequence(): 6370 # Now do non-nullable sequences. Our success code is just to break to 6371 # where we set the element in the array. Note that we bump the 6372 # sequenceWrapLevel around this call so that nested sequence conversions 6373 # will use different iteration variables. 6374 global sequenceWrapLevel 6375 index = "sequenceIdx%d" % sequenceWrapLevel 6376 sequenceWrapLevel += 1 6377 innerTemplate = wrapForType( 6378 type.inner, descriptorProvider, 6379 { 6380 'result': "%s[%s]" % (result, index), 6381 'successCode': "break;\n", 6382 'jsvalRef': "tmp", 6383 'jsvalHandle': "&tmp", 6384 'returnsNewObject': returnsNewObject, 6385 'exceptionCode': exceptionCode, 6386 'obj': "returnArray", 6387 'typedArraysAreStructs': typedArraysAreStructs 6388 }) 6389 sequenceWrapLevel -= 1 6390 code = fill( 6391 """ 6392 6393 uint32_t length = ${result}.Length(); 6394 JS::Rooted<JSObject*> returnArray(cx, JS_NewArrayObject(cx, length)); 6395 if (!returnArray) { 6396 $*{exceptionCode} 6397 } 6398 // Scope for 'tmp' 6399 { 6400 JS::Rooted<JS::Value> tmp(cx); 6401 for (uint32_t ${index} = 0; ${index} < length; ++${index}) { 6402 // Control block to let us common up the JS_DefineElement calls when there 6403 // are different ways to succeed at wrapping the object. 6404 do { 6405 $*{innerTemplate} 6406 } while (0); 6407 if (!JS_DefineElement(cx, returnArray, ${index}, tmp, 6408 JSPROP_ENUMERATE)) { 6409 $*{exceptionCode} 6410 } 6411 } 6412 } 6413 $*{set} 6414 """, 6415 result=result, 6416 exceptionCode=exceptionCode, 6417 index=index, 6418 innerTemplate=innerTemplate, 6419 set=setObject("*returnArray")) 6420 6421 return (code, False) 6422 6423 if type.isMozMap(): 6424 # Now do non-nullable MozMap. Our success code is just to break to 6425 # where we define the property on the object. Note that we bump the 6426 # mozMapWrapLevel around this call so that nested MozMap conversions 6427 # will use different temp value names. 6428 global mozMapWrapLevel 6429 valueName = "mozMapValue%d" % mozMapWrapLevel 6430 mozMapWrapLevel += 1 6431 innerTemplate = wrapForType( 6432 type.inner, descriptorProvider, 6433 { 6434 'result': valueName, 6435 'successCode': "break;\n", 6436 'jsvalRef': "tmp", 6437 'jsvalHandle': "&tmp", 6438 'returnsNewObject': returnsNewObject, 6439 'exceptionCode': exceptionCode, 6440 'obj': "returnObj", 6441 'typedArraysAreStructs': typedArraysAreStructs 6442 }) 6443 mozMapWrapLevel -= 1 6444 code = fill( 6445 """ 6446 6447 nsTArray<nsString> keys; 6448 ${result}.GetKeys(keys); 6449 JS::Rooted<JSObject*> returnObj(cx, JS_NewPlainObject(cx)); 6450 if (!returnObj) { 6451 $*{exceptionCode} 6452 } 6453 // Scope for 'tmp' 6454 { 6455 JS::Rooted<JS::Value> tmp(cx); 6456 for (size_t idx = 0; idx < keys.Length(); ++idx) { 6457 auto& ${valueName} = ${result}.Get(keys[idx]); 6458 // Control block to let us common up the JS_DefineUCProperty calls when there 6459 // are different ways to succeed at wrapping the value. 6460 do { 6461 $*{innerTemplate} 6462 } while (0); 6463 if (!JS_DefineUCProperty(cx, returnObj, keys[idx].get(), 6464 keys[idx].Length(), tmp, 6465 JSPROP_ENUMERATE)) { 6466 $*{exceptionCode} 6467 } 6468 } 6469 } 6470 $*{set} 6471 """, 6472 result=result, 6473 exceptionCode=exceptionCode, 6474 valueName=valueName, 6475 innerTemplate=innerTemplate, 6476 set=setObject("*returnObj")) 6477 6478 return (code, False) 6479 6480 if type.isGeckoInterface() and not type.isCallbackInterface(): 6481 descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name) 6482 if type.nullable(): 6483 wrappingCode = ("if (!%s) {\n" % (result) + 6484 indent(setNull()) + 6485 "}\n") 6486 else: 6487 wrappingCode = "" 6488 6489 if not descriptor.interface.isExternal(): 6490 if descriptor.wrapperCache: 6491 wrapMethod = "GetOrCreateDOMReflector" 6492 wrapArgs = "cx, %s, ${jsvalHandle}" % result 6493 else: 6494 # Hack: the "Promise" interface is OK to return from 6495 # non-newobject things even when it's not wrappercached; that 6496 # happens when using SpiderMonkey promises, and the WrapObject() 6497 # method will just return the existing reflector, which is just 6498 # not stored in a wrappercache. 6499 if (not returnsNewObject and 6500 descriptor.interface.identifier.name != "Promise"): 6501 raise MethodNotNewObjectError(descriptor.interface.identifier.name) 6502 wrapMethod = "WrapNewBindingNonWrapperCachedObject" 6503 wrapArgs = "cx, ${obj}, %s, ${jsvalHandle}" % result 6504 if isConstructorRetval: 6505 wrapArgs += ", desiredProto" 6506 wrap = "%s(%s)" % (wrapMethod, wrapArgs) 6507 if not descriptor.hasXPConnectImpls: 6508 # Can only fail to wrap as a new-binding object 6509 # if they already threw an exception. 6510 # XXX Assertion disabled for now, see bug 991271. 6511 failed = ("MOZ_ASSERT(true || JS_IsExceptionPending(cx));\n" + 6512 exceptionCode) 6513 else: 6514 if descriptor.notflattened: 6515 raise TypeError("%s has XPConnect impls but not flattened; " 6516 "fallback won't work correctly" % 6517 descriptor.interface.identifier.name) 6518 # Try old-style wrapping for bindings which might be XPConnect impls. 6519 failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalHandle})" % result) 6520 else: 6521 if descriptor.notflattened: 6522 getIID = "&NS_GET_IID(%s), " % descriptor.nativeType 6523 else: 6524 getIID = "" 6525 wrap = "WrapObject(cx, %s, %s${jsvalHandle})" % (result, getIID) 6526 failed = None 6527 6528 wrappingCode += wrapAndSetPtr(wrap, failed) 6529 return (wrappingCode, False) 6530 6531 if type.isDOMString() or type.isUSVString(): 6532 if type.nullable(): 6533 return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result), False) 6534 else: 6535 return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result), False) 6536 6537 if type.isByteString(): 6538 if type.nullable(): 6539 return (wrapAndSetPtr("ByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False) 6540 else: 6541 return (wrapAndSetPtr("NonVoidByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False) 6542 6543 if type.isEnum(): 6544 if type.nullable(): 6545 resultLoc = "%s.Value()" % result 6546 else: 6547 resultLoc = result 6548 conversion = fill( 6549 """ 6550 if (!ToJSValue(cx, ${result}, $${jsvalHandle})) { 6551 $*{exceptionCode} 6552 } 6553 $*{successCode} 6554 """, 6555 result=resultLoc, 6556 exceptionCode=exceptionCode, 6557 successCode=successCode) 6558 6559 if type.nullable(): 6560 conversion = CGIfElseWrapper( 6561 "%s.IsNull()" % result, 6562 CGGeneric(setNull()), 6563 CGGeneric(conversion)).define() 6564 return conversion, False 6565 6566 if type.isCallback() or type.isCallbackInterface(): 6567 wrapCode = setObject( 6568 "*GetCallbackFromCallbackObject(%(result)s)", 6569 wrapAsType=type) 6570 if type.nullable(): 6571 wrapCode = ( 6572 "if (%(result)s) {\n" + 6573 indent(wrapCode) + 6574 "} else {\n" + 6575 indent(setNull()) + 6576 "}\n") 6577 wrapCode = wrapCode % {"result": result} 6578 return wrapCode, False 6579 6580 if type.isAny(): 6581 # See comments in GetOrCreateDOMReflector explaining why we need 6582 # to wrap here. 6583 # NB: _setValue(..., type-that-is-any) calls JS_WrapValue(), so is fallible 6584 head = "JS::ExposeValueToActiveJS(%s);\n" % result 6585 return (head + _setValue(result, wrapAsType=type), False) 6586 6587 if (type.isObject() or (type.isSpiderMonkeyInterface() and 6588 not typedArraysAreStructs)): 6589 # See comments in GetOrCreateDOMReflector explaining why we need 6590 # to wrap here. 6591 if type.nullable(): 6592 toValue = "%s" 6593 setter = setObjectOrNull 6594 head = """if (%s) { 6595 JS::ExposeObjectToActiveJS(%s); 6596 } 6597 """ % (result, result) 6598 else: 6599 toValue = "*%s" 6600 setter = setObject 6601 head = "JS::ExposeObjectToActiveJS(%s);\n" % result 6602 # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible 6603 return (head + setter(toValue % result, wrapAsType=type), False) 6604 6605 if not (type.isUnion() or type.isPrimitive() or type.isDictionary() or 6606 type.isDate() or 6607 (type.isSpiderMonkeyInterface() and typedArraysAreStructs)): 6608 raise TypeError("Need to learn to wrap %s" % type) 6609 6610 if type.nullable(): 6611 recTemplate, recInfal = getWrapTemplateForType(type.inner, descriptorProvider, 6612 "%s.Value()" % result, successCode, 6613 returnsNewObject, exceptionCode, 6614 typedArraysAreStructs) 6615 return ("if (%s.IsNull()) {\n" % result + 6616 indent(setNull()) + 6617 "}\n" + 6618 recTemplate, recInfal) 6619 6620 if type.isSpiderMonkeyInterface(): 6621 assert typedArraysAreStructs 6622 # See comments in GetOrCreateDOMReflector explaining why we need 6623 # to wrap here. 6624 # NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible 6625 return (setObject("*%s.Obj()" % result, 6626 wrapAsType=type), False) 6627 6628 if type.isUnion(): 6629 return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result), 6630 False) 6631 6632 if type.isDictionary(): 6633 return (wrapAndSetPtr("%s.ToObjectInternal(cx, ${jsvalHandle})" % result), 6634 False) 6635 6636 if type.isDate(): 6637 return (wrapAndSetPtr("%s.ToDateObject(cx, ${jsvalHandle})" % result), 6638 False) 6639 6640 tag = type.tag() 6641 6642 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16, 6643 IDLType.Tags.uint16, IDLType.Tags.int32]: 6644 return (setInt32("int32_t(%s)" % result), True) 6645 6646 elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64, 6647 IDLType.Tags.unrestricted_float, IDLType.Tags.float, 6648 IDLType.Tags.unrestricted_double, IDLType.Tags.double]: 6649 # XXXbz will cast to double do the "even significand" thing that webidl 6650 # calls for for 64-bit ints? Do we care? 6651 return (setDouble("double(%s)" % result), True) 6652 6653 elif tag == IDLType.Tags.uint32: 6654 return (setUint32(result), True) 6655 6656 elif tag == IDLType.Tags.bool: 6657 return (setBoolean(result), True) 6658 6659 else: 6660 raise TypeError("Need to learn to wrap primitive: %s" % type) 6661 6662 6663def wrapForType(type, descriptorProvider, templateValues): 6664 """ 6665 Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict 6666 that should contain: 6667 6668 * 'jsvalRef': something that can have .address() called on it to get a 6669 JS::Value* and .set() called on it to set it to a JS::Value. 6670 This can be a JS::MutableHandle<JS::Value> or a 6671 JS::Rooted<JS::Value>. 6672 * 'jsvalHandle': something that can be passed to methods taking a 6673 JS::MutableHandle<JS::Value>. This can be a 6674 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*. 6675 * 'obj' (optional): the name of the variable that contains the JSObject to 6676 use as a scope when wrapping, if not supplied 'obj' 6677 will be used as the name 6678 * 'result' (optional): the name of the variable in which the C++ value is 6679 stored, if not supplied 'result' will be used as 6680 the name 6681 * 'successCode' (optional): the code to run once we have successfully 6682 done the conversion, if not supplied 'return 6683 true;' will be used as the code. The 6684 successCode must ensure that once it runs no 6685 more of the conversion template will be 6686 executed (e.g. by doing a 'return' or 'break' 6687 as appropriate). 6688 * 'returnsNewObject' (optional): If true, we're wrapping for the return 6689 value of a [NewObject] method. Assumed 6690 false if not set. 6691 * 'exceptionCode' (optional): Code to run when a JS exception is thrown. 6692 The default is "return false;". The code 6693 passed here must return. 6694 * 'isConstructorRetval' (optional): If true, we're wrapping a constructor 6695 return value. 6696 """ 6697 wrap = getWrapTemplateForType( 6698 type, descriptorProvider, 6699 templateValues.get('result', 'result'), 6700 templateValues.get('successCode', None), 6701 templateValues.get('returnsNewObject', False), 6702 templateValues.get('exceptionCode', "return false;\n"), 6703 templateValues.get('typedArraysAreStructs', False), 6704 isConstructorRetval=templateValues.get('isConstructorRetval', False))[0] 6705 6706 defaultValues = {'obj': 'obj'} 6707 return string.Template(wrap).substitute(defaultValues, **templateValues) 6708 6709 6710def infallibleForMember(member, type, descriptorProvider): 6711 """ 6712 Determine the fallibility of changing a C++ value of IDL type "type" into 6713 JS for the given attribute. Apart from returnsNewObject, all the defaults 6714 are used, since the fallbility does not change based on the boolean values, 6715 and the template will be discarded. 6716 6717 CURRENT ASSUMPTIONS: 6718 We assume that successCode for wrapping up return values cannot contain 6719 failure conditions. 6720 """ 6721 return getWrapTemplateForType(type, descriptorProvider, 'result', None, 6722 memberReturnsNewObject(member), "return false;\n", 6723 False)[1] 6724 6725 6726def leafTypeNeedsCx(type, retVal): 6727 return (type.isAny() or type.isObject() or 6728 (retVal and type.isSpiderMonkeyInterface())) 6729 6730 6731def leafTypeNeedsScopeObject(type, retVal): 6732 return retVal and type.isSpiderMonkeyInterface() 6733 6734 6735def leafTypeNeedsRooting(type): 6736 return leafTypeNeedsCx(type, False) or type.isSpiderMonkeyInterface() 6737 6738 6739def typeNeedsRooting(type): 6740 return typeMatchesLambda(type, 6741 lambda t: leafTypeNeedsRooting(t)) 6742 6743 6744def typeNeedsCx(type, retVal=False): 6745 return typeMatchesLambda(type, 6746 lambda t: leafTypeNeedsCx(t, retVal)) 6747 6748 6749def typeNeedsScopeObject(type, retVal=False): 6750 return typeMatchesLambda(type, 6751 lambda t: leafTypeNeedsScopeObject(t, retVal)) 6752 6753 6754def typeMatchesLambda(type, func): 6755 if type is None: 6756 return False 6757 if type.nullable(): 6758 return typeMatchesLambda(type.inner, func) 6759 if type.isSequence() or type.isMozMap(): 6760 return typeMatchesLambda(type.inner, func) 6761 if type.isUnion(): 6762 return any(typeMatchesLambda(t, func) for t in 6763 type.unroll().flatMemberTypes) 6764 if type.isDictionary(): 6765 return dictionaryMatchesLambda(type.inner, func) 6766 return func(type) 6767 6768 6769def dictionaryMatchesLambda(dictionary, func): 6770 return (any(typeMatchesLambda(m.type, func) for m in dictionary.members) or 6771 (dictionary.parent and dictionaryMatchesLambda(dictionary.parent, func))) 6772 6773 6774# Whenever this is modified, please update CGNativeMember.getRetvalInfo as 6775# needed to keep the types compatible. 6776def getRetvalDeclarationForType(returnType, descriptorProvider, 6777 isMember=False): 6778 """ 6779 Returns a tuple containing five things: 6780 6781 1) A CGThing for the type of the return value, or None if there is no need 6782 for a return value. 6783 6784 2) A value indicating the kind of ourparam to pass the value as. Valid 6785 options are None to not pass as an out param at all, "ref" (to pass a 6786 reference as an out param), and "ptr" (to pass a pointer as an out 6787 param). 6788 6789 3) A CGThing for a tracer for the return value, or None if no tracing is 6790 needed. 6791 6792 4) An argument string to pass to the retval declaration 6793 constructor or None if there are no arguments. 6794 6795 5) The name of a function that needs to be called with the return value 6796 before using it, or None if no function needs to be called. 6797 """ 6798 if returnType is None or returnType.isVoid(): 6799 # Nothing to declare 6800 return None, None, None, None, None 6801 if returnType.isPrimitive() and returnType.tag() in builtinNames: 6802 result = CGGeneric(builtinNames[returnType.tag()]) 6803 if returnType.nullable(): 6804 result = CGTemplatedType("Nullable", result) 6805 return result, None, None, None, None 6806 if returnType.isDOMString() or returnType.isUSVString(): 6807 if isMember: 6808 return CGGeneric("nsString"), "ref", None, None, None 6809 return CGGeneric("DOMString"), "ref", None, None, None 6810 if returnType.isByteString(): 6811 return CGGeneric("nsCString"), "ref", None, None, None 6812 if returnType.isEnum(): 6813 result = CGGeneric(returnType.unroll().inner.identifier.name) 6814 if returnType.nullable(): 6815 result = CGTemplatedType("Nullable", result) 6816 return result, None, None, None, None 6817 if returnType.isGeckoInterface(): 6818 result = CGGeneric(descriptorProvider.getDescriptor( 6819 returnType.unroll().inner.identifier.name).nativeType) 6820 conversion = None 6821 if isMember: 6822 result = CGGeneric("StrongPtrForMember<%s>::Type" % result.define()) 6823 else: 6824 conversion = CGGeneric("StrongOrRawPtr<%s>" % result.define()) 6825 result = CGGeneric("auto") 6826 return result, None, None, None, conversion 6827 if returnType.isCallback(): 6828 name = returnType.unroll().callback.identifier.name 6829 return CGGeneric("RefPtr<%s>" % name), None, None, None, None 6830 if returnType.isAny(): 6831 if isMember: 6832 return CGGeneric("JS::Value"), None, None, None, None 6833 return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx", None 6834 if returnType.isObject() or returnType.isSpiderMonkeyInterface(): 6835 if isMember: 6836 return CGGeneric("JSObject*"), None, None, None, None 6837 return CGGeneric("JS::Rooted<JSObject*>"), "ptr", None, "cx", None 6838 if returnType.isSequence(): 6839 nullable = returnType.nullable() 6840 if nullable: 6841 returnType = returnType.inner 6842 result, _, _, _, _ = getRetvalDeclarationForType(returnType.inner, 6843 descriptorProvider, 6844 isMember="Sequence") 6845 # While we have our inner type, set up our rooter, if needed 6846 if not isMember and typeNeedsRooting(returnType): 6847 rooter = CGGeneric("SequenceRooter<%s > resultRooter(cx, &result);\n" % 6848 result.define()) 6849 else: 6850 rooter = None 6851 result = CGTemplatedType("nsTArray", result) 6852 if nullable: 6853 result = CGTemplatedType("Nullable", result) 6854 return result, "ref", rooter, None, None 6855 if returnType.isMozMap(): 6856 nullable = returnType.nullable() 6857 if nullable: 6858 returnType = returnType.inner 6859 result, _, _, _, _ = getRetvalDeclarationForType(returnType.inner, 6860 descriptorProvider, 6861 isMember="MozMap") 6862 # While we have our inner type, set up our rooter, if needed 6863 if not isMember and typeNeedsRooting(returnType): 6864 rooter = CGGeneric("MozMapRooter<%s> resultRooter(cx, &result);\n" % 6865 result.define()) 6866 else: 6867 rooter = None 6868 result = CGTemplatedType("MozMap", result) 6869 if nullable: 6870 result = CGTemplatedType("Nullable", result) 6871 return result, "ref", rooter, None, None 6872 if returnType.isDictionary(): 6873 nullable = returnType.nullable() 6874 dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner) 6875 result = CGGeneric(dictName) 6876 if not isMember and typeNeedsRooting(returnType): 6877 if nullable: 6878 result = CGTemplatedType("NullableRootedDictionary", result) 6879 else: 6880 result = CGTemplatedType("RootedDictionary", result) 6881 resultArgs = "cx" 6882 else: 6883 if nullable: 6884 result = CGTemplatedType("Nullable", result) 6885 resultArgs = None 6886 return result, "ref", None, resultArgs, None 6887 if returnType.isUnion(): 6888 result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True)) 6889 if not isMember and typeNeedsRooting(returnType): 6890 if returnType.nullable(): 6891 result = CGTemplatedType("NullableRootedUnion", result) 6892 else: 6893 result = CGTemplatedType("RootedUnion", result) 6894 resultArgs = "cx" 6895 else: 6896 if returnType.nullable(): 6897 result = CGTemplatedType("Nullable", result) 6898 resultArgs = None 6899 return result, "ref", None, resultArgs, None 6900 if returnType.isDate(): 6901 result = CGGeneric("Date") 6902 if returnType.nullable(): 6903 result = CGTemplatedType("Nullable", result) 6904 return result, None, None, None, None 6905 raise TypeError("Don't know how to declare return value for %s" % 6906 returnType) 6907 6908 6909def needCx(returnType, arguments, extendedAttributes, considerTypes, 6910 static=False): 6911 return (not static and considerTypes and 6912 (typeNeedsCx(returnType, True) or 6913 any(typeNeedsCx(a.type) for a in arguments)) or 6914 'implicitJSContext' in extendedAttributes) 6915 6916 6917def needScopeObject(returnType, arguments, extendedAttributes, 6918 isWrapperCached, considerTypes, isMember): 6919 """ 6920 isMember should be true if we're dealing with an attribute 6921 annotated as [StoreInSlot]. 6922 """ 6923 return (considerTypes and not isWrapperCached and 6924 ((not isMember and typeNeedsScopeObject(returnType, True)) or 6925 any(typeNeedsScopeObject(a.type) for a in arguments))) 6926 6927 6928class CGCallGenerator(CGThing): 6929 """ 6930 A class to generate an actual call to a C++ object. Assumes that the C++ 6931 object is stored in a variable whose name is given by the |object| argument. 6932 6933 needsSubjectPrincipal is a boolean indicating whether the call should 6934 receive the subject nsIPrincipal as argument. 6935 6936 needsCallerType is a boolean indicating whether the call should receive 6937 a PrincipalType for the caller. 6938 6939 isFallible is a boolean indicating whether the call should be fallible. 6940 6941 resultVar: If the returnType is not void, then the result of the call is 6942 stored in a C++ variable named by resultVar. The caller is responsible for 6943 declaring the result variable. If the caller doesn't care about the result 6944 value, resultVar can be omitted. 6945 """ 6946 def __init__(self, isFallible, needsSubjectPrincipal, needsCallerType, 6947 arguments, argsPre, returnType, extendedAttributes, descriptor, 6948 nativeMethodName, static, object="self", argsPost=[], 6949 resultVar=None): 6950 CGThing.__init__(self) 6951 6952 result, resultOutParam, resultRooter, resultArgs, resultConversion = \ 6953 getRetvalDeclarationForType(returnType, descriptor) 6954 6955 args = CGList([CGGeneric(arg) for arg in argsPre], ", ") 6956 for a, name in arguments: 6957 arg = CGGeneric(name) 6958 6959 # Now constify the things that need it 6960 def needsConst(a): 6961 if a.type.isDictionary(): 6962 return True 6963 if a.type.isSequence(): 6964 return True 6965 if a.type.isMozMap(): 6966 return True 6967 # isObject() types are always a JS::Rooted, whether 6968 # nullable or not, and it turns out a const JS::Rooted 6969 # is not very helpful at all (in particular, it won't 6970 # even convert to a JS::Handle). 6971 # XXX bz Well, why not??? 6972 if a.type.nullable() and not a.type.isObject(): 6973 return True 6974 if a.type.isString(): 6975 return True 6976 if a.canHaveMissingValue(): 6977 # This will need an Optional or it's a variadic; 6978 # in both cases it should be const. 6979 return True 6980 if a.type.isUnion(): 6981 return True 6982 if a.type.isSpiderMonkeyInterface(): 6983 return True 6984 return False 6985 if needsConst(a): 6986 arg = CGWrapper(arg, pre="Constify(", post=")") 6987 # And convert NonNull<T> to T& 6988 if (((a.type.isGeckoInterface() or a.type.isCallback()) and not a.type.nullable()) or 6989 a.type.isDOMString()): 6990 arg = CGWrapper(arg, pre="NonNullHelper(", post=")") 6991 args.append(arg) 6992 6993 needResultDecl = False 6994 6995 # Return values that go in outparams go here 6996 if resultOutParam is not None: 6997 if resultVar is None: 6998 needResultDecl = True 6999 resultVar = "result" 7000 if resultOutParam == "ref": 7001 args.append(CGGeneric(resultVar)) 7002 else: 7003 assert resultOutParam == "ptr" 7004 args.append(CGGeneric("&" + resultVar)) 7005 7006 if needsSubjectPrincipal: 7007 args.append(CGGeneric("subjectPrincipal")) 7008 7009 if needsCallerType: 7010 args.append(CGGeneric("callerType")) 7011 7012 if isFallible: 7013 args.append(CGGeneric("rv")) 7014 args.extend(CGGeneric(arg) for arg in argsPost) 7015 7016 # Build up our actual call 7017 self.cgRoot = CGList([]) 7018 7019 call = CGGeneric(nativeMethodName) 7020 if not static: 7021 call = CGWrapper(call, pre="%s->" % object) 7022 call = CGList([call, CGWrapper(args, pre="(", post=")")]) 7023 if resultConversion is not None: 7024 call = CGList([resultConversion, CGWrapper(call, pre="(", post=")")]) 7025 if resultVar is None and result is not None: 7026 needResultDecl = True 7027 resultVar = "result" 7028 7029 if needResultDecl: 7030 if resultRooter is not None: 7031 self.cgRoot.prepend(resultRooter) 7032 if resultArgs is not None: 7033 resultArgsStr = "(%s)" % resultArgs 7034 else: 7035 resultArgsStr = "" 7036 result = CGWrapper(result, post=(" %s%s" % (resultVar, resultArgsStr))) 7037 if resultOutParam is None and resultArgs is None: 7038 call = CGList([result, CGWrapper(call, pre="(", post=")")]) 7039 else: 7040 self.cgRoot.prepend(CGWrapper(result, post=";\n")) 7041 if resultOutParam is None: 7042 call = CGWrapper(call, pre=resultVar + " = ") 7043 elif result is not None: 7044 assert resultOutParam is None 7045 call = CGWrapper(call, pre=resultVar + " = ") 7046 7047 call = CGWrapper(call, post=";\n") 7048 self.cgRoot.append(call) 7049 7050 if needsSubjectPrincipal: 7051 getPrincipal = dedent( 7052 """ 7053 JSCompartment* compartment = js::GetContextCompartment(cx); 7054 MOZ_ASSERT(compartment); 7055 JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment); 7056 """) 7057 7058 if descriptor.interface.isExposedInAnyWorker(): 7059 self.cgRoot.prepend(CGGeneric(fill( 7060 """ 7061 Maybe<nsIPrincipal*> subjectPrincipal; 7062 if (NS_IsMainThread()) { 7063 $*{getPrincipal} 7064 subjectPrincipal.emplace(nsJSPrincipals::get(principals)); 7065 } 7066 """, 7067 getPrincipal=getPrincipal))) 7068 else: 7069 self.cgRoot.prepend(CGGeneric(fill( 7070 """ 7071 $*{getPrincipal} 7072 // Initializing a nonnull is pretty darn annoying... 7073 NonNull<nsIPrincipal> subjectPrincipal; 7074 subjectPrincipal = static_cast<nsIPrincipal*>(nsJSPrincipals::get(principals)); 7075 """, 7076 getPrincipal=getPrincipal))) 7077 7078 if needsCallerType: 7079 # Note that we do not want to use 7080 # IsCallerChrome/ThreadsafeIsCallerChrome directly because those 7081 # will pull in the check for UniversalXPConnect, which we ideally 7082 # don't want to have in the new thing we're doing here. If not 7083 # NS_IsMainThread(), though, we'll go ahead and call 7084 # ThreasafeIsCallerChrome(), since that won't mess with 7085 # UnivesalXPConnect and we don't want to worry about the right 7086 # worker includes here. 7087 callerCheck = CGGeneric("callerType = nsContentUtils::IsSystemPrincipal(nsContentUtils::SubjectPrincipal()) ? CallerType::System : CallerType::NonSystem;\n") 7088 if descriptor.interface.isExposedInAnyWorker(): 7089 callerCheck = CGIfElseWrapper( 7090 "NS_IsMainThread()", 7091 callerCheck, 7092 CGGeneric("callerType = nsContentUtils::ThreadsafeIsCallerChrome() ? CallerType::System : CallerType::NonSystem;\n")); 7093 self.cgRoot.prepend(callerCheck) 7094 self.cgRoot.prepend(CGGeneric("CallerType callerType;\n")) 7095 7096 if isFallible: 7097 self.cgRoot.prepend(CGGeneric("binding_detail::FastErrorResult rv;\n")) 7098 self.cgRoot.append(CGGeneric(dedent( 7099 """ 7100 if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx))) { 7101 return false; 7102 } 7103 """))) 7104 7105 self.cgRoot.append(CGGeneric("MOZ_ASSERT(!JS_IsExceptionPending(cx));\n")) 7106 7107 def define(self): 7108 return self.cgRoot.define() 7109 7110 7111def getUnionMemberName(type): 7112 if type.isGeckoInterface(): 7113 return type.inner.identifier.name 7114 if type.isEnum(): 7115 return type.inner.identifier.name 7116 return type.name 7117 7118 7119class MethodNotNewObjectError(Exception): 7120 def __init__(self, typename): 7121 self.typename = typename 7122 7123# A counter for making sure that when we're wrapping up things in 7124# nested sequences we don't use the same variable name to iterate over 7125# different sequences. 7126sequenceWrapLevel = 0 7127mapWrapLevel = 0 7128 7129 7130def wrapTypeIntoCurrentCompartment(type, value, isMember=True): 7131 """ 7132 Take the thing named by "value" and if it contains "any", 7133 "object", or spidermonkey-interface types inside return a CGThing 7134 that will wrap them into the current compartment. 7135 """ 7136 if type.isAny(): 7137 assert not type.nullable() 7138 if isMember: 7139 value = "JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" % value 7140 else: 7141 value = "&" + value 7142 return CGGeneric("if (!JS_WrapValue(cx, %s)) {\n" 7143 " return false;\n" 7144 "}\n" % value) 7145 7146 if type.isObject(): 7147 if isMember: 7148 value = "JS::MutableHandle<JSObject*>::fromMarkedLocation(&%s)" % value 7149 else: 7150 value = "&" + value 7151 return CGGeneric("if (!JS_WrapObject(cx, %s)) {\n" 7152 " return false;\n" 7153 "}\n" % value) 7154 7155 if type.isSpiderMonkeyInterface(): 7156 origValue = value 7157 if type.nullable(): 7158 value = "%s.Value()" % value 7159 wrapCode = CGGeneric("if (!%s.WrapIntoNewCompartment(cx)) {\n" 7160 " return false;\n" 7161 "}\n" % value) 7162 if type.nullable(): 7163 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue) 7164 return wrapCode 7165 7166 if type.isSequence(): 7167 origValue = value 7168 origType = type 7169 if type.nullable(): 7170 type = type.inner 7171 value = "%s.Value()" % value 7172 global sequenceWrapLevel 7173 index = "indexName%d" % sequenceWrapLevel 7174 sequenceWrapLevel += 1 7175 wrapElement = wrapTypeIntoCurrentCompartment(type.inner, 7176 "%s[%s]" % (value, index)) 7177 sequenceWrapLevel -= 1 7178 if not wrapElement: 7179 return None 7180 wrapCode = CGWrapper(CGIndenter(wrapElement), 7181 pre=("for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n" % 7182 (index, index, value, index)), 7183 post="}\n") 7184 if origType.nullable(): 7185 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue) 7186 return wrapCode 7187 7188 if type.isMozMap(): 7189 origValue = value 7190 origType = type 7191 if type.nullable(): 7192 type = type.inner 7193 value = "%s.Value()" % value 7194 global mapWrapLevel 7195 key = "mapName%d" % mapWrapLevel 7196 mapWrapLevel += 1 7197 wrapElement = wrapTypeIntoCurrentCompartment(type.inner, 7198 "%s.Get(%sKeys[%sIndex])" % (value, key, key)) 7199 mapWrapLevel -= 1 7200 if not wrapElement: 7201 return None 7202 wrapCode = CGWrapper(CGIndenter(wrapElement), 7203 pre=(""" 7204 nsTArray<nsString> %sKeys; 7205 %s.GetKeys(%sKeys); 7206 for (uint32_t %sIndex = 0; %sIndex < %sKeys.Length(); ++%sIndex) { 7207 """ % (key, value, key, key, key, key, key)), 7208 post="}\n") 7209 if origType.nullable(): 7210 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue) 7211 return wrapCode 7212 7213 if type.isDictionary(): 7214 assert not type.nullable() 7215 myDict = type.inner 7216 memberWraps = [] 7217 while myDict: 7218 for member in myDict.members: 7219 memberWrap = wrapArgIntoCurrentCompartment( 7220 member, 7221 "%s.%s" % (value, CGDictionary.makeMemberName(member.identifier.name))) 7222 if memberWrap: 7223 memberWraps.append(memberWrap) 7224 myDict = myDict.parent 7225 return CGList(memberWraps) if len(memberWraps) != 0 else None 7226 7227 if type.isUnion(): 7228 memberWraps = [] 7229 if type.nullable(): 7230 type = type.inner 7231 value = "%s.Value()" % value 7232 for member in type.flatMemberTypes: 7233 memberName = getUnionMemberName(member) 7234 memberWrap = wrapTypeIntoCurrentCompartment( 7235 member, "%s.GetAs%s()" % (value, memberName)) 7236 if memberWrap: 7237 memberWrap = CGIfWrapper( 7238 memberWrap, "%s.Is%s()" % (value, memberName)) 7239 memberWraps.append(memberWrap) 7240 return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None 7241 7242 if (type.isString() or type.isPrimitive() or type.isEnum() or 7243 type.isGeckoInterface() or type.isCallback() or type.isDate()): 7244 # All of these don't need wrapping 7245 return None 7246 7247 raise TypeError("Unknown type; we don't know how to wrap it in constructor " 7248 "arguments: %s" % type) 7249 7250 7251def wrapArgIntoCurrentCompartment(arg, value, isMember=True): 7252 """ 7253 As wrapTypeIntoCurrentCompartment but handles things being optional 7254 """ 7255 origValue = value 7256 isOptional = arg.canHaveMissingValue() 7257 if isOptional: 7258 value = value + ".Value()" 7259 wrap = wrapTypeIntoCurrentCompartment(arg.type, value, isMember) 7260 if wrap and isOptional: 7261 wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue) 7262 return wrap 7263 7264 7265def needsContainsHack(m): 7266 return m.getExtendedAttribute("ReturnValueNeedsContainsHack") 7267 7268def needsCallerType(m): 7269 return m.getExtendedAttribute("NeedsCallerType") 7270 7271class CGPerSignatureCall(CGThing): 7272 """ 7273 This class handles the guts of generating code for a particular 7274 call signature. A call signature consists of four things: 7275 7276 1) A return type, which can be None to indicate that there is no 7277 actual return value (e.g. this is an attribute setter) or an 7278 IDLType if there's an IDL type involved (including |void|). 7279 2) An argument list, which is allowed to be empty. 7280 3) A name of a native method to call. 7281 4) Whether or not this method is static. Note that this only controls how 7282 the method is called (|self->nativeMethodName(...)| vs 7283 |nativeMethodName(...)|). 7284 7285 We also need to know whether this is a method or a getter/setter 7286 to do error reporting correctly. 7287 7288 The idlNode parameter can be either a method or an attr. We can query 7289 |idlNode.identifier| in both cases, so we can be agnostic between the two. 7290 """ 7291 # XXXbz For now each entry in the argument list is either an 7292 # IDLArgument or a FakeArgument, but longer-term we may want to 7293 # have ways of flagging things like JSContext* or optional_argc in 7294 # there. 7295 7296 def __init__(self, returnType, arguments, nativeMethodName, static, 7297 descriptor, idlNode, argConversionStartsAt=0, getter=False, 7298 setter=False, isConstructor=False, useCounterName=None, 7299 resultVar=None): 7300 assert idlNode.isMethod() == (not getter and not setter) 7301 assert idlNode.isAttr() == (getter or setter) 7302 # Constructors are always static 7303 assert not isConstructor or static 7304 7305 CGThing.__init__(self) 7306 self.returnType = returnType 7307 self.descriptor = descriptor 7308 self.idlNode = idlNode 7309 self.extendedAttributes = descriptor.getExtendedAttributes(idlNode, 7310 getter=getter, 7311 setter=setter) 7312 self.arguments = arguments 7313 self.argCount = len(arguments) 7314 self.isConstructor = isConstructor 7315 cgThings = [] 7316 7317 # Here, we check if the current getter, setter, method, interface or 7318 # inherited interfaces have the UnsafeInPrerendering extended attribute 7319 # and if so, we add a check to make sure it is safe. 7320 if (idlNode.getExtendedAttribute("UnsafeInPrerendering") or 7321 descriptor.interface.getExtendedAttribute("UnsafeInPrerendering") or 7322 any(i.getExtendedAttribute("UnsafeInPrerendering") 7323 for i in descriptor.interface.getInheritedInterfaces())): 7324 cgThings.append(CGGeneric(dedent( 7325 """ 7326 if (!mozilla::dom::EnforceNotInPrerendering(cx, obj)) { 7327 // Return false from the JSNative in order to trigger 7328 // an uncatchable exception. 7329 MOZ_ASSERT(!JS_IsExceptionPending(cx)); 7330 return false; 7331 } 7332 """))) 7333 7334 deprecated = (idlNode.getExtendedAttribute("Deprecated") or 7335 (idlNode.isStatic() and descriptor.interface.getExtendedAttribute("Deprecated"))) 7336 if deprecated: 7337 cgThings.append(CGGeneric(dedent( 7338 """ 7339 DeprecationWarning(cx, obj, nsIDocument::e%s); 7340 """ % deprecated[0]))) 7341 7342 lenientFloatCode = None 7343 if (idlNode.getExtendedAttribute('LenientFloat') is not None and 7344 (setter or idlNode.isMethod())): 7345 cgThings.append(CGGeneric(dedent( 7346 """ 7347 bool foundNonFiniteFloat = false; 7348 """))) 7349 lenientFloatCode = "foundNonFiniteFloat = true;\n" 7350 7351 argsPre = [] 7352 if idlNode.isStatic(): 7353 # If we're a constructor, "obj" may not be a function, so calling 7354 # XrayAwareCalleeGlobal() on it is not safe. Of course in the 7355 # constructor case either "obj" is an Xray or we're already in the 7356 # content compartment, not the Xray compartment, so just 7357 # constructing the GlobalObject from "obj" is fine. 7358 if isConstructor: 7359 objForGlobalObject = "obj" 7360 else: 7361 objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)" 7362 cgThings.append(CGGeneric(fill( 7363 """ 7364 GlobalObject global(cx, ${obj}); 7365 if (global.Failed()) { 7366 return false; 7367 } 7368 7369 """, 7370 obj=objForGlobalObject))) 7371 argsPre.append("global") 7372 7373 # For JS-implemented interfaces we do not want to base the 7374 # needsCx decision on the types involved, just on our extended 7375 # attributes. Also, JSContext is not needed for the static case 7376 # since GlobalObject already contains the context. 7377 needsCx = needCx(returnType, arguments, self.extendedAttributes, 7378 not descriptor.interface.isJSImplemented(), static) 7379 if needsCx: 7380 argsPre.append("cx") 7381 7382 # Hack for making Promise.prototype.then work well over Xrays. 7383 if (not idlNode.isStatic() and 7384 descriptor.name == "Promise" and 7385 idlNode.isMethod() and 7386 idlNode.identifier.name == "then"): 7387 cgThings.append(CGGeneric(dedent( 7388 """ 7389 JS::Rooted<JSObject*> calleeGlobal(cx, xpc::XrayAwareCalleeGlobal(&args.callee())); 7390 """))) 7391 argsPre.append("calleeGlobal") 7392 7393 needsUnwrap = False 7394 argsPost = [] 7395 if isConstructor: 7396 if descriptor.name == "Promise": 7397 # Hack for Promise for now: pass in our desired proto so the 7398 # implementation can create the reflector with the right proto. 7399 argsPost.append("desiredProto") 7400 # Also, we do not want to enter the content compartment when the 7401 # Promise constructor is called via Xrays, because we want to 7402 # create our callback functions that we will hand to our caller 7403 # in the Xray compartment. The reason we want to do that is the 7404 # following situation, over Xrays: 7405 # 7406 # contentWindow.Promise.race([Promise.resolve(5)]) 7407 # 7408 # Ideally this would work. Internally, race() does a 7409 # contentWindow.Promise.resolve() on everything in the array. 7410 # Per spec, to support subclassing, 7411 # contentWindow.Promise.resolve has to do: 7412 # 7413 # var resolve, reject; 7414 # var p = new contentWindow.Promise(function(a, b) { 7415 # resolve = a; 7416 # reject = b; 7417 # }); 7418 # resolve(arg); 7419 # return p; 7420 # 7421 # where "arg" is, in this case, the chrome-side return value of 7422 # Promise.resolve(5). But if the "resolve" function in that 7423 # case were created in the content compartment, then calling it 7424 # would wrap "arg" in an opaque wrapper, and that function tries 7425 # to get .then off the argument, which would throw. So we need 7426 # to create the "resolve" function in the chrome compartment, 7427 # and hence want to be running the entire Promise constructor 7428 # (which creates that function) in the chrome compartment in 7429 # this case. So don't set needsUnwrap here. 7430 else: 7431 needsUnwrap = True 7432 needsUnwrappedVar = False 7433 unwrappedVar = "obj" 7434 elif descriptor.interface.isJSImplemented(): 7435 if not idlNode.isStatic(): 7436 needsUnwrap = True 7437 needsUnwrappedVar = True 7438 argsPost.append("js::GetObjectCompartment(unwrappedObj ? *unwrappedObj : obj)") 7439 elif needScopeObject(returnType, arguments, self.extendedAttributes, 7440 descriptor.wrapperCache, True, 7441 idlNode.getExtendedAttribute("StoreInSlot")): 7442 needsUnwrap = True 7443 needsUnwrappedVar = True 7444 argsPre.append("unwrappedObj ? *unwrappedObj : obj") 7445 7446 if idlNode.isStatic() and not isConstructor and descriptor.name == "Promise": 7447 # Hack for Promise for now: pass in the "this" value to 7448 # Promise static methods. 7449 argsPre.append("args.thisv()") 7450 7451 if needsUnwrap and needsUnwrappedVar: 7452 # We cannot assign into obj because it's a Handle, not a 7453 # MutableHandle, so we need a separate Rooted. 7454 cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;\n")) 7455 unwrappedVar = "unwrappedObj.ref()" 7456 7457 if idlNode.isMethod() and idlNode.isLegacycaller(): 7458 # If we can have legacycaller with identifier, we can't 7459 # just use the idlNode to determine whether we're 7460 # generating code for the legacycaller or not. 7461 assert idlNode.isIdentifierLess() 7462 # Pass in our thisVal 7463 argsPre.append("args.thisv()") 7464 7465 ourName = "%s.%s" % (descriptor.interface.identifier.name, 7466 idlNode.identifier.name) 7467 if idlNode.isMethod(): 7468 argDescription = "argument %(index)d of " + ourName 7469 elif setter: 7470 argDescription = "value being assigned to %s" % ourName 7471 else: 7472 assert self.argCount == 0 7473 7474 if needsUnwrap: 7475 # It's very important that we construct our unwrappedObj, if we need 7476 # to do it, before we might start setting up Rooted things for our 7477 # arguments, so that we don't violate the stack discipline Rooted 7478 # depends on. 7479 cgThings.append(CGGeneric( 7480 "bool objIsXray = xpc::WrapperFactory::IsXrayWrapper(obj);\n")) 7481 if needsUnwrappedVar: 7482 cgThings.append(CGIfWrapper( 7483 CGGeneric("unwrappedObj.emplace(cx, obj);\n"), 7484 "objIsXray")) 7485 7486 for i in range(argConversionStartsAt, self.argCount): 7487 cgThings.append( 7488 CGArgumentConverter(arguments[i], i, self.descriptor, 7489 argDescription % {"index": i + 1}, 7490 idlNode, invalidEnumValueFatal=not setter, 7491 lenientFloatCode=lenientFloatCode)) 7492 7493 # Now that argument processing is done, enforce the LenientFloat stuff 7494 if lenientFloatCode: 7495 if setter: 7496 foundNonFiniteFloatBehavior = "return true;\n" 7497 else: 7498 assert idlNode.isMethod() 7499 foundNonFiniteFloatBehavior = dedent( 7500 """ 7501 args.rval().setUndefined(); 7502 return true; 7503 """) 7504 cgThings.append(CGGeneric(fill( 7505 """ 7506 if (foundNonFiniteFloat) { 7507 $*{returnSteps} 7508 } 7509 """, 7510 returnSteps=foundNonFiniteFloatBehavior))) 7511 7512 if needsUnwrap: 7513 # Something depends on having the unwrapped object, so unwrap it now. 7514 xraySteps = [] 7515 # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is 7516 # not null. 7517 xraySteps.append( 7518 CGGeneric(fill( 7519 """ 7520 ${obj} = js::CheckedUnwrap(${obj}); 7521 if (!${obj}) { 7522 return false; 7523 } 7524 """, 7525 obj=unwrappedVar))) 7526 if isConstructor: 7527 # If we're called via an xray, we need to enter the underlying 7528 # object's compartment and then wrap up all of our arguments into 7529 # that compartment as needed. This is all happening after we've 7530 # already done the conversions from JS values to WebIDL (C++) 7531 # values, so we only need to worry about cases where there are 'any' 7532 # or 'object' types, or other things that we represent as actual 7533 # JSAPI types, present. Effectively, we're emulating a 7534 # CrossCompartmentWrapper, but working with the C++ types, not the 7535 # original list of JS::Values. 7536 cgThings.append(CGGeneric("Maybe<JSAutoCompartment> ac;\n")) 7537 xraySteps.append(CGGeneric("ac.emplace(cx, obj);\n")) 7538 xraySteps.append(CGGeneric(dedent( 7539 """ 7540 if (!JS_WrapObject(cx, &desiredProto)) { 7541 return false; 7542 } 7543 """))) 7544 xraySteps.extend( 7545 wrapArgIntoCurrentCompartment(arg, argname, isMember=False) 7546 for arg, argname in self.getArguments()) 7547 7548 cgThings.append( 7549 CGIfWrapper(CGList(xraySteps), 7550 "objIsXray")) 7551 7552 # If this is a method that was generated by a maplike/setlike 7553 # interface, use the maplike/setlike generator to fill in the body. 7554 # Otherwise, use CGCallGenerator to call the native method. 7555 if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod(): 7556 if (idlNode.maplikeOrSetlikeOrIterable.isMaplike() or 7557 idlNode.maplikeOrSetlikeOrIterable.isSetlike()): 7558 cgThings.append(CGMaplikeOrSetlikeMethodGenerator(descriptor, 7559 idlNode.maplikeOrSetlikeOrIterable, 7560 idlNode.identifier.name)) 7561 else: 7562 cgThings.append(CGIterableMethodGenerator(descriptor, 7563 idlNode.maplikeOrSetlikeOrIterable, 7564 idlNode.identifier.name)) 7565 else: 7566 cgThings.append(CGCallGenerator( 7567 self.isFallible(), 7568 idlNode.getExtendedAttribute('NeedsSubjectPrincipal'), 7569 needsCallerType(idlNode), 7570 self.getArguments(), argsPre, returnType, 7571 self.extendedAttributes, descriptor, 7572 nativeMethodName, 7573 static, argsPost=argsPost, resultVar=resultVar)) 7574 7575 if useCounterName: 7576 # Generate a telemetry call for when [UseCounter] is used. 7577 code = "SetDocumentAndPageUseCounter(cx, obj, eUseCounter_%s);\n" % useCounterName 7578 cgThings.append(CGGeneric(code)) 7579 7580 self.cgRoot = CGList(cgThings) 7581 7582 def getArguments(self): 7583 return [(a, "arg" + str(i)) for i, a in enumerate(self.arguments)] 7584 7585 def isFallible(self): 7586 return 'infallible' not in self.extendedAttributes 7587 7588 def wrap_return_value(self): 7589 wrapCode = "" 7590 7591 returnsNewObject = memberReturnsNewObject(self.idlNode) 7592 if (returnsNewObject and 7593 self.returnType.isGeckoInterface()): 7594 wrapCode += dedent( 7595 """ 7596 static_assert(!IsPointer<decltype(result)>::value, 7597 "NewObject implies that we need to keep the object alive with a strong reference."); 7598 """) 7599 7600 setSlot = self.idlNode.isAttr() and self.idlNode.slotIndices is not None 7601 if setSlot: 7602 # For attributes in slots, we want to do some 7603 # post-processing once we've wrapped them. 7604 successCode = "break;\n" 7605 else: 7606 successCode = None 7607 7608 resultTemplateValues = { 7609 'jsvalRef': 'args.rval()', 7610 'jsvalHandle': 'args.rval()', 7611 'returnsNewObject': returnsNewObject, 7612 'isConstructorRetval': self.isConstructor, 7613 'successCode': successCode, 7614 # 'obj' in this dictionary is the thing whose compartment we are 7615 # trying to do the to-JS conversion in. We're going to put that 7616 # thing in a variable named "conversionScope" if setSlot is true. 7617 # Otherwise, just use "obj" for lack of anything better. 7618 'obj': "conversionScope" if setSlot else "obj" 7619 } 7620 try: 7621 wrapCode += wrapForType(self.returnType, self.descriptor, resultTemplateValues) 7622 except MethodNotNewObjectError, err: 7623 assert not returnsNewObject 7624 raise TypeError("%s being returned from non-NewObject method or property %s.%s" % 7625 (err.typename, 7626 self.descriptor.interface.identifier.name, 7627 self.idlNode.identifier.name)) 7628 if setSlot: 7629 # When using a slot on the Xray expando, we need to make sure that 7630 # our initial conversion to a JS::Value is done in the caller 7631 # compartment. When using a slot on our reflector, we want to do 7632 # the conversion in the compartment of that reflector (that is, 7633 # slotStorage). In both cases we want to make sure that we finally 7634 # set up args.rval() to be in the caller compartment. We also need 7635 # to make sure that the conversion steps happen inside a do/while 7636 # that they can break out of on success. 7637 # 7638 # Of course we always have to wrap the value into the slotStorage 7639 # compartment before we store it in slotStorage. 7640 7641 # postConversionSteps are the steps that run while we're still in 7642 # the compartment we do our conversion in but after we've finished 7643 # the initial conversion into args.rval(). 7644 postConversionSteps = "" 7645 if needsContainsHack(self.idlNode): 7646 # Define a .contains on the object that has the same value as 7647 # .includes; needed for backwards compat in extensions as we 7648 # migrate some DOMStringLists to FrozenArray. 7649 postConversionSteps += dedent( 7650 """ 7651 if (args.rval().isObject() && nsContentUtils::ThreadsafeIsCallerChrome()) { 7652 JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject()); 7653 JS::Rooted<JS::Value> includesVal(cx); 7654 if (!JS_GetProperty(cx, rvalObj, "includes", &includesVal) || 7655 !JS_DefineProperty(cx, rvalObj, "contains", includesVal, JSPROP_ENUMERATE)) { 7656 return false; 7657 } 7658 } 7659 7660 """) 7661 if self.idlNode.getExtendedAttribute("Frozen"): 7662 assert self.idlNode.type.isSequence() or self.idlNode.type.isDictionary() 7663 freezeValue = CGGeneric( 7664 "JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());\n" 7665 "if (!JS_FreezeObject(cx, rvalObj)) {\n" 7666 " return false;\n" 7667 "}\n") 7668 if self.idlNode.type.nullable(): 7669 freezeValue = CGIfWrapper(freezeValue, 7670 "args.rval().isObject()") 7671 postConversionSteps += freezeValue.define() 7672 7673 # slotStorageSteps are steps that run once we have entered the 7674 # slotStorage compartment. 7675 slotStorageSteps= fill( 7676 """ 7677 // Make a copy so that we don't do unnecessary wrapping on args.rval(). 7678 JS::Rooted<JS::Value> storedVal(cx, args.rval()); 7679 if (!${maybeWrap}(cx, &storedVal)) { 7680 return false; 7681 } 7682 js::SetReservedSlot(slotStorage, slotIndex, storedVal); 7683 """, 7684 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type)) 7685 7686 checkForXray = mayUseXrayExpandoSlots(self.descriptor, self.idlNode) 7687 7688 # For the case of Cached attributes, go ahead and preserve our 7689 # wrapper if needed. We need to do this because otherwise the 7690 # wrapper could get garbage-collected and the cached value would 7691 # suddenly disappear, but the whole premise of cached values is that 7692 # they never change without explicit action on someone's part. We 7693 # don't do this for StoreInSlot, since those get dealt with during 7694 # wrapper setup, and failure would involve us trying to clear an 7695 # already-preserved wrapper. 7696 if (self.idlNode.getExtendedAttribute("Cached") and 7697 self.descriptor.wrapperCache): 7698 preserveWrapper = dedent( 7699 """ 7700 PreserveWrapper(self); 7701 """) 7702 if checkForXray: 7703 preserveWrapper = fill( 7704 """ 7705 if (!isXray) { 7706 // In the Xray case we don't need to do this, because getting the 7707 // expando object already preserved our wrapper. 7708 $*{preserveWrapper} 7709 } 7710 """, 7711 preserveWrapper=preserveWrapper) 7712 slotStorageSteps += preserveWrapper 7713 7714 if checkForXray: 7715 conversionScope = "isXray ? obj : slotStorage" 7716 else: 7717 conversionScope = "slotStorage" 7718 7719 wrapCode = fill( 7720 """ 7721 { 7722 JS::Rooted<JSObject*> conversionScope(cx, ${conversionScope}); 7723 JSAutoCompartment ac(cx, conversionScope); 7724 do { // block we break out of when done wrapping 7725 $*{wrapCode} 7726 } while (0); 7727 $*{postConversionSteps} 7728 } 7729 { // And now store things in the compartment of our slotStorage. 7730 JSAutoCompartment ac(cx, slotStorage); 7731 $*{slotStorageSteps} 7732 } 7733 // And now make sure args.rval() is in the caller compartment 7734 return ${maybeWrap}(cx, args.rval()); 7735 """, 7736 conversionScope=conversionScope, 7737 wrapCode=wrapCode, 7738 postConversionSteps=postConversionSteps, 7739 slotStorageSteps=slotStorageSteps, 7740 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type)) 7741 return wrapCode 7742 7743 def define(self): 7744 return (self.cgRoot.define() + self.wrap_return_value()) 7745 7746 7747class CGSwitch(CGList): 7748 """ 7749 A class to generate code for a switch statement. 7750 7751 Takes three constructor arguments: an expression, a list of cases, 7752 and an optional default. 7753 7754 Each case is a CGCase. The default is a CGThing for the body of 7755 the default case, if any. 7756 """ 7757 def __init__(self, expression, cases, default=None): 7758 CGList.__init__(self, [CGIndenter(c) for c in cases]) 7759 self.prepend(CGGeneric("switch (" + expression + ") {\n")) 7760 if default is not None: 7761 self.append( 7762 CGIndenter( 7763 CGWrapper( 7764 CGIndenter(default), 7765 pre="default: {\n", 7766 post=" break;\n}\n"))) 7767 7768 self.append(CGGeneric("}\n")) 7769 7770 7771class CGCase(CGList): 7772 """ 7773 A class to generate code for a case statement. 7774 7775 Takes three constructor arguments: an expression, a CGThing for 7776 the body (allowed to be None if there is no body), and an optional 7777 argument (defaulting to False) for whether to fall through. 7778 """ 7779 def __init__(self, expression, body, fallThrough=False): 7780 CGList.__init__(self, []) 7781 self.append(CGGeneric("case " + expression + ": {\n")) 7782 bodyList = CGList([body]) 7783 if fallThrough: 7784 bodyList.append(CGGeneric("MOZ_FALLTHROUGH;\n")) 7785 else: 7786 bodyList.append(CGGeneric("break;\n")) 7787 self.append(CGIndenter(bodyList)) 7788 self.append(CGGeneric("}\n")) 7789 7790 7791class CGMethodCall(CGThing): 7792 """ 7793 A class to generate selection of a method signature from a set of 7794 signatures and generation of a call to that signature. 7795 """ 7796 def __init__(self, nativeMethodName, static, descriptor, method, 7797 isConstructor=False, constructorName=None): 7798 CGThing.__init__(self) 7799 7800 if isConstructor: 7801 assert constructorName is not None 7802 methodName = constructorName 7803 else: 7804 methodName = "%s.%s" % (descriptor.interface.identifier.name, method.identifier.name) 7805 argDesc = "argument %d of " + methodName 7806 7807 if method.getExtendedAttribute("UseCounter"): 7808 useCounterName = methodName.replace(".", "_") 7809 else: 7810 useCounterName = None 7811 7812 if method.isStatic(): 7813 nativeType = descriptor.nativeType 7814 staticTypeOverride = PropertyDefiner.getStringAttr(method, "StaticClassOverride") 7815 if (staticTypeOverride): 7816 nativeType = staticTypeOverride 7817 nativeMethodName = "%s::%s" % (nativeType, nativeMethodName) 7818 7819 def requiredArgCount(signature): 7820 arguments = signature[1] 7821 if len(arguments) == 0: 7822 return 0 7823 requiredArgs = len(arguments) 7824 while requiredArgs and arguments[requiredArgs-1].optional: 7825 requiredArgs -= 1 7826 return requiredArgs 7827 7828 def getPerSignatureCall(signature, argConversionStartsAt=0): 7829 return CGPerSignatureCall(signature[0], signature[1], 7830 nativeMethodName, static, descriptor, 7831 method, 7832 argConversionStartsAt=argConversionStartsAt, 7833 isConstructor=isConstructor, 7834 useCounterName=useCounterName) 7835 7836 signatures = method.signatures() 7837 if len(signatures) == 1: 7838 # Special case: we can just do a per-signature method call 7839 # here for our one signature and not worry about switching 7840 # on anything. 7841 signature = signatures[0] 7842 self.cgRoot = CGList([getPerSignatureCall(signature)]) 7843 requiredArgs = requiredArgCount(signature) 7844 7845 # Skip required arguments check for maplike/setlike interfaces, as 7846 # they can have arguments which are not passed, and are treated as 7847 # if undefined had been explicitly passed. 7848 if requiredArgs > 0 and not method.isMaplikeOrSetlikeOrIterableMethod(): 7849 code = fill( 7850 """ 7851 if (MOZ_UNLIKELY(args.length() < ${requiredArgs})) { 7852 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${methodName}"); 7853 } 7854 """, 7855 requiredArgs=requiredArgs, 7856 methodName=methodName) 7857 self.cgRoot.prepend(CGGeneric(code)) 7858 return 7859 7860 # Need to find the right overload 7861 maxArgCount = method.maxArgCount 7862 allowedArgCounts = method.allowedArgCounts 7863 7864 argCountCases = [] 7865 for argCountIdx, argCount in enumerate(allowedArgCounts): 7866 possibleSignatures = method.signaturesForArgCount(argCount) 7867 7868 # Try to optimize away cases when the next argCount in the list 7869 # will have the same code as us; if it does, we can fall through to 7870 # that case. 7871 if argCountIdx+1 < len(allowedArgCounts): 7872 nextPossibleSignatures = method.signaturesForArgCount(allowedArgCounts[argCountIdx+1]) 7873 else: 7874 nextPossibleSignatures = None 7875 if possibleSignatures == nextPossibleSignatures: 7876 # Same set of signatures means we better have the same 7877 # distinguishing index. So we can in fact just fall through to 7878 # the next case here. 7879 assert (len(possibleSignatures) == 1 or 7880 (method.distinguishingIndexForArgCount(argCount) == 7881 method.distinguishingIndexForArgCount(allowedArgCounts[argCountIdx+1]))) 7882 argCountCases.append(CGCase(str(argCount), None, True)) 7883 continue 7884 7885 if len(possibleSignatures) == 1: 7886 # easy case! 7887 signature = possibleSignatures[0] 7888 argCountCases.append( 7889 CGCase(str(argCount), getPerSignatureCall(signature))) 7890 continue 7891 7892 distinguishingIndex = method.distinguishingIndexForArgCount(argCount) 7893 7894 def distinguishingArgument(signature): 7895 args = signature[1] 7896 if distinguishingIndex < len(args): 7897 return args[distinguishingIndex] 7898 assert args[-1].variadic 7899 return args[-1] 7900 7901 def distinguishingType(signature): 7902 return distinguishingArgument(signature).type 7903 7904 for sig in possibleSignatures: 7905 # We should not have "any" args at distinguishingIndex, 7906 # since we have multiple possible signatures remaining, 7907 # but "any" is never distinguishable from anything else. 7908 assert not distinguishingType(sig).isAny() 7909 # We can't handle unions at the distinguishing index. 7910 if distinguishingType(sig).isUnion(): 7911 raise TypeError("No support for unions as distinguishing " 7912 "arguments yet: %s" % 7913 distinguishingArgument(sig).location) 7914 # We don't support variadics as the distinguishingArgument yet. 7915 # If you want to add support, consider this case: 7916 # 7917 # void(long... foo); 7918 # void(long bar, Int32Array baz); 7919 # 7920 # in which we have to convert argument 0 to long before picking 7921 # an overload... but all the variadic stuff needs to go into a 7922 # single array in case we pick that overload, so we have to have 7923 # machinery for converting argument 0 to long and then either 7924 # placing it in the variadic bit or not. Or something. We may 7925 # be able to loosen this restriction if the variadic arg is in 7926 # fact at distinguishingIndex, perhaps. Would need to 7927 # double-check. 7928 if distinguishingArgument(sig).variadic: 7929 raise TypeError("No support for variadics as distinguishing " 7930 "arguments yet: %s" % 7931 distinguishingArgument(sig).location) 7932 7933 # Convert all our arguments up to the distinguishing index. 7934 # Doesn't matter which of the possible signatures we use, since 7935 # they all have the same types up to that point; just use 7936 # possibleSignatures[0] 7937 caseBody = [CGArgumentConverter(possibleSignatures[0][1][i], 7938 i, descriptor, 7939 argDesc % (i + 1), method) 7940 for i in range(0, distinguishingIndex)] 7941 7942 # Select the right overload from our set. 7943 distinguishingArg = "args[%d]" % distinguishingIndex 7944 7945 def tryCall(signature, indent, isDefinitelyObject=False, 7946 isNullOrUndefined=False): 7947 assert not isDefinitelyObject or not isNullOrUndefined 7948 assert isDefinitelyObject or isNullOrUndefined 7949 if isDefinitelyObject: 7950 failureCode = "break;\n" 7951 else: 7952 failureCode = None 7953 type = distinguishingType(signature) 7954 # The argument at index distinguishingIndex can't possibly be 7955 # unset here, because we've already checked that argc is large 7956 # enough that we can examine this argument. But note that we 7957 # still want to claim that optional arguments are optional, in 7958 # case undefined was passed in. 7959 argIsOptional = distinguishingArgument(signature).canHaveMissingValue() 7960 testCode = instantiateJSToNativeConversion( 7961 getJSToNativeConversionInfo(type, descriptor, 7962 failureCode=failureCode, 7963 isDefinitelyObject=isDefinitelyObject, 7964 isNullOrUndefined=isNullOrUndefined, 7965 isOptional=argIsOptional, 7966 sourceDescription=(argDesc % (distinguishingIndex + 1))), 7967 { 7968 "declName": "arg%d" % distinguishingIndex, 7969 "holderName": ("arg%d" % distinguishingIndex) + "_holder", 7970 "val": distinguishingArg, 7971 "obj": "obj", 7972 "haveValue": "args.hasDefined(%d)" % distinguishingIndex, 7973 "passedToJSImpl": toStringBool(isJSImplementedDescriptor(descriptor)) 7974 }, 7975 checkForValue=argIsOptional) 7976 caseBody.append(CGIndenter(testCode, indent)) 7977 7978 # If we got this far, we know we unwrapped to the right 7979 # C++ type, so just do the call. Start conversion with 7980 # distinguishingIndex + 1, since we already converted 7981 # distinguishingIndex. 7982 caseBody.append(CGIndenter( 7983 getPerSignatureCall(signature, distinguishingIndex + 1), 7984 indent)) 7985 7986 def hasConditionalConversion(type): 7987 """ 7988 Return whether the argument conversion for this type will be 7989 conditional on the type of incoming JS value. For example, for 7990 interface types the conversion is conditional on the incoming 7991 value being isObject(). 7992 7993 For the types for which this returns false, we do not have to 7994 output extra isUndefined() or isNullOrUndefined() cases, because 7995 null/undefined values will just fall through into our 7996 unconditional conversion. 7997 """ 7998 if type.isString() or type.isEnum(): 7999 return False 8000 if type.isBoolean(): 8001 distinguishingTypes = (distinguishingType(s) for s in 8002 possibleSignatures) 8003 return any(t.isString() or t.isEnum() or t.isNumeric() 8004 for t in distinguishingTypes) 8005 if type.isNumeric(): 8006 distinguishingTypes = (distinguishingType(s) for s in 8007 possibleSignatures) 8008 return any(t.isString() or t.isEnum() 8009 for t in distinguishingTypes) 8010 return True 8011 8012 def needsNullOrUndefinedCase(type): 8013 """ 8014 Return true if the type needs a special isNullOrUndefined() case 8015 """ 8016 return ((type.nullable() and 8017 hasConditionalConversion(type)) or 8018 type.isDictionary()) 8019 8020 # First check for undefined and optional distinguishing arguments 8021 # and output a special branch for that case. Note that we don't 8022 # use distinguishingArgument here because we actualy want to 8023 # exclude variadic arguments. Also note that we skip this check if 8024 # we plan to output a isNullOrUndefined() special case for this 8025 # argument anyway, since that will subsume our isUndefined() check. 8026 # This is safe, because there can be at most one nullable 8027 # distinguishing argument, so if we're it we'll definitely get 8028 # picked up by the nullable handling. Also, we can skip this check 8029 # if the argument has an unconditional conversion later on. 8030 undefSigs = [s for s in possibleSignatures if 8031 distinguishingIndex < len(s[1]) and 8032 s[1][distinguishingIndex].optional and 8033 hasConditionalConversion(s[1][distinguishingIndex].type) and 8034 not needsNullOrUndefinedCase(s[1][distinguishingIndex].type)] 8035 # Can't have multiple signatures with an optional argument at the 8036 # same index. 8037 assert len(undefSigs) < 2 8038 if len(undefSigs) > 0: 8039 caseBody.append(CGGeneric("if (%s.isUndefined()) {\n" % 8040 distinguishingArg)) 8041 tryCall(undefSigs[0], 2, isNullOrUndefined=True) 8042 caseBody.append(CGGeneric("}\n")) 8043 8044 # Next, check for null or undefined. That means looking for 8045 # nullable arguments at the distinguishing index and outputting a 8046 # separate branch for them. But if the nullable argument has an 8047 # unconditional conversion, we don't need to do that. The reason 8048 # for that is that at most one argument at the distinguishing index 8049 # is nullable (since two nullable arguments are not 8050 # distinguishable), and null/undefined values will always fall 8051 # through to the unconditional conversion we have, if any, since 8052 # they will fail whatever the conditions on the input value are for 8053 # our other conversions. 8054 nullOrUndefSigs = [s for s in possibleSignatures 8055 if needsNullOrUndefinedCase(distinguishingType(s))] 8056 # Can't have multiple nullable types here 8057 assert len(nullOrUndefSigs) < 2 8058 if len(nullOrUndefSigs) > 0: 8059 caseBody.append(CGGeneric("if (%s.isNullOrUndefined()) {\n" % 8060 distinguishingArg)) 8061 tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True) 8062 caseBody.append(CGGeneric("}\n")) 8063 8064 # Now check for distinguishingArg being various kinds of objects. 8065 # The spec says to check for the following things in order: 8066 # 1) A platform object that's not a platform array object, being 8067 # passed to an interface or "object" arg. 8068 # 2) A Date object being passed to a Date or "object" arg. 8069 # 3) A RegExp object being passed to a RegExp or "object" arg. 8070 # 4) A callable object being passed to a callback or "object" arg. 8071 # 5) An iterable object being passed to a sequence arg. 8072 # 6) Any non-Date and non-RegExp object being passed to a 8073 # array or callback interface or dictionary or 8074 # "object" arg. 8075 8076 # First grab all the overloads that have a non-callback interface 8077 # (which includes typed arrays and arraybuffers) at the 8078 # distinguishing index. We can also include the ones that have an 8079 # "object" here, since if those are present no other object-typed 8080 # argument will be. 8081 objectSigs = [ 8082 s for s in possibleSignatures 8083 if (distinguishingType(s).isObject() or 8084 distinguishingType(s).isNonCallbackInterface())] 8085 8086 # And all the overloads that take Date 8087 objectSigs.extend(s for s in possibleSignatures 8088 if distinguishingType(s).isDate()) 8089 8090 # And all the overloads that take callbacks 8091 objectSigs.extend(s for s in possibleSignatures 8092 if distinguishingType(s).isCallback()) 8093 8094 # And all the overloads that take sequences 8095 objectSigs.extend(s for s in possibleSignatures 8096 if distinguishingType(s).isSequence()) 8097 8098 # Now append all the overloads that take a dictionary or callback 8099 # interface or MozMap. There should be only one of these! 8100 genericObjectSigs = [ 8101 s for s in possibleSignatures 8102 if (distinguishingType(s).isDictionary() or 8103 distinguishingType(s).isMozMap() or 8104 distinguishingType(s).isCallbackInterface())] 8105 assert len(genericObjectSigs) <= 1 8106 objectSigs.extend(genericObjectSigs) 8107 8108 # There might be more than one thing in objectSigs; we need to check 8109 # which ones we unwrap to. 8110 if len(objectSigs) > 0: 8111 # Here it's enough to guard on our argument being an object. The 8112 # code for unwrapping non-callback interfaces, typed arrays, 8113 # sequences, and Dates will just bail out and move on to 8114 # the next overload if the object fails to unwrap correctly, 8115 # while "object" accepts any object anyway. We could even not 8116 # do the isObject() check up front here, but in cases where we 8117 # have multiple object overloads it makes sense to do it only 8118 # once instead of for each overload. That will also allow the 8119 # unwrapping test to skip having to do codegen for the 8120 # null-or-undefined case, which we already handled above. 8121 caseBody.append(CGGeneric("if (%s.isObject()) {\n" % 8122 distinguishingArg)) 8123 for sig in objectSigs: 8124 caseBody.append(CGIndenter(CGGeneric("do {\n"))) 8125 # Indent by 4, since we need to indent further 8126 # than our "do" statement 8127 tryCall(sig, 4, isDefinitelyObject=True) 8128 caseBody.append(CGIndenter(CGGeneric("} while (0);\n"))) 8129 8130 caseBody.append(CGGeneric("}\n")) 8131 8132 # Now we only have to consider booleans, numerics, and strings. If 8133 # we only have one of them, then we can just output it. But if not, 8134 # then we need to output some of the cases conditionally: if we have 8135 # a string overload, then boolean and numeric are conditional, and 8136 # if not then boolean is conditional if we have a numeric overload. 8137 def findUniqueSignature(filterLambda): 8138 sigs = filter(filterLambda, possibleSignatures) 8139 assert len(sigs) < 2 8140 if len(sigs) > 0: 8141 return sigs[0] 8142 return None 8143 8144 stringSignature = findUniqueSignature( 8145 lambda s: (distinguishingType(s).isString() or 8146 distinguishingType(s).isEnum())) 8147 numericSignature = findUniqueSignature( 8148 lambda s: distinguishingType(s).isNumeric()) 8149 booleanSignature = findUniqueSignature( 8150 lambda s: distinguishingType(s).isBoolean()) 8151 8152 if stringSignature or numericSignature: 8153 booleanCondition = "%s.isBoolean()" 8154 else: 8155 booleanCondition = None 8156 8157 if stringSignature: 8158 numericCondition = "%s.isNumber()" 8159 else: 8160 numericCondition = None 8161 8162 def addCase(sig, condition): 8163 sigCode = getPerSignatureCall(sig, distinguishingIndex) 8164 if condition: 8165 sigCode = CGIfWrapper(sigCode, 8166 condition % distinguishingArg) 8167 caseBody.append(sigCode) 8168 8169 if booleanSignature: 8170 addCase(booleanSignature, booleanCondition) 8171 if numericSignature: 8172 addCase(numericSignature, numericCondition) 8173 if stringSignature: 8174 addCase(stringSignature, None) 8175 8176 if (not booleanSignature and not numericSignature and 8177 not stringSignature): 8178 # Just throw; we have no idea what we're supposed to 8179 # do with this. 8180 caseBody.append(CGGeneric( 8181 'return ThrowErrorMessage(cx, MSG_OVERLOAD_RESOLUTION_FAILED, "%d", "%d", "%s");\n' % 8182 (distinguishingIndex + 1, argCount, methodName))) 8183 8184 argCountCases.append(CGCase(str(argCount), CGList(caseBody))) 8185 8186 overloadCGThings = [] 8187 overloadCGThings.append( 8188 CGGeneric("unsigned argcount = std::min(args.length(), %du);\n" % 8189 maxArgCount)) 8190 overloadCGThings.append( 8191 CGSwitch("argcount", 8192 argCountCases, 8193 CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n' % 8194 methodName))) 8195 overloadCGThings.append( 8196 CGGeneric('MOZ_CRASH("We have an always-returning default case");\n' 8197 'return false;\n')) 8198 self.cgRoot = CGList(overloadCGThings) 8199 8200 def define(self): 8201 return self.cgRoot.define() 8202 8203 8204class CGGetterCall(CGPerSignatureCall): 8205 """ 8206 A class to generate a native object getter call for a particular IDL 8207 getter. 8208 """ 8209 def __init__(self, returnType, nativeMethodName, descriptor, attr): 8210 if attr.getExtendedAttribute("UseCounter"): 8211 useCounterName = "%s_%s_getter" % (descriptor.interface.identifier.name, 8212 attr.identifier.name) 8213 else: 8214 useCounterName = None 8215 if attr.isStatic(): 8216 nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName) 8217 CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName, 8218 attr.isStatic(), descriptor, attr, 8219 getter=True, useCounterName=useCounterName) 8220 8221 8222class CGNavigatorGetterCall(CGPerSignatureCall): 8223 """ 8224 A class to generate a native object getter call for an IDL getter for a 8225 property generated by NavigatorProperty. 8226 """ 8227 def __init__(self, returnType, _, descriptor, attr): 8228 nativeMethodName = "%s::ConstructNavigatorObject" % (toBindingNamespace(returnType.inner.identifier.name)) 8229 CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName, 8230 True, descriptor, attr, getter=True) 8231 8232 def getArguments(self): 8233 # The navigator object should be associated with the global of 8234 # the navigator it's coming from, which will be the global of 8235 # the object whose slot it gets cached in. That's stored in 8236 # "slotStorage". 8237 return [(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.object], 8238 self.idlNode), 8239 "slotStorage")] 8240 8241 8242class FakeIdentifier(): 8243 def __init__(self, name): 8244 self.name = name 8245 8246 8247class FakeArgument(): 8248 """ 8249 A class that quacks like an IDLArgument. This is used to make 8250 setters look like method calls or for special operations. 8251 """ 8252 def __init__(self, type, interfaceMember, name="arg", allowTreatNonCallableAsNull=False): 8253 self.type = type 8254 self.optional = False 8255 self.variadic = False 8256 self.defaultValue = None 8257 self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull 8258 # For FakeArguments generated by maplike/setlike convenience functions, 8259 # we won't have an interfaceMember to pass in. 8260 if interfaceMember: 8261 self.treatNullAs = interfaceMember.treatNullAs 8262 else: 8263 self.treatNullAs = "Default" 8264 if isinstance(interfaceMember, IDLAttribute): 8265 self.enforceRange = interfaceMember.enforceRange 8266 self.clamp = interfaceMember.clamp 8267 else: 8268 self.enforceRange = False 8269 self.clamp = False 8270 8271 self.identifier = FakeIdentifier(name) 8272 8273 def allowTreatNonCallableAsNull(self): 8274 return self._allowTreatNonCallableAsNull 8275 8276 def canHaveMissingValue(self): 8277 return False 8278 8279 8280class CGSetterCall(CGPerSignatureCall): 8281 """ 8282 A class to generate a native object setter call for a particular IDL 8283 setter. 8284 """ 8285 def __init__(self, argType, nativeMethodName, descriptor, attr): 8286 if attr.getExtendedAttribute("UseCounter"): 8287 useCounterName = "%s_%s_setter" % (descriptor.interface.identifier.name, 8288 attr.identifier.name) 8289 else: 8290 useCounterName = None 8291 if attr.isStatic(): 8292 nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName) 8293 CGPerSignatureCall.__init__(self, None, 8294 [FakeArgument(argType, attr, allowTreatNonCallableAsNull=True)], 8295 nativeMethodName, attr.isStatic(), 8296 descriptor, attr, setter=True, useCounterName=useCounterName) 8297 8298 def wrap_return_value(self): 8299 attr = self.idlNode 8300 if self.descriptor.wrapperCache and attr.slotIndices is not None: 8301 if attr.getExtendedAttribute("StoreInSlot"): 8302 args = "cx, self" 8303 else: 8304 args = "self" 8305 clearSlot = ("%s(%s);\n" % 8306 (MakeClearCachedValueNativeName(self.idlNode), args)) 8307 else: 8308 clearSlot = "" 8309 8310 # We have no return value 8311 return ("\n" 8312 "%s" 8313 "return true;\n" % clearSlot) 8314 8315 8316class CGAbstractBindingMethod(CGAbstractStaticMethod): 8317 """ 8318 Common class to generate the JSNatives for all our methods, getters, and 8319 setters. This will generate the function declaration and unwrap the 8320 |this| object. Subclasses are expected to override the generate_code 8321 function to do the rest of the work. This function should return a 8322 CGThing which is already properly indented. 8323 8324 getThisObj should be code for getting a JSObject* for the binding 8325 object. If this is None, we will auto-generate code based on 8326 descriptor to do the right thing. "" can be passed in if the 8327 binding object is already stored in 'obj'. 8328 8329 callArgs should be code for getting a JS::CallArgs into a variable 8330 called 'args'. This can be "" if there is already such a variable 8331 around. 8332 8333 If allowCrossOriginThis is true, then this-unwrapping will first do an 8334 UncheckedUnwrap and after that operate on the result. 8335 """ 8336 def __init__(self, descriptor, name, args, unwrapFailureCode=None, 8337 getThisObj=None, 8338 callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n", 8339 allowCrossOriginThis=False): 8340 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args) 8341 8342 if unwrapFailureCode is None: 8343 self.unwrapFailureCode = 'return ThrowErrorMessage(cx, MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, "Value", "%s");\n' % descriptor.interface.identifier.name 8344 else: 8345 self.unwrapFailureCode = unwrapFailureCode 8346 8347 if getThisObj == "": 8348 self.getThisObj = None 8349 else: 8350 if getThisObj is None: 8351 if descriptor.interface.isOnGlobalProtoChain(): 8352 ensureCondition = "!args.thisv().isNullOrUndefined() && !args.thisv().isObject()" 8353 getThisObj = "args.thisv().isObject() ? &args.thisv().toObject() : js::GetGlobalForObjectCrossCompartment(&args.callee())" 8354 else: 8355 ensureCondition = "!args.thisv().isObject()" 8356 getThisObj = "&args.thisv().toObject()" 8357 unwrapFailureCode = self.unwrapFailureCode % {'securityError': 'false'} 8358 ensureThisObj = CGIfWrapper(CGGeneric(unwrapFailureCode), 8359 ensureCondition) 8360 else: 8361 ensureThisObj = None 8362 self.getThisObj = CGList( 8363 [ensureThisObj, 8364 CGGeneric("JS::Rooted<JSObject*> obj(cx, %s);\n" % 8365 getThisObj)]) 8366 self.callArgs = callArgs 8367 self.allowCrossOriginThis = allowCrossOriginThis 8368 8369 def definition_body(self): 8370 body = self.callArgs 8371 if self.getThisObj is not None: 8372 body += self.getThisObj.define() + "\n" 8373 body += "%s* self;\n" % self.descriptor.nativeType 8374 body += dedent( 8375 """ 8376 JS::Rooted<JS::Value> rootSelf(cx, JS::ObjectValue(*obj)); 8377 """) 8378 8379 # Our descriptor might claim that we're not castable, simply because 8380 # we're someone's consequential interface. But for this-unwrapping, we 8381 # know that we're the real deal. So fake a descriptor here for 8382 # consumption by CastableObjectUnwrapper. 8383 body += str(CastableObjectUnwrapper( 8384 self.descriptor, 8385 "rootSelf", 8386 "&rootSelf", 8387 "self", 8388 self.unwrapFailureCode, 8389 allowCrossOriginObj=self.allowCrossOriginThis)) 8390 8391 return body + self.generate_code().define() 8392 8393 def generate_code(self): 8394 assert False # Override me 8395 8396 8397class CGAbstractStaticBindingMethod(CGAbstractStaticMethod): 8398 """ 8399 Common class to generate the JSNatives for all our static methods, getters 8400 and setters. This will generate the function declaration and unwrap the 8401 global object. Subclasses are expected to override the generate_code 8402 function to do the rest of the work. This function should return a 8403 CGThing which is already properly indented. 8404 """ 8405 def __init__(self, descriptor, name): 8406 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", 8407 JSNativeArguments()) 8408 8409 def definition_body(self): 8410 # Make sure that "obj" is in the same compartment as "cx", since we'll 8411 # later use it to wrap return values. 8412 unwrap = dedent(""" 8413 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 8414 JS::Rooted<JSObject*> obj(cx, &args.callee()); 8415 8416 """) 8417 return unwrap + self.generate_code().define() 8418 8419 def generate_code(self): 8420 assert False # Override me 8421 8422 8423def MakeNativeName(name): 8424 return name[0].upper() + IDLToCIdentifier(name[1:]) 8425 8426 8427class CGGenericMethod(CGAbstractBindingMethod): 8428 """ 8429 A class for generating the C++ code for an IDL method. 8430 8431 If allowCrossOriginThis is true, then this-unwrapping will first do an 8432 UncheckedUnwrap and after that operate on the result. 8433 """ 8434 def __init__(self, descriptor, allowCrossOriginThis=False): 8435 unwrapFailureCode = ( 8436 'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' % 8437 descriptor.interface.identifier.name) 8438 name = "genericCrossOriginMethod" if allowCrossOriginThis else "genericMethod" 8439 CGAbstractBindingMethod.__init__(self, descriptor, name, 8440 JSNativeArguments(), 8441 unwrapFailureCode=unwrapFailureCode, 8442 allowCrossOriginThis=allowCrossOriginThis) 8443 8444 def generate_code(self): 8445 return CGGeneric(dedent(""" 8446 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 8447 MOZ_ASSERT(info->type() == JSJitInfo::Method); 8448 JSJitMethodOp method = info->method; 8449 bool ok = method(cx, obj, self, JSJitMethodCallArgs(args)); 8450 #ifdef DEBUG 8451 if (ok) { 8452 AssertReturnTypeMatchesJitinfo(info, args.rval()); 8453 } 8454 #endif 8455 return ok; 8456 """)) 8457 8458 8459class CGGenericPromiseReturningMethod(CGAbstractBindingMethod): 8460 """ 8461 A class for generating the C++ code for an IDL method that returns a Promise. 8462 8463 Does not handle cross-origin this. 8464 """ 8465 def __init__(self, descriptor): 8466 unwrapFailureCode = dedent(""" 8467 ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n 8468 return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee), 8469 args.rval());\n""" % 8470 descriptor.interface.identifier.name) 8471 8472 name = "genericPromiseReturningMethod" 8473 customCallArgs = dedent(""" 8474 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 8475 // Make sure to save the callee before someone maybe messes with rval(). 8476 JS::Rooted<JSObject*> callee(cx, &args.callee()); 8477 """) 8478 8479 CGAbstractBindingMethod.__init__(self, descriptor, name, 8480 JSNativeArguments(), 8481 callArgs=customCallArgs, 8482 unwrapFailureCode=unwrapFailureCode) 8483 8484 def generate_code(self): 8485 return CGGeneric(dedent(""" 8486 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 8487 MOZ_ASSERT(info->type() == JSJitInfo::Method); 8488 JSJitMethodOp method = info->method; 8489 bool ok = method(cx, obj, self, JSJitMethodCallArgs(args)); 8490 if (ok) { 8491 #ifdef DEBUG 8492 AssertReturnTypeMatchesJitinfo(info, args.rval()); 8493 #endif 8494 return true; 8495 } 8496 8497 MOZ_ASSERT(info->returnType() == JSVAL_TYPE_OBJECT); 8498 return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee), 8499 args.rval()); 8500 """)) 8501 8502 8503class CGSpecializedMethod(CGAbstractStaticMethod): 8504 """ 8505 A class for generating the C++ code for a specialized method that the JIT 8506 can call with lower overhead. 8507 """ 8508 def __init__(self, descriptor, method): 8509 self.method = method 8510 name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name)) 8511 args = [Argument('JSContext*', 'cx'), 8512 Argument('JS::Handle<JSObject*>', 'obj'), 8513 Argument('%s*' % descriptor.nativeType, 'self'), 8514 Argument('const JSJitMethodCallArgs&', 'args')] 8515 CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args) 8516 8517 def definition_body(self): 8518 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, 8519 self.method) 8520 return CGMethodCall(nativeName, self.method.isStatic(), self.descriptor, 8521 self.method).define() 8522 8523 @staticmethod 8524 def makeNativeName(descriptor, method): 8525 name = method.identifier.name 8526 return MakeNativeName(descriptor.binaryNameFor(name)) 8527 8528 8529class CGMethodPromiseWrapper(CGAbstractStaticMethod): 8530 """ 8531 A class for generating a wrapper around another method that will 8532 convert exceptions to promises. 8533 """ 8534 def __init__(self, descriptor, methodToWrap): 8535 self.method = methodToWrap 8536 name = self.makeName(methodToWrap.name) 8537 args = list(methodToWrap.args) 8538 CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args) 8539 8540 def definition_body(self): 8541 return fill( 8542 """ 8543 // Make sure to save the callee before someone maybe messes 8544 // with rval(). 8545 JS::Rooted<JSObject*> callee(cx, &args.callee()); 8546 bool ok = ${methodName}(${args}); 8547 if (ok) { 8548 return true; 8549 } 8550 return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee), 8551 args.rval()); 8552 """, 8553 methodName=self.method.name, 8554 args=", ".join(arg.name for arg in self.args)) 8555 8556 @staticmethod 8557 def makeName(methodName): 8558 return methodName + "_promiseWrapper" 8559 8560 8561class CGJsonifierMethod(CGSpecializedMethod): 8562 def __init__(self, descriptor, method): 8563 assert method.isJsonifier() 8564 CGSpecializedMethod.__init__(self, descriptor, method) 8565 8566 def definition_body(self): 8567 ret = dedent(""" 8568 JS::Rooted<JSObject*> result(cx, JS_NewPlainObject(cx)); 8569 if (!result) { 8570 return false; 8571 } 8572 """) 8573 8574 jsonDescriptors = [self.descriptor] 8575 interface = self.descriptor.interface.parent 8576 while interface: 8577 descriptor = self.descriptor.getDescriptor(interface.identifier.name) 8578 if descriptor.operations['Jsonifier']: 8579 jsonDescriptors.append(descriptor) 8580 interface = interface.parent 8581 8582 # Iterate the array in reverse: oldest ancestor first 8583 for descriptor in jsonDescriptors[::-1]: 8584 ret += fill( 8585 """ 8586 if (!${parentclass}::JsonifyAttributes(cx, obj, self, result)) { 8587 return false; 8588 } 8589 """, 8590 parentclass=toBindingNamespace(descriptor.name) 8591 ) 8592 ret += ('args.rval().setObject(*result);\n' 8593 'return true;\n') 8594 return ret 8595 8596 8597class CGLegacyCallHook(CGAbstractBindingMethod): 8598 """ 8599 Call hook for our object 8600 """ 8601 def __init__(self, descriptor): 8602 self._legacycaller = descriptor.operations["LegacyCaller"] 8603 # Our "self" is actually the callee in this case, not the thisval. 8604 CGAbstractBindingMethod.__init__( 8605 self, descriptor, LEGACYCALLER_HOOK_NAME, 8606 JSNativeArguments(), getThisObj="&args.callee()") 8607 8608 def define(self): 8609 if not self._legacycaller: 8610 return "" 8611 return CGAbstractBindingMethod.define(self) 8612 8613 def generate_code(self): 8614 name = self._legacycaller.identifier.name 8615 nativeName = MakeNativeName(self.descriptor.binaryNameFor(name)) 8616 return CGMethodCall(nativeName, False, self.descriptor, 8617 self._legacycaller) 8618 8619 8620class CGResolveHook(CGAbstractClassHook): 8621 """ 8622 Resolve hook for objects that have the NeedResolve extended attribute. 8623 """ 8624 def __init__(self, descriptor): 8625 assert descriptor.interface.getExtendedAttribute("NeedResolve") 8626 8627 args = [Argument('JSContext*', 'cx'), 8628 Argument('JS::Handle<JSObject*>', 'obj'), 8629 Argument('JS::Handle<jsid>', 'id'), 8630 Argument('bool*', 'resolvedp')] 8631 CGAbstractClassHook.__init__(self, descriptor, RESOLVE_HOOK_NAME, 8632 "bool", args) 8633 8634 def generate_code(self): 8635 return dedent(""" 8636 JS::Rooted<JS::PropertyDescriptor> desc(cx); 8637 if (!self->DoResolve(cx, obj, id, &desc)) { 8638 return false; 8639 } 8640 if (!desc.object()) { 8641 return true; 8642 } 8643 // If desc.value() is undefined, then the DoResolve call 8644 // has already defined it on the object. Don't try to also 8645 // define it. 8646 if (!desc.value().isUndefined()) { 8647 desc.attributesRef() |= JSPROP_RESOLVING; 8648 if (!JS_DefinePropertyById(cx, obj, id, desc)) { 8649 return false; 8650 } 8651 } 8652 *resolvedp = true; 8653 return true; 8654 """) 8655 8656 def definition_body(self): 8657 if self.descriptor.isGlobal(): 8658 # Resolve standard classes 8659 prefix = dedent(""" 8660 if (!ResolveGlobal(cx, obj, id, resolvedp)) { 8661 return false; 8662 } 8663 if (*resolvedp) { 8664 return true; 8665 } 8666 8667 """) 8668 else: 8669 prefix = "" 8670 return prefix + CGAbstractClassHook.definition_body(self) 8671 8672 8673class CGMayResolveHook(CGAbstractStaticMethod): 8674 """ 8675 Resolve hook for objects that have the NeedResolve extended attribute. 8676 """ 8677 def __init__(self, descriptor): 8678 assert descriptor.interface.getExtendedAttribute("NeedResolve") 8679 8680 args = [Argument('const JSAtomState&', 'names'), 8681 Argument('jsid', 'id'), 8682 Argument('JSObject*', 'maybeObj')] 8683 CGAbstractStaticMethod.__init__(self, descriptor, MAY_RESOLVE_HOOK_NAME, 8684 "bool", args) 8685 8686 def definition_body(self): 8687 if self.descriptor.isGlobal(): 8688 # Check whether this would resolve as a standard class. 8689 prefix = dedent(""" 8690 if (MayResolveGlobal(names, id, maybeObj)) { 8691 return true; 8692 } 8693 8694 """) 8695 else: 8696 prefix = "" 8697 return (prefix + 8698 "return %s::MayResolve(id);\n" % self.descriptor.nativeType) 8699 8700 8701class CGEnumerateHook(CGAbstractBindingMethod): 8702 """ 8703 Enumerate hook for objects with custom hooks. 8704 """ 8705 def __init__(self, descriptor): 8706 assert descriptor.interface.getExtendedAttribute("NeedResolve") 8707 8708 args = [Argument('JSContext*', 'cx'), 8709 Argument('JS::Handle<JSObject*>', 'obj')] 8710 # Our "self" is actually the "obj" argument in this case, not the thisval. 8711 CGAbstractBindingMethod.__init__( 8712 self, descriptor, ENUMERATE_HOOK_NAME, 8713 args, getThisObj="", callArgs="") 8714 8715 def generate_code(self): 8716 return CGGeneric(dedent(""" 8717 AutoTArray<nsString, 8> names; 8718 binding_detail::FastErrorResult rv; 8719 self->GetOwnPropertyNames(cx, names, rv); 8720 if (rv.MaybeSetPendingException(cx)) { 8721 return false; 8722 } 8723 bool dummy; 8724 for (uint32_t i = 0; i < names.Length(); ++i) { 8725 if (!JS_HasUCProperty(cx, obj, names[i].get(), names[i].Length(), &dummy)) { 8726 return false; 8727 } 8728 } 8729 return true; 8730 """)) 8731 8732 def definition_body(self): 8733 if self.descriptor.isGlobal(): 8734 # Enumerate standard classes 8735 prefix = dedent(""" 8736 if (!EnumerateGlobal(cx, obj)) { 8737 return false; 8738 } 8739 8740 """) 8741 else: 8742 prefix = "" 8743 return prefix + CGAbstractBindingMethod.definition_body(self) 8744 8745 8746class CppKeywords(): 8747 """ 8748 A class for checking if method names declared in webidl 8749 are not in conflict with C++ keywords. 8750 """ 8751 keywords = frozenset([ 8752 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'assert', 'auto', 'bitand', 'bitor', 'bool', 8753 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', 'compl', 'const', 8754 'constexpr', 'const_cast', 'continue', 'decltype', 'default', 'delete', 'do', 'double', 8755 'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'final', 'float', 8756 'for', 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new', 8757 'noexcept', 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq', 'override', 'private', 8758 'protected', 'public', 'register', 'reinterpret_cast', 'return', 'short', 'signed', 8759 'sizeof', 'static', 'static_assert', 'static_cast', 'struct', 'switch', 'template', 'this', 8760 'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union', 8761 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq']) 8762 8763 @staticmethod 8764 def checkMethodName(name): 8765 # Double '_' because 'assert' and '_assert' cannot be used in MS2013 compiler. 8766 # Bug 964892 and bug 963560. 8767 if name in CppKeywords.keywords: 8768 name = '_' + name + '_' 8769 return name 8770 8771 8772class CGStaticMethod(CGAbstractStaticBindingMethod): 8773 """ 8774 A class for generating the C++ code for an IDL static method. 8775 """ 8776 def __init__(self, descriptor, method): 8777 self.method = method 8778 name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name)) 8779 CGAbstractStaticBindingMethod.__init__(self, descriptor, name) 8780 8781 def generate_code(self): 8782 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, 8783 self.method) 8784 return CGMethodCall(nativeName, True, self.descriptor, self.method) 8785 8786 8787class CGGenericGetter(CGAbstractBindingMethod): 8788 """ 8789 A class for generating the C++ code for an IDL attribute getter. 8790 """ 8791 def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False): 8792 if lenientThis: 8793 name = "genericLenientGetter" 8794 unwrapFailureCode = dedent(""" 8795 MOZ_ASSERT(!JS_IsExceptionPending(cx)); 8796 if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) { 8797 return false; 8798 } 8799 args.rval().set(JS::UndefinedValue()); 8800 return true; 8801 """) 8802 else: 8803 if allowCrossOriginThis: 8804 name = "genericCrossOriginGetter" 8805 else: 8806 name = "genericGetter" 8807 unwrapFailureCode = ( 8808 'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' % 8809 descriptor.interface.identifier.name) 8810 CGAbstractBindingMethod.__init__(self, descriptor, name, JSNativeArguments(), 8811 unwrapFailureCode, 8812 allowCrossOriginThis=allowCrossOriginThis) 8813 8814 def generate_code(self): 8815 return CGGeneric(dedent(""" 8816 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 8817 MOZ_ASSERT(info->type() == JSJitInfo::Getter); 8818 JSJitGetterOp getter = info->getter; 8819 bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args)); 8820 #ifdef DEBUG 8821 if (ok) { 8822 AssertReturnTypeMatchesJitinfo(info, args.rval()); 8823 } 8824 #endif 8825 return ok; 8826 """)) 8827 8828 8829class CGSpecializedGetter(CGAbstractStaticMethod): 8830 """ 8831 A class for generating the code for a specialized attribute getter 8832 that the JIT can call with lower overhead. 8833 """ 8834 def __init__(self, descriptor, attr): 8835 self.attr = attr 8836 name = 'get_' + IDLToCIdentifier(attr.identifier.name) 8837 args = [ 8838 Argument('JSContext*', 'cx'), 8839 Argument('JS::Handle<JSObject*>', 'obj'), 8840 Argument('%s*' % descriptor.nativeType, 'self'), 8841 Argument('JSJitGetterCallArgs', 'args') 8842 ] 8843 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args) 8844 8845 def definition_body(self): 8846 if self.attr.isMaplikeOrSetlikeAttr(): 8847 # If the interface is maplike/setlike, there will be one getter 8848 # method for the size property of the backing object. Due to having 8849 # to unpack the backing object from the slot, this requires its own 8850 # generator. 8851 return getMaplikeOrSetlikeSizeGetterBody(self.descriptor, self.attr) 8852 nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, 8853 self.attr) 8854 if self.attr.slotIndices is not None: 8855 if self.descriptor.hasXPConnectImpls: 8856 raise TypeError("Interface '%s' has XPConnect impls, so we " 8857 "can't use our slot for property '%s'!" % 8858 (self.descriptor.interface.identifier.name, 8859 self.attr.identifier.name)) 8860 8861 # We're going to store this return value in a slot on some object, 8862 # to cache it. The question is, which object? For dictionary and 8863 # sequence return values, we want to use a slot on the Xray expando 8864 # if we're called via Xrays, and a slot on our reflector otherwise. 8865 # On the other hand, when dealing with some interfacce types 8866 # (navigator properties, window.document) we want to avoid calling 8867 # the getter more than once. In the case of navigator properties 8868 # that's because the getter actually creates a new object each time. 8869 # In the case of window.document, it's because the getter can start 8870 # returning null, which would get hidden in he non-Xray case by the 8871 # fact that it's [StoreOnSlot], so the cached version is always 8872 # around. 8873 # 8874 # The upshot is that we use the reflector slot for any getter whose 8875 # type is a gecko interface, whether we're called via Xrays or not. 8876 # Since [Cached] and [StoreInSlot] cannot be used with "NewObject", 8877 # we know that in the interface type case the returned object is 8878 # wrappercached. So creating Xrays to it is reasonable. 8879 if mayUseXrayExpandoSlots(self.descriptor, self.attr): 8880 prefix = fill( 8881 """ 8882 // Have to either root across the getter call or reget after. 8883 bool isXray; 8884 JS::Rooted<JSObject*> slotStorage(cx, GetCachedSlotStorageObject(cx, obj, &isXray)); 8885 if (!slotStorage) { 8886 return false; 8887 } 8888 const size_t slotIndex = isXray ? ${xraySlotIndex} : ${slotIndex}; 8889 """, 8890 xraySlotIndex=memberXrayExpandoReservedSlot(self.attr, 8891 self.descriptor), 8892 slotIndex=memberReservedSlot(self.attr, self.descriptor)) 8893 else: 8894 prefix = fill( 8895 """ 8896 // Have to either root across the getter call or reget after. 8897 JS::Rooted<JSObject*> slotStorage(cx, js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false)); 8898 MOZ_ASSERT(IsDOMObject(slotStorage)); 8899 const size_t slotIndex = ${slotIndex}; 8900 """, 8901 slotIndex=memberReservedSlot(self.attr, self.descriptor)) 8902 8903 prefix += fill( 8904 """ 8905 MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(js::GetObjectClass(slotStorage)) > slotIndex); 8906 { 8907 // Scope for cachedVal 8908 JS::Value cachedVal = js::GetReservedSlot(slotStorage, slotIndex); 8909 if (!cachedVal.isUndefined()) { 8910 args.rval().set(cachedVal); 8911 // The cached value is in the compartment of slotStorage, 8912 // so wrap into the caller compartment as needed. 8913 return ${maybeWrap}(cx, args.rval()); 8914 } 8915 } 8916 8917 """, 8918 maybeWrap=getMaybeWrapValueFuncForType(self.attr.type)) 8919 else: 8920 prefix = "" 8921 8922 if self.attr.navigatorObjectGetter: 8923 cgGetterCall = CGNavigatorGetterCall 8924 else: 8925 cgGetterCall = CGGetterCall 8926 return (prefix + 8927 cgGetterCall(self.attr.type, nativeName, 8928 self.descriptor, self.attr).define()) 8929 8930 @staticmethod 8931 def makeNativeName(descriptor, attr): 8932 name = attr.identifier.name 8933 nativeName = MakeNativeName(descriptor.binaryNameFor(name)) 8934 _, resultOutParam, _, _, _ = getRetvalDeclarationForType(attr.type, 8935 descriptor) 8936 infallible = ('infallible' in 8937 descriptor.getExtendedAttributes(attr, getter=True)) 8938 if resultOutParam or attr.type.nullable() or not infallible: 8939 nativeName = "Get" + nativeName 8940 return nativeName 8941 8942 8943class CGStaticGetter(CGAbstractStaticBindingMethod): 8944 """ 8945 A class for generating the C++ code for an IDL static attribute getter. 8946 """ 8947 def __init__(self, descriptor, attr): 8948 self.attr = attr 8949 name = 'get_' + IDLToCIdentifier(attr.identifier.name) 8950 CGAbstractStaticBindingMethod.__init__(self, descriptor, name) 8951 8952 def generate_code(self): 8953 nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, 8954 self.attr) 8955 return CGGetterCall(self.attr.type, nativeName, self.descriptor, 8956 self.attr) 8957 8958 8959class CGGenericSetter(CGAbstractBindingMethod): 8960 """ 8961 A class for generating the C++ code for an IDL attribute setter. 8962 """ 8963 def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False): 8964 if lenientThis: 8965 name = "genericLenientSetter" 8966 unwrapFailureCode = dedent(""" 8967 MOZ_ASSERT(!JS_IsExceptionPending(cx)); 8968 if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) { 8969 return false; 8970 } 8971 args.rval().set(JS::UndefinedValue()); 8972 return true; 8973 """) 8974 else: 8975 if allowCrossOriginThis: 8976 name = "genericCrossOriginSetter" 8977 else: 8978 name = "genericSetter" 8979 unwrapFailureCode = ( 8980 'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' % 8981 descriptor.interface.identifier.name) 8982 8983 CGAbstractBindingMethod.__init__(self, descriptor, name, JSNativeArguments(), 8984 unwrapFailureCode, 8985 allowCrossOriginThis=allowCrossOriginThis) 8986 8987 def generate_code(self): 8988 return CGGeneric(fill( 8989 """ 8990 if (args.length() == 0) { 8991 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} attribute setter"); 8992 } 8993 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 8994 MOZ_ASSERT(info->type() == JSJitInfo::Setter); 8995 JSJitSetterOp setter = info->setter; 8996 if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) { 8997 return false; 8998 } 8999 args.rval().setUndefined(); 9000 #ifdef DEBUG 9001 AssertReturnTypeMatchesJitinfo(info, args.rval()); 9002 #endif 9003 return true; 9004 """, 9005 name=self.descriptor.interface.identifier.name)) 9006 9007 9008class CGSpecializedSetter(CGAbstractStaticMethod): 9009 """ 9010 A class for generating the code for a specialized attribute setter 9011 that the JIT can call with lower overhead. 9012 """ 9013 def __init__(self, descriptor, attr): 9014 self.attr = attr 9015 name = 'set_' + IDLToCIdentifier(attr.identifier.name) 9016 args = [Argument('JSContext*', 'cx'), 9017 Argument('JS::Handle<JSObject*>', 'obj'), 9018 Argument('%s*' % descriptor.nativeType, 'self'), 9019 Argument('JSJitSetterCallArgs', 'args')] 9020 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args) 9021 9022 def definition_body(self): 9023 nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, 9024 self.attr) 9025 return CGSetterCall(self.attr.type, nativeName, self.descriptor, 9026 self.attr).define() 9027 9028 @staticmethod 9029 def makeNativeName(descriptor, attr): 9030 name = attr.identifier.name 9031 return "Set" + MakeNativeName(descriptor.binaryNameFor(name)) 9032 9033 9034class CGStaticSetter(CGAbstractStaticBindingMethod): 9035 """ 9036 A class for generating the C++ code for an IDL static attribute setter. 9037 """ 9038 def __init__(self, descriptor, attr): 9039 self.attr = attr 9040 name = 'set_' + IDLToCIdentifier(attr.identifier.name) 9041 CGAbstractStaticBindingMethod.__init__(self, descriptor, name) 9042 9043 def generate_code(self): 9044 nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, 9045 self.attr) 9046 checkForArg = CGGeneric(fill( 9047 """ 9048 if (args.length() == 0) { 9049 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} setter"); 9050 } 9051 """, 9052 name=self.attr.identifier.name)) 9053 call = CGSetterCall(self.attr.type, nativeName, self.descriptor, 9054 self.attr) 9055 return CGList([checkForArg, call]) 9056 9057 9058class CGSpecializedForwardingSetter(CGSpecializedSetter): 9059 """ 9060 A class for generating the code for a specialized attribute setter with 9061 PutForwards that the JIT can call with lower overhead. 9062 """ 9063 def __init__(self, descriptor, attr): 9064 CGSpecializedSetter.__init__(self, descriptor, attr) 9065 9066 def definition_body(self): 9067 attrName = self.attr.identifier.name 9068 forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0] 9069 # JS_GetProperty and JS_SetProperty can only deal with ASCII 9070 assert all(ord(c) < 128 for c in attrName) 9071 assert all(ord(c) < 128 for c in forwardToAttrName) 9072 return fill( 9073 """ 9074 JS::Rooted<JS::Value> v(cx); 9075 if (!JS_GetProperty(cx, obj, "${attr}", &v)) { 9076 return false; 9077 } 9078 9079 if (!v.isObject()) { 9080 return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "${interface}.${attr}"); 9081 } 9082 9083 JS::Rooted<JSObject*> targetObj(cx, &v.toObject()); 9084 return JS_SetProperty(cx, targetObj, "${forwardToAttrName}", args[0]); 9085 """, 9086 attr=attrName, 9087 interface=self.descriptor.interface.identifier.name, 9088 forwardToAttrName=forwardToAttrName) 9089 9090 9091class CGSpecializedReplaceableSetter(CGSpecializedSetter): 9092 """ 9093 A class for generating the code for a specialized attribute setter with 9094 Replaceable that the JIT can call with lower overhead. 9095 """ 9096 def __init__(self, descriptor, attr): 9097 CGSpecializedSetter.__init__(self, descriptor, attr) 9098 9099 def definition_body(self): 9100 attrName = self.attr.identifier.name 9101 # JS_DefineProperty can only deal with ASCII 9102 assert all(ord(c) < 128 for c in attrName) 9103 return ('return JS_DefineProperty(cx, obj, "%s", args[0], JSPROP_ENUMERATE);\n' % 9104 attrName) 9105 9106 9107class CGSpecializedLenientSetter(CGSpecializedSetter): 9108 """ 9109 A class for generating the code for a specialized attribute setter with 9110 LenientSetter that the JIT can call with lower overhead. 9111 """ 9112 def __init__(self, descriptor, attr): 9113 CGSpecializedSetter.__init__(self, descriptor, attr) 9114 9115 def definition_body(self): 9116 attrName = self.attr.identifier.name 9117 # JS_DefineProperty can only deal with ASCII 9118 assert all(ord(c) < 128 for c in attrName) 9119 return dedent(""" 9120 DeprecationWarning(cx, obj, nsIDocument::eLenientSetter); 9121 return true; 9122 """) 9123 9124 9125def memberReturnsNewObject(member): 9126 return member.getExtendedAttribute("NewObject") is not None 9127 9128 9129class CGMemberJITInfo(CGThing): 9130 """ 9131 A class for generating the JITInfo for a property that points to 9132 our specialized getter and setter. 9133 """ 9134 def __init__(self, descriptor, member): 9135 self.member = member 9136 self.descriptor = descriptor 9137 9138 def declare(self): 9139 return "" 9140 9141 def defineJitInfo(self, infoName, opName, opType, infallible, movable, 9142 eliminatable, aliasSet, alwaysInSlot, lazilyInSlot, 9143 slotIndex, returnTypes, args): 9144 """ 9145 aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit. 9146 9147 args is None if we don't want to output argTypes for some 9148 reason (e.g. we have overloads or we're not a method) and 9149 otherwise an iterable of the arguments for this method. 9150 """ 9151 assert(not movable or aliasSet != "AliasEverything") # Can't move write-aliasing things 9152 assert(not alwaysInSlot or movable) # Things always in slots had better be movable 9153 assert(not eliminatable or aliasSet != "AliasEverything") # Can't eliminate write-aliasing things 9154 assert(not alwaysInSlot or eliminatable) # Things always in slots had better be eliminatable 9155 9156 def jitInfoInitializer(isTypedMethod): 9157 initializer = fill( 9158 """ 9159 { 9160 { ${opName} }, 9161 { prototypes::id::${name} }, 9162 { PrototypeTraits<prototypes::id::${name}>::Depth }, 9163 JSJitInfo::${opType}, 9164 JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */ 9165 ${returnType}, /* returnType. Not relevant for setters. */ 9166 ${isInfallible}, /* isInfallible. False in setters. */ 9167 ${isMovable}, /* isMovable. Not relevant for setters. */ 9168 ${isEliminatable}, /* isEliminatable. Not relevant for setters. */ 9169 ${isAlwaysInSlot}, /* isAlwaysInSlot. Only relevant for getters. */ 9170 ${isLazilyCachedInSlot}, /* isLazilyCachedInSlot. Only relevant for getters. */ 9171 ${isTypedMethod}, /* isTypedMethod. Only relevant for methods. */ 9172 ${slotIndex} /* Reserved slot index, if we're stored in a slot, else 0. */ 9173 } 9174 """, 9175 opName=opName, 9176 name=self.descriptor.name, 9177 opType=opType, 9178 aliasSet=aliasSet, 9179 returnType=reduce(CGMemberJITInfo.getSingleReturnType, returnTypes, 9180 ""), 9181 isInfallible=toStringBool(infallible), 9182 isMovable=toStringBool(movable), 9183 isEliminatable=toStringBool(eliminatable), 9184 isAlwaysInSlot=toStringBool(alwaysInSlot), 9185 isLazilyCachedInSlot=toStringBool(lazilyInSlot), 9186 isTypedMethod=toStringBool(isTypedMethod), 9187 slotIndex=slotIndex) 9188 return initializer.rstrip() 9189 9190 slotAssert = fill( 9191 """ 9192 static_assert(${slotIndex} <= JSJitInfo::maxSlotIndex, "We won't fit"); 9193 static_assert(${slotIndex} < ${classReservedSlots}, "There is no slot for us"); 9194 """, 9195 slotIndex=slotIndex, 9196 classReservedSlots=INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots) 9197 if args is not None: 9198 argTypes = "%s_argTypes" % infoName 9199 args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args] 9200 args.append("JSJitInfo::ArgTypeListEnd") 9201 argTypesDecl = ( 9202 "static const JSJitInfo::ArgType %s[] = { %s };\n" % 9203 (argTypes, ", ".join(args))) 9204 return fill( 9205 """ 9206 $*{argTypesDecl} 9207 static const JSTypedMethodJitInfo ${infoName} = { 9208 ${jitInfo}, 9209 ${argTypes} 9210 }; 9211 $*{slotAssert} 9212 """, 9213 argTypesDecl=argTypesDecl, 9214 infoName=infoName, 9215 jitInfo=indent(jitInfoInitializer(True)), 9216 argTypes=argTypes, 9217 slotAssert=slotAssert) 9218 9219 return fill( 9220 """ 9221 static const JSJitInfo ${infoName} = ${jitInfo}; 9222 $*{slotAssert} 9223 """, 9224 infoName=infoName, 9225 jitInfo=jitInfoInitializer(False), 9226 slotAssert=slotAssert) 9227 9228 def define(self): 9229 if self.member.isAttr(): 9230 getterinfo = ("%s_getterinfo" % 9231 IDLToCIdentifier(self.member.identifier.name)) 9232 # We need the cast here because JSJitGetterOp has a "void* self" 9233 # while we have the right type. 9234 getter = ("(JSJitGetterOp)get_%s" % 9235 IDLToCIdentifier(self.member.identifier.name)) 9236 getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True) 9237 9238 movable = self.mayBeMovable() and getterinfal 9239 eliminatable = self.mayBeEliminatable() and getterinfal 9240 aliasSet = self.aliasSet() 9241 9242 getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor) 9243 isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot") 9244 if self.member.slotIndices is not None: 9245 assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached") 9246 isLazilyCachedInSlot = not isAlwaysInSlot 9247 slotIndex = memberReservedSlot(self.member, self.descriptor) 9248 # We'll statically assert that this is not too big in 9249 # CGUpdateMemberSlotsMethod, in the case when 9250 # isAlwaysInSlot is true. 9251 else: 9252 isLazilyCachedInSlot = False 9253 slotIndex = "0" 9254 9255 result = self.defineJitInfo(getterinfo, getter, "Getter", 9256 getterinfal, movable, eliminatable, 9257 aliasSet, isAlwaysInSlot, 9258 isLazilyCachedInSlot, slotIndex, 9259 [self.member.type], None) 9260 if (not self.member.readonly or 9261 self.member.getExtendedAttribute("PutForwards") is not None or 9262 self.member.getExtendedAttribute("Replaceable") is not None or 9263 self.member.getExtendedAttribute("LenientSetter") is not None): 9264 setterinfo = ("%s_setterinfo" % 9265 IDLToCIdentifier(self.member.identifier.name)) 9266 # Actually a JSJitSetterOp, but JSJitGetterOp is first in the 9267 # union. 9268 setter = ("(JSJitGetterOp)set_%s" % 9269 IDLToCIdentifier(self.member.identifier.name)) 9270 # Setters are always fallible, since they have to do a typed unwrap. 9271 result += self.defineJitInfo(setterinfo, setter, "Setter", 9272 False, False, False, "AliasEverything", 9273 False, False, "0", 9274 [BuiltinTypes[IDLBuiltinType.Types.void]], 9275 None) 9276 return result 9277 if self.member.isMethod(): 9278 methodinfo = ("%s_methodinfo" % 9279 IDLToCIdentifier(self.member.identifier.name)) 9280 name = CppKeywords.checkMethodName( 9281 IDLToCIdentifier(self.member.identifier.name)) 9282 if self.member.returnsPromise(): 9283 name = CGMethodPromiseWrapper.makeName(name) 9284 # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union. 9285 method = ("(JSJitGetterOp)%s" % name) 9286 9287 # Methods are infallible if they are infallible, have no arguments 9288 # to unwrap, and have a return type that's infallible to wrap up for 9289 # return. 9290 sigs = self.member.signatures() 9291 if len(sigs) != 1: 9292 # Don't handle overloading. If there's more than one signature, 9293 # one of them must take arguments. 9294 methodInfal = False 9295 args = None 9296 movable = False 9297 eliminatable = False 9298 else: 9299 sig = sigs[0] 9300 # For methods that affect nothing, it's OK to set movable to our 9301 # notion of infallible on the C++ side, without considering 9302 # argument conversions, since argument conversions that can 9303 # reliably throw would be effectful anyway and the jit doesn't 9304 # move effectful things. 9305 hasInfallibleImpl = "infallible" in self.descriptor.getExtendedAttributes(self.member) 9306 movable = self.mayBeMovable() and hasInfallibleImpl 9307 eliminatable = self.mayBeEliminatable() and hasInfallibleImpl 9308 # XXXbz can we move the smarts about fallibility due to arg 9309 # conversions into the JIT, using our new args stuff? 9310 if (len(sig[1]) != 0 or 9311 not infallibleForMember(self.member, sig[0], self.descriptor)): 9312 # We have arguments or our return-value boxing can fail 9313 methodInfal = False 9314 else: 9315 methodInfal = hasInfallibleImpl 9316 # For now, only bother to output args if we're side-effect-free. 9317 if self.member.affects == "Nothing": 9318 args = sig[1] 9319 else: 9320 args = None 9321 9322 aliasSet = self.aliasSet() 9323 result = self.defineJitInfo(methodinfo, method, "Method", 9324 methodInfal, movable, eliminatable, 9325 aliasSet, False, False, "0", 9326 [s[0] for s in sigs], args) 9327 return result 9328 raise TypeError("Illegal member type to CGPropertyJITInfo") 9329 9330 def mayBeMovable(self): 9331 """ 9332 Returns whether this attribute or method may be movable, just 9333 based on Affects/DependsOn annotations. 9334 """ 9335 affects = self.member.affects 9336 dependsOn = self.member.dependsOn 9337 assert affects in IDLInterfaceMember.AffectsValues 9338 assert dependsOn in IDLInterfaceMember.DependsOnValues 9339 # Things that are DependsOn=DeviceState are not movable, because we 9340 # don't want them coalesced with each other or loop-hoisted, since 9341 # their return value can change even if nothing is going on from our 9342 # point of view. 9343 return (affects == "Nothing" and 9344 (dependsOn != "Everything" and dependsOn != "DeviceState")) 9345 9346 def mayBeEliminatable(self): 9347 """ 9348 Returns whether this attribute or method may be eliminatable, just 9349 based on Affects/DependsOn annotations. 9350 """ 9351 # dependsOn shouldn't affect this decision at all, except in jitinfo we 9352 # have no way to express "Depends on everything, affects nothing", 9353 # because we only have three alias set values: AliasNone ("depends on 9354 # nothing, affects nothing"), AliasDOMSets ("depends on DOM sets, 9355 # affects nothing"), AliasEverything ("depends on everything, affects 9356 # everything"). So the [Affects=Nothing, DependsOn=Everything] case 9357 # gets encoded as AliasEverything and defineJitInfo asserts that if our 9358 # alias state is AliasEverything then we're not eliminatable (because it 9359 # thinks we might have side-effects at that point). Bug 1155796 is 9360 # tracking possible solutions for this. 9361 affects = self.member.affects 9362 dependsOn = self.member.dependsOn 9363 assert affects in IDLInterfaceMember.AffectsValues 9364 assert dependsOn in IDLInterfaceMember.DependsOnValues 9365 return affects == "Nothing" and dependsOn != "Everything" 9366 9367 def aliasSet(self): 9368 """ 9369 Returns the alias set to store in the jitinfo. This may not be the 9370 effective alias set the JIT uses, depending on whether we have enough 9371 information about our args to allow the JIT to prove that effectful 9372 argument conversions won't happen. 9373 """ 9374 dependsOn = self.member.dependsOn 9375 assert dependsOn in IDLInterfaceMember.DependsOnValues 9376 9377 if dependsOn == "Nothing" or dependsOn == "DeviceState": 9378 assert self.member.affects == "Nothing" 9379 return "AliasNone" 9380 9381 if dependsOn == "DOMState": 9382 assert self.member.affects == "Nothing" 9383 return "AliasDOMSets" 9384 9385 return "AliasEverything" 9386 9387 @staticmethod 9388 def getJSReturnTypeTag(t): 9389 if t.nullable(): 9390 # Sometimes it might return null, sometimes not 9391 return "JSVAL_TYPE_UNKNOWN" 9392 if t.isVoid(): 9393 # No return, every time 9394 return "JSVAL_TYPE_UNDEFINED" 9395 if t.isSequence(): 9396 return "JSVAL_TYPE_OBJECT" 9397 if t.isMozMap(): 9398 return "JSVAL_TYPE_OBJECT" 9399 if t.isGeckoInterface(): 9400 return "JSVAL_TYPE_OBJECT" 9401 if t.isString(): 9402 return "JSVAL_TYPE_STRING" 9403 if t.isEnum(): 9404 return "JSVAL_TYPE_STRING" 9405 if t.isCallback(): 9406 return "JSVAL_TYPE_OBJECT" 9407 if t.isAny(): 9408 # The whole point is to return various stuff 9409 return "JSVAL_TYPE_UNKNOWN" 9410 if t.isObject(): 9411 return "JSVAL_TYPE_OBJECT" 9412 if t.isSpiderMonkeyInterface(): 9413 return "JSVAL_TYPE_OBJECT" 9414 if t.isUnion(): 9415 u = t.unroll() 9416 if u.hasNullableType: 9417 # Might be null or not 9418 return "JSVAL_TYPE_UNKNOWN" 9419 return reduce(CGMemberJITInfo.getSingleReturnType, 9420 u.flatMemberTypes, "") 9421 if t.isDictionary(): 9422 return "JSVAL_TYPE_OBJECT" 9423 if t.isDate(): 9424 return "JSVAL_TYPE_OBJECT" 9425 if not t.isPrimitive(): 9426 raise TypeError("No idea what type " + str(t) + " is.") 9427 tag = t.tag() 9428 if tag == IDLType.Tags.bool: 9429 return "JSVAL_TYPE_BOOLEAN" 9430 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, 9431 IDLType.Tags.int16, IDLType.Tags.uint16, 9432 IDLType.Tags.int32]: 9433 return "JSVAL_TYPE_INT32" 9434 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64, 9435 IDLType.Tags.unrestricted_float, IDLType.Tags.float, 9436 IDLType.Tags.unrestricted_double, IDLType.Tags.double]: 9437 # These all use JS_NumberValue, which can return int or double. 9438 # But TI treats "double" as meaning "int or double", so we're 9439 # good to return JSVAL_TYPE_DOUBLE here. 9440 return "JSVAL_TYPE_DOUBLE" 9441 if tag != IDLType.Tags.uint32: 9442 raise TypeError("No idea what type " + str(t) + " is.") 9443 # uint32 is sometimes int and sometimes double. 9444 return "JSVAL_TYPE_DOUBLE" 9445 9446 @staticmethod 9447 def getSingleReturnType(existingType, t): 9448 type = CGMemberJITInfo.getJSReturnTypeTag(t) 9449 if existingType == "": 9450 # First element of the list; just return its type 9451 return type 9452 9453 if type == existingType: 9454 return existingType 9455 if ((type == "JSVAL_TYPE_DOUBLE" and 9456 existingType == "JSVAL_TYPE_INT32") or 9457 (existingType == "JSVAL_TYPE_DOUBLE" and 9458 type == "JSVAL_TYPE_INT32")): 9459 # Promote INT32 to DOUBLE as needed 9460 return "JSVAL_TYPE_DOUBLE" 9461 # Different types 9462 return "JSVAL_TYPE_UNKNOWN" 9463 9464 @staticmethod 9465 def getJSArgType(t): 9466 assert not t.isVoid() 9467 if t.nullable(): 9468 # Sometimes it might return null, sometimes not 9469 return "JSJitInfo::ArgType(JSJitInfo::Null | %s)" % CGMemberJITInfo.getJSArgType(t.inner) 9470 if t.isSequence(): 9471 return "JSJitInfo::Object" 9472 if t.isGeckoInterface(): 9473 return "JSJitInfo::Object" 9474 if t.isString(): 9475 return "JSJitInfo::String" 9476 if t.isEnum(): 9477 return "JSJitInfo::String" 9478 if t.isCallback(): 9479 return "JSJitInfo::Object" 9480 if t.isAny(): 9481 # The whole point is to return various stuff 9482 return "JSJitInfo::Any" 9483 if t.isObject(): 9484 return "JSJitInfo::Object" 9485 if t.isSpiderMonkeyInterface(): 9486 return "JSJitInfo::Object" 9487 if t.isUnion(): 9488 u = t.unroll() 9489 type = "JSJitInfo::Null" if u.hasNullableType else "" 9490 return ("JSJitInfo::ArgType(%s)" % 9491 reduce(CGMemberJITInfo.getSingleArgType, 9492 u.flatMemberTypes, type)) 9493 if t.isDictionary(): 9494 return "JSJitInfo::Object" 9495 if t.isDate(): 9496 return "JSJitInfo::Object" 9497 if not t.isPrimitive(): 9498 raise TypeError("No idea what type " + str(t) + " is.") 9499 tag = t.tag() 9500 if tag == IDLType.Tags.bool: 9501 return "JSJitInfo::Boolean" 9502 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, 9503 IDLType.Tags.int16, IDLType.Tags.uint16, 9504 IDLType.Tags.int32]: 9505 return "JSJitInfo::Integer" 9506 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64, 9507 IDLType.Tags.unrestricted_float, IDLType.Tags.float, 9508 IDLType.Tags.unrestricted_double, IDLType.Tags.double]: 9509 # These all use JS_NumberValue, which can return int or double. 9510 # But TI treats "double" as meaning "int or double", so we're 9511 # good to return JSVAL_TYPE_DOUBLE here. 9512 return "JSJitInfo::Double" 9513 if tag != IDLType.Tags.uint32: 9514 raise TypeError("No idea what type " + str(t) + " is.") 9515 # uint32 is sometimes int and sometimes double. 9516 return "JSJitInfo::Double" 9517 9518 @staticmethod 9519 def getSingleArgType(existingType, t): 9520 type = CGMemberJITInfo.getJSArgType(t) 9521 if existingType == "": 9522 # First element of the list; just return its type 9523 return type 9524 9525 if type == existingType: 9526 return existingType 9527 return "%s | %s" % (existingType, type) 9528 9529 9530class CGStaticMethodJitinfo(CGGeneric): 9531 """ 9532 A class for generating the JITInfo for a promise-returning static method. 9533 """ 9534 def __init__(self, method): 9535 CGGeneric.__init__( 9536 self, 9537 "\n" 9538 "static const JSJitInfo %s_methodinfo = {\n" 9539 " { (JSJitGetterOp)%s },\n" 9540 " { prototypes::id::_ID_Count }, { 0 }, JSJitInfo::StaticMethod,\n" 9541 " JSJitInfo::AliasEverything, JSVAL_TYPE_MISSING, false, false,\n" 9542 " false, false, 0\n" 9543 "};\n" % 9544 (IDLToCIdentifier(method.identifier.name), 9545 CppKeywords.checkMethodName( 9546 IDLToCIdentifier(method.identifier.name)))) 9547 9548 9549def getEnumValueName(value): 9550 # Some enum values can be empty strings. Others might have weird 9551 # characters in them. Deal with the former by returning "_empty", 9552 # deal with possible name collisions from that by throwing if the 9553 # enum value is actually "_empty", and throw on any value 9554 # containing non-ASCII chars for now. Replace all chars other than 9555 # [0-9A-Za-z_] with '_'. 9556 if re.match("[^\x20-\x7E]", value): 9557 raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters') 9558 if re.match("^[0-9]", value): 9559 return '_' + value 9560 value = re.sub(r'[^0-9A-Za-z_]', '_', value) 9561 if re.match("^_[A-Z]|__", value): 9562 raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec') 9563 if value == "_empty": 9564 raise SyntaxError('"_empty" is not an IDL enum value we support yet') 9565 if value == "": 9566 return "_empty" 9567 nativeName = MakeNativeName(value) 9568 if nativeName == "EndGuard_": 9569 raise SyntaxError('Enum value "' + value + '" cannot be used because it' 9570 ' collides with our internal EndGuard_ value. Please' 9571 ' rename our internal EndGuard_ to something else') 9572 return nativeName 9573 9574class CGEnumToJSValue(CGAbstractMethod): 9575 def __init__(self, enum): 9576 enumType = enum.identifier.name 9577 self.stringsArray = enumType + "Values::" + ENUM_ENTRY_VARIABLE_NAME 9578 CGAbstractMethod.__init__(self, None, "ToJSValue", "bool", 9579 [Argument("JSContext*", "aCx"), 9580 Argument(enumType, "aArgument"), 9581 Argument("JS::MutableHandle<JS::Value>", 9582 "aValue")]) 9583 9584 def definition_body(self): 9585 return fill( 9586 """ 9587 MOZ_ASSERT(uint32_t(aArgument) < ArrayLength(${strings})); 9588 JSString* resultStr = 9589 JS_NewStringCopyN(aCx, ${strings}[uint32_t(aArgument)].value, 9590 ${strings}[uint32_t(aArgument)].length); 9591 if (!resultStr) { 9592 return false; 9593 } 9594 aValue.setString(resultStr); 9595 return true; 9596 """, 9597 strings=self.stringsArray) 9598 9599 9600class CGEnum(CGThing): 9601 def __init__(self, enum): 9602 CGThing.__init__(self) 9603 self.enum = enum 9604 strings = CGNamespace( 9605 self.stringsNamespace(), 9606 CGGeneric(declare=("extern const EnumEntry %s[%d];\n" % 9607 (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings())), 9608 define=fill( 9609 """ 9610 extern const EnumEntry ${name}[${count}] = { 9611 $*{entries} 9612 { nullptr, 0 } 9613 }; 9614 """, 9615 name=ENUM_ENTRY_VARIABLE_NAME, 9616 count=self.nEnumStrings(), 9617 entries=''.join('{"%s", %d},\n' % (val, len(val)) 9618 for val in self.enum.values())))) 9619 toJSValue = CGEnumToJSValue(enum) 9620 self.cgThings = CGList([strings, toJSValue], "\n") 9621 9622 def stringsNamespace(self): 9623 return self.enum.identifier.name + "Values" 9624 9625 def nEnumStrings(self): 9626 return len(self.enum.values()) + 1 9627 9628 def declare(self): 9629 decl = fill( 9630 """ 9631 enum class ${name} : uint32_t { 9632 $*{enums} 9633 EndGuard_ 9634 }; 9635 """, 9636 name=self.enum.identifier.name, 9637 enums=",\n".join(map(getEnumValueName, self.enum.values())) + ",\n") 9638 strings = CGNamespace(self.stringsNamespace(), 9639 CGGeneric(declare="extern const EnumEntry %s[%d];\n" 9640 % (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings()))) 9641 return decl + "\n" + self.cgThings.declare() 9642 9643 def define(self): 9644 return self.cgThings.define() 9645 9646 def deps(self): 9647 return self.enum.getDeps() 9648 9649 9650def getUnionAccessorSignatureType(type, descriptorProvider): 9651 """ 9652 Returns the types that are used in the getter and setter signatures for 9653 union types 9654 """ 9655 # Flat member types have already unwrapped nullables. 9656 assert not type.nullable() 9657 9658 if type.isSequence() or type.isMozMap(): 9659 if type.isSequence(): 9660 wrapperType = "Sequence" 9661 else: 9662 wrapperType = "MozMap" 9663 # We don't use the returned template here, so it's OK to just pass no 9664 # sourceDescription. 9665 elementInfo = getJSToNativeConversionInfo(type.inner, 9666 descriptorProvider, 9667 isMember=wrapperType) 9668 return CGTemplatedType(wrapperType, elementInfo.declType, 9669 isConst=True, isReference=True) 9670 9671 # Nested unions are unwrapped automatically into our flatMemberTypes. 9672 assert not type.isUnion() 9673 9674 if type.isGeckoInterface(): 9675 descriptor = descriptorProvider.getDescriptor( 9676 type.unroll().inner.identifier.name) 9677 typeName = CGGeneric(descriptor.nativeType) 9678 # Allow null pointers for old-binding classes. 9679 if type.unroll().inner.isExternal(): 9680 typeName = CGWrapper(typeName, post="*") 9681 else: 9682 typeName = CGWrapper(typeName, post="&") 9683 return typeName 9684 9685 if type.isSpiderMonkeyInterface(): 9686 typeName = CGGeneric(type.name) 9687 return CGWrapper(typeName, post=" const &") 9688 9689 if type.isDOMString() or type.isUSVString(): 9690 return CGGeneric("const nsAString&") 9691 9692 if type.isByteString(): 9693 return CGGeneric("const nsCString&") 9694 9695 if type.isEnum(): 9696 return CGGeneric(type.inner.identifier.name) 9697 9698 if type.isCallback(): 9699 return CGGeneric("%s&" % type.unroll().callback.identifier.name) 9700 9701 if type.isAny(): 9702 return CGGeneric("JS::Value") 9703 9704 if type.isObject(): 9705 return CGGeneric("JSObject*") 9706 9707 if type.isDictionary(): 9708 return CGGeneric("const %s&" % type.inner.identifier.name) 9709 9710 if not type.isPrimitive(): 9711 raise TypeError("Need native type for argument type '%s'" % str(type)) 9712 9713 return CGGeneric(builtinNames[type.tag()]) 9714 9715 9716def getUnionTypeTemplateVars(unionType, type, descriptorProvider, 9717 ownsMembers=False): 9718 name = getUnionMemberName(type) 9719 holderName = "m" + name + "Holder" 9720 9721 # By the time tryNextCode is invoked, we're guaranteed the union has been 9722 # constructed as some type, since we've been trying to convert into the 9723 # corresponding member. 9724 prefix = "" if ownsMembers else "mUnion." 9725 tryNextCode = ("$*{destroyHolder}\n" 9726 "%sDestroy%s();\n" 9727 "tryNext = true;\n" 9728 "return true;\n" % (prefix, name)) 9729 9730 conversionInfo = getJSToNativeConversionInfo( 9731 type, descriptorProvider, failureCode=tryNextCode, 9732 isDefinitelyObject=not type.isDictionary(), 9733 isMember=("OwningUnion" if ownsMembers else None), 9734 sourceDescription="member of %s" % unionType) 9735 9736 if conversionInfo.holderType is not None: 9737 assert not ownsMembers 9738 destroyHolder = "%s.reset();\n" % holderName 9739 else: 9740 destroyHolder = "" 9741 9742 ctorNeedsCx = conversionInfo.declArgs == "cx" 9743 ctorArgs = "cx" if ctorNeedsCx else "" 9744 9745 structType = conversionInfo.declType.define() 9746 externalType = getUnionAccessorSignatureType(type, descriptorProvider).define() 9747 9748 if type.isObject(): 9749 if ownsMembers: 9750 body = dedent(""" 9751 MOZ_ASSERT(mType == eUninitialized); 9752 mValue.mObject.SetValue(obj); 9753 mType = eObject; 9754 """) 9755 else: 9756 body = dedent(""" 9757 MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized); 9758 mUnion.mValue.mObject.SetValue(cx, obj); 9759 mUnion.mType = mUnion.eObject; 9760 """) 9761 9762 # It's a bit sketchy to do the security check after setting the value, 9763 # but it keeps the code cleaner and lets us avoid rooting |obj| over the 9764 # call to CallerSubsumes(). 9765 body = body + dedent(""" 9766 if (passedToJSImpl && !CallerSubsumes(obj)) { 9767 ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "%s"); 9768 return false; 9769 } 9770 return true; 9771 """) 9772 9773 setter = ClassMethod("SetToObject", "bool", 9774 [Argument("JSContext*", "cx"), 9775 Argument("JSObject*", "obj"), 9776 Argument("bool", "passedToJSImpl", default="false")], 9777 inline=True, bodyInHeader=True, 9778 body=body) 9779 9780 else: 9781 # Important: we need to not have our declName involve 9782 # maybe-GCing operations. 9783 if conversionInfo.holderType is not None: 9784 holderArgs = conversionInfo.holderArgs 9785 if holderArgs is None: 9786 holderArgs = "" 9787 initHolder = "%s.emplace(%s);\n" % (holderName, holderArgs) 9788 else: 9789 initHolder = "" 9790 9791 jsConversion = fill( 9792 initHolder + conversionInfo.template, 9793 val="value", 9794 maybeMutableVal="value", 9795 declName="memberSlot", 9796 holderName=(holderName if ownsMembers else "%s.ref()" % holderName), 9797 destroyHolder=destroyHolder, 9798 passedToJSImpl="passedToJSImpl") 9799 9800 jsConversion = fill( 9801 """ 9802 tryNext = false; 9803 { // scope for memberSlot 9804 ${structType}& memberSlot = RawSetAs${name}(${ctorArgs}); 9805 $*{jsConversion} 9806 } 9807 return true; 9808 """, 9809 structType=structType, 9810 name=name, 9811 ctorArgs=ctorArgs, 9812 jsConversion=jsConversion) 9813 9814 if ownsMembers: 9815 handleType = "JS::Handle<JS::Value>" 9816 else: 9817 handleType = "JS::MutableHandle<JS::Value>" 9818 9819 setter = ClassMethod("TrySetTo" + name, "bool", 9820 [Argument("JSContext*", "cx"), 9821 Argument(handleType, "value"), 9822 Argument("bool&", "tryNext"), 9823 Argument("bool", "passedToJSImpl", default="false")], 9824 inline=not ownsMembers, 9825 bodyInHeader=not ownsMembers, 9826 body=jsConversion) 9827 9828 return { 9829 "name": name, 9830 "structType": structType, 9831 "externalType": externalType, 9832 "setter": setter, 9833 "holderType": conversionInfo.holderType.define() if conversionInfo.holderType else None, 9834 "ctorArgs": ctorArgs, 9835 "ctorArgList": [Argument("JSContext*", "cx")] if ctorNeedsCx else [] 9836 } 9837 9838 9839class CGUnionStruct(CGThing): 9840 def __init__(self, type, descriptorProvider, ownsMembers=False): 9841 CGThing.__init__(self) 9842 self.type = type.unroll() 9843 self.descriptorProvider = descriptorProvider 9844 self.ownsMembers = ownsMembers 9845 self.struct = self.getStruct() 9846 9847 def declare(self): 9848 return self.struct.declare() 9849 9850 def define(self): 9851 return self.struct.define() 9852 9853 def deps(self): 9854 return self.type.getDeps() 9855 9856 def getStruct(self): 9857 9858 members = [ClassMember("mType", "Type", body="eUninitialized"), 9859 ClassMember("mValue", "Value")] 9860 ctor = ClassConstructor([], bodyInHeader=True, visibility="public", 9861 explicit=True) 9862 9863 methods = [] 9864 enumValues = ["eUninitialized"] 9865 toJSValCases = [CGCase("eUninitialized", CGGeneric("return false;\n"))] 9866 destructorCases = [CGCase("eUninitialized", None)] 9867 assignmentCases = [ 9868 CGCase("eUninitialized", 9869 CGGeneric('MOZ_ASSERT(mType == eUninitialized,\n' 9870 ' "We need to destroy ourselves?");\n'))] 9871 traceCases = [] 9872 unionValues = [] 9873 if self.type.hasNullableType: 9874 enumValues.append("eNull") 9875 methods.append(ClassMethod("IsNull", "bool", [], const=True, inline=True, 9876 body="return mType == eNull;\n", 9877 bodyInHeader=True)) 9878 methods.append(ClassMethod("SetNull", "void", [], inline=True, 9879 body=("Uninit();\n" 9880 "mType = eNull;\n"), 9881 bodyInHeader=True)) 9882 destructorCases.append(CGCase("eNull", None)) 9883 assignmentCases.append(CGCase("eNull", 9884 CGGeneric("MOZ_ASSERT(mType == eUninitialized);\n" 9885 "mType = eNull;\n"))) 9886 toJSValCases.append(CGCase("eNull", CGGeneric("rval.setNull();\n" 9887 "return true;\n"))) 9888 9889 hasObjectType = any(t.isObject() for t in self.type.flatMemberTypes) 9890 for t in self.type.flatMemberTypes: 9891 vars = getUnionTypeTemplateVars(self.type, 9892 t, self.descriptorProvider, 9893 ownsMembers=self.ownsMembers) 9894 if vars["name"] != "Object" or self.ownsMembers: 9895 body = fill( 9896 """ 9897 if (mType == e${name}) { 9898 return mValue.m${name}.Value(); 9899 } 9900 %s 9901 mType = e${name}; 9902 return mValue.m${name}.SetValue(${ctorArgs}); 9903 """, 9904 **vars) 9905 9906 # bodyInHeader must be false for return values because they own 9907 # their union members and we don't want include headers in 9908 # UnionTypes.h just to call Addref/Release 9909 methods.append(ClassMethod( 9910 "RawSetAs" + vars["name"], 9911 vars["structType"] + "&", 9912 vars["ctorArgList"], 9913 bodyInHeader=not self.ownsMembers, 9914 body=body % "MOZ_ASSERT(mType == eUninitialized);")) 9915 uninit = "Uninit();" 9916 if hasObjectType and not self.ownsMembers: 9917 uninit = 'MOZ_ASSERT(mType != eObject, "This will not play well with Rooted");\n' + uninit 9918 methods.append(ClassMethod( 9919 "SetAs" + vars["name"], 9920 vars["structType"] + "&", 9921 vars["ctorArgList"], 9922 bodyInHeader=not self.ownsMembers, 9923 body=body % uninit)) 9924 if self.ownsMembers: 9925 methods.append(vars["setter"]) 9926 # Provide a SetStringData() method to support string defaults. 9927 if t.isByteString(): 9928 methods.append( 9929 ClassMethod("SetStringData", "void", 9930 [Argument("const nsCString::char_type*", "aData"), 9931 Argument("nsCString::size_type", "aLength")], 9932 inline=True, bodyInHeader=True, 9933 body="RawSetAs%s().Assign(aData, aLength);\n" % t.name)) 9934 elif t.isString(): 9935 methods.append( 9936 ClassMethod("SetStringData", "void", 9937 [Argument("const nsString::char_type*", "aData"), 9938 Argument("nsString::size_type", "aLength")], 9939 inline=True, bodyInHeader=True, 9940 body="RawSetAs%s().Assign(aData, aLength);\n" % t.name)) 9941 9942 body = fill( 9943 """ 9944 MOZ_ASSERT(Is${name}(), "Wrong type!"); 9945 mValue.m${name}.Destroy(); 9946 mType = eUninitialized; 9947 """, 9948 **vars) 9949 methods.append(ClassMethod("Destroy" + vars["name"], 9950 "void", 9951 [], 9952 visibility="private", 9953 bodyInHeader=not self.ownsMembers, 9954 body=body)) 9955 9956 body = fill("return mType == e${name};\n", **vars) 9957 methods.append(ClassMethod("Is" + vars["name"], 9958 "bool", 9959 [], 9960 const=True, 9961 bodyInHeader=True, 9962 body=body)) 9963 9964 body = fill( 9965 """ 9966 MOZ_ASSERT(Is${name}(), "Wrong type!"); 9967 return mValue.m${name}.Value(); 9968 """, 9969 **vars) 9970 # The non-const version of GetAs* returns our internal type 9971 getterReturnType = "%s&" % vars["structType"] 9972 methods.append(ClassMethod("GetAs" + vars["name"], 9973 getterReturnType, 9974 [], 9975 bodyInHeader=True, 9976 body=body)) 9977 # The const version of GetAs* returns our internal type 9978 # for owning unions, but our external type for non-owning 9979 # ones. 9980 if self.ownsMembers: 9981 getterReturnType = "%s const &" % vars["structType"] 9982 else: 9983 getterReturnType = vars["externalType"] 9984 methods.append(ClassMethod("GetAs" + vars["name"], 9985 getterReturnType, 9986 [], 9987 const=True, 9988 bodyInHeader=True, 9989 body=body)) 9990 9991 unionValues.append( 9992 fill("UnionMember<${structType} > m${name}", **vars)) 9993 enumValues.append("e" + vars["name"]) 9994 9995 skipToJSVal = False 9996 try: 9997 toJSValCases.append( 9998 CGCase("e" + vars["name"], 9999 self.getConversionToJS(vars, t))) 10000 except MethodNotNewObjectError: 10001 # If we can't have a ToJSVal() because one of our members can 10002 # only be returned from [NewObject] methods, then just skip 10003 # generating ToJSVal. 10004 skipToJSVal = True 10005 destructorCases.append( 10006 CGCase("e" + vars["name"], 10007 CGGeneric("Destroy%s();\n" % vars["name"]))) 10008 assignmentCases.append( 10009 CGCase("e" + vars["name"], 10010 CGGeneric("SetAs%s() = aOther.GetAs%s();\n" % 10011 (vars["name"], vars["name"])))) 10012 if self.ownsMembers and typeNeedsRooting(t): 10013 if t.isObject(): 10014 traceCases.append( 10015 CGCase("e" + vars["name"], 10016 CGGeneric('JS::UnsafeTraceRoot(trc, %s, "%s");\n' % 10017 ("&mValue.m" + vars["name"] + ".Value()", 10018 "mValue.m" + vars["name"])))) 10019 elif t.isDictionary(): 10020 traceCases.append( 10021 CGCase("e" + vars["name"], 10022 CGGeneric("mValue.m%s.Value().TraceDictionary(trc);\n" % 10023 vars["name"]))) 10024 elif t.isSequence(): 10025 traceCases.append( 10026 CGCase("e" + vars["name"], 10027 CGGeneric("DoTraceSequence(trc, mValue.m%s.Value());\n" % 10028 vars["name"]))) 10029 elif t.isMozMap(): 10030 traceCases.append( 10031 CGCase("e" + vars["name"], 10032 CGGeneric("TraceMozMap(trc, mValue.m%s.Value());\n" % 10033 vars["name"]))) 10034 else: 10035 assert t.isSpiderMonkeyInterface() 10036 traceCases.append( 10037 CGCase("e" + vars["name"], 10038 CGGeneric("mValue.m%s.Value().TraceSelf(trc);\n" % 10039 vars["name"]))) 10040 10041 dtor = CGSwitch("mType", destructorCases).define() 10042 10043 methods.append(ClassMethod("Uninit", "void", [], 10044 visibility="public", body=dtor, 10045 bodyInHeader=not self.ownsMembers, 10046 inline=not self.ownsMembers)) 10047 10048 if not skipToJSVal: 10049 methods.append( 10050 ClassMethod( 10051 "ToJSVal", 10052 "bool", 10053 [ 10054 Argument("JSContext*", "cx"), 10055 Argument("JS::Handle<JSObject*>", "scopeObj"), 10056 Argument("JS::MutableHandle<JS::Value>", "rval") 10057 ], 10058 body=CGSwitch("mType", toJSValCases, 10059 default=CGGeneric("return false;\n")).define() + "\nreturn false;\n", 10060 const=True)) 10061 10062 constructors = [ctor] 10063 selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers) 10064 if self.ownsMembers: 10065 if traceCases: 10066 traceBody = CGSwitch("mType", traceCases, 10067 default=CGGeneric("")).define() 10068 else: 10069 traceBody = "" 10070 methods.append(ClassMethod("TraceUnion", "void", 10071 [Argument("JSTracer*", "trc")], 10072 body=traceBody)) 10073 if CGUnionStruct.isUnionCopyConstructible(self.type): 10074 constructors.append( 10075 ClassConstructor( 10076 [Argument("const %s&" % selfName, "aOther")], 10077 bodyInHeader=True, 10078 visibility="public", 10079 explicit=True, 10080 body="*this = aOther;\n")) 10081 methods.append(ClassMethod( 10082 "operator=", "void", 10083 [Argument("const %s&" % selfName, "aOther")], 10084 body=CGSwitch("aOther.mType", assignmentCases).define())) 10085 disallowCopyConstruction = False 10086 else: 10087 disallowCopyConstruction = True 10088 else: 10089 disallowCopyConstruction = True 10090 10091 if self.ownsMembers: 10092 friend = " friend void ImplCycleCollectionUnlink(%s& aUnion);\n" % CGUnionStruct.unionTypeName(self.type, True) 10093 else: 10094 friend = " friend class %sArgument;\n" % str(self.type) 10095 10096 bases = [ClassBase("AllOwningUnionBase")] if self.ownsMembers else [] 10097 return CGClass(selfName, 10098 bases=bases, 10099 members=members, 10100 constructors=constructors, 10101 methods=methods, 10102 disallowCopyConstruction=disallowCopyConstruction, 10103 extradeclarations=friend, 10104 destructor=ClassDestructor(visibility="public", 10105 body="Uninit();\n", 10106 bodyInHeader=True), 10107 enums=[ClassEnum("Type", enumValues, visibility="private")], 10108 unions=[ClassUnion("Value", unionValues, visibility="private")]) 10109 10110 def getConversionToJS(self, templateVars, type): 10111 assert not type.nullable() # flatMemberTypes never has nullable types 10112 val = "mValue.m%(name)s.Value()" % templateVars 10113 wrapCode = wrapForType( 10114 type, self.descriptorProvider, 10115 { 10116 "jsvalRef": "rval", 10117 "jsvalHandle": "rval", 10118 "obj": "scopeObj", 10119 "result": val, 10120 "typedArraysAreStructs": True 10121 }) 10122 return CGGeneric(wrapCode) 10123 10124 @staticmethod 10125 def isUnionCopyConstructible(type): 10126 return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes) 10127 10128 @staticmethod 10129 def unionTypeName(type, ownsMembers): 10130 """ 10131 Returns a string name for this known union type. 10132 """ 10133 assert type.isUnion() and not type.nullable() 10134 return ("Owning" if ownsMembers else "") + type.name 10135 10136 @staticmethod 10137 def unionTypeDecl(type, ownsMembers): 10138 """ 10139 Returns a string for declaring this possibly-nullable union type. 10140 """ 10141 assert type.isUnion() 10142 nullable = type.nullable() 10143 if nullable: 10144 type = type.inner 10145 decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers)) 10146 if nullable: 10147 decl = CGTemplatedType("Nullable", decl) 10148 return decl.define() 10149 10150 10151class CGUnionConversionStruct(CGThing): 10152 def __init__(self, type, descriptorProvider): 10153 CGThing.__init__(self) 10154 self.type = type.unroll() 10155 self.descriptorProvider = descriptorProvider 10156 10157 def declare(self): 10158 10159 structName = str(self.type) 10160 members = [ClassMember("mUnion", structName + "&", 10161 body="const_cast<%s&>(aUnion)" % structName)] 10162 # Argument needs to be a const ref because that's all Maybe<> allows 10163 ctor = ClassConstructor([Argument("const %s&" % structName, "aUnion")], 10164 bodyInHeader=True, 10165 visibility="public", 10166 explicit=True) 10167 methods = [] 10168 10169 if self.type.hasNullableType: 10170 methods.append(ClassMethod("SetNull", "bool", [], 10171 body=("MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);\n" 10172 "mUnion.mType = mUnion.eNull;\n" 10173 "return true;\n"), 10174 inline=True, bodyInHeader=True)) 10175 10176 for t in self.type.flatMemberTypes: 10177 vars = getUnionTypeTemplateVars(self.type, 10178 t, self.descriptorProvider) 10179 methods.append(vars["setter"]) 10180 if vars["name"] != "Object": 10181 body = fill( 10182 """ 10183 MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized); 10184 mUnion.mType = mUnion.e${name}; 10185 return mUnion.mValue.m${name}.SetValue(${ctorArgs}); 10186 """, 10187 **vars) 10188 methods.append(ClassMethod("RawSetAs" + vars["name"], 10189 vars["structType"] + "&", 10190 vars["ctorArgList"], 10191 bodyInHeader=True, 10192 body=body, 10193 visibility="private")) 10194 # Provide a SetStringData() method to support string defaults. 10195 if t.isByteString(): 10196 methods.append( 10197 ClassMethod("SetStringData", "void", 10198 [Argument("const nsDependentCString::char_type*", "aData"), 10199 Argument("nsDependentCString::size_type", "aLength")], 10200 inline=True, bodyInHeader=True, 10201 body="RawSetAs%s().Rebind(aData, aLength);\n" % t.name)) 10202 elif t.isString(): 10203 methods.append( 10204 ClassMethod("SetStringData", "void", 10205 [Argument("const nsDependentString::char_type*", "aData"), 10206 Argument("nsDependentString::size_type", "aLength")], 10207 inline=True, bodyInHeader=True, 10208 body="RawSetAs%s().Rebind(aData, aLength);\n" % t.name)) 10209 10210 if vars["holderType"] is not None: 10211 holderType = CGTemplatedType("Maybe", 10212 CGGeneric(vars["holderType"])).define() 10213 members.append(ClassMember("m%sHolder" % vars["name"], 10214 holderType)) 10215 10216 return CGClass(structName + "Argument", 10217 members=members, 10218 constructors=[ctor], 10219 methods=methods, 10220 disallowCopyConstruction=True).declare() 10221 10222 def define(self): 10223 return "" 10224 10225 def deps(self): 10226 return set() 10227 10228 10229class ClassItem: 10230 """ Use with CGClass """ 10231 def __init__(self, name, visibility): 10232 self.name = name 10233 self.visibility = visibility 10234 10235 def declare(self, cgClass): 10236 assert False 10237 10238 def define(self, cgClass): 10239 assert False 10240 10241 10242class ClassBase(ClassItem): 10243 def __init__(self, name, visibility='public'): 10244 ClassItem.__init__(self, name, visibility) 10245 10246 def declare(self, cgClass): 10247 return '%s %s' % (self.visibility, self.name) 10248 10249 def define(self, cgClass): 10250 # Only in the header 10251 return '' 10252 10253 10254class ClassMethod(ClassItem): 10255 def __init__(self, name, returnType, args, inline=False, static=False, 10256 virtual=False, const=False, bodyInHeader=False, 10257 templateArgs=None, visibility='public', body=None, 10258 breakAfterReturnDecl="\n", 10259 breakAfterSelf="\n", override=False): 10260 """ 10261 override indicates whether to flag the method as override 10262 """ 10263 assert not override or virtual 10264 assert not (override and static) 10265 self.returnType = returnType 10266 self.args = args 10267 self.inline = inline or bodyInHeader 10268 self.static = static 10269 self.virtual = virtual 10270 self.const = const 10271 self.bodyInHeader = bodyInHeader 10272 self.templateArgs = templateArgs 10273 self.body = body 10274 self.breakAfterReturnDecl = breakAfterReturnDecl 10275 self.breakAfterSelf = breakAfterSelf 10276 self.override = override 10277 ClassItem.__init__(self, name, visibility) 10278 10279 def getDecorators(self, declaring): 10280 decorators = [] 10281 if self.inline: 10282 decorators.append('inline') 10283 if declaring: 10284 if self.static: 10285 decorators.append('static') 10286 if self.virtual: 10287 decorators.append('virtual') 10288 if decorators: 10289 return ' '.join(decorators) + ' ' 10290 return '' 10291 10292 def getBody(self): 10293 # Override me or pass a string to constructor 10294 assert self.body is not None 10295 return self.body 10296 10297 def declare(self, cgClass): 10298 templateClause = ('template <%s>\n' % ', '.join(self.templateArgs) 10299 if self.bodyInHeader and self.templateArgs else '') 10300 args = ', '.join([a.declare() for a in self.args]) 10301 if self.bodyInHeader: 10302 body = indent(self.getBody()) 10303 body = '\n{\n' + body + '}\n' 10304 else: 10305 body = ';\n' 10306 10307 return fill( 10308 "${templateClause}${decorators}${returnType}${breakAfterReturnDecl}" 10309 "${name}(${args})${const}${override}${body}" 10310 "${breakAfterSelf}", 10311 templateClause=templateClause, 10312 decorators=self.getDecorators(True), 10313 returnType=self.returnType, 10314 breakAfterReturnDecl=self.breakAfterReturnDecl, 10315 name=self.name, 10316 args=args, 10317 const=' const' if self.const else '', 10318 override=' override' if self.override else '', 10319 body=body, 10320 breakAfterSelf=self.breakAfterSelf) 10321 10322 def define(self, cgClass): 10323 if self.bodyInHeader: 10324 return '' 10325 10326 templateArgs = cgClass.templateArgs 10327 if templateArgs: 10328 if cgClass.templateSpecialization: 10329 templateArgs = \ 10330 templateArgs[len(cgClass.templateSpecialization):] 10331 10332 if templateArgs: 10333 templateClause = \ 10334 'template <%s>\n' % ', '.join([str(a) for a in templateArgs]) 10335 else: 10336 templateClause = '' 10337 10338 return fill( 10339 """ 10340 ${templateClause}${decorators}${returnType} 10341 ${className}::${name}(${args})${const} 10342 { 10343 $*{body} 10344 } 10345 """, 10346 templateClause=templateClause, 10347 decorators=self.getDecorators(False), 10348 returnType=self.returnType, 10349 className=cgClass.getNameString(), 10350 name=self.name, 10351 args=', '.join([a.define() for a in self.args]), 10352 const=' const' if self.const else '', 10353 body=self.getBody()) 10354 10355 10356class ClassUsingDeclaration(ClassItem): 10357 """ 10358 Used for importing a name from a base class into a CGClass 10359 10360 baseClass is the name of the base class to import the name from 10361 10362 name is the name to import 10363 10364 visibility determines the visibility of the name (public, 10365 protected, private), defaults to public. 10366 """ 10367 def __init__(self, baseClass, name, visibility='public'): 10368 self.baseClass = baseClass 10369 ClassItem.__init__(self, name, visibility) 10370 10371 def declare(self, cgClass): 10372 return "using %s::%s;\n\n" % (self.baseClass, self.name) 10373 10374 def define(self, cgClass): 10375 return '' 10376 10377 10378class ClassConstructor(ClassItem): 10379 """ 10380 Used for adding a constructor to a CGClass. 10381 10382 args is a list of Argument objects that are the arguments taken by the 10383 constructor. 10384 10385 inline should be True if the constructor should be marked inline. 10386 10387 bodyInHeader should be True if the body should be placed in the class 10388 declaration in the header. 10389 10390 visibility determines the visibility of the constructor (public, 10391 protected, private), defaults to private. 10392 10393 explicit should be True if the constructor should be marked explicit. 10394 10395 baseConstructors is a list of strings containing calls to base constructors, 10396 defaults to None. 10397 10398 body contains a string with the code for the constructor, defaults to empty. 10399 """ 10400 def __init__(self, args, inline=False, bodyInHeader=False, 10401 visibility="private", explicit=False, constexpr=False, baseConstructors=None, 10402 body=""): 10403 assert not (inline and constexpr) 10404 assert not (bodyInHeader and constexpr) 10405 self.args = args 10406 self.inline = inline or bodyInHeader 10407 self.bodyInHeader = bodyInHeader or constexpr 10408 self.explicit = explicit 10409 self.constexpr = constexpr 10410 self.baseConstructors = baseConstructors or [] 10411 self.body = body 10412 ClassItem.__init__(self, None, visibility) 10413 10414 def getDecorators(self, declaring): 10415 decorators = [] 10416 if self.explicit: 10417 decorators.append('explicit') 10418 if self.inline and declaring: 10419 decorators.append('inline') 10420 if self.constexpr and declaring: 10421 decorators.append('constexpr') 10422 if decorators: 10423 return ' '.join(decorators) + ' ' 10424 return '' 10425 10426 def getInitializationList(self, cgClass): 10427 items = [str(c) for c in self.baseConstructors] 10428 for m in cgClass.members: 10429 if not m.static: 10430 initialize = m.body 10431 if initialize: 10432 items.append(m.name + "(" + initialize + ")") 10433 10434 if len(items) > 0: 10435 return '\n : ' + ',\n '.join(items) 10436 return '' 10437 10438 def getBody(self): 10439 return self.body 10440 10441 def declare(self, cgClass): 10442 args = ', '.join([a.declare() for a in self.args]) 10443 if self.bodyInHeader: 10444 body = self.getInitializationList(cgClass) + '\n{\n' + indent(self.getBody()) + '}\n' 10445 else: 10446 body = ';\n' 10447 10448 return fill( 10449 "${decorators}${className}(${args})${body}\n", 10450 decorators=self.getDecorators(True), 10451 className=cgClass.getNameString(), 10452 args=args, 10453 body=body) 10454 10455 def define(self, cgClass): 10456 if self.bodyInHeader: 10457 return '' 10458 10459 return fill( 10460 """ 10461 ${decorators} 10462 ${className}::${className}(${args})${initializationList} 10463 { 10464 $*{body} 10465 } 10466 """, 10467 decorators=self.getDecorators(False), 10468 className=cgClass.getNameString(), 10469 args=', '.join([a.define() for a in self.args]), 10470 initializationList=self.getInitializationList(cgClass), 10471 body=self.getBody()) 10472 10473 10474class ClassDestructor(ClassItem): 10475 """ 10476 Used for adding a destructor to a CGClass. 10477 10478 inline should be True if the destructor should be marked inline. 10479 10480 bodyInHeader should be True if the body should be placed in the class 10481 declaration in the header. 10482 10483 visibility determines the visibility of the destructor (public, 10484 protected, private), defaults to private. 10485 10486 body contains a string with the code for the destructor, defaults to empty. 10487 10488 virtual determines whether the destructor is virtual, defaults to False. 10489 """ 10490 def __init__(self, inline=False, bodyInHeader=False, 10491 visibility="private", body='', virtual=False): 10492 self.inline = inline or bodyInHeader 10493 self.bodyInHeader = bodyInHeader 10494 self.body = body 10495 self.virtual = virtual 10496 ClassItem.__init__(self, None, visibility) 10497 10498 def getDecorators(self, declaring): 10499 decorators = [] 10500 if self.virtual and declaring: 10501 decorators.append('virtual') 10502 if self.inline and declaring: 10503 decorators.append('inline') 10504 if decorators: 10505 return ' '.join(decorators) + ' ' 10506 return '' 10507 10508 def getBody(self): 10509 return self.body 10510 10511 def declare(self, cgClass): 10512 if self.bodyInHeader: 10513 body = '\n{\n' + indent(self.getBody()) + '}\n' 10514 else: 10515 body = ';\n' 10516 10517 return fill( 10518 "${decorators}~${className}()${body}\n", 10519 decorators=self.getDecorators(True), 10520 className=cgClass.getNameString(), 10521 body=body) 10522 10523 def define(self, cgClass): 10524 if self.bodyInHeader: 10525 return '' 10526 return fill( 10527 """ 10528 ${decorators} 10529 ${className}::~${className}() 10530 { 10531 $*{body} 10532 } 10533 """, 10534 decorators=self.getDecorators(False), 10535 className=cgClass.getNameString(), 10536 body=self.getBody()) 10537 10538 10539class ClassMember(ClassItem): 10540 def __init__(self, name, type, visibility="private", static=False, 10541 body=None, hasIgnoreInitCheckFlag=False): 10542 self.type = type 10543 self.static = static 10544 self.body = body 10545 self.hasIgnoreInitCheckFlag = hasIgnoreInitCheckFlag; 10546 ClassItem.__init__(self, name, visibility) 10547 10548 def declare(self, cgClass): 10549 return '%s%s%s %s;\n' % ('static ' if self.static else '', 10550 'MOZ_INIT_OUTSIDE_CTOR ' 10551 if self.hasIgnoreInitCheckFlag else '', 10552 self.type, self.name) 10553 10554 def define(self, cgClass): 10555 if not self.static: 10556 return '' 10557 if self.body: 10558 body = " = " + self.body 10559 else: 10560 body = "" 10561 return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(), 10562 self.name, body) 10563 10564 10565class ClassTypedef(ClassItem): 10566 def __init__(self, name, type, visibility="public"): 10567 self.type = type 10568 ClassItem.__init__(self, name, visibility) 10569 10570 def declare(self, cgClass): 10571 return 'typedef %s %s;\n' % (self.type, self.name) 10572 10573 def define(self, cgClass): 10574 # Only goes in the header 10575 return '' 10576 10577 10578class ClassEnum(ClassItem): 10579 def __init__(self, name, entries, values=None, visibility="public"): 10580 self.entries = entries 10581 self.values = values 10582 ClassItem.__init__(self, name, visibility) 10583 10584 def declare(self, cgClass): 10585 entries = [] 10586 for i in range(0, len(self.entries)): 10587 if not self.values or i >= len(self.values): 10588 entry = '%s' % self.entries[i] 10589 else: 10590 entry = '%s = %s' % (self.entries[i], self.values[i]) 10591 entries.append(entry) 10592 name = '' if not self.name else ' ' + self.name 10593 return 'enum%s\n{\n%s\n};\n' % (name, indent(',\n'.join(entries))) 10594 10595 def define(self, cgClass): 10596 # Only goes in the header 10597 return '' 10598 10599 10600class ClassUnion(ClassItem): 10601 def __init__(self, name, entries, visibility="public"): 10602 self.entries = [entry + ";\n" for entry in entries] 10603 ClassItem.__init__(self, name, visibility) 10604 10605 def declare(self, cgClass): 10606 return "union %s\n{\n%s\n};\n" % (self.name, indent(''.join(self.entries))) 10607 10608 def define(self, cgClass): 10609 # Only goes in the header 10610 return '' 10611 10612 10613class CGClass(CGThing): 10614 def __init__(self, name, bases=[], members=[], constructors=[], 10615 destructor=None, methods=[], 10616 typedefs=[], enums=[], unions=[], templateArgs=[], 10617 templateSpecialization=[], isStruct=False, 10618 disallowCopyConstruction=False, indent='', 10619 decorators='', 10620 extradeclarations='', 10621 extradefinitions=''): 10622 CGThing.__init__(self) 10623 self.name = name 10624 self.bases = bases 10625 self.members = members 10626 self.constructors = constructors 10627 # We store our single destructor in a list, since all of our 10628 # code wants lists of members. 10629 self.destructors = [destructor] if destructor else [] 10630 self.methods = methods 10631 self.typedefs = typedefs 10632 self.enums = enums 10633 self.unions = unions 10634 self.templateArgs = templateArgs 10635 self.templateSpecialization = templateSpecialization 10636 self.isStruct = isStruct 10637 self.disallowCopyConstruction = disallowCopyConstruction 10638 self.indent = indent 10639 self.defaultVisibility = 'public' if isStruct else 'private' 10640 self.decorators = decorators 10641 self.extradeclarations = extradeclarations 10642 self.extradefinitions = extradefinitions 10643 10644 def getNameString(self): 10645 className = self.name 10646 if self.templateSpecialization: 10647 className += '<%s>' % ', '.join([str(a) 10648 for a in self.templateSpecialization]) 10649 return className 10650 10651 def declare(self): 10652 result = '' 10653 if self.templateArgs: 10654 templateArgs = [a.declare() for a in self.templateArgs] 10655 templateArgs = templateArgs[len(self.templateSpecialization):] 10656 result += ('template <%s>\n' % 10657 ','.join([str(a) for a in templateArgs])) 10658 10659 type = 'struct' if self.isStruct else 'class' 10660 10661 if self.templateSpecialization: 10662 specialization = \ 10663 '<%s>' % ', '.join([str(a) for a in self.templateSpecialization]) 10664 else: 10665 specialization = '' 10666 10667 myself = '%s %s%s' % (type, self.name, specialization) 10668 if self.decorators != '': 10669 myself += " " + self.decorators 10670 result += myself 10671 10672 if self.bases: 10673 inherit = ' : ' 10674 result += inherit 10675 # Grab our first base 10676 baseItems = [CGGeneric(b.declare(self)) for b in self.bases] 10677 bases = baseItems[:1] 10678 # Indent the rest 10679 bases.extend(CGIndenter(b, len(myself) + len(inherit)) 10680 for b in baseItems[1:]) 10681 result += ",\n".join(b.define() for b in bases) 10682 10683 result += '\n{\n' 10684 10685 result += self.extradeclarations 10686 10687 def declareMembers(cgClass, memberList, defaultVisibility): 10688 members = {'private': [], 'protected': [], 'public': []} 10689 10690 for member in memberList: 10691 members[member.visibility].append(member) 10692 10693 if defaultVisibility == 'public': 10694 order = ['public', 'protected', 'private'] 10695 else: 10696 order = ['private', 'protected', 'public'] 10697 10698 result = '' 10699 10700 lastVisibility = defaultVisibility 10701 for visibility in order: 10702 list = members[visibility] 10703 if list: 10704 if visibility != lastVisibility: 10705 result += visibility + ':\n' 10706 for member in list: 10707 result += indent(member.declare(cgClass)) 10708 lastVisibility = visibility 10709 return (result, lastVisibility) 10710 10711 if self.disallowCopyConstruction: 10712 class DisallowedCopyConstructor(object): 10713 def __init__(self): 10714 self.visibility = "private" 10715 10716 def declare(self, cgClass): 10717 name = cgClass.getNameString() 10718 return ("%s(const %s&) = delete;\n" 10719 "void operator=(const %s&) = delete;\n" % (name, name, name)) 10720 10721 disallowedCopyConstructors = [DisallowedCopyConstructor()] 10722 else: 10723 disallowedCopyConstructors = [] 10724 10725 order = [self.enums, self.unions, 10726 self.typedefs, self.members, 10727 self.constructors + disallowedCopyConstructors, 10728 self.destructors, self.methods] 10729 10730 lastVisibility = self.defaultVisibility 10731 pieces = [] 10732 for memberList in order: 10733 code, lastVisibility = declareMembers(self, memberList, lastVisibility) 10734 10735 if code: 10736 code = code.rstrip() + "\n" # remove extra blank lines at the end 10737 pieces.append(code) 10738 10739 result += '\n'.join(pieces) 10740 result += '};\n' 10741 result = indent(result, len(self.indent)) 10742 return result 10743 10744 def define(self): 10745 def defineMembers(cgClass, memberList, itemCount, separator=''): 10746 result = '' 10747 for member in memberList: 10748 if itemCount != 0: 10749 result = result + separator 10750 definition = member.define(cgClass) 10751 if definition: 10752 # Member variables would only produce empty lines here. 10753 result += definition 10754 itemCount += 1 10755 return (result, itemCount) 10756 10757 order = [(self.members, ''), (self.constructors, '\n'), 10758 (self.destructors, '\n'), (self.methods, '\n')] 10759 10760 result = self.extradefinitions 10761 itemCount = 0 10762 for memberList, separator in order: 10763 memberString, itemCount = defineMembers(self, memberList, 10764 itemCount, separator) 10765 result = result + memberString 10766 return result 10767 10768 10769class CGResolveOwnProperty(CGAbstractStaticMethod): 10770 def __init__(self, descriptor): 10771 args = [Argument('JSContext*', 'cx'), 10772 Argument('JS::Handle<JSObject*>', 'wrapper'), 10773 Argument('JS::Handle<JSObject*>', 'obj'), 10774 Argument('JS::Handle<jsid>', 'id'), 10775 Argument('JS::MutableHandle<JS::PropertyDescriptor>', 'desc'), 10776 ] 10777 CGAbstractStaticMethod.__init__(self, descriptor, "ResolveOwnProperty", 10778 "bool", args) 10779 10780 def definition_body(self): 10781 return "return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n" 10782 10783 10784class CGResolveOwnPropertyViaResolve(CGAbstractBindingMethod): 10785 """ 10786 An implementation of Xray ResolveOwnProperty stuff for things that have a 10787 resolve hook. 10788 """ 10789 def __init__(self, descriptor): 10790 args = [Argument('JSContext*', 'cx'), 10791 Argument('JS::Handle<JSObject*>', 'wrapper'), 10792 Argument('JS::Handle<JSObject*>', 'obj'), 10793 Argument('JS::Handle<jsid>', 'id'), 10794 Argument('JS::MutableHandle<JS::PropertyDescriptor>', 'desc')] 10795 CGAbstractBindingMethod.__init__(self, descriptor, 10796 "ResolveOwnPropertyViaResolve", 10797 args, getThisObj="", 10798 callArgs="") 10799 10800 def generate_code(self): 10801 return CGGeneric(dedent(""" 10802 { 10803 // Since we're dealing with an Xray, do the resolve on the 10804 // underlying object first. That gives it a chance to 10805 // define properties on the actual object as needed, and 10806 // then use the fact that it created the objects as a flag 10807 // to avoid re-resolving the properties if someone deletes 10808 // them. 10809 JSAutoCompartment ac(cx, obj); 10810 JS::Rooted<JS::PropertyDescriptor> objDesc(cx); 10811 if (!self->DoResolve(cx, obj, id, &objDesc)) { 10812 return false; 10813 } 10814 // If desc.value() is undefined, then the DoResolve call 10815 // has already defined the property on the object. Don't 10816 // try to also define it. 10817 if (objDesc.object() && 10818 !objDesc.value().isUndefined() && 10819 !JS_DefinePropertyById(cx, obj, id, objDesc)) { 10820 return false; 10821 } 10822 } 10823 return self->DoResolve(cx, wrapper, id, desc); 10824 """)) 10825 10826 10827class CGEnumerateOwnProperties(CGAbstractStaticMethod): 10828 def __init__(self, descriptor): 10829 args = [Argument('JSContext*', 'cx'), 10830 Argument('JS::Handle<JSObject*>', 'wrapper'), 10831 Argument('JS::Handle<JSObject*>', 'obj'), 10832 Argument('JS::AutoIdVector&', 'props')] 10833 CGAbstractStaticMethod.__init__(self, descriptor, 10834 "EnumerateOwnProperties", "bool", args) 10835 10836 def definition_body(self): 10837 return "return js::GetProxyHandler(obj)->ownPropertyKeys(cx, wrapper, props);\n" 10838 10839 10840class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod): 10841 """ 10842 An implementation of Xray EnumerateOwnProperties stuff for things 10843 that have a resolve hook. 10844 """ 10845 def __init__(self, descriptor): 10846 args = [Argument('JSContext*', 'cx'), 10847 Argument('JS::Handle<JSObject*>', 'wrapper'), 10848 Argument('JS::Handle<JSObject*>', 'obj'), 10849 Argument('JS::AutoIdVector&', 'props')] 10850 CGAbstractBindingMethod.__init__(self, descriptor, 10851 "EnumerateOwnPropertiesViaGetOwnPropertyNames", 10852 args, getThisObj="", 10853 callArgs="") 10854 10855 def generate_code(self): 10856 return CGGeneric(dedent(""" 10857 AutoTArray<nsString, 8> names; 10858 binding_detail::FastErrorResult rv; 10859 self->GetOwnPropertyNames(cx, names, rv); 10860 if (rv.MaybeSetPendingException(cx)) { 10861 return false; 10862 } 10863 // OK to pass null as "proxy" because it's ignored if 10864 // shadowPrototypeProperties is true 10865 return AppendNamedPropertyIds(cx, nullptr, names, true, props); 10866 """)) 10867 10868 10869class CGPrototypeTraitsClass(CGClass): 10870 def __init__(self, descriptor, indent=''): 10871 templateArgs = [Argument('prototypes::ID', 'PrototypeID')] 10872 templateSpecialization = ['prototypes::id::' + descriptor.name] 10873 enums = [ClassEnum('', ['Depth'], 10874 [descriptor.interface.inheritanceDepth()])] 10875 CGClass.__init__(self, 'PrototypeTraits', indent=indent, 10876 templateArgs=templateArgs, 10877 templateSpecialization=templateSpecialization, 10878 enums=enums, isStruct=True) 10879 10880 def deps(self): 10881 return set() 10882 10883 10884class CGClassForwardDeclare(CGThing): 10885 def __init__(self, name, isStruct=False): 10886 CGThing.__init__(self) 10887 self.name = name 10888 self.isStruct = isStruct 10889 10890 def declare(self): 10891 type = 'struct' if self.isStruct else 'class' 10892 return '%s %s;\n' % (type, self.name) 10893 10894 def define(self): 10895 # Header only 10896 return '' 10897 10898 def deps(self): 10899 return set() 10900 10901 10902class CGProxySpecialOperation(CGPerSignatureCall): 10903 """ 10904 Base class for classes for calling an indexed or named special operation 10905 (don't use this directly, use the derived classes below). 10906 10907 If checkFound is False, will just assert that the prop is found instead of 10908 checking that it is before wrapping the value. 10909 10910 resultVar: See the docstring for CGCallGenerator. 10911 10912 foundVar: For getters and deleters, the generated code can also set a bool 10913 variable, declared by the caller, if the given indexed or named property 10914 already existed. If the caller wants this, it should pass the name of the 10915 bool variable as the foundVar keyword argument to the constructor. The 10916 caller is responsible for declaring the variable and initializing it to 10917 false. 10918 """ 10919 def __init__(self, descriptor, operation, checkFound=True, 10920 argumentHandleValue=None, resultVar=None, foundVar=None): 10921 self.checkFound = checkFound 10922 self.foundVar = foundVar or "found" 10923 10924 nativeName = MakeNativeName(descriptor.binaryNameFor(operation)) 10925 operation = descriptor.operations[operation] 10926 assert len(operation.signatures()) == 1 10927 signature = operation.signatures()[0] 10928 10929 returnType, arguments = signature 10930 10931 # We pass len(arguments) as the final argument so that the 10932 # CGPerSignatureCall won't do any argument conversion of its own. 10933 CGPerSignatureCall.__init__(self, returnType, arguments, nativeName, 10934 False, descriptor, operation, 10935 len(arguments), resultVar=resultVar) 10936 10937 if operation.isSetter() or operation.isCreator(): 10938 # arguments[0] is the index or name of the item that we're setting. 10939 argument = arguments[1] 10940 info = getJSToNativeConversionInfo( 10941 argument.type, descriptor, 10942 treatNullAs=argument.treatNullAs, 10943 sourceDescription=("value being assigned to %s setter" % 10944 descriptor.interface.identifier.name)) 10945 if argumentHandleValue is None: 10946 argumentHandleValue = "desc.value()" 10947 rootedValue = fill( 10948 """ 10949 JS::Rooted<JS::Value> rootedValue(cx, ${argumentHandleValue}); 10950 """, 10951 argumentHandleValue = argumentHandleValue) 10952 templateValues = { 10953 "declName": argument.identifier.name, 10954 "holderName": argument.identifier.name + "_holder", 10955 "val": argumentHandleValue, 10956 "maybeMutableVal": "&rootedValue", 10957 "obj": "obj", 10958 "passedToJSImpl": "false" 10959 } 10960 self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues)) 10961 # rootedValue needs to come before the conversion, so we 10962 # need to prepend it last. 10963 self.cgRoot.prepend(CGGeneric(rootedValue)) 10964 elif operation.isGetter() or operation.isDeleter(): 10965 if foundVar is None: 10966 self.cgRoot.prepend(CGGeneric("bool found = false;\n")) 10967 10968 def getArguments(self): 10969 args = [(a, a.identifier.name) for a in self.arguments] 10970 if self.idlNode.isGetter() or self.idlNode.isDeleter(): 10971 args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean], 10972 self.idlNode), 10973 self.foundVar)) 10974 return args 10975 10976 def wrap_return_value(self): 10977 if not self.idlNode.isGetter() or self.templateValues is None: 10978 return "" 10979 10980 wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues)) 10981 if self.checkFound: 10982 wrap = CGIfWrapper(wrap, self.foundVar) 10983 else: 10984 wrap = CGList([CGGeneric("MOZ_ASSERT(" + self.foundVar + ");\n"), wrap]) 10985 return "\n" + wrap.define() 10986 10987 10988class CGProxyIndexedOperation(CGProxySpecialOperation): 10989 """ 10990 Class to generate a call to an indexed operation. 10991 10992 If doUnwrap is False, the caller is responsible for making sure a variable 10993 named 'self' holds the C++ object somewhere where the code we generate 10994 will see it. 10995 10996 If checkFound is False, will just assert that the prop is found instead of 10997 checking that it is before wrapping the value. 10998 10999 resultVar: See the docstring for CGCallGenerator. 11000 11001 foundVar: See the docstring for CGProxySpecialOperation. 11002 """ 11003 def __init__(self, descriptor, name, doUnwrap=True, checkFound=True, 11004 argumentHandleValue=None, resultVar=None, foundVar=None): 11005 self.doUnwrap = doUnwrap 11006 CGProxySpecialOperation.__init__(self, descriptor, name, checkFound, 11007 argumentHandleValue=argumentHandleValue, 11008 resultVar=resultVar, 11009 foundVar=foundVar) 11010 11011 def define(self): 11012 # Our first argument is the id we're getting. 11013 argName = self.arguments[0].identifier.name 11014 if argName == "index": 11015 # We already have our index in a variable with that name 11016 setIndex = "" 11017 else: 11018 setIndex = "uint32_t %s = index;\n" % argName 11019 if self.doUnwrap: 11020 unwrap = "%s* self = UnwrapProxy(proxy);\n" % self.descriptor.nativeType 11021 else: 11022 unwrap = "" 11023 return (setIndex + unwrap + 11024 CGProxySpecialOperation.define(self)) 11025 11026 11027class CGProxyIndexedGetter(CGProxyIndexedOperation): 11028 """ 11029 Class to generate a call to an indexed getter. If templateValues is not None 11030 the returned value will be wrapped with wrapForType using templateValues. 11031 11032 If doUnwrap is False, the caller is responsible for making sure a variable 11033 named 'self' holds the C++ object somewhere where the code we generate 11034 will see it. 11035 11036 If checkFound is False, will just assert that the prop is found instead of 11037 checking that it is before wrapping the value. 11038 11039 foundVar: See the docstring for CGProxySpecialOperation. 11040 """ 11041 def __init__(self, descriptor, templateValues=None, doUnwrap=True, 11042 checkFound=True, foundVar=None): 11043 self.templateValues = templateValues 11044 CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedGetter', 11045 doUnwrap, checkFound, foundVar=foundVar) 11046 11047 11048class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter): 11049 """ 11050 Class to generate a call that checks whether an indexed property exists. 11051 11052 For now, we just delegate to CGProxyIndexedGetter 11053 11054 foundVar: See the docstring for CGProxySpecialOperation. 11055 """ 11056 def __init__(self, descriptor, foundVar): 11057 CGProxyIndexedGetter.__init__(self, descriptor, foundVar=foundVar) 11058 self.cgRoot.append(CGGeneric("(void)result;\n")) 11059 11060 11061class CGProxyIndexedSetter(CGProxyIndexedOperation): 11062 """ 11063 Class to generate a call to an indexed setter. 11064 """ 11065 def __init__(self, descriptor, argumentHandleValue=None): 11066 CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedSetter', 11067 argumentHandleValue=argumentHandleValue) 11068 11069 11070class CGProxyNamedOperation(CGProxySpecialOperation): 11071 """ 11072 Class to generate a call to a named operation. 11073 11074 'value' is the jsval to use for the name; None indicates that it should be 11075 gotten from the property id. 11076 11077 resultVar: See the docstring for CGCallGenerator. 11078 11079 foundVar: See the docstring for CGProxySpecialOperation. 11080 """ 11081 def __init__(self, descriptor, name, value=None, argumentHandleValue=None, 11082 resultVar=None, foundVar=None): 11083 CGProxySpecialOperation.__init__(self, descriptor, name, 11084 argumentHandleValue=argumentHandleValue, 11085 resultVar=resultVar, 11086 foundVar=foundVar) 11087 self.value = value 11088 11089 def define(self): 11090 # Our first argument is the id we're getting. 11091 argName = self.arguments[0].identifier.name 11092 if argName == "id": 11093 # deal with the name collision 11094 decls = "JS::Rooted<jsid> id_(cx, id);\n" 11095 idName = "id_" 11096 else: 11097 decls = "" 11098 idName = "id" 11099 11100 decls += "binding_detail::FakeString %s;\n" % argName 11101 11102 main = fill( 11103 """ 11104 ${nativeType}* self = UnwrapProxy(proxy); 11105 $*{op} 11106 """, 11107 nativeType=self.descriptor.nativeType, 11108 op=CGProxySpecialOperation.define(self)) 11109 11110 if self.value is None: 11111 return fill( 11112 """ 11113 $*{decls} 11114 bool isSymbol; 11115 if (!ConvertIdToString(cx, ${idName}, ${argName}, isSymbol)) { 11116 return false; 11117 } 11118 if (!isSymbol) { 11119 $*{main} 11120 } 11121 """, 11122 decls=decls, 11123 idName=idName, 11124 argName=argName, 11125 main=main) 11126 11127 # Sadly, we have to set up nameVal even if we have an atom id, 11128 # because we don't know for sure, and we can end up needing it 11129 # so it needs to be higher up the stack. Using a Maybe here 11130 # seems like probable overkill. 11131 return fill( 11132 """ 11133 $*{decls} 11134 JS::Rooted<JS::Value> nameVal(cx, ${value}); 11135 if (!nameVal.isSymbol()) { 11136 if (!ConvertJSValueToString(cx, nameVal, eStringify, eStringify, 11137 ${argName})) { 11138 return false; 11139 } 11140 $*{main} 11141 } 11142 """, 11143 decls=decls, 11144 value=self.value, 11145 argName=argName, 11146 main=main) 11147 11148 11149class CGProxyNamedGetter(CGProxyNamedOperation): 11150 """ 11151 Class to generate a call to an named getter. If templateValues is not None 11152 the returned value will be wrapped with wrapForType using templateValues. 11153 'value' is the jsval to use for the name; None indicates that it should be 11154 gotten from the property id. 11155 11156 foundVar: See the docstring for CGProxySpecialOperation. 11157 """ 11158 def __init__(self, descriptor, templateValues=None, value=None, 11159 foundVar=None): 11160 self.templateValues = templateValues 11161 CGProxyNamedOperation.__init__(self, descriptor, 'NamedGetter', value, 11162 foundVar=foundVar) 11163 11164 11165class CGProxyNamedPresenceChecker(CGProxyNamedGetter): 11166 """ 11167 Class to generate a call that checks whether a named property exists. 11168 11169 For now, we just delegate to CGProxyNamedGetter 11170 11171 foundVar: See the docstring for CGProxySpecialOperation. 11172 """ 11173 def __init__(self, descriptor, foundVar=None): 11174 CGProxyNamedGetter.__init__(self, descriptor, foundVar=foundVar) 11175 self.cgRoot.append(CGGeneric("(void)result;\n")) 11176 11177 11178class CGProxyNamedSetter(CGProxyNamedOperation): 11179 """ 11180 Class to generate a call to a named setter. 11181 """ 11182 def __init__(self, descriptor, argumentHandleValue=None): 11183 CGProxyNamedOperation.__init__(self, descriptor, 'NamedSetter', 11184 argumentHandleValue=argumentHandleValue) 11185 11186 11187class CGProxyNamedDeleter(CGProxyNamedOperation): 11188 """ 11189 Class to generate a call to a named deleter. 11190 11191 resultVar: See the docstring for CGCallGenerator. 11192 11193 foundVar: See the docstring for CGProxySpecialOperation. 11194 """ 11195 def __init__(self, descriptor, resultVar=None, foundVar=None): 11196 CGProxyNamedOperation.__init__(self, descriptor, 'NamedDeleter', 11197 resultVar=resultVar, 11198 foundVar=foundVar) 11199 11200 11201class CGProxyIsProxy(CGAbstractMethod): 11202 def __init__(self, descriptor): 11203 args = [Argument('JSObject*', 'obj')] 11204 CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True) 11205 11206 def declare(self): 11207 return "" 11208 11209 def definition_body(self): 11210 return "return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();\n" 11211 11212 11213class CGProxyUnwrap(CGAbstractMethod): 11214 def __init__(self, descriptor): 11215 args = [Argument('JSObject*', 'obj')] 11216 CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", descriptor.nativeType + '*', args, alwaysInline=True) 11217 11218 def declare(self): 11219 return "" 11220 11221 def definition_body(self): 11222 return fill( 11223 """ 11224 MOZ_ASSERT(js::IsProxy(obj)); 11225 if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) { 11226 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj)); 11227 obj = js::UncheckedUnwrap(obj); 11228 } 11229 MOZ_ASSERT(IsProxy(obj)); 11230 return static_cast<${type}*>(js::GetProxyPrivate(obj).toPrivate()); 11231 """, 11232 type=self.descriptor.nativeType) 11233 11234 11235class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod): 11236 def __init__(self, descriptor): 11237 args = [Argument('JSContext*', 'cx'), 11238 Argument('JS::Handle<JSObject*>', 'proxy'), 11239 Argument('JS::Handle<jsid>', 'id'), 11240 Argument('bool', 'ignoreNamedProps'), 11241 Argument('JS::MutableHandle<JS::PropertyDescriptor>', 'desc')] 11242 ClassMethod.__init__(self, "getOwnPropDescriptor", "bool", args, 11243 virtual=True, override=True, const=True) 11244 self.descriptor = descriptor 11245 11246 def getBody(self): 11247 indexedGetter = self.descriptor.operations['IndexedGetter'] 11248 indexedSetter = self.descriptor.operations['IndexedSetter'] 11249 11250 if self.descriptor.supportsIndexedProperties(): 11251 readonly = toStringBool(indexedSetter is None) 11252 fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;\n" % readonly 11253 templateValues = { 11254 'jsvalRef': 'desc.value()', 11255 'jsvalHandle': 'desc.value()', 11256 'obj': 'proxy', 11257 'successCode': fillDescriptor 11258 } 11259 getIndexed = fill( 11260 """ 11261 uint32_t index = GetArrayIndexFromId(cx, id); 11262 if (IsArrayIndex(index)) { 11263 $*{callGetter} 11264 } 11265 11266 """, 11267 callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define()) 11268 else: 11269 getIndexed = "" 11270 11271 if self.descriptor.supportsNamedProperties(): 11272 operations = self.descriptor.operations 11273 readonly = toStringBool(operations['NamedSetter'] is None) 11274 fillDescriptor = ( 11275 "FillPropertyDescriptor(desc, proxy, %s, %s);\n" 11276 "return true;\n" % 11277 (readonly, 11278 toStringBool(self.descriptor.namedPropertiesEnumerable))) 11279 templateValues = {'jsvalRef': 'desc.value()', 'jsvalHandle': 'desc.value()', 11280 'obj': 'proxy', 'successCode': fillDescriptor} 11281 11282 computeCondition = dedent(""" 11283 bool hasOnProto; 11284 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) { 11285 return false; 11286 } 11287 callNamedGetter = !hasOnProto; 11288 """) 11289 if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): 11290 computeCondition = fill( 11291 """ 11292 if (!isXray) { 11293 callNamedGetter = true; 11294 } else { 11295 $*{hasOnProto} 11296 } 11297 """, 11298 hasOnProto=computeCondition) 11299 11300 outerCondition = "!ignoreNamedProps" 11301 if self.descriptor.supportsIndexedProperties(): 11302 outerCondition = "!IsArrayIndex(index) && " + outerCondition 11303 11304 namedGetCode = CGProxyNamedGetter(self.descriptor, 11305 templateValues).define() 11306 namedGet = fill(""" 11307 bool callNamedGetter = false; 11308 if (${outerCondition}) { 11309 $*{computeCondition} 11310 } 11311 if (callNamedGetter) { 11312 $*{namedGetCode} 11313 } 11314 """, 11315 outerCondition=outerCondition, 11316 computeCondition=computeCondition, 11317 namedGetCode=namedGetCode) 11318 namedGet += "\n" 11319 else: 11320 namedGet = "" 11321 11322 return fill( 11323 """ 11324 bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy); 11325 $*{getIndexed} 11326 JS::Rooted<JSObject*> expando(cx); 11327 if (!isXray && (expando = GetExpandoObject(proxy))) { 11328 if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc)) { 11329 return false; 11330 } 11331 if (desc.object()) { 11332 // Pretend the property lives on the wrapper. 11333 desc.object().set(proxy); 11334 return true; 11335 } 11336 } 11337 11338 $*{namedGet} 11339 desc.object().set(nullptr); 11340 return true; 11341 """, 11342 getIndexed=getIndexed, 11343 namedGet=namedGet) 11344 11345 11346class CGDOMJSProxyHandler_defineProperty(ClassMethod): 11347 def __init__(self, descriptor): 11348 # The usual convention is to name the ObjectOpResult out-parameter 11349 # `result`, but that name is a bit overloaded around here. 11350 args = [Argument('JSContext*', 'cx'), 11351 Argument('JS::Handle<JSObject*>', 'proxy'), 11352 Argument('JS::Handle<jsid>', 'id'), 11353 Argument('JS::Handle<JS::PropertyDescriptor>', 'desc'), 11354 Argument('JS::ObjectOpResult&', 'opresult'), 11355 Argument('bool*', 'defined')] 11356 ClassMethod.__init__(self, "defineProperty", "bool", args, virtual=True, override=True, const=True) 11357 self.descriptor = descriptor 11358 11359 def getBody(self): 11360 set = "" 11361 11362 indexedSetter = self.descriptor.operations['IndexedSetter'] 11363 if indexedSetter: 11364 if self.descriptor.operations['IndexedCreator'] is not indexedSetter: 11365 raise TypeError("Can't handle creator that's different from the setter") 11366 set += fill( 11367 """ 11368 uint32_t index = GetArrayIndexFromId(cx, id); 11369 if (IsArrayIndex(index)) { 11370 *defined = true; 11371 $*{callSetter} 11372 return opresult.succeed(); 11373 } 11374 """, 11375 callSetter=CGProxyIndexedSetter(self.descriptor).define()) 11376 elif self.descriptor.supportsIndexedProperties(): 11377 # We allow untrusted content to prevent Xrays from setting a 11378 # property if that property is an indexed property and we have no 11379 # indexed setter. That's how the object would normally behave if 11380 # you tried to set the property on it. That means we don't need to 11381 # do anything special for Xrays here. 11382 set += dedent( 11383 """ 11384 if (IsArrayIndex(GetArrayIndexFromId(cx, id))) { 11385 *defined = true; 11386 return opresult.failNoIndexedSetter(); 11387 } 11388 """) 11389 11390 namedSetter = self.descriptor.operations['NamedSetter'] 11391 if namedSetter: 11392 if self.descriptor.hasUnforgeableMembers: 11393 raise TypeError("Can't handle a named setter on an interface " 11394 "that has unforgeables. Figure out how that " 11395 "should work!") 11396 if self.descriptor.operations['NamedCreator'] is not namedSetter: 11397 raise TypeError("Can't handle creator that's different from the setter") 11398 # If we support indexed properties, we won't get down here for 11399 # indices, so we can just do our setter unconditionally here. 11400 set += fill( 11401 """ 11402 *defined = true; 11403 $*{callSetter} 11404 11405 return opresult.succeed(); 11406 """, 11407 callSetter=CGProxyNamedSetter(self.descriptor).define()) 11408 else: 11409 # We allow untrusted content to prevent Xrays from setting a 11410 # property if that property is already a named property on the 11411 # object and we have no named setter. That's how the object would 11412 # normally behave if you tried to set the property on it. That 11413 # means we don't need to do anything special for Xrays here. 11414 if self.descriptor.supportsNamedProperties(): 11415 set += fill( 11416 """ 11417 bool found = false; 11418 $*{presenceChecker} 11419 11420 if (found) { 11421 *defined = true; 11422 return opresult.failNoNamedSetter(); 11423 } 11424 """, 11425 presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define()) 11426 set += ("return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n" % 11427 ", ".join(a.name for a in self.args)) 11428 return set 11429 11430 11431def getDeleterBody(descriptor, type, foundVar=None): 11432 """ 11433 type should be "Named" or "Indexed" 11434 11435 The possible outcomes: 11436 - an error happened (the emitted code returns false) 11437 - own property not found (foundVar=false, deleteSucceeded=true) 11438 - own property found and deleted (foundVar=true, deleteSucceeded=true) 11439 - own property found but can't be deleted (foundVar=true, deleteSucceeded=false) 11440 """ 11441 assert type in ("Named", "Indexed") 11442 deleter = descriptor.operations[type + 'Deleter'] 11443 if deleter: 11444 assert type == "Named" 11445 assert foundVar is not None 11446 if descriptor.hasUnforgeableMembers: 11447 raise TypeError("Can't handle a deleter on an interface " 11448 "that has unforgeables. Figure out how " 11449 "that should work!") 11450 # See if the deleter method is fallible. 11451 t = deleter.signatures()[0][0] 11452 if t.isPrimitive() and not t.nullable() and t.tag() == IDLType.Tags.bool: 11453 # The deleter method has a boolean return value. When a 11454 # property is found, the return value indicates whether it 11455 # was successfully deleted. 11456 setDS = fill( 11457 """ 11458 if (!${foundVar}) { 11459 deleteSucceeded = true; 11460 } 11461 """, 11462 foundVar=foundVar) 11463 else: 11464 # No boolean return value: if a property is found, 11465 # deleting it always succeeds. 11466 setDS = "deleteSucceeded = true;\n" 11467 11468 body = (CGProxyNamedDeleter(descriptor, 11469 resultVar="deleteSucceeded", 11470 foundVar=foundVar).define() + 11471 setDS) 11472 elif getattr(descriptor, "supports%sProperties" % type)(): 11473 presenceCheckerClass = globals()["CGProxy%sPresenceChecker" % type] 11474 foundDecl = "" 11475 if foundVar is None: 11476 foundVar = "found" 11477 foundDecl = "bool found = false;\n" 11478 body = fill( 11479 """ 11480 $*{foundDecl} 11481 $*{presenceChecker} 11482 deleteSucceeded = !${foundVar}; 11483 """, 11484 foundDecl=foundDecl, 11485 presenceChecker=presenceCheckerClass(descriptor, foundVar=foundVar).define(), 11486 foundVar=foundVar) 11487 else: 11488 body = None 11489 return body 11490 11491 11492class CGDeleteNamedProperty(CGAbstractStaticMethod): 11493 def __init__(self, descriptor): 11494 args = [Argument('JSContext*', 'cx'), 11495 Argument('JS::Handle<JSObject*>', 'xray'), 11496 Argument('JS::Handle<JSObject*>', 'proxy'), 11497 Argument('JS::Handle<jsid>', 'id'), 11498 Argument('JS::ObjectOpResult&', 'opresult')] 11499 CGAbstractStaticMethod.__init__(self, descriptor, "DeleteNamedProperty", 11500 "bool", args) 11501 11502 def definition_body(self): 11503 return fill( 11504 """ 11505 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(xray)); 11506 MOZ_ASSERT(js::IsProxy(proxy)); 11507 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy)); 11508 JSAutoCompartment ac(cx, proxy); 11509 bool deleteSucceeded; 11510 bool found = false; 11511 $*{namedBody} 11512 if (!found || deleteSucceeded) { 11513 return opresult.succeed(); 11514 } 11515 return opresult.failCantDelete(); 11516 """, 11517 namedBody=getDeleterBody(self.descriptor, "Named", foundVar="found")) 11518 11519 11520class CGDOMJSProxyHandler_delete(ClassMethod): 11521 def __init__(self, descriptor): 11522 args = [Argument('JSContext*', 'cx'), 11523 Argument('JS::Handle<JSObject*>', 'proxy'), 11524 Argument('JS::Handle<jsid>', 'id'), 11525 Argument('JS::ObjectOpResult&', 'opresult')] 11526 ClassMethod.__init__(self, "delete_", "bool", args, 11527 virtual=True, override=True, const=True) 11528 self.descriptor = descriptor 11529 11530 def getBody(self): 11531 delete = dedent(""" 11532 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), 11533 "Should not have a XrayWrapper here"); 11534 11535 """) 11536 11537 indexedBody = getDeleterBody(self.descriptor, "Indexed") 11538 if indexedBody is not None: 11539 delete += fill( 11540 """ 11541 uint32_t index = GetArrayIndexFromId(cx, id); 11542 if (IsArrayIndex(index)) { 11543 bool deleteSucceeded; 11544 $*{indexedBody} 11545 return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete(); 11546 } 11547 """, 11548 indexedBody=indexedBody) 11549 11550 namedBody = getDeleterBody(self.descriptor, "Named", foundVar="found") 11551 if namedBody is not None: 11552 delete += dedent( 11553 """ 11554 // Try named delete only if the named property visibility 11555 // algorithm says the property is visible. 11556 bool tryNamedDelete = true; 11557 { // Scope for expando 11558 JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy)); 11559 if (expando) { 11560 bool hasProp; 11561 if (!JS_HasPropertyById(cx, expando, id, &hasProp)) { 11562 return false; 11563 } 11564 tryNamedDelete = !hasProp; 11565 } 11566 } 11567 """) 11568 11569 if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): 11570 delete += dedent( 11571 """ 11572 if (tryNamedDelete) { 11573 bool hasOnProto; 11574 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) { 11575 return false; 11576 } 11577 tryNamedDelete = !hasOnProto; 11578 } 11579 """) 11580 11581 # We always return above for an index id in the case when we support 11582 # indexed properties, so we can just treat the id as a name 11583 # unconditionally here. 11584 delete += fill( 11585 """ 11586 if (tryNamedDelete) { 11587 bool found = false; 11588 bool deleteSucceeded; 11589 $*{namedBody} 11590 if (found) { 11591 return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete(); 11592 } 11593 } 11594 """, 11595 namedBody=namedBody) 11596 11597 delete += dedent(""" 11598 11599 return dom::DOMProxyHandler::delete_(cx, proxy, id, opresult); 11600 """) 11601 11602 return delete 11603 11604 11605class CGDOMJSProxyHandler_ownPropNames(ClassMethod): 11606 def __init__(self, descriptor, ): 11607 args = [Argument('JSContext*', 'cx'), 11608 Argument('JS::Handle<JSObject*>', 'proxy'), 11609 Argument('unsigned', 'flags'), 11610 Argument('JS::AutoIdVector&', 'props')] 11611 ClassMethod.__init__(self, "ownPropNames", "bool", args, 11612 virtual=True, override=True, const=True) 11613 self.descriptor = descriptor 11614 11615 def getBody(self): 11616 # Per spec, we do indices, then named props, then everything else 11617 if self.descriptor.supportsIndexedProperties(): 11618 addIndices = dedent(""" 11619 11620 uint32_t length = UnwrapProxy(proxy)->Length(); 11621 MOZ_ASSERT(int32_t(length) >= 0); 11622 for (int32_t i = 0; i < int32_t(length); ++i) { 11623 if (!props.append(INT_TO_JSID(i))) { 11624 return false; 11625 } 11626 } 11627 """) 11628 else: 11629 addIndices = "" 11630 11631 if self.descriptor.supportsNamedProperties(): 11632 if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): 11633 shadow = "!isXray" 11634 else: 11635 shadow = "false" 11636 addNames = fill( 11637 """ 11638 nsTArray<nsString> names; 11639 UnwrapProxy(proxy)->GetSupportedNames(names); 11640 if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) { 11641 return false; 11642 } 11643 """, 11644 shadow=shadow) 11645 if not self.descriptor.namedPropertiesEnumerable: 11646 addNames = CGIfWrapper(CGGeneric(addNames), 11647 "flags & JSITER_HIDDEN").define() 11648 addNames = "\n" + addNames 11649 else: 11650 addNames = "" 11651 11652 return fill( 11653 """ 11654 bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy); 11655 $*{addIndices} 11656 $*{addNames} 11657 11658 JS::Rooted<JSObject*> expando(cx); 11659 if (!isXray && (expando = DOMProxyHandler::GetExpandoObject(proxy)) && 11660 !js::GetPropertyKeys(cx, expando, flags, &props)) { 11661 return false; 11662 } 11663 11664 return true; 11665 """, 11666 addIndices=addIndices, 11667 addNames=addNames) 11668 11669 11670class CGDOMJSProxyHandler_hasOwn(ClassMethod): 11671 def __init__(self, descriptor): 11672 args = [Argument('JSContext*', 'cx'), 11673 Argument('JS::Handle<JSObject*>', 'proxy'), 11674 Argument('JS::Handle<jsid>', 'id'), 11675 Argument('bool*', 'bp')] 11676 ClassMethod.__init__(self, "hasOwn", "bool", args, 11677 virtual=True, override=True, const=True) 11678 self.descriptor = descriptor 11679 11680 def getBody(self): 11681 if self.descriptor.supportsIndexedProperties(): 11682 indexed = fill( 11683 """ 11684 uint32_t index = GetArrayIndexFromId(cx, id); 11685 if (IsArrayIndex(index)) { 11686 bool found = false; 11687 $*{presenceChecker} 11688 11689 *bp = found; 11690 return true; 11691 } 11692 11693 """, 11694 presenceChecker=CGProxyIndexedPresenceChecker(self.descriptor, foundVar="found").define()) 11695 else: 11696 indexed = "" 11697 11698 if self.descriptor.supportsNamedProperties(): 11699 # If we support indexed properties we always return above for index 11700 # property names, so no need to check for those here. 11701 named = fill( 11702 """ 11703 bool found = false; 11704 $*{presenceChecker} 11705 11706 *bp = found; 11707 """, 11708 presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define()) 11709 if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): 11710 named = fill( 11711 """ 11712 bool hasOnProto; 11713 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) { 11714 return false; 11715 } 11716 if (!hasOnProto) { 11717 $*{protoLacksProperty} 11718 return true; 11719 } 11720 """, 11721 protoLacksProperty=named) 11722 named += "*bp = false;\n" 11723 else: 11724 named += "\n" 11725 else: 11726 named = "*bp = false;\n" 11727 11728 return fill( 11729 """ 11730 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), 11731 "Should not have a XrayWrapper here"); 11732 11733 $*{indexed} 11734 11735 JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy)); 11736 if (expando) { 11737 bool b = true; 11738 bool ok = JS_HasPropertyById(cx, expando, id, &b); 11739 *bp = !!b; 11740 if (!ok || *bp) { 11741 return ok; 11742 } 11743 } 11744 11745 $*{named} 11746 return true; 11747 """, 11748 indexed=indexed, 11749 named=named) 11750 11751 11752class CGDOMJSProxyHandler_get(ClassMethod): 11753 def __init__(self, descriptor): 11754 args = [Argument('JSContext*', 'cx'), 11755 Argument('JS::Handle<JSObject*>', 'proxy'), 11756 Argument('JS::Handle<JS::Value>', 'receiver'), 11757 Argument('JS::Handle<jsid>', 'id'), 11758 Argument('JS::MutableHandle<JS::Value>', 'vp')] 11759 ClassMethod.__init__(self, "get", "bool", args, 11760 virtual=True, override=True, const=True) 11761 self.descriptor = descriptor 11762 11763 def getBody(self): 11764 getUnforgeableOrExpando = dedent(""" 11765 { // Scope for expando 11766 JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy)); 11767 if (expando) { 11768 bool hasProp; 11769 if (!JS_HasPropertyById(cx, expando, id, &hasProp)) { 11770 return false; 11771 } 11772 11773 if (hasProp) { 11774 // Forward the get to the expando object, but our receiver is whatever our 11775 // receiver is. 11776 return JS_ForwardGetPropertyTo(cx, expando, id, receiver, vp); 11777 } 11778 } 11779 } 11780 """) 11781 11782 templateValues = {'jsvalRef': 'vp', 'jsvalHandle': 'vp', 'obj': 'proxy'} 11783 11784 if self.descriptor.supportsIndexedProperties(): 11785 getIndexedOrExpando = fill( 11786 """ 11787 uint32_t index = GetArrayIndexFromId(cx, id); 11788 if (IsArrayIndex(index)) { 11789 $*{callGetter} 11790 // Even if we don't have this index, we don't forward the 11791 // get on to our expando object. 11792 } else { 11793 $*{getUnforgeableOrExpando} 11794 } 11795 """, 11796 callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define(), 11797 getUnforgeableOrExpando=getUnforgeableOrExpando) 11798 else: 11799 getIndexedOrExpando = getUnforgeableOrExpando 11800 11801 if self.descriptor.supportsNamedProperties(): 11802 getNamed = CGProxyNamedGetter(self.descriptor, templateValues) 11803 if self.descriptor.supportsIndexedProperties(): 11804 getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)") 11805 getNamed = getNamed.define() + "\n" 11806 else: 11807 getNamed = "" 11808 11809 getOnPrototype = dedent(""" 11810 bool foundOnPrototype; 11811 if (!GetPropertyOnPrototype(cx, proxy, receiver, id, &foundOnPrototype, vp)) { 11812 return false; 11813 } 11814 11815 if (foundOnPrototype) { 11816 return true; 11817 } 11818 11819 """) 11820 if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): 11821 getNamed = getNamed + getOnPrototype 11822 else: 11823 getNamed = getOnPrototype + getNamed 11824 11825 return fill( 11826 """ 11827 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), 11828 "Should not have a XrayWrapper here"); 11829 11830 $*{indexedOrExpando} 11831 11832 $*{named} 11833 vp.setUndefined(); 11834 return true; 11835 """, 11836 indexedOrExpando=getIndexedOrExpando, 11837 named=getNamed) 11838 11839 11840class CGDOMJSProxyHandler_setCustom(ClassMethod): 11841 def __init__(self, descriptor): 11842 args = [Argument('JSContext*', 'cx'), 11843 Argument('JS::Handle<JSObject*>', 'proxy'), 11844 Argument('JS::Handle<jsid>', 'id'), 11845 Argument('JS::Handle<JS::Value>', 'v'), 11846 Argument('bool*', 'done')] 11847 ClassMethod.__init__(self, "setCustom", "bool", args, virtual=True, override=True, const=True) 11848 self.descriptor = descriptor 11849 11850 def getBody(self): 11851 assertion = ("MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n" 11852 ' "Should not have a XrayWrapper here");\n') 11853 11854 # Correctness first. If we have a NamedSetter and [OverrideBuiltins], 11855 # always call the NamedSetter and never do anything else. 11856 namedSetter = self.descriptor.operations['NamedSetter'] 11857 if (namedSetter is not None and 11858 self.descriptor.interface.getExtendedAttribute('OverrideBuiltins')): 11859 # Check assumptions. 11860 if self.descriptor.supportsIndexedProperties(): 11861 raise ValueError("In interface " + self.descriptor.name + ": " + 11862 "Can't cope with [OverrideBuiltins] and an indexed getter") 11863 if self.descriptor.operations['NamedCreator'] is not namedSetter: 11864 raise ValueError("In interface " + self.descriptor.name + ": " + 11865 "Can't cope with named setter that is not also a named creator") 11866 if self.descriptor.hasUnforgeableMembers: 11867 raise ValueError("In interface " + self.descriptor.name + ": " + 11868 "Can't cope with [OverrideBuiltins] and unforgeable members") 11869 11870 callSetter = CGProxyNamedSetter(self.descriptor, argumentHandleValue="v") 11871 return (assertion + 11872 callSetter.define() + 11873 "*done = true;\n" 11874 "return true;\n") 11875 11876 # As an optimization, if we are going to call an IndexedSetter, go 11877 # ahead and call it and have done. 11878 indexedSetter = self.descriptor.operations['IndexedSetter'] 11879 if indexedSetter is not None: 11880 if self.descriptor.operations['IndexedCreator'] is not indexedSetter: 11881 raise ValueError("In interface " + self.descriptor.name + ": " + 11882 "Can't cope with indexed setter that is not " + 11883 "also an indexed creator") 11884 setIndexed = fill( 11885 """ 11886 uint32_t index = GetArrayIndexFromId(cx, id); 11887 if (IsArrayIndex(index)) { 11888 $*{callSetter} 11889 *done = true; 11890 return true; 11891 } 11892 11893 """, 11894 callSetter=CGProxyIndexedSetter(self.descriptor, 11895 argumentHandleValue="v").define()) 11896 else: 11897 setIndexed = "" 11898 11899 return (assertion + 11900 setIndexed + 11901 "*done = false;\n" 11902 "return true;\n") 11903 11904 11905class CGDOMJSProxyHandler_className(ClassMethod): 11906 def __init__(self, descriptor): 11907 args = [Argument('JSContext*', 'cx'), 11908 Argument('JS::Handle<JSObject*>', 'proxy')] 11909 ClassMethod.__init__(self, "className", "const char*", args, 11910 virtual=True, override=True, const=True) 11911 self.descriptor = descriptor 11912 11913 def getBody(self): 11914 return 'return "%s";\n' % self.descriptor.name 11915 11916 11917class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod): 11918 def __init__(self, descriptor): 11919 args = [Argument('const JS::Value&', 'priv')] 11920 ClassMethod.__init__(self, "finalizeInBackground", "bool", args, 11921 virtual=True, override=True, const=True) 11922 self.descriptor = descriptor 11923 11924 def getBody(self): 11925 return "return false;\n" 11926 11927 11928class CGDOMJSProxyHandler_finalize(ClassMethod): 11929 def __init__(self, descriptor): 11930 args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')] 11931 ClassMethod.__init__(self, "finalize", "void", args, 11932 virtual=True, override=True, const=True) 11933 self.descriptor = descriptor 11934 11935 def getBody(self): 11936 return (("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(proxy);\n" % 11937 (self.descriptor.nativeType, self.descriptor.nativeType)) + 11938 finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define()) 11939 11940 11941class CGDOMJSProxyHandler_getElements(ClassMethod): 11942 def __init__(self, descriptor): 11943 assert descriptor.supportsIndexedProperties() 11944 11945 args = [Argument('JSContext*', 'cx'), 11946 Argument('JS::Handle<JSObject*>', 'proxy'), 11947 Argument('uint32_t', 'begin'), 11948 Argument('uint32_t', 'end'), 11949 Argument('js::ElementAdder*', 'adder')] 11950 ClassMethod.__init__(self, "getElements", "bool", args, virtual=True, override=True, const=True) 11951 self.descriptor = descriptor 11952 11953 def getBody(self): 11954 # Just like ownPropertyKeys we'll assume that we have no holes, so 11955 # we have all properties from 0 to length. If that ever changes 11956 # (unlikely), we'll need to do something a bit more clever with how we 11957 # forward on to our ancestor. 11958 11959 templateValues = { 11960 'jsvalRef': 'temp', 11961 'jsvalHandle': '&temp', 11962 'obj': 'proxy', 11963 'successCode': ("if (!adder->append(cx, temp)) return false;\n" 11964 "continue;\n") 11965 } 11966 get = CGProxyIndexedGetter(self.descriptor, templateValues, False, False).define() 11967 11968 return fill( 11969 """ 11970 JS::Rooted<JS::Value> temp(cx); 11971 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), 11972 "Should not have a XrayWrapper here"); 11973 11974 ${nativeType}* self = UnwrapProxy(proxy); 11975 uint32_t length = self->Length(); 11976 // Compute the end of the indices we'll get ourselves 11977 uint32_t ourEnd = std::max(begin, std::min(end, length)); 11978 11979 for (uint32_t index = begin; index < ourEnd; ++index) { 11980 $*{get} 11981 } 11982 11983 if (end > ourEnd) { 11984 JS::Rooted<JSObject*> proto(cx); 11985 if (!js::GetObjectProto(cx, proxy, &proto)) { 11986 return false; 11987 } 11988 return js::GetElementsWithAdder(cx, proto, proxy, ourEnd, end, adder); 11989 } 11990 11991 return true; 11992 """, 11993 nativeType=self.descriptor.nativeType, 11994 get=get) 11995 11996 11997class CGDOMJSProxyHandler_getInstance(ClassMethod): 11998 def __init__(self): 11999 ClassMethod.__init__(self, "getInstance", "const DOMProxyHandler*", [], static=True) 12000 12001 def getBody(self): 12002 return dedent(""" 12003 static const DOMProxyHandler instance; 12004 return &instance; 12005 """) 12006 12007 12008class CGDOMJSProxyHandler_getPrototypeIfOrdinary(ClassMethod): 12009 def __init__(self): 12010 args = [Argument('JSContext*', 'cx'), 12011 Argument('JS::Handle<JSObject*>', 'proxy'), 12012 Argument('bool*', 'isOrdinary'), 12013 Argument('JS::MutableHandle<JSObject*>', 'proto')] 12014 12015 ClassMethod.__init__(self, "getPrototypeIfOrdinary", "bool", args, 12016 virtual=True, override=True, const=True) 12017 12018 def getBody(self): 12019 return dedent(""" 12020 *isOrdinary = false; 12021 return true; 12022 """) 12023 12024 12025class CGDOMJSProxyHandler_call(ClassMethod): 12026 def __init__(self): 12027 args = [Argument('JSContext*', 'cx'), 12028 Argument('JS::Handle<JSObject*>', 'proxy'), 12029 Argument('const JS::CallArgs&', 'args')] 12030 12031 ClassMethod.__init__(self, "call", "bool", args, virtual=True, override=True, const=True) 12032 12033 def getBody(self): 12034 return fill( 12035 """ 12036 return js::ForwardToNative(cx, ${legacyCaller}, args); 12037 """, 12038 legacyCaller=LEGACYCALLER_HOOK_NAME) 12039 12040 12041class CGDOMJSProxyHandler_isCallable(ClassMethod): 12042 def __init__(self): 12043 ClassMethod.__init__(self, "isCallable", "bool", 12044 [Argument('JSObject*', 'obj')], 12045 virtual=True, override=True, const=True) 12046 12047 def getBody(self): 12048 return dedent(""" 12049 return true; 12050 """) 12051 12052 12053class CGDOMJSProxyHandler(CGClass): 12054 def __init__(self, descriptor): 12055 assert (descriptor.supportsIndexedProperties() or 12056 descriptor.supportsNamedProperties() or 12057 descriptor.hasNonOrdinaryGetPrototypeOf()) 12058 methods = [CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor), 12059 CGDOMJSProxyHandler_defineProperty(descriptor), 12060 ClassUsingDeclaration("mozilla::dom::DOMProxyHandler", 12061 "defineProperty"), 12062 CGDOMJSProxyHandler_ownPropNames(descriptor), 12063 CGDOMJSProxyHandler_hasOwn(descriptor), 12064 CGDOMJSProxyHandler_get(descriptor), 12065 CGDOMJSProxyHandler_className(descriptor), 12066 CGDOMJSProxyHandler_finalizeInBackground(descriptor), 12067 CGDOMJSProxyHandler_finalize(descriptor), 12068 CGDOMJSProxyHandler_getInstance(), 12069 CGDOMJSProxyHandler_delete(descriptor)] 12070 constructors = [ 12071 ClassConstructor( 12072 [], 12073 constexpr=True, 12074 visibility="public", 12075 explicit=True) 12076 ] 12077 12078 if descriptor.supportsIndexedProperties(): 12079 methods.append(CGDOMJSProxyHandler_getElements(descriptor)) 12080 if (descriptor.operations['IndexedSetter'] is not None or 12081 (descriptor.operations['NamedSetter'] is not None and 12082 descriptor.interface.getExtendedAttribute('OverrideBuiltins'))): 12083 methods.append(CGDOMJSProxyHandler_setCustom(descriptor)) 12084 if descriptor.hasNonOrdinaryGetPrototypeOf(): 12085 methods.append(CGDOMJSProxyHandler_getPrototypeIfOrdinary()) 12086 if descriptor.operations['LegacyCaller']: 12087 methods.append(CGDOMJSProxyHandler_call()) 12088 methods.append(CGDOMJSProxyHandler_isCallable()) 12089 12090 if descriptor.interface.getExtendedAttribute('OverrideBuiltins'): 12091 parentClass = 'ShadowingDOMProxyHandler' 12092 else: 12093 parentClass = 'mozilla::dom::DOMProxyHandler' 12094 12095 CGClass.__init__(self, 'DOMProxyHandler', 12096 bases=[ClassBase(parentClass)], 12097 constructors=constructors, 12098 methods=methods) 12099 12100 12101class CGDOMJSProxyHandlerDeclarer(CGThing): 12102 """ 12103 A class for declaring a DOMProxyHandler. 12104 """ 12105 def __init__(self, handlerThing): 12106 self.handlerThing = handlerThing 12107 12108 def declare(self): 12109 # Our class declaration should happen when we're defining 12110 return "" 12111 12112 def define(self): 12113 return self.handlerThing.declare() 12114 12115 12116class CGDOMJSProxyHandlerDefiner(CGThing): 12117 """ 12118 A class for defining a DOMProxyHandler. 12119 """ 12120 def __init__(self, handlerThing): 12121 self.handlerThing = handlerThing 12122 12123 def declare(self): 12124 return "" 12125 12126 def define(self): 12127 return self.handlerThing.define() 12128 12129 12130def stripTrailingWhitespace(text): 12131 tail = '\n' if text.endswith('\n') else '' 12132 lines = text.splitlines() 12133 return '\n'.join(line.rstrip() for line in lines) + tail 12134 12135 12136class MemberProperties: 12137 def __init__(self): 12138 self.isGenericMethod = False 12139 self.isCrossOriginMethod = False 12140 self.isPromiseReturningMethod = False 12141 self.isGenericGetter = False 12142 self.isLenientGetter = False 12143 self.isCrossOriginGetter = False 12144 self.isGenericSetter = False 12145 self.isLenientSetter = False 12146 self.isCrossOriginSetter = False 12147 self.isJsonifier = False 12148 12149 12150def memberProperties(m, descriptor): 12151 props = MemberProperties() 12152 if m.isMethod(): 12153 if m == descriptor.operations['Jsonifier']: 12154 props.isGenericMethod = descriptor.needsSpecialGenericOps() 12155 props.isJsonifier = True 12156 elif (not m.isIdentifierLess() or m == descriptor.operations['Stringifier']): 12157 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject(): 12158 if descriptor.needsSpecialGenericOps(): 12159 if m.returnsPromise(): 12160 props.isPromiseReturningMethod = True 12161 else: 12162 props.isGenericMethod = True 12163 if m.getExtendedAttribute("CrossOriginCallable"): 12164 props.isCrossOriginMethod = True 12165 elif m.isAttr(): 12166 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject(): 12167 if m.hasLenientThis(): 12168 props.isLenientGetter = True 12169 elif m.getExtendedAttribute("CrossOriginReadable"): 12170 props.isCrossOriginGetter = True 12171 elif descriptor.needsSpecialGenericOps(): 12172 props.isGenericGetter = True 12173 if not m.readonly: 12174 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject(): 12175 if m.hasLenientThis(): 12176 props.isLenientSetter = True 12177 elif IsCrossOriginWritable(m, descriptor): 12178 props.isCrossOriginSetter = True 12179 elif descriptor.needsSpecialGenericOps(): 12180 props.isGenericSetter = True 12181 elif m.getExtendedAttribute("PutForwards"): 12182 if IsCrossOriginWritable(m, descriptor): 12183 props.isCrossOriginSetter = True 12184 elif descriptor.needsSpecialGenericOps(): 12185 props.isGenericSetter = True 12186 elif (m.getExtendedAttribute("Replaceable") or 12187 m.getExtendedAttribute("LenientSetter")): 12188 if descriptor.needsSpecialGenericOps(): 12189 props.isGenericSetter = True 12190 12191 return props 12192 12193 12194class CGDescriptor(CGThing): 12195 def __init__(self, descriptor): 12196 CGThing.__init__(self) 12197 12198 assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject() 12199 12200 self._deps = descriptor.interface.getDeps() 12201 12202 cgThings = [] 12203 cgThings.append(CGGeneric(declare="typedef %s NativeType;\n" % 12204 descriptor.nativeType)) 12205 parent = descriptor.interface.parent 12206 if parent: 12207 cgThings.append(CGGeneric("static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n" 12208 " \"Can't inherit from an interface with a different ownership model.\");\n" % 12209 toBindingNamespace(descriptor.parentPrototypeName))) 12210 12211 # These are set to true if at least one non-static 12212 # method/getter/setter or jsonifier exist on the interface. 12213 (hasMethod, hasGetter, hasLenientGetter, hasSetter, hasLenientSetter, 12214 hasPromiseReturningMethod) = False, False, False, False, False, False 12215 jsonifierMethod = None 12216 crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set() 12217 unscopableNames = list() 12218 for n in descriptor.interface.namedConstructors: 12219 cgThings.append(CGClassConstructor(descriptor, n, 12220 NamedConstructorName(n))) 12221 for m in descriptor.interface.members: 12222 if m.isMethod() and m.identifier.name == 'queryInterface': 12223 continue 12224 12225 props = memberProperties(m, descriptor) 12226 12227 if m.isMethod(): 12228 if m.getExtendedAttribute("Unscopable"): 12229 assert not m.isStatic() 12230 unscopableNames.append(m.identifier.name) 12231 if props.isJsonifier: 12232 jsonifierMethod = m 12233 elif not m.isIdentifierLess() or m == descriptor.operations['Stringifier']: 12234 if m.isStatic(): 12235 assert descriptor.interface.hasInterfaceObject() 12236 cgThings.append(CGStaticMethod(descriptor, m)) 12237 if m.returnsPromise(): 12238 cgThings.append(CGStaticMethodJitinfo(m)) 12239 elif descriptor.interface.hasInterfacePrototypeObject(): 12240 specializedMethod = CGSpecializedMethod(descriptor, m) 12241 cgThings.append(specializedMethod) 12242 if m.returnsPromise(): 12243 cgThings.append(CGMethodPromiseWrapper(descriptor, specializedMethod)) 12244 cgThings.append(CGMemberJITInfo(descriptor, m)) 12245 if props.isCrossOriginMethod: 12246 crossOriginMethods.add(m.identifier.name) 12247 # If we've hit the maplike/setlike member itself, go ahead and 12248 # generate its convenience functions. 12249 elif m.isMaplikeOrSetlike(): 12250 cgThings.append(CGMaplikeOrSetlikeHelperGenerator(descriptor, m)) 12251 elif m.isAttr(): 12252 if m.stringifier: 12253 raise TypeError("Stringifier attributes not supported yet. " 12254 "See bug 824857.\n" 12255 "%s" % m.location) 12256 if m.getExtendedAttribute("Unscopable"): 12257 assert not m.isStatic() 12258 unscopableNames.append(m.identifier.name) 12259 if m.isStatic(): 12260 assert descriptor.interface.hasInterfaceObject() 12261 cgThings.append(CGStaticGetter(descriptor, m)) 12262 elif descriptor.interface.hasInterfacePrototypeObject(): 12263 if isNonExposedNavigatorObjectGetter(m, descriptor): 12264 continue 12265 cgThings.append(CGSpecializedGetter(descriptor, m)) 12266 if props.isCrossOriginGetter: 12267 crossOriginGetters.add(m.identifier.name) 12268 if not m.readonly: 12269 if m.isStatic(): 12270 assert descriptor.interface.hasInterfaceObject() 12271 cgThings.append(CGStaticSetter(descriptor, m)) 12272 elif descriptor.interface.hasInterfacePrototypeObject(): 12273 cgThings.append(CGSpecializedSetter(descriptor, m)) 12274 if props.isCrossOriginSetter: 12275 crossOriginSetters.add(m.identifier.name) 12276 elif m.getExtendedAttribute("PutForwards"): 12277 cgThings.append(CGSpecializedForwardingSetter(descriptor, m)) 12278 if props.isCrossOriginSetter: 12279 crossOriginSetters.add(m.identifier.name) 12280 elif m.getExtendedAttribute("Replaceable"): 12281 cgThings.append(CGSpecializedReplaceableSetter(descriptor, m)) 12282 elif m.getExtendedAttribute("LenientSetter"): 12283 cgThings.append(CGSpecializedLenientSetter(descriptor, m)) 12284 if (not m.isStatic() and 12285 descriptor.interface.hasInterfacePrototypeObject()): 12286 cgThings.append(CGMemberJITInfo(descriptor, m)) 12287 12288 hasMethod = hasMethod or props.isGenericMethod 12289 hasPromiseReturningMethod = (hasPromiseReturningMethod or 12290 props.isPromiseReturningMethod) 12291 hasGetter = hasGetter or props.isGenericGetter 12292 hasLenientGetter = hasLenientGetter or props.isLenientGetter 12293 hasSetter = hasSetter or props.isGenericSetter 12294 hasLenientSetter = hasLenientSetter or props.isLenientSetter 12295 12296 if jsonifierMethod: 12297 cgThings.append(CGJsonifyAttributesMethod(descriptor)) 12298 cgThings.append(CGJsonifierMethod(descriptor, jsonifierMethod)) 12299 cgThings.append(CGMemberJITInfo(descriptor, jsonifierMethod)) 12300 if hasMethod: 12301 cgThings.append(CGGenericMethod(descriptor)) 12302 if hasPromiseReturningMethod: 12303 cgThings.append(CGGenericPromiseReturningMethod(descriptor)) 12304 if len(crossOriginMethods): 12305 cgThings.append(CGGenericMethod(descriptor, 12306 allowCrossOriginThis=True)) 12307 if hasGetter: 12308 cgThings.append(CGGenericGetter(descriptor)) 12309 if hasLenientGetter: 12310 cgThings.append(CGGenericGetter(descriptor, lenientThis=True)) 12311 if len(crossOriginGetters): 12312 cgThings.append(CGGenericGetter(descriptor, 12313 allowCrossOriginThis=True)) 12314 if hasSetter: 12315 cgThings.append(CGGenericSetter(descriptor)) 12316 if hasLenientSetter: 12317 cgThings.append(CGGenericSetter(descriptor, lenientThis=True)) 12318 if len(crossOriginSetters): 12319 cgThings.append(CGGenericSetter(descriptor, 12320 allowCrossOriginThis=True)) 12321 12322 if descriptor.interface.isNavigatorProperty(): 12323 cgThings.append(CGConstructNavigatorObject(descriptor)) 12324 12325 if descriptor.concrete and not descriptor.proxy: 12326 if wantsAddProperty(descriptor): 12327 cgThings.append(CGAddPropertyHook(descriptor)) 12328 12329 # Always have a finalize hook, regardless of whether the class 12330 # wants a custom hook. 12331 cgThings.append(CGClassFinalizeHook(descriptor)) 12332 12333 if descriptor.concrete and descriptor.wrapperCache: 12334 cgThings.append(CGClassObjectMovedHook(descriptor)) 12335 12336 # Generate the _ClearCachedFooValue methods before the property arrays that use them. 12337 if descriptor.interface.isJSImplemented(): 12338 for m in clearableCachedAttrs(descriptor): 12339 cgThings.append(CGJSImplClearCachedValueMethod(descriptor, m)) 12340 12341 # Need to output our generated hasinstance bits before 12342 # PropertyArrays tries to use them. 12343 if (descriptor.interface.hasInterfaceObject() and 12344 NeedsGeneratedHasInstance(descriptor)): 12345 cgThings.append(CGHasInstanceHook(descriptor)) 12346 12347 properties = PropertyArrays(descriptor) 12348 cgThings.append(CGGeneric(define=str(properties))) 12349 cgThings.append(CGNativeProperties(descriptor, properties)) 12350 12351 if descriptor.interface.hasInterfaceObject(): 12352 cgThings.append(CGClassConstructor(descriptor, 12353 descriptor.interface.ctor())) 12354 cgThings.append(CGInterfaceObjectJSClass(descriptor, properties)) 12355 cgThings.append(CGNamedConstructors(descriptor)) 12356 12357 cgThings.append(CGLegacyCallHook(descriptor)) 12358 if descriptor.interface.getExtendedAttribute("NeedResolve"): 12359 cgThings.append(CGResolveHook(descriptor)) 12360 cgThings.append(CGMayResolveHook(descriptor)) 12361 cgThings.append(CGEnumerateHook(descriptor)) 12362 12363 if descriptor.hasNamedPropertiesObject: 12364 cgThings.append(CGGetNamedPropertiesObjectMethod(descriptor)) 12365 12366 if descriptor.interface.hasInterfacePrototypeObject(): 12367 cgThings.append(CGPrototypeJSClass(descriptor, properties)) 12368 12369 if ((descriptor.interface.hasInterfaceObject() or descriptor.interface.isNavigatorProperty()) and 12370 not descriptor.interface.isExternal() and 12371 descriptor.isExposedConditionally()): 12372 cgThings.append(CGConstructorEnabled(descriptor)) 12373 12374 if descriptor.registersGlobalNamesOnWindow: 12375 cgThings.append(CGDefineDOMInterfaceMethod(descriptor)) 12376 12377 if (descriptor.interface.hasMembersInSlots() and 12378 descriptor.interface.hasChildInterfaces()): 12379 raise TypeError("We don't support members in slots on " 12380 "non-leaf interfaces like %s" % 12381 descriptor.interface.identifier.name) 12382 12383 if descriptor.concrete: 12384 if descriptor.proxy: 12385 if descriptor.interface.totalMembersInSlots != 0: 12386 raise TypeError("We can't have extra reserved slots for " 12387 "proxy interface %s" % 12388 descriptor.interface.identifier.name) 12389 cgThings.append(CGGeneric(fill( 12390 """ 12391 static_assert(IsBaseOf<nsISupports, ${nativeType} >::value, 12392 "We don't support non-nsISupports native classes for " 12393 "proxy-based bindings yet"); 12394 12395 """, 12396 nativeType=descriptor.nativeType))) 12397 if not descriptor.wrapperCache: 12398 raise TypeError("We need a wrappercache to support expandos for proxy-based " 12399 "bindings (" + descriptor.name + ")") 12400 handlerThing = CGDOMJSProxyHandler(descriptor) 12401 cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing)) 12402 cgThings.append(CGProxyIsProxy(descriptor)) 12403 cgThings.append(CGProxyUnwrap(descriptor)) 12404 cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing)) 12405 cgThings.append(CGDOMProxyJSClass(descriptor)) 12406 else: 12407 cgThings.append(CGDOMJSClass(descriptor)) 12408 cgThings.append(CGGetJSClassMethod(descriptor)) 12409 if descriptor.interface.hasMembersInSlots(): 12410 cgThings.append(CGUpdateMemberSlotsMethod(descriptor)) 12411 12412 if descriptor.isGlobal(): 12413 assert descriptor.wrapperCache 12414 cgThings.append(CGWrapGlobalMethod(descriptor, properties)) 12415 elif descriptor.wrapperCache: 12416 cgThings.append(CGWrapWithCacheMethod(descriptor, properties)) 12417 cgThings.append(CGWrapMethod(descriptor)) 12418 else: 12419 cgThings.append(CGWrapNonWrapperCacheMethod(descriptor, 12420 properties)) 12421 12422 # Set up our Xray callbacks as needed. This needs to come 12423 # after we have our DOMProxyHandler defined. 12424 if descriptor.wantsXrays: 12425 if descriptor.concrete and descriptor.proxy: 12426 cgThings.append(CGResolveOwnProperty(descriptor)) 12427 cgThings.append(CGEnumerateOwnProperties(descriptor)) 12428 if descriptor.needsXrayNamedDeleterHook(): 12429 cgThings.append(CGDeleteNamedProperty(descriptor)) 12430 elif descriptor.needsXrayResolveHooks(): 12431 cgThings.append(CGResolveOwnPropertyViaResolve(descriptor)) 12432 cgThings.append(CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor)) 12433 if descriptor.wantsXrayExpandoClass: 12434 cgThings.append(CGXrayExpandoJSClass(descriptor)) 12435 12436 # Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff 12437 # done, set up our NativePropertyHooks. 12438 cgThings.append(CGNativePropertyHooks(descriptor, properties)) 12439 12440 # If we're not wrappercached, we don't know how to clear our 12441 # cached values, since we can't get at the JSObject. 12442 if descriptor.wrapperCache: 12443 cgThings.extend(CGClearCachedValueMethod(descriptor, m) for 12444 m in clearableCachedAttrs(descriptor)) 12445 12446 haveUnscopables = (len(unscopableNames) != 0 and 12447 descriptor.interface.hasInterfacePrototypeObject()) 12448 if haveUnscopables: 12449 cgThings.append( 12450 CGList([CGGeneric("static const char* const unscopableNames[] = {"), 12451 CGIndenter(CGList([CGGeneric('"%s"' % name) for 12452 name in unscopableNames] + 12453 [CGGeneric("nullptr")], ",\n")), 12454 CGGeneric("};\n")], "\n")) 12455 12456 # CGCreateInterfaceObjectsMethod needs to come after our 12457 # CGDOMJSClass and unscopables, if any. 12458 cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties, 12459 haveUnscopables)) 12460 12461 # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need 12462 # to come after CGCreateInterfaceObjectsMethod. 12463 if descriptor.interface.hasInterfacePrototypeObject(): 12464 cgThings.append(CGGetProtoObjectHandleMethod(descriptor)) 12465 if descriptor.interface.hasChildInterfaces(): 12466 cgThings.append(CGGetProtoObjectMethod(descriptor)) 12467 if descriptor.interface.hasInterfaceObject(): 12468 cgThings.append(CGGetConstructorObjectHandleMethod(descriptor)) 12469 cgThings.append(CGGetConstructorObjectMethod(descriptor)) 12470 12471 # See whether we need we need to generate an IsPermitted method 12472 if crossOriginGetters or crossOriginSetters or crossOriginMethods: 12473 cgThings.append(CGIsPermittedMethod(descriptor, 12474 crossOriginGetters, 12475 crossOriginSetters, 12476 crossOriginMethods)) 12477 12478 cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n") 12479 cgThings = CGWrapper(cgThings, pre='\n', post='\n') 12480 self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name), 12481 cgThings), 12482 post='\n') 12483 12484 def declare(self): 12485 return self.cgRoot.declare() 12486 12487 def define(self): 12488 return self.cgRoot.define() 12489 12490 def deps(self): 12491 return self._deps 12492 12493 12494class CGNamespacedEnum(CGThing): 12495 def __init__(self, namespace, enumName, names, values, comment=""): 12496 12497 if not values: 12498 values = [] 12499 12500 # Account for explicit enum values. 12501 entries = [] 12502 for i in range(0, len(names)): 12503 if len(values) > i and values[i] is not None: 12504 entry = "%s = %s" % (names[i], values[i]) 12505 else: 12506 entry = names[i] 12507 entries.append(entry) 12508 12509 # Append a Count. 12510 entries.append('_' + enumName + '_Count') 12511 12512 # Indent. 12513 entries = [' ' + e for e in entries] 12514 12515 # Build the enum body. 12516 enumstr = comment + 'enum %s : uint16_t\n{\n%s\n};\n' % (enumName, ',\n'.join(entries)) 12517 curr = CGGeneric(declare=enumstr) 12518 12519 # Add some whitespace padding. 12520 curr = CGWrapper(curr, pre='\n', post='\n') 12521 12522 # Add the namespace. 12523 curr = CGNamespace(namespace, curr) 12524 12525 # Add the typedef 12526 typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName) 12527 curr = CGList([curr, CGGeneric(declare=typedef)]) 12528 12529 # Save the result. 12530 self.node = curr 12531 12532 def declare(self): 12533 return self.node.declare() 12534 12535 def define(self): 12536 return "" 12537 12538 12539def initIdsClassMethod(identifiers, atomCacheName): 12540 idinit = ['!atomsCache->%s.init(cx, "%s")' % 12541 (CGDictionary.makeIdName(id), 12542 id) 12543 for id in identifiers] 12544 idinit.reverse() 12545 body = fill( 12546 """ 12547 MOZ_ASSERT(!*reinterpret_cast<jsid**>(atomsCache)); 12548 12549 // Initialize these in reverse order so that any failure leaves the first one 12550 // uninitialized. 12551 if (${idinit}) { 12552 return false; 12553 } 12554 return true; 12555 """, 12556 idinit=" ||\n ".join(idinit)) 12557 return ClassMethod("InitIds", "bool", [ 12558 Argument("JSContext*", "cx"), 12559 Argument("%s*" % atomCacheName, "atomsCache") 12560 ], static=True, body=body, visibility="private") 12561 12562 12563class CGDictionary(CGThing): 12564 def __init__(self, dictionary, descriptorProvider): 12565 self.dictionary = dictionary 12566 self.descriptorProvider = descriptorProvider 12567 self.needToInitIds = len(dictionary.members) > 0 12568 self.memberInfo = [ 12569 (member, 12570 getJSToNativeConversionInfo( 12571 member.type, 12572 descriptorProvider, 12573 isEnforceRange=member.enforceRange, 12574 isClamp=member.clamp, 12575 isMember="Dictionary", 12576 isOptional=member.canHaveMissingValue(), 12577 defaultValue=member.defaultValue, 12578 sourceDescription=self.getMemberSourceDescription(member))) 12579 for member in dictionary.members] 12580 12581 # If we have a union member containing something in the same 12582 # file as us, bail: the C++ includes won't work out. 12583 for member in dictionary.members: 12584 type = member.type.unroll() 12585 if type.isUnion(): 12586 for t in type.flatMemberTypes: 12587 if (t.isDictionary() and 12588 CGHeaders.getDeclarationFilename(t.inner) == 12589 CGHeaders.getDeclarationFilename(dictionary)): 12590 raise TypeError( 12591 "Dictionary contains a union that contains a " 12592 "dictionary in the same WebIDL file. This won't " 12593 "compile. Move the inner dictionary to a " 12594 "different file.\n%s\n%s" % 12595 (t.location, t.inner.location)) 12596 self.structs = self.getStructs() 12597 12598 def declare(self): 12599 return self.structs.declare() 12600 12601 def define(self): 12602 return self.structs.define() 12603 12604 def base(self): 12605 if self.dictionary.parent: 12606 return self.makeClassName(self.dictionary.parent) 12607 return "DictionaryBase" 12608 12609 def initMethod(self): 12610 """ 12611 This function outputs the body of the Init() method for the dictionary. 12612 12613 For the most part, this is some bookkeeping for our atoms so 12614 we can avoid atomizing strings all the time, then we just spit 12615 out the getMemberConversion() output for each member, 12616 separated by newlines. 12617 12618 """ 12619 body = dedent(""" 12620 // Passing a null JSContext is OK only if we're initing from null, 12621 // Since in that case we will not have to do any property gets 12622 MOZ_ASSERT_IF(!cx, val.isNull()); 12623 """) 12624 12625 if self.needToInitIds: 12626 body += fill( 12627 """ 12628 ${dictName}Atoms* atomsCache = nullptr; 12629 if (cx) { 12630 atomsCache = GetAtomCache<${dictName}Atoms>(cx); 12631 if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) { 12632 return false; 12633 } 12634 } 12635 12636 """, 12637 dictName=self.makeClassName(self.dictionary)) 12638 12639 if self.dictionary.parent: 12640 body += fill( 12641 """ 12642 // Per spec, we init the parent's members first 12643 if (!${dictName}::Init(cx, val)) { 12644 return false; 12645 } 12646 12647 """, 12648 dictName=self.makeClassName(self.dictionary.parent)) 12649 else: 12650 body += dedent( 12651 """ 12652 { // scope for isConvertible 12653 bool isConvertible; 12654 if (!IsConvertibleToDictionary(cx, val, &isConvertible)) { 12655 return false; 12656 } 12657 if (!isConvertible) { 12658 return ThrowErrorMessage(cx, MSG_NOT_DICTIONARY, sourceDescription); 12659 } 12660 } 12661 12662 """) 12663 12664 memberInits = [self.getMemberConversion(m).define() 12665 for m in self.memberInfo] 12666 if memberInits: 12667 body += fill( 12668 """ 12669 bool isNull = val.isNullOrUndefined(); 12670 // We only need these if !isNull, in which case we have |cx|. 12671 Maybe<JS::Rooted<JSObject *> > object; 12672 Maybe<JS::Rooted<JS::Value> > temp; 12673 if (!isNull) { 12674 MOZ_ASSERT(cx); 12675 object.emplace(cx, &val.toObject()); 12676 temp.emplace(cx); 12677 } 12678 $*{memberInits} 12679 """, 12680 memberInits="\n".join(memberInits)) 12681 12682 body += "return true;\n" 12683 12684 return ClassMethod("Init", "bool", [ 12685 Argument('JSContext*', 'cx'), 12686 Argument('JS::Handle<JS::Value>', 'val'), 12687 Argument('const char*', 'sourceDescription', default='"Value"'), 12688 Argument('bool', 'passedToJSImpl', default='false') 12689 ], body=body) 12690 12691 def initFromJSONMethod(self): 12692 return ClassMethod( 12693 "Init", "bool", 12694 [Argument('const nsAString&', 'aJSON')], 12695 body=dedent(""" 12696 AutoJSAPI jsapi; 12697 JSObject* cleanGlobal = SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::BindingDetail); 12698 if (!cleanGlobal) { 12699 return false; 12700 } 12701 if (!jsapi.Init(cleanGlobal)) { 12702 return false; 12703 } 12704 JSContext* cx = jsapi.cx(); 12705 JS::Rooted<JS::Value> json(cx); 12706 bool ok = ParseJSON(cx, aJSON, &json); 12707 NS_ENSURE_TRUE(ok, false); 12708 return Init(cx, json); 12709 """)) 12710 12711 def toJSONMethod(self): 12712 return ClassMethod( 12713 "ToJSON", "bool", 12714 [Argument('nsAString&', 'aJSON')], 12715 body=dedent(""" 12716 AutoJSAPI jsapi; 12717 jsapi.Init(); 12718 JSContext *cx = jsapi.cx(); 12719 // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here 12720 // because we'll only be creating objects, in ways that have no 12721 // side-effects, followed by a call to JS::ToJSONMaybeSafely, 12722 // which likewise guarantees no side-effects for the sorts of 12723 // things we will pass it. 12724 JSAutoCompartment ac(cx, binding_detail::UnprivilegedJunkScopeOrWorkerGlobal()); 12725 JS::Rooted<JS::Value> val(cx); 12726 if (!ToObjectInternal(cx, &val)) { 12727 return false; 12728 } 12729 JS::Rooted<JSObject*> obj(cx, &val.toObject()); 12730 return StringifyToJSON(cx, obj, aJSON); 12731 """), const=True) 12732 12733 def toObjectInternalMethod(self): 12734 body = "" 12735 if self.needToInitIds: 12736 body += fill( 12737 """ 12738 ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx); 12739 if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) { 12740 return false; 12741 } 12742 12743 """, 12744 dictName=self.makeClassName(self.dictionary)) 12745 12746 if self.dictionary.parent: 12747 body += fill( 12748 """ 12749 // Per spec, we define the parent's members first 12750 if (!${dictName}::ToObjectInternal(cx, rval)) { 12751 return false; 12752 } 12753 JS::Rooted<JSObject*> obj(cx, &rval.toObject()); 12754 12755 """, 12756 dictName=self.makeClassName(self.dictionary.parent)) 12757 else: 12758 body += dedent( 12759 """ 12760 JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx)); 12761 if (!obj) { 12762 return false; 12763 } 12764 rval.set(JS::ObjectValue(*obj)); 12765 12766 """) 12767 12768 if self.memberInfo: 12769 body += "\n".join(self.getMemberDefinition(m).define() 12770 for m in self.memberInfo) 12771 body += "\nreturn true;\n" 12772 12773 return ClassMethod("ToObjectInternal", "bool", [ 12774 Argument('JSContext*', 'cx'), 12775 Argument('JS::MutableHandle<JS::Value>', 'rval'), 12776 ], const=True, body=body) 12777 12778 def initIdsMethod(self): 12779 assert self.needToInitIds 12780 return initIdsClassMethod([m.identifier.name for m in self.dictionary.members], 12781 "%sAtoms" % self.makeClassName(self.dictionary)) 12782 12783 def traceDictionaryMethod(self): 12784 body = "" 12785 if self.dictionary.parent: 12786 cls = self.makeClassName(self.dictionary.parent) 12787 body += "%s::TraceDictionary(trc);\n" % cls 12788 12789 memberTraces = [self.getMemberTrace(m) 12790 for m in self.dictionary.members 12791 if typeNeedsRooting(m.type)] 12792 12793 if memberTraces: 12794 body += "\n".join(memberTraces) 12795 12796 return ClassMethod("TraceDictionary", "void", [ 12797 Argument("JSTracer*", "trc"), 12798 ], body=body) 12799 12800 def assignmentOperator(self): 12801 body = CGList([]) 12802 if self.dictionary.parent: 12803 body.append(CGGeneric( 12804 "%s::operator=(aOther);\n" % 12805 self.makeClassName(self.dictionary.parent))) 12806 for m, _ in self.memberInfo: 12807 memberName = self.makeMemberName(m.identifier.name) 12808 if m.canHaveMissingValue(): 12809 memberAssign = CGGeneric(fill( 12810 """ 12811 ${name}.Reset(); 12812 if (aOther.${name}.WasPassed()) { 12813 ${name}.Construct(aOther.${name}.Value()); 12814 } 12815 """, 12816 name=memberName)) 12817 else: 12818 memberAssign = CGGeneric( 12819 "%s = aOther.%s;\n" % (memberName, memberName)) 12820 body.append(memberAssign) 12821 return ClassMethod( 12822 "operator=", "void", 12823 [Argument("const %s&" % self.makeClassName(self.dictionary), 12824 "aOther")], 12825 body=body.define()) 12826 12827 def getStructs(self): 12828 d = self.dictionary 12829 selfName = self.makeClassName(d) 12830 members = [ClassMember(self.makeMemberName(m[0].identifier.name), 12831 self.getMemberType(m), 12832 visibility="public", 12833 body=self.getMemberInitializer(m), 12834 hasIgnoreInitCheckFlag=True) 12835 for m in self.memberInfo] 12836 if d.parent: 12837 # We always want to init our parent with our non-initializing 12838 # constructor arg, because either we're about to init ourselves (and 12839 # hence our parent) or we don't want any init happening. 12840 baseConstructors = [ 12841 "%s(%s)" % (self.makeClassName(d.parent), 12842 self.getNonInitializingCtorArg()) 12843 ] 12844 else: 12845 baseConstructors = None 12846 ctors = [ 12847 ClassConstructor( 12848 [], 12849 visibility="public", 12850 baseConstructors=baseConstructors, 12851 body=( 12852 "// Safe to pass a null context if we pass a null value\n" 12853 "Init(nullptr, JS::NullHandleValue);\n")), 12854 ClassConstructor( 12855 [Argument("const FastDictionaryInitializer&", "")], 12856 visibility="public", 12857 baseConstructors=baseConstructors, 12858 explicit=True, 12859 bodyInHeader=True, 12860 body='// Do nothing here; this is used by our "Fast" subclass\n') 12861 ] 12862 methods = [] 12863 12864 if self.needToInitIds: 12865 methods.append(self.initIdsMethod()) 12866 12867 methods.append(self.initMethod()) 12868 canBeRepresentedAsJSON = self.dictionarySafeToJSONify(self.dictionary) 12869 if canBeRepresentedAsJSON: 12870 methods.append(self.initFromJSONMethod()) 12871 try: 12872 methods.append(self.toObjectInternalMethod()) 12873 if canBeRepresentedAsJSON: 12874 methods.append(self.toJSONMethod()) 12875 except MethodNotNewObjectError: 12876 # If we can't have a ToObjectInternal() because one of our members 12877 # can only be returned from [NewObject] methods, then just skip 12878 # generating ToObjectInternal() and ToJSON (since the latter depens 12879 # on the former). 12880 pass 12881 methods.append(self.traceDictionaryMethod()) 12882 12883 if CGDictionary.isDictionaryCopyConstructible(d): 12884 disallowCopyConstruction = False 12885 # Note: no base constructors because our operator= will 12886 # deal with that. 12887 ctors.append(ClassConstructor([Argument("const %s&" % selfName, 12888 "aOther")], 12889 bodyInHeader=True, 12890 visibility="public", 12891 explicit=True, 12892 body="*this = aOther;\n")) 12893 methods.append(self.assignmentOperator()) 12894 else: 12895 disallowCopyConstruction = True 12896 12897 struct = CGClass(selfName, 12898 bases=[ClassBase(self.base())], 12899 members=members, 12900 constructors=ctors, 12901 methods=methods, 12902 isStruct=True, 12903 disallowCopyConstruction=disallowCopyConstruction) 12904 12905 fastDictionaryCtor = ClassConstructor( 12906 [], 12907 visibility="public", 12908 bodyInHeader=True, 12909 baseConstructors=["%s(%s)" % 12910 (selfName, 12911 self.getNonInitializingCtorArg())], 12912 body="// Doesn't matter what int we pass to the parent constructor\n") 12913 12914 fastStruct = CGClass("Fast" + selfName, 12915 bases=[ClassBase(selfName)], 12916 constructors=[fastDictionaryCtor], 12917 isStruct=True) 12918 12919 return CGList([struct, 12920 CGNamespace('binding_detail', fastStruct)], 12921 "\n") 12922 12923 def deps(self): 12924 return self.dictionary.getDeps() 12925 12926 @staticmethod 12927 def makeDictionaryName(dictionary): 12928 return dictionary.identifier.name 12929 12930 def makeClassName(self, dictionary): 12931 return self.makeDictionaryName(dictionary) 12932 12933 @staticmethod 12934 def makeMemberName(name): 12935 return "m" + name[0].upper() + IDLToCIdentifier(name[1:]) 12936 12937 def getMemberType(self, memberInfo): 12938 _, conversionInfo = memberInfo 12939 # We can't handle having a holderType here 12940 assert conversionInfo.holderType is None 12941 declType = conversionInfo.declType 12942 if conversionInfo.dealWithOptional: 12943 declType = CGTemplatedType("Optional", declType) 12944 return declType.define() 12945 12946 def getMemberConversion(self, memberInfo): 12947 """ 12948 A function that outputs the initialization of a single dictionary 12949 member from the given dictionary value. 12950 12951 We start with our conversionInfo, which tells us how to 12952 convert a JS::Value to whatever type this member is. We 12953 substiture the template from the conversionInfo with values 12954 that point to our "temp" JS::Value and our member (which is 12955 the C++ value we want to produce). The output is a string of 12956 code to do the conversion. We store this string in 12957 conversionReplacements["convert"]. 12958 12959 Now we have three different ways we might use (or skip) this 12960 string of code, depending on whether the value is required, 12961 optional with default value, or optional without default 12962 value. We set up a template in the 'conversion' variable for 12963 exactly how to do this, then substitute into it from the 12964 conversionReplacements dictionary. 12965 """ 12966 member, conversionInfo = memberInfo 12967 replacements = { 12968 "val": "temp.ref()", 12969 "maybeMutableVal": "temp.ptr()", 12970 "declName": self.makeMemberName(member.identifier.name), 12971 # We need a holder name for external interfaces, but 12972 # it's scoped down to the conversion so we can just use 12973 # anything we want. 12974 "holderName": "holder", 12975 "passedToJSImpl": "passedToJSImpl" 12976 } 12977 # We can't handle having a holderType here 12978 assert conversionInfo.holderType is None 12979 if conversionInfo.dealWithOptional: 12980 replacements["declName"] = "(" + replacements["declName"] + ".Value())" 12981 if member.defaultValue: 12982 replacements["haveValue"] = "!isNull && !temp->isUndefined()" 12983 12984 propId = self.makeIdName(member.identifier.name) 12985 propGet = ("JS_GetPropertyById(cx, *object, atomsCache->%s, temp.ptr())" % 12986 propId) 12987 12988 conversionReplacements = { 12989 "prop": self.makeMemberName(member.identifier.name), 12990 "convert": string.Template(conversionInfo.template).substitute(replacements), 12991 "propGet": propGet 12992 } 12993 # The conversion code will only run where a default value or a value passed 12994 # by the author needs to get converted, so we can remember if we have any 12995 # members present here. 12996 conversionReplacements["convert"] += "mIsAnyMemberPresent = true;\n" 12997 setTempValue = CGGeneric(dedent( 12998 """ 12999 if (!${propGet}) { 13000 return false; 13001 } 13002 """)) 13003 conditions = getConditionList(member, "cx", "*object") 13004 if len(conditions) != 0: 13005 setTempValue = CGIfElseWrapper(conditions.define(), 13006 setTempValue, 13007 CGGeneric("temp->setUndefined();\n")) 13008 setTempValue = CGIfWrapper(setTempValue, "!isNull") 13009 conversion = setTempValue.define() 13010 if member.defaultValue: 13011 if (member.type.isUnion() and 13012 (not member.type.nullable() or 13013 not isinstance(member.defaultValue, IDLNullValue))): 13014 # Since this has a default value, it might have been initialized 13015 # already. Go ahead and uninit it before we try to init it 13016 # again. 13017 memberName = self.makeMemberName(member.identifier.name) 13018 if member.type.nullable(): 13019 conversion += fill( 13020 """ 13021 if (!${memberName}.IsNull()) { 13022 ${memberName}.Value().Uninit(); 13023 } 13024 """, 13025 memberName=memberName) 13026 else: 13027 conversion += "%s.Uninit();\n" % memberName 13028 conversion += "${convert}" 13029 elif not conversionInfo.dealWithOptional: 13030 # We're required, but have no default value. Make sure 13031 # that we throw if we have no value provided. 13032 conversion += dedent( 13033 """ 13034 if (!isNull && !temp->isUndefined()) { 13035 ${convert} 13036 } else if (cx) { 13037 // Don't error out if we have no cx. In that 13038 // situation the caller is default-constructing us and we'll 13039 // just assume they know what they're doing. 13040 return ThrowErrorMessage(cx, MSG_MISSING_REQUIRED_DICTIONARY_MEMBER, 13041 "%s"); 13042 } 13043 """ % self.getMemberSourceDescription(member)) 13044 conversionReplacements["convert"] = indent(conversionReplacements["convert"]).rstrip() 13045 else: 13046 conversion += ( 13047 "if (!isNull && !temp->isUndefined()) {\n" 13048 " ${prop}.Construct();\n" 13049 "${convert}" 13050 "}\n") 13051 conversionReplacements["convert"] = indent(conversionReplacements["convert"]) 13052 13053 return CGGeneric( 13054 string.Template(conversion).substitute(conversionReplacements)) 13055 13056 def getMemberDefinition(self, memberInfo): 13057 member = memberInfo[0] 13058 declType = memberInfo[1].declType 13059 memberLoc = self.makeMemberName(member.identifier.name) 13060 if not member.canHaveMissingValue(): 13061 memberData = memberLoc 13062 else: 13063 # The data is inside the Optional<> 13064 memberData = "%s.InternalValue()" % memberLoc 13065 13066 # If you have to change this list (which you shouldn't!), make sure it 13067 # continues to match the list in test_Object.prototype_props.html 13068 if (member.identifier.name in 13069 ["constructor", "toSource", "toString", "toLocaleString", "valueOf", 13070 "watch", "unwatch", "hasOwnProperty", "isPrototypeOf", 13071 "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", 13072 "__lookupGetter__", "__lookupSetter__", "__proto__"]): 13073 raise TypeError("'%s' member of %s dictionary shadows " 13074 "a property of Object.prototype, and Xrays to " 13075 "Object can't handle that.\n" 13076 "%s" % 13077 (member.identifier.name, 13078 self.dictionary.identifier.name, 13079 member.location)) 13080 13081 propDef = ( 13082 'JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, JSPROP_ENUMERATE)' % 13083 self.makeIdName(member.identifier.name)) 13084 13085 innerTemplate = wrapForType( 13086 member.type, self.descriptorProvider, 13087 { 13088 'result': "currentValue", 13089 'successCode': ("if (!%s) {\n" 13090 " return false;\n" 13091 "}\n" 13092 "break;\n" % propDef), 13093 'jsvalRef': "temp", 13094 'jsvalHandle': "&temp", 13095 'returnsNewObject': False, 13096 # 'obj' can just be allowed to be the string "obj", since that 13097 # will be our dictionary object, which is presumably itself in 13098 # the right scope. 13099 'typedArraysAreStructs': True 13100 }) 13101 conversion = CGGeneric(innerTemplate) 13102 conversion = CGWrapper(conversion, 13103 pre=("JS::Rooted<JS::Value> temp(cx);\n" 13104 "%s const & currentValue = %s;\n" % 13105 (declType.define(), memberData) 13106 )) 13107 13108 # Now make sure that our successCode can actually break out of the 13109 # conversion. This incidentally gives us a scope for 'temp' and 13110 # 'currentValue'. 13111 conversion = CGWrapper( 13112 CGIndenter(conversion), 13113 pre=("do {\n" 13114 " // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"), 13115 post="} while(0);\n") 13116 if member.canHaveMissingValue(): 13117 # Only do the conversion if we have a value 13118 conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc) 13119 conditions = getConditionList(member, "cx", "obj") 13120 if len(conditions) != 0: 13121 conversion = CGIfWrapper(conversion, conditions.define()) 13122 return conversion 13123 13124 def getMemberTrace(self, member): 13125 type = member.type 13126 assert typeNeedsRooting(type) 13127 memberLoc = self.makeMemberName(member.identifier.name) 13128 if not member.canHaveMissingValue(): 13129 memberData = memberLoc 13130 else: 13131 # The data is inside the Optional<> 13132 memberData = "%s.Value()" % memberLoc 13133 13134 memberName = "%s.%s" % (self.makeClassName(self.dictionary), 13135 memberLoc) 13136 13137 if type.isObject(): 13138 trace = CGGeneric('JS::UnsafeTraceRoot(trc, %s, "%s");\n' % 13139 ("&"+memberData, memberName)) 13140 if type.nullable(): 13141 trace = CGIfWrapper(trace, memberData) 13142 elif type.isAny(): 13143 trace = CGGeneric('JS::UnsafeTraceRoot(trc, %s, "%s");\n' % 13144 ("&"+memberData, memberName)) 13145 elif (type.isSequence() or type.isDictionary() or 13146 type.isSpiderMonkeyInterface() or type.isUnion()): 13147 if type.nullable(): 13148 memberNullable = memberData 13149 memberData = "%s.Value()" % memberData 13150 if type.isSequence(): 13151 trace = CGGeneric('DoTraceSequence(trc, %s);\n' % memberData) 13152 elif type.isDictionary(): 13153 trace = CGGeneric('%s.TraceDictionary(trc);\n' % memberData) 13154 elif type.isUnion(): 13155 trace = CGGeneric('%s.TraceUnion(trc);\n' % memberData) 13156 else: 13157 assert type.isSpiderMonkeyInterface() 13158 trace = CGGeneric('%s.TraceSelf(trc);\n' % memberData) 13159 if type.nullable(): 13160 trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable) 13161 elif type.isMozMap(): 13162 # If you implement this, add a MozMap<object> to 13163 # TestInterfaceJSDictionary and test it in test_bug1036214.html 13164 # to make sure we end up with the correct security properties. 13165 assert False 13166 else: 13167 assert False # unknown type 13168 13169 if member.canHaveMissingValue(): 13170 trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc) 13171 13172 return trace.define() 13173 13174 def getMemberInitializer(self, memberInfo): 13175 """ 13176 Get the right initializer for the member. Most members don't need one, 13177 but we need to pre-initialize 'any' and 'object' that have a default 13178 value, so they're safe to trace at all times. 13179 """ 13180 member, _ = memberInfo 13181 if member.canHaveMissingValue(): 13182 # Allowed missing value means no need to set it up front, since it's 13183 # inside an Optional and won't get traced until it's actually set 13184 # up. 13185 return None 13186 type = member.type 13187 if type.isAny(): 13188 return "JS::UndefinedValue()" 13189 if type.isObject(): 13190 return "nullptr" 13191 if type.isDictionary(): 13192 # When we construct ourselves, we don't want to init our member 13193 # dictionaries. Either we're being constructed-but-not-initialized 13194 # ourselves (and then we don't want to init them) or we're about to 13195 # init ourselves and then we'll init them anyway. 13196 return CGDictionary.getNonInitializingCtorArg() 13197 return None 13198 13199 def getMemberSourceDescription(self, member): 13200 return ("'%s' member of %s" % 13201 (member.identifier.name, self.dictionary.identifier.name)) 13202 13203 @staticmethod 13204 def makeIdName(name): 13205 return IDLToCIdentifier(name) + "_id" 13206 13207 @staticmethod 13208 def getNonInitializingCtorArg(): 13209 return "FastDictionaryInitializer()" 13210 13211 @staticmethod 13212 def isDictionaryCopyConstructible(dictionary): 13213 if (dictionary.parent and 13214 not CGDictionary.isDictionaryCopyConstructible(dictionary.parent)): 13215 return False 13216 return all(isTypeCopyConstructible(m.type) for m in dictionary.members) 13217 13218 @staticmethod 13219 def typeSafeToJSONify(type): 13220 """ 13221 Determine whether the given type is safe to convert to JSON. The 13222 restriction is that this needs to be safe while in a global controlled 13223 by an adversary, and "safe" means no side-effects when the JS 13224 representation of this type is converted to JSON. That means that we 13225 have to be pretty restrictive about what things we can allow. For 13226 example, "object" is out, because it may have accessor properties on it. 13227 """ 13228 if type.nullable(): 13229 # Converting null to JSON is always OK. 13230 return CGDictionary.typeSafeToJSONify(type.inner) 13231 13232 if type.isSequence(): 13233 # Sequences are arrays we create ourselves, with no holes. They 13234 # should be safe if their contents are safe, as long as we suppress 13235 # invocation of .toJSON on objects. 13236 return CGDictionary.typeSafeToJSONify(type.inner) 13237 13238 if type.isUnion(): 13239 # OK if everything in it is ok. 13240 return all(CGDictionary.typeSafeToJSONify(t) 13241 for t in type.flatMemberTypes) 13242 13243 if type.isDictionary(): 13244 # OK if the dictionary is OK 13245 return CGDictionary.dictionarySafeToJSONify(type.inner) 13246 13247 if type.isString() or type.isEnum(): 13248 # Strings are always OK. 13249 return True 13250 13251 if type.isPrimitive(): 13252 # Primitives (numbers and booleans) are ok, as long as 13253 # they're not unrestricted float/double. 13254 return not type.isFloat() or not type.isUnrestricted() 13255 13256 return False 13257 13258 @staticmethod 13259 def dictionarySafeToJSONify(dictionary): 13260 # The dictionary itself is OK, so we're good if all our types are. 13261 return all(CGDictionary.typeSafeToJSONify(m.type) 13262 for m in dictionary.members) 13263 13264 13265class CGRegisterWorkerBindings(CGAbstractMethod): 13266 def __init__(self, config): 13267 CGAbstractMethod.__init__(self, None, 'RegisterWorkerBindings', 'bool', 13268 [Argument('JSContext*', 'aCx'), 13269 Argument('JS::Handle<JSObject*>', 'aObj')]) 13270 self.config = config 13271 13272 def definition_body(self): 13273 descriptors = self.config.getDescriptors(hasInterfaceObject=True, 13274 isExposedInAnyWorker=True, 13275 register=True) 13276 conditions = [] 13277 for desc in descriptors: 13278 bindingNS = toBindingNamespace(desc.name) 13279 condition = "!%s::GetConstructorObject(aCx)" % bindingNS 13280 if desc.isExposedConditionally(): 13281 condition = ( 13282 "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS 13283 + condition) 13284 conditions.append(condition) 13285 lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for 13286 condition in conditions] 13287 lines.append(CGGeneric("return true;\n")) 13288 return CGList(lines, "\n").define() 13289 13290class CGRegisterWorkerDebuggerBindings(CGAbstractMethod): 13291 def __init__(self, config): 13292 CGAbstractMethod.__init__(self, None, 'RegisterWorkerDebuggerBindings', 'bool', 13293 [Argument('JSContext*', 'aCx'), 13294 Argument('JS::Handle<JSObject*>', 'aObj')]) 13295 self.config = config 13296 13297 def definition_body(self): 13298 descriptors = self.config.getDescriptors(hasInterfaceObject=True, 13299 isExposedInWorkerDebugger=True, 13300 register=True) 13301 conditions = [] 13302 for desc in descriptors: 13303 bindingNS = toBindingNamespace(desc.name) 13304 condition = "!%s::GetConstructorObject(aCx)" % bindingNS 13305 if desc.isExposedConditionally(): 13306 condition = ( 13307 "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS 13308 + condition) 13309 conditions.append(condition) 13310 lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for 13311 condition in conditions] 13312 lines.append(CGGeneric("return true;\n")) 13313 return CGList(lines, "\n").define() 13314 13315class CGRegisterWorkletBindings(CGAbstractMethod): 13316 def __init__(self, config): 13317 CGAbstractMethod.__init__(self, None, 'RegisterWorkletBindings', 'bool', 13318 [Argument('JSContext*', 'aCx'), 13319 Argument('JS::Handle<JSObject*>', 'aObj')]) 13320 self.config = config 13321 13322 def definition_body(self): 13323 descriptors = self.config.getDescriptors(hasInterfaceObject=True, 13324 isExposedInAnyWorklet=True, 13325 register=True) 13326 conditions = [] 13327 for desc in descriptors: 13328 bindingNS = toBindingNamespace(desc.name) 13329 condition = "!%s::GetConstructorObject(aCx)" % bindingNS 13330 if desc.isExposedConditionally(): 13331 condition = ( 13332 "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS 13333 + condition) 13334 conditions.append(condition) 13335 lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for 13336 condition in conditions] 13337 lines.append(CGGeneric("return true;\n")) 13338 return CGList(lines, "\n").define() 13339 13340class CGResolveSystemBinding(CGAbstractMethod): 13341 def __init__(self, config): 13342 CGAbstractMethod.__init__(self, None, 'ResolveSystemBinding', 'bool', 13343 [Argument('JSContext*', 'aCx'), 13344 Argument('JS::Handle<JSObject*>', 'aObj'), 13345 Argument('JS::Handle<jsid>', 'aId'), 13346 Argument('bool*', 'aResolvedp')]) 13347 self.config = config 13348 13349 def definition_body(self): 13350 descriptors = self.config.getDescriptors(hasInterfaceObject=True, 13351 isExposedInSystemGlobals=True, 13352 register=True) 13353 13354 def descNameToId(name): 13355 return "s%s_id" % name 13356 jsidNames = [descNameToId(desc.name) for desc in descriptors] 13357 jsidDecls = CGList(CGGeneric("static jsid %s;\n" % name) 13358 for name in jsidNames) 13359 13360 jsidInits = CGList( 13361 (CGIfWrapper( 13362 CGGeneric("return false;\n"), 13363 '!AtomizeAndPinJSString(aCx, %s, "%s")' % 13364 (descNameToId(desc.name), desc.interface.identifier.name)) 13365 for desc in descriptors), 13366 "\n") 13367 jsidInits.append(CGGeneric("idsInited = true;\n")) 13368 jsidInits = CGIfWrapper(jsidInits, "!idsInited") 13369 jsidInits = CGList([CGGeneric("static bool idsInited = false;\n"), 13370 jsidInits]) 13371 13372 definitions = CGList([], "\n") 13373 for desc in descriptors: 13374 bindingNS = toBindingNamespace(desc.name) 13375 defineCode = "!%s::GetConstructorObject(aCx)" % bindingNS 13376 defineCode = CGIfWrapper(CGGeneric("return false;\n"), defineCode) 13377 defineCode = CGList([defineCode, 13378 CGGeneric("*aResolvedp = true;\n")]) 13379 13380 condition = "JSID_IS_VOID(aId) || aId == %s" % descNameToId(desc.name) 13381 if desc.isExposedConditionally(): 13382 condition = "(%s) && %s::ConstructorEnabled(aCx, aObj)" % (condition, bindingNS) 13383 13384 definitions.append(CGIfWrapper(defineCode, condition)) 13385 13386 return CGList([CGGeneric("MOZ_ASSERT(NS_IsMainThread());\n"), 13387 jsidDecls, 13388 jsidInits, 13389 definitions, 13390 CGGeneric("return true;\n")], 13391 "\n").define() 13392 13393 13394def getGlobalNames(config): 13395 names = [] 13396 for desc in config.getDescriptors(registersGlobalNamesOnWindow=True): 13397 names.append((desc.name, desc)) 13398 names.extend((n.identifier.name, desc) for n in desc.interface.namedConstructors) 13399 return names 13400 13401class CGGlobalNamesString(CGGeneric): 13402 def __init__(self, config): 13403 globalNames = getGlobalNames(config) 13404 currentOffset = 0 13405 strings = [] 13406 for (name, _) in globalNames: 13407 strings.append('/* %i */ "%s\\0"' % (currentOffset, name)) 13408 currentOffset += len(name) + 1 # Add trailing null. 13409 define = fill(""" 13410 const uint32_t WebIDLGlobalNameHash::sCount = ${count}; 13411 13412 const char WebIDLGlobalNameHash::sNames[] = 13413 $*{strings} 13414 13415 """, 13416 count=len(globalNames), 13417 strings="\n".join(strings) + ";\n") 13418 13419 CGGeneric.__init__(self, define=define) 13420 13421 13422class CGRegisterGlobalNames(CGAbstractMethod): 13423 def __init__(self, config): 13424 CGAbstractMethod.__init__(self, None, 'RegisterWebIDLGlobalNames', 13425 'void', []) 13426 self.config = config 13427 13428 def definition_body(self): 13429 def getCheck(desc): 13430 if not desc.isExposedConditionally(): 13431 return "nullptr" 13432 return "%sBinding::ConstructorEnabled" % desc.name 13433 13434 define = "" 13435 currentOffset = 0 13436 for (name, desc) in getGlobalNames(self.config): 13437 length = len(name) 13438 define += "WebIDLGlobalNameHash::Register(%i, %i, %sBinding::DefineDOMInterface, %s);\n" % (currentOffset, length, desc.name, getCheck(desc)) 13439 currentOffset += length + 1 # Add trailing null. 13440 return define 13441 13442 13443def dependencySortObjects(objects, dependencyGetter, nameGetter): 13444 """ 13445 Sort IDL objects with dependencies on each other such that if A 13446 depends on B then B will come before A. This is needed for 13447 declaring C++ classes in the right order, for example. Objects 13448 that have no dependencies are just sorted by name. 13449 13450 objects should be something that can produce a set of objects 13451 (e.g. a set, iterator, list, etc). 13452 13453 dependencyGetter is something that, given an object, should return 13454 the set of objects it depends on. 13455 """ 13456 # XXXbz this will fail if we have two webidl files F1 and F2 such that F1 13457 # declares an object which depends on an object in F2, and F2 declares an 13458 # object (possibly a different one!) that depends on an object in F1. The 13459 # good news is that I expect this to never happen. 13460 sortedObjects = [] 13461 objects = set(objects) 13462 while len(objects) != 0: 13463 # Find the dictionaries that don't depend on anything else 13464 # anymore and move them over. 13465 toMove = [o for o in objects if 13466 len(dependencyGetter(o) & objects) == 0] 13467 if len(toMove) == 0: 13468 raise TypeError("Loop in dependency graph\n" + 13469 "\n".join(o.location for o in objects)) 13470 objects = objects - set(toMove) 13471 sortedObjects.extend(sorted(toMove, key=nameGetter)) 13472 return sortedObjects 13473 13474 13475class ForwardDeclarationBuilder: 13476 """ 13477 Create a canonical representation of a set of namespaced forward 13478 declarations. 13479 """ 13480 def __init__(self): 13481 """ 13482 The set of declarations is represented as a tree of nested namespaces. 13483 Each tree node has a set of declarations |decls| and a dict |children|. 13484 Each declaration is a pair consisting of the class name and a boolean 13485 that is true iff the class is really a struct. |children| maps the 13486 names of inner namespaces to the declarations in that namespace. 13487 """ 13488 self.decls = set() 13489 self.children = {} 13490 13491 def _ensureNonTemplateType(self, type): 13492 if "<" in type: 13493 # This is a templated type. We don't really know how to 13494 # forward-declare those, and trying to do it naively is not going to 13495 # go well (e.g. we may have :: characters inside the type we're 13496 # templated on!). Just bail out. 13497 raise TypeError("Attempt to use ForwardDeclarationBuilder on " 13498 "templated type %s. We don't know how to do that " 13499 "yet." % type) 13500 13501 def _listAdd(self, namespaces, name, isStruct=False): 13502 """ 13503 Add a forward declaration, where |namespaces| is a list of namespaces. 13504 |name| should not contain any other namespaces. 13505 """ 13506 if namespaces: 13507 child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder()) 13508 child._listAdd(namespaces[1:], name, isStruct) 13509 else: 13510 assert '::' not in name 13511 self.decls.add((name, isStruct)) 13512 13513 def addInMozillaDom(self, name, isStruct=False): 13514 """ 13515 Add a forward declaration to the mozilla::dom:: namespace. |name| should not 13516 contain any other namespaces. 13517 """ 13518 self._ensureNonTemplateType(name); 13519 self._listAdd(["mozilla", "dom"], name, isStruct) 13520 13521 def add(self, nativeType, isStruct=False): 13522 """ 13523 Add a forward declaration, where |nativeType| is a string containing 13524 the type and its namespaces, in the usual C++ way. 13525 """ 13526 self._ensureNonTemplateType(nativeType); 13527 components = nativeType.split('::') 13528 self._listAdd(components[:-1], components[-1], isStruct) 13529 13530 def _build(self, atTopLevel): 13531 """ 13532 Return a codegenerator for the forward declarations. 13533 """ 13534 decls = [] 13535 if self.decls: 13536 decls.append(CGList([CGClassForwardDeclare(cname, isStruct) 13537 for cname, isStruct in sorted(self.decls)])) 13538 for namespace, child in sorted(self.children.iteritems()): 13539 decls.append(CGNamespace(namespace, child._build(atTopLevel=False), declareOnly=True)) 13540 13541 cg = CGList(decls, "\n") 13542 if not atTopLevel and len(decls) + len(self.decls) > 1: 13543 cg = CGWrapper(cg, pre='\n', post='\n') 13544 return cg 13545 13546 def build(self): 13547 return self._build(atTopLevel=True) 13548 13549 def forwardDeclareForType(self, t, config): 13550 t = t.unroll() 13551 if t.isGeckoInterface(): 13552 name = t.inner.identifier.name 13553 try: 13554 desc = config.getDescriptor(name) 13555 self.add(desc.nativeType) 13556 except NoSuchDescriptorError: 13557 pass 13558 13559 # Note: Spidermonkey interfaces are typedefs, so can't be 13560 # forward-declared 13561 elif t.isCallback(): 13562 self.addInMozillaDom(t.callback.identifier.name) 13563 elif t.isDictionary(): 13564 self.addInMozillaDom(t.inner.identifier.name, isStruct=True) 13565 elif t.isCallbackInterface(): 13566 self.addInMozillaDom(t.inner.identifier.name) 13567 elif t.isUnion(): 13568 # Forward declare both the owning and non-owning version, 13569 # since we don't know which one we might want 13570 self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False)) 13571 self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True)) 13572 elif t.isMozMap(): 13573 self.forwardDeclareForType(t.inner, config) 13574 # Don't need to do anything for void, primitive, string, any or object. 13575 # There may be some other cases we are missing. 13576 13577 13578class CGForwardDeclarations(CGWrapper): 13579 """ 13580 Code generate the forward declarations for a header file. 13581 additionalDeclarations is a list of tuples containing a classname and a 13582 boolean. If the boolean is true we will declare a struct, otherwise we'll 13583 declare a class. 13584 """ 13585 def __init__(self, config, descriptors, callbacks, 13586 dictionaries, callbackInterfaces, additionalDeclarations=[]): 13587 builder = ForwardDeclarationBuilder() 13588 13589 # Needed for at least Wrap. 13590 for d in descriptors: 13591 # If this is a generated iterator interface, we only create these 13592 # in the generated bindings, and don't need to forward declare. 13593 if d.interface.isIteratorInterface(): 13594 continue 13595 builder.add(d.nativeType) 13596 # If we're an interface and we have a maplike/setlike declaration, 13597 # we'll have helper functions exposed to the native side of our 13598 # bindings, which will need to show up in the header. If either of 13599 # our key/value types are interfaces, they'll be passed as 13600 # arguments to helper functions, and they'll need to be forward 13601 # declared in the header. 13602 if d.interface.maplikeOrSetlikeOrIterable: 13603 if d.interface.maplikeOrSetlikeOrIterable.hasKeyType(): 13604 builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType, 13605 config) 13606 if d.interface.maplikeOrSetlikeOrIterable.hasValueType(): 13607 builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.valueType, 13608 config) 13609 13610 # We just about always need NativePropertyHooks 13611 builder.addInMozillaDom("NativePropertyHooks", isStruct=True) 13612 builder.addInMozillaDom("ProtoAndIfaceCache") 13613 # Add the atoms cache type, even if we don't need it. 13614 for d in descriptors: 13615 # Iterators have native types that are template classes, so 13616 # creating an 'Atoms' cache type doesn't work for them, and is one 13617 # of the cases where we don't need it anyways. 13618 if d.interface.isIteratorInterface(): 13619 continue 13620 builder.add(d.nativeType + "Atoms", isStruct=True) 13621 13622 for callback in callbacks: 13623 builder.addInMozillaDom(callback.identifier.name) 13624 for t in getTypesFromCallback(callback): 13625 builder.forwardDeclareForType(t, config) 13626 13627 for d in callbackInterfaces: 13628 builder.add(d.nativeType) 13629 builder.add(d.nativeType + "Atoms", isStruct=True) 13630 for t in getTypesFromDescriptor(d): 13631 builder.forwardDeclareForType(t, config) 13632 13633 for d in dictionaries: 13634 if len(d.members) > 0: 13635 builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True) 13636 for t in getTypesFromDictionary(d): 13637 builder.forwardDeclareForType(t, config) 13638 13639 for className, isStruct in additionalDeclarations: 13640 builder.add(className, isStruct=isStruct) 13641 13642 CGWrapper.__init__(self, builder.build()) 13643 13644 13645class CGBindingRoot(CGThing): 13646 """ 13647 Root codegen class for binding generation. Instantiate the class, and call 13648 declare or define to generate header or cpp code (respectively). 13649 """ 13650 def __init__(self, config, prefix, webIDLFile): 13651 bindingHeaders = dict.fromkeys(( 13652 'mozilla/dom/NonRefcountedDOMObject.h', 13653 ), 13654 True) 13655 bindingDeclareHeaders = dict.fromkeys(( 13656 'mozilla/dom/BindingDeclarations.h', 13657 'mozilla/dom/Nullable.h', 13658 'mozilla/ErrorResult.h', 13659 ), 13660 True) 13661 13662 descriptors = config.getDescriptors(webIDLFile=webIDLFile, 13663 hasInterfaceOrInterfacePrototypeObject=True) 13664 13665 unionTypes = UnionsForFile(config, webIDLFile) 13666 13667 (unionHeaders, unionImplheaders, unionDeclarations, traverseMethods, 13668 unlinkMethods, unionStructs) = UnionTypes(unionTypes, config) 13669 13670 bindingDeclareHeaders.update(dict.fromkeys(unionHeaders, True)) 13671 bindingHeaders.update(dict.fromkeys(unionImplheaders, True)) 13672 bindingDeclareHeaders["mozilla/dom/UnionMember.h"] = len(unionStructs) > 0 13673 bindingDeclareHeaders["mozilla/dom/FakeString.h"] = len(unionStructs) > 0 13674 # BindingUtils.h is only needed for SetToObject. 13675 # If it stops being inlined or stops calling CallerSubsumes 13676 # both this bit and the bit in UnionTypes can be removed. 13677 bindingDeclareHeaders["mozilla/dom/BindingUtils.h"] = any(d.isObject() for t in unionTypes 13678 for d in t.flatMemberTypes) 13679 bindingDeclareHeaders["mozilla/dom/IterableIterator.h"] = any(d.interface.isIteratorInterface() or 13680 d.interface.isIterable() for d in descriptors) 13681 13682 def descriptorHasCrossOriginProperties(desc): 13683 def hasCrossOriginProperty(m): 13684 props = memberProperties(m, desc) 13685 return (props.isCrossOriginMethod or 13686 props.isCrossOriginGetter or 13687 props.isCrossOriginSetter) 13688 13689 return any(hasCrossOriginProperty(m) for m in desc.interface.members) 13690 13691 bindingDeclareHeaders["jsapi.h"] = any(descriptorHasCrossOriginProperties(d) for d in descriptors) 13692 bindingDeclareHeaders["jspubtd.h"] = not bindingDeclareHeaders["jsapi.h"] 13693 bindingDeclareHeaders["js/RootingAPI.h"] = not bindingDeclareHeaders["jsapi.h"] 13694 13695 def descriptorRequiresPreferences(desc): 13696 iface = desc.interface 13697 return any(m.getExtendedAttribute("Pref") for m in iface.members + [iface]) 13698 13699 def descriptorDeprecated(desc): 13700 iface = desc.interface 13701 return any(m.getExtendedAttribute("Deprecated") for m in iface.members + [iface]) 13702 13703 bindingHeaders["nsIDocument.h"] = any( 13704 descriptorDeprecated(d) for d in descriptors) 13705 bindingHeaders["mozilla/Preferences.h"] = any( 13706 descriptorRequiresPreferences(d) for d in descriptors) 13707 bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any( 13708 d.concrete and d.proxy for d in descriptors) 13709 13710 def descriptorHasChromeOnly(desc): 13711 ctor = desc.interface.ctor() 13712 13713 return (any(isChromeOnly(a) or needsContainsHack(a) or 13714 needsCallerType(a) 13715 for a in desc.interface.members) or 13716 desc.interface.getExtendedAttribute("ChromeOnly") is not None or 13717 # JS-implemented interfaces with an interface object get a 13718 # chromeonly _create method. And interfaces with an 13719 # interface object might have a ChromeOnly constructor. 13720 (desc.interface.hasInterfaceObject() and 13721 (desc.interface.isJSImplemented() or 13722 (ctor and isChromeOnly(ctor)))) or 13723 # JS-implemented interfaces with clearable cached 13724 # attrs have chromeonly _clearFoo methods. 13725 (desc.interface.isJSImplemented() and 13726 any(clearableCachedAttrs(desc)))) 13727 13728 # XXXkhuey ugly hack but this is going away soon. 13729 bindingHeaders['xpcprivate.h'] = webIDLFile.endswith("EventTarget.webidl") 13730 13731 hasThreadChecks = any(d.hasThreadChecks() for d in descriptors) 13732 bindingHeaders["nsThreadUtils.h"] = hasThreadChecks 13733 13734 dictionaries = config.getDictionaries(webIDLFile) 13735 13736 def dictionaryHasChromeOnly(dictionary): 13737 while dictionary: 13738 if (any(isChromeOnly(m) for m in dictionary.members)): 13739 return True 13740 dictionary = dictionary.parent 13741 return False 13742 13743 bindingHeaders["nsContentUtils.h"] = ( 13744 any(descriptorHasChromeOnly(d) for d in descriptors) or 13745 any(dictionaryHasChromeOnly(d) for d in dictionaries)) 13746 hasNonEmptyDictionaries = any( 13747 len(dict.members) > 0 for dict in dictionaries) 13748 callbacks = config.getCallbacks(webIDLFile) 13749 callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile, 13750 isCallback=True) 13751 jsImplemented = config.getDescriptors(webIDLFile=webIDLFile, 13752 isJSImplemented=True) 13753 bindingDeclareHeaders["nsWeakReference.h"] = jsImplemented 13754 bindingHeaders["nsIGlobalObject.h"] = jsImplemented 13755 bindingHeaders["AtomList.h"] = hasNonEmptyDictionaries or jsImplemented or callbackDescriptors 13756 13757 def descriptorClearsPropsInSlots(descriptor): 13758 if not descriptor.wrapperCache: 13759 return False 13760 return any(m.isAttr() and m.getExtendedAttribute("StoreInSlot") 13761 for m in descriptor.interface.members) 13762 bindingHeaders["nsJSUtils.h"] = any(descriptorClearsPropsInSlots(d) for d in descriptors) 13763 13764 # Do codegen for all the enums 13765 enums = config.getEnums(webIDLFile) 13766 cgthings = [CGEnum(e) for e in enums] 13767 13768 hasCode = (descriptors or callbackDescriptors or dictionaries or 13769 callbacks) 13770 bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode 13771 bindingHeaders["mozilla/OwningNonNull.h"] = hasCode 13772 bindingHeaders["mozilla/dom/BindingDeclarations.h"] = ( 13773 not hasCode and enums) 13774 13775 bindingHeaders["WrapperFactory.h"] = descriptors 13776 bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors 13777 bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI 13778 # Ensure we see our enums in the generated .cpp file, for the ToJSValue 13779 # method body. Also ensure that we see jsapi.h. 13780 if enums: 13781 bindingHeaders[CGHeaders.getDeclarationFilename(enums[0])] = True 13782 bindingHeaders["jsapi.h"] = True 13783 13784 # For things that have [UseCounter] 13785 def descriptorRequiresTelemetry(desc): 13786 iface = desc.interface 13787 return any(m.getExtendedAttribute("UseCounter") for m in iface.members) 13788 bindingHeaders["mozilla/UseCounter.h"] = any( 13789 descriptorRequiresTelemetry(d) for d in descriptors) 13790 bindingHeaders["mozilla/dom/SimpleGlobalObject.h"] = any( 13791 CGDictionary.dictionarySafeToJSONify(d) for d in dictionaries) 13792 bindingHeaders["XrayWrapper.h"] = any( 13793 d.wantsXrays and d.wantsXrayExpandoClass for d in descriptors) 13794 bindingHeaders["mozilla/dom/XrayExpandoClass.h"] = any( 13795 d.wantsXrays for d in descriptors) 13796 13797 cgthings.extend(traverseMethods) 13798 cgthings.extend(unlinkMethods) 13799 13800 # Do codegen for all the dictionaries. We have to be a bit careful 13801 # here, because we have to generate these in order from least derived 13802 # to most derived so that class inheritance works out. We also have to 13803 # generate members before the dictionary that contains them. 13804 13805 def getDependenciesFromType(type): 13806 if type.isDictionary(): 13807 return set([type.unroll().inner]) 13808 if type.isSequence(): 13809 return getDependenciesFromType(type.unroll()) 13810 if type.isUnion(): 13811 return set([type.unroll()]) 13812 return set() 13813 13814 def getDependencies(unionTypeOrDictionary): 13815 if isinstance(unionTypeOrDictionary, IDLDictionary): 13816 deps = set() 13817 if unionTypeOrDictionary.parent: 13818 deps.add(unionTypeOrDictionary.parent) 13819 for member in unionTypeOrDictionary.members: 13820 deps |= getDependenciesFromType(member.type) 13821 return deps 13822 13823 assert unionTypeOrDictionary.isType() and unionTypeOrDictionary.isUnion() 13824 deps = set() 13825 for member in unionTypeOrDictionary.flatMemberTypes: 13826 deps |= getDependenciesFromType(member) 13827 return deps 13828 13829 def getName(unionTypeOrDictionary): 13830 if isinstance(unionTypeOrDictionary, IDLDictionary): 13831 return unionTypeOrDictionary.identifier.name 13832 13833 assert unionTypeOrDictionary.isType() and unionTypeOrDictionary.isUnion() 13834 return unionTypeOrDictionary.name 13835 13836 for t in dependencySortObjects(dictionaries + unionStructs, getDependencies, getName): 13837 if t.isDictionary(): 13838 cgthings.append(CGDictionary(t, config)) 13839 else: 13840 assert t.isUnion() 13841 cgthings.append(CGUnionStruct(t, config)) 13842 cgthings.append(CGUnionStruct(t, config, True)) 13843 13844 # Do codegen for all the callbacks. 13845 cgthings.extend(CGCallbackFunction(c, config) for c in callbacks) 13846 13847 cgthings.extend([CGNamespace('binding_detail', CGFastCallback(c)) 13848 for c in callbacks]) 13849 13850 # Do codegen for all the descriptors 13851 cgthings.extend([CGDescriptor(x) for x in descriptors]) 13852 13853 # Do codegen for all the callback interfaces. 13854 cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors]) 13855 13856 cgthings.extend([CGNamespace('binding_detail', 13857 CGFastCallback(x.interface)) 13858 for x in callbackDescriptors]) 13859 13860 # Do codegen for JS implemented classes 13861 def getParentDescriptor(desc): 13862 if not desc.interface.parent: 13863 return set() 13864 return {desc.getDescriptor(desc.interface.parent.identifier.name)} 13865 for x in dependencySortObjects(jsImplemented, getParentDescriptor, 13866 lambda d: d.interface.identifier.name): 13867 cgthings.append(CGCallbackInterface(x, typedArraysAreStructs=True)) 13868 cgthings.append(CGJSImplClass(x)) 13869 13870 # And make sure we have the right number of newlines at the end 13871 curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n") 13872 13873 # Wrap all of that in our namespaces. 13874 curr = CGNamespace.build(['mozilla', 'dom'], 13875 CGWrapper(curr, pre="\n")) 13876 13877 curr = CGList([CGForwardDeclarations(config, descriptors, 13878 callbacks, 13879 dictionaries, 13880 callbackDescriptors + jsImplemented, 13881 additionalDeclarations=unionDeclarations), 13882 curr], 13883 "\n") 13884 13885 # Add header includes. 13886 bindingHeaders = [header 13887 for header, include in bindingHeaders.iteritems() 13888 if include] 13889 bindingDeclareHeaders = [header 13890 for header, include in bindingDeclareHeaders.iteritems() 13891 if include] 13892 13893 curr = CGHeaders(descriptors, 13894 dictionaries, 13895 callbacks, 13896 callbackDescriptors, 13897 bindingDeclareHeaders, 13898 bindingHeaders, 13899 prefix, 13900 curr, 13901 config, 13902 jsImplemented) 13903 13904 # Add include guards. 13905 curr = CGIncludeGuard(prefix, curr) 13906 13907 # Add the auto-generated comment. 13908 curr = CGWrapper( 13909 curr, 13910 pre=(AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT % 13911 os.path.basename(webIDLFile))) 13912 13913 # Store the final result. 13914 self.root = curr 13915 13916 def declare(self): 13917 return stripTrailingWhitespace(self.root.declare()) 13918 13919 def define(self): 13920 return stripTrailingWhitespace(self.root.define()) 13921 13922 def deps(self): 13923 return self.root.deps() 13924 13925 13926class CGNativeMember(ClassMethod): 13927 def __init__(self, descriptorProvider, member, name, signature, extendedAttrs, 13928 breakAfter=True, passJSBitsAsNeeded=True, visibility="public", 13929 typedArraysAreStructs=True, variadicIsSequence=False, 13930 resultNotAddRefed=False, 13931 virtual=False, 13932 override=False): 13933 """ 13934 If typedArraysAreStructs is false, typed arrays will be passed as 13935 JS::Handle<JSObject*>. If it's true they will be passed as one of the 13936 dom::TypedArray subclasses. 13937 13938 If passJSBitsAsNeeded is false, we don't automatically pass in a 13939 JSContext* or a JSObject* based on the return and argument types. We 13940 can still pass it based on 'implicitJSContext' annotations. 13941 """ 13942 self.descriptorProvider = descriptorProvider 13943 self.member = member 13944 self.extendedAttrs = extendedAttrs 13945 self.resultAlreadyAddRefed = not resultNotAddRefed 13946 self.passJSBitsAsNeeded = passJSBitsAsNeeded 13947 self.typedArraysAreStructs = typedArraysAreStructs 13948 self.variadicIsSequence = variadicIsSequence 13949 breakAfterSelf = "\n" if breakAfter else "" 13950 ClassMethod.__init__(self, name, 13951 self.getReturnType(signature[0], False), 13952 self.getArgs(signature[0], signature[1]), 13953 static=member.isStatic(), 13954 # Mark our getters, which are attrs that 13955 # have a non-void return type, as const. 13956 const=(not member.isStatic() and member.isAttr() and 13957 not signature[0].isVoid()), 13958 breakAfterReturnDecl=" ", 13959 breakAfterSelf=breakAfterSelf, 13960 visibility=visibility, 13961 virtual=virtual, 13962 override=override) 13963 13964 def getReturnType(self, type, isMember): 13965 return self.getRetvalInfo(type, isMember)[0] 13966 13967 def getRetvalInfo(self, type, isMember): 13968 """ 13969 Returns a tuple: 13970 13971 The first element is the type declaration for the retval 13972 13973 The second element is a default value that can be used on error returns. 13974 For cases whose behavior depends on isMember, the second element will be 13975 None if isMember is true. 13976 13977 The third element is a template for actually returning a value stored in 13978 "${declName}" and "${holderName}". This means actually returning it if 13979 we're not outparam, else assigning to the "retval" outparam. If 13980 isMember is true, this can be None, since in that case the caller will 13981 never examine this value. 13982 """ 13983 if type.isVoid(): 13984 return "void", "", "" 13985 if type.isPrimitive() and type.tag() in builtinNames: 13986 result = CGGeneric(builtinNames[type.tag()]) 13987 defaultReturnArg = "0" 13988 if type.nullable(): 13989 result = CGTemplatedType("Nullable", result) 13990 defaultReturnArg = "" 13991 return (result.define(), 13992 "%s(%s)" % (result.define(), defaultReturnArg), 13993 "return ${declName};\n") 13994 if type.isDOMString() or type.isUSVString(): 13995 if isMember: 13996 # No need for a third element in the isMember case 13997 return "nsString", None, None 13998 # Outparam 13999 return "void", "", "aRetVal = ${declName};\n" 14000 if type.isByteString(): 14001 if isMember: 14002 # No need for a third element in the isMember case 14003 return "nsCString", None, None 14004 # Outparam 14005 return "void", "", "aRetVal = ${declName};\n" 14006 if type.isEnum(): 14007 enumName = type.unroll().inner.identifier.name 14008 if type.nullable(): 14009 enumName = CGTemplatedType("Nullable", 14010 CGGeneric(enumName)).define() 14011 defaultValue = "%s()" % enumName 14012 else: 14013 defaultValue = "%s(0)" % enumName 14014 return enumName, defaultValue, "return ${declName};\n" 14015 if type.isGeckoInterface(): 14016 iface = type.unroll().inner 14017 result = CGGeneric(self.descriptorProvider.getDescriptor( 14018 iface.identifier.name).prettyNativeType) 14019 if self.resultAlreadyAddRefed: 14020 if isMember: 14021 holder = "RefPtr" 14022 else: 14023 holder = "already_AddRefed" 14024 if memberReturnsNewObject(self.member) or isMember: 14025 warning = "" 14026 else: 14027 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" 14028 result = CGWrapper(result, 14029 pre=("%s%s<" % (warning, holder)), 14030 post=">") 14031 else: 14032 result = CGWrapper(result, post="*") 14033 # Since we always force an owning type for callback return values, 14034 # our ${declName} is an OwningNonNull or RefPtr. So we can just 14035 # .forget() to get our already_AddRefed. 14036 return result.define(), "nullptr", "return ${declName}.forget();\n" 14037 if type.isCallback(): 14038 return ("already_AddRefed<%s>" % type.unroll().callback.identifier.name, 14039 "nullptr", "return ${declName}.forget();\n") 14040 if type.isAny(): 14041 if isMember: 14042 # No need for a third element in the isMember case 14043 return "JS::Value", None, None 14044 # Outparam 14045 return "void", "", "aRetVal.set(${declName});\n" 14046 14047 if type.isObject(): 14048 if isMember: 14049 # No need for a third element in the isMember case 14050 return "JSObject*", None, None 14051 return "void", "", "aRetVal.set(${declName});\n" 14052 if type.isSpiderMonkeyInterface(): 14053 if isMember: 14054 # No need for a third element in the isMember case 14055 return "JSObject*", None, None 14056 if type.nullable(): 14057 returnCode = "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj()" 14058 else: 14059 returnCode = "${declName}.Obj()" 14060 return "void", "", "aRetVal.set(%s);\n" % returnCode 14061 if type.isSequence(): 14062 # If we want to handle sequence-of-sequences return values, we're 14063 # going to need to fix example codegen to not produce nsTArray<void> 14064 # for the relevant argument... 14065 assert not isMember 14066 # Outparam. 14067 if type.nullable(): 14068 returnCode = dedent(""" 14069 if (${declName}.IsNull()) { 14070 aRetVal.SetNull(); 14071 } else { 14072 aRetVal.SetValue().SwapElements(${declName}.Value()); 14073 } 14074 """) 14075 else: 14076 returnCode = "aRetVal.SwapElements(${declName});\n" 14077 return "void", "", returnCode 14078 if type.isMozMap(): 14079 # If we want to handle MozMap-of-MozMap return values, we're 14080 # going to need to fix example codegen to not produce MozMap<void> 14081 # for the relevant argument... 14082 assert not isMember 14083 # In this case we convert directly into our outparam to start with 14084 return "void", "", "" 14085 if type.isDate(): 14086 result = CGGeneric("Date") 14087 if type.nullable(): 14088 result = CGTemplatedType("Nullable", result) 14089 return (result.define(), "%s()" % result.define(), 14090 "return ${declName};\n") 14091 if type.isDictionary(): 14092 if isMember: 14093 # Only the first member of the tuple matters here, but return 14094 # bogus values for the others in case someone decides to use 14095 # them. 14096 return CGDictionary.makeDictionaryName(type.inner), None, None 14097 # In this case we convert directly into our outparam to start with 14098 return "void", "", "" 14099 if type.isUnion(): 14100 if isMember: 14101 # Only the first member of the tuple matters here, but return 14102 # bogus values for the others in case someone decides to use 14103 # them. 14104 return CGUnionStruct.unionTypeDecl(type, True), None, None 14105 # In this case we convert directly into our outparam to start with 14106 return "void", "", "" 14107 14108 raise TypeError("Don't know how to declare return value for %s" % 14109 type) 14110 14111 def getArgs(self, returnType, argList): 14112 args = [self.getArg(arg) for arg in argList] 14113 # Now the outparams 14114 if returnType.isDOMString() or returnType.isUSVString(): 14115 args.append(Argument("nsString&", "aRetVal")) 14116 elif returnType.isByteString(): 14117 args.append(Argument("nsCString&", "aRetVal")) 14118 elif returnType.isSequence(): 14119 nullable = returnType.nullable() 14120 if nullable: 14121 returnType = returnType.inner 14122 # And now the actual underlying type 14123 elementDecl = self.getReturnType(returnType.inner, True) 14124 type = CGTemplatedType("nsTArray", CGGeneric(elementDecl)) 14125 if nullable: 14126 type = CGTemplatedType("Nullable", type) 14127 args.append(Argument("%s&" % type.define(), "aRetVal")) 14128 elif returnType.isMozMap(): 14129 nullable = returnType.nullable() 14130 if nullable: 14131 returnType = returnType.inner 14132 # And now the actual underlying type 14133 elementDecl = self.getReturnType(returnType.inner, True) 14134 type = CGTemplatedType("MozMap", CGGeneric(elementDecl)) 14135 if nullable: 14136 type = CGTemplatedType("Nullable", type) 14137 args.append(Argument("%s&" % type.define(), "aRetVal")) 14138 elif returnType.isDictionary(): 14139 nullable = returnType.nullable() 14140 if nullable: 14141 returnType = returnType.inner 14142 dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner)) 14143 if nullable: 14144 dictType = CGTemplatedType("Nullable", dictType) 14145 args.append(Argument("%s&" % dictType.define(), "aRetVal")) 14146 elif returnType.isUnion(): 14147 args.append(Argument("%s&" % 14148 CGUnionStruct.unionTypeDecl(returnType, True), 14149 "aRetVal")) 14150 elif returnType.isAny(): 14151 args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal")) 14152 elif returnType.isObject() or returnType.isSpiderMonkeyInterface(): 14153 args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal")) 14154 14155 # And the nsIPrincipal 14156 if self.member.getExtendedAttribute('NeedsSubjectPrincipal'): 14157 # Cheat and assume self.descriptorProvider is a descriptor 14158 if self.descriptorProvider.interface.isExposedInAnyWorker(): 14159 args.append(Argument("Maybe<nsIPrincipal*>", "aSubjectPrincipal")) 14160 else: 14161 args.append(Argument("nsIPrincipal&", "aPrincipal")) 14162 # And the caller type, if desired. 14163 if needsCallerType(self.member): 14164 args.append(Argument("CallerType", "aCallerType")) 14165 # And the ErrorResult 14166 if 'infallible' not in self.extendedAttrs: 14167 # Use aRv so it won't conflict with local vars named "rv" 14168 args.append(Argument("ErrorResult&", "aRv")) 14169 # The legacycaller thisval 14170 if self.member.isMethod() and self.member.isLegacycaller(): 14171 # If it has an identifier, we can't deal with it yet 14172 assert self.member.isIdentifierLess() 14173 args.insert(0, Argument("const JS::Value&", "aThisVal")) 14174 # And jscontext bits. 14175 if needCx(returnType, argList, self.extendedAttrs, 14176 self.passJSBitsAsNeeded, self.member.isStatic()): 14177 args.insert(0, Argument("JSContext*", "cx")) 14178 if needScopeObject(returnType, argList, self.extendedAttrs, 14179 self.descriptorProvider.wrapperCache, 14180 self.passJSBitsAsNeeded, 14181 self.member.getExtendedAttribute("StoreInSlot")): 14182 args.insert(1, Argument("JS::Handle<JSObject*>", "obj")) 14183 # And if we're static, a global 14184 if self.member.isStatic(): 14185 args.insert(0, Argument("const GlobalObject&", "global")) 14186 return args 14187 14188 def doGetArgType(self, type, optional, isMember): 14189 """ 14190 The main work of getArgType. Returns a string type decl, whether this 14191 is a const ref, as well as whether the type should be wrapped in 14192 Nullable as needed. 14193 14194 isMember can be false or one of the strings "Sequence", "Variadic", 14195 "MozMap" 14196 """ 14197 if type.isSequence(): 14198 nullable = type.nullable() 14199 if nullable: 14200 type = type.inner 14201 elementType = type.inner 14202 argType = self.getArgType(elementType, False, "Sequence")[0] 14203 decl = CGTemplatedType("Sequence", argType) 14204 return decl.define(), True, True 14205 14206 if type.isMozMap(): 14207 nullable = type.nullable() 14208 if nullable: 14209 type = type.inner 14210 elementType = type.inner 14211 argType = self.getArgType(elementType, False, "MozMap")[0] 14212 decl = CGTemplatedType("MozMap", argType) 14213 return decl.define(), True, True 14214 14215 if type.isUnion(): 14216 # unionTypeDecl will handle nullable types, so return False for 14217 # auto-wrapping in Nullable 14218 return CGUnionStruct.unionTypeDecl(type, isMember), True, False 14219 14220 if type.isGeckoInterface() and not type.isCallbackInterface(): 14221 iface = type.unroll().inner 14222 argIsPointer = type.nullable() or iface.isExternal() 14223 forceOwningType = (iface.isCallback() or isMember or 14224 iface.identifier.name == "Promise") 14225 if argIsPointer: 14226 if (optional or isMember) and forceOwningType: 14227 typeDecl = "RefPtr<%s>" 14228 else: 14229 typeDecl = "%s*" 14230 else: 14231 if optional or isMember: 14232 if forceOwningType: 14233 typeDecl = "OwningNonNull<%s>" 14234 else: 14235 typeDecl = "NonNull<%s>" 14236 else: 14237 typeDecl = "%s&" 14238 return ((typeDecl % 14239 self.descriptorProvider.getDescriptor(iface.identifier.name).prettyNativeType), 14240 False, False) 14241 14242 if type.isSpiderMonkeyInterface(): 14243 if not self.typedArraysAreStructs: 14244 return "JS::Handle<JSObject*>", False, False 14245 14246 # Unroll for the name, in case we're nullable. 14247 return type.unroll().name, True, True 14248 14249 if type.isDOMString() or type.isUSVString(): 14250 if isMember: 14251 declType = "nsString" 14252 else: 14253 declType = "nsAString" 14254 return declType, True, False 14255 14256 if type.isByteString(): 14257 declType = "nsCString" 14258 return declType, True, False 14259 14260 if type.isEnum(): 14261 return type.unroll().inner.identifier.name, False, True 14262 14263 if type.isCallback() or type.isCallbackInterface(): 14264 forceOwningType = optional or isMember 14265 if type.nullable(): 14266 if forceOwningType: 14267 declType = "RefPtr<%s>" 14268 else: 14269 declType = "%s*" 14270 else: 14271 if forceOwningType: 14272 declType = "OwningNonNull<%s>" 14273 else: 14274 declType = "%s&" 14275 if type.isCallback(): 14276 name = type.unroll().callback.identifier.name 14277 else: 14278 name = type.unroll().inner.identifier.name 14279 return declType % name, False, False 14280 14281 if type.isAny(): 14282 # Don't do the rooting stuff for variadics for now 14283 if isMember: 14284 declType = "JS::Value" 14285 else: 14286 declType = "JS::Handle<JS::Value>" 14287 return declType, False, False 14288 14289 if type.isObject(): 14290 if isMember: 14291 declType = "JSObject*" 14292 else: 14293 declType = "JS::Handle<JSObject*>" 14294 return declType, False, False 14295 14296 if type.isDictionary(): 14297 typeName = CGDictionary.makeDictionaryName(type.inner) 14298 return typeName, True, True 14299 14300 if type.isDate(): 14301 return "Date", False, True 14302 14303 assert type.isPrimitive() 14304 14305 return builtinNames[type.tag()], False, True 14306 14307 def getArgType(self, type, optional, isMember): 14308 """ 14309 Get the type of an argument declaration. Returns the type CGThing, and 14310 whether this should be a const ref. 14311 14312 isMember can be False, "Sequence", or "Variadic" 14313 """ 14314 decl, ref, handleNullable = self.doGetArgType(type, optional, isMember) 14315 decl = CGGeneric(decl) 14316 if handleNullable and type.nullable(): 14317 decl = CGTemplatedType("Nullable", decl) 14318 ref = True 14319 if isMember == "Variadic": 14320 arrayType = "Sequence" if self.variadicIsSequence else "nsTArray" 14321 decl = CGTemplatedType(arrayType, decl) 14322 ref = True 14323 elif optional: 14324 # Note: All variadic args claim to be optional, but we can just use 14325 # empty arrays to represent them not being present. 14326 decl = CGTemplatedType("Optional", decl) 14327 ref = True 14328 return (decl, ref) 14329 14330 def getArg(self, arg): 14331 """ 14332 Get the full argument declaration for an argument 14333 """ 14334 decl, ref = self.getArgType(arg.type, arg.canHaveMissingValue(), 14335 "Variadic" if arg.variadic else False) 14336 if ref: 14337 decl = CGWrapper(decl, pre="const ", post="&") 14338 14339 return Argument(decl.define(), arg.identifier.name) 14340 14341 def arguments(self): 14342 return self.member.signatures()[0][1] 14343 14344 14345class CGExampleMethod(CGNativeMember): 14346 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): 14347 CGNativeMember.__init__(self, descriptor, method, 14348 CGSpecializedMethod.makeNativeName(descriptor, 14349 method), 14350 signature, 14351 descriptor.getExtendedAttributes(method), 14352 breakAfter=breakAfter, 14353 variadicIsSequence=True) 14354 14355 def declare(self, cgClass): 14356 assert self.member.isMethod() 14357 # We skip declaring ourselves if this is a maplike/setlike/iterable 14358 # method, because those get implemented automatically by the binding 14359 # machinery, so the implementor of the interface doesn't have to worry 14360 # about it. 14361 if self.member.isMaplikeOrSetlikeOrIterableMethod(): 14362 return '' 14363 return CGNativeMember.declare(self, cgClass); 14364 14365 def define(self, cgClass): 14366 return '' 14367 14368 14369class CGExampleGetter(CGNativeMember): 14370 def __init__(self, descriptor, attr): 14371 CGNativeMember.__init__(self, descriptor, attr, 14372 CGSpecializedGetter.makeNativeName(descriptor, 14373 attr), 14374 (attr.type, []), 14375 descriptor.getExtendedAttributes(attr, 14376 getter=True)) 14377 14378 def declare(self, cgClass): 14379 assert self.member.isAttr() 14380 # We skip declaring ourselves if this is a maplike/setlike attr (in 14381 # practice, "size"), because those get implemented automatically by the 14382 # binding machinery, so the implementor of the interface doesn't have to 14383 # worry about it. 14384 if self.member.isMaplikeOrSetlikeAttr(): 14385 return '' 14386 return CGNativeMember.declare(self, cgClass); 14387 14388 def define(self, cgClass): 14389 return '' 14390 14391 14392class CGExampleSetter(CGNativeMember): 14393 def __init__(self, descriptor, attr): 14394 CGNativeMember.__init__(self, descriptor, attr, 14395 CGSpecializedSetter.makeNativeName(descriptor, 14396 attr), 14397 (BuiltinTypes[IDLBuiltinType.Types.void], 14398 [FakeArgument(attr.type, attr)]), 14399 descriptor.getExtendedAttributes(attr, 14400 setter=True)) 14401 14402 def define(self, cgClass): 14403 return '' 14404 14405 14406class CGBindingImplClass(CGClass): 14407 """ 14408 Common codegen for generating a C++ implementation of a WebIDL interface 14409 """ 14410 def __init__(self, descriptor, cgMethod, cgGetter, cgSetter, wantGetParent=True, wrapMethodName="WrapObject", skipStaticMethods=False): 14411 """ 14412 cgMethod, cgGetter and cgSetter are classes used to codegen methods, 14413 getters and setters. 14414 """ 14415 self.descriptor = descriptor 14416 self._deps = descriptor.interface.getDeps() 14417 14418 iface = descriptor.interface 14419 14420 self.methodDecls = [] 14421 14422 def appendMethod(m, isConstructor=False): 14423 sigs = m.signatures() 14424 for s in sigs[:-1]: 14425 # Don't put a blank line after overloads, until we 14426 # get to the last one. 14427 self.methodDecls.append(cgMethod(descriptor, m, s, 14428 isConstructor, 14429 breakAfter=False)) 14430 self.methodDecls.append(cgMethod(descriptor, m, sigs[-1], 14431 isConstructor)) 14432 14433 if iface.ctor(): 14434 appendMethod(iface.ctor(), isConstructor=True) 14435 for n in iface.namedConstructors: 14436 appendMethod(n, isConstructor=True) 14437 for m in iface.members: 14438 if m.isMethod(): 14439 if m.isIdentifierLess(): 14440 continue 14441 if not m.isStatic() or not skipStaticMethods: 14442 appendMethod(m) 14443 elif m.isAttr(): 14444 self.methodDecls.append(cgGetter(descriptor, m)) 14445 if not m.readonly: 14446 self.methodDecls.append(cgSetter(descriptor, m)) 14447 14448 # Now do the special operations 14449 def appendSpecialOperation(name, op): 14450 if op is None: 14451 return 14452 if name == "IndexedCreator" or name == "NamedCreator": 14453 # These are identical to the setters 14454 return 14455 assert len(op.signatures()) == 1 14456 returnType, args = op.signatures()[0] 14457 # Make a copy of the args, since we plan to modify them. 14458 args = list(args) 14459 if op.isGetter() or op.isDeleter(): 14460 # This is a total hack. The '&' belongs with the 14461 # type, not the name! But it works, and is simpler 14462 # than trying to somehow make this pretty. 14463 args.append(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean], 14464 op, name="&found")) 14465 if name == "Stringifier": 14466 if op.isIdentifierLess(): 14467 # XXXbz I wish we were consistent about our renaming here. 14468 name = "Stringify" 14469 else: 14470 # We already added this method 14471 return 14472 if name == "LegacyCaller": 14473 if op.isIdentifierLess(): 14474 # XXXbz I wish we were consistent about our renaming here. 14475 name = "LegacyCall" 14476 else: 14477 # We already added this method 14478 return 14479 if name == "Jsonifier": 14480 # We already added this method 14481 return 14482 self.methodDecls.append( 14483 CGNativeMember(descriptor, op, 14484 name, 14485 (returnType, args), 14486 descriptor.getExtendedAttributes(op))) 14487 # Sort things by name so we get stable ordering in the output. 14488 ops = descriptor.operations.items() 14489 ops.sort(key=lambda x: x[0]) 14490 for name, op in ops: 14491 appendSpecialOperation(name, op) 14492 # If we support indexed properties, then we need a Length() 14493 # method so we know which indices are supported. 14494 if descriptor.supportsIndexedProperties(): 14495 # But we don't need it if we already have an infallible 14496 # "length" attribute, which we often do. 14497 haveLengthAttr = any( 14498 m for m in iface.members if m.isAttr() and 14499 CGSpecializedGetter.makeNativeName(descriptor, m) == "Length") 14500 if not haveLengthAttr: 14501 self.methodDecls.append( 14502 CGNativeMember(descriptor, FakeMember(), 14503 "Length", 14504 (BuiltinTypes[IDLBuiltinType.Types.unsigned_long], 14505 []), 14506 {"infallible": True})) 14507 # And if we support named properties we need to be able to 14508 # enumerate the supported names. 14509 if descriptor.supportsNamedProperties(): 14510 self.methodDecls.append( 14511 CGNativeMember( 14512 descriptor, FakeMember(), 14513 "GetSupportedNames", 14514 (IDLSequenceType(None, 14515 BuiltinTypes[IDLBuiltinType.Types.domstring]), 14516 []), 14517 {"infallible": True})) 14518 14519 wrapArgs = [Argument('JSContext*', 'aCx'), 14520 Argument('JS::Handle<JSObject*>', 'aGivenProto')] 14521 if not descriptor.wrapperCache: 14522 wrapReturnType = "bool" 14523 wrapArgs.append(Argument('JS::MutableHandle<JSObject*>', 14524 'aReflector')) 14525 else: 14526 wrapReturnType = "JSObject*" 14527 self.methodDecls.insert(0, 14528 ClassMethod(wrapMethodName, wrapReturnType, 14529 wrapArgs, virtual=descriptor.wrapperCache, 14530 breakAfterReturnDecl=" ", 14531 override=descriptor.wrapperCache, 14532 body=self.getWrapObjectBody())) 14533 if wantGetParent: 14534 self.methodDecls.insert(0, 14535 ClassMethod("GetParentObject", 14536 self.getGetParentObjectReturnType(), 14537 [], const=True, 14538 breakAfterReturnDecl=" ", 14539 body=self.getGetParentObjectBody())) 14540 14541 # Invoke CGClass.__init__ in any subclasses afterwards to do the actual codegen. 14542 14543 def getWrapObjectBody(self): 14544 return None 14545 14546 def getGetParentObjectReturnType(self): 14547 return ("// TODO: return something sensible here, and change the return type\n" 14548 "%s*" % self.descriptor.nativeType.split('::')[-1]) 14549 14550 def getGetParentObjectBody(self): 14551 return None 14552 14553 def deps(self): 14554 return self._deps 14555 14556 14557class CGExampleClass(CGBindingImplClass): 14558 """ 14559 Codegen for the actual example class implementation for this descriptor 14560 """ 14561 def __init__(self, descriptor): 14562 CGBindingImplClass.__init__(self, descriptor, 14563 CGExampleMethod, CGExampleGetter, CGExampleSetter, 14564 wantGetParent=descriptor.wrapperCache) 14565 14566 self.parentIface = descriptor.interface.parent 14567 if self.parentIface: 14568 self.parentDesc = descriptor.getDescriptor( 14569 self.parentIface.identifier.name) 14570 bases = [ClassBase(self.nativeLeafName(self.parentDesc))] 14571 else: 14572 bases = [ClassBase("nsISupports /* or NonRefcountedDOMObject if this is a non-refcounted object */")] 14573 if descriptor.wrapperCache: 14574 bases.append(ClassBase("nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */")) 14575 14576 destructorVisibility = "protected" 14577 if self.parentIface: 14578 extradeclarations = ( 14579 "public:\n" 14580 " NS_DECL_ISUPPORTS_INHERITED\n" 14581 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n" 14582 "\n" % (self.nativeLeafName(descriptor), 14583 self.nativeLeafName(self.parentDesc))) 14584 else: 14585 extradeclarations = ( 14586 "public:\n" 14587 " NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n" 14588 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n" 14589 "\n" % self.nativeLeafName(descriptor)) 14590 14591 if descriptor.interface.hasChildInterfaces(): 14592 decorators = "" 14593 else: 14594 decorators = "final" 14595 14596 CGClass.__init__(self, self.nativeLeafName(descriptor), 14597 bases=bases, 14598 constructors=[ClassConstructor([], 14599 visibility="public")], 14600 destructor=ClassDestructor(visibility=destructorVisibility), 14601 methods=self.methodDecls, 14602 decorators=decorators, 14603 extradeclarations=extradeclarations) 14604 14605 def define(self): 14606 # Just override CGClass and do our own thing 14607 ctordtor = dedent(""" 14608 ${nativeType}::${nativeType}() 14609 { 14610 // Add |MOZ_COUNT_CTOR(${nativeType});| for a non-refcounted object. 14611 } 14612 14613 ${nativeType}::~${nativeType}() 14614 { 14615 // Add |MOZ_COUNT_DTOR(${nativeType});| for a non-refcounted object. 14616 } 14617 """) 14618 14619 if self.parentIface: 14620 ccImpl = dedent(""" 14621 14622 // Only needed for refcounted objects. 14623 # error "If you don't have members that need cycle collection, 14624 # then remove all the cycle collection bits from this 14625 # implementation and the corresponding header. If you do, you 14626 # want NS_IMPL_CYCLE_COLLECTION_INHERITED(${nativeType}, 14627 # ${parentType}, your, members, here)" 14628 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType}) 14629 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType}) 14630 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType}) 14631 NS_INTERFACE_MAP_END_INHERITING(${parentType}) 14632 14633 """) 14634 else: 14635 ccImpl = dedent(""" 14636 14637 // Only needed for refcounted objects. 14638 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType}) 14639 NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType}) 14640 NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType}) 14641 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType}) 14642 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 14643 NS_INTERFACE_MAP_ENTRY(nsISupports) 14644 NS_INTERFACE_MAP_END 14645 14646 """) 14647 14648 if self.descriptor.wrapperCache: 14649 reflectorArg = "" 14650 reflectorPassArg = "" 14651 returnType = "JSObject*" 14652 else: 14653 reflectorArg = ", JS::MutableHandle<JSObject*> aReflector" 14654 reflectorPassArg = ", aReflector" 14655 returnType = "bool" 14656 classImpl = ccImpl + ctordtor + "\n" + dedent(""" 14657 ${returnType} 14658 ${nativeType}::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto${reflectorArg}) 14659 { 14660 return ${ifaceName}Binding::Wrap(aCx, this, aGivenProto${reflectorPassArg}); 14661 } 14662 14663 """) 14664 return string.Template(classImpl).substitute( 14665 ifaceName=self.descriptor.name, 14666 nativeType=self.nativeLeafName(self.descriptor), 14667 parentType=self.nativeLeafName(self.parentDesc) if self.parentIface else "", 14668 returnType=returnType, 14669 reflectorArg=reflectorArg, 14670 reflectorPassArg=reflectorPassArg) 14671 14672 @staticmethod 14673 def nativeLeafName(descriptor): 14674 return descriptor.nativeType.split('::')[-1] 14675 14676 14677class CGExampleRoot(CGThing): 14678 """ 14679 Root codegen class for example implementation generation. Instantiate the 14680 class and call declare or define to generate header or cpp code, 14681 respectively. 14682 """ 14683 def __init__(self, config, interfaceName): 14684 descriptor = config.getDescriptor(interfaceName) 14685 14686 self.root = CGWrapper(CGExampleClass(descriptor), 14687 pre="\n", post="\n") 14688 14689 self.root = CGNamespace.build(["mozilla", "dom"], self.root) 14690 14691 builder = ForwardDeclarationBuilder() 14692 for member in descriptor.interface.members: 14693 if not member.isAttr() and not member.isMethod(): 14694 continue 14695 if member.isStatic(): 14696 builder.addInMozillaDom("GlobalObject") 14697 if member.isAttr() and not member.isMaplikeOrSetlikeAttr(): 14698 builder.forwardDeclareForType(member.type, config) 14699 else: 14700 assert member.isMethod() 14701 if not member.isMaplikeOrSetlikeOrIterableMethod(): 14702 for sig in member.signatures(): 14703 builder.forwardDeclareForType(sig[0], config) 14704 for arg in sig[1]: 14705 builder.forwardDeclareForType(arg.type, config) 14706 14707 self.root = CGList([builder.build(), 14708 self.root], "\n") 14709 14710 # Throw in our #includes 14711 self.root = CGHeaders([], [], [], [], 14712 ["nsWrapperCache.h", 14713 "nsCycleCollectionParticipant.h", 14714 "mozilla/Attributes.h", 14715 "mozilla/ErrorResult.h", 14716 "mozilla/dom/BindingDeclarations.h", 14717 "js/TypeDecls.h"], 14718 ["mozilla/dom/%s.h" % interfaceName, 14719 ("mozilla/dom/%s" % 14720 CGHeaders.getDeclarationFilename(descriptor.interface))], "", self.root) 14721 14722 # And now some include guards 14723 self.root = CGIncludeGuard(interfaceName, self.root) 14724 14725 # And our license block comes before everything else 14726 self.root = CGWrapper(self.root, pre=dedent(""" 14727 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 14728 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 14729 /* This Source Code Form is subject to the terms of the Mozilla Public 14730 * License, v. 2.0. If a copy of the MPL was not distributed with this 14731 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 14732 14733 """)) 14734 14735 def declare(self): 14736 return self.root.declare() 14737 14738 def define(self): 14739 return self.root.define() 14740 14741 14742def jsImplName(name): 14743 return name + "JSImpl" 14744 14745 14746class CGJSImplMember(CGNativeMember): 14747 """ 14748 Base class for generating code for the members of the implementation class 14749 for a JS-implemented WebIDL interface. 14750 """ 14751 def __init__(self, descriptorProvider, member, name, signature, 14752 extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True, 14753 visibility="public", variadicIsSequence=False, 14754 virtual=False, override=False): 14755 CGNativeMember.__init__(self, descriptorProvider, member, name, 14756 signature, extendedAttrs, breakAfter=breakAfter, 14757 passJSBitsAsNeeded=passJSBitsAsNeeded, 14758 visibility=visibility, 14759 variadicIsSequence=variadicIsSequence, 14760 virtual=virtual, 14761 override=override) 14762 self.body = self.getImpl() 14763 14764 def getArgs(self, returnType, argList): 14765 args = CGNativeMember.getArgs(self, returnType, argList) 14766 args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) 14767 return args 14768 14769 14770class CGJSImplMethod(CGJSImplMember): 14771 """ 14772 Class for generating code for the methods for a JS-implemented WebIDL 14773 interface. 14774 """ 14775 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): 14776 virtual = False 14777 override = False 14778 if (method.identifier.name == "eventListenerWasAdded" or 14779 method.identifier.name == "eventListenerWasRemoved"): 14780 virtual = True 14781 override = True 14782 14783 self.signature = signature 14784 self.descriptor = descriptor 14785 self.isConstructor = isConstructor 14786 CGJSImplMember.__init__(self, descriptor, method, 14787 CGSpecializedMethod.makeNativeName(descriptor, 14788 method), 14789 signature, 14790 descriptor.getExtendedAttributes(method), 14791 breakAfter=breakAfter, 14792 variadicIsSequence=True, 14793 passJSBitsAsNeeded=False, 14794 virtual=virtual, 14795 override=override) 14796 14797 def getArgs(self, returnType, argList): 14798 if self.isConstructor: 14799 # Skip the JSCompartment bits for constructors; it's handled 14800 # manually in getImpl. 14801 return CGNativeMember.getArgs(self, returnType, argList) 14802 return CGJSImplMember.getArgs(self, returnType, argList) 14803 14804 def getImpl(self): 14805 args = self.getArgs(self.signature[0], self.signature[1]) 14806 if not self.isConstructor: 14807 return 'return mImpl->%s(%s);\n' % (self.name, ", ".join(arg.name for arg in args)) 14808 14809 assert self.descriptor.interface.isJSImplemented() 14810 if self.name != 'Constructor': 14811 raise TypeError("Named constructors are not supported for JS implemented WebIDL. See bug 851287.") 14812 if len(self.signature[1]) != 0: 14813 # The first two arguments to the constructor implementation are not 14814 # arguments to the WebIDL constructor, so don't pass them to __Init() 14815 assert args[0].argType == 'const GlobalObject&' 14816 assert args[1].argType == 'JSContext*' 14817 constructorArgs = [arg.name for arg in args[2:]] 14818 constructorArgs.append("js::GetObjectCompartment(scopeObj)") 14819 initCall = fill( 14820 """ 14821 // Wrap the object before calling __Init so that __DOM_IMPL__ is available. 14822 JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject()); 14823 MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx)); 14824 JS::Rooted<JS::Value> wrappedVal(cx); 14825 if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal)) { 14826 //XXX Assertion disabled for now, see bug 991271. 14827 MOZ_ASSERT(true || JS_IsExceptionPending(cx)); 14828 aRv.Throw(NS_ERROR_UNEXPECTED); 14829 return nullptr; 14830 } 14831 // Initialize the object with the constructor arguments. 14832 impl->mImpl->__Init(${args}); 14833 if (aRv.Failed()) { 14834 return nullptr; 14835 } 14836 """, 14837 args=", ".join(constructorArgs)) 14838 else: 14839 initCall = "" 14840 return genConstructorBody(self.descriptor, initCall) 14841 14842 14843def genConstructorBody(descriptor, initCall=""): 14844 return fill( 14845 """ 14846 JS::Rooted<JSObject*> jsImplObj(cx); 14847 nsCOMPtr<nsIGlobalObject> globalHolder = 14848 ConstructJSImplementation("${contractId}", global, &jsImplObj, aRv); 14849 if (aRv.Failed()) { 14850 return nullptr; 14851 } 14852 // Build the C++ implementation. 14853 RefPtr<${implClass}> impl = new ${implClass}(jsImplObj, globalHolder); 14854 $*{initCall} 14855 return impl.forget(); 14856 """, 14857 contractId=descriptor.interface.getJSImplementation(), 14858 implClass=descriptor.name, 14859 initCall=initCall) 14860 14861 14862# We're always fallible 14863def callbackGetterName(attr, descriptor): 14864 return "Get" + MakeNativeName( 14865 descriptor.binaryNameFor(attr.identifier.name)) 14866 14867 14868def callbackSetterName(attr, descriptor): 14869 return "Set" + MakeNativeName( 14870 descriptor.binaryNameFor(attr.identifier.name)) 14871 14872 14873class CGJSImplClearCachedValueMethod(CGAbstractBindingMethod): 14874 def __init__(self, descriptor, attr): 14875 if attr.getExtendedAttribute("StoreInSlot"): 14876 raise TypeError("[StoreInSlot] is not supported for JS-implemented WebIDL. See bug 1056325.") 14877 14878 CGAbstractBindingMethod.__init__(self, descriptor, 14879 MakeJSImplClearCachedValueNativeName(attr), 14880 JSNativeArguments()) 14881 self.attr = attr 14882 14883 def generate_code(self): 14884 return CGGeneric(fill( 14885 """ 14886 ${bindingNamespace}::${fnName}(self); 14887 args.rval().setUndefined(); 14888 return true; 14889 """, 14890 bindingNamespace=toBindingNamespace(self.descriptor.name), 14891 fnName=MakeClearCachedValueNativeName(self.attr))) 14892 14893 14894class CGJSImplGetter(CGJSImplMember): 14895 """ 14896 Class for generating code for the getters of attributes for a JS-implemented 14897 WebIDL interface. 14898 """ 14899 def __init__(self, descriptor, attr): 14900 CGJSImplMember.__init__(self, descriptor, attr, 14901 CGSpecializedGetter.makeNativeName(descriptor, 14902 attr), 14903 (attr.type, []), 14904 descriptor.getExtendedAttributes(attr, 14905 getter=True), 14906 passJSBitsAsNeeded=False) 14907 14908 def getImpl(self): 14909 callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])] 14910 return 'return mImpl->%s(%s);\n' % ( 14911 callbackGetterName(self.member, self.descriptorProvider), 14912 ", ".join(callbackArgs)) 14913 14914 14915class CGJSImplSetter(CGJSImplMember): 14916 """ 14917 Class for generating code for the setters of attributes for a JS-implemented 14918 WebIDL interface. 14919 """ 14920 def __init__(self, descriptor, attr): 14921 CGJSImplMember.__init__(self, descriptor, attr, 14922 CGSpecializedSetter.makeNativeName(descriptor, 14923 attr), 14924 (BuiltinTypes[IDLBuiltinType.Types.void], 14925 [FakeArgument(attr.type, attr)]), 14926 descriptor.getExtendedAttributes(attr, 14927 setter=True), 14928 passJSBitsAsNeeded=False) 14929 14930 def getImpl(self): 14931 callbackArgs = [arg.name for arg in self.getArgs(BuiltinTypes[IDLBuiltinType.Types.void], 14932 [FakeArgument(self.member.type, self.member)])] 14933 return 'mImpl->%s(%s);\n' % ( 14934 callbackSetterName(self.member, self.descriptorProvider), 14935 ", ".join(callbackArgs)) 14936 14937 14938class CGJSImplClass(CGBindingImplClass): 14939 def __init__(self, descriptor): 14940 CGBindingImplClass.__init__(self, descriptor, CGJSImplMethod, CGJSImplGetter, CGJSImplSetter, skipStaticMethods=True) 14941 14942 if descriptor.interface.parent: 14943 parentClass = descriptor.getDescriptor( 14944 descriptor.interface.parent.identifier.name).jsImplParent 14945 baseClasses = [ClassBase(parentClass)] 14946 isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n" 14947 ccDecl = ("NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" % 14948 (descriptor.name, parentClass)) 14949 constructorBody = dedent(""" 14950 // Make sure we're an nsWrapperCache already 14951 MOZ_ASSERT(static_cast<nsWrapperCache*>(this)); 14952 // And that our ancestor has not called SetIsNotDOMBinding() 14953 MOZ_ASSERT(IsDOMBinding()); 14954 """) 14955 extradefinitions = fill( 14956 """ 14957 NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent) 14958 NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass}) 14959 NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass}) 14960 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${ifaceName}) 14961 NS_INTERFACE_MAP_END_INHERITING(${parentClass}) 14962 """, 14963 ifaceName=self.descriptor.name, 14964 parentClass=parentClass) 14965 else: 14966 baseClasses = [ClassBase("nsSupportsWeakReference"), 14967 ClassBase("nsWrapperCache")] 14968 isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n" 14969 ccDecl = ("NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n" % 14970 descriptor.name) 14971 extradefinitions = fill( 14972 """ 14973 NS_IMPL_CYCLE_COLLECTION_CLASS(${ifaceName}) 14974 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(${ifaceName}) 14975 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl) 14976 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) 14977 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 14978 tmp->ClearWeakReferences(); 14979 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 14980 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName}) 14981 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl) 14982 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) 14983 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 14984 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 14985 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(${ifaceName}) 14986 NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName}) 14987 NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName}) 14988 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName}) 14989 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 14990 NS_INTERFACE_MAP_ENTRY(nsISupports) 14991 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 14992 NS_INTERFACE_MAP_END 14993 """, 14994 ifaceName=self.descriptor.name) 14995 14996 extradeclarations = fill( 14997 """ 14998 public: 14999 $*{isupportsDecl} 15000 $*{ccDecl} 15001 15002 private: 15003 RefPtr<${jsImplName}> mImpl; 15004 nsCOMPtr<nsISupports> mParent; 15005 15006 """, 15007 isupportsDecl=isupportsDecl, 15008 ccDecl=ccDecl, 15009 jsImplName=jsImplName(descriptor.name)) 15010 15011 if descriptor.interface.hasChildInterfaces(): 15012 decorators = "" 15013 # We need a protected virtual destructor our subclasses can use 15014 destructor = ClassDestructor(virtual=True, visibility="protected") 15015 else: 15016 decorators = "final" 15017 destructor = ClassDestructor(virtual=False, visibility="private") 15018 15019 baseConstructors = [ 15020 ("mImpl(new %s(nullptr, aJSImplObject, /* aIncumbentGlobal = */ nullptr))" % 15021 jsImplName(descriptor.name)), 15022 "mParent(aParent)"] 15023 parentInterface = descriptor.interface.parent 15024 while parentInterface: 15025 if parentInterface.isJSImplemented(): 15026 baseConstructors.insert( 15027 0, "%s(aJSImplObject, aParent)" % parentClass) 15028 break 15029 parentInterface = parentInterface.parent 15030 if not parentInterface and descriptor.interface.parent: 15031 # We only have C++ ancestors, so only pass along the window 15032 baseConstructors.insert(0, 15033 "%s(aParent)" % parentClass) 15034 15035 constructor = ClassConstructor( 15036 [Argument("JS::Handle<JSObject*>", "aJSImplObject"), 15037 Argument("nsIGlobalObject*", "aParent")], 15038 visibility="public", 15039 baseConstructors=baseConstructors) 15040 15041 self.methodDecls.append( 15042 ClassMethod("_Create", 15043 "bool", 15044 JSNativeArguments(), 15045 static=True, 15046 body=self.getCreateFromExistingBody())) 15047 15048 CGClass.__init__(self, descriptor.name, 15049 bases=baseClasses, 15050 constructors=[constructor], 15051 destructor=destructor, 15052 methods=self.methodDecls, 15053 decorators=decorators, 15054 extradeclarations=extradeclarations, 15055 extradefinitions=extradefinitions) 15056 15057 def getWrapObjectBody(self): 15058 return fill( 15059 """ 15060 JS::Rooted<JSObject*> obj(aCx, ${name}Binding::Wrap(aCx, this, aGivenProto)); 15061 if (!obj) { 15062 return nullptr; 15063 } 15064 15065 // Now define it on our chrome object 15066 JSAutoCompartment ac(aCx, mImpl->Callback()); 15067 if (!JS_WrapObject(aCx, &obj)) { 15068 return nullptr; 15069 } 15070 if (!JS_DefineProperty(aCx, mImpl->Callback(), "__DOM_IMPL__", obj, 0)) { 15071 return nullptr; 15072 } 15073 return obj; 15074 """, 15075 name=self.descriptor.name) 15076 15077 def getGetParentObjectReturnType(self): 15078 return "nsISupports*" 15079 15080 def getGetParentObjectBody(self): 15081 return "return mParent;\n" 15082 15083 def getCreateFromExistingBody(self): 15084 # XXXbz we could try to get parts of this (e.g. the argument 15085 # conversions) auto-generated by somehow creating an IDLMethod and 15086 # adding it to our interface, but we'd still need to special-case the 15087 # implementation slightly to have it not try to forward to the JS 15088 # object... 15089 return fill( 15090 """ 15091 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 15092 if (args.length() < 2) { 15093 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${ifaceName}._create"); 15094 } 15095 if (!args[0].isObject()) { 15096 return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 1 of ${ifaceName}._create"); 15097 } 15098 if (!args[1].isObject()) { 15099 return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 2 of ${ifaceName}._create"); 15100 } 15101 15102 // GlobalObject will go through wrappers as needed for us, and 15103 // is simpler than the right UnwrapArg incantation. 15104 GlobalObject global(cx, &args[0].toObject()); 15105 if (global.Failed()) { 15106 return false; 15107 } 15108 nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(global.GetAsSupports()); 15109 MOZ_ASSERT(globalHolder); 15110 JS::Rooted<JSObject*> arg(cx, &args[1].toObject()); 15111 RefPtr<${implName}> impl = new ${implName}(arg, globalHolder); 15112 MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx)); 15113 return GetOrCreateDOMReflector(cx, impl, args.rval()); 15114 """, 15115 ifaceName=self.descriptor.interface.identifier.name, 15116 implName=self.descriptor.name) 15117 15118 15119def isJSImplementedDescriptor(descriptorProvider): 15120 return (isinstance(descriptorProvider, Descriptor) and 15121 descriptorProvider.interface.isJSImplemented()) 15122 15123 15124class CGCallback(CGClass): 15125 def __init__(self, idlObject, descriptorProvider, baseName, methods, 15126 getters=[], setters=[]): 15127 self.baseName = baseName 15128 self._deps = idlObject.getDeps() 15129 self.idlObject = idlObject 15130 self.name = idlObject.identifier.name 15131 if isJSImplementedDescriptor(descriptorProvider): 15132 self.name = jsImplName(self.name) 15133 # For our public methods that needThisHandling we want most of the 15134 # same args and the same return type as what CallbackMember 15135 # generates. So we want to take advantage of all its 15136 # CGNativeMember infrastructure, but that infrastructure can't deal 15137 # with templates and most especially template arguments. So just 15138 # cheat and have CallbackMember compute all those things for us. 15139 realMethods = [] 15140 for method in methods: 15141 if not isinstance(method, CallbackMember) or not method.needThisHandling: 15142 realMethods.append(method) 15143 else: 15144 realMethods.extend(self.getMethodImpls(method)) 15145 realMethods.append( 15146 ClassMethod("operator==", "bool", 15147 [Argument("const %s&" % self.name, "aOther")], 15148 inline=True, bodyInHeader=True, 15149 const=True, 15150 body=("return %s::operator==(aOther);\n" % baseName))) 15151 CGClass.__init__(self, self.name, 15152 bases=[ClassBase(baseName)], 15153 constructors=self.getConstructors(), 15154 methods=realMethods+getters+setters) 15155 15156 def getConstructors(self): 15157 if (not self.idlObject.isInterface() and 15158 not self.idlObject._treatNonObjectAsNull): 15159 body = "MOZ_ASSERT(JS::IsCallable(mCallback));\n" 15160 else: 15161 # Not much we can assert about it, other than not being null, and 15162 # CallbackObject does that already. 15163 body = "" 15164 return [ 15165 ClassConstructor( 15166 [Argument("JSContext*", "aCx"), 15167 Argument("JS::Handle<JSObject*>", "aCallback"), 15168 Argument("nsIGlobalObject*", "aIncumbentGlobal")], 15169 bodyInHeader=True, 15170 visibility="public", 15171 explicit=True, 15172 baseConstructors=[ 15173 "%s(aCx, aCallback, aIncumbentGlobal)" % self.baseName, 15174 ], 15175 body=body), 15176 ClassConstructor( 15177 [Argument("JSContext*", "aCx"), 15178 Argument("JS::Handle<JSObject*>", "aCallback"), 15179 Argument("nsIGlobalObject*", "aIncumbentGlobal"), 15180 Argument("const FastCallbackConstructor&", "")], 15181 bodyInHeader=True, 15182 visibility="public", 15183 explicit=True, 15184 baseConstructors=[ 15185 "%s(aCx, aCallback, aIncumbentGlobal, FastCallbackConstructor())" % self.baseName, 15186 ], 15187 body=body), 15188 ClassConstructor( 15189 [Argument("JS::Handle<JSObject*>", "aCallback"), 15190 Argument("JS::Handle<JSObject*>", "aAsyncStack"), 15191 Argument("nsIGlobalObject*", "aIncumbentGlobal")], 15192 bodyInHeader=True, 15193 visibility="public", 15194 explicit=True, 15195 baseConstructors=[ 15196 "%s(aCallback, aAsyncStack, aIncumbentGlobal)" % self.baseName, 15197 ], 15198 body=body)] 15199 15200 def getMethodImpls(self, method): 15201 assert method.needThisHandling 15202 args = list(method.args) 15203 # Strip out the JSContext*/JSObject* args 15204 # that got added. 15205 assert args[0].name == "cx" and args[0].argType == "JSContext*" 15206 assert args[1].name == "aThisVal" and args[1].argType == "JS::Handle<JS::Value>" 15207 args = args[2:] 15208 15209 # Now remember which index the ErrorResult argument is at; 15210 # we'll need this below. 15211 assert args[-1].name == "aRv" and args[-1].argType == "ErrorResult&" 15212 rvIndex = len(args) - 1 15213 assert rvIndex >= 0 15214 15215 # Record the names of all the arguments, so we can use them when we call 15216 # the private method. 15217 argnames = [arg.name for arg in args] 15218 argnamesWithThis = ["s.GetContext()", "thisValJS"] + argnames 15219 argnamesWithoutThis = ["s.GetContext()", "JS::UndefinedHandleValue"] + argnames 15220 # Now that we've recorded the argnames for our call to our private 15221 # method, insert our optional argument for the execution reason. 15222 args.append(Argument("const char*", "aExecutionReason", 15223 "nullptr")) 15224 15225 # Make copies of the arg list for the two "without rv" overloads. Note 15226 # that those don't need aExceptionHandling or aCompartment arguments 15227 # because those would make not sense anyway: the only sane thing to do 15228 # with exceptions in the "without rv" cases is to report them. 15229 argsWithoutRv = list(args) 15230 argsWithoutRv.pop(rvIndex) 15231 argsWithoutThisAndRv = list(argsWithoutRv) 15232 15233 # Add the potional argument for deciding whether the CallSetup should 15234 # re-throw exceptions on aRv. 15235 args.append(Argument("ExceptionHandling", "aExceptionHandling", 15236 "eReportExceptions")) 15237 # And the argument for communicating when exceptions should really be 15238 # rethrown. In particular, even when aExceptionHandling is 15239 # eRethrowExceptions they won't get rethrown if aCompartment is provided 15240 # and its principal doesn't subsume either the callback or the 15241 # exception. 15242 args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) 15243 # And now insert our template argument. 15244 argsWithoutThis = list(args) 15245 args.insert(0, Argument("const T&", "thisVal")) 15246 argsWithoutRv.insert(0, Argument("const T&", "thisVal")) 15247 15248 argnamesWithoutThisAndRv = [arg.name for arg in argsWithoutThisAndRv] 15249 argnamesWithoutThisAndRv.insert(rvIndex, "rv"); 15250 # If we just leave things like that, and have no actual arguments in the 15251 # IDL, we will end up trying to call the templated "without rv" overload 15252 # with "rv" as the thisVal. That's no good. So explicitly append the 15253 # aExceptionHandling and aCompartment values we need to end up matching 15254 # the signature of our non-templated "with rv" overload. 15255 argnamesWithoutThisAndRv.extend(["eReportExceptions", "nullptr"]) 15256 15257 argnamesWithoutRv = [arg.name for arg in argsWithoutRv] 15258 # Note that we need to insert at rvIndex + 1, since we inserted a 15259 # thisVal arg at the start. 15260 argnamesWithoutRv.insert(rvIndex + 1, "rv") 15261 15262 errorReturn = method.getDefaultRetval() 15263 15264 setupCall = fill( 15265 """ 15266 if (!aExecutionReason) { 15267 aExecutionReason = "${executionReason}"; 15268 } 15269 CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aCompartment); 15270 if (!s.GetContext()) { 15271 MOZ_ASSERT(aRv.Failed()); 15272 return${errorReturn}; 15273 } 15274 """, 15275 errorReturn=errorReturn, 15276 executionReason=method.getPrettyName()) 15277 15278 bodyWithThis = fill( 15279 """ 15280 $*{setupCall} 15281 JS::Rooted<JS::Value> thisValJS(s.GetContext()); 15282 if (!ToJSValue(s.GetContext(), thisVal, &thisValJS)) { 15283 aRv.Throw(NS_ERROR_FAILURE); 15284 return${errorReturn}; 15285 } 15286 return ${methodName}(${callArgs}); 15287 """, 15288 setupCall=setupCall, 15289 errorReturn=errorReturn, 15290 methodName=method.name, 15291 callArgs=", ".join(argnamesWithThis)) 15292 bodyWithoutThis = fill( 15293 """ 15294 $*{setupCall} 15295 return ${methodName}(${callArgs}); 15296 """, 15297 setupCall=setupCall, 15298 errorReturn=errorReturn, 15299 methodName=method.name, 15300 callArgs=", ".join(argnamesWithoutThis)) 15301 bodyWithThisWithoutRv = fill( 15302 """ 15303 IgnoredErrorResult rv; 15304 return ${methodName}(${callArgs}); 15305 """, 15306 methodName=method.name, 15307 callArgs=", ".join(argnamesWithoutRv)) 15308 bodyWithoutThisAndRv = fill( 15309 """ 15310 IgnoredErrorResult rv; 15311 return ${methodName}(${callArgs}); 15312 """, 15313 methodName=method.name, 15314 callArgs=", ".join(argnamesWithoutThisAndRv)) 15315 15316 return [ClassMethod(method.name, method.returnType, args, 15317 bodyInHeader=True, 15318 templateArgs=["typename T"], 15319 body=bodyWithThis), 15320 ClassMethod(method.name, method.returnType, argsWithoutThis, 15321 bodyInHeader=True, 15322 body=bodyWithoutThis), 15323 ClassMethod(method.name, method.returnType, argsWithoutRv, 15324 bodyInHeader=True, 15325 templateArgs=["typename T"], 15326 body=bodyWithThisWithoutRv), 15327 ClassMethod(method.name, method.returnType, argsWithoutThisAndRv, 15328 bodyInHeader=True, 15329 body=bodyWithoutThisAndRv), 15330 method] 15331 15332 def deps(self): 15333 return self._deps 15334 15335 15336class CGCallbackFunction(CGCallback): 15337 def __init__(self, callback, descriptorProvider): 15338 self.callback = callback 15339 CGCallback.__init__(self, callback, descriptorProvider, 15340 "CallbackFunction", 15341 methods=[CallCallback(callback, descriptorProvider)]) 15342 15343 def getConstructors(self): 15344 return CGCallback.getConstructors(self) + [ 15345 ClassConstructor( 15346 [Argument("CallbackFunction*", "aOther")], 15347 bodyInHeader=True, 15348 visibility="public", 15349 explicit=True, 15350 baseConstructors=["CallbackFunction(aOther)"])] 15351 15352 15353class CGFastCallback(CGClass): 15354 def __init__(self, idlObject): 15355 self._deps = idlObject.getDeps() 15356 baseName = idlObject.identifier.name 15357 constructor = ClassConstructor( 15358 [Argument("JSContext*", "aCx"), 15359 Argument("JS::Handle<JSObject*>", "aCallback"), 15360 Argument("nsIGlobalObject*", "aIncumbentGlobal")], 15361 bodyInHeader=True, 15362 visibility="public", 15363 explicit=True, 15364 baseConstructors=[ 15365 "%s(aCx, aCallback, aIncumbentGlobal, FastCallbackConstructor())" % 15366 baseName, 15367 ], 15368 body="") 15369 15370 traceMethod = ClassMethod("Trace", "void", 15371 [Argument("JSTracer*", "aTracer")], 15372 inline=True, 15373 bodyInHeader=True, 15374 visibility="public", 15375 body="%s::Trace(aTracer);\n" % baseName) 15376 holdMethod = ClassMethod("HoldJSObjectsIfMoreThanOneOwner", "void", 15377 [], 15378 inline=True, 15379 bodyInHeader=True, 15380 visibility="public", 15381 body=( 15382 "%s::HoldJSObjectsIfMoreThanOneOwner();\n" % 15383 baseName)) 15384 15385 CGClass.__init__(self, "Fast%s" % baseName, 15386 bases=[ClassBase(baseName)], 15387 constructors=[constructor], 15388 methods=[traceMethod, holdMethod]) 15389 15390 def deps(self): 15391 return self._deps 15392 15393 15394class CGCallbackInterface(CGCallback): 15395 def __init__(self, descriptor, typedArraysAreStructs=False): 15396 iface = descriptor.interface 15397 attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()] 15398 getters = [CallbackGetter(a, descriptor, typedArraysAreStructs) 15399 for a in attrs] 15400 setters = [CallbackSetter(a, descriptor, typedArraysAreStructs) 15401 for a in attrs if not a.readonly] 15402 methods = [m for m in iface.members 15403 if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()] 15404 methods = [CallbackOperation(m, sig, descriptor, typedArraysAreStructs) 15405 for m in methods for sig in m.signatures()] 15406 if iface.isJSImplemented() and iface.ctor(): 15407 sigs = descriptor.interface.ctor().signatures() 15408 if len(sigs) != 1: 15409 raise TypeError("We only handle one constructor. See bug 869268.") 15410 methods.append(CGJSImplInitOperation(sigs[0], descriptor)) 15411 if any(m.isAttr() or m.isMethod() for m in iface.members) or (iface.isJSImplemented() and iface.ctor()): 15412 methods.append(initIdsClassMethod([descriptor.binaryNameFor(m.identifier.name) 15413 for m in iface.members 15414 if m.isAttr() or m.isMethod()] + 15415 (["__init"] if iface.isJSImplemented() and iface.ctor() else []), 15416 iface.identifier.name + "Atoms")) 15417 CGCallback.__init__(self, iface, descriptor, "CallbackInterface", 15418 methods, getters=getters, setters=setters) 15419 15420 15421class FakeMember(): 15422 def __init__(self, name=None): 15423 self.treatNullAs = "Default" 15424 if name is not None: 15425 self.identifier = FakeIdentifier(name) 15426 15427 def isStatic(self): 15428 return False 15429 15430 def isAttr(self): 15431 return False 15432 15433 def isMethod(self): 15434 return False 15435 15436 def getExtendedAttribute(self, name): 15437 # Claim to be a [NewObject] so we can avoid the "return a raw pointer" 15438 # comments CGNativeMember codegen would otherwise stick in. 15439 if name == "NewObject": 15440 return True 15441 return None 15442 15443 15444class CallbackMember(CGNativeMember): 15445 # XXXbz It's OK to use CallbackKnownNotGray for wrapScope because 15446 # CallSetup already handled the unmark-gray bits for us. we don't have 15447 # anything better to use for 'obj', really... 15448 def __init__(self, sig, name, descriptorProvider, needThisHandling, 15449 rethrowContentException=False, typedArraysAreStructs=False, 15450 wrapScope='CallbackKnownNotGray()'): 15451 """ 15452 needThisHandling is True if we need to be able to accept a specified 15453 thisObj, False otherwise. 15454 """ 15455 assert not rethrowContentException or not needThisHandling 15456 15457 self.retvalType = sig[0] 15458 self.originalSig = sig 15459 args = sig[1] 15460 self.argCount = len(args) 15461 if self.argCount > 0: 15462 # Check for variadic arguments 15463 lastArg = args[self.argCount-1] 15464 if lastArg.variadic: 15465 self.argCountStr = ("(%d - 1) + %s.Length()" % 15466 (self.argCount, lastArg.identifier.name)) 15467 else: 15468 self.argCountStr = "%d" % self.argCount 15469 self.needThisHandling = needThisHandling 15470 # If needThisHandling, we generate ourselves as private and the caller 15471 # will handle generating public versions that handle the "this" stuff. 15472 visibility = "private" if needThisHandling else "public" 15473 self.rethrowContentException = rethrowContentException 15474 15475 self.wrapScope = wrapScope 15476 # We don't care, for callback codegen, whether our original member was 15477 # a method or attribute or whatnot. Just always pass FakeMember() 15478 # here. 15479 CGNativeMember.__init__(self, descriptorProvider, FakeMember(), 15480 name, (self.retvalType, args), 15481 extendedAttrs={}, 15482 passJSBitsAsNeeded=False, 15483 visibility=visibility, 15484 typedArraysAreStructs=typedArraysAreStructs) 15485 # We have to do all the generation of our body now, because 15486 # the caller relies on us throwing if we can't manage it. 15487 self.exceptionCode = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n" 15488 "return%s;\n" % self.getDefaultRetval()) 15489 self.body = self.getImpl() 15490 15491 def getImpl(self): 15492 setupCall = self.getCallSetup() 15493 declRval = self.getRvalDecl() 15494 if self.argCount > 0: 15495 argvDecl = fill( 15496 """ 15497 JS::AutoValueVector argv(cx); 15498 if (!argv.resize(${argCount})) { 15499 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 15500 return${errorReturn}; 15501 } 15502 """, 15503 argCount=self.argCountStr, 15504 errorReturn=self.getDefaultRetval()) 15505 else: 15506 # Avoid weird 0-sized arrays 15507 argvDecl = "" 15508 convertArgs = self.getArgConversions() 15509 doCall = self.getCall() 15510 returnResult = self.getResultConversion() 15511 15512 return setupCall + declRval + argvDecl + convertArgs + doCall + returnResult 15513 15514 def getResultConversion(self): 15515 replacements = { 15516 "val": "rval", 15517 "holderName": "rvalHolder", 15518 "declName": "rvalDecl", 15519 # We actually want to pass in a null scope object here, because 15520 # wrapping things into our current compartment (that of mCallback) 15521 # is what we want. 15522 "obj": "nullptr", 15523 "passedToJSImpl": "false" 15524 } 15525 15526 if isJSImplementedDescriptor(self.descriptorProvider): 15527 isCallbackReturnValue = "JSImpl" 15528 else: 15529 isCallbackReturnValue = "Callback" 15530 sourceDescription = "return value of %s" % self.getPrettyName() 15531 convertType = instantiateJSToNativeConversion( 15532 getJSToNativeConversionInfo(self.retvalType, 15533 self.descriptorProvider, 15534 exceptionCode=self.exceptionCode, 15535 isCallbackReturnValue=isCallbackReturnValue, 15536 # Allow returning a callback type that 15537 # allows non-callable objects. 15538 allowTreatNonCallableAsNull=True, 15539 sourceDescription=sourceDescription), 15540 replacements) 15541 assignRetval = string.Template( 15542 self.getRetvalInfo(self.retvalType, 15543 False)[2]).substitute(replacements) 15544 type = convertType.define() 15545 return type + assignRetval 15546 15547 def getArgConversions(self): 15548 # Just reget the arglist from self.originalSig, because our superclasses 15549 # just have way to many members they like to clobber, so I can't find a 15550 # safe member name to store it in. 15551 argConversions = [self.getArgConversion(i, arg) 15552 for i, arg in enumerate(self.originalSig[1])] 15553 if not argConversions: 15554 return "\n" 15555 15556 # Do them back to front, so our argc modifications will work 15557 # correctly, because we examine trailing arguments first. 15558 argConversions.reverse() 15559 # Wrap each one in a scope so that any locals it has don't leak out, and 15560 # also so that we can just "break;" for our successCode. 15561 argConversions = [CGWrapper(CGIndenter(CGGeneric(c)), 15562 pre="do {\n", 15563 post="} while (0);\n") 15564 for c in argConversions] 15565 if self.argCount > 0: 15566 argConversions.insert(0, self.getArgcDecl()) 15567 # And slap them together. 15568 return CGList(argConversions, "\n").define() + "\n" 15569 15570 def getArgConversion(self, i, arg): 15571 argval = arg.identifier.name 15572 15573 if arg.variadic: 15574 argval = argval + "[idx]" 15575 jsvalIndex = "%d + idx" % i 15576 else: 15577 jsvalIndex = "%d" % i 15578 if arg.canHaveMissingValue(): 15579 argval += ".Value()" 15580 if arg.type.isDOMString(): 15581 # XPConnect string-to-JS conversion wants to mutate the string. So 15582 # let's give it a string it can mutate 15583 # XXXbz if we try to do a sequence of strings, this will kinda fail. 15584 result = "mutableStr" 15585 prepend = "nsString mutableStr(%s);\n" % argval 15586 else: 15587 result = argval 15588 prepend = "" 15589 15590 try: 15591 conversion = prepend + wrapForType( 15592 arg.type, self.descriptorProvider, 15593 { 15594 'result': result, 15595 'successCode': "continue;\n" if arg.variadic else "break;\n", 15596 'jsvalRef': "argv[%s]" % jsvalIndex, 15597 'jsvalHandle': "argv[%s]" % jsvalIndex, 15598 'obj': self.wrapScope, 15599 'returnsNewObject': False, 15600 'exceptionCode': self.exceptionCode, 15601 'typedArraysAreStructs': self.typedArraysAreStructs 15602 }) 15603 except MethodNotNewObjectError as err: 15604 raise TypeError("%s being passed as an argument to %s but is not " 15605 "wrapper cached, so can't be reliably converted to " 15606 "a JS object." % 15607 (err.typename, self.getPrettyName())) 15608 if arg.variadic: 15609 conversion = fill( 15610 """ 15611 for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) { 15612 $*{conversion} 15613 } 15614 break; 15615 """, 15616 arg=arg.identifier.name, 15617 conversion=conversion) 15618 elif arg.canHaveMissingValue(): 15619 conversion = fill( 15620 """ 15621 if (${argName}.WasPassed()) { 15622 $*{conversion} 15623 } else if (argc == ${iPlus1}) { 15624 // This is our current trailing argument; reduce argc 15625 --argc; 15626 } else { 15627 argv[${i}].setUndefined(); 15628 } 15629 """, 15630 argName=arg.identifier.name, 15631 conversion=conversion, 15632 iPlus1=i + 1, 15633 i=i) 15634 return conversion 15635 15636 def getDefaultRetval(self): 15637 default = self.getRetvalInfo(self.retvalType, False)[1] 15638 if len(default) != 0: 15639 default = " " + default 15640 return default 15641 15642 def getArgs(self, returnType, argList): 15643 args = CGNativeMember.getArgs(self, returnType, argList) 15644 if not self.needThisHandling: 15645 # Since we don't need this handling, we're the actual method that 15646 # will be called, so we need an aRethrowExceptions argument. 15647 if not self.rethrowContentException: 15648 args.append(Argument("const char*", "aExecutionReason", 15649 "nullptr")) 15650 args.append(Argument("ExceptionHandling", "aExceptionHandling", 15651 "eReportExceptions")) 15652 args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) 15653 return args 15654 # We want to allow the caller to pass in a "this" value, as 15655 # well as a JSContext. 15656 return [Argument("JSContext*", "cx"), 15657 Argument("JS::Handle<JS::Value>", "aThisVal")] + args 15658 15659 def getCallSetup(self): 15660 if self.needThisHandling: 15661 # It's been done for us already 15662 return "" 15663 callSetup = "CallSetup s(this, aRv" 15664 if self.rethrowContentException: 15665 # getArgs doesn't add the aExceptionHandling argument but does add 15666 # aCompartment for us. 15667 callSetup += ', "%s", eRethrowContentExceptions, aCompartment, /* aIsJSImplementedWebIDL = */ ' % self.getPrettyName() 15668 callSetup += toStringBool(isJSImplementedDescriptor(self.descriptorProvider)) 15669 else: 15670 callSetup += ', "%s", aExceptionHandling, aCompartment' % self.getPrettyName() 15671 callSetup += ");\n" 15672 return fill( 15673 """ 15674 $*{callSetup} 15675 JSContext* cx = s.GetContext(); 15676 if (!cx) { 15677 MOZ_ASSERT(aRv.Failed()); 15678 return${errorReturn}; 15679 } 15680 """, 15681 callSetup=callSetup, 15682 errorReturn=self.getDefaultRetval()) 15683 15684 def getArgcDecl(self): 15685 return CGGeneric("unsigned argc = %s;\n" % self.argCountStr) 15686 15687 @staticmethod 15688 def ensureASCIIName(idlObject): 15689 type = "attribute" if idlObject.isAttr() else "operation" 15690 if re.match("[^\x20-\x7E]", idlObject.identifier.name): 15691 raise SyntaxError('Callback %s name "%s" contains non-ASCII ' 15692 "characters. We can't handle that. %s" % 15693 (type, idlObject.identifier.name, 15694 idlObject.location)) 15695 if re.match('"', idlObject.identifier.name): 15696 raise SyntaxError("Callback %s name '%s' contains " 15697 "double-quote character. We can't handle " 15698 "that. %s" % 15699 (type, idlObject.identifier.name, 15700 idlObject.location)) 15701 15702 15703class CallbackMethod(CallbackMember): 15704 def __init__(self, sig, name, descriptorProvider, needThisHandling, 15705 rethrowContentException=False, typedArraysAreStructs=False): 15706 CallbackMember.__init__(self, sig, name, descriptorProvider, 15707 needThisHandling, rethrowContentException, 15708 typedArraysAreStructs=typedArraysAreStructs) 15709 15710 def getRvalDecl(self): 15711 return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n" 15712 15713 def getCall(self): 15714 if self.argCount > 0: 15715 args = "JS::HandleValueArray::subarray(argv, 0, argc)" 15716 else: 15717 args = "JS::HandleValueArray::empty()" 15718 15719 return fill( 15720 """ 15721 $*{declCallable} 15722 $*{declThis} 15723 if (${callGuard}!JS::Call(cx, ${thisVal}, callable, 15724 ${args}, &rval)) { 15725 aRv.NoteJSContextException(cx); 15726 return${errorReturn}; 15727 } 15728 """, 15729 declCallable=self.getCallableDecl(), 15730 declThis=self.getThisDecl(), 15731 callGuard=self.getCallGuard(), 15732 thisVal=self.getThisVal(), 15733 args=args, 15734 errorReturn=self.getDefaultRetval()) 15735 15736 15737class CallCallback(CallbackMethod): 15738 def __init__(self, callback, descriptorProvider): 15739 self.callback = callback 15740 CallbackMethod.__init__(self, callback.signatures()[0], "Call", 15741 descriptorProvider, needThisHandling=True) 15742 15743 def getThisDecl(self): 15744 return "" 15745 15746 def getThisVal(self): 15747 return "aThisVal" 15748 15749 def getCallableDecl(self): 15750 return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n" 15751 15752 def getPrettyName(self): 15753 return self.callback.identifier.name 15754 15755 def getCallGuard(self): 15756 if self.callback._treatNonObjectAsNull: 15757 return "JS::IsCallable(mCallback) && " 15758 return "" 15759 15760 15761class CallbackOperationBase(CallbackMethod): 15762 """ 15763 Common class for implementing various callback operations. 15764 """ 15765 def __init__(self, signature, jsName, nativeName, descriptor, 15766 singleOperation, rethrowContentException=False, 15767 typedArraysAreStructs=False): 15768 self.singleOperation = singleOperation 15769 self.methodName = descriptor.binaryNameFor(jsName) 15770 CallbackMethod.__init__(self, signature, nativeName, descriptor, 15771 singleOperation, rethrowContentException, 15772 typedArraysAreStructs=typedArraysAreStructs) 15773 15774 def getThisDecl(self): 15775 if not self.singleOperation: 15776 return "JS::Rooted<JS::Value> thisValue(cx, JS::ObjectValue(*mCallback));\n" 15777 # This relies on getCallableDecl declaring a boolean 15778 # isCallable in the case when we're a single-operation 15779 # interface. 15780 return dedent(""" 15781 JS::Rooted<JS::Value> thisValue(cx, isCallable ? aThisVal.get() 15782 : JS::ObjectValue(*mCallback)); 15783 """) 15784 15785 def getThisVal(self): 15786 return "thisValue" 15787 15788 def getCallableDecl(self): 15789 getCallableFromProp = fill( 15790 """ 15791 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx); 15792 if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) || 15793 !GetCallableProperty(cx, atomsCache->${methodAtomName}, &callable)) { 15794 aRv.Throw(NS_ERROR_UNEXPECTED); 15795 return${errorReturn}; 15796 } 15797 """, 15798 methodAtomName=CGDictionary.makeIdName(self.methodName), 15799 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms", 15800 errorReturn=self.getDefaultRetval()) 15801 if not self.singleOperation: 15802 return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp 15803 return fill( 15804 """ 15805 bool isCallable = JS::IsCallable(mCallback); 15806 JS::Rooted<JS::Value> callable(cx); 15807 if (isCallable) { 15808 callable = JS::ObjectValue(*mCallback); 15809 } else { 15810 $*{getCallableFromProp} 15811 } 15812 """, 15813 getCallableFromProp=getCallableFromProp) 15814 15815 def getCallGuard(self): 15816 return "" 15817 15818 15819class CallbackOperation(CallbackOperationBase): 15820 """ 15821 Codegen actual WebIDL operations on callback interfaces. 15822 """ 15823 def __init__(self, method, signature, descriptor, typedArraysAreStructs): 15824 self.ensureASCIIName(method) 15825 self.method = method 15826 jsName = method.identifier.name 15827 CallbackOperationBase.__init__(self, signature, 15828 jsName, 15829 MakeNativeName(descriptor.binaryNameFor(jsName)), 15830 descriptor, descriptor.interface.isSingleOperationInterface(), 15831 rethrowContentException=descriptor.interface.isJSImplemented(), 15832 typedArraysAreStructs=typedArraysAreStructs) 15833 15834 def getPrettyName(self): 15835 return "%s.%s" % (self.descriptorProvider.interface.identifier.name, 15836 self.method.identifier.name) 15837 15838 15839class CallbackAccessor(CallbackMember): 15840 """ 15841 Shared superclass for CallbackGetter and CallbackSetter. 15842 """ 15843 def __init__(self, attr, sig, name, descriptor, typedArraysAreStructs): 15844 self.ensureASCIIName(attr) 15845 self.attrName = attr.identifier.name 15846 CallbackMember.__init__(self, sig, name, descriptor, 15847 needThisHandling=False, 15848 rethrowContentException=descriptor.interface.isJSImplemented(), 15849 typedArraysAreStructs=typedArraysAreStructs) 15850 15851 def getPrettyName(self): 15852 return "%s.%s" % (self.descriptorProvider.interface.identifier.name, 15853 self.attrName) 15854 15855 15856class CallbackGetter(CallbackAccessor): 15857 def __init__(self, attr, descriptor, typedArraysAreStructs): 15858 CallbackAccessor.__init__(self, attr, 15859 (attr.type, []), 15860 callbackGetterName(attr, descriptor), 15861 descriptor, 15862 typedArraysAreStructs) 15863 15864 def getRvalDecl(self): 15865 return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n" 15866 15867 def getCall(self): 15868 return fill( 15869 """ 15870 JS::Rooted<JSObject *> callback(cx, mCallback); 15871 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx); 15872 if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) || 15873 !JS_GetPropertyById(cx, callback, atomsCache->${attrAtomName}, &rval)) { 15874 aRv.Throw(NS_ERROR_UNEXPECTED); 15875 return${errorReturn}; 15876 } 15877 """, 15878 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms", 15879 attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)), 15880 errorReturn=self.getDefaultRetval()) 15881 15882 15883class CallbackSetter(CallbackAccessor): 15884 def __init__(self, attr, descriptor, typedArraysAreStructs): 15885 CallbackAccessor.__init__(self, attr, 15886 (BuiltinTypes[IDLBuiltinType.Types.void], 15887 [FakeArgument(attr.type, attr)]), 15888 callbackSetterName(attr, descriptor), 15889 descriptor, typedArraysAreStructs) 15890 15891 def getRvalDecl(self): 15892 # We don't need an rval 15893 return "" 15894 15895 def getCall(self): 15896 return fill( 15897 """ 15898 MOZ_ASSERT(argv.length() == 1); 15899 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx); 15900 if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) || 15901 !JS_SetPropertyById(cx, CallbackKnownNotGray(), atomsCache->${attrAtomName}, argv[0])) { 15902 aRv.Throw(NS_ERROR_UNEXPECTED); 15903 return${errorReturn}; 15904 } 15905 """, 15906 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms", 15907 attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)), 15908 errorReturn=self.getDefaultRetval()) 15909 15910 def getArgcDecl(self): 15911 return None 15912 15913 15914class CGJSImplInitOperation(CallbackOperationBase): 15915 """ 15916 Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL. 15917 """ 15918 def __init__(self, sig, descriptor): 15919 assert sig in descriptor.interface.ctor().signatures() 15920 CallbackOperationBase.__init__(self, (BuiltinTypes[IDLBuiltinType.Types.void], sig[1]), 15921 "__init", "__Init", descriptor, 15922 singleOperation=False, 15923 rethrowContentException=True, 15924 typedArraysAreStructs=True) 15925 15926 def getPrettyName(self): 15927 return "__init" 15928 15929 15930def getMaplikeOrSetlikeErrorReturn(helperImpl): 15931 """ 15932 Generate return values based on whether a maplike or setlike generated 15933 method is an interface method (which returns bool) or a helper function 15934 (which uses ErrorResult). 15935 """ 15936 if helperImpl: 15937 return dedent( 15938 """ 15939 aRv.Throw(NS_ERROR_UNEXPECTED); 15940 return%s; 15941 """ % helperImpl.getDefaultRetval()) 15942 return "return false;\n" 15943 15944 15945def getMaplikeOrSetlikeBackingObject(descriptor, maplikeOrSetlike, helperImpl=None): 15946 """ 15947 Generate code to get/create a JS backing object for a maplike/setlike 15948 declaration from the declaration slot. 15949 """ 15950 func_prefix = maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title() 15951 ret = fill( 15952 """ 15953 JS::Rooted<JSObject*> backingObj(cx); 15954 bool created = false; 15955 if (!Get${func_prefix}BackingObject(cx, obj, ${slot}, &backingObj, &created)) { 15956 $*{errorReturn} 15957 } 15958 if (created) { 15959 PreserveWrapper<${selfType}>(self); 15960 } 15961 """, 15962 slot=memberReservedSlot(maplikeOrSetlike, descriptor), 15963 func_prefix=func_prefix, 15964 errorReturn=getMaplikeOrSetlikeErrorReturn(helperImpl), 15965 selfType=descriptor.nativeType) 15966 return ret 15967 15968 15969def getMaplikeOrSetlikeSizeGetterBody(descriptor, attr): 15970 """ 15971 Creates the body for the size getter method of maplike/setlike interfaces. 15972 """ 15973 # We should only have one declaration attribute currently 15974 assert attr.identifier.name == "size" 15975 assert attr.isMaplikeOrSetlikeAttr() 15976 return fill( 15977 """ 15978 $*{getBackingObj} 15979 uint32_t result = JS::${funcPrefix}Size(cx, backingObj); 15980 MOZ_ASSERT(!JS_IsExceptionPending(cx)); 15981 args.rval().setNumber(result); 15982 return true; 15983 """, 15984 getBackingObj=getMaplikeOrSetlikeBackingObject(descriptor, 15985 attr.maplikeOrSetlike), 15986 funcPrefix=attr.maplikeOrSetlike.prefix) 15987 15988 15989class CGMaplikeOrSetlikeMethodGenerator(CGThing): 15990 """ 15991 Creates methods for maplike/setlike interfaces. It is expected that all 15992 methods will be have a maplike/setlike object attached. Unwrapping/wrapping 15993 will be taken care of by the usual method generation machinery in 15994 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of 15995 using CGCallGenerator. 15996 """ 15997 def __init__(self, descriptor, maplikeOrSetlike, methodName, 15998 helperImpl=None): 15999 CGThing.__init__(self) 16000 # True if this will be the body of a C++ helper function. 16001 self.helperImpl = helperImpl 16002 self.descriptor = descriptor 16003 self.maplikeOrSetlike = maplikeOrSetlike 16004 self.cgRoot = CGList([]) 16005 impl_method_name = methodName 16006 if impl_method_name[0] == "_": 16007 # double underscore means this is a js-implemented chrome only rw 16008 # function. Truncate the double underscore so calling the right 16009 # underlying JSAPI function still works. 16010 impl_method_name = impl_method_name[2:] 16011 self.cgRoot.append(CGGeneric( 16012 getMaplikeOrSetlikeBackingObject(self.descriptor, 16013 self.maplikeOrSetlike, 16014 self.helperImpl))) 16015 self.returnStmt = getMaplikeOrSetlikeErrorReturn(self.helperImpl) 16016 16017 # Generates required code for the method. Method descriptions included 16018 # in definitions below. Throw if we don't have a method to fill in what 16019 # we're looking for. 16020 try: 16021 methodGenerator = getattr(self, impl_method_name) 16022 except AttributeError: 16023 raise TypeError("Missing %s method definition '%s'" % 16024 (self.maplikeOrSetlike.maplikeOrSetlikeType, 16025 methodName)) 16026 # Method generator returns tuple, containing: 16027 # 16028 # - a list of CGThings representing setup code for preparing to call 16029 # the JS API function 16030 # - a list of arguments needed for the JS API function we're calling 16031 # - list of code CGThings needed for return value conversion. 16032 (setupCode, arguments, setResult) = methodGenerator() 16033 16034 # Create the actual method call, and then wrap it with the code to 16035 # return the value if needed. 16036 funcName = (self.maplikeOrSetlike.prefix + 16037 MakeNativeName(impl_method_name)) 16038 # Append the list of setup code CGThings 16039 self.cgRoot.append(CGList(setupCode)) 16040 # Create the JS API call 16041 self.cgRoot.append(CGWrapper( 16042 CGGeneric(fill( 16043 """ 16044 if (!JS::${funcName}(${args})) { 16045 $*{errorReturn} 16046 } 16047 """, 16048 funcName=funcName, 16049 args=", ".join(["cx", "backingObj"] + arguments), 16050 errorReturn=self.returnStmt)))) 16051 # Append result conversion 16052 self.cgRoot.append(CGList(setResult)) 16053 16054 def mergeTuples(self, a, b): 16055 """ 16056 Expecting to take 2 tuples were all elements are lists, append the lists in 16057 the second tuple to the lists in the first. 16058 """ 16059 return tuple([x + y for x, y in zip(a, b)]) 16060 16061 def appendArgConversion(self, name): 16062 """ 16063 Generate code to convert arguments to JS::Values, so they can be 16064 passed into JSAPI functions. 16065 """ 16066 return CGGeneric(fill( 16067 """ 16068 JS::Rooted<JS::Value> ${name}Val(cx); 16069 if (!ToJSValue(cx, ${name}, &${name}Val)) { 16070 $*{errorReturn} 16071 } 16072 """, 16073 name=name, 16074 errorReturn=self.returnStmt)) 16075 16076 def appendKeyArgConversion(self): 16077 """ 16078 Generates the key argument for methods. Helper functions will use 16079 an AutoValueVector, while interface methods have seperate JS::Values. 16080 """ 16081 if self.helperImpl: 16082 return ([], ["argv[0]"], []) 16083 return ([self.appendArgConversion("arg0")], ["arg0Val"], []) 16084 16085 def appendKeyAndValueArgConversion(self): 16086 """ 16087 Generates arguments for methods that require a key and value. Helper 16088 functions will use an AutoValueVector, while interface methods have 16089 seperate JS::Values. 16090 """ 16091 r = self.appendKeyArgConversion() 16092 if self.helperImpl: 16093 return self.mergeTuples(r, ([], ["argv[1]"], [])) 16094 return self.mergeTuples(r, ([self.appendArgConversion("arg1")], 16095 ["arg1Val"], 16096 [])) 16097 16098 def appendIteratorResult(self): 16099 """ 16100 Generate code to output JSObject* return values, needed for functions that 16101 return iterators. Iterators cannot currently be wrapped via Xrays. If 16102 something that would return an iterator is called via Xray, fail early. 16103 """ 16104 # TODO: Bug 1173651 - Remove check once bug 1023984 is fixed. 16105 code = CGGeneric(dedent( 16106 """ 16107 // TODO (Bug 1173651): Xrays currently cannot wrap iterators. Change 16108 // after bug 1023984 is fixed. 16109 if (xpc::WrapperFactory::IsXrayWrapper(obj)) { 16110 JS_ReportErrorASCII(cx, "Xray wrapping of iterators not supported."); 16111 return false; 16112 } 16113 JS::Rooted<JSObject*> result(cx); 16114 JS::Rooted<JS::Value> v(cx); 16115 """)) 16116 arguments = "&v" 16117 setResult = CGGeneric(dedent( 16118 """ 16119 result = &v.toObject(); 16120 """)) 16121 return ([code], [arguments], [setResult]) 16122 16123 def appendSelfResult(self): 16124 """ 16125 Generate code to return the interface object itself. 16126 """ 16127 code = CGGeneric(dedent( 16128 """ 16129 JS::Rooted<JSObject*> result(cx); 16130 """)) 16131 setResult = CGGeneric(dedent( 16132 """ 16133 result = obj; 16134 """)) 16135 return ([code], [], [setResult]) 16136 16137 def appendBoolResult(self): 16138 if self.helperImpl: 16139 return ([CGGeneric()], ["&aRetVal"], []) 16140 return ([CGGeneric("bool result;\n")], ["&result"], []) 16141 16142 def forEach(self): 16143 """ 16144 void forEach(callback c, any thisval); 16145 16146 ForEach takes a callback, and a possible value to use as 'this'. The 16147 callback needs to take value, key, and the interface object 16148 implementing maplike/setlike. In order to make sure that the third arg 16149 is our interface object instead of the map/set backing object, we 16150 create a js function with the callback and original object in its 16151 storage slots, then use a helper function in BindingUtils to make sure 16152 the callback is called correctly. 16153 """ 16154 assert(not self.helperImpl) 16155 code = [CGGeneric(dedent( 16156 """ 16157 // Create a wrapper function. 16158 JSFunction* func = js::NewFunctionWithReserved(cx, ForEachHandler, 3, 0, nullptr); 16159 if (!func) { 16160 return false; 16161 } 16162 JS::Rooted<JSObject*> funcObj(cx, JS_GetFunctionObject(func)); 16163 JS::Rooted<JS::Value> funcVal(cx, JS::ObjectValue(*funcObj)); 16164 js::SetFunctionNativeReserved(funcObj, FOREACH_CALLBACK_SLOT, 16165 JS::ObjectValue(*arg0)); 16166 js::SetFunctionNativeReserved(funcObj, FOREACH_MAPLIKEORSETLIKEOBJ_SLOT, 16167 JS::ObjectValue(*obj)); 16168 """))] 16169 arguments = ["funcVal", "arg1"] 16170 return (code, arguments, []) 16171 16172 def set(self): 16173 """ 16174 object set(key, value); 16175 16176 Maplike only function, takes key and sets value to it, returns 16177 interface object unless being called from a C++ helper. 16178 """ 16179 assert self.maplikeOrSetlike.isMaplike() 16180 r = self.appendKeyAndValueArgConversion() 16181 if self.helperImpl: 16182 return r 16183 return self.mergeTuples(r, self.appendSelfResult()) 16184 16185 def add(self): 16186 """ 16187 object add(value); 16188 16189 Setlike only function, adds value to set, returns interface object 16190 unless being called from a C++ helper 16191 """ 16192 assert self.maplikeOrSetlike.isSetlike() 16193 r = self.appendKeyArgConversion() 16194 if self.helperImpl: 16195 return r 16196 return self.mergeTuples(r, self.appendSelfResult()) 16197 16198 def get(self): 16199 """ 16200 type? get(key); 16201 16202 Retrieves a value from a backing object based on the key. Returns value 16203 if key is in backing object, undefined otherwise. 16204 """ 16205 assert self.maplikeOrSetlike.isMaplike() 16206 r = self.appendKeyArgConversion() 16207 code = [CGGeneric(dedent( 16208 """ 16209 JS::Rooted<JS::Value> result(cx); 16210 """))] 16211 arguments = ["&result"] 16212 return self.mergeTuples(r, (code, arguments, [])) 16213 16214 def has(self): 16215 """ 16216 bool has(key); 16217 16218 Check if an entry exists in the backing object. Returns true if value 16219 exists in backing object, false otherwise. 16220 """ 16221 return self.mergeTuples(self.appendKeyArgConversion(), 16222 self.appendBoolResult()) 16223 16224 def keys(self): 16225 """ 16226 object keys(); 16227 16228 Returns new object iterator with all keys from backing object. 16229 """ 16230 return self.appendIteratorResult() 16231 16232 def values(self): 16233 """ 16234 object values(); 16235 16236 Returns new object iterator with all values from backing object. 16237 """ 16238 return self.appendIteratorResult() 16239 16240 def entries(self): 16241 """ 16242 object entries(); 16243 16244 Returns new object iterator with all keys and values from backing 16245 object. Keys will be null for set. 16246 """ 16247 return self.appendIteratorResult() 16248 16249 def clear(self): 16250 """ 16251 void clear(); 16252 16253 Removes all entries from map/set. 16254 """ 16255 return ([], [], []) 16256 16257 def delete(self): 16258 """ 16259 bool delete(key); 16260 16261 Deletes an entry from the backing object. Returns true if value existed 16262 in backing object, false otherwise. 16263 """ 16264 return self.mergeTuples(self.appendKeyArgConversion(), 16265 self.appendBoolResult()) 16266 16267 def define(self): 16268 return self.cgRoot.define() 16269 16270 16271class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember): 16272 """ 16273 Generates code to allow C++ to perform operations on backing objects. Gets 16274 a context from the binding wrapper, turns arguments into JS::Values (via 16275 CallbackMember/CGNativeMember argument conversion), then uses 16276 CGMaplikeOrSetlikeMethodGenerator to generate the body. 16277 16278 """ 16279 16280 class HelperFunction(CGAbstractMethod): 16281 """ 16282 Generates context retrieval code and rooted JSObject for interface for 16283 CGMaplikeOrSetlikeMethodGenerator to use 16284 """ 16285 def __init__(self, descriptor, name, args, code, needsBoolReturn=False): 16286 self.code = code 16287 CGAbstractMethod.__init__(self, descriptor, name, 16288 "bool" if needsBoolReturn else "void", 16289 args) 16290 16291 def definition_body(self): 16292 return self.code 16293 16294 def __init__(self, descriptor, maplikeOrSetlike, name, needsKeyArg=False, 16295 needsValueArg=False, needsBoolReturn=False): 16296 args = [] 16297 self.maplikeOrSetlike = maplikeOrSetlike 16298 self.needsBoolReturn = needsBoolReturn 16299 if needsKeyArg: 16300 args.append(FakeArgument(maplikeOrSetlike.keyType, None, 'aKey')) 16301 if needsValueArg: 16302 assert needsKeyArg 16303 args.append(FakeArgument(maplikeOrSetlike.valueType, None, 'aValue')) 16304 # Run CallbackMember init function to generate argument conversion code. 16305 # wrapScope is set to 'obj' when generating maplike or setlike helper 16306 # functions, as we don't have access to the CallbackPreserveColor 16307 # method. 16308 CallbackMember.__init__(self, 16309 [BuiltinTypes[IDLBuiltinType.Types.void], args], 16310 name, descriptor, False, 16311 wrapScope='obj') 16312 # Wrap CallbackMember body code into a CGAbstractMethod to make 16313 # generation easier. 16314 self.implMethod = CGMaplikeOrSetlikeHelperFunctionGenerator.HelperFunction( 16315 descriptor, name, self.args, self.body, needsBoolReturn) 16316 16317 def getCallSetup(self): 16318 return dedent( 16319 """ 16320 MOZ_ASSERT(self); 16321 AutoJSAPI jsapi; 16322 jsapi.Init(); 16323 JSContext* cx = jsapi.cx(); 16324 // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here because 16325 // all we want is to wrap into _some_ scope and then unwrap to find 16326 // the reflector, and wrapping has no side-effects. 16327 JSAutoCompartment tempCompartment(cx, binding_detail::UnprivilegedJunkScopeOrWorkerGlobal()); 16328 JS::Rooted<JS::Value> v(cx); 16329 if(!ToJSValue(cx, self, &v)) { 16330 aRv.Throw(NS_ERROR_UNEXPECTED); 16331 return%s; 16332 } 16333 // This is a reflector, but due to trying to name things 16334 // similarly across method generators, it's called obj here. 16335 JS::Rooted<JSObject*> obj(cx); 16336 obj = js::UncheckedUnwrap(&v.toObject(), /* stopAtWindowProxy = */ false); 16337 JSAutoCompartment reflectorCompartment(cx, obj); 16338 """ % self.getDefaultRetval()) 16339 16340 def getArgs(self, returnType, argList): 16341 # We don't need the context or the value. We'll generate those instead. 16342 args = CGNativeMember.getArgs(self, returnType, argList) 16343 # Prepend a pointer to the binding object onto the arguments 16344 return [Argument(self.descriptorProvider.nativeType + "*", "self")] + args 16345 16346 def getResultConversion(self): 16347 if self.needsBoolReturn: 16348 return "return aRetVal;\n" 16349 return "return;\n" 16350 16351 def getRvalDecl(self): 16352 if self.needsBoolReturn: 16353 return "bool aRetVal;\n" 16354 return "" 16355 16356 def getArgcDecl(self): 16357 # Don't need argc for anything. 16358 return None 16359 16360 def getDefaultRetval(self): 16361 if self.needsBoolReturn: 16362 return " false" 16363 return "" 16364 16365 def getCall(self): 16366 return CGMaplikeOrSetlikeMethodGenerator(self.descriptorProvider, 16367 self.maplikeOrSetlike, 16368 self.name.lower(), 16369 helperImpl=self).define() 16370 16371 def getPrettyName(self): 16372 return self.name 16373 16374 def declare(self): 16375 return self.implMethod.declare() 16376 16377 def define(self): 16378 return self.implMethod.define() 16379 16380 16381class CGMaplikeOrSetlikeHelperGenerator(CGNamespace): 16382 """ 16383 Declares and defines convenience methods for accessing backing objects on 16384 setlike/maplike interface. Generates function signatures, un/packs 16385 backing objects from slot, etc. 16386 """ 16387 def __init__(self, descriptor, maplikeOrSetlike): 16388 self.descriptor = descriptor 16389 # Since iterables are folded in with maplike/setlike, make sure we've 16390 # got the right type here. 16391 assert maplikeOrSetlike.isMaplike() or maplikeOrSetlike.isSetlike() 16392 self.maplikeOrSetlike = maplikeOrSetlike 16393 self.namespace = "%sHelpers" % (self.maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title()) 16394 self.helpers = [ 16395 CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor, 16396 maplikeOrSetlike, 16397 "Clear"), 16398 CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor, 16399 maplikeOrSetlike, 16400 "Delete", 16401 needsKeyArg=True, 16402 needsBoolReturn=True), 16403 CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor, 16404 maplikeOrSetlike, 16405 "Has", 16406 needsKeyArg=True, 16407 needsBoolReturn=True)] 16408 if self.maplikeOrSetlike.isMaplike(): 16409 self.helpers.append( 16410 CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor, 16411 maplikeOrSetlike, 16412 "Set", 16413 needsKeyArg=True, 16414 needsValueArg=True)) 16415 else: 16416 assert(self.maplikeOrSetlike.isSetlike()) 16417 self.helpers.append( 16418 CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor, 16419 maplikeOrSetlike, 16420 "Add", 16421 needsKeyArg=True)) 16422 CGNamespace.__init__(self, self.namespace, CGList(self.helpers)) 16423 16424 16425class CGIterableMethodGenerator(CGGeneric): 16426 """ 16427 Creates methods for iterable interfaces. Unwrapping/wrapping 16428 will be taken care of by the usual method generation machinery in 16429 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of 16430 using CGCallGenerator. 16431 """ 16432 def __init__(self, descriptor, iterable, methodName): 16433 if methodName == "forEach": 16434 CGGeneric.__init__(self, fill( 16435 """ 16436 if (!JS::IsCallable(arg0)) { 16437 ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "Argument 1 of ${ifaceName}.forEach"); 16438 return false; 16439 } 16440 JS::AutoValueArray<3> callArgs(cx); 16441 callArgs[2].setObject(*obj); 16442 JS::Rooted<JS::Value> ignoredReturnVal(cx); 16443 for (size_t i = 0; i < self->GetIterableLength(); ++i) { 16444 if (!ToJSValue(cx, self->GetValueAtIndex(i), callArgs[0])) { 16445 return false; 16446 } 16447 if (!ToJSValue(cx, self->GetKeyAtIndex(i), callArgs[1])) { 16448 return false; 16449 } 16450 if (!JS::Call(cx, arg1, arg0, JS::HandleValueArray(callArgs), 16451 &ignoredReturnVal)) { 16452 return false; 16453 } 16454 } 16455 """, 16456 ifaceName=descriptor.interface.identifier.name)) 16457 return 16458 CGGeneric.__init__(self, fill( 16459 """ 16460 typedef ${iterClass} itrType; 16461 RefPtr<itrType> result(new itrType(self, 16462 itrType::IterableIteratorType::${itrMethod}, 16463 &${ifaceName}IteratorBinding::Wrap)); 16464 """, 16465 iterClass=iteratorNativeType(descriptor), 16466 ifaceName=descriptor.interface.identifier.name, 16467 itrMethod=methodName.title())) 16468 16469 16470class GlobalGenRoots(): 16471 """ 16472 Roots for global codegen. 16473 16474 To generate code, call the method associated with the target, and then 16475 call the appropriate define/declare method. 16476 """ 16477 16478 @staticmethod 16479 def GeneratedAtomList(config): 16480 # Atom enum 16481 dictionaries = config.dictionaries 16482 16483 structs = [] 16484 16485 def memberToAtomCacheMember(binaryNameFor, m): 16486 binaryMemberName = binaryNameFor(m.identifier.name) 16487 return ClassMember(CGDictionary.makeIdName(binaryMemberName), 16488 "PinnedStringId", visibility="public") 16489 16490 def buildAtomCacheStructure(idlobj, binaryNameFor, members): 16491 classMembers = [memberToAtomCacheMember(binaryNameFor, m) 16492 for m in members] 16493 structName = idlobj.identifier.name + "Atoms" 16494 return (structName, 16495 CGWrapper(CGClass(structName, 16496 bases=None, 16497 isStruct=True, 16498 members=classMembers), post='\n')) 16499 16500 for dict in dictionaries: 16501 if len(dict.members) == 0: 16502 continue 16503 16504 structs.append(buildAtomCacheStructure(dict, lambda x: x, dict.members)) 16505 16506 for d in (config.getDescriptors(isJSImplemented=True) + 16507 config.getDescriptors(isCallback=True)): 16508 members = [m for m in d.interface.members if m.isAttr() or m.isMethod()] 16509 if d.interface.isJSImplemented() and d.interface.ctor(): 16510 # We'll have an __init() method. 16511 members.append(FakeMember('__init')) 16512 if len(members) == 0: 16513 continue 16514 16515 structs.append(buildAtomCacheStructure(d.interface, 16516 lambda x: d.binaryNameFor(x), 16517 members)) 16518 16519 structs.sort() 16520 generatedStructs = [struct for structName, struct in structs] 16521 structNames = [structName for structName, struct in structs] 16522 16523 mainStruct = CGWrapper(CGClass("PerThreadAtomCache", 16524 bases=[ClassBase(structName) for structName in structNames], 16525 isStruct=True), 16526 post='\n') 16527 16528 structs = CGList(generatedStructs + [mainStruct]) 16529 16530 # Wrap all of that in our namespaces. 16531 curr = CGNamespace.build(['mozilla', 'dom'], 16532 CGWrapper(structs, pre='\n')) 16533 curr = CGWrapper(curr, post='\n') 16534 16535 # Add include statement for PinnedStringId. 16536 declareIncludes = ['mozilla/dom/BindingUtils.h'] 16537 curr = CGHeaders([], [], [], [], declareIncludes, [], 'GeneratedAtomList', 16538 curr) 16539 16540 # Add include guards. 16541 curr = CGIncludeGuard('GeneratedAtomList', curr) 16542 16543 # Add the auto-generated comment. 16544 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) 16545 16546 # Done. 16547 return curr 16548 16549 @staticmethod 16550 def GeneratedEventList(config): 16551 eventList = CGList([]) 16552 for generatedEvent in config.generatedEvents: 16553 eventList.append(CGGeneric(declare=("GENERATED_EVENT(%s)\n" % generatedEvent))) 16554 return eventList 16555 16556 @staticmethod 16557 def PrototypeList(config): 16558 16559 # Prototype ID enum. 16560 descriptorsWithPrototype = config.getDescriptors(hasInterfacePrototypeObject=True) 16561 protos = [d.name for d in descriptorsWithPrototype] 16562 idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + protos, 16563 [0, '_ID_Start']) 16564 idEnum = CGList([idEnum]) 16565 16566 def fieldSizeAssert(amount, jitInfoField, message): 16567 maxFieldValue = "(uint64_t(1) << (sizeof(((JSJitInfo*)nullptr)->%s) * 8))" % jitInfoField 16568 return CGGeneric(declare="static_assert(%s < %s, \"%s\");\n\n" 16569 % (amount, maxFieldValue, message)) 16570 16571 idEnum.append(fieldSizeAssert("id::_ID_Count", "protoID", 16572 "Too many prototypes!")) 16573 16574 # Wrap all of that in our namespaces. 16575 idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'], 16576 CGWrapper(idEnum, pre='\n')) 16577 idEnum = CGWrapper(idEnum, post='\n') 16578 16579 curr = CGList([CGGeneric(define="#include <stdint.h>\n\n"), 16580 idEnum]) 16581 16582 # Let things know the maximum length of the prototype chain. 16583 maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH" 16584 maxMacro = CGGeneric(declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength)) 16585 curr.append(CGWrapper(maxMacro, post='\n\n')) 16586 curr.append(fieldSizeAssert(maxMacroName, "depth", 16587 "Some inheritance chain is too long!")) 16588 16589 # Constructor ID enum. 16590 constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)] 16591 idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + constructors, 16592 ['prototypes::id::_ID_Count', '_ID_Start']) 16593 16594 # Wrap all of that in our namespaces. 16595 idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'], 16596 CGWrapper(idEnum, pre='\n')) 16597 idEnum = CGWrapper(idEnum, post='\n') 16598 16599 curr.append(idEnum) 16600 16601 # Named properties object enum. 16602 namedPropertiesObjects = [d.name for d in config.getDescriptors(hasNamedPropertiesObject=True)] 16603 idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + namedPropertiesObjects, 16604 ['constructors::id::_ID_Count', '_ID_Start']) 16605 16606 # Wrap all of that in our namespaces. 16607 idEnum = CGNamespace.build(['mozilla', 'dom', 'namedpropertiesobjects'], 16608 CGWrapper(idEnum, pre='\n')) 16609 idEnum = CGWrapper(idEnum, post='\n') 16610 16611 curr.append(idEnum) 16612 16613 traitsDecls = [CGGeneric(declare=dedent(""" 16614 template <prototypes::ID PrototypeID> 16615 struct PrototypeTraits; 16616 """))] 16617 traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype) 16618 16619 ifaceNamesWithProto = [d.interface.identifier.name 16620 for d in descriptorsWithPrototype] 16621 traitsDecls.append(CGStringTable("NamesOfInterfacesWithProtos", 16622 ifaceNamesWithProto)) 16623 16624 traitsDecl = CGNamespace.build(['mozilla', 'dom'], 16625 CGList(traitsDecls)) 16626 16627 curr.append(traitsDecl) 16628 16629 # Add include guards. 16630 curr = CGIncludeGuard('PrototypeList', curr) 16631 16632 # Add the auto-generated comment. 16633 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) 16634 16635 # Done. 16636 return curr 16637 16638 @staticmethod 16639 def RegisterBindings(config): 16640 16641 curr = CGList([CGGlobalNamesString(config), CGRegisterGlobalNames(config)]) 16642 16643 # Wrap all of that in our namespaces. 16644 curr = CGNamespace.build(['mozilla', 'dom'], 16645 CGWrapper(curr, post='\n')) 16646 curr = CGWrapper(curr, post='\n') 16647 16648 # Add the includes 16649 defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface) 16650 for desc in config.getDescriptors(hasInterfaceObject=True, 16651 isExposedInWindow=True, 16652 register=True)] 16653 defineIncludes.append('mozilla/dom/WebIDLGlobalNameHash.h') 16654 defineIncludes.extend([CGHeaders.getDeclarationFilename(desc.interface) 16655 for desc in config.getDescriptors(isNavigatorProperty=True, 16656 register=True)]) 16657 curr = CGHeaders([], [], [], [], [], defineIncludes, 'RegisterBindings', 16658 curr) 16659 16660 # Add include guards. 16661 curr = CGIncludeGuard('RegisterBindings', curr) 16662 16663 # Done. 16664 return curr 16665 16666 @staticmethod 16667 def RegisterWorkerBindings(config): 16668 16669 curr = CGRegisterWorkerBindings(config) 16670 16671 # Wrap all of that in our namespaces. 16672 curr = CGNamespace.build(['mozilla', 'dom'], 16673 CGWrapper(curr, post='\n')) 16674 curr = CGWrapper(curr, post='\n') 16675 16676 # Add the includes 16677 defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface) 16678 for desc in config.getDescriptors(hasInterfaceObject=True, 16679 register=True, 16680 isExposedInAnyWorker=True)] 16681 16682 curr = CGHeaders([], [], [], [], [], defineIncludes, 16683 'RegisterWorkerBindings', curr) 16684 16685 # Add include guards. 16686 curr = CGIncludeGuard('RegisterWorkerBindings', curr) 16687 16688 # Done. 16689 return curr 16690 16691 @staticmethod 16692 def RegisterWorkerDebuggerBindings(config): 16693 16694 curr = CGRegisterWorkerDebuggerBindings(config) 16695 16696 # Wrap all of that in our namespaces. 16697 curr = CGNamespace.build(['mozilla', 'dom'], 16698 CGWrapper(curr, post='\n')) 16699 curr = CGWrapper(curr, post='\n') 16700 16701 # Add the includes 16702 defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface) 16703 for desc in config.getDescriptors(hasInterfaceObject=True, 16704 register=True, 16705 isExposedInWorkerDebugger=True)] 16706 16707 curr = CGHeaders([], [], [], [], [], defineIncludes, 16708 'RegisterWorkerDebuggerBindings', curr) 16709 16710 # Add include guards. 16711 curr = CGIncludeGuard('RegisterWorkerDebuggerBindings', curr) 16712 16713 # Done. 16714 return curr 16715 16716 @staticmethod 16717 def RegisterWorkletBindings(config): 16718 16719 curr = CGRegisterWorkletBindings(config) 16720 16721 # Wrap all of that in our namespaces. 16722 curr = CGNamespace.build(['mozilla', 'dom'], 16723 CGWrapper(curr, post='\n')) 16724 curr = CGWrapper(curr, post='\n') 16725 16726 # Add the includes 16727 defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface) 16728 for desc in config.getDescriptors(hasInterfaceObject=True, 16729 register=True, 16730 isExposedInAnyWorklet=True)] 16731 16732 curr = CGHeaders([], [], [], [], [], defineIncludes, 16733 'RegisterWorkletBindings', curr) 16734 16735 # Add include guards. 16736 curr = CGIncludeGuard('RegisterWorkletBindings', curr) 16737 16738 # Done. 16739 return curr 16740 16741 @staticmethod 16742 def ResolveSystemBinding(config): 16743 16744 curr = CGResolveSystemBinding(config) 16745 16746 # Wrap all of that in our namespaces. 16747 curr = CGNamespace.build(['mozilla', 'dom'], 16748 CGWrapper(curr, post='\n')) 16749 curr = CGWrapper(curr, post='\n') 16750 16751 # Add the includes 16752 defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface) 16753 for desc in config.getDescriptors(hasInterfaceObject=True, 16754 register=True, 16755 isExposedInSystemGlobals=True)] 16756 defineIncludes.append("nsThreadUtils.h") # For NS_IsMainThread 16757 defineIncludes.append("js/Id.h") # For jsid 16758 defineIncludes.append("mozilla/dom/BindingUtils.h") # AtomizeAndPinJSString 16759 16760 curr = CGHeaders([], [], [], [], [], defineIncludes, 16761 'ResolveSystemBinding', curr) 16762 16763 # Add include guards. 16764 curr = CGIncludeGuard('ResolveSystemBinding', curr) 16765 16766 # Done. 16767 return curr 16768 16769 @staticmethod 16770 def UnionTypes(config): 16771 unionTypes = UnionsForFile(config, None) 16772 (includes, implincludes, declarations, 16773 traverseMethods, unlinkMethods, 16774 unionStructs) = UnionTypes(unionTypes, config) 16775 16776 unions = CGList(traverseMethods + 16777 unlinkMethods + 16778 [CGUnionStruct(t, config) for t in unionStructs] + 16779 [CGUnionStruct(t, config, True) for t in unionStructs], 16780 "\n") 16781 16782 includes.add("mozilla/OwningNonNull.h") 16783 includes.add("mozilla/dom/UnionMember.h") 16784 includes.add("mozilla/dom/BindingDeclarations.h") 16785 # BindingUtils.h is only needed for SetToObject. 16786 # If it stops being inlined or stops calling CallerSubsumes 16787 # both this bit and the bit in CGBindingRoot can be removed. 16788 includes.add("mozilla/dom/BindingUtils.h") 16789 implincludes.add("mozilla/dom/PrimitiveConversions.h") 16790 16791 # Wrap all of that in our namespaces. 16792 curr = CGNamespace.build(['mozilla', 'dom'], unions) 16793 16794 curr = CGWrapper(curr, post='\n') 16795 16796 builder = ForwardDeclarationBuilder() 16797 for className, isStruct in declarations: 16798 builder.add(className, isStruct=isStruct) 16799 16800 curr = CGList([builder.build(), curr], "\n") 16801 16802 curr = CGHeaders([], [], [], [], includes, implincludes, 'UnionTypes', 16803 curr) 16804 16805 # Add include guards. 16806 curr = CGIncludeGuard('UnionTypes', curr) 16807 16808 # Done. 16809 return curr 16810 16811 @staticmethod 16812 def UnionConversions(config): 16813 unionTypes = [] 16814 for l in config.unionsPerFilename.itervalues(): 16815 unionTypes.extend(l) 16816 unionTypes.sort(key=lambda u: u.name) 16817 headers, unions = UnionConversions(unionTypes, 16818 config) 16819 16820 # Wrap all of that in our namespaces. 16821 curr = CGNamespace.build(['mozilla', 'dom'], unions) 16822 16823 curr = CGWrapper(curr, post='\n') 16824 16825 headers.update(["nsDebug.h", "mozilla/dom/UnionTypes.h"]) 16826 curr = CGHeaders([], [], [], [], headers, [], 'UnionConversions', curr) 16827 16828 # Add include guards. 16829 curr = CGIncludeGuard('UnionConversions', curr) 16830 16831 # Done. 16832 return curr 16833 16834 16835# Code generator for simple events 16836class CGEventGetter(CGNativeMember): 16837 def __init__(self, descriptor, attr): 16838 ea = descriptor.getExtendedAttributes(attr, getter=True) 16839 CGNativeMember.__init__(self, descriptor, attr, 16840 CGSpecializedGetter.makeNativeName(descriptor, 16841 attr), 16842 (attr.type, []), 16843 ea, 16844 resultNotAddRefed=not attr.type.isSequence()) 16845 self.body = self.getMethodBody() 16846 16847 def getArgs(self, returnType, argList): 16848 if 'infallible' not in self.extendedAttrs: 16849 raise TypeError("Event code generator does not support [Throws]!") 16850 if not self.member.isAttr(): 16851 raise TypeError("Event code generator does not support methods") 16852 if self.member.isStatic(): 16853 raise TypeError("Event code generators does not support static attributes") 16854 return CGNativeMember.getArgs(self, returnType, argList) 16855 16856 def getMethodBody(self): 16857 type = self.member.type 16858 memberName = CGDictionary.makeMemberName(self.member.identifier.name) 16859 if (type.isPrimitive() and type.tag() in builtinNames) or type.isEnum() or type.isGeckoInterface(): 16860 return "return " + memberName + ";\n" 16861 if type.isDOMString() or type.isByteString() or type.isUSVString(): 16862 return "aRetVal = " + memberName + ";\n" 16863 if type.isSpiderMonkeyInterface() or type.isObject(): 16864 return fill( 16865 """ 16866 if (${memberName}) { 16867 JS::ExposeObjectToActiveJS(${memberName}); 16868 } 16869 aRetVal.set(${memberName}); 16870 return; 16871 """, 16872 memberName=memberName) 16873 if type.isAny(): 16874 return fill( 16875 """ 16876 ${selfName}(aRetVal); 16877 """, 16878 selfName=self.name) 16879 if type.isUnion(): 16880 return "aRetVal = " + memberName + ";\n" 16881 if type.isSequence(): 16882 return "aRetVal = " + memberName + ";\n" 16883 raise TypeError("Event code generator does not support this type!") 16884 16885 def declare(self, cgClass): 16886 if getattr(self.member, "originatingInterface", 16887 cgClass.descriptor.interface) != cgClass.descriptor.interface: 16888 return "" 16889 return CGNativeMember.declare(self, cgClass) 16890 16891 def define(self, cgClass): 16892 if getattr(self.member, "originatingInterface", 16893 cgClass.descriptor.interface) != cgClass.descriptor.interface: 16894 return "" 16895 return CGNativeMember.define(self, cgClass) 16896 16897 16898class CGEventSetter(CGNativeMember): 16899 def __init__(self): 16900 raise TypeError("Event code generator does not support setters!") 16901 16902 16903class CGEventMethod(CGNativeMember): 16904 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): 16905 self.isInit = False 16906 16907 CGNativeMember.__init__(self, descriptor, method, 16908 CGSpecializedMethod.makeNativeName(descriptor, 16909 method), 16910 signature, 16911 descriptor.getExtendedAttributes(method), 16912 breakAfter=breakAfter, 16913 variadicIsSequence=True) 16914 self.originalArgs = list(self.args) 16915 16916 iface = descriptor.interface 16917 allowed = isConstructor 16918 if not allowed and iface.getExtendedAttribute("LegacyEventInit"): 16919 # Allow it, only if it fits the initFooEvent profile exactly 16920 # We could check the arg types but it's not worth the effort. 16921 if (method.identifier.name == "init" + iface.identifier.name and 16922 signature[1][0].type.isDOMString() and 16923 signature[1][1].type.isBoolean() and 16924 signature[1][2].type.isBoolean() and 16925 # -3 on the left to ignore the type, bubbles, and cancelable parameters 16926 # -1 on the right to ignore the .trusted property which bleeds through 16927 # here because it is [Unforgeable]. 16928 len(signature[1]) - 3 == len(filter(lambda x: x.isAttr(), iface.members)) - 1): 16929 allowed = True 16930 self.isInit = True 16931 16932 if not allowed: 16933 raise TypeError("Event code generator does not support methods!") 16934 16935 def getArgs(self, returnType, argList): 16936 args = [self.getArg(arg) for arg in argList] 16937 return args 16938 16939 def getArg(self, arg): 16940 decl, ref = self.getArgType(arg.type, 16941 arg.canHaveMissingValue(), 16942 "Variadic" if arg.variadic else False) 16943 if ref: 16944 decl = CGWrapper(decl, pre="const ", post="&") 16945 16946 name = arg.identifier.name 16947 name = "a" + name[0].upper() + name[1:] 16948 return Argument(decl.define(), name) 16949 16950 def declare(self, cgClass): 16951 if self.isInit: 16952 constructorForNativeCaller = "" 16953 else: 16954 self.args = list(self.originalArgs) 16955 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner")) 16956 constructorForNativeCaller = CGNativeMember.declare(self, cgClass) 16957 16958 self.args = list(self.originalArgs) 16959 if needCx(None, self.arguments(), [], considerTypes=True, static=True): 16960 self.args.insert(0, Argument("JSContext*", "aCx")) 16961 if not self.isInit: 16962 self.args.insert(0, Argument("const GlobalObject&", "aGlobal")) 16963 self.args.append(Argument('ErrorResult&', 'aRv')) 16964 return constructorForNativeCaller + CGNativeMember.declare(self, cgClass) 16965 16966 def defineInit(self, cgClass): 16967 iface = self.descriptorProvider.interface 16968 members = "" 16969 while iface.identifier.name != "Event": 16970 i = 3 # Skip the boilerplate args: type, bubble,s cancelable. 16971 for m in iface.members: 16972 if m.isAttr(): 16973 # We need to initialize all the member variables that do 16974 # not come from Event. 16975 if getattr(m, "originatingInterface", 16976 iface).identifier.name == "Event": 16977 continue 16978 name = CGDictionary.makeMemberName(m.identifier.name) 16979 members += "%s = %s;\n" % (name, self.args[i].name) 16980 i += 1 16981 iface = iface.parent 16982 16983 self.body = fill( 16984 """ 16985 InitEvent(${typeArg}, ${bubblesArg}, ${cancelableArg}); 16986 ${members} 16987 """, 16988 typeArg=self.args[0].name, 16989 bubblesArg=self.args[1].name, 16990 cancelableArg=self.args[2].name, 16991 members=members) 16992 16993 return CGNativeMember.define(self, cgClass) 16994 16995 def define(self, cgClass): 16996 self.args = list(self.originalArgs) 16997 if self.isInit: 16998 return self.defineInit(cgClass) 16999 members = "" 17000 holdJS = "" 17001 iface = self.descriptorProvider.interface 17002 while iface.identifier.name != "Event": 17003 for m in self.descriptorProvider.getDescriptor(iface.identifier.name).interface.members: 17004 if m.isAttr(): 17005 # We initialize all the other member variables in the 17006 # Constructor except those ones coming from the Event. 17007 if getattr(m, "originatingInterface", 17008 cgClass.descriptor.interface).identifier.name == "Event": 17009 continue 17010 name = CGDictionary.makeMemberName(m.identifier.name) 17011 if m.type.isSequence(): 17012 # For sequences we may not be able to do a simple 17013 # assignment because the underlying types may not match. 17014 # For example, the argument can be a 17015 # Sequence<OwningNonNull<SomeInterface>> while our 17016 # member is an nsTArray<RefPtr<SomeInterface>>. So 17017 # use AppendElements, which is actually a template on 17018 # the incoming type on nsTArray and does the right thing 17019 # for this case. 17020 target = name 17021 source = "%s.%s" % (self.args[1].name, name) 17022 sequenceCopy = "e->%s.AppendElements(%s);\n" 17023 if m.type.nullable(): 17024 sequenceCopy = CGIfWrapper( 17025 CGGeneric(sequenceCopy), 17026 "!%s.IsNull()" % source).define() 17027 target += ".SetValue()" 17028 source += ".Value()" 17029 members += sequenceCopy % (target, source) 17030 elif m.type.isSpiderMonkeyInterface(): 17031 srcname = "%s.%s" % (self.args[1].name, name) 17032 if m.type.nullable(): 17033 members += fill( 17034 """ 17035 if (${srcname}.IsNull()) { 17036 e->${varname} = nullptr; 17037 } else { 17038 e->${varname} = ${srcname}.Value().Obj(); 17039 } 17040 """, 17041 varname=name, 17042 srcname=srcname) 17043 else: 17044 members += fill( 17045 """ 17046 e->${varname}.set(${srcname}.Obj()); 17047 """, 17048 varname=name, srcname=srcname) 17049 else: 17050 members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name) 17051 if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface(): 17052 holdJS = "mozilla::HoldJSObjects(e.get());\n" 17053 iface = iface.parent 17054 17055 self.body = fill( 17056 """ 17057 RefPtr<${nativeType}> e = new ${nativeType}(aOwner); 17058 bool trusted = e->Init(aOwner); 17059 e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable); 17060 $*{members} 17061 e->SetTrusted(trusted); 17062 e->SetComposed(${eventInit}.mComposed); 17063 $*{holdJS} 17064 return e.forget(); 17065 """, 17066 nativeType=self.descriptorProvider.nativeType.split('::')[-1], 17067 eventType=self.args[0].name, 17068 eventInit=self.args[1].name, 17069 members=members, 17070 holdJS=holdJS) 17071 17072 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner")) 17073 constructorForNativeCaller = CGNativeMember.define(self, cgClass) + "\n" 17074 self.args = list(self.originalArgs) 17075 self.body = fill( 17076 """ 17077 nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports()); 17078 return Constructor(owner, ${arg0}, ${arg1}); 17079 """, 17080 arg0=self.args[0].name, 17081 arg1=self.args[1].name) 17082 if needCx(None, self.arguments(), [], considerTypes=True, static=True): 17083 self.args.insert(0, Argument("JSContext*", "aCx")) 17084 self.args.insert(0, Argument("const GlobalObject&", "aGlobal")) 17085 self.args.append(Argument('ErrorResult&', 'aRv')) 17086 return constructorForNativeCaller + CGNativeMember.define(self, cgClass) 17087 17088 17089class CGEventClass(CGBindingImplClass): 17090 """ 17091 Codegen for the actual Event class implementation for this descriptor 17092 """ 17093 def __init__(self, descriptor): 17094 CGBindingImplClass.__init__(self, descriptor, CGEventMethod, CGEventGetter, CGEventSetter, False, "WrapObjectInternal") 17095 members = [] 17096 extraMethods = [] 17097 for m in descriptor.interface.members: 17098 if m.isAttr(): 17099 if m.type.isAny(): 17100 # Add a getter that doesn't need a JSContext. Note that we 17101 # don't need to do this if our originating interface is not 17102 # the descriptor's interface, because in that case we 17103 # wouldn't generate the getter that _does_ need a JSContext 17104 # either. 17105 extraMethods.append( 17106 ClassMethod( 17107 CGSpecializedGetter.makeNativeName(descriptor, m), 17108 "void", 17109 [Argument("JS::MutableHandle<JS::Value>", 17110 "aRetVal")], 17111 const=True, 17112 body=fill( 17113 """ 17114 JS::ExposeValueToActiveJS(${memberName}); 17115 aRetVal.set(${memberName}); 17116 """, 17117 memberName=CGDictionary.makeMemberName(m.identifier.name)))) 17118 if getattr(m, "originatingInterface", 17119 descriptor.interface) != descriptor.interface: 17120 continue 17121 nativeType = self.getNativeTypeForIDLType(m.type).define() 17122 members.append(ClassMember(CGDictionary.makeMemberName(m.identifier.name), 17123 nativeType, 17124 visibility="private", 17125 body="body")) 17126 17127 parent = self.descriptor.interface.parent 17128 self.parentType = self.descriptor.getDescriptor(parent.identifier.name).nativeType.split('::')[-1] 17129 baseDeclarations = fill( 17130 """ 17131 public: 17132 NS_DECL_ISUPPORTS_INHERITED 17133 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(${nativeType}, ${parentType}) 17134 protected: 17135 virtual ~${nativeType}(); 17136 explicit ${nativeType}(mozilla::dom::EventTarget* aOwner); 17137 17138 """, 17139 nativeType=self.descriptor.nativeType.split('::')[-1], 17140 parentType=self.parentType) 17141 17142 className = descriptor.nativeType.split('::')[-1] 17143 asConcreteTypeMethod = ClassMethod("As%s" % className, 17144 "%s*" % className, 17145 [], 17146 virtual=True, 17147 body="return this;\n", 17148 breakAfterReturnDecl=" ", 17149 override=True) 17150 extraMethods.append(asConcreteTypeMethod) 17151 17152 CGClass.__init__(self, className, 17153 bases=[ClassBase(self.parentType)], 17154 methods=extraMethods+self.methodDecls, 17155 members=members, 17156 extradeclarations=baseDeclarations) 17157 17158 def getWrapObjectBody(self): 17159 return "return %sBinding::Wrap(aCx, this, aGivenProto);\n" % self.descriptor.name 17160 17161 def implTraverse(self): 17162 retVal = "" 17163 for m in self.descriptor.interface.members: 17164 # Unroll the type so we pick up sequences of interfaces too. 17165 if m.isAttr() and idlTypeNeedsCycleCollection(m.type): 17166 retVal += (" NS_IMPL_CYCLE_COLLECTION_TRAVERSE(" + 17167 CGDictionary.makeMemberName(m.identifier.name) + 17168 ")\n") 17169 return retVal 17170 17171 def implUnlink(self): 17172 retVal = "" 17173 for m in self.descriptor.interface.members: 17174 if m.isAttr(): 17175 name = CGDictionary.makeMemberName(m.identifier.name) 17176 # Unroll the type so we pick up sequences of interfaces too. 17177 if idlTypeNeedsCycleCollection(m.type): 17178 retVal += " NS_IMPL_CYCLE_COLLECTION_UNLINK(" + name + ")\n" 17179 elif m.type.isAny(): 17180 retVal += " tmp->" + name + ".setUndefined();\n" 17181 elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): 17182 retVal += " tmp->" + name + " = nullptr;\n" 17183 return retVal 17184 17185 def implTrace(self): 17186 retVal = "" 17187 for m in self.descriptor.interface.members: 17188 if m.isAttr(): 17189 name = CGDictionary.makeMemberName(m.identifier.name) 17190 if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface(): 17191 retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(" + name + ")\n" 17192 elif typeNeedsRooting(m.type): 17193 raise TypeError("Need to implement tracing for event " 17194 "member of type %s" % m.type) 17195 return retVal 17196 17197 def define(self): 17198 dropJS = "" 17199 for m in self.descriptor.interface.members: 17200 if m.isAttr(): 17201 member = CGDictionary.makeMemberName(m.identifier.name) 17202 if m.type.isAny(): 17203 dropJS += member + " = JS::UndefinedValue();\n" 17204 elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): 17205 dropJS += member + " = nullptr;\n" 17206 if dropJS != "": 17207 dropJS += "mozilla::DropJSObjects(this);\n" 17208 # Just override CGClass and do our own thing 17209 nativeType = self.descriptor.nativeType.split('::')[-1] 17210 ctorParams = ("aOwner, nullptr, nullptr" if self.parentType == "Event" 17211 else "aOwner") 17212 17213 classImpl = fill( 17214 """ 17215 17216 NS_IMPL_CYCLE_COLLECTION_CLASS(${nativeType}) 17217 17218 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType}) 17219 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType}) 17220 17221 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(${nativeType}, ${parentType}) 17222 $*{traverse} 17223 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 17224 17225 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(${nativeType}, ${parentType}) 17226 $*{trace} 17227 NS_IMPL_CYCLE_COLLECTION_TRACE_END 17228 17229 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(${nativeType}, ${parentType}) 17230 $*{unlink} 17231 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 17232 17233 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${nativeType}) 17234 NS_INTERFACE_MAP_END_INHERITING(${parentType}) 17235 17236 ${nativeType}::${nativeType}(mozilla::dom::EventTarget* aOwner) 17237 : ${parentType}(${ctorParams}) 17238 { 17239 } 17240 17241 ${nativeType}::~${nativeType}() 17242 { 17243 $*{dropJS} 17244 } 17245 17246 """, 17247 ifaceName=self.descriptor.name, 17248 nativeType=nativeType, 17249 ctorParams=ctorParams, 17250 parentType=self.parentType, 17251 traverse=self.implTraverse(), 17252 unlink=self.implUnlink(), 17253 trace=self.implTrace(), 17254 dropJS=dropJS) 17255 return classImpl + CGBindingImplClass.define(self) 17256 17257 def getNativeTypeForIDLType(self, type): 17258 if type.isPrimitive() and type.tag() in builtinNames: 17259 nativeType = CGGeneric(builtinNames[type.tag()]) 17260 if type.nullable(): 17261 nativeType = CGTemplatedType("Nullable", nativeType) 17262 elif type.isEnum(): 17263 nativeType = CGGeneric(type.unroll().inner.identifier.name) 17264 if type.nullable(): 17265 nativeType = CGTemplatedType("Nullable", nativeType) 17266 elif type.isDOMString() or type.isUSVString(): 17267 nativeType = CGGeneric("nsString") 17268 elif type.isByteString(): 17269 nativeType = CGGeneric("nsCString") 17270 elif type.isGeckoInterface(): 17271 iface = type.unroll().inner 17272 nativeType = self.descriptor.getDescriptor( 17273 iface.identifier.name).nativeType 17274 # Now trim off unnecessary namespaces 17275 nativeType = nativeType.split("::") 17276 if nativeType[0] == "mozilla": 17277 nativeType.pop(0) 17278 if nativeType[0] == "dom": 17279 nativeType.pop(0) 17280 nativeType = CGWrapper(CGGeneric("::".join(nativeType)), pre="RefPtr<", post=">") 17281 elif type.isAny(): 17282 nativeType = CGGeneric("JS::Heap<JS::Value>") 17283 elif type.isObject() or type.isSpiderMonkeyInterface(): 17284 nativeType = CGGeneric("JS::Heap<JSObject*>") 17285 elif type.isUnion(): 17286 nativeType = CGGeneric(CGUnionStruct.unionTypeDecl(type, True)) 17287 elif type.isSequence(): 17288 if type.nullable(): 17289 innerType = type.inner.inner 17290 else: 17291 innerType = type.inner 17292 if (not innerType.isPrimitive() and not innerType.isEnum() and 17293 not innerType.isDOMString() and not innerType.isByteString() and 17294 not innerType.isGeckoInterface()): 17295 raise TypeError("Don't know how to properly manage GC/CC for " 17296 "event member of type %s" % 17297 type) 17298 nativeType = CGTemplatedType( 17299 "nsTArray", 17300 self.getNativeTypeForIDLType(innerType)) 17301 if type.nullable(): 17302 nativeType = CGTemplatedType("Nullable", nativeType) 17303 else: 17304 raise TypeError("Don't know how to declare event member of type %s" % 17305 type) 17306 return nativeType 17307 17308 17309class CGEventRoot(CGThing): 17310 def __init__(self, config, interfaceName): 17311 descriptor = config.getDescriptor(interfaceName) 17312 17313 self.root = CGWrapper(CGEventClass(descriptor), 17314 pre="\n", post="\n") 17315 17316 self.root = CGNamespace.build(["mozilla", "dom"], self.root) 17317 17318 self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True), 17319 self.root]) 17320 17321 parent = descriptor.interface.parent.identifier.name 17322 17323 # Throw in our #includes 17324 self.root = CGHeaders( 17325 [descriptor], 17326 [], 17327 [], 17328 [], 17329 [ 17330 config.getDescriptor(parent).headerFile, 17331 "mozilla/Attributes.h", 17332 "mozilla/ErrorResult.h", 17333 "mozilla/dom/%sBinding.h" % interfaceName, 17334 'mozilla/dom/BindingUtils.h', 17335 ], 17336 [ 17337 "%s.h" % interfaceName, 17338 "js/GCAPI.h", 17339 'mozilla/dom/Nullable.h', 17340 ], 17341 "", self.root, config) 17342 17343 # And now some include guards 17344 self.root = CGIncludeGuard(interfaceName, self.root) 17345 17346 self.root = CGWrapper( 17347 self.root, 17348 pre=(AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT % 17349 os.path.basename(descriptor.interface.filename()))) 17350 17351 self.root = CGWrapper(self.root, pre=dedent(""" 17352 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 17353 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 17354 /* This Source Code Form is subject to the terms of the Mozilla Public 17355 * License, v. 2.0. If a copy of the MPL was not distributed with this 17356 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 17357 17358 """)) 17359 17360 def declare(self): 17361 return self.root.declare() 17362 17363 def define(self): 17364 return self.root.define() 17365