tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Codegen.py (918394B)


      1 # This Source Code Form is subject to the terms of the Mozilla Public
      2 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 # You can obtain one at http://mozilla.org/MPL/2.0/.
      4 
      5 # Common codegen classes.
      6 
      7 import functools
      8 import math
      9 from operator import attrgetter
     10 import os
     11 import re
     12 import string
     13 import textwrap
     14 
     15 from Configuration import (
     16    Configuration,
     17    Descriptor,
     18    MemberIsLegacyUnforgeable,
     19    NoSuchDescriptorError,
     20    getAllTypes,
     21    getTypesFromCallback,
     22    getTypesFromDescriptor,
     23    getTypesFromDictionary,
     24 )
     25 from perfecthash import PerfectHash
     26 from WebIDL import (
     27    BuiltinTypes,
     28    IDLAttribute,
     29    IDLBuiltinType,
     30    IDLDefaultDictionaryValue,
     31    IDLDictionary,
     32    IDLEmptySequenceValue,
     33    IDLInterfaceMember,
     34    IDLNullValue,
     35    IDLSequenceType,
     36    IDLType,
     37    IDLTypedef,
     38    IDLUndefinedValue,
     39 )
     40 
     41 AUTOGENERATED_WARNING_COMMENT = (
     42    "/* THIS FILE IS AUTOGENERATED BY Codegen.py - DO NOT EDIT */\n\n"
     43 )
     44 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT = (
     45    "/* THIS FILE IS AUTOGENERATED FROM %s BY Codegen.py - DO NOT EDIT */\n\n"
     46 )
     47 FINALIZE_HOOK_NAME = "_finalize"
     48 CONSTRUCT_HOOK_NAME = "_constructor"
     49 LEGACYCALLER_HOOK_NAME = "_legacycaller"
     50 RESOLVE_HOOK_NAME = "_resolve"
     51 MAY_RESOLVE_HOOK_NAME = "_mayResolve"
     52 NEW_ENUMERATE_HOOK_NAME = "_newEnumerate"
     53 INSTANCE_RESERVED_SLOTS = 1
     54 
     55 # This size is arbitrary. It is a power of 2 to make using it as a modulo
     56 # operand cheap, and is usually around 1/3-1/5th of the set size (sometimes
     57 # smaller for very large sets).
     58 GLOBAL_NAMES_PHF_SIZE = 256
     59 
     60 # If you have to change this list (which you shouldn't!), make sure it
     61 # continues to match the list in test_Object.prototype_props.html
     62 JS_OBJECT_PROTOTYPE_PROPERTIES = [
     63    "constructor",
     64    "toString",
     65    "toLocaleString",
     66    "valueOf",
     67    "hasOwnProperty",
     68    "isPrototypeOf",
     69    "propertyIsEnumerable",
     70    "__defineGetter__",
     71    "__defineSetter__",
     72    "__lookupGetter__",
     73    "__lookupSetter__",
     74    "__proto__",
     75 ]
     76 
     77 
     78 def reservedSlot(slotIndex, forXray):
     79    base = "DOM_EXPANDO_RESERVED_SLOTS" if forXray else "DOM_INSTANCE_RESERVED_SLOTS"
     80    return "(%s + %d)" % (base, slotIndex)
     81 
     82 
     83 def getSlotIndex(member, descriptor):
     84    slotIndex = member.slotIndices[descriptor.interface.identifier.name]
     85    return slotIndex[0] if isinstance(slotIndex, tuple) else slotIndex
     86 
     87 
     88 def memberReservedSlot(member, descriptor):
     89    return reservedSlot(getSlotIndex(member, descriptor), False)
     90 
     91 
     92 def memberXrayExpandoReservedSlot(member, descriptor):
     93    return reservedSlot(getSlotIndex(member, descriptor), True)
     94 
     95 
     96 def mayUseXrayExpandoSlots(descriptor, attr):
     97    assert not attr.getExtendedAttribute("NewObject")
     98    # For attributes whose type is a Gecko interface we always use
     99    # slots on the reflector for caching.  Also, for interfaces that
    100    # don't want Xrays we obviously never use the Xray expando slot.
    101    return descriptor.wantsXrays and not attr.type.isGeckoInterface()
    102 
    103 
    104 def reflectedHTMLAttributesArrayIndex(descriptor, attr):
    105    slots = attr.slotIndices[descriptor.interface.identifier.name]
    106    return slots[1]
    107 
    108 
    109 def getReflectedHTMLAttributesIface(descriptor):
    110    iface = descriptor.interface
    111    while iface:
    112        if iface.reflectedHTMLAttributesReturningFrozenArray:
    113            return iface
    114        iface = iface.parent
    115    return None
    116 
    117 
    118 def toStringBool(arg):
    119    """
    120    Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false)
    121    """
    122    return str(not not arg).lower()
    123 
    124 
    125 def toBindingNamespace(arg):
    126    return arg + "_Binding"
    127 
    128 
    129 def isTypeCopyConstructible(type):
    130    # Nullable and sequence stuff doesn't affect copy-constructibility
    131    type = type.unroll()
    132    return (
    133        type.isUndefined()
    134        or type.isPrimitive()
    135        or type.isString()
    136        or type.isEnum()
    137        or (type.isUnion() and CGUnionStruct.isUnionCopyConstructible(type))
    138        or (
    139            type.isDictionary()
    140            and CGDictionary.isDictionaryCopyConstructible(type.inner)
    141        )
    142        or
    143        # Interface types are only copy-constructible if they're Gecko
    144        # interfaces.  SpiderMonkey interfaces are not copy-constructible
    145        # because of rooting issues.
    146        (type.isInterface() and type.isGeckoInterface())
    147    )
    148 
    149 
    150 class CycleCollectionUnsupported(TypeError):
    151    def __init__(self, message):
    152        TypeError.__init__(self, message)
    153 
    154 
    155 def idlTypeNeedsCycleCollection(type):
    156    type = type.unroll()  # Takes care of sequences and nullables
    157    if (
    158        (type.isPrimitive() and type.tag() in builtinNames)
    159        or type.isUndefined()
    160        or type.isEnum()
    161        or type.isString()
    162        or type.isAny()
    163        or type.isObject()
    164        or type.isSpiderMonkeyInterface()
    165    ):
    166        return False
    167    elif type.isCallback() or type.isPromise() or type.isGeckoInterface():
    168        return True
    169    elif type.isUnion():
    170        return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
    171    elif type.isRecord():
    172        if idlTypeNeedsCycleCollection(type.inner):
    173            raise CycleCollectionUnsupported(
    174                "Cycle collection for type %s is not supported" % type
    175            )
    176        return False
    177    elif type.isDictionary():
    178        return CGDictionary.dictionaryNeedsCycleCollection(type.inner)
    179    else:
    180        raise CycleCollectionUnsupported(
    181            "Don't know whether to cycle-collect type %s" % type
    182        )
    183 
    184 
    185 def idlTypeNeedsCallContext(type, descriptor=None, allowTreatNonCallableAsNull=False):
    186    """
    187    Returns whether the given type needs error reporting via a
    188    BindingCallContext for JS-to-C++ conversions.  This will happen when the
    189    conversion can throw an exception due to logic in the IDL spec or
    190    Gecko-specific security checks.  In particular, a type needs a
    191    BindingCallContext if and only if the JS-to-C++ conversion for that type can
    192    end up calling ThrowErrorMessage.
    193 
    194    For some types this depends on the descriptor (e.g. because we do certain
    195    checks only for some kinds of interfaces).
    196 
    197    The allowTreatNonCallableAsNull optimization is there so we can avoid
    198    generating an unnecessary BindingCallContext for all the event handler
    199    attribute setters.
    200 
    201    """
    202    while True:
    203        if type.isSequence():
    204            # Sequences can always throw "not an object"
    205            return True
    206        if type.nullable():
    207            # treatNonObjectAsNull() and treatNonCallableAsNull() are
    208            # only sane things to test on nullable types, so do that now.
    209            if (
    210                allowTreatNonCallableAsNull
    211                and type.isCallback()
    212                and (type.treatNonObjectAsNull() or type.treatNonCallableAsNull())
    213            ):
    214                # This can't throw. so never needs a method description.
    215                return False
    216            type = type.inner
    217        else:
    218            break
    219 
    220    if type.isUndefined():
    221        # Clearly doesn't need a method description; we can only get here from
    222        # CGHeaders trying to decide whether to include the method description
    223        # header.
    224        return False
    225    # The float check needs to come before the isPrimitive() check,
    226    # because floats are primitives too.
    227    if type.isFloat():
    228        # Floats can throw if restricted.
    229        return not type.isUnrestricted()
    230    if type.isPrimitive() and type.tag() in builtinNames:
    231        # Numbers can throw if enforcing range.
    232        return type.hasEnforceRange()
    233    if type.isEnum():
    234        # Can throw on invalid value.
    235        return True
    236    if type.isString():
    237        # Can throw if it's a ByteString
    238        return type.isByteString()
    239    if type.isAny():
    240        # JS-implemented interfaces do extra security checks so need a
    241        # method description here.  If we have no descriptor, this
    242        # might be JS-implemented thing, so it will do the security
    243        # check and we need the method description.
    244        return not descriptor or descriptor.interface.isJSImplemented()
    245    if type.isPromise():
    246        # JS-to-Promise conversion won't cause us to throw any
    247        # specific exceptions, so does not need a method description.
    248        return False
    249    if (
    250        type.isObject()
    251        or type.isInterface()
    252        or type.isCallback()
    253        or type.isDictionary()
    254        or type.isRecord()
    255        or type.isObservableArray()
    256    ):
    257        # These can all throw if a primitive is passed in, at the very least.
    258        # There are some rare cases when we know we have an object, but those
    259        # are not worth the complexity of optimizing for.
    260        #
    261        # Note that we checked the [LegacyTreatNonObjectAsNull] case already when
    262        # unwrapping nullables.
    263        return True
    264    if type.isUnion():
    265        # Can throw if a type not in the union is passed in.
    266        return True
    267    raise TypeError("Don't know whether type '%s' needs a method description" % type)
    268 
    269 
    270 # TryPreserveWrapper uses the addProperty hook to preserve the wrapper of
    271 # non-nsISupports cycle collected objects, so if wantsPreservedWrapper is changed
    272 # to not cover that case then TryPreserveWrapper will need to be changed.
    273 def wantsPreservedWrapper(desc):
    274    return desc.concrete and desc.wrapperCache and not desc.isGlobal()
    275 
    276 
    277 def wantsGetWrapperCache(desc):
    278    return (
    279        desc.concrete and desc.wrapperCache and not desc.isGlobal() and not desc.proxy
    280    )
    281 
    282 
    283 def indent(s, indentLevel=2):
    284    """
    285    Indent C++ code.
    286 
    287    Weird secret feature: this doesn't indent lines that start with # (such as
    288    #include lines or #ifdef/#endif).
    289    """
    290 
    291    # We'll want to insert the indent at the beginnings of lines, but we
    292    # don't want to indent empty lines.
    293    padding = indentLevel * " "
    294    return "\n".join(
    295        [
    296            (padding + line) if line and line[0] != "#" else line
    297            for line in s.split("\n")
    298        ]
    299    )
    300 
    301 
    302 # dedent() and fill() are often called on the same string multiple
    303 # times.  We want to memoize their return values so we don't keep
    304 # recomputing them all the time.
    305 def memoize(fn):
    306    """
    307    Decorator to memoize a function of one argument.  The cache just
    308    grows without bound.
    309    """
    310    cache = {}
    311 
    312    @functools.wraps(fn)
    313    def wrapper(arg):
    314        retval = cache.get(arg)
    315        if retval is None:
    316            retval = cache[arg] = fn(arg)
    317        return retval
    318 
    319    return wrapper
    320 
    321 
    322 @memoize
    323 def dedent(s):
    324    """
    325    Remove all leading whitespace from s, and remove a blank line
    326    at the beginning.
    327    """
    328    if s.startswith("\n"):
    329        s = s[1:]
    330    return textwrap.dedent(s)
    331 
    332 
    333 # This works by transforming the fill()-template to an equivalent
    334 # string.Template.
    335 fill_multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?")
    336 
    337 
    338 find_substitutions = re.compile(r"\${")
    339 
    340 
    341 @memoize
    342 def compile_fill_template(template):
    343    """
    344    Helper function for fill().  Given the template string passed to fill(),
    345    do the reusable part of template processing and return a pair (t,
    346    argModList) that can be used every time fill() is called with that
    347    template argument.
    348 
    349    argsModList is list of tuples that represent modifications to be
    350    made to args.  Each modification has, in order: i) the arg name,
    351    ii) the modified name, iii) the indent depth.
    352    """
    353    t = dedent(template)
    354    assert t.endswith("\n") or "\n" not in t
    355    argModList = []
    356 
    357    def replace(match):
    358        """
    359        Replaces a line like '  $*{xyz}\n' with '${xyz_n}',
    360        where n is the indent depth, and add a corresponding entry to
    361        argModList.
    362 
    363        Note that this needs to close over argModList, so it has to be
    364        defined inside compile_fill_template().
    365        """
    366        indentation, name, nl = match.groups()
    367        depth = len(indentation)
    368 
    369        # Check that $*{xyz} appears by itself on a line.
    370        prev = match.string[: match.start()]
    371        if (prev and not prev.endswith("\n")) or nl is None:
    372            raise ValueError(
    373                "Invalid fill() template: $*{%s} must appear by itself on a line" % name
    374            )
    375 
    376        # Now replace this whole line of template with the indented equivalent.
    377        modified_name = name + "_" + str(depth)
    378        argModList.append((name, modified_name, depth))
    379        return "${" + modified_name + "}"
    380 
    381    t = re.sub(fill_multiline_substitution_re, replace, t)
    382    if not re.search(find_substitutions, t):
    383        raise TypeError("Using fill() when dedent() would do.")
    384    return (string.Template(t), argModList)
    385 
    386 
    387 def fill(template, **args):
    388    """
    389    Convenience function for filling in a multiline template.
    390 
    391    `fill(template, name1=v1, name2=v2)` is a lot like
    392    `string.Template(template).substitute({"name1": v1, "name2": v2})`.
    393 
    394    However, it's shorter, and has a few nice features:
    395 
    396      * If `template` is indented, fill() automatically dedents it!
    397        This makes code using fill() with Python's multiline strings
    398        much nicer to look at.
    399 
    400      * If `template` starts with a blank line, fill() strips it off.
    401        (Again, convenient with multiline strings.)
    402 
    403      * fill() recognizes a special kind of substitution
    404        of the form `$*{name}`.
    405 
    406        Use this to paste in, and automatically indent, multiple lines.
    407        (Mnemonic: The `*` is for "multiple lines").
    408 
    409        A `$*` substitution must appear by itself on a line, with optional
    410        preceding indentation (spaces only). The whole line is replaced by the
    411        corresponding keyword argument, indented appropriately.  If the
    412        argument is an empty string, no output is generated, not even a blank
    413        line.
    414    """
    415 
    416    t, argModList = compile_fill_template(template)
    417    # Now apply argModList to args
    418    for name, modified_name, depth in argModList:
    419        if not (args[name] == "" or args[name].endswith("\n")):
    420            raise ValueError(
    421                "Argument %s with value %r is missing a newline" % (name, args[name])
    422            )
    423        args[modified_name] = indent(args[name], depth)
    424 
    425    return t.substitute(args)
    426 
    427 
    428 class CGThing:
    429    """
    430    Abstract base class for things that spit out code.
    431    """
    432 
    433    def __init__(self):
    434        pass  # Nothing for now
    435 
    436    def declare(self):
    437        """Produce code for a header file."""
    438        assert False  # Override me!
    439 
    440    def define(self):
    441        """Produce code for a cpp file."""
    442        assert False  # Override me!
    443 
    444    def forward_declare(self):
    445        """Produce code for a header file."""
    446        return ""  # This can be skipped for most of the classes
    447 
    448    def deps(self):
    449        """Produce the deps for a pp file"""
    450        assert False  # Override me!
    451 
    452 
    453 class CGStringTable(CGThing):
    454    """
    455    Generate a function accessor for a WebIDL string table, using the existing
    456    concatenated names string and mapping indexes to offsets in that string:
    457 
    458    const char *accessorName(unsigned int index) {
    459      static const uint16_t offsets = { ... };
    460      return BindingName(offsets[index]);
    461    }
    462 
    463    This is more efficient than the more natural:
    464 
    465    const char *table[] = {
    466      ...
    467    };
    468 
    469    The uint16_t offsets are smaller than the pointer equivalents, and the
    470    concatenated string requires no runtime relocations.
    471    """
    472 
    473    def __init__(self, accessorName, strings, static=False):
    474        CGThing.__init__(self)
    475        self.accessorName = accessorName
    476        self.strings = strings
    477        self.static = static
    478 
    479    def declare(self):
    480        if self.static:
    481            return ""
    482        return "const char *%s(unsigned int aIndex);\n" % self.accessorName
    483 
    484    def define(self):
    485        offsets = []
    486        for s in self.strings:
    487            offsets.append(BindingNamesOffsetEnum(s))
    488        return fill(
    489            """
    490            ${static}const char *${name}(unsigned int aIndex)
    491            {
    492              static const BindingNamesOffset offsets[] = {
    493                $*{offsets}
    494              };
    495              return BindingName(offsets[aIndex]);
    496            }
    497            """,
    498            static="static " if self.static else "",
    499            name=self.accessorName,
    500            offsets="".join("BindingNamesOffset::%s,\n" % o for o in offsets),
    501        )
    502 
    503 
    504 class CGNativePropertyHooks(CGThing):
    505    """
    506    Generate a NativePropertyHooks for a given descriptor
    507    """
    508 
    509    def __init__(self, descriptor, properties):
    510        CGThing.__init__(self)
    511        assert descriptor.wantsXrays
    512        self.descriptor = descriptor
    513        self.properties = properties
    514 
    515    def declare(self):
    516        return ""
    517 
    518    def define(self):
    519        if (
    520            self.descriptor.concrete
    521            and self.descriptor.proxy
    522            and not self.descriptor.isMaybeCrossOriginObject()
    523        ):
    524            if self.descriptor.needsXrayNamedDeleterHook():
    525                deleteNamedProperty = "DeleteNamedProperty"
    526            else:
    527                deleteNamedProperty = "nullptr"
    528            namedOrIndexed = fill(
    529                """
    530                const NativeNamedOrIndexedPropertyHooks sNativeNamedOrIndexedPropertyHooks = {
    531                  binding_detail::ResolveOwnProperty,
    532                  binding_detail::EnumerateOwnProperties,
    533                  ${deleteNamedProperty}
    534                };
    535                """,
    536                deleteNamedProperty=deleteNamedProperty,
    537            )
    538            namedOrIndexedPointer = "&sNativeNamedOrIndexedPropertyHooks"
    539        elif self.descriptor.needsXrayResolveHooks():
    540            namedOrIndexed = dedent(
    541                """
    542                const NativeNamedOrIndexedPropertyHooks sNativeNamedOrIndexedPropertyHooks = {
    543                  ResolveOwnPropertyViaResolve,
    544                  EnumerateOwnPropertiesViaGetOwnPropertyNames,
    545                  nullptr
    546                };
    547                """
    548            )
    549            namedOrIndexedPointer = "&sNativeNamedOrIndexedPropertyHooks"
    550        else:
    551            namedOrIndexed = ""
    552            namedOrIndexedPointer = "nullptr"
    553        if self.properties.hasNonChromeOnly():
    554            regular = "sNativeProperties.Upcast()"
    555        else:
    556            regular = "nullptr"
    557        if self.properties.hasChromeOnly():
    558            chrome = "sChromeOnlyNativeProperties.Upcast()"
    559        else:
    560            chrome = "nullptr"
    561        constructorID = "constructors::id::"
    562        if self.descriptor.interface.hasInterfaceObject():
    563            constructorID += self.descriptor.name
    564        else:
    565            constructorID += "_ID_Count"
    566        prototypeID = "prototypes::id::"
    567        if self.descriptor.interface.hasInterfacePrototypeObject():
    568            prototypeID += self.descriptor.name
    569        else:
    570            prototypeID += "_ID_Count"
    571 
    572        if self.descriptor.wantsXrayExpandoClass:
    573            expandoClass = "&sXrayExpandoObjectClass"
    574        else:
    575            expandoClass = "&DefaultXrayExpandoObjectClass"
    576 
    577        return namedOrIndexed + fill(
    578            """
    579            bool sNativePropertiesInited = false;
    580            const NativePropertyHooks sNativePropertyHooks = {
    581              ${namedOrIndexedPointer},
    582              { ${regular}, ${chrome}, &sNativePropertiesInited },
    583              ${prototypeID},
    584              ${constructorID},
    585              ${expandoClass}
    586            };
    587            """,
    588            namedOrIndexedPointer=namedOrIndexedPointer,
    589            regular=regular,
    590            chrome=chrome,
    591            prototypeID=prototypeID,
    592            constructorID=constructorID,
    593            expandoClass=expandoClass,
    594        )
    595 
    596 
    597 def NativePropertyHooks(descriptor):
    598    return (
    599        "&sEmptyNativePropertyHooks"
    600        if not descriptor.wantsXrays
    601        else "&sNativePropertyHooks"
    602    )
    603 
    604 
    605 def DOMClass(descriptor):
    606    protoList = ["prototypes::id::" + proto for proto in descriptor.prototypeNameChain]
    607    # Pad out the list to the right length with _ID_Count so we
    608    # guarantee that all the lists are the same length.  _ID_Count
    609    # is never the ID of any prototype, so it's safe to use as
    610    # padding.
    611    protoList.extend(
    612        ["prototypes::id::_ID_Count"]
    613        * (descriptor.config.maxProtoChainLength - len(protoList))
    614    )
    615 
    616    if descriptor.interface.isSerializable():
    617        serializer = "Serialize"
    618    else:
    619        serializer = "nullptr"
    620 
    621    if wantsGetWrapperCache(descriptor):
    622        wrapperCacheGetter = (
    623            "NativeTypeHelpers<%s>::GetWrapperCache" % descriptor.nativeType
    624        )
    625    else:
    626        wrapperCacheGetter = "nullptr"
    627 
    628    if descriptor.hasOrdinaryObjectPrototype():
    629        getProto = "JS::GetRealmObjectPrototypeHandle"
    630    else:
    631        getProto = "GetProtoObjectHandle"
    632 
    633    return fill(
    634        """
    635          { ${protoChain} },
    636          std::is_base_of_v<nsISupports, ${nativeType}>,
    637          ${hooks},
    638          FindAssociatedGlobalForNative<${nativeType}>::Get,
    639          ${getProto},
    640          GetCCParticipant<${nativeType}>::Get(),
    641          ${serializer},
    642          ${wrapperCacheGetter}
    643        """,
    644        protoChain=", ".join(protoList),
    645        nativeType=descriptor.nativeType,
    646        hooks=NativePropertyHooks(descriptor),
    647        serializer=serializer,
    648        wrapperCacheGetter=wrapperCacheGetter,
    649        getProto=getProto,
    650    )
    651 
    652 
    653 def InstanceReservedSlots(descriptor):
    654    slots = INSTANCE_RESERVED_SLOTS + descriptor.interface.totalMembersInSlots
    655    if descriptor.isMaybeCrossOriginObject():
    656        # We need a slot for the cross-origin holder too.
    657        if descriptor.interface.hasChildInterfaces():
    658            raise TypeError(
    659                "We don't support non-leaf cross-origin interfaces "
    660                "like %s" % descriptor.interface.identifier.name
    661            )
    662        slots += 1
    663    return slots
    664 
    665 
    666 class CGDOMJSClass(CGThing):
    667    """
    668    Generate a DOMJSClass for a given descriptor
    669    """
    670 
    671    def __init__(self, descriptor):
    672        CGThing.__init__(self)
    673        self.descriptor = descriptor
    674 
    675    def declare(self):
    676        return ""
    677 
    678    def define(self):
    679        callHook = (
    680            LEGACYCALLER_HOOK_NAME
    681            if self.descriptor.operations["LegacyCaller"]
    682            else "nullptr"
    683        )
    684        if self.descriptor.wrapperCache:
    685            classExtension = (
    686                "&NativeTypeHelpers<%s>::sClassExtension" % self.descriptor.nativeType
    687            )
    688        else:
    689            classExtension = "JS_NULL_CLASS_EXT"
    690        slotCount = InstanceReservedSlots(self.descriptor)
    691        classFlags = "JSCLASS_IS_DOMJSCLASS | JSCLASS_FOREGROUND_FINALIZE | "
    692        if self.descriptor.isGlobal():
    693            classFlags += (
    694                "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)"
    695            )
    696            traceHook = "JS_GlobalObjectTraceHook"
    697            reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
    698        else:
    699            classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
    700            iface = getReflectedHTMLAttributesIface(self.descriptor)
    701            if iface:
    702                traceHook = (
    703                    "%s::ReflectedHTMLAttributeSlots::Trace"
    704                    % toBindingNamespace(iface.identifier.name)
    705                )
    706            else:
    707                traceHook = "nullptr"
    708            reservedSlots = slotCount
    709        if self.descriptor.interface.hasProbablyShortLivingWrapper():
    710            if not self.descriptor.wrapperCache:
    711                raise TypeError(
    712                    "Need a wrapper cache to support nursery "
    713                    "allocation of DOM objects"
    714                )
    715            classFlags += " | JSCLASS_SKIP_NURSERY_FINALIZE"
    716 
    717        if wantsPreservedWrapper(self.descriptor):
    718            classFlags += " | JSCLASS_PRESERVES_WRAPPER"
    719 
    720        if self.descriptor.interface.getExtendedAttribute("NeedResolve"):
    721            resolveHook = RESOLVE_HOOK_NAME
    722            mayResolveHook = MAY_RESOLVE_HOOK_NAME
    723            newEnumerateHook = NEW_ENUMERATE_HOOK_NAME
    724        elif self.descriptor.isGlobal():
    725            resolveHook = "mozilla::dom::ResolveGlobal"
    726            mayResolveHook = "mozilla::dom::MayResolveGlobal"
    727            newEnumerateHook = "mozilla::dom::EnumerateGlobal"
    728        else:
    729            resolveHook = "nullptr"
    730            mayResolveHook = "nullptr"
    731            newEnumerateHook = "nullptr"
    732 
    733        return fill(
    734            """
    735            static const JSClassOps sClassOps = {
    736              nullptr,               /* addProperty */
    737              nullptr,               /* delProperty */
    738              nullptr,               /* enumerate */
    739              ${newEnumerate}, /* newEnumerate */
    740              ${resolve}, /* resolve */
    741              ${mayResolve}, /* mayResolve */
    742              ${finalize}, /* finalize */
    743              ${call}, /* call */
    744              nullptr,               /* construct */
    745              ${trace}, /* trace */
    746            };
    747 
    748            static const DOMJSClass sClass = {
    749              { "${name}",
    750                ${flags},
    751                &sClassOps,
    752                JS_NULL_CLASS_SPEC,
    753                ${classExtension},
    754                JS_NULL_OBJECT_OPS
    755              },
    756              $*{descriptor}
    757            };
    758            static_assert(${instanceReservedSlots} == DOM_INSTANCE_RESERVED_SLOTS,
    759                          "Must have the right minimal number of reserved slots.");
    760            static_assert(${reservedSlots} >= ${slotCount},
    761                          "Must have enough reserved slots.");
    762            """,
    763            name=self.descriptor.interface.getClassName(),
    764            flags=classFlags,
    765            newEnumerate=newEnumerateHook,
    766            resolve=resolveHook,
    767            mayResolve=mayResolveHook,
    768            finalize=FINALIZE_HOOK_NAME,
    769            call=callHook,
    770            trace=traceHook,
    771            classExtension=classExtension,
    772            descriptor=DOMClass(self.descriptor),
    773            instanceReservedSlots=INSTANCE_RESERVED_SLOTS,
    774            reservedSlots=reservedSlots,
    775            slotCount=slotCount,
    776        )
    777 
    778 
    779 class CGDOMProxyJSClass(CGThing):
    780    """
    781    Generate a DOMJSClass for a given proxy descriptor
    782    """
    783 
    784    def __init__(self, descriptor):
    785        CGThing.__init__(self)
    786        self.descriptor = descriptor
    787 
    788    def declare(self):
    789        return ""
    790 
    791    def define(self):
    792        slotCount = InstanceReservedSlots(self.descriptor)
    793        # We need one reserved slot (DOM_OBJECT_SLOT).
    794        flags = ["JSCLASS_IS_DOMJSCLASS", "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount]
    795        # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
    796        # we don't want people ever adding that to any interface other than
    797        # HTMLAllCollection.  So just hardcode it here.
    798        if self.descriptor.interface.identifier.name == "HTMLAllCollection":
    799            flags.append("JSCLASS_EMULATES_UNDEFINED")
    800        return fill(
    801            """
    802            static const DOMJSClass sClass = {
    803              PROXY_CLASS_DEF("${name}",
    804                              ${flags}),
    805              $*{descriptor}
    806            };
    807            """,
    808            name=self.descriptor.interface.identifier.name,
    809            flags=" | ".join(flags),
    810            descriptor=DOMClass(self.descriptor),
    811        )
    812 
    813 
    814 class CGXrayExpandoJSClass(CGThing):
    815    """
    816    Generate a JSClass for an Xray expando object.  This is only
    817    needed if we have members in slots (for [Cached] or [StoreInSlot]
    818    stuff).
    819    """
    820 
    821    def __init__(self, descriptor):
    822        assert descriptor.interface.totalMembersInSlots != 0
    823        assert descriptor.wantsXrays
    824        assert descriptor.wantsXrayExpandoClass
    825        CGThing.__init__(self)
    826        self.descriptor = descriptor
    827 
    828    def declare(self):
    829        return ""
    830 
    831    def define(self):
    832        iface = getReflectedHTMLAttributesIface(self.descriptor)
    833        if iface:
    834            ops = (
    835                "&%s::ReflectedHTMLAttributeSlots::sXrayExpandoObjectClassOps"
    836                % toBindingNamespace(iface.identifier.name)
    837            )
    838        else:
    839            ops = "&xpc::XrayExpandoObjectClassOps"
    840        return fill(
    841            """
    842            // This may allocate too many slots, because we only really need
    843            // slots for our non-interface-typed members that we cache.  But
    844            // allocating slots only for those would make the slot index
    845            // computations much more complicated, so let's do this the simple
    846            // way for now.
    847            DEFINE_XRAY_EXPANDO_CLASS_WITH_OPS(static, sXrayExpandoObjectClass, ${memberSlots},
    848                                               ${ops});
    849            """,
    850            memberSlots=self.descriptor.interface.totalMembersInSlots,
    851            ops=ops,
    852        )
    853 
    854 
    855 def PrototypeIDAndDepth(descriptor):
    856    prototypeID = "prototypes::id::"
    857    if descriptor.interface.hasInterfacePrototypeObject():
    858        prototypeID += descriptor.interface.identifier.name
    859        depth = "PrototypeTraits<%s>::Depth" % prototypeID
    860    else:
    861        prototypeID += "_ID_Count"
    862        depth = "0"
    863    return (prototypeID, depth)
    864 
    865 
    866 def InterfacePrototypeObjectProtoGetter(descriptor):
    867    """
    868    Returns a tuple with two elements:
    869 
    870        1) The name of the function to call to get the prototype to use for the
    871           interface prototype object as a JSObject*.
    872 
    873        2) The name of the function to call to get the prototype to use for the
    874           interface prototype object as a JS::Handle<JSObject*> or None if no
    875           such function exists.
    876    """
    877    parentProtoName = descriptor.parentPrototypeName
    878    if descriptor.hasNamedPropertiesObject:
    879        protoGetter = "GetNamedPropertiesObject"
    880        protoHandleGetter = None
    881    elif parentProtoName is None:
    882        protoHandleGetter = None
    883        if descriptor.interface.getExtendedAttribute("ExceptionClass"):
    884            protoGetter = "JS::GetRealmErrorPrototype"
    885        elif descriptor.interface.isIteratorInterface():
    886            protoGetter = "JS::GetRealmIteratorPrototype"
    887        elif descriptor.interface.isAsyncIteratorInterface():
    888            protoGetter = "JS::GetRealmAsyncIteratorPrototype"
    889        else:
    890            protoGetter = "JS::GetRealmObjectPrototype"
    891            protoHandleGetter = "JS::GetRealmObjectPrototypeHandle"
    892    else:
    893        prefix = toBindingNamespace(parentProtoName)
    894        protoGetter = prefix + "::GetProtoObject"
    895        protoHandleGetter = prefix + "::GetProtoObjectHandle"
    896 
    897    return (protoGetter, protoHandleGetter)
    898 
    899 
    900 class CGPrototypeJSClass(CGThing):
    901    def __init__(self, descriptor, properties):
    902        CGThing.__init__(self)
    903        self.descriptor = descriptor
    904        self.properties = properties
    905 
    906    def declare(self):
    907        # We're purely for internal consumption
    908        return ""
    909 
    910    def define(self):
    911        prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
    912        slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
    913        # Globals handle unforgeables directly in Wrap() instead of
    914        # via a holder.
    915        if (
    916            self.descriptor.hasLegacyUnforgeableMembers
    917            and not self.descriptor.isGlobal()
    918        ):
    919            slotCount += (
    920                " + 1 /* slot for the JSObject holding the unforgeable properties */"
    921            )
    922        (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor)
    923        type = (
    924            "eGlobalInterfacePrototype"
    925            if self.descriptor.isGlobal()
    926            else "eInterfacePrototype"
    927        )
    928        return fill(
    929            """
    930            static const DOMIfaceAndProtoJSClass sPrototypeClass = {
    931              {
    932                "${name}Prototype",
    933                JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
    934                JS_NULL_CLASS_OPS,
    935                JS_NULL_CLASS_SPEC,
    936                JS_NULL_CLASS_EXT,
    937                JS_NULL_OBJECT_OPS
    938              },
    939              ${type},
    940              ${prototypeID},
    941              ${depth},
    942              ${hooks},
    943              ${protoGetter}
    944            };
    945            """,
    946            name=self.descriptor.interface.getClassName(),
    947            slotCount=slotCount,
    948            type=type,
    949            hooks=NativePropertyHooks(self.descriptor),
    950            prototypeID=prototypeID,
    951            depth=depth,
    952            protoGetter=protoGetter,
    953        )
    954 
    955 
    956 def InterfaceObjectProtoGetter(descriptor):
    957    """
    958    Returns the name of the function to call to get the prototype to use for the
    959    interface object's prototype as a JS::Handle<JSObject*>.
    960    """
    961    assert not descriptor.interface.isNamespace()
    962    parentInterface = descriptor.interface.parent
    963    if parentInterface:
    964        parentIfaceName = parentInterface.identifier.name
    965        parentDesc = descriptor.getDescriptor(parentIfaceName)
    966        prefix = toBindingNamespace(parentDesc.name)
    967        protoHandleGetter = prefix + "::GetConstructorObjectHandle"
    968    else:
    969        protoHandleGetter = "JS::GetRealmFunctionPrototypeHandle"
    970    return protoHandleGetter
    971 
    972 
    973 class CGNamespaceObjectJSClass(CGThing):
    974    def __init__(self, descriptor):
    975        CGThing.__init__(self)
    976        self.descriptor = descriptor
    977 
    978    def declare(self):
    979        # We're purely for internal consumption
    980        return ""
    981 
    982    def define(self):
    983        classString = self.descriptor.interface.getExtendedAttribute("ClassString")
    984        if classString is None:
    985            classString = self.descriptor.interface.identifier.name
    986        else:
    987            classString = classString[0]
    988        return fill(
    989            """
    990            static const DOMIfaceAndProtoJSClass sNamespaceObjectClass = {
    991              {
    992                "${classString}",
    993                JSCLASS_IS_DOMIFACEANDPROTOJSCLASS,
    994                JS_NULL_CLASS_OPS,
    995                JS_NULL_CLASS_SPEC,
    996                JS_NULL_CLASS_EXT,
    997                JS_NULL_OBJECT_OPS
    998              },
    999              eNamespace,
   1000              prototypes::id::_ID_Count,
   1001              0,
   1002              ${hooks},
   1003              // This isn't strictly following the spec (see
   1004              // https://console.spec.whatwg.org/#ref-for-dfn-namespace-object),
   1005              // but should be ok for Xrays.
   1006              JS::GetRealmObjectPrototype
   1007            };
   1008            """,
   1009            classString=classString,
   1010            hooks=NativePropertyHooks(self.descriptor),
   1011        )
   1012 
   1013 
   1014 class CGInterfaceObjectInfo(CGThing):
   1015    def __init__(self, descriptor):
   1016        CGThing.__init__(self)
   1017        self.descriptor = descriptor
   1018 
   1019    def declare(self):
   1020        # We're purely for internal consumption
   1021        return ""
   1022 
   1023    def define(self):
   1024        if self.descriptor.interface.ctor():
   1025            ctorname = CONSTRUCT_HOOK_NAME
   1026            constructorArgs = methodLength(self.descriptor.interface.ctor())
   1027        else:
   1028            ctorname = "ThrowingConstructor"
   1029            constructorArgs = 0
   1030        constructorName = self.descriptor.interface.getClassName()
   1031        wantsIsInstance = self.descriptor.interface.hasInterfacePrototypeObject()
   1032 
   1033        prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
   1034        protoHandleGetter = InterfaceObjectProtoGetter(self.descriptor)
   1035 
   1036        return fill(
   1037            """
   1038            static const DOMInterfaceInfo sInterfaceObjectInfo = {
   1039              { ${ctorname}, ${hooks} },
   1040              ${protoHandleGetter},
   1041              ${depth},
   1042              ${prototypeID},
   1043              ${wantsIsInstance},
   1044              ${constructorArgs},
   1045              "${constructorName}",
   1046            };
   1047            """,
   1048            ctorname=ctorname,
   1049            hooks=NativePropertyHooks(self.descriptor),
   1050            protoHandleGetter=protoHandleGetter,
   1051            depth=depth,
   1052            prototypeID=prototypeID,
   1053            wantsIsInstance=toStringBool(wantsIsInstance),
   1054            constructorArgs=constructorArgs,
   1055            constructorName=constructorName,
   1056        )
   1057 
   1058 
   1059 class CGList(CGThing):
   1060    """
   1061    Generate code for a list of GCThings.  Just concatenates them together, with
   1062    an optional joiner string.  "\n" is a common joiner.
   1063    """
   1064 
   1065    def __init__(self, children, joiner=""):
   1066        CGThing.__init__(self)
   1067        # Make a copy of the kids into a list, because if someone passes in a
   1068        # generator we won't be able to both declare and define ourselves, or
   1069        # define ourselves more than once!
   1070        self.children = list(children)
   1071        self.joiner = joiner
   1072 
   1073    def append(self, child):
   1074        self.children.append(child)
   1075 
   1076    def prepend(self, child):
   1077        self.children.insert(0, child)
   1078 
   1079    def extend(self, kids):
   1080        self.children.extend(kids)
   1081 
   1082    def join(self, iterable):
   1083        return self.joiner.join(s for s in iterable if len(s) > 0)
   1084 
   1085    def declare(self):
   1086        return self.join(
   1087            child.declare() for child in self.children if child is not None
   1088        )
   1089 
   1090    def define(self):
   1091        return self.join(child.define() for child in self.children if child is not None)
   1092 
   1093    def forward_declare(self):
   1094        return self.join(
   1095            child.forward_declare() for child in self.children if child is not None
   1096        )
   1097 
   1098    def deps(self):
   1099        deps = set()
   1100        for child in self.children:
   1101            if child is None:
   1102                continue
   1103            deps = deps.union(child.deps())
   1104        return deps
   1105 
   1106    def __len__(self):
   1107        return len(self.children)
   1108 
   1109 
   1110 class CGGeneric(CGThing):
   1111    """
   1112    A class that spits out a fixed string into the codegen.  Can spit out a
   1113    separate string for the declaration too.
   1114    """
   1115 
   1116    def __init__(self, define="", declare="", forward_declare=""):
   1117        self.declareText = declare
   1118        self.defineText = define
   1119        self.forwardDeclareText = forward_declare
   1120 
   1121    def declare(self):
   1122        return self.declareText
   1123 
   1124    def define(self):
   1125        return self.defineText
   1126 
   1127    def forward_declare(self):
   1128        return self.forwardDeclareText
   1129 
   1130    def deps(self):
   1131        return set()
   1132 
   1133 
   1134 class CGIndenter(CGThing):
   1135    """
   1136    A class that takes another CGThing and generates code that indents that
   1137    CGThing by some number of spaces.  The default indent is two spaces.
   1138    """
   1139 
   1140    def __init__(self, child, indentLevel=2, declareOnly=False):
   1141        assert isinstance(child, CGThing)
   1142        CGThing.__init__(self)
   1143        self.child = child
   1144        self.indentLevel = indentLevel
   1145        self.declareOnly = declareOnly
   1146 
   1147    def declare(self):
   1148        return indent(self.child.declare(), self.indentLevel)
   1149 
   1150    def define(self):
   1151        defn = self.child.define()
   1152        if self.declareOnly:
   1153            return defn
   1154        else:
   1155            return indent(defn, self.indentLevel)
   1156 
   1157 
   1158 class CGWrapper(CGThing):
   1159    """
   1160    Generic CGThing that wraps other CGThings with pre and post text.
   1161    """
   1162 
   1163    def __init__(
   1164        self,
   1165        child: CGThing,
   1166        pre="",
   1167        post="",
   1168        declarePre=None,
   1169        declarePost=None,
   1170        definePre=None,
   1171        definePost=None,
   1172        forwardDeclarePre=None,
   1173        forwardDeclarePost=None,
   1174        declareOnly=False,
   1175        defineOnly=False,
   1176        reindent=False,
   1177    ):
   1178        CGThing.__init__(self)
   1179        self.child = child
   1180        self.declarePre = declarePre or pre
   1181        self.declarePost = declarePost or post
   1182        self.definePre = definePre or pre
   1183        self.definePost = definePost or post
   1184        self.forwardDeclarePre = forwardDeclarePre or pre
   1185        self.forwardDeclarePost = forwardDeclarePost or post
   1186        self.declareOnly = declareOnly
   1187        self.defineOnly = defineOnly
   1188        self.reindent = reindent
   1189 
   1190    def declare(self):
   1191        if self.defineOnly:
   1192            return ""
   1193        decl = self.child.declare()
   1194        if self.reindent:
   1195            decl = self.reindentString(decl, self.declarePre)
   1196        return self.declarePre + decl + self.declarePost
   1197 
   1198    def define(self):
   1199        if self.declareOnly:
   1200            return ""
   1201        defn = self.child.define()
   1202        if self.reindent:
   1203            defn = self.reindentString(defn, self.definePre)
   1204        return self.definePre + defn + self.definePost
   1205 
   1206    def forward_declare(self):
   1207        if self.defineOnly:
   1208            return ""
   1209        decl = self.child.forward_declare()
   1210        if self.reindent:
   1211            decl = self.reindentString(decl, self.forwardDeclarePre)
   1212        return self.forwardDeclarePre + decl + self.forwardDeclarePost
   1213 
   1214    @staticmethod
   1215    def reindentString(stringToIndent, widthString):
   1216        # We don't use lineStartDetector because we don't want to
   1217        # insert whitespace at the beginning of our _first_ line.
   1218        # Use the length of the last line of width string, in case
   1219        # it is a multiline string.
   1220        lastLineWidth = len(widthString.splitlines()[-1])
   1221        return stripTrailingWhitespace(
   1222            stringToIndent.replace("\n", "\n" + (" " * lastLineWidth))
   1223        )
   1224 
   1225    def deps(self):
   1226        return self.child.deps()
   1227 
   1228 
   1229 class CGIfWrapper(CGList):
   1230    def __init__(self, child, condition):
   1231        CGList.__init__(
   1232            self,
   1233            [
   1234                CGWrapper(
   1235                    CGGeneric(condition), pre="if (", post=") {\n", reindent=True
   1236                ),
   1237                CGIndenter(child),
   1238                CGGeneric("}\n"),
   1239            ],
   1240        )
   1241 
   1242 
   1243 class CGIfElseWrapper(CGList):
   1244    def __init__(self, condition, ifTrue, ifFalse):
   1245        CGList.__init__(
   1246            self,
   1247            [
   1248                CGWrapper(
   1249                    CGGeneric(condition), pre="if (", post=") {\n", reindent=True
   1250                ),
   1251                CGIndenter(ifTrue),
   1252                CGGeneric("} else {\n"),
   1253                CGIndenter(ifFalse),
   1254                CGGeneric("}\n"),
   1255            ],
   1256        )
   1257 
   1258 
   1259 class CGElseChain(CGThing):
   1260    """
   1261    Concatenate if statements in an if-else-if-else chain.
   1262    """
   1263 
   1264    def __init__(self, children):
   1265        self.children = [c for c in children if c is not None]
   1266 
   1267    def declare(self):
   1268        assert False
   1269 
   1270    def define(self):
   1271        if not self.children:
   1272            return ""
   1273        s = self.children[0].define()
   1274        assert s.endswith("\n")
   1275        for child in self.children[1:]:
   1276            code = child.define()
   1277            assert code.startswith("if") or code.startswith("{")
   1278            assert code.endswith("\n")
   1279            s = s.rstrip() + " else " + code
   1280        return s
   1281 
   1282 
   1283 class CGTemplatedType(CGWrapper):
   1284    def __init__(self, templateName, child, isConst=False, isReference=False):
   1285        if isinstance(child, list):
   1286            child = CGList(child, ", ")
   1287        const = "const " if isConst else ""
   1288        pre = "%s%s<" % (const, templateName)
   1289        ref = "&" if isReference else ""
   1290        post = ">%s" % ref
   1291        CGWrapper.__init__(self, child, pre=pre, post=post)
   1292 
   1293 
   1294 class CGNamespace(CGThing):
   1295    """
   1296    Generates namespace block that wraps other CGThings.
   1297    """
   1298 
   1299    def __init__(self, namespace, child):
   1300        CGThing.__init__(self)
   1301        self.child = child
   1302        self.pre = "namespace %s {\n" % namespace
   1303        self.post = "} // namespace %s\n" % namespace
   1304 
   1305    def declare(self):
   1306        decl = self.child.declare()
   1307        if len(decl.strip()) == 0:
   1308            return ""
   1309        return self.pre + decl + self.post
   1310 
   1311    def define(self):
   1312        defn = self.child.define()
   1313        if len(defn.strip()) == 0:
   1314            return ""
   1315        return self.pre + defn + self.post
   1316 
   1317    def forward_declare(self):
   1318        decl = self.child.forward_declare()
   1319        if len(decl.strip()) == 0:
   1320            return ""
   1321        return self.pre + decl + self.post
   1322 
   1323    def deps(self):
   1324        return self.child.deps()
   1325 
   1326    @staticmethod
   1327    def build(namespaces, child):
   1328        """
   1329        Static helper method to build multiple wrapped namespaces.
   1330        """
   1331        if not namespaces:
   1332            return CGWrapper(child)
   1333        return CGNamespace("::".join(namespaces), child)
   1334 
   1335 
   1336 class CGIncludeGuard(CGWrapper):
   1337    """
   1338    Generates include guards for a header.
   1339    """
   1340 
   1341    def __init__(self, prefix, child):
   1342        """|prefix| is the filename without the extension."""
   1343        define = "DOM_%s_H_" % prefix.upper()
   1344        forward_define = "DOM_%sFWD_H_" % prefix.upper()
   1345        CGWrapper.__init__(
   1346            self,
   1347            child,
   1348            declarePre="#ifndef %s\n#define %s\n\n" % (define, define),
   1349            declarePost="\n#endif // %s\n" % define,
   1350            forwardDeclarePre="#ifndef %s\n#define %s\n\n"
   1351            % (forward_define, forward_define),
   1352            forwardDeclarePost="\n#endif // %s\n" % forward_define,
   1353        )
   1354 
   1355 
   1356 class CGHeaders(CGWrapper):
   1357    """
   1358    Generates the appropriate include statements.
   1359    """
   1360 
   1361    def __init__(
   1362        self,
   1363        descriptors,
   1364        dictionaries,
   1365        callbacks,
   1366        callbackDescriptors,
   1367        declareIncludes,
   1368        defineIncludes,
   1369        prefix,
   1370        child,
   1371        config=None,
   1372        jsImplementedDescriptors=[],
   1373    ):
   1374        """
   1375        Builds a set of includes to cover |descriptors|.
   1376 
   1377        Also includes the files in |declareIncludes| in the header
   1378        file and the files in |defineIncludes| in the .cpp.
   1379 
   1380        |prefix| contains the basename of the file that we generate include
   1381        statements for.
   1382        """
   1383 
   1384        # Determine the filenames for which we need headers.
   1385        interfaceDeps = [d.interface for d in descriptors]
   1386        ancestors = []
   1387        for iface in interfaceDeps:
   1388            if iface.parent:
   1389                # We're going to need our parent's prototype, to use as the
   1390                # prototype of our prototype object.
   1391                ancestors.append(iface.parent)
   1392                # And if we have an interface object, we'll need the nearest
   1393                # ancestor with an interface object too, so we can use its
   1394                # interface object as the proto of our interface object.
   1395                if iface.hasInterfaceObject():
   1396                    parent = iface.parent
   1397                    while parent and not parent.hasInterfaceObject():
   1398                        parent = parent.parent
   1399                    if parent:
   1400                        ancestors.append(parent)
   1401        interfaceDeps.extend(ancestors)
   1402 
   1403        # Include parent interface headers needed for default toJSON code.
   1404        jsonInterfaceParents = []
   1405        for desc in descriptors:
   1406            if not desc.hasDefaultToJSON:
   1407                continue
   1408            parent = desc.interface.parent
   1409            while parent:
   1410                parentDesc = desc.getDescriptor(parent.identifier.name)
   1411                if parentDesc.hasDefaultToJSON:
   1412                    jsonInterfaceParents.append(parentDesc.interface)
   1413                parent = parent.parent
   1414        interfaceDeps.extend(jsonInterfaceParents)
   1415 
   1416        bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)
   1417 
   1418        # Grab all the implementation declaration files we need.
   1419        implementationIncludes = set(
   1420            d.headerFile for d in descriptors if d.needsHeaderInclude()
   1421        )
   1422 
   1423        # Now find all the things we'll need as arguments because we
   1424        # need to wrap or unwrap them.
   1425        bindingHeaders = set()
   1426        declareIncludes = set(declareIncludes)
   1427 
   1428        def addHeadersForType(typeAndPossibleOriginType):
   1429            """
   1430            Add the relevant headers for this type.  We use its origin type, if
   1431            passed, to decide what to do with interface types.
   1432            """
   1433            t, originType = typeAndPossibleOriginType
   1434            isFromDictionary = originType and originType.isDictionary()
   1435            isFromCallback = originType and originType.isCallback()
   1436            # Dictionaries have members that need to be actually
   1437            # declared, not just forward-declared.
   1438            # Callbacks have nullable union arguments that need to be actually
   1439            # declared, not just forward-declared.
   1440            if isFromDictionary:
   1441                headerSet = declareIncludes
   1442            elif isFromCallback and t.nullable() and t.isUnion():
   1443                headerSet = declareIncludes
   1444            else:
   1445                headerSet = bindingHeaders
   1446            # Strip off outer layers and add headers they might require.  (This
   1447            # is conservative: only nullable non-pointer types need Nullable.h;
   1448            # only sequences or observable arrays outside unions need
   1449            # ForOfIterator.h; only functions that return, and attributes that
   1450            # are, sequences or observable arrays in interfaces need Array.h, &c.)
   1451            unrolled = t
   1452            while True:
   1453                if idlTypeNeedsCallContext(unrolled):
   1454                    bindingHeaders.add("mozilla/dom/BindingCallContext.h")
   1455                if unrolled.nullable():
   1456                    headerSet.add("mozilla/dom/Nullable.h")
   1457                elif unrolled.isSequence() or unrolled.isObservableArray():
   1458                    bindingHeaders.add("js/Array.h")
   1459                    bindingHeaders.add("js/ForOfIterator.h")
   1460                    if unrolled.isObservableArray():
   1461                        bindingHeaders.add("mozilla/dom/ObservableArrayProxyHandler.h")
   1462                else:
   1463                    break
   1464                unrolled = unrolled.inner
   1465            if unrolled.isUnion():
   1466                headerSet.add(self.getUnionDeclarationFilename(config, unrolled))
   1467                for t in unrolled.flatMemberTypes:
   1468                    addHeadersForType((t, None))
   1469            elif unrolled.isPromise():
   1470                # See comment in the isInterface() case for why we add
   1471                # Promise.h to headerSet, not bindingHeaders.
   1472                headerSet.add("mozilla/dom/Promise.h")
   1473                # We need ToJSValue to do the Promise to JS conversion.
   1474                bindingHeaders.add("mozilla/dom/ToJSValue.h")
   1475            elif unrolled.isInterface():
   1476                if unrolled.isSpiderMonkeyInterface():
   1477                    bindingHeaders.add("jsfriendapi.h")
   1478                    if jsImplementedDescriptors:
   1479                        # Since we can't forward-declare typed array types
   1480                        # (because they're typedefs), we have to go ahead and
   1481                        # just include their header if we need to have functions
   1482                        # taking references to them declared in that header.
   1483                        headerSet = declareIncludes
   1484                    headerSet.add("mozilla/dom/TypedArray.h")
   1485                else:
   1486                    try:
   1487                        typeDesc = config.getDescriptor(unrolled.inner.identifier.name)
   1488                    except NoSuchDescriptorError:
   1489                        return
   1490                    # Dictionaries with interface members rely on the
   1491                    # actual class definition of that interface member
   1492                    # being visible in the binding header, because they
   1493                    # store them in RefPtr and have inline
   1494                    # constructors/destructors.
   1495                    #
   1496                    # XXXbz maybe dictionaries with interface members
   1497                    # should just have out-of-line constructors and
   1498                    # destructors?
   1499                    headerSet.add(typeDesc.headerFile)
   1500            elif unrolled.isDictionary():
   1501                headerSet.add(self.getDeclarationFilename(unrolled.inner))
   1502                # And if it needs rooting, we need RootedDictionary too
   1503                if typeNeedsRooting(unrolled):
   1504                    headerSet.add("mozilla/dom/RootedDictionary.h")
   1505            elif unrolled.isCallback():
   1506                headerSet.add(self.getDeclarationFilename(unrolled.callback))
   1507            elif unrolled.isFloat() and not unrolled.isUnrestricted():
   1508                # Restricted floats are tested for finiteness
   1509                bindingHeaders.add("mozilla/FloatingPoint.h")
   1510                bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
   1511            elif unrolled.isEnum():
   1512                filename = self.getDeclarationFilename(unrolled.inner)
   1513                declareIncludes.add(filename)
   1514            elif unrolled.isPrimitive():
   1515                bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
   1516            elif unrolled.isRecord():
   1517                if isFromDictionary or jsImplementedDescriptors:
   1518                    declareIncludes.add("mozilla/dom/Record.h")
   1519                else:
   1520                    bindingHeaders.add("mozilla/dom/Record.h")
   1521                # Also add headers for the type the record is
   1522                # parametrized over, if needed.
   1523                addHeadersForType((t.inner, originType if isFromDictionary else None))
   1524 
   1525        for t in getAllTypes(
   1526            descriptors + callbackDescriptors, dictionaries, callbacks
   1527        ):
   1528            addHeadersForType(t)
   1529 
   1530        def addHeaderForFunc(func, desc):
   1531            if func is None:
   1532                return
   1533            # Include the right class header, which we can only do
   1534            # if this is a class member function.
   1535            if desc is not None and not desc.headerIsDefault:
   1536                # An explicit header file was provided, assume that we know
   1537                # what we're doing.
   1538                return
   1539 
   1540            if "::" in func:
   1541                # Strip out the function name and convert "::" to "/"
   1542                bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
   1543 
   1544        # Now for non-callback descriptors make sure we include any
   1545        # headers needed by Func declarations and other things like that.
   1546        for desc in descriptors:
   1547            # If this is an iterator or an async iterator interface generated
   1548            # for a separate iterable interface, skip generating type includes,
   1549            # as we have what we need in IterableIterator.h
   1550            if (
   1551                desc.interface.isIteratorInterface()
   1552                or desc.interface.isAsyncIteratorInterface()
   1553            ):
   1554                continue
   1555 
   1556            for m in desc.interface.members:
   1557                addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), desc)
   1558                staticTypeOverride = PropertyDefiner.getStringAttr(
   1559                    m, "StaticClassOverride"
   1560                )
   1561                if staticTypeOverride:
   1562                    bindingHeaders.add("/".join(staticTypeOverride.split("::")) + ".h")
   1563            # getExtendedAttribute() returns a list, extract the entry.
   1564            funcList = desc.interface.getExtendedAttribute("Func")
   1565            if funcList is not None:
   1566                addHeaderForFunc(funcList[0], desc)
   1567 
   1568            if desc.interface.maplikeOrSetlikeOrIterable:
   1569                # We need ToJSValue.h for maplike/setlike type conversions
   1570                bindingHeaders.add("mozilla/dom/ToJSValue.h")
   1571                # Add headers for the key and value types of the
   1572                # maplike/setlike/iterable, since they'll be needed for
   1573                # convenience functions
   1574                if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType():
   1575                    addHeadersForType(
   1576                        (desc.interface.maplikeOrSetlikeOrIterable.keyType, None)
   1577                    )
   1578                if desc.interface.maplikeOrSetlikeOrIterable.hasValueType():
   1579                    addHeadersForType(
   1580                        (desc.interface.maplikeOrSetlikeOrIterable.valueType, None)
   1581                    )
   1582 
   1583        for d in dictionaries:
   1584            if d.parent:
   1585                declareIncludes.add(self.getDeclarationFilename(d.parent))
   1586            bindingHeaders.add(self.getDeclarationFilename(d))
   1587            for m in d.members:
   1588                addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), None)
   1589            # No need to worry about Func on members of ancestors, because that
   1590            # will happen automatically in whatever files those ancestors live
   1591            # in.
   1592 
   1593        for c in callbacks:
   1594            bindingHeaders.add(self.getDeclarationFilename(c))
   1595 
   1596        for c in callbackDescriptors:
   1597            bindingHeaders.add(self.getDeclarationFilename(c.interface))
   1598 
   1599        if len(callbacks) != 0:
   1600            # We need CallbackFunction to serve as our parent class
   1601            declareIncludes.add("mozilla/dom/CallbackFunction.h")
   1602            # And we need ToJSValue.h so we can wrap "this" objects
   1603            declareIncludes.add("mozilla/dom/ToJSValue.h")
   1604 
   1605        if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0:
   1606            # We need CallbackInterface to serve as our parent class
   1607            declareIncludes.add("mozilla/dom/CallbackInterface.h")
   1608            # And we need ToJSValue.h so we can wrap "this" objects
   1609            declareIncludes.add("mozilla/dom/ToJSValue.h")
   1610 
   1611        # Also need to include the headers for ancestors of
   1612        # JS-implemented interfaces.
   1613        for jsImplemented in jsImplementedDescriptors:
   1614            jsParent = jsImplemented.interface.parent
   1615            if jsParent:
   1616                parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name)
   1617                declareIncludes.add(parentDesc.jsImplParentHeader)
   1618 
   1619        # Now make sure we're not trying to include the header from inside itself
   1620        declareIncludes.discard(prefix + ".h")
   1621 
   1622        # Let the machinery do its thing.
   1623        def _includeString(includes):
   1624            def headerName(include):
   1625                # System headers are specified inside angle brackets.
   1626                if include.startswith("<"):
   1627                    return include
   1628                # Non-system headers need to be placed in quotes.
   1629                return '"%s"' % include
   1630 
   1631            return "".join(["#include %s\n" % headerName(i) for i in includes]) + "\n"
   1632 
   1633        CGWrapper.__init__(
   1634            self,
   1635            child,
   1636            declarePre=_includeString(sorted(declareIncludes)),
   1637            definePre=_includeString(
   1638                sorted(
   1639                    set(defineIncludes)
   1640                    | bindingIncludes
   1641                    | bindingHeaders
   1642                    | implementationIncludes
   1643                )
   1644            ),
   1645        )
   1646 
   1647    @staticmethod
   1648    def getDeclarationFilename(decl):
   1649        # Use our local version of the header, not the exported one, so that
   1650        # test bindings, which don't export, will work correctly.
   1651        basename = os.path.basename(decl.filename)
   1652        return basename.replace(".webidl", "Binding.h")
   1653 
   1654    @staticmethod
   1655    def getUnionDeclarationFilename(config, unionType):
   1656        assert unionType.isUnion()
   1657        assert unionType.unroll() == unionType
   1658        # If a union is "defined" in multiple files, it goes in UnionTypes.h.
   1659        if len(config.filenamesPerUnion[unionType.name]) > 1:
   1660            return "mozilla/dom/UnionTypes.h"
   1661        # If a union is defined by a built-in typedef, it also goes in
   1662        # UnionTypes.h.
   1663        assert len(config.filenamesPerUnion[unionType.name]) == 1
   1664        if "<unknown>" in config.filenamesPerUnion[unionType.name]:
   1665            return "mozilla/dom/UnionTypes.h"
   1666        return CGHeaders.getDeclarationFilename(unionType)
   1667 
   1668 
   1669 def SortedDictValues(d):
   1670    """
   1671    Returns a list of values from the dict sorted by key.
   1672    """
   1673    return [v for k, v in sorted(d.items())]
   1674 
   1675 
   1676 def UnionsForFile(config, webIDLFile):
   1677    """
   1678    Returns a list of union types for all union types that are only used in
   1679    webIDLFile. If webIDLFile is None this will return the list of tuples for
   1680    union types that are used in more than one WebIDL file.
   1681    """
   1682    return config.unionsPerFilename.get(webIDLFile, [])
   1683 
   1684 
   1685 def UnionTypes(unionTypes, config):
   1686    """
   1687    The unionTypes argument should be a list of union types. This is typically
   1688    the list generated by UnionsForFile.
   1689 
   1690    Returns a tuple containing a set of header filenames to include in
   1691    the header for the types in unionTypes, a set of header filenames to
   1692    include in the implementation file for the types in unionTypes, a set
   1693    of tuples containing a type declaration and a boolean if the type is a
   1694    struct for member types of the union, a list of traverse methods,
   1695    unlink methods and a list of union types. These last three lists only
   1696    contain unique union types.
   1697    """
   1698 
   1699    headers = set()
   1700    implheaders = set()
   1701    declarations = set()
   1702    unionStructs = dict()
   1703    traverseMethods = dict()
   1704    unlinkMethods = dict()
   1705 
   1706    for t in unionTypes:
   1707        name = str(t)
   1708        if name not in unionStructs:
   1709            unionStructs[name] = t
   1710 
   1711            def addHeadersForType(f):
   1712                if f.nullable():
   1713                    headers.add("mozilla/dom/Nullable.h")
   1714                isSequence = f.isSequence()
   1715                if isSequence:
   1716                    # Dealing with sequences requires for-of-compatible
   1717                    # iteration.
   1718                    implheaders.add("js/ForOfIterator.h")
   1719                    # Sequences can always throw "not an object" exceptions.
   1720                    implheaders.add("mozilla/dom/BindingCallContext.h")
   1721                    if typeNeedsRooting(f):
   1722                        headers.add("mozilla/dom/RootedSequence.h")
   1723                f = f.unroll()
   1724                if idlTypeNeedsCallContext(f):
   1725                    implheaders.add("mozilla/dom/BindingCallContext.h")
   1726                if f.isPromise():
   1727                    headers.add("mozilla/dom/Promise.h")
   1728                    # We need ToJSValue to do the Promise to JS conversion.
   1729                    headers.add("mozilla/dom/ToJSValue.h")
   1730                elif f.isInterface():
   1731                    if f.isSpiderMonkeyInterface():
   1732                        headers.add("js/RootingAPI.h")
   1733                        headers.add("js/Value.h")
   1734                        headers.add("mozilla/dom/TypedArray.h")
   1735                    else:
   1736                        try:
   1737                            typeDesc = config.getDescriptor(f.inner.identifier.name)
   1738                        except NoSuchDescriptorError:
   1739                            return
   1740                        if typeDesc.interface.isCallback() or isSequence:
   1741                            # Callback interfaces always use strong refs, so
   1742                            # we need to include the right header to be able
   1743                            # to Release() in our inlined code.
   1744                            #
   1745                            # Similarly, sequences always contain strong
   1746                            # refs, so we'll need the header to handler
   1747                            # those.
   1748                            headers.add(typeDesc.headerFile)
   1749                        elif typeDesc.interface.identifier.name == "WindowProxy":
   1750                            # In UnionTypes.h we need to see the declaration of the
   1751                            # WindowProxyHolder that we use to store the WindowProxy, so
   1752                            # we have its sizeof and know how big to make our union.
   1753                            headers.add(typeDesc.headerFile)
   1754                        else:
   1755                            declarations.add((typeDesc.nativeType, False))
   1756                            implheaders.add(typeDesc.headerFile)
   1757                elif f.isDictionary():
   1758                    # For a dictionary, we need to see its declaration in
   1759                    # UnionTypes.h so we have its sizeof and know how big to
   1760                    # make our union.
   1761                    headers.add(CGHeaders.getDeclarationFilename(f.inner))
   1762                    # And if it needs rooting, we need RootedDictionary too
   1763                    if typeNeedsRooting(f):
   1764                        headers.add("mozilla/dom/RootedDictionary.h")
   1765                elif f.isFloat() and not f.isUnrestricted():
   1766                    # Restricted floats are tested for finiteness
   1767                    implheaders.add("mozilla/FloatingPoint.h")
   1768                    implheaders.add("mozilla/dom/PrimitiveConversions.h")
   1769                elif f.isEnum():
   1770                    # Need to see the actual definition of the enum,
   1771                    # unfortunately.
   1772                    headers.add(CGHeaders.getDeclarationFilename(f.inner))
   1773                elif f.isPrimitive():
   1774                    implheaders.add("mozilla/dom/PrimitiveConversions.h")
   1775                elif f.isCallback():
   1776                    # Callbacks always use strong refs, so we need to include
   1777                    # the right header to be able to Release() in our inlined
   1778                    # code.
   1779                    headers.add(CGHeaders.getDeclarationFilename(f.callback))
   1780                elif f.isRecord():
   1781                    headers.add("mozilla/dom/Record.h")
   1782                    # And add headers for the type we're parametrized over
   1783                    addHeadersForType(f.inner)
   1784                    # And if it needs rooting, we need RootedRecord too
   1785                    if typeNeedsRooting(f):
   1786                        headers.add("mozilla/dom/RootedRecord.h")
   1787 
   1788            implheaders.add(CGHeaders.getUnionDeclarationFilename(config, t))
   1789            for f in t.flatMemberTypes:
   1790                assert not f.nullable()
   1791                addHeadersForType(f)
   1792 
   1793            if idlTypeNeedsCycleCollection(t):
   1794                declarations.add(
   1795                    ("mozilla::dom::%s" % CGUnionStruct.unionTypeName(t, True), False)
   1796                )
   1797                traverseMethods[name] = CGCycleCollectionTraverseForOwningUnionMethod(t)
   1798                unlinkMethods[name] = CGCycleCollectionUnlinkForOwningUnionMethod(t)
   1799 
   1800    # The order of items in CGList is important.
   1801    # Since the union structs friend the unlinkMethods, the forward-declaration
   1802    # for these methods should come before the class declaration. Otherwise
   1803    # some compilers treat the friend declaration as a forward-declaration in
   1804    # the class scope.
   1805    return (
   1806        headers,
   1807        implheaders,
   1808        declarations,
   1809        SortedDictValues(traverseMethods),
   1810        SortedDictValues(unlinkMethods),
   1811        SortedDictValues(unionStructs),
   1812    )
   1813 
   1814 
   1815 class Argument:
   1816    """
   1817    A class for outputting the type and name of an argument
   1818    """
   1819 
   1820    def __init__(self, argType, name, default=None):
   1821        self.argType = argType
   1822        self.name = name
   1823        self.default = default
   1824 
   1825    def declare(self):
   1826        string = self.argType + " " + self.name
   1827        if self.default is not None:
   1828            string += " = " + self.default
   1829        return string
   1830 
   1831    def define(self):
   1832        return self.argType + " " + self.name
   1833 
   1834 
   1835 class CGAbstractMethod(CGThing):
   1836    """
   1837    An abstract class for generating code for a method.  Subclasses
   1838    should override definition_body to create the actual code.
   1839 
   1840    descriptor is the descriptor for the interface the method is associated with
   1841 
   1842    name is the name of the method as a string
   1843 
   1844    returnType is the IDLType of the return value
   1845 
   1846    args is a list of Argument objects
   1847 
   1848    inline should be True to generate an inline method, whose body is
   1849    part of the declaration.
   1850 
   1851    alwaysInline should be True to generate an inline method annotated with
   1852    MOZ_ALWAYS_INLINE.
   1853 
   1854    static should be True to generate a static method, which only has
   1855    a definition.
   1856 
   1857    If templateArgs is not None it should be a list of strings containing
   1858    template arguments, and the function will be templatized using those
   1859    arguments.
   1860 
   1861    canRunScript should be True to generate a MOZ_CAN_RUN_SCRIPT annotation.
   1862 
   1863    signatureOnly should be True to only declare the signature (either in
   1864                  the header, or if static is True in the cpp file).
   1865    """
   1866 
   1867    def __init__(
   1868        self,
   1869        descriptor,
   1870        name,
   1871        returnType,
   1872        args,
   1873        inline=False,
   1874        alwaysInline=False,
   1875        static=False,
   1876        templateArgs=None,
   1877        canRunScript=False,
   1878        signatureOnly=False,
   1879    ):
   1880        CGThing.__init__(self)
   1881        self.descriptor = descriptor
   1882        self.name = name
   1883        self.returnType = returnType
   1884        self.args = args
   1885        self.inline = inline
   1886        self.alwaysInline = alwaysInline
   1887        self.static = static
   1888        self.templateArgs = templateArgs
   1889        self.canRunScript = canRunScript
   1890        self.signatureOnly = signatureOnly
   1891 
   1892    def _argstring(self, declare):
   1893        return ", ".join([a.declare() if declare else a.define() for a in self.args])
   1894 
   1895    def _template(self):
   1896        if self.templateArgs is None:
   1897            return ""
   1898        return "template <%s>\n" % ", ".join(self.templateArgs)
   1899 
   1900    def _decorators(self):
   1901        decorators = []
   1902        if self.canRunScript:
   1903            decorators.append("MOZ_CAN_RUN_SCRIPT")
   1904        if self.alwaysInline:
   1905            decorators.append("MOZ_ALWAYS_INLINE")
   1906        elif self.inline:
   1907            decorators.append("inline")
   1908        if self.static:
   1909            decorators.append("static")
   1910        decorators.append(self.returnType)
   1911        maybeNewline = " " if self.inline else "\n"
   1912        return " ".join(decorators) + maybeNewline
   1913 
   1914    def signature(self):
   1915        return "%s%s%s(%s);\n" % (
   1916            self._template(),
   1917            self._decorators(),
   1918            self.name,
   1919            self._argstring(True),
   1920        )
   1921 
   1922    def declare(self):
   1923        if self.static:
   1924            return ""
   1925        if self.inline:
   1926            return self._define(True)
   1927        return self.signature()
   1928 
   1929    def indent_body(self, body):
   1930        """
   1931        Indent the code returned by self.definition_body(). Most classes
   1932        simply indent everything two spaces. This is here for
   1933        CGRegisterProtos, which needs custom indentation.
   1934        """
   1935        return indent(body)
   1936 
   1937    def _define(self, fromDeclare=False):
   1938        return (
   1939            self.definition_prologue(fromDeclare)
   1940            + self.indent_body(self.definition_body())
   1941            + self.definition_epilogue()
   1942        )
   1943 
   1944    def define(self):
   1945        if self.signatureOnly:
   1946            if self.static:
   1947                # self.static makes us not output anything in the header, so output the signature here.
   1948                return self.signature()
   1949            return ""
   1950        return "" if (self.inline and not self.static) else self._define()
   1951 
   1952    def definition_prologue(self, fromDeclare):
   1953        error_reporting_label = self.error_reporting_label()
   1954        if error_reporting_label:
   1955            # We're going to want a BindingCallContext.  Rename our JSContext*
   1956            # arg accordingly.
   1957            i = 0
   1958            while i < len(self.args):
   1959                arg = self.args[i]
   1960                if arg.argType == "JSContext*":
   1961                    cxname = arg.name
   1962                    self.args[i] = Argument(arg.argType, "cx_", arg.default)
   1963                    break
   1964                i += 1
   1965            if i == len(self.args):
   1966                raise TypeError("Must have a JSContext* to create a BindingCallContext")
   1967 
   1968        prologue = "%s%s%s(%s)\n{\n" % (
   1969            self._template(),
   1970            self._decorators(),
   1971            self.name,
   1972            self._argstring(fromDeclare),
   1973        )
   1974        if error_reporting_label:
   1975            prologue += indent(
   1976                fill(
   1977                    """
   1978                BindingCallContext ${cxname}(cx_, ${label});
   1979                """,
   1980                    cxname=cxname,
   1981                    label=error_reporting_label,
   1982                )
   1983            )
   1984 
   1985        profiler_label = self.auto_profiler_label()
   1986        if profiler_label:
   1987            prologue += indent(profiler_label) + "\n"
   1988 
   1989        return prologue
   1990 
   1991    def definition_epilogue(self):
   1992        return "}\n"
   1993 
   1994    def definition_body(self):
   1995        assert False  # Override me!
   1996 
   1997    """
   1998    Override this method to return a pair of (descriptive string, name of a
   1999    JSContext* variable) in order to generate a profiler label for this method.
   2000    """
   2001 
   2002    def auto_profiler_label(self):
   2003        return None  # Override me!
   2004 
   2005    """
   2006    Override this method to return a string to be used as the label for a
   2007    BindingCallContext.  If this does not return None, one of the arguments of
   2008    this method must be of type 'JSContext*'.  Its name will be replaced with
   2009    'cx_' and a BindingCallContext named 'cx' will be instantiated with the
   2010    given label.
   2011    """
   2012 
   2013    def error_reporting_label(self):
   2014        return None  # Override me!
   2015 
   2016 
   2017 class CGAbstractStaticMethod(CGAbstractMethod):
   2018    """
   2019    Abstract base class for codegen of implementation-only (no
   2020    declaration) static methods.
   2021    """
   2022 
   2023    def __init__(self, descriptor, name, returnType, args, canRunScript=False):
   2024        CGAbstractMethod.__init__(
   2025            self,
   2026            descriptor,
   2027            name,
   2028            returnType,
   2029            args,
   2030            inline=False,
   2031            static=True,
   2032            canRunScript=canRunScript,
   2033        )
   2034 
   2035 
   2036 class CGAbstractClassHook(CGAbstractStaticMethod):
   2037    """
   2038    Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
   2039    'this' unwrapping as it assumes that the unwrapped type is always known.
   2040    """
   2041 
   2042    def __init__(self, descriptor, name, returnType, args):
   2043        CGAbstractStaticMethod.__init__(self, descriptor, name, returnType, args)
   2044 
   2045    def definition_body_prologue(self):
   2046        return "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" % (
   2047            self.descriptor.nativeType,
   2048            self.descriptor.nativeType,
   2049        )
   2050 
   2051    def definition_body(self):
   2052        return self.definition_body_prologue() + self.generate_code()
   2053 
   2054    def generate_code(self):
   2055        assert False  # Override me!
   2056 
   2057 
   2058 class CGGetWrapperCacheHook(CGAbstractClassHook):
   2059    """
   2060    A hook for GetWrapperCache, used by HasReleasedWrapper to get the
   2061    nsWrapperCache pointer for a non-nsISupports object.
   2062    """
   2063 
   2064    def __init__(self, descriptor):
   2065        args = [Argument("JS::Handle<JSObject*>", "obj")]
   2066        CGAbstractClassHook.__init__(
   2067            self, descriptor, GETWRAPPERCACHE_HOOK_NAME, "nsWrapperCache*", args
   2068        )
   2069 
   2070    def generate_code(self):
   2071        assert self.descriptor.wrapperCache
   2072        return dedent(
   2073            """
   2074            return self;
   2075            """
   2076        )
   2077 
   2078 
   2079 class CGDefineHTMLAttributeSlots(CGThing):
   2080    """
   2081    Function to get the slots object for reflected HTML attributes that return
   2082    a FrozenArray<Element> value.
   2083    """
   2084 
   2085    def __init__(self, descriptor):
   2086        self.descriptor = descriptor
   2087        CGThing.__init__(self)
   2088 
   2089    def declare(self):
   2090        atts = self.descriptor.interface.reflectedHTMLAttributesReturningFrozenArray
   2091        return fill(
   2092            """
   2093            using ReflectedHTMLAttributeSlots = binding_detail::ReflectedHTMLAttributeSlots<${slotIndex}, ${xraySlotIndex}, ${arrayLength}>;
   2094            """,
   2095            slotIndex=reservedSlot(atts.slotIndex, False),
   2096            xraySlotIndex=reservedSlot(atts.slotIndex, True),
   2097            arrayLength=atts.totalMembersInSlots,
   2098        )
   2099 
   2100    def define(self):
   2101        return ""
   2102 
   2103 
   2104 def finalizeHook(descriptor, gcx, obj):
   2105    finalize = "JS::SetReservedSlot(%s, DOM_OBJECT_SLOT, JS::UndefinedValue());\n" % obj
   2106    if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
   2107        finalize += fill(
   2108            """
   2109            // Either our proxy created an expando object or not.  If it did,
   2110            // then we would have preserved ourselves, and hence if we're going
   2111            // away so is our C++ object and we should reset its expando value.
   2112            // It's possible that in this situation the C++ object's reflector
   2113            // pointer has been nulled out, but if not it's pointing to us.  If
   2114            // our proxy did _not_ create an expando object then it's possible
   2115            // that we're no longer the reflector for our C++ object (and
   2116            // incremental finalization is finally getting to us), and that in
   2117            // the meantime the new reflector has created an expando object.
   2118            // In that case we do NOT want to clear the expando pointer in the
   2119            // C++ object.
   2120            //
   2121            // It's important to do this before we ClearWrapper, of course.
   2122            JSObject* reflector = self->GetWrapperMaybeDead();
   2123            if (!reflector || reflector == ${obj}) {
   2124              self->mExpandoAndGeneration.expando = JS::UndefinedValue();
   2125            }
   2126            """,
   2127            obj=obj,
   2128        )
   2129    for m in descriptor.interface.members:
   2130        if m.isAttr() and m.type.isObservableArray():
   2131            finalize += fill(
   2132                """
   2133                {
   2134                  JS::Value val = JS::GetReservedSlot(obj, ${slot});
   2135                  if (!val.isUndefined()) {
   2136                    JSObject* obj = &val.toObject();
   2137                    js::SetProxyReservedSlot(obj, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT, JS::UndefinedValue());
   2138                  }
   2139                }
   2140                """,
   2141                slot=memberReservedSlot(m, descriptor),
   2142            )
   2143    iface = getReflectedHTMLAttributesIface(descriptor)
   2144    if iface:
   2145        finalize += "%s::ReflectedHTMLAttributeSlots::Finalize(%s);\n" % (
   2146            toBindingNamespace(iface.identifier.name),
   2147            obj,
   2148        )
   2149    if descriptor.wrapperCache:
   2150        finalize += "ClearWrapper(self, self, %s);\n" % obj
   2151    if descriptor.isGlobal():
   2152        finalize += "mozilla::dom::FinalizeGlobal(%s, %s);\n" % (gcx, obj)
   2153    finalize += fill(
   2154        """
   2155        if (size_t mallocBytes = BindingJSObjectMallocBytes(self)) {
   2156          JS::RemoveAssociatedMemory(${obj}, mallocBytes,
   2157                                     JS::MemoryUse::DOMBinding);
   2158        }
   2159        """,
   2160        obj=obj,
   2161    )
   2162    finalize += "AddForDeferredFinalization<%s>(self);\n" % descriptor.nativeType
   2163    return CGIfWrapper(CGGeneric(finalize), "self")
   2164 
   2165 
   2166 class CGClassFinalizeHook(CGAbstractClassHook):
   2167    """
   2168    A hook for finalize, used to release our native object.
   2169    """
   2170 
   2171    def __init__(self, descriptor):
   2172        args = [Argument("JS::GCContext*", "gcx"), Argument("JSObject*", "obj")]
   2173        CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, "void", args)
   2174 
   2175    def generate_code(self):
   2176        return finalizeHook(
   2177            self.descriptor, self.args[0].name, self.args[1].name
   2178        ).define()
   2179 
   2180 
   2181 def JSNativeArguments():
   2182    return [
   2183        Argument("JSContext*", "cx"),
   2184        Argument("unsigned", "argc"),
   2185        Argument("JS::Value*", "vp"),
   2186    ]
   2187 
   2188 
   2189 class CGClassConstructor(CGAbstractStaticMethod):
   2190    """
   2191    JS-visible constructor for our objects
   2192    """
   2193 
   2194    def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
   2195        CGAbstractStaticMethod.__init__(
   2196            self, descriptor, name, "bool", JSNativeArguments()
   2197        )
   2198        self._ctor = ctor
   2199 
   2200    def define(self):
   2201        if not self._ctor:
   2202            return ""
   2203        return CGAbstractStaticMethod.define(self)
   2204 
   2205    def definition_body(self):
   2206        return self.generate_code()
   2207 
   2208    def generate_code(self):
   2209        if self._ctor.isHTMLConstructor():
   2210            # We better have a prototype object.  Otherwise our proto
   2211            # id won't make sense.
   2212            assert self.descriptor.interface.hasInterfacePrototypeObject()
   2213            # We also better have a constructor object, if this is
   2214            # getting called!
   2215            assert self.descriptor.interface.hasInterfaceObject()
   2216            # We can't just pass null for the CreateInterfaceObjects callback,
   2217            # because our newTarget might be in a different compartment, in
   2218            # which case we'll need to look up constructor objects in that
   2219            # compartment.
   2220            return fill(
   2221                """
   2222                return HTMLConstructor(cx, argc, vp,
   2223                                       constructors::id::${name},
   2224                                       prototypes::id::${name},
   2225                                       CreateInterfaceObjects);
   2226                """,
   2227                name=self.descriptor.name,
   2228            )
   2229 
   2230        # If the interface is already SecureContext, notify getConditionList to skip that check,
   2231        # because the constructor won't be exposed in non-secure contexts to start with.
   2232        alreadySecureContext = self.descriptor.interface.getExtendedAttribute(
   2233            "SecureContext"
   2234        )
   2235 
   2236        # We want to throw if any of the conditions returned by getConditionList are false.
   2237        conditionsCheck = ""
   2238        rawConditions = getRawConditionList(
   2239            self._ctor, "cx", "obj", alreadySecureContext
   2240        )
   2241        if len(rawConditions) > 0:
   2242            notConditions = " ||\n".join("!" + cond for cond in rawConditions)
   2243            failedCheckAction = CGGeneric("return ThrowingConstructor(cx, argc, vp);\n")
   2244            conditionsCheck = (
   2245                CGIfWrapper(failedCheckAction, notConditions).define() + "\n"
   2246            )
   2247 
   2248        # Additionally, we want to throw if a caller does a bareword invocation
   2249        # of a constructor without |new|.
   2250        ctorName = GetConstructorNameForReporting(self.descriptor, self._ctor)
   2251 
   2252        preamble = fill(
   2253            """
   2254            JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   2255            JS::Rooted<JSObject*> obj(cx, &args.callee());
   2256            $*{conditionsCheck}
   2257            if (!args.isConstructing()) {
   2258              return ThrowConstructorWithoutNew(cx, "${ctorName}");
   2259            }
   2260 
   2261            JS::Rooted<JSObject*> desiredProto(cx);
   2262            if (!GetDesiredProto(cx, args,
   2263                                 prototypes::id::${name},
   2264                                 CreateInterfaceObjects,
   2265                                 &desiredProto)) {
   2266              return false;
   2267            }
   2268            """,
   2269            conditionsCheck=conditionsCheck,
   2270            ctorName=ctorName,
   2271            name=self.descriptor.name,
   2272        )
   2273 
   2274        name = self._ctor.identifier.name
   2275        nativeName = MakeNativeName(self.descriptor.binaryNameFor(name, True))
   2276        callGenerator = CGMethodCall(
   2277            nativeName, True, self.descriptor, self._ctor, isConstructor=True
   2278        )
   2279        return preamble + "\n" + callGenerator.define()
   2280 
   2281    def auto_profiler_label(self):
   2282        return fill(
   2283            """
   2284            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
   2285              "${ctorName}", "constructor", DOM, cx,
   2286              uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
   2287            """,
   2288            ctorName=GetConstructorNameForReporting(self.descriptor, self._ctor),
   2289        )
   2290 
   2291    def error_reporting_label(self):
   2292        return CGSpecializedMethod.error_reporting_label_helper(
   2293            self.descriptor, self._ctor, isConstructor=True
   2294        )
   2295 
   2296 
   2297 def LegacyFactoryFunctionName(m):
   2298    return "_" + m.identifier.name
   2299 
   2300 
   2301 class CGLegacyFactoryFunctions(CGThing):
   2302    def __init__(self, descriptor):
   2303        self.descriptor = descriptor
   2304        CGThing.__init__(self)
   2305 
   2306    def declare(self):
   2307        return ""
   2308 
   2309    def define(self):
   2310        if len(self.descriptor.interface.legacyFactoryFunctions) == 0:
   2311            return ""
   2312 
   2313        constructorID = "constructors::id::"
   2314        if self.descriptor.interface.hasInterfaceObject():
   2315            constructorID += self.descriptor.name
   2316        else:
   2317            constructorID += "_ID_Count"
   2318 
   2319        legacyFactoryFunctions = ""
   2320        for n in self.descriptor.interface.legacyFactoryFunctions:
   2321            legacyFactoryFunctions += (
   2322                '{ "%s", { %s, &sLegacyFactoryFunctionNativePropertyHooks }, %i },\n'
   2323                % (n.identifier.name, LegacyFactoryFunctionName(n), methodLength(n))
   2324            )
   2325 
   2326        return fill(
   2327            """
   2328            bool sLegacyFactoryFunctionNativePropertiesInited = true;
   2329            const NativePropertyHooks sLegacyFactoryFunctionNativePropertyHooks = {
   2330                nullptr,
   2331                { nullptr, nullptr, &sLegacyFactoryFunctionNativePropertiesInited },
   2332                prototypes::id::${name},
   2333                ${constructorID},
   2334                nullptr
   2335            };
   2336 
   2337            static const LegacyFactoryFunction legacyFactoryFunctions[] = {
   2338              $*{legacyFactoryFunctions}
   2339            };
   2340            """,
   2341            name=self.descriptor.name,
   2342            constructorID=constructorID,
   2343            legacyFactoryFunctions=legacyFactoryFunctions,
   2344        )
   2345 
   2346 
   2347 def isChromeOnly(m):
   2348    return m.getExtendedAttribute("ChromeOnly")
   2349 
   2350 
   2351 def prefIdentifier(pref):
   2352    return pref.replace(".", "_").replace("-", "_")
   2353 
   2354 
   2355 def prefHeader(pref):
   2356    return "mozilla/StaticPrefs_%s.h" % pref.partition(".")[0]
   2357 
   2358 
   2359 def computeGlobalNamesFromExposureSet(exposureSet):
   2360    assert exposureSet is None or isinstance(exposureSet, set)
   2361 
   2362    if exposureSet:
   2363        # Nonempty set
   2364        return " | ".join(map(lambda g: "GlobalNames::%s" % g, sorted(exposureSet)))
   2365 
   2366    return "0"
   2367 
   2368 
   2369 class MemberCondition:
   2370    """
   2371    An object representing the condition for a member to actually be
   2372    exposed.  Any of the arguments can be None.  If not
   2373    None, they should have the following types:
   2374 
   2375    pref: The name of the preference.
   2376    func: The name of the function.
   2377    secureContext: A bool indicating whether a secure context is required.
   2378    nonExposedGlobals: A set of names of globals.  Can be empty, in which case
   2379                       it's treated the same way as None.
   2380    trial: The name of the origin trial.
   2381    """
   2382 
   2383    def __init__(
   2384        self,
   2385        pref=None,
   2386        func=None,
   2387        secureContext=False,
   2388        nonExposedGlobals=None,
   2389        trial=None,
   2390    ):
   2391        assert pref is None or isinstance(pref, str)
   2392        assert func is None or isinstance(func, str)
   2393        assert trial is None or isinstance(trial, str)
   2394        assert isinstance(secureContext, bool)
   2395        self.pref = pref
   2396        if self.pref:
   2397            identifier = prefIdentifier(self.pref)
   2398            self.prefFuncIndex = "WebIDLPrefIndex::" + identifier
   2399        else:
   2400            self.prefFuncIndex = "WebIDLPrefIndex::NoPref"
   2401 
   2402        self.secureContext = secureContext
   2403 
   2404        def toFuncPtr(val):
   2405            if val is None:
   2406                return "nullptr"
   2407            return "&" + val
   2408 
   2409        self.func = toFuncPtr(func)
   2410 
   2411        self.nonExposedGlobals = computeGlobalNamesFromExposureSet(nonExposedGlobals)
   2412 
   2413        if trial:
   2414            self.trial = "OriginTrial::" + trial
   2415        else:
   2416            self.trial = "OriginTrial(0)"
   2417 
   2418    def __eq__(self, other):
   2419        return (
   2420            self.pref == other.pref
   2421            and self.func == other.func
   2422            and self.secureContext == other.secureContext
   2423            and self.nonExposedGlobals == other.nonExposedGlobals
   2424            and self.trial == other.trial
   2425        )
   2426 
   2427    def __ne__(self, other):
   2428        return not self.__eq__(other)
   2429 
   2430    def hasDisablers(self):
   2431        return (
   2432            self.pref is not None
   2433            or self.secureContext
   2434            or self.func != "nullptr"
   2435            or self.nonExposedGlobals != "0"
   2436            or self.trial != "OriginTrial(0)"
   2437        )
   2438 
   2439 
   2440 class PropertyDefiner:
   2441    """
   2442    A common superclass for defining things on prototype objects.
   2443 
   2444    Subclasses should implement generateArray to generate the actual arrays of
   2445    things we're defining.  They should also set self.chrome to the list of
   2446    things only exposed to chrome and self.regular to the list of things exposed
   2447    to both chrome and web pages.
   2448    """
   2449 
   2450    def __init__(self, descriptor, name):
   2451        self.descriptor = descriptor
   2452        self.name = name
   2453 
   2454    def hasChromeOnly(self):
   2455        return len(self.chrome) > 0
   2456 
   2457    def hasNonChromeOnly(self):
   2458        return len(self.regular) > 0
   2459 
   2460    def variableName(self, chrome):
   2461        if chrome:
   2462            if self.hasChromeOnly():
   2463                return "sChrome" + self.name
   2464        else:
   2465            if self.hasNonChromeOnly():
   2466                return "s" + self.name
   2467        return "nullptr"
   2468 
   2469    def usedForXrays(self):
   2470        return self.descriptor.wantsXrays
   2471 
   2472    def length(self, chrome):
   2473        return len(self.chrome) if chrome else len(self.regular)
   2474 
   2475    def __str__(self):
   2476        # We only need to generate id arrays for things that will end
   2477        # up used via ResolveProperty or EnumerateProperties.
   2478        str = self.generateArray(self.regular, self.variableName(False))
   2479        if self.hasChromeOnly():
   2480            str += self.generateArray(self.chrome, self.variableName(True))
   2481        return str
   2482 
   2483    @staticmethod
   2484    def getStringAttr(member, name):
   2485        attr = member.getExtendedAttribute(name)
   2486        if attr is None:
   2487            return None
   2488        # It's a list of strings
   2489        assert len(attr) == 1
   2490        assert attr[0] is not None
   2491        return attr[0]
   2492 
   2493    @staticmethod
   2494    def getControllingCondition(interfaceMember, descriptor):
   2495        interface = descriptor.interface
   2496        nonExposureSet = interface.exposureSet - interfaceMember.exposureSet
   2497 
   2498        trial = PropertyDefiner.getStringAttr(interfaceMember, "Trial")
   2499        if trial and interface.identifier.name in ["Window", "Document"]:
   2500            raise TypeError(
   2501                "[Trial] not yet supported for %s.%s, see bug 1757935"
   2502                % (interface.identifier.name, interfaceMember.identifier.name)
   2503            )
   2504 
   2505        return MemberCondition(
   2506            PropertyDefiner.getStringAttr(interfaceMember, "Pref"),
   2507            PropertyDefiner.getStringAttr(interfaceMember, "Func"),
   2508            interfaceMember.getExtendedAttribute("SecureContext") is not None,
   2509            nonExposureSet,
   2510            trial,
   2511        )
   2512 
   2513    @staticmethod
   2514    def generatePrefableArrayValues(
   2515        array,
   2516        descriptor,
   2517        specFormatter,
   2518        specTerminator,
   2519        getCondition,
   2520        getDataTuple,
   2521        switchToCondition=None,
   2522    ):
   2523        """
   2524        This method generates an array of spec entries for interface members. It returns
   2525          a tuple containing the array of spec entries and the maximum of the number of
   2526          spec entries per condition.
   2527 
   2528        array is an array of interface members.
   2529 
   2530        descriptor is the descriptor for the interface that array contains members of.
   2531 
   2532        specFormatter is a function that takes a single argument, a tuple,
   2533          and returns a string, a spec array entry.
   2534 
   2535        specTerminator is a terminator for the spec array (inserted every time
   2536          our controlling pref changes and at the end of the array).
   2537 
   2538        getCondition is a callback function that takes an array entry and
   2539          returns the corresponding MemberCondition.
   2540 
   2541        getDataTuple is a callback function that takes an array entry and
   2542          returns a tuple suitable to be passed to specFormatter.
   2543 
   2544        switchToCondition is a function that takes a MemberCondition and an array of
   2545          previously generated spec entries. If None is passed for this function then all
   2546          the interface members should return the same value from getCondition.
   2547        """
   2548 
   2549        def unsupportedSwitchToCondition(condition, specs):
   2550            # If no specs have been added yet then this is just the first call to
   2551            # switchToCondition that we call to avoid putting a specTerminator at the
   2552            # front of the list.
   2553            if len(specs) == 0:
   2554                return
   2555            raise "Not supported"
   2556 
   2557        if switchToCondition is None:
   2558            switchToCondition = unsupportedSwitchToCondition
   2559 
   2560        specs = []
   2561        numSpecsInCurPrefable = 0
   2562        maxNumSpecsInPrefable = 0
   2563 
   2564        # So we won't put a specTerminator at the very front of the list:
   2565        lastCondition = getCondition(array[0], descriptor)
   2566 
   2567        switchToCondition(lastCondition, specs)
   2568 
   2569        for member in array:
   2570            curCondition = getCondition(member, descriptor)
   2571            if lastCondition != curCondition:
   2572                # Terminate previous list
   2573                specs.append(specTerminator)
   2574                if numSpecsInCurPrefable > maxNumSpecsInPrefable:
   2575                    maxNumSpecsInPrefable = numSpecsInCurPrefable
   2576                numSpecsInCurPrefable = 0
   2577                # And switch to our new condition
   2578                switchToCondition(curCondition, specs)
   2579                lastCondition = curCondition
   2580            # And the actual spec
   2581            specs.append(specFormatter(getDataTuple(member, descriptor)))
   2582            numSpecsInCurPrefable += 1
   2583        if numSpecsInCurPrefable > maxNumSpecsInPrefable:
   2584            maxNumSpecsInPrefable = numSpecsInCurPrefable
   2585        specs.append(specTerminator)
   2586 
   2587        return (specs, maxNumSpecsInPrefable)
   2588 
   2589    def generatePrefableArray(
   2590        self,
   2591        array,
   2592        name,
   2593        specFormatter,
   2594        specTerminator,
   2595        specType,
   2596        getCondition,
   2597        getDataTuple,
   2598    ):
   2599        """
   2600        This method generates our various arrays.
   2601 
   2602        array is an array of interface members as passed to generateArray
   2603 
   2604        name is the name as passed to generateArray
   2605 
   2606        specFormatter is a function that takes a single argument, a tuple,
   2607          and returns a string, a spec array entry
   2608 
   2609        specTerminator is a terminator for the spec array (inserted every time
   2610          our controlling pref changes and at the end of the array)
   2611 
   2612        specType is the actual typename of our spec
   2613 
   2614        getCondition is a callback function that takes an array entry and
   2615          returns the corresponding MemberCondition.
   2616 
   2617        getDataTuple is a callback function that takes an array entry and
   2618          returns a tuple suitable to be passed to specFormatter.
   2619        """
   2620 
   2621        # We want to generate a single list of specs, but with specTerminator
   2622        # inserted at every point where the pref name controlling the member
   2623        # changes.  That will make sure the order of the properties as exposed
   2624        # on the interface and interface prototype objects does not change when
   2625        # pref control is added to members while still allowing us to define all
   2626        # the members in the smallest number of JSAPI calls.
   2627        assert len(array) != 0
   2628 
   2629        disablers = []
   2630        prefableSpecs = []
   2631 
   2632        disablersTemplate = dedent(
   2633            """
   2634            static const PrefableDisablers %s_disablers%d = {
   2635              %s, %s, %s, %s, %s
   2636            };
   2637            """
   2638        )
   2639        prefableWithDisablersTemplate = "  { &%s_disablers%d, &%s_specs[%d] }"
   2640        prefableWithoutDisablersTemplate = "  { nullptr, &%s_specs[%d] }"
   2641 
   2642        def switchToCondition(condition, specs):
   2643            # Set up pointers to the new sets of specs inside prefableSpecs
   2644            if condition.hasDisablers():
   2645                prefableSpecs.append(
   2646                    prefableWithDisablersTemplate % (name, len(specs), name, len(specs))
   2647                )
   2648                disablers.append(
   2649                    disablersTemplate
   2650                    % (
   2651                        name,
   2652                        len(specs),
   2653                        condition.prefFuncIndex,
   2654                        condition.nonExposedGlobals,
   2655                        toStringBool(condition.secureContext),
   2656                        condition.trial,
   2657                        condition.func,
   2658                    )
   2659                )
   2660            else:
   2661                prefableSpecs.append(
   2662                    prefableWithoutDisablersTemplate % (name, len(specs))
   2663                )
   2664 
   2665        specs, maxNumSpecsInPrefable = self.generatePrefableArrayValues(
   2666            array,
   2667            self.descriptor,
   2668            specFormatter,
   2669            specTerminator,
   2670            getCondition,
   2671            getDataTuple,
   2672            switchToCondition,
   2673        )
   2674        prefableSpecs.append("  { nullptr, nullptr }")
   2675 
   2676        specType = "const " + specType
   2677        arrays = fill(
   2678            """
   2679            MOZ_GLOBINIT static ${specType} ${name}_specs[] = {
   2680            ${specs}
   2681            };
   2682 
   2683            ${disablers}
   2684            static const Prefable<${specType}> ${name}[] = {
   2685            ${prefableSpecs}
   2686            };
   2687 
   2688            """,
   2689            specType=specType,
   2690            name=name,
   2691            disablers="\n".join(disablers),
   2692            specs=",\n".join(specs),
   2693            prefableSpecs=",\n".join(prefableSpecs),
   2694        )
   2695 
   2696        if self.usedForXrays():
   2697            arrays = fill(
   2698                """
   2699                $*{arrays}
   2700                static_assert(${numPrefableSpecs} <= 1ull << NUM_BITS_PROPERTY_INFO_PREF_INDEX,
   2701                    "We have a prefable index that is >= (1 << NUM_BITS_PROPERTY_INFO_PREF_INDEX)");
   2702                static_assert(${maxNumSpecsInPrefable} <= 1ull << NUM_BITS_PROPERTY_INFO_SPEC_INDEX,
   2703                    "We have a spec index that is >= (1 << NUM_BITS_PROPERTY_INFO_SPEC_INDEX)");
   2704 
   2705                """,
   2706                arrays=arrays,
   2707                # Minus 1 because there's a list terminator in prefableSpecs.
   2708                numPrefableSpecs=len(prefableSpecs) - 1,
   2709                maxNumSpecsInPrefable=maxNumSpecsInPrefable,
   2710            )
   2711 
   2712        return arrays
   2713 
   2714 
   2715 # The length of a method is the minimum of the lengths of the
   2716 # argument lists of all its overloads.
   2717 def overloadLength(arguments):
   2718    i = len(arguments)
   2719    while i > 0 and arguments[i - 1].optional:
   2720        i -= 1
   2721    return i
   2722 
   2723 
   2724 def methodLength(method):
   2725    signatures = method.signatures()
   2726    return min(overloadLength(arguments) for retType, arguments in signatures)
   2727 
   2728 
   2729 def clearableCachedAttrs(descriptor):
   2730    return (
   2731        m
   2732        for m in descriptor.interface.members
   2733        if m.isAttr() and
   2734        # Constants should never need clearing!
   2735        m.dependsOn != "Nothing" and m.slotIndices is not None
   2736    )
   2737 
   2738 
   2739 def MakeClearCachedValueNativeName(member):
   2740    return "ClearCached%sValue" % MakeNativeName(member.identifier.name)
   2741 
   2742 
   2743 def IDLToCIdentifier(name):
   2744    return name.replace("-", "_")
   2745 
   2746 
   2747 def EnumerabilityFlags(member):
   2748    if member.getExtendedAttribute("NonEnumerable"):
   2749        return "0"
   2750    return "JSPROP_ENUMERATE"
   2751 
   2752 
   2753 class MethodDefiner(PropertyDefiner):
   2754    """
   2755    A class for defining methods on a prototype object.
   2756    """
   2757 
   2758    def __init__(self, descriptor, name, crossOriginOnly, static, unforgeable=False):
   2759        assert not (static and unforgeable)
   2760        PropertyDefiner.__init__(self, descriptor, name)
   2761 
   2762        # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
   2763        #       We should be able to check for special operations without an
   2764        #       identifier. For now we check if the name starts with __
   2765 
   2766        # Ignore non-static methods for interfaces without a proto object
   2767        if descriptor.interface.hasInterfacePrototypeObject() or static:
   2768            methods = [
   2769                m
   2770                for m in descriptor.interface.members
   2771                if m.isMethod()
   2772                and m.isStatic() == static
   2773                and MemberIsLegacyUnforgeable(m, descriptor) == unforgeable
   2774                and (
   2775                    not crossOriginOnly or m.getExtendedAttribute("CrossOriginCallable")
   2776                )
   2777                and not m.isIdentifierLess()
   2778                and not m.getExtendedAttribute("Unexposed")
   2779            ]
   2780        else:
   2781            methods = []
   2782        self.chrome = []
   2783        self.regular = []
   2784        for m in methods:
   2785            method = self.methodData(m, descriptor)
   2786 
   2787            if m.isStatic():
   2788                method["nativeName"] = CppKeywords.checkMethodName(
   2789                    IDLToCIdentifier(m.identifier.name)
   2790                )
   2791 
   2792            if isChromeOnly(m):
   2793                self.chrome.append(method)
   2794            else:
   2795                self.regular.append(method)
   2796 
   2797        # TODO: Once iterable is implemented, use tiebreak rules instead of
   2798        # failing. Also, may be more tiebreak rules to implement once spec bug
   2799        # is resolved.
   2800        # https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592
   2801        def hasIterator(methods, regular):
   2802            return any("@@iterator" in m.aliases for m in methods) or any(
   2803                "@@iterator" == r["name"] for r in regular
   2804            )
   2805 
   2806        # Check whether we need to output an @@iterator due to having an indexed
   2807        # getter.  We only do this while outputting non-static and
   2808        # non-unforgeable methods, since the @@iterator function will be
   2809        # neither.
   2810        if not static and not unforgeable and descriptor.supportsIndexedProperties():
   2811            if hasIterator(methods, self.regular):
   2812                raise TypeError(
   2813                    "Cannot have indexed getter/attr on "
   2814                    "interface %s with other members "
   2815                    "that generate @@iterator, such as "
   2816                    "maplike/setlike or aliased functions."
   2817                    % self.descriptor.interface.identifier.name
   2818                )
   2819            self.regular.append(
   2820                {
   2821                    "name": "@@iterator",
   2822                    "methodInfo": False,
   2823                    "selfHostedName": "$ArrayValues",
   2824                    "length": 0,
   2825                    "flags": "0",  # Not enumerable, per spec.
   2826                    "condition": MemberCondition(),
   2827                }
   2828            )
   2829 
   2830        # Generate the keys/values/entries aliases for value iterables.
   2831        maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
   2832        if (
   2833            not static
   2834            and not unforgeable
   2835            and maplikeOrSetlikeOrIterable
   2836            and maplikeOrSetlikeOrIterable.isIterable()
   2837            and maplikeOrSetlikeOrIterable.isValueIterator()
   2838        ):
   2839            # Add our keys/values/entries/forEach
   2840            self.regular.append(
   2841                {
   2842                    "name": "keys",
   2843                    "methodInfo": False,
   2844                    "selfHostedName": "ArrayKeys",
   2845                    "length": 0,
   2846                    "flags": "JSPROP_ENUMERATE",
   2847                    "condition": PropertyDefiner.getControllingCondition(
   2848                        maplikeOrSetlikeOrIterable, descriptor
   2849                    ),
   2850                }
   2851            )
   2852            self.regular.append(
   2853                {
   2854                    "name": "values",
   2855                    "methodInfo": False,
   2856                    "selfHostedName": "$ArrayValues",
   2857                    "length": 0,
   2858                    "flags": "JSPROP_ENUMERATE",
   2859                    "condition": PropertyDefiner.getControllingCondition(
   2860                        maplikeOrSetlikeOrIterable, descriptor
   2861                    ),
   2862                }
   2863            )
   2864            self.regular.append(
   2865                {
   2866                    "name": "entries",
   2867                    "methodInfo": False,
   2868                    "selfHostedName": "ArrayEntries",
   2869                    "length": 0,
   2870                    "flags": "JSPROP_ENUMERATE",
   2871                    "condition": PropertyDefiner.getControllingCondition(
   2872                        maplikeOrSetlikeOrIterable, descriptor
   2873                    ),
   2874                }
   2875            )
   2876            self.regular.append(
   2877                {
   2878                    "name": "forEach",
   2879                    "methodInfo": False,
   2880                    "selfHostedName": "ArrayForEach",
   2881                    "length": 1,
   2882                    "flags": "JSPROP_ENUMERATE",
   2883                    "condition": PropertyDefiner.getControllingCondition(
   2884                        maplikeOrSetlikeOrIterable, descriptor
   2885                    ),
   2886                }
   2887            )
   2888 
   2889        if not static:
   2890            stringifier = descriptor.operations["Stringifier"]
   2891            if stringifier and unforgeable == MemberIsLegacyUnforgeable(
   2892                stringifier, descriptor
   2893            ):
   2894                toStringDesc = {
   2895                    "name": GetWebExposedName(stringifier, descriptor),
   2896                    "nativeName": stringifier.identifier.name,
   2897                    "length": 0,
   2898                    "flags": "JSPROP_ENUMERATE",
   2899                    "condition": PropertyDefiner.getControllingCondition(
   2900                        stringifier, descriptor
   2901                    ),
   2902                }
   2903                if isChromeOnly(stringifier):
   2904                    self.chrome.append(toStringDesc)
   2905                else:
   2906                    self.regular.append(toStringDesc)
   2907            if unforgeable and descriptor.interface.getExtendedAttribute(
   2908                "LegacyUnforgeable"
   2909            ):
   2910                # Synthesize our valueOf method
   2911                self.regular.append(
   2912                    {
   2913                        "name": "valueOf",
   2914                        "selfHostedName": "Object_valueOf",
   2915                        "methodInfo": False,
   2916                        "length": 0,
   2917                        "flags": "0",  # readonly/permanent added automatically.
   2918                        "condition": MemberCondition(),
   2919                    }
   2920                )
   2921 
   2922        if descriptor.interface.isJSImplemented():
   2923            if static:
   2924                if descriptor.interface.hasInterfaceObject():
   2925                    self.chrome.append(
   2926                        {
   2927                            "name": "_create",
   2928                            "nativeName": ("%s::_Create" % descriptor.name),
   2929                            "methodInfo": False,
   2930                            "length": 2,
   2931                            "flags": "0",
   2932                            "condition": MemberCondition(),
   2933                        }
   2934                    )
   2935 
   2936        self.unforgeable = unforgeable
   2937 
   2938        if static:
   2939            if not descriptor.interface.hasInterfaceObject():
   2940                # static methods go on the interface object
   2941                assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
   2942        else:
   2943            if not descriptor.interface.hasInterfacePrototypeObject():
   2944                # non-static methods go on the interface prototype object
   2945                assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
   2946 
   2947    @staticmethod
   2948    def methodData(m, descriptor, overrideFlags=None):
   2949        return {
   2950            "name": m.identifier.name,
   2951            "methodInfo": not m.isStatic(),
   2952            "length": methodLength(m),
   2953            "flags": (
   2954                EnumerabilityFlags(m) if (overrideFlags is None) else overrideFlags
   2955            ),
   2956            "condition": PropertyDefiner.getControllingCondition(m, descriptor),
   2957            "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
   2958            "returnsPromise": m.returnsPromise(),
   2959            "hasIteratorAlias": "@@iterator" in m.aliases,
   2960        }
   2961 
   2962    @staticmethod
   2963    def formatSpec(fields):
   2964        if fields[0].startswith("@@"):
   2965            fields = (fields[0][2:],) + fields[1:]
   2966            return "  JS_SYM_FNSPEC(%s, %s, %s, %s, %s, %s)" % fields
   2967        return '  JS_FNSPEC("%s", %s, %s, %s, %s, %s)' % fields
   2968 
   2969    @staticmethod
   2970    def specData(m, descriptor, unforgeable=False):
   2971        def flags(m, unforgeable):
   2972            unforgeable = " | JSPROP_PERMANENT | JSPROP_READONLY" if unforgeable else ""
   2973            return m["flags"] + unforgeable
   2974 
   2975        if "selfHostedName" in m:
   2976            selfHostedName = '"%s"' % m["selfHostedName"]
   2977            assert not m.get("methodInfo", True)
   2978            accessor = "nullptr"
   2979            jitinfo = "nullptr"
   2980        else:
   2981            selfHostedName = "nullptr"
   2982            # When defining symbols, function name may not match symbol name
   2983            methodName = m.get("methodName", m["name"])
   2984            accessor = m.get("nativeName", IDLToCIdentifier(methodName))
   2985            if m.get("methodInfo", True):
   2986                if m.get("returnsPromise", False):
   2987                    exceptionPolicy = "ConvertExceptionsToPromises"
   2988                else:
   2989                    exceptionPolicy = "ThrowExceptions"
   2990 
   2991                # Cast this in case the methodInfo is a
   2992                # JSTypedMethodJitInfo.
   2993                jitinfo = (
   2994                    "reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor
   2995                )
   2996                if m.get("allowCrossOriginThis", False):
   2997                    accessor = (
   2998                        "(GenericMethod<CrossOriginThisPolicy, %s>)" % exceptionPolicy
   2999                    )
   3000                elif descriptor.interface.hasDescendantWithCrossOriginMembers:
   3001                    accessor = (
   3002                        "(GenericMethod<MaybeCrossOriginObjectThisPolicy, %s>)"
   3003                        % exceptionPolicy
   3004                    )
   3005                elif descriptor.interface.isOnGlobalProtoChain():
   3006                    accessor = (
   3007                        "(GenericMethod<MaybeGlobalThisPolicy, %s>)" % exceptionPolicy
   3008                    )
   3009                else:
   3010                    accessor = "(GenericMethod<NormalThisPolicy, %s>)" % exceptionPolicy
   3011            else:
   3012                if m.get("returnsPromise", False):
   3013                    jitinfo = "&%s_methodinfo" % accessor
   3014                    accessor = "StaticMethodPromiseWrapper"
   3015                else:
   3016                    jitinfo = "nullptr"
   3017 
   3018        return (
   3019            m["name"],
   3020            accessor,
   3021            jitinfo,
   3022            m["length"],
   3023            flags(m, unforgeable),
   3024            selfHostedName,
   3025        )
   3026 
   3027    @staticmethod
   3028    def condition(m, d):
   3029        return m["condition"]
   3030 
   3031    def generateArray(self, array, name):
   3032        if len(array) == 0:
   3033            return ""
   3034 
   3035        return self.generatePrefableArray(
   3036            array,
   3037            name,
   3038            self.formatSpec,
   3039            "  JS_FS_END",
   3040            "JSFunctionSpec",
   3041            self.condition,
   3042            functools.partial(self.specData, unforgeable=self.unforgeable),
   3043        )
   3044 
   3045 
   3046 class AttrDefiner(PropertyDefiner):
   3047    def __init__(self, descriptor, name, crossOriginOnly, static, unforgeable=False):
   3048        assert not (static and unforgeable)
   3049        PropertyDefiner.__init__(self, descriptor, name)
   3050        self.name = name
   3051        # Ignore non-static attributes for interfaces without a proto object
   3052        if descriptor.interface.hasInterfacePrototypeObject() or static:
   3053            idlAttrs = [
   3054                m
   3055                for m in descriptor.interface.members
   3056                if m.isAttr()
   3057                and m.isStatic() == static
   3058                and MemberIsLegacyUnforgeable(m, descriptor) == unforgeable
   3059                and (
   3060                    not crossOriginOnly
   3061                    or m.getExtendedAttribute("CrossOriginReadable")
   3062                    or m.getExtendedAttribute("CrossOriginWritable")
   3063                )
   3064            ]
   3065        else:
   3066            idlAttrs = []
   3067 
   3068        attributes = []
   3069        for attr in idlAttrs:
   3070            attributes.extend(self.attrData(attr, unforgeable))
   3071        self.chrome = [m for m in attributes if isChromeOnly(m["attr"])]
   3072        self.regular = [m for m in attributes if not isChromeOnly(m["attr"])]
   3073        self.static = static
   3074 
   3075        if static:
   3076            if not descriptor.interface.hasInterfaceObject():
   3077                # static attributes go on the interface object
   3078                assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
   3079        else:
   3080            if not descriptor.interface.hasInterfacePrototypeObject():
   3081                # non-static attributes go on the interface prototype object
   3082                assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
   3083 
   3084    @staticmethod
   3085    def attrData(attr, unforgeable=False, overrideFlags=None):
   3086        if overrideFlags is None:
   3087            permanent = " | JSPROP_PERMANENT" if unforgeable else ""
   3088            flags = EnumerabilityFlags(attr) + permanent
   3089        else:
   3090            flags = overrideFlags
   3091        return (
   3092            {"name": name, "attr": attr, "flags": flags}
   3093            for name in [attr.identifier.name] + attr.bindingAliases
   3094        )
   3095 
   3096    @staticmethod
   3097    def condition(m, d):
   3098        return PropertyDefiner.getControllingCondition(m["attr"], d)
   3099 
   3100    @staticmethod
   3101    def specData(entry, descriptor, static=False, crossOriginOnly=False):
   3102        def getter(attr):
   3103            if crossOriginOnly and not attr.getExtendedAttribute("CrossOriginReadable"):
   3104                return "nullptr, nullptr"
   3105            if static:
   3106                if attr.type.isPromise():
   3107                    raise TypeError(
   3108                        "Don't know how to handle "
   3109                        "static Promise-returning "
   3110                        "attribute %s.%s" % (descriptor.name, attr.identifier.name)
   3111                    )
   3112                accessor = "get_" + IDLToCIdentifier(attr.identifier.name)
   3113                jitinfo = "nullptr"
   3114            else:
   3115                if attr.type.isPromise():
   3116                    exceptionPolicy = "ConvertExceptionsToPromises"
   3117                else:
   3118                    exceptionPolicy = "ThrowExceptions"
   3119 
   3120                if attr.hasLegacyLenientThis():
   3121                    if attr.getExtendedAttribute("CrossOriginReadable"):
   3122                        raise TypeError(
   3123                            "Can't handle lenient cross-origin "
   3124                            "readable attribute %s.%s"
   3125                            % (descriptor.name, attr.identifier.name)
   3126                        )
   3127                    if descriptor.interface.hasDescendantWithCrossOriginMembers:
   3128                        accessor = (
   3129                            "GenericGetter<MaybeCrossOriginObjectLenientThisPolicy, %s>"
   3130                            % exceptionPolicy
   3131                        )
   3132                    else:
   3133                        accessor = (
   3134                            "GenericGetter<LenientThisPolicy, %s>" % exceptionPolicy
   3135                        )
   3136                elif attr.getExtendedAttribute("CrossOriginReadable"):
   3137                    accessor = (
   3138                        "GenericGetter<CrossOriginThisPolicy, %s>" % exceptionPolicy
   3139                    )
   3140                elif descriptor.interface.hasDescendantWithCrossOriginMembers:
   3141                    accessor = (
   3142                        "GenericGetter<MaybeCrossOriginObjectThisPolicy, %s>"
   3143                        % exceptionPolicy
   3144                    )
   3145                elif descriptor.interface.isOnGlobalProtoChain():
   3146                    accessor = (
   3147                        "GenericGetter<MaybeGlobalThisPolicy, %s>" % exceptionPolicy
   3148                    )
   3149                else:
   3150                    accessor = "GenericGetter<NormalThisPolicy, %s>" % exceptionPolicy
   3151                jitinfo = "&%s_getterinfo" % IDLToCIdentifier(attr.identifier.name)
   3152            return "%s, %s" % (accessor, jitinfo)
   3153 
   3154        def setter(attr):
   3155            if (
   3156                attr.readonly
   3157                and attr.getExtendedAttribute("PutForwards") is None
   3158                and attr.getExtendedAttribute("Replaceable") is None
   3159                and attr.getExtendedAttribute("LegacyLenientSetter") is None
   3160            ):
   3161                return "nullptr, nullptr"
   3162            if crossOriginOnly and not attr.getExtendedAttribute("CrossOriginWritable"):
   3163                return "nullptr, nullptr"
   3164            if static:
   3165                accessor = "set_" + IDLToCIdentifier(attr.identifier.name)
   3166                jitinfo = "nullptr"
   3167            else:
   3168                if attr.hasLegacyLenientThis():
   3169                    if attr.getExtendedAttribute("CrossOriginWritable"):
   3170                        raise TypeError(
   3171                            "Can't handle lenient cross-origin "
   3172                            "writable attribute %s.%s"
   3173                            % (descriptor.name, attr.identifier.name)
   3174                        )
   3175                    if descriptor.interface.hasDescendantWithCrossOriginMembers:
   3176                        accessor = (
   3177                            "GenericSetter<MaybeCrossOriginObjectLenientThisPolicy>"
   3178                        )
   3179                    else:
   3180                        accessor = "GenericSetter<LenientThisPolicy>"
   3181                elif attr.getExtendedAttribute("CrossOriginWritable"):
   3182                    accessor = "GenericSetter<CrossOriginThisPolicy>"
   3183                elif descriptor.interface.hasDescendantWithCrossOriginMembers:
   3184                    accessor = "GenericSetter<MaybeCrossOriginObjectThisPolicy>"
   3185                elif descriptor.interface.isOnGlobalProtoChain():
   3186                    accessor = "GenericSetter<MaybeGlobalThisPolicy>"
   3187                else:
   3188                    accessor = "GenericSetter<NormalThisPolicy>"
   3189                jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name)
   3190            return "%s, %s" % (accessor, jitinfo)
   3191 
   3192        name, attr, flags = entry["name"], entry["attr"], entry["flags"]
   3193        return (name, flags, getter(attr), setter(attr))
   3194 
   3195    @staticmethod
   3196    def formatSpec(fields):
   3197        return '  JSPropertySpec::nativeAccessors("%s", %s, %s, %s)' % fields
   3198 
   3199    def generateArray(self, array, name):
   3200        if len(array) == 0:
   3201            return ""
   3202 
   3203        return self.generatePrefableArray(
   3204            array,
   3205            name,
   3206            self.formatSpec,
   3207            "  JS_PS_END",
   3208            "JSPropertySpec",
   3209            self.condition,
   3210            functools.partial(self.specData, static=self.static),
   3211        )
   3212 
   3213 
   3214 class ConstDefiner(PropertyDefiner):
   3215    """
   3216    A class for definining constants on the interface object
   3217    """
   3218 
   3219    def __init__(self, descriptor, name):
   3220        PropertyDefiner.__init__(self, descriptor, name)
   3221        self.name = name
   3222        constants = [m for m in descriptor.interface.members if m.isConst()]
   3223        self.chrome = [m for m in constants if isChromeOnly(m)]
   3224        self.regular = [m for m in constants if not isChromeOnly(m)]
   3225 
   3226    def generateArray(self, array, name):
   3227        if len(array) == 0:
   3228            return ""
   3229 
   3230        def specData(const, descriptor):
   3231            return (const.identifier.name, convertConstIDLValueToJSVal(const.value))
   3232 
   3233        return self.generatePrefableArray(
   3234            array,
   3235            name,
   3236            lambda fields: '  { "%s", %s }' % fields,
   3237            "  { 0, JS::UndefinedValue() }",
   3238            "ConstantSpec",
   3239            PropertyDefiner.getControllingCondition,
   3240            specData,
   3241        )
   3242 
   3243 
   3244 class PropertyArrays:
   3245    def __init__(self, descriptor, crossOriginOnly=False):
   3246        self.staticMethods = MethodDefiner(
   3247            descriptor, "StaticMethods", crossOriginOnly, static=True
   3248        )
   3249        self.staticAttrs = AttrDefiner(
   3250            descriptor, "StaticAttributes", crossOriginOnly, static=True
   3251        )
   3252        self.methods = MethodDefiner(
   3253            descriptor, "Methods", crossOriginOnly, static=False
   3254        )
   3255        self.attrs = AttrDefiner(
   3256            descriptor, "Attributes", crossOriginOnly, static=False
   3257        )
   3258        self.unforgeableMethods = MethodDefiner(
   3259            descriptor,
   3260            "UnforgeableMethods",
   3261            crossOriginOnly,
   3262            static=False,
   3263            unforgeable=True,
   3264        )
   3265        self.unforgeableAttrs = AttrDefiner(
   3266            descriptor,
   3267            "UnforgeableAttributes",
   3268            crossOriginOnly,
   3269            static=False,
   3270            unforgeable=True,
   3271        )
   3272        self.consts = ConstDefiner(descriptor, "Constants")
   3273 
   3274    @staticmethod
   3275    def arrayNames():
   3276        return [
   3277            "staticMethods",
   3278            "staticAttrs",
   3279            "methods",
   3280            "attrs",
   3281            "unforgeableMethods",
   3282            "unforgeableAttrs",
   3283            "consts",
   3284        ]
   3285 
   3286    def hasChromeOnly(self):
   3287        return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
   3288 
   3289    def hasNonChromeOnly(self):
   3290        return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())
   3291 
   3292    def __str__(self):
   3293        define = ""
   3294        for array in self.arrayNames():
   3295            define += str(getattr(self, array))
   3296        return define
   3297 
   3298 
   3299 class CGConstDefinition(CGThing):
   3300    """
   3301    Given a const member of an interface, return the C++ static const definition
   3302    for the member. Should be part of the interface namespace in the header
   3303    file.
   3304    """
   3305 
   3306    def __init__(self, member):
   3307        assert (
   3308            member.isConst()
   3309            and member.value.type.isPrimitive()
   3310            and not member.value.type.nullable()
   3311        )
   3312 
   3313        name = CppKeywords.checkMethodName(IDLToCIdentifier(member.identifier.name))
   3314        tag = member.value.type.tag()
   3315        value = member.value.value
   3316        if tag == IDLType.Tags.bool:
   3317            value = toStringBool(member.value.value)
   3318        self.const = "static const %s %s = %s;" % (builtinNames[tag], name, value)
   3319 
   3320    def declare(self):
   3321        return self.const
   3322 
   3323    def define(self):
   3324        return ""
   3325 
   3326    def deps(self):
   3327        return []
   3328 
   3329 
   3330 class CGNativeProperties(CGList):
   3331    def __init__(self, descriptor, properties):
   3332        def generateNativeProperties(name, chrome):
   3333            def check(p):
   3334                return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()
   3335 
   3336            nativePropsInts = []
   3337            nativePropsPtrs = []
   3338            nativePropsDuos = []
   3339 
   3340            duosOffset = 0
   3341            idsOffset = 0
   3342            for array in properties.arrayNames():
   3343                propertyArray = getattr(properties, array)
   3344                if check(propertyArray):
   3345                    varName = propertyArray.variableName(chrome)
   3346                    bitfields = "true,  %d /* %s */" % (duosOffset, varName)
   3347                    duosOffset += 1
   3348                    nativePropsInts.append(CGGeneric(bitfields))
   3349 
   3350                    if propertyArray.usedForXrays():
   3351                        ids = "&%s_propertyInfos[%d]" % (name, idsOffset)
   3352                        idsOffset += propertyArray.length(chrome)
   3353                    else:
   3354                        ids = "nullptr"
   3355                    duo = "{ %s, %s }" % (varName, ids)
   3356                    nativePropsDuos.append(CGGeneric(duo))
   3357                else:
   3358                    bitfields = "false, 0"
   3359                    nativePropsInts.append(CGGeneric(bitfields))
   3360 
   3361            iteratorAliasIndex = -1
   3362            for index, item in enumerate(properties.methods.regular):
   3363                if item.get("hasIteratorAlias"):
   3364                    iteratorAliasIndex = index
   3365                    break
   3366            nativePropsInts.append(CGGeneric(str(iteratorAliasIndex)))
   3367 
   3368            nativePropsDuos = [
   3369                CGWrapper(
   3370                    CGIndenter(CGList(nativePropsDuos, ",\n")), pre="{\n", post="\n}"
   3371                )
   3372            ]
   3373 
   3374            pre = "static const NativePropertiesN<%d> %s = {\n" % (duosOffset, name)
   3375            post = "\n};\n"
   3376            if descriptor.wantsXrays:
   3377                pre = fill(
   3378                    """
   3379                    static uint16_t ${name}_sortedPropertyIndices[${size}];
   3380                    static PropertyInfo ${name}_propertyInfos[${size}];
   3381 
   3382                    $*{pre}
   3383                    """,
   3384                    name=name,
   3385                    size=idsOffset,
   3386                    pre=pre,
   3387                )
   3388                if iteratorAliasIndex > 0:
   3389                    # The iteratorAliasMethodIndex is a signed integer, so the
   3390                    # max value it can store is 2^(nbits-1)-1.
   3391                    post = fill(
   3392                        """
   3393                        $*{post}
   3394                        static_assert(${iteratorAliasIndex} < 1ull << (CHAR_BIT * sizeof(${name}.iteratorAliasMethodIndex) - 1),
   3395                            "We have an iterator alias index that is oversized");
   3396                        """,
   3397                        post=post,
   3398                        iteratorAliasIndex=iteratorAliasIndex,
   3399                        name=name,
   3400                    )
   3401                post = fill(
   3402                    """
   3403                    $*{post}
   3404                    static_assert(${propertyInfoCount} < 1ull << (CHAR_BIT * sizeof(${name}.propertyInfoCount)),
   3405                        "We have a property info count that is oversized");
   3406                    """,
   3407                    post=post,
   3408                    propertyInfoCount=idsOffset,
   3409                    name=name,
   3410                )
   3411                nativePropsInts.append(CGGeneric("%d" % idsOffset))
   3412                nativePropsPtrs.append(CGGeneric("%s_sortedPropertyIndices" % name))
   3413            else:
   3414                nativePropsInts.append(CGGeneric("0"))
   3415                nativePropsPtrs.append(CGGeneric("nullptr"))
   3416            nativeProps = nativePropsInts + nativePropsPtrs + nativePropsDuos
   3417            return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")), pre=pre, post=post)
   3418 
   3419        nativeProperties = []
   3420        if properties.hasNonChromeOnly():
   3421            nativeProperties.append(
   3422                generateNativeProperties("sNativeProperties", False)
   3423            )
   3424        if properties.hasChromeOnly():
   3425            nativeProperties.append(
   3426                generateNativeProperties("sChromeOnlyNativeProperties", True)
   3427            )
   3428 
   3429        CGList.__init__(self, nativeProperties, "\n")
   3430 
   3431    def declare(self):
   3432        return ""
   3433 
   3434    def define(self):
   3435        return CGList.define(self)
   3436 
   3437 
   3438 class CGCollectJSONAttributesMethod(CGAbstractMethod):
   3439    """
   3440    Generate the CollectJSONAttributes method for an interface descriptor
   3441    """
   3442 
   3443    def __init__(self, descriptor, toJSONMethod):
   3444        args = [
   3445            Argument("JSContext*", "cx"),
   3446            Argument("JS::Handle<JSObject*>", "obj"),
   3447            Argument("%s*" % descriptor.nativeType, "self"),
   3448            Argument("JS::Rooted<JSObject*>&", "result"),
   3449        ]
   3450        CGAbstractMethod.__init__(
   3451            self, descriptor, "CollectJSONAttributes", "bool", args, canRunScript=True
   3452        )
   3453        self.toJSONMethod = toJSONMethod
   3454 
   3455    def definition_body(self):
   3456        ret = ""
   3457        interface = self.descriptor.interface
   3458        toJSONCondition = PropertyDefiner.getControllingCondition(
   3459            self.toJSONMethod, self.descriptor
   3460        )
   3461        needUnwrappedObj = False
   3462        for m in interface.members:
   3463            if m.isAttr() and not m.isStatic() and m.type.isJSONType():
   3464                getAndDefine = fill(
   3465                    """
   3466                    JS::Rooted<JS::Value> temp(cx);
   3467                    if (!get_${name}(cx, obj, self, JSJitGetterCallArgs(&temp))) {
   3468                      return false;
   3469                    }
   3470                    if (!JS_DefineProperty(cx, result, "${name}", temp, JSPROP_ENUMERATE)) {
   3471                      return false;
   3472                    }
   3473                    """,
   3474                    name=IDLToCIdentifier(m.identifier.name),
   3475                )
   3476                # Make sure we don't include things which are supposed to be
   3477                # disabled.  Things that either don't have disablers or whose
   3478                # disablers match the disablers for our toJSON method can't
   3479                # possibly be disabled, but other things might be.
   3480                condition = PropertyDefiner.getControllingCondition(m, self.descriptor)
   3481                if condition.hasDisablers() and condition != toJSONCondition:
   3482                    needUnwrappedObj = True
   3483                    ret += fill(
   3484                        """
   3485                        // This is unfortunately a linear scan through sAttributes, but we
   3486                        // only do it for things which _might_ be disabled, which should
   3487                        // help keep the performance problems down.
   3488                        if (IsGetterEnabled(cx, unwrappedObj, (JSJitGetterOp)get_${name}, sAttributes)) {
   3489                          $*{getAndDefine}
   3490                        }
   3491                        """,
   3492                        name=IDLToCIdentifier(m.identifier.name),
   3493                        getAndDefine=getAndDefine,
   3494                    )
   3495                else:
   3496                    ret += fill(
   3497                        """
   3498                        { // scope for "temp"
   3499                          $*{getAndDefine}
   3500                        }
   3501                        """,
   3502                        getAndDefine=getAndDefine,
   3503                    )
   3504        ret += "return true;\n"
   3505 
   3506        if needUnwrappedObj:
   3507            # If we started allowing cross-origin objects here, we'd need to
   3508            # use CheckedUnwrapDynamic and figure out whether it makes sense.
   3509            # But in practice no one is trying to add toJSON methods to those,
   3510            # so let's just guard against it.
   3511            assert not self.descriptor.isMaybeCrossOriginObject()
   3512            ret = fill(
   3513                """
   3514                JS::Rooted<JSObject*> unwrappedObj(cx, js::CheckedUnwrapStatic(obj));
   3515                if (!unwrappedObj) {
   3516                  // How did that happen?  We managed to get called with that
   3517                  // object as "this"!  Just give up on sanity.
   3518                  return false;
   3519                }
   3520 
   3521                $*{ret}
   3522                """,
   3523                ret=ret,
   3524            )
   3525 
   3526        return ret
   3527 
   3528 
   3529 class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
   3530    """
   3531    Generate the CreateInterfaceObjects method for an interface descriptor.
   3532 
   3533    properties should be a PropertyArrays instance.
   3534    """
   3535 
   3536    def __init__(
   3537        self, descriptor, properties, haveUnscopables, haveLegacyWindowAliases, static
   3538    ):
   3539        args = [
   3540            Argument("JSContext*", "aCx"),
   3541            Argument("JS::Handle<JSObject*>", "aGlobal"),
   3542            Argument("ProtoAndIfaceCache&", "aProtoAndIfaceCache"),
   3543            Argument("DefineInterfaceProperty", "aDefineOnGlobal"),
   3544        ]
   3545        CGAbstractMethod.__init__(
   3546            self, descriptor, "CreateInterfaceObjects", "void", args, static=static
   3547        )
   3548        self.properties = properties
   3549        self.haveUnscopables = haveUnscopables
   3550        self.haveLegacyWindowAliases = haveLegacyWindowAliases
   3551 
   3552    def definition_body(self):
   3553        needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
   3554        if needInterfaceObject and self.descriptor.isExposedConditionally():
   3555            # This code might be called when we're trying to create an object
   3556            # in a non-system compartment, for example when system code is
   3557            # calling a constructor through Xrays. In that case we do want to
   3558            # create an interface object in the non-system compartment, but we
   3559            # don't want to expose the name on the non-system global if the
   3560            # interface itself is marked as ChromeOnly.
   3561            defineOnGlobal = (
   3562                "ShouldExpose<%s::ConstructorEnabled>(aCx, aGlobal, aDefineOnGlobal)"
   3563                % toBindingNamespace(self.descriptor.name)
   3564            )
   3565        else:
   3566            defineOnGlobal = "aDefineOnGlobal != DefineInterfaceProperty::No"
   3567        if needInterfaceObject:
   3568            if self.descriptor.interface.isNamespace():
   3569                if self.descriptor.interface.getExtendedAttribute("ProtoObjectHack"):
   3570                    getConstructorProto = "GetHackedNamespaceProtoObject"
   3571                else:
   3572                    getConstructorProto = "JS::GetRealmObjectPrototype"
   3573                getConstructorProto = "aCx, " + getConstructorProto
   3574                constructorProtoType = "Rooted"
   3575            else:
   3576                getConstructorProto = InterfaceObjectProtoGetter(self.descriptor)
   3577                constructorProtoType = "Handle"
   3578 
   3579            getConstructorProto = fill(
   3580                """
   3581                JS::${type}<JSObject*> constructorProto(${getConstructorProto}(aCx));
   3582                if (!constructorProto) {
   3583                  return;
   3584                }
   3585                """,
   3586                type=constructorProtoType,
   3587                getConstructorProto=getConstructorProto,
   3588            )
   3589 
   3590            interfaceInfo = "&sInterfaceObjectInfo"
   3591            interfaceCache = (
   3592                "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)"
   3593                % self.descriptor.name
   3594            )
   3595            getConstructorProto = CGGeneric(getConstructorProto)
   3596            constructorProto = "constructorProto"
   3597        else:
   3598            # We don't have slots to store the legacy factory functions.
   3599            assert len(self.descriptor.interface.legacyFactoryFunctions) == 0
   3600            interfaceInfo = "nullptr"
   3601            interfaceCache = "nullptr"
   3602            getConstructorProto = None
   3603            constructorProto = "nullptr"
   3604 
   3605        if self.properties.hasNonChromeOnly():
   3606            properties = "sNativeProperties.Upcast()"
   3607        else:
   3608            properties = "nullptr"
   3609        if self.properties.hasChromeOnly():
   3610            chromeProperties = "sChromeOnlyNativeProperties.Upcast()"
   3611        else:
   3612            chromeProperties = "nullptr"
   3613 
   3614        # We use getClassName here. This should be the right thing to pass as
   3615        # the name argument to CreateInterfaceObjects. This is generally the
   3616        # interface identifier, except for the synthetic interfaces created for
   3617        # the default iterator objects. If needInterfaceObject is true then
   3618        # we'll use the name to install a property on the global object, so
   3619        # there shouldn't be any spaces in the name.
   3620        name = self.descriptor.interface.getClassName()
   3621        assert not (needInterfaceObject and " " in name)
   3622 
   3623        if self.descriptor.interface.isNamespace():
   3624            # If we don't need to create anything, why are we generating this?
   3625            assert needInterfaceObject
   3626 
   3627            call = fill(
   3628                """
   3629                JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
   3630                dom::CreateNamespaceObject(aCx, aGlobal, ${constructorProto},
   3631                                           sNamespaceObjectClass,
   3632                                           interfaceCache,
   3633                                           ${properties},
   3634                                           ${chromeProperties},
   3635                                           "${name}",
   3636                                           ${defineOnGlobal});
   3637                """,
   3638                interfaceCache=interfaceCache,
   3639                constructorProto=constructorProto,
   3640                properties=properties,
   3641                chromeProperties=chromeProperties,
   3642                name=name,
   3643                defineOnGlobal=defineOnGlobal,
   3644            )
   3645            return CGList(
   3646                [
   3647                    getConstructorProto,
   3648                    CGGeneric(call),
   3649                ],
   3650                "\n",
   3651            ).define()
   3652 
   3653        needInterfacePrototypeObject = (
   3654            self.descriptor.interface.hasInterfacePrototypeObject()
   3655        )
   3656 
   3657        # If we don't need to create anything, why are we generating this?
   3658        assert needInterfaceObject or needInterfacePrototypeObject
   3659 
   3660        if needInterfacePrototypeObject:
   3661            (protoGetter, protoHandleGetter) = InterfacePrototypeObjectProtoGetter(
   3662                self.descriptor
   3663            )
   3664            if protoHandleGetter is None:
   3665                parentProtoType = "Rooted"
   3666                getParentProto = "aCx, " + protoGetter
   3667            else:
   3668                parentProtoType = "Handle"
   3669                getParentProto = protoHandleGetter
   3670 
   3671            getParentProto = fill(
   3672                """
   3673                JS::${type}<JSObject*> parentProto(${getParentProto}(aCx));
   3674                if (!parentProto) {
   3675                  return;
   3676                }
   3677                """,
   3678                type=parentProtoType,
   3679                getParentProto=getParentProto,
   3680            )
   3681 
   3682            protoClass = "&sPrototypeClass"
   3683            protoCache = (
   3684                "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)"
   3685                % self.descriptor.name
   3686            )
   3687            parentProto = "parentProto"
   3688            getParentProto = CGGeneric(getParentProto)
   3689        else:
   3690            protoClass = "nullptr"
   3691            protoCache = "nullptr"
   3692            parentProto = "nullptr"
   3693            getParentProto = None
   3694 
   3695        if self.descriptor.interface.ctor():
   3696            constructArgs = methodLength(self.descriptor.interface.ctor())
   3697            isConstructorChromeOnly = isChromeOnly(self.descriptor.interface.ctor())
   3698        else:
   3699            constructArgs = 0
   3700            isConstructorChromeOnly = False
   3701        if len(self.descriptor.interface.legacyFactoryFunctions) > 0:
   3702            legacyFactoryFunctions = "Span(legacyFactoryFunctions)"
   3703        else:
   3704            legacyFactoryFunctions = "Span<const LegacyFactoryFunction, 0>{}"
   3705 
   3706        isGlobal = self.descriptor.isGlobal() is not None
   3707 
   3708        ensureCaches = fill(
   3709            """
   3710            JS::Heap<JSObject*>* protoCache = ${protoCache};
   3711            JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
   3712            """,
   3713            protoCache=protoCache,
   3714            interfaceCache=interfaceCache,
   3715        )
   3716        call = fill(
   3717            """
   3718            dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
   3719                                        ${protoClass}, protoCache,
   3720                                        ${constructorProto}, ${interfaceInfo}, ${constructArgs}, ${isConstructorChromeOnly}, ${legacyFactoryFunctions},
   3721                                        interfaceCache,
   3722                                        ${properties},
   3723                                        ${chromeProperties},
   3724                                        "${name}",
   3725                                        ${defineOnGlobal},
   3726                                        ${unscopableNames},
   3727                                        ${isGlobal},
   3728                                        ${legacyWindowAliases});
   3729            """,
   3730            protoClass=protoClass,
   3731            parentProto=parentProto,
   3732            constructorProto=constructorProto,
   3733            interfaceInfo=interfaceInfo,
   3734            constructArgs=constructArgs,
   3735            isConstructorChromeOnly=toStringBool(isConstructorChromeOnly),
   3736            legacyFactoryFunctions=legacyFactoryFunctions,
   3737            properties=properties,
   3738            chromeProperties=chromeProperties,
   3739            name=name,
   3740            defineOnGlobal=defineOnGlobal,
   3741            unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr",
   3742            isGlobal=toStringBool(isGlobal),
   3743            legacyWindowAliases=(
   3744                "legacyWindowAliases" if self.haveLegacyWindowAliases else "nullptr"
   3745            ),
   3746        )
   3747 
   3748        # If we fail after here, we must clear interface and prototype caches
   3749        # using this code: intermediate failure must not expose the interface in
   3750        # partially-constructed state.  Note that every case after here needs an
   3751        # interface prototype object.
   3752        failureCode = dedent(
   3753            """
   3754            *protoCache = nullptr;
   3755            if (interfaceCache) {
   3756              *interfaceCache = nullptr;
   3757            }
   3758            return;
   3759            """
   3760        )
   3761 
   3762        needProtoVar = False
   3763 
   3764        aliasedMembers = [
   3765            m for m in self.descriptor.interface.members if m.isMethod() and m.aliases
   3766        ]
   3767        if aliasedMembers:
   3768            assert needInterfacePrototypeObject
   3769 
   3770            def defineAlias(alias):
   3771                if alias == "@@iterator" or alias == "@@asyncIterator":
   3772                    name = alias[2:]
   3773 
   3774                    symbolJSID = (
   3775                        "JS::GetWellKnownSymbolKey(aCx, JS::SymbolCode::%s)" % name
   3776                    )
   3777                    prop = "%sId" % name
   3778                    getSymbolJSID = CGGeneric(
   3779                        fill(
   3780                            "JS::Rooted<jsid> ${prop}(aCx, ${symbolJSID});",
   3781                            prop=prop,
   3782                            symbolJSID=symbolJSID,
   3783                        )
   3784                    )
   3785                    defineFn = "JS_DefinePropertyById"
   3786                    enumFlags = "0"  # Not enumerable, per spec.
   3787                elif alias.startswith("@@"):
   3788                    raise TypeError(
   3789                        "Can't handle any well-known Symbol other than @@iterator and @@asyncIterator"
   3790                    )
   3791                else:
   3792                    getSymbolJSID = None
   3793                    defineFn = "JS_DefineProperty"
   3794                    prop = '"%s"' % alias
   3795                    # XXX If we ever create non-enumerable properties that can
   3796                    #     be aliased, we should consider making the aliases
   3797                    #     match the enumerability of the property being aliased.
   3798                    enumFlags = "JSPROP_ENUMERATE"
   3799                return CGList(
   3800                    [
   3801                        getSymbolJSID,
   3802                        CGGeneric(
   3803                            fill(
   3804                                """
   3805                        if (!${defineFn}(aCx, proto, ${prop}, aliasedVal, ${enumFlags})) {
   3806                          $*{failureCode}
   3807                        }
   3808                        """,
   3809                                defineFn=defineFn,
   3810                                prop=prop,
   3811                                enumFlags=enumFlags,
   3812                                failureCode=failureCode,
   3813                            )
   3814                        ),
   3815                    ],
   3816                    "\n",
   3817                )
   3818 
   3819            def defineAliasesFor(m):
   3820                return CGList(
   3821                    [
   3822                        CGGeneric(
   3823                            fill(
   3824                                """
   3825                        if (!JS_GetProperty(aCx, proto, \"${prop}\", &aliasedVal)) {
   3826                          $*{failureCode}
   3827                        }
   3828                        """,
   3829                                failureCode=failureCode,
   3830                                prop=m.identifier.name,
   3831                            )
   3832                        )
   3833                    ]
   3834                    + [defineAlias(alias) for alias in sorted(m.aliases)]
   3835                )
   3836 
   3837            defineAliases = CGList(
   3838                [
   3839                    CGGeneric(
   3840                        dedent(
   3841                            """
   3842                    // Set up aliases on the interface prototype object we just created.
   3843                    """
   3844                        )
   3845                    ),
   3846                    CGGeneric("JS::Rooted<JS::Value> aliasedVal(aCx);\n\n"),
   3847                ]
   3848                + [
   3849                    defineAliasesFor(m)
   3850                    for m in sorted(aliasedMembers, key=lambda m: m.identifier.name)
   3851                ]
   3852            )
   3853            needProtoVar = True
   3854        else:
   3855            defineAliases = None
   3856 
   3857        # Globals handle unforgeables directly in Wrap() instead of
   3858        # via a holder.
   3859        if (
   3860            self.descriptor.hasLegacyUnforgeableMembers
   3861            and not self.descriptor.isGlobal()
   3862        ):
   3863            assert needInterfacePrototypeObject
   3864 
   3865            # We want to use the same JSClass and prototype as the object we'll
   3866            # end up defining the unforgeable properties on in the end, so that
   3867            # we can use JS_InitializePropertiesFromCompatibleNativeObject to do
   3868            # a fast copy.  In the case of proxies that's null, because the
   3869            # expando object is a vanilla object, but in the case of other DOM
   3870            # objects it's whatever our class is.
   3871            if self.descriptor.proxy:
   3872                holderClass = "nullptr"
   3873                holderProto = "nullptr"
   3874            else:
   3875                holderClass = "sClass.ToJSClass()"
   3876                holderProto = "proto"
   3877                needProtoVar = True
   3878            createUnforgeableHolder = CGGeneric(
   3879                fill(
   3880                    """
   3881                JS::Rooted<JSObject*> unforgeableHolder(
   3882                    aCx, JS_NewObjectWithoutMetadata(aCx, ${holderClass}, ${holderProto}));
   3883                if (!unforgeableHolder) {
   3884                  $*{failureCode}
   3885                }
   3886                """,
   3887                    holderProto=holderProto,
   3888                    holderClass=holderClass,
   3889                    failureCode=failureCode,
   3890                )
   3891            )
   3892            defineUnforgeables = InitUnforgeablePropertiesOnHolder(
   3893                self.descriptor, self.properties, failureCode
   3894            )
   3895            createUnforgeableHolder = CGList(
   3896                [createUnforgeableHolder, defineUnforgeables]
   3897            )
   3898 
   3899            installUnforgeableHolder = CGGeneric(
   3900                dedent(
   3901                    """
   3902                if (*protoCache) {
   3903                  JS::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE,
   3904                                      JS::ObjectValue(*unforgeableHolder));
   3905                }
   3906                """
   3907                )
   3908            )
   3909 
   3910            unforgeableHolderSetup = CGList(
   3911                [createUnforgeableHolder, installUnforgeableHolder], "\n"
   3912            )
   3913        else:
   3914            unforgeableHolderSetup = None
   3915 
   3916        if (
   3917            self.descriptor.interface.isOnGlobalProtoChain()
   3918            and needInterfacePrototypeObject
   3919        ):
   3920            makeProtoPrototypeImmutable = CGGeneric(
   3921                fill(
   3922                    """
   3923                {
   3924                  bool succeeded;
   3925                  if (!JS_SetImmutablePrototype(aCx, proto, &succeeded)) {
   3926                    $*{failureCode}
   3927                  }
   3928 
   3929                  MOZ_ASSERT(succeeded,
   3930                             "making a fresh prototype object's [[Prototype]] "
   3931                             "immutable can internally fail, but it should "
   3932                             "never be unsuccessful");
   3933                }
   3934                """,
   3935                    protoCache=protoCache,
   3936                    failureCode=failureCode,
   3937                )
   3938            )
   3939            needProtoVar = True
   3940        else:
   3941            makeProtoPrototypeImmutable = None
   3942 
   3943        if needProtoVar:
   3944            defineProtoVar = CGGeneric(
   3945                fill(
   3946                    """
   3947                JS::AssertObjectIsNotGray(*protoCache);
   3948                JS::Handle<JSObject*> proto = JS::Handle<JSObject*>::fromMarkedLocation(protoCache->unsafeAddress());
   3949                if (!proto) {
   3950                  $*{failureCode}
   3951                }
   3952                """,
   3953                    failureCode=failureCode,
   3954                )
   3955            )
   3956        else:
   3957            defineProtoVar = None
   3958 
   3959        # ensureCaches needs to come first as it crashes on failure (like OOM).
   3960        # We want to make sure that the caches do exist before we try to return
   3961        # to the caller, so it can rely on that (and detect other failures by
   3962        # checking for null in the caches).
   3963        return CGList(
   3964            [
   3965                CGGeneric(ensureCaches),
   3966                getParentProto,
   3967                getConstructorProto,
   3968                CGGeneric(call),
   3969                defineProtoVar,
   3970                defineAliases,
   3971                unforgeableHolderSetup,
   3972                makeProtoPrototypeImmutable,
   3973            ],
   3974            "\n",
   3975        ).define()
   3976 
   3977 
   3978 class CGCreateAndDefineOnGlobalMethod(CGAbstractMethod):
   3979    """
   3980    A method for creating the interface or namespace object and defining
   3981    properties for it on the global.
   3982    """
   3983 
   3984    def __init__(self, descriptor):
   3985        CGAbstractMethod.__init__(
   3986            self,
   3987            descriptor,
   3988            "CreateAndDefineOnGlobal",
   3989            "bool",
   3990            [
   3991                Argument("JSContext*", "aCx"),
   3992            ],
   3993            inline=True,
   3994        )
   3995 
   3996    def definition_body(self):
   3997        return fill(
   3998            """
   3999            // Get the interface or namespace object for this class. This will
   4000            // create the object as needed and always define the properties for
   4001            // it on the global. The caller should make sure the interface or
   4002            // namespace is exposed on the global before calling this.
   4003            return GetPerInterfaceObjectHandle(aCx, constructors::id::${name},
   4004                                               &CreateInterfaceObjects,
   4005                                               DefineInterfaceProperty::Always);
   4006 
   4007            """,
   4008            name=self.descriptor.name,
   4009        )
   4010 
   4011 
   4012 class CGGetProtoObjectHandleMethod(CGAbstractMethod):
   4013    """
   4014    A method for getting the interface prototype object.
   4015    """
   4016 
   4017    def __init__(self, descriptor, static, signatureOnly=False):
   4018        CGAbstractMethod.__init__(
   4019            self,
   4020            descriptor,
   4021            "GetProtoObjectHandle",
   4022            "JS::Handle<JSObject*>",
   4023            [Argument("JSContext*", "aCx")],
   4024            static=static,
   4025            signatureOnly=signatureOnly,
   4026        )
   4027 
   4028    def definition_body(self):
   4029        return fill(
   4030            """
   4031            /* Get the interface prototype object for this class.  This will create the
   4032               object as needed. */
   4033            return GetPerInterfaceObjectHandle(aCx, prototypes::id::${name},
   4034                                               &CreateInterfaceObjects,
   4035                                               DefineInterfaceProperty::CheckExposure);
   4036 
   4037            """,
   4038            name=self.descriptor.name,
   4039        )
   4040 
   4041 
   4042 class CGGetProtoObjectMethod(CGAbstractMethod):
   4043    """
   4044    A method for getting the interface prototype object.
   4045    """
   4046 
   4047    def __init__(self, descriptor):
   4048        CGAbstractMethod.__init__(
   4049            self,
   4050            descriptor,
   4051            "GetProtoObject",
   4052            "JSObject*",
   4053            [Argument("JSContext*", "aCx")],
   4054        )
   4055 
   4056    def definition_body(self):
   4057        return "return GetProtoObjectHandle(aCx);\n"
   4058 
   4059 
   4060 class CGGetConstructorObjectHandleMethod(CGAbstractMethod):
   4061    """
   4062    A method for getting the interface constructor object.
   4063    """
   4064 
   4065    def __init__(self, descriptor):
   4066        CGAbstractMethod.__init__(
   4067            self,
   4068            descriptor,
   4069            "GetConstructorObjectHandle",
   4070            "JS::Handle<JSObject*>",
   4071            [
   4072                Argument("JSContext*", "aCx"),
   4073            ],
   4074        )
   4075 
   4076    def definition_body(self):
   4077        return fill(
   4078            """
   4079            /* Get the interface object for this class.  This will create the object as
   4080               needed. */
   4081 
   4082            return GetPerInterfaceObjectHandle(aCx, constructors::id::${name},
   4083                                               &CreateInterfaceObjects,
   4084                                               DefineInterfaceProperty::CheckExposure);
   4085            """,
   4086            name=self.descriptor.name,
   4087        )
   4088 
   4089 
   4090 class CGGetNamedPropertiesObjectMethod(CGAbstractStaticMethod):
   4091    def __init__(self, descriptor):
   4092        args = [Argument("JSContext*", "aCx")]
   4093        CGAbstractStaticMethod.__init__(
   4094            self, descriptor, "GetNamedPropertiesObject", "JSObject*", args
   4095        )
   4096 
   4097    def definition_body(self):
   4098        parentProtoName = self.descriptor.parentPrototypeName
   4099        if parentProtoName is None:
   4100            getParentProto = ""
   4101            parentProto = "nullptr"
   4102        else:
   4103            getParentProto = fill(
   4104                """
   4105                JS::Rooted<JSObject*> parentProto(aCx, ${parent}::GetProtoObjectHandle(aCx));
   4106                if (!parentProto) {
   4107                  return nullptr;
   4108                }
   4109                """,
   4110                parent=toBindingNamespace(parentProtoName),
   4111            )
   4112            parentProto = "parentProto"
   4113        return fill(
   4114            """
   4115            /* Make sure our global is sane.  Hopefully we can remove this sometime */
   4116            JSObject* global = JS::CurrentGlobalOrNull(aCx);
   4117            if (!(JS::GetClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
   4118              return nullptr;
   4119            }
   4120 
   4121            /* Check to see whether the named properties object has already been created */
   4122            ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
   4123 
   4124            JS::Heap<JSObject*>& namedPropertiesObject = protoAndIfaceCache.EntrySlotOrCreate(namedpropertiesobjects::id::${ifaceName});
   4125            if (!namedPropertiesObject) {
   4126              $*{getParentProto}
   4127              namedPropertiesObject = ${nativeType}::CreateNamedPropertiesObject(aCx, ${parentProto});
   4128              DebugOnly<const DOMIfaceAndProtoJSClass*> clasp =
   4129                DOMIfaceAndProtoJSClass::FromJSClass(JS::GetClass(namedPropertiesObject));
   4130              MOZ_ASSERT(clasp->mType == eNamedPropertiesObject,
   4131                         "Expected ${nativeType}::CreateNamedPropertiesObject to return a named properties object");
   4132              MOZ_ASSERT(clasp->mNativeHooks,
   4133                         "The named properties object for ${nativeType} should have NativePropertyHooks.");
   4134              MOZ_ASSERT(!clasp->mNativeHooks->mIndexedOrNamedNativeProperties ||
   4135                         !clasp->mNativeHooks->mIndexedOrNamedNativeProperties->mResolveOwnProperty,
   4136                         "Shouldn't resolve the properties of the named properties object for ${nativeType} for Xrays.");
   4137              MOZ_ASSERT(!clasp->mNativeHooks->mIndexedOrNamedNativeProperties ||
   4138                         !clasp->mNativeHooks->mIndexedOrNamedNativeProperties->mEnumerateOwnProperties,
   4139                         "Shouldn't enumerate the properties of the named properties object for ${nativeType} for Xrays.");
   4140            }
   4141            return namedPropertiesObject.get();
   4142            """,
   4143            getParentProto=getParentProto,
   4144            ifaceName=self.descriptor.name,
   4145            parentProto=parentProto,
   4146            nativeType=self.descriptor.nativeType,
   4147        )
   4148 
   4149 
   4150 def getRawConditionList(idlobj, cxName, objName, ignoreSecureContext=False):
   4151    """
   4152    Get the list of conditions for idlobj (to be used in "is this enabled"
   4153    checks).  This will be returned as a CGList with " &&\n" as the separator,
   4154    for readability.
   4155 
   4156    objName is the name of the object that we're working with, because some of
   4157    our test functions want that.
   4158 
   4159    ignoreSecureContext is used only for constructors in which the WebIDL interface
   4160    itself is already marked as [SecureContext]. There is no need to do the work twice.
   4161    """
   4162    conditions = []
   4163    pref = idlobj.getExtendedAttribute("Pref")
   4164    if pref:
   4165        assert isinstance(pref, list) and len(pref) == 1
   4166        conditions.append("StaticPrefs::%s()" % prefIdentifier(pref[0]))
   4167    if isChromeOnly(idlobj):
   4168        conditions.append("nsContentUtils::ThreadsafeIsSystemCaller(%s)" % cxName)
   4169    func = idlobj.getExtendedAttribute("Func")
   4170    if func:
   4171        assert isinstance(func, list) and len(func) == 1
   4172        conditions.append("%s(%s, %s)" % (func[0], cxName, objName))
   4173    trial = idlobj.getExtendedAttribute("Trial")
   4174    if trial:
   4175        assert isinstance(trial, list) and len(trial) == 1
   4176        conditions.append(
   4177            "OriginTrials::IsEnabled(%s, %s, OriginTrial::%s)"
   4178            % (cxName, objName, trial[0])
   4179        )
   4180    if not ignoreSecureContext and idlobj.getExtendedAttribute("SecureContext"):
   4181        conditions.append(
   4182            "mozilla::dom::IsSecureContextOrObjectIsFromSecureContext(%s, %s)"
   4183            % (cxName, objName)
   4184        )
   4185    return conditions
   4186 
   4187 
   4188 def getConditionList(idlobj, cxName, objName, ignoreSecureContext=False):
   4189    """
   4190    Get the list of conditions from getRawConditionList
   4191    See comment on getRawConditionList above for more info about arguments.
   4192 
   4193    The return value is a possibly-empty conjunctive CGList of conditions.
   4194    """
   4195    conditions = getRawConditionList(idlobj, cxName, objName, ignoreSecureContext)
   4196    return CGList((CGGeneric(cond) for cond in conditions), " &&\n")
   4197 
   4198 
   4199 class CGConstructorEnabled(CGAbstractMethod):
   4200    """
   4201    A method for testing whether we should be exposing this interface object.
   4202    This can perform various tests depending on what conditions are specified
   4203    on the interface.
   4204    """
   4205 
   4206    def __init__(self, descriptor):
   4207        CGAbstractMethod.__init__(
   4208            self,
   4209            descriptor,
   4210            "ConstructorEnabled",
   4211            "bool",
   4212            [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
   4213        )
   4214 
   4215    def definition_body(self):
   4216        body = CGList([], "\n")
   4217 
   4218        iface = self.descriptor.interface
   4219 
   4220        if not iface.isExposedInWindow():
   4221            exposedInWindowCheck = dedent(
   4222                """
   4223                MOZ_ASSERT(!NS_IsMainThread(), "Why did we even get called?");
   4224                """
   4225            )
   4226            body.append(CGGeneric(exposedInWindowCheck))
   4227 
   4228        if iface.isExposedInSomeButNotAllWorkers():
   4229            workerGlobals = sorted(iface.getWorkerExposureSet())
   4230            workerCondition = CGList(
   4231                (
   4232                    CGGeneric('strcmp(name, "%s")' % workerGlobal)
   4233                    for workerGlobal in workerGlobals
   4234                ),
   4235                " && ",
   4236            )
   4237            exposedInWorkerCheck = fill(
   4238                """
   4239                const char* name = JS::GetClass(aObj)->name;
   4240                if (${workerCondition}) {
   4241                  return false;
   4242                }
   4243                """,
   4244                workerCondition=workerCondition.define(),
   4245            )
   4246            exposedInWorkerCheck = CGGeneric(exposedInWorkerCheck)
   4247            if iface.isExposedInWindow():
   4248                exposedInWorkerCheck = CGIfWrapper(
   4249                    exposedInWorkerCheck, "!NS_IsMainThread()"
   4250                )
   4251            body.append(exposedInWorkerCheck)
   4252 
   4253        conditions = getConditionList(iface, "aCx", "aObj")
   4254 
   4255        # We should really have some conditions
   4256        assert len(body) or len(conditions)
   4257 
   4258        conditionsWrapper = ""
   4259        if len(conditions):
   4260            conditionsWrapper = CGWrapper(
   4261                conditions, pre="return ", post=";\n", reindent=True
   4262            )
   4263        else:
   4264            conditionsWrapper = CGGeneric("return true;\n")
   4265 
   4266        body.append(conditionsWrapper)
   4267        return body.define()
   4268 
   4269 
   4270 def StructuredCloneTag(name):
   4271    return "SCTAG_DOM_%s" % name.upper()
   4272 
   4273 
   4274 class CGSerializer(CGAbstractStaticMethod):
   4275    """
   4276    Implementation of serialization for things marked [Serializable].
   4277    This gets stored in our DOMJSClass, so it can be static.
   4278 
   4279    The caller is expected to pass in the object whose DOMJSClass it
   4280    used to get the serializer.
   4281    """
   4282 
   4283    def __init__(self, descriptor):
   4284        args = [
   4285            Argument("JSContext*", "aCx"),
   4286            Argument("JSStructuredCloneWriter*", "aWriter"),
   4287            Argument("JS::Handle<JSObject*>", "aObj"),
   4288        ]
   4289        CGAbstractStaticMethod.__init__(self, descriptor, "Serialize", "bool", args)
   4290 
   4291    def definition_body(self):
   4292        return fill(
   4293            """
   4294            MOZ_ASSERT(IsDOMObject(aObj), "Non-DOM object passed");
   4295            MOZ_ASSERT(GetDOMClass(aObj)->mSerializer == &Serialize,
   4296                       "Wrong object passed");
   4297            return JS_WriteUint32Pair(aWriter, ${tag}, 0) &&
   4298                   UnwrapDOMObject<${type}>(aObj)->WriteStructuredClone(aCx, aWriter);
   4299            """,
   4300            tag=StructuredCloneTag(self.descriptor.name),
   4301            type=self.descriptor.nativeType,
   4302        )
   4303 
   4304 
   4305 class CGDeserializer(CGAbstractMethod):
   4306    """
   4307    Implementation of deserialization for things marked [Serializable].
   4308    This will need to be accessed from WebIDLSerializable, so can't be static.
   4309    """
   4310 
   4311    def __init__(self, descriptor):
   4312        args = [
   4313            Argument("JSContext*", "aCx"),
   4314            Argument("nsIGlobalObject*", "aGlobal"),
   4315            Argument("JSStructuredCloneReader*", "aReader"),
   4316        ]
   4317        CGAbstractMethod.__init__(self, descriptor, "Deserialize", "JSObject*", args)
   4318 
   4319    def definition_body(self):
   4320        # WrapObject has different signatures depending on whether
   4321        # the object is wrappercached.
   4322        if self.descriptor.wrapperCache:
   4323            wrapCall = dedent(
   4324                """
   4325                result = obj->WrapObject(aCx, nullptr);
   4326                if (!result) {
   4327                  return nullptr;
   4328                }
   4329                """
   4330            )
   4331        else:
   4332            wrapCall = dedent(
   4333                """
   4334                if (!obj->WrapObject(aCx, nullptr, &result)) {
   4335                  return nullptr;
   4336                }
   4337                """
   4338            )
   4339 
   4340        return fill(
   4341            """
   4342            // Protect the result from a moving GC in ~RefPtr
   4343            JS::Rooted<JSObject*> result(aCx);
   4344            {  // Scope for the RefPtr
   4345              RefPtr<${type}> obj = ${type}::ReadStructuredClone(aCx, aGlobal, aReader);
   4346              if (!obj) {
   4347                return nullptr;
   4348              }
   4349              $*{wrapCall}
   4350            }
   4351            return result;
   4352            """,
   4353            type=self.descriptor.nativeType,
   4354            wrapCall=wrapCall,
   4355        )
   4356 
   4357 
   4358 def CreateBindingJSObject(descriptor):
   4359    objDecl = "BindingJSObjectCreator<%s> creator(aCx);\n" % descriptor.nativeType
   4360 
   4361    # We don't always need to root obj, but there are a variety
   4362    # of cases where we do, so for simplicity, just always root it.
   4363    if descriptor.proxy:
   4364        if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
   4365            assert not descriptor.isMaybeCrossOriginObject()
   4366            create = dedent(
   4367                """
   4368                aObject->mExpandoAndGeneration.expando.setUndefined();
   4369                JS::Rooted<JS::Value> expandoValue(aCx, JS::PrivateValue(&aObject->mExpandoAndGeneration));
   4370                creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
   4371                                          proto, /* aLazyProto = */ false, aObject,
   4372                                          expandoValue, aReflector);
   4373                """
   4374            )
   4375        else:
   4376            if descriptor.isMaybeCrossOriginObject():
   4377                proto = "nullptr"
   4378                lazyProto = "true"
   4379            else:
   4380                proto = "proto"
   4381                lazyProto = "false"
   4382            create = fill(
   4383                """
   4384                creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
   4385                                          ${proto}, /* aLazyProto = */ ${lazyProto},
   4386                                          aObject, JS::UndefinedHandleValue, aReflector);
   4387                """,
   4388                proto=proto,
   4389                lazyProto=lazyProto,
   4390            )
   4391    else:
   4392        create = dedent(
   4393            """
   4394            creator.CreateObject(aCx, sClass.ToJSClass(), proto, aObject, aReflector);
   4395            """
   4396        )
   4397    return (
   4398        objDecl
   4399        + create
   4400        + dedent(
   4401            """
   4402        if (!aReflector) {
   4403          return false;
   4404        }
   4405        """
   4406        )
   4407    )
   4408 
   4409 
   4410 def InitUnforgeablePropertiesOnHolder(
   4411    descriptor, properties, failureCode, holderName="unforgeableHolder"
   4412 ):
   4413    """
   4414    Define the unforgeable properties on the unforgeable holder for
   4415    the interface represented by descriptor.
   4416 
   4417    properties is a PropertyArrays instance.
   4418 
   4419    """
   4420    assert (
   4421        properties.unforgeableAttrs.hasNonChromeOnly()
   4422        or properties.unforgeableAttrs.hasChromeOnly()
   4423        or properties.unforgeableMethods.hasNonChromeOnly()
   4424        or properties.unforgeableMethods.hasChromeOnly()
   4425    )
   4426 
   4427    unforgeables = []
   4428 
   4429    defineUnforgeableAttrs = fill(
   4430        """
   4431        if (!DefineLegacyUnforgeableAttributes(aCx, ${holderName}, %s)) {
   4432          $*{failureCode}
   4433        }
   4434        """,
   4435        failureCode=failureCode,
   4436        holderName=holderName,
   4437    )
   4438    defineUnforgeableMethods = fill(
   4439        """
   4440        if (!DefineLegacyUnforgeableMethods(aCx, ${holderName}, %s)) {
   4441          $*{failureCode}
   4442        }
   4443        """,
   4444        failureCode=failureCode,
   4445        holderName=holderName,
   4446    )
   4447 
   4448    unforgeableMembers = [
   4449        (defineUnforgeableAttrs, properties.unforgeableAttrs),
   4450        (defineUnforgeableMethods, properties.unforgeableMethods),
   4451    ]
   4452    for template, array in unforgeableMembers:
   4453        if array.hasNonChromeOnly():
   4454            unforgeables.append(CGGeneric(template % array.variableName(False)))
   4455        if array.hasChromeOnly():
   4456            unforgeables.append(
   4457                CGIfWrapper(
   4458                    CGGeneric(template % array.variableName(True)),
   4459                    "nsContentUtils::ThreadsafeIsSystemCaller(aCx)",
   4460                )
   4461            )
   4462 
   4463    if descriptor.interface.getExtendedAttribute("LegacyUnforgeable"):
   4464        # We do our undefined toPrimitive here, not as a regular property
   4465        # because we don't have a concept of value props anywhere in IDL.
   4466        unforgeables.append(
   4467            CGGeneric(
   4468                fill(
   4469                    """
   4470            JS::Rooted<JS::PropertyKey> toPrimitive(aCx,
   4471              JS::GetWellKnownSymbolKey(aCx, JS::SymbolCode::toPrimitive));
   4472            if (!JS_DefinePropertyById(aCx, ${holderName}, toPrimitive,
   4473                                       JS::UndefinedHandleValue,
   4474                                       JSPROP_READONLY | JSPROP_PERMANENT)) {
   4475              $*{failureCode}
   4476            }
   4477            """,
   4478                    failureCode=failureCode,
   4479                    holderName=holderName,
   4480                )
   4481            )
   4482        )
   4483 
   4484    return CGWrapper(CGList(unforgeables), pre="\n")
   4485 
   4486 
   4487 def CopyUnforgeablePropertiesToInstance(descriptor, failureCode):
   4488    """
   4489    Copy the unforgeable properties from the unforgeable holder for
   4490    this interface to the instance object we have.
   4491    """
   4492    assert not descriptor.isGlobal()
   4493 
   4494    if not descriptor.hasLegacyUnforgeableMembers:
   4495        return ""
   4496 
   4497    copyCode = [
   4498        CGGeneric(
   4499            dedent(
   4500                """
   4501            // Important: do unforgeable property setup after we have handed
   4502            // over ownership of the C++ object to obj as needed, so that if
   4503            // we fail and it ends up GCed it won't have problems in the
   4504            // finalizer trying to drop its ownership of the C++ object.
   4505            """
   4506            )
   4507        )
   4508    ]
   4509 
   4510    # For proxies, we want to define on the expando object, not directly on the
   4511    # reflector, so we can make sure we don't get confused by named getters.
   4512    if descriptor.proxy:
   4513        copyCode.append(
   4514            CGGeneric(
   4515                fill(
   4516                    """
   4517            JS::Rooted<JSObject*> expando(aCx,
   4518              DOMProxyHandler::EnsureExpandoObject(aCx, aReflector));
   4519            if (!expando) {
   4520              $*{failureCode}
   4521            }
   4522            """,
   4523                    failureCode=failureCode,
   4524                )
   4525            )
   4526        )
   4527        obj = "expando"
   4528    else:
   4529        obj = "aReflector"
   4530 
   4531    copyCode.append(
   4532        CGGeneric(
   4533            fill(
   4534                """
   4535        JS::Rooted<JSObject*> unforgeableHolder(aCx,
   4536          &JS::GetReservedSlot(canonicalProto, DOM_INTERFACE_PROTO_SLOTS_BASE).toObject());
   4537        if (!JS_InitializePropertiesFromCompatibleNativeObject(aCx, ${obj}, unforgeableHolder)) {
   4538          $*{failureCode}
   4539        }
   4540        """,
   4541                obj=obj,
   4542                failureCode=failureCode,
   4543            )
   4544        )
   4545    )
   4546 
   4547    return CGWrapper(CGList(copyCode), pre="\n").define()
   4548 
   4549 
   4550 def AssertInheritanceChain(descriptor):
   4551    # We can skip the reinterpret_cast check for the descriptor's nativeType
   4552    # if aObject is a pointer of that type.
   4553    asserts = fill(
   4554        """
   4555        static_assert(std::is_same_v<decltype(aObject), ${nativeType}*>);
   4556        """,
   4557        nativeType=descriptor.nativeType,
   4558    )
   4559    iface = descriptor.interface
   4560    while iface.parent:
   4561        iface = iface.parent
   4562        desc = descriptor.getDescriptor(iface.identifier.name)
   4563        asserts += (
   4564            "MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
   4565            "           reinterpret_cast<%s*>(aObject),\n"
   4566            '           "Multiple inheritance for %s is broken.");\n'
   4567            % (desc.nativeType, desc.nativeType, desc.nativeType)
   4568        )
   4569    asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
   4570    return asserts
   4571 
   4572 
   4573 def InitMemberSlots(descriptor, failureCode):
   4574    """
   4575    Initialize member slots on our JS object if we're supposed to have some.
   4576 
   4577    Note that this is called after the SetWrapper() call in the
   4578    wrapperCache case, since that can affect how our getters behave
   4579    and we plan to invoke them here.  So if we fail, we need to
   4580    ClearWrapper.
   4581    """
   4582    if not descriptor.interface.hasMembersInSlots():
   4583        return ""
   4584    return fill(
   4585        """
   4586        if (!UpdateMemberSlots(aCx, aReflector, aObject)) {
   4587          $*{failureCode}
   4588        }
   4589        """,
   4590        failureCode=failureCode,
   4591    )
   4592 
   4593 
   4594 def DeclareProto(descriptor, noGivenProto=False):
   4595    """
   4596    Declare the canonicalProto and proto we have for our wrapping operation.
   4597    """
   4598    getCanonical = dedent(
   4599        """
   4600        JS::Handle<JSObject*> ${canonicalProto} = GetProtoObjectHandle(aCx);
   4601        if (!${canonicalProto}) {
   4602          return false;
   4603        }
   4604        """
   4605    )
   4606 
   4607    if noGivenProto:
   4608        return fill(getCanonical, canonicalProto="proto")
   4609 
   4610    getCanonical = fill(getCanonical, canonicalProto="canonicalProto")
   4611 
   4612    preamble = getCanonical + dedent(
   4613        """
   4614        JS::Rooted<JSObject*> proto(aCx);
   4615        """
   4616    )
   4617    if descriptor.isMaybeCrossOriginObject():
   4618        return preamble + dedent(
   4619            """
   4620            MOZ_ASSERT(!aGivenProto,
   4621                       "Shouldn't have constructors on cross-origin objects");
   4622            // Set proto to canonicalProto to avoid preserving our wrapper if
   4623            // we don't have to.
   4624            proto = canonicalProto;
   4625            """
   4626        )
   4627 
   4628    return preamble + dedent(
   4629        """
   4630        if (aGivenProto) {
   4631          proto = aGivenProto;
   4632          // Unfortunately, while aGivenProto was in the compartment of aCx
   4633          // coming in, we changed compartments to that of "parent" so may need
   4634          // to wrap the proto here.
   4635          if (js::GetContextCompartment(aCx) != JS::GetCompartment(proto)) {
   4636            if (!JS_WrapObject(aCx, &proto)) {
   4637              return false;
   4638            }
   4639          }
   4640        } else {
   4641          proto = canonicalProto;
   4642        }
   4643        """
   4644    )
   4645 
   4646 
   4647 class CGWrapWithCacheMethod(CGAbstractMethod):
   4648    """
   4649    Create a wrapper JSObject for a given native that implements nsWrapperCache.
   4650    """
   4651 
   4652    def __init__(self, descriptor):
   4653        assert descriptor.interface.hasInterfacePrototypeObject()
   4654        args = [
   4655            Argument("JSContext*", "aCx"),
   4656            Argument(descriptor.nativeType + "*", "aObject"),
   4657            Argument("nsWrapperCache*", "aCache"),
   4658            Argument("JS::Handle<JSObject*>", "aGivenProto"),
   4659            Argument("JS::MutableHandle<JSObject*>", "aReflector"),
   4660        ]
   4661        CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args)
   4662 
   4663    def definition_body(self):
   4664        failureCode = dedent(
   4665            """
   4666            aCache->ReleaseWrapper(aObject);
   4667            aCache->ClearWrapper();
   4668            return false;
   4669            """
   4670        )
   4671 
   4672        if self.descriptor.proxy:
   4673            finalize = "DOMProxyHandler::getInstance()->finalize"
   4674        else:
   4675            finalize = FINALIZE_HOOK_NAME
   4676 
   4677        return fill(
   4678            """
   4679            static_assert(!std::is_base_of_v<NonRefcountedDOMObject, ${nativeType}>,
   4680                          "Shouldn't have wrappercached things that are not refcounted.");
   4681            $*{assertInheritance}
   4682            MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));
   4683            MOZ_ASSERT(!aCache->GetWrapper(),
   4684                       "You should probably not be using Wrap() directly; use "
   4685                       "GetOrCreateDOMReflector instead");
   4686 
   4687            MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
   4688                       "nsISupports must be on our primary inheritance chain");
   4689 
   4690            // If the wrapper cache contains a dead reflector then finalize that
   4691            // now, ensuring that the finalizer for the old reflector always
   4692            // runs before the new reflector is created and attached. This
   4693            // avoids the awkward situation where there are multiple reflector
   4694            // objects that contain pointers to the same native.
   4695 
   4696            if (JSObject* oldReflector = aCache->GetWrapperMaybeDead()) {
   4697              ${finalize}(nullptr /* unused */, oldReflector);
   4698              MOZ_ASSERT(!aCache->GetWrapperMaybeDead());
   4699            }
   4700 
   4701            JS::Rooted<JSObject*> global(aCx, FindAssociatedGlobal(aCx, aObject->GetParentObject()));
   4702            if (!global) {
   4703              return false;
   4704            }
   4705            MOZ_ASSERT(JS_IsGlobalObject(global));
   4706            JS::AssertObjectIsNotGray(global);
   4707 
   4708            // That might have ended up wrapping us already, due to the wonders
   4709            // of XBL.  Check for that, and bail out as needed.
   4710            aReflector.set(aCache->GetWrapper());
   4711            if (aReflector) {
   4712            #ifdef DEBUG
   4713              AssertReflectorHasGivenProto(aCx, aReflector, aGivenProto);
   4714            #endif // DEBUG
   4715              return true;
   4716            }
   4717 
   4718            JSAutoRealm ar(aCx, global);
   4719            $*{declareProto}
   4720 
   4721            $*{createObject}
   4722 
   4723            aCache->SetWrapper(aReflector);
   4724            $*{unforgeable}
   4725            $*{slots}
   4726            creator.InitializationSucceeded();
   4727 
   4728            MOZ_ASSERT(aCache->GetWrapperPreserveColor() &&
   4729                       aCache->GetWrapperPreserveColor() == aReflector);
   4730            // If proto != canonicalProto, we have to preserve our wrapper;
   4731            // otherwise we won't be able to properly recreate it later, since
   4732            // we won't know what proto to use.  Note that we don't check
   4733            // aGivenProto here, since it's entirely possible (and even
   4734            // somewhat common) to have a non-null aGivenProto which is the
   4735            // same as canonicalProto.
   4736            if (proto != canonicalProto) {
   4737              PreserveWrapper(aObject);
   4738            }
   4739 
   4740            return true;
   4741            """,
   4742            nativeType=self.descriptor.nativeType,
   4743            assertInheritance=AssertInheritanceChain(self.descriptor),
   4744            declareProto=DeclareProto(self.descriptor),
   4745            createObject=CreateBindingJSObject(self.descriptor),
   4746            unforgeable=CopyUnforgeablePropertiesToInstance(
   4747                self.descriptor, failureCode
   4748            ),
   4749            slots=InitMemberSlots(self.descriptor, failureCode),
   4750            finalize=finalize,
   4751        )
   4752 
   4753 
   4754 class CGWrapMethod(CGAbstractMethod):
   4755    def __init__(self, descriptor):
   4756        # XXX can we wrap if we don't have an interface prototype object?
   4757        assert descriptor.interface.hasInterfacePrototypeObject()
   4758        args = [
   4759            Argument("JSContext*", "aCx"),
   4760            Argument("T*", "aObject"),
   4761            Argument("JS::Handle<JSObject*>", "aGivenProto"),
   4762        ]
   4763        CGAbstractMethod.__init__(
   4764            self,
   4765            descriptor,
   4766            "Wrap",
   4767            "JSObject*",
   4768            args,
   4769            inline=True,
   4770            templateArgs=["class T"],
   4771        )
   4772 
   4773    def definition_body(self):
   4774        return dedent(
   4775            """
   4776            JS::Rooted<JSObject*> reflector(aCx);
   4777            return Wrap(aCx, aObject, aObject, aGivenProto, &reflector) ? reflector.get() : nullptr;
   4778            """
   4779        )
   4780 
   4781 
   4782 class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
   4783    """
   4784    Create a wrapper JSObject for a given native that does not implement
   4785    nsWrapperCache.
   4786    """
   4787 
   4788    def __init__(self, descriptor, static=False, signatureOnly=False):
   4789        # XXX can we wrap if we don't have an interface prototype object?
   4790        assert descriptor.interface.hasInterfacePrototypeObject()
   4791        self.noGivenProto = (
   4792            descriptor.interface.isIteratorInterface()
   4793            or descriptor.interface.isAsyncIteratorInterface()
   4794        )
   4795        args = [
   4796            Argument("JSContext*", "aCx"),
   4797            Argument(descriptor.nativeType + "*", "aObject"),
   4798        ]
   4799        if not self.noGivenProto:
   4800            args.append(Argument("JS::Handle<JSObject*>", "aGivenProto"))
   4801        args.append(Argument("JS::MutableHandle<JSObject*>", "aReflector"))
   4802        CGAbstractMethod.__init__(
   4803            self,
   4804            descriptor,
   4805            "Wrap",
   4806            "bool",
   4807            args,
   4808            static=static,
   4809            signatureOnly=signatureOnly,
   4810        )
   4811 
   4812    def definition_body(self):
   4813        failureCode = "return false;\n"
   4814 
   4815        declareProto = DeclareProto(self.descriptor, noGivenProto=self.noGivenProto)
   4816        if self.noGivenProto:
   4817            assertGivenProto = ""
   4818        else:
   4819            assertGivenProto = dedent(
   4820                """
   4821                MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));
   4822                """
   4823            )
   4824        return fill(
   4825            """
   4826            $*{assertions}
   4827            $*{assertGivenProto}
   4828 
   4829            JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
   4830            $*{declareProto}
   4831 
   4832            $*{createObject}
   4833 
   4834            $*{unforgeable}
   4835 
   4836            $*{slots}
   4837 
   4838            creator.InitializationSucceeded();
   4839            return true;
   4840            """,
   4841            assertions=AssertInheritanceChain(self.descriptor),
   4842            assertGivenProto=assertGivenProto,
   4843            declareProto=declareProto,
   4844            createObject=CreateBindingJSObject(self.descriptor),
   4845            unforgeable=CopyUnforgeablePropertiesToInstance(
   4846                self.descriptor, failureCode
   4847            ),
   4848            slots=InitMemberSlots(self.descriptor, failureCode),
   4849        )
   4850 
   4851 
   4852 class CGWrapGlobalMethod(CGAbstractMethod):
   4853    """
   4854    Create a wrapper JSObject for a global.  The global must implement
   4855    nsWrapperCache.
   4856 
   4857    properties should be a PropertyArrays instance.
   4858    """
   4859 
   4860    def __init__(self, descriptor, properties):
   4861        assert (
   4862            descriptor.interface.hasInterfacePrototypeObject()
   4863            or descriptor.hasOrdinaryObjectPrototype()
   4864        )
   4865        args = [
   4866            Argument("JSContext*", "aCx"),
   4867            Argument(descriptor.nativeType + "*", "aObject"),
   4868            Argument("nsWrapperCache*", "aCache"),
   4869            Argument("JS::RealmOptions&", "aOptions"),
   4870            Argument("JSPrincipals*", "aPrincipal"),
   4871            Argument("JS::MutableHandle<JSObject*>", "aReflector"),
   4872        ]
   4873        CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args)
   4874        self.descriptor = descriptor
   4875        self.properties = properties
   4876 
   4877    def definition_body(self):
   4878        if self.properties.hasNonChromeOnly():
   4879            properties = "sNativeProperties.Upcast()"
   4880        else:
   4881            properties = "nullptr"
   4882        if self.properties.hasChromeOnly():
   4883            chromeProperties = "nsContentUtils::ThreadsafeIsSystemCaller(aCx) ? sChromeOnlyNativeProperties.Upcast() : nullptr"
   4884        else:
   4885            chromeProperties = "nullptr"
   4886 
   4887        failureCode = dedent(
   4888            """
   4889            aCache->ReleaseWrapper(aObject);
   4890            aCache->ClearWrapper();
   4891            return false;
   4892            """
   4893        )
   4894 
   4895        if self.descriptor.hasLegacyUnforgeableMembers:
   4896            unforgeable = InitUnforgeablePropertiesOnHolder(
   4897                self.descriptor, self.properties, failureCode, "aReflector"
   4898            ).define()
   4899        else:
   4900            unforgeable = ""
   4901 
   4902        if self.descriptor.hasOrdinaryObjectPrototype():
   4903            getProto = "JS::GetRealmObjectPrototypeHandle"
   4904        else:
   4905            getProto = "GetProtoObjectHandle"
   4906        return fill(
   4907            """
   4908            $*{assertions}
   4909            MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
   4910                       "nsISupports must be on our primary inheritance chain");
   4911 
   4912            if (!CreateGlobal<${nativeType}, ${getProto}>(aCx,
   4913                                             aObject,
   4914                                             aCache,
   4915                                             sClass.ToJSClass(),
   4916                                             aOptions,
   4917                                             aPrincipal,
   4918                                             aReflector)) {
   4919              $*{failureCode}
   4920            }
   4921 
   4922            // aReflector is a new global, so has a new realm.  Enter it
   4923            // before doing anything with it.
   4924            JSAutoRealm ar(aCx, aReflector);
   4925 
   4926            if (!DefineProperties(aCx, aReflector, ${properties}, ${chromeProperties})) {
   4927              $*{failureCode}
   4928            }
   4929            $*{unforgeable}
   4930 
   4931            $*{slots}
   4932 
   4933            return true;
   4934            """,
   4935            assertions=AssertInheritanceChain(self.descriptor),
   4936            nativeType=self.descriptor.nativeType,
   4937            getProto=getProto,
   4938            properties=properties,
   4939            chromeProperties=chromeProperties,
   4940            failureCode=failureCode,
   4941            unforgeable=unforgeable,
   4942            slots=InitMemberSlots(self.descriptor, failureCode),
   4943        )
   4944 
   4945 
   4946 class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod):
   4947    def __init__(self, descriptor):
   4948        args = [
   4949            Argument("JSContext*", "aCx"),
   4950            Argument("JS::Handle<JSObject*>", "aWrapper"),
   4951            Argument(descriptor.nativeType + "*", "aObject"),
   4952        ]
   4953        CGAbstractStaticMethod.__init__(
   4954            self, descriptor, "UpdateMemberSlots", "bool", args
   4955        )
   4956 
   4957    def definition_body(self):
   4958        body = "JS::Rooted<JS::Value> temp(aCx);\n" "JSJitGetterCallArgs args(&temp);\n"
   4959        for m in self.descriptor.interface.members:
   4960            if m.isAttr() and m.getExtendedAttribute("StoreInSlot"):
   4961                # Skip doing this for the "window" and "self" attributes on the
   4962                # Window interface, because those can't be gotten safely until
   4963                # we have hooked it up correctly to the outer window.  The
   4964                # window code handles doing the get itself.
   4965                if self.descriptor.interface.identifier.name == "Window" and (
   4966                    m.identifier.name == "window" or m.identifier.name == "self"
   4967                ):
   4968                    continue
   4969                body += fill(
   4970                    """
   4971 
   4972                    static_assert(${slot} < JS::shadow::Object::MAX_FIXED_SLOTS,
   4973                                  "Not enough fixed slots to fit '${interface}.${member}.  Ion's visitGetDOMMemberV/visitGetDOMMemberT assume StoreInSlot things are all in fixed slots.");
   4974                    if (!get_${member}(aCx, aWrapper, aObject, args)) {
   4975                      return false;
   4976                    }
   4977                    // Getter handled setting our reserved slots
   4978                    """,
   4979                    slot=memberReservedSlot(m, self.descriptor),
   4980                    interface=self.descriptor.interface.identifier.name,
   4981                    member=m.identifier.name,
   4982                )
   4983 
   4984        body += "\nreturn true;\n"
   4985        return body
   4986 
   4987 
   4988 class CGClearCachedValueMethod(CGAbstractMethod):
   4989    def __init__(self, descriptor, member):
   4990        self.member = member
   4991        # If we're StoreInSlot, we'll need to call the getter
   4992        if member.getExtendedAttribute("StoreInSlot"):
   4993            args = [Argument("JSContext*", "aCx")]
   4994            returnType = "bool"
   4995        else:
   4996            args = []
   4997            returnType = "void"
   4998        args.append(Argument(descriptor.nativeType + "*", "aObject"))
   4999        name = MakeClearCachedValueNativeName(member)
   5000        CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
   5001 
   5002    def definition_body(self):
   5003        slotIndex = memberReservedSlot(self.member, self.descriptor)
   5004        clearCachedValue = fill(
   5005            """
   5006            JS::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue());
   5007            """,
   5008            slotIndex=slotIndex,
   5009        )
   5010        if self.member.getExtendedAttribute("StoreInSlot"):
   5011            # We have to root things and save the old value in case
   5012            # regetting fails, so we can restore it.
   5013            declObj = "JS::Rooted<JSObject*> obj(aCx);\n"
   5014            noopRetval = " true"
   5015            saveMember = (
   5016                "JS::Rooted<JS::Value> oldValue(aCx, JS::GetReservedSlot(obj, %s));\n"
   5017                % slotIndex
   5018            )
   5019            regetMember = fill(
   5020                """
   5021                JS::Rooted<JS::Value> temp(aCx);
   5022                JSJitGetterCallArgs args(&temp);
   5023                JSAutoRealm ar(aCx, obj);
   5024                if (!get_${name}(aCx, obj, aObject, args)) {
   5025                  JS::SetReservedSlot(obj, ${slotIndex}, oldValue);
   5026                  return false;
   5027                }
   5028                return true;
   5029                """,
   5030                name=self.member.identifier.name,
   5031                slotIndex=slotIndex,
   5032            )
   5033        else:
   5034            declObj = "JSObject* obj;\n"
   5035            noopRetval = ""
   5036            saveMember = ""
   5037            if self.member.getExtendedAttribute(
   5038                "ReflectedHTMLAttributeReturningFrozenArray"
   5039            ):
   5040                clearCachedValue = fill(
   5041                    """
   5042                    ReflectedHTMLAttributeSlots::Clear(obj, ${arrayIndex});
   5043                    """,
   5044                    arrayIndex=reflectedHTMLAttributesArrayIndex(
   5045                        self.descriptor, self.member
   5046                    ),
   5047                )
   5048            regetMember = ""
   5049 
   5050        if self.descriptor.wantsXrays:
   5051            if self.member.getExtendedAttribute("StoreInSlot"):
   5052                cx = "JS::RootingContext::get(aCx)"
   5053            else:
   5054                cx = "RootingCx()"
   5055            if self.member.getExtendedAttribute(
   5056                "ReflectedHTMLAttributeReturningFrozenArray"
   5057            ):
   5058                clearXrayExpandoSlots = fill(
   5059                    """
   5060                    ReflectedHTMLAttributeSlots::ClearInXrays(${cx}, obj, ${arrayIndex});
   5061                    """,
   5062                    cx=cx,
   5063                    arrayIndex=reflectedHTMLAttributesArrayIndex(
   5064                        self.descriptor, self.member
   5065                    ),
   5066                )
   5067            else:
   5068                clearXrayExpandoSlots = fill(
   5069                    """
   5070                    ClearXrayExpandoSlots(${cx}, obj, ${xraySlotIndex});
   5071                    """,
   5072                    cx=cx,
   5073                    xraySlotIndex=memberXrayExpandoReservedSlot(
   5074                        self.member, self.descriptor
   5075                    ),
   5076                )
   5077        else:
   5078            clearXrayExpandoSlots = ""
   5079 
   5080        return fill(
   5081            """
   5082            $*{declObj}
   5083            obj = aObject->GetWrapper();
   5084            if (!obj) {
   5085              return${noopRetval};
   5086            }
   5087            $*{saveMember}
   5088            $*{clearCachedValue}
   5089            $*{clearXrayExpandoSlots}
   5090            $*{regetMember}
   5091            """,
   5092            declObj=declObj,
   5093            noopRetval=noopRetval,
   5094            saveMember=saveMember,
   5095            slotIndex=slotIndex,
   5096            clearCachedValue=clearCachedValue,
   5097            clearXrayExpandoSlots=clearXrayExpandoSlots,
   5098            regetMember=regetMember,
   5099        )
   5100 
   5101 
   5102 class CGCrossOriginProperties(CGThing):
   5103    def __init__(self, descriptor):
   5104        attrs = []
   5105        chromeOnlyAttrs = []
   5106        methods = []
   5107        chromeOnlyMethods = []
   5108        for m in descriptor.interface.members:
   5109            if m.isAttr() and (
   5110                m.getExtendedAttribute("CrossOriginReadable")
   5111                or m.getExtendedAttribute("CrossOriginWritable")
   5112            ):
   5113                if m.isStatic():
   5114                    raise TypeError(
   5115                        "Don't know how to deal with static method %s"
   5116                        % m.identifier.name
   5117                    )
   5118                if PropertyDefiner.getControllingCondition(
   5119                    m, descriptor
   5120                ).hasDisablers():
   5121                    raise TypeError(
   5122                        "Don't know how to deal with disabler for %s"
   5123                        % m.identifier.name
   5124                    )
   5125                if len(m.bindingAliases) > 0:
   5126                    raise TypeError(
   5127                        "Don't know how to deal with aliases for %s" % m.identifier.name
   5128                    )
   5129                if m.getExtendedAttribute("ChromeOnly") is not None:
   5130                    chromeOnlyAttrs.extend(AttrDefiner.attrData(m, overrideFlags="0"))
   5131                else:
   5132                    attrs.extend(AttrDefiner.attrData(m, overrideFlags="0"))
   5133            elif m.isMethod() and m.getExtendedAttribute("CrossOriginCallable"):
   5134                if m.isStatic():
   5135                    raise TypeError(
   5136                        "Don't know how to deal with static method %s"
   5137                        % m.identifier.name
   5138                    )
   5139                if PropertyDefiner.getControllingCondition(
   5140                    m, descriptor
   5141                ).hasDisablers():
   5142                    raise TypeError(
   5143                        "Don't know how to deal with disabler for %s"
   5144                        % m.identifier.name
   5145                    )
   5146                if len(m.aliases) > 0:
   5147                    raise TypeError(
   5148                        "Don't know how to deal with aliases for %s" % m.identifier.name
   5149                    )
   5150                if m.getExtendedAttribute("ChromeOnly") is not None:
   5151                    chromeOnlyMethods.append(
   5152                        MethodDefiner.methodData(
   5153                            m, descriptor, overrideFlags="JSPROP_READONLY"
   5154                        )
   5155                    )
   5156                else:
   5157                    methods.append(
   5158                        MethodDefiner.methodData(
   5159                            m, descriptor, overrideFlags="JSPROP_READONLY"
   5160                        )
   5161                    )
   5162 
   5163        if len(attrs) > 0:
   5164            self.attributeSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
   5165                attrs,
   5166                descriptor,
   5167                AttrDefiner.formatSpec,
   5168                "  JS_PS_END\n",
   5169                AttrDefiner.condition,
   5170                functools.partial(AttrDefiner.specData, crossOriginOnly=True),
   5171            )
   5172        else:
   5173            self.attributeSpecs = [" JS_PS_END\n"]
   5174        if len(methods) > 0:
   5175            self.methodSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
   5176                methods,
   5177                descriptor,
   5178                MethodDefiner.formatSpec,
   5179                "  JS_FS_END\n",
   5180                MethodDefiner.condition,
   5181                MethodDefiner.specData,
   5182            )
   5183        else:
   5184            self.methodSpecs = ["  JS_FS_END\n"]
   5185 
   5186        if len(chromeOnlyAttrs) > 0:
   5187            (
   5188                self.chromeOnlyAttributeSpecs,
   5189                _,
   5190            ) = PropertyDefiner.generatePrefableArrayValues(
   5191                chromeOnlyAttrs,
   5192                descriptor,
   5193                AttrDefiner.formatSpec,
   5194                "  JS_PS_END\n",
   5195                AttrDefiner.condition,
   5196                functools.partial(AttrDefiner.specData, crossOriginOnly=True),
   5197            )
   5198        else:
   5199            self.chromeOnlyAttributeSpecs = []
   5200        if len(chromeOnlyMethods) > 0:
   5201            self.chromeOnlyMethodSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
   5202                chromeOnlyMethods,
   5203                descriptor,
   5204                MethodDefiner.formatSpec,
   5205                "  JS_FS_END\n",
   5206                MethodDefiner.condition,
   5207                MethodDefiner.specData,
   5208            )
   5209        else:
   5210            self.chromeOnlyMethodSpecs = []
   5211 
   5212    def declare(self):
   5213        return dedent(
   5214            """
   5215            extern const CrossOriginProperties sCrossOriginProperties;
   5216            """
   5217        )
   5218 
   5219    def define(self):
   5220        def defineChromeOnly(name, specs, specType):
   5221            if len(specs) == 0:
   5222                return ("", "nullptr")
   5223            name = "sChromeOnlyCrossOrigin" + name
   5224            define = fill(
   5225                """
   5226                static const ${specType} ${name}[] = {
   5227                  $*{specs}
   5228                };
   5229                """,
   5230                specType=specType,
   5231                name=name,
   5232                specs=",\n".join(specs),
   5233            )
   5234            return (define, name)
   5235 
   5236        chromeOnlyAttributes = defineChromeOnly(
   5237            "Attributes", self.chromeOnlyAttributeSpecs, "JSPropertySpec"
   5238        )
   5239        chromeOnlyMethods = defineChromeOnly(
   5240            "Methods", self.chromeOnlyMethodSpecs, "JSFunctionSpec"
   5241        )
   5242        return fill(
   5243            """
   5244            static const JSPropertySpec sCrossOriginAttributes[] = {
   5245              $*{attributeSpecs}
   5246            };
   5247            static const JSFunctionSpec sCrossOriginMethods[] = {
   5248              $*{methodSpecs}
   5249            };
   5250            $*{chromeOnlyAttributeSpecs}
   5251            $*{chromeOnlyMethodSpecs}
   5252            const CrossOriginProperties sCrossOriginProperties = {
   5253              sCrossOriginAttributes,
   5254              sCrossOriginMethods,
   5255              ${chromeOnlyAttributes},
   5256              ${chromeOnlyMethods}
   5257            };
   5258            """,
   5259            attributeSpecs=",\n".join(self.attributeSpecs),
   5260            methodSpecs=",\n".join(self.methodSpecs),
   5261            chromeOnlyAttributeSpecs=chromeOnlyAttributes[0],
   5262            chromeOnlyMethodSpecs=chromeOnlyMethods[0],
   5263            chromeOnlyAttributes=chromeOnlyAttributes[1],
   5264            chromeOnlyMethods=chromeOnlyMethods[1],
   5265        )
   5266 
   5267 
   5268 class CGCycleCollectionTraverseForOwningUnionMethod(CGAbstractMethod):
   5269    """
   5270    ImplCycleCollectionUnlink for owning union type.
   5271    """
   5272 
   5273    def __init__(self, type):
   5274        self.type = type
   5275        args = [
   5276            Argument("nsCycleCollectionTraversalCallback&", "aCallback"),
   5277            Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion"),
   5278            Argument("const char*", "aName"),
   5279            Argument("uint32_t", "aFlags", "0"),
   5280        ]
   5281        CGAbstractMethod.__init__(
   5282            self, None, "ImplCycleCollectionTraverse", "void", args
   5283        )
   5284 
   5285    def deps(self):
   5286        return self.type.getDeps()
   5287 
   5288    def definition_body(self):
   5289        memberNames = [
   5290            getUnionMemberName(t)
   5291            for t in self.type.flatMemberTypes
   5292            if idlTypeNeedsCycleCollection(t)
   5293        ]
   5294        assert memberNames
   5295 
   5296        conditionTemplate = "aUnion.Is%s()"
   5297        functionCallTemplate = (
   5298            'ImplCycleCollectionTraverse(aCallback, aUnion.GetAs%s(), "m%s", aFlags);\n'
   5299        )
   5300 
   5301        ifStaments = (
   5302            CGIfWrapper(CGGeneric(functionCallTemplate % (m, m)), conditionTemplate % m)
   5303            for m in memberNames
   5304        )
   5305 
   5306        return CGElseChain(ifStaments).define()
   5307 
   5308 
   5309 class CGCycleCollectionUnlinkForOwningUnionMethod(CGAbstractMethod):
   5310    """
   5311    ImplCycleCollectionUnlink for owning union type.
   5312    """
   5313 
   5314    def __init__(self, type):
   5315        self.type = type
   5316        args = [Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion")]
   5317        CGAbstractMethod.__init__(self, None, "ImplCycleCollectionUnlink", "void", args)
   5318 
   5319    def deps(self):
   5320        return self.type.getDeps()
   5321 
   5322    def definition_body(self):
   5323        return "aUnion.Uninit();\n"
   5324 
   5325 
   5326 builtinNames = {
   5327    IDLType.Tags.bool: "bool",
   5328    IDLType.Tags.int8: "int8_t",
   5329    IDLType.Tags.int16: "int16_t",
   5330    IDLType.Tags.int32: "int32_t",
   5331    IDLType.Tags.int64: "int64_t",
   5332    IDLType.Tags.uint8: "uint8_t",
   5333    IDLType.Tags.uint16: "uint16_t",
   5334    IDLType.Tags.uint32: "uint32_t",
   5335    IDLType.Tags.uint64: "uint64_t",
   5336    IDLType.Tags.unrestricted_float: "float",
   5337    IDLType.Tags.float: "float",
   5338    IDLType.Tags.unrestricted_double: "double",
   5339    IDLType.Tags.double: "double",
   5340 }
   5341 
   5342 numericSuffixes = {
   5343    IDLType.Tags.int8: "",
   5344    IDLType.Tags.uint8: "",
   5345    IDLType.Tags.int16: "",
   5346    IDLType.Tags.uint16: "",
   5347    IDLType.Tags.int32: "",
   5348    IDLType.Tags.uint32: "U",
   5349    IDLType.Tags.int64: "LL",
   5350    IDLType.Tags.uint64: "ULL",
   5351    IDLType.Tags.unrestricted_float: "F",
   5352    IDLType.Tags.float: "F",
   5353    IDLType.Tags.unrestricted_double: "",
   5354    IDLType.Tags.double: "",
   5355 }
   5356 
   5357 
   5358 def numericValue(t, v):
   5359    if t == IDLType.Tags.unrestricted_double or t == IDLType.Tags.unrestricted_float:
   5360        typeName = builtinNames[t]
   5361        if v == float("inf"):
   5362            return "mozilla::PositiveInfinity<%s>()" % typeName
   5363        if v == float("-inf"):
   5364            return "mozilla::NegativeInfinity<%s>()" % typeName
   5365        if math.isnan(v):
   5366            return "mozilla::UnspecifiedNaN<%s>()" % typeName
   5367    return "%s%s" % (v, numericSuffixes[t])
   5368 
   5369 
   5370 class CastableObjectUnwrapper:
   5371    """
   5372    A class for unwrapping an object stored in a JS Value (or
   5373    MutableHandle<Value> or Handle<Value>) named by the "source" and
   5374    "mutableSource" arguments based on the passed-in descriptor and storing it
   5375    in a variable called by the name in the "target" argument.  The "source"
   5376    argument should be able to produce a Value or Handle<Value>; the
   5377    "mutableSource" argument should be able to produce a MutableHandle<Value>
   5378 
   5379    codeOnFailure is the code to run if unwrapping fails.
   5380 
   5381    If isCallbackReturnValue is "JSImpl" and our descriptor is also
   5382    JS-implemented, fall back to just creating the right object if what we
   5383    have isn't one already.
   5384    """
   5385 
   5386    def __init__(
   5387        self,
   5388        descriptor,
   5389        source,
   5390        mutableSource,
   5391        target,
   5392        codeOnFailure,
   5393        exceptionCode=None,
   5394        isCallbackReturnValue=False,
   5395    ):
   5396        self.substitution = {
   5397            "type": descriptor.nativeType,
   5398            "protoID": "prototypes::id::" + descriptor.name,
   5399            "target": target,
   5400            "codeOnFailure": codeOnFailure,
   5401            "source": source,
   5402            "mutableSource": mutableSource,
   5403        }
   5404 
   5405        if isCallbackReturnValue == "JSImpl" and descriptor.interface.isJSImplemented():
   5406            exceptionCode = exceptionCode or codeOnFailure
   5407            self.substitution["codeOnFailure"] = fill(
   5408                """
   5409                // Be careful to not wrap random DOM objects here, even if
   5410                // they're wrapped in opaque security wrappers for some reason.
   5411                // XXXbz Wish we could check for a JS-implemented object
   5412                // that already has a content reflection...
   5413                if (!IsDOMObject(js::UncheckedUnwrap(&${source}.toObject()))) {
   5414                  nsCOMPtr<nsIGlobalObject> contentGlobal;
   5415                  JS::Rooted<JSObject*> callback(cx, CallbackOrNull());
   5416                  if (!callback ||
   5417                      !GetContentGlobalForJSImplementedObject(cx, callback, getter_AddRefs(contentGlobal))) {
   5418                    $*{exceptionCode}
   5419                  }
   5420                  JS::Rooted<JSObject*> jsImplSourceObj(cx, &${source}.toObject());
   5421                  MOZ_RELEASE_ASSERT(!js::IsWrapper(jsImplSourceObj),
   5422                                     "Don't return JS implementations from other compartments");
   5423                  JS::Rooted<JSObject*> jsImplSourceGlobal(cx, JS::GetNonCCWObjectGlobal(jsImplSourceObj));
   5424                  ${target} = new ${type}(jsImplSourceObj, jsImplSourceGlobal, contentGlobal);
   5425                } else {
   5426                  $*{codeOnFailure}
   5427                }
   5428                """,
   5429                exceptionCode=exceptionCode,
   5430                **self.substitution,
   5431            )
   5432        else:
   5433            self.substitution["codeOnFailure"] = codeOnFailure
   5434 
   5435    def __str__(self):
   5436        substitution = self.substitution.copy()
   5437        substitution["codeOnFailure"] %= {
   5438            "securityError": "rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO"
   5439        }
   5440        return fill(
   5441            """
   5442            {
   5443              // Our JSContext should be in the right global to do unwrapping in.
   5444              nsresult rv = UnwrapObject<${protoID}, ${type}>(${mutableSource}, ${target}, cx);
   5445              if (NS_FAILED(rv)) {
   5446                $*{codeOnFailure}
   5447              }
   5448            }
   5449            """,
   5450            **substitution,
   5451        )
   5452 
   5453 
   5454 class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
   5455    """
   5456    As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
   5457    """
   5458 
   5459    def __init__(
   5460        self,
   5461        descriptor,
   5462        source,
   5463        mutableSource,
   5464        target,
   5465        exceptionCode,
   5466        isCallbackReturnValue,
   5467        sourceDescription,
   5468    ):
   5469        CastableObjectUnwrapper.__init__(
   5470            self,
   5471            descriptor,
   5472            source,
   5473            mutableSource,
   5474            target,
   5475            'cx.ThrowErrorMessage<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("%s", "%s");\n'
   5476            "%s"
   5477            % (sourceDescription, descriptor.interface.identifier.name, exceptionCode),
   5478            exceptionCode,
   5479            isCallbackReturnValue,
   5480        )
   5481 
   5482 
   5483 def getCallbackConversionInfo(
   5484    type, idlObject, isMember, isCallbackReturnValue, isOptional
   5485 ):
   5486    """
   5487    Returns a tuple containing the declType, declArgs, and basic
   5488    conversion for the given callback type, with the given callback
   5489    idl object in the given context (isMember/isCallbackReturnValue/isOptional).
   5490    """
   5491    name = idlObject.identifier.name
   5492 
   5493    # We can't use fast callbacks if isOptional because then we get an
   5494    # Optional<RootedCallback> thing, which is not transparent to consumers.
   5495    useFastCallback = (
   5496        (not isMember or isMember == "Union")
   5497        and not isCallbackReturnValue
   5498        and not isOptional
   5499    )
   5500    if useFastCallback:
   5501        name = "binding_detail::Fast%s" % name
   5502        rootArgs = ""
   5503        args = "&${val}.toObject(), JS::CurrentGlobalOrNull(cx)"
   5504    else:
   5505        rootArgs = dedent(
   5506            """
   5507            JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject());
   5508            JS::Rooted<JSObject*> tempGlobalRoot(cx, JS::CurrentGlobalOrNull(cx));
   5509            """
   5510        )
   5511        args = "cx, tempRoot, tempGlobalRoot, GetIncumbentGlobal()"
   5512 
   5513    if type.nullable() or isCallbackReturnValue:
   5514        declType = CGGeneric("RefPtr<%s>" % name)
   5515    else:
   5516        declType = CGGeneric("OwningNonNull<%s>" % name)
   5517 
   5518    if useFastCallback:
   5519        declType = CGTemplatedType("RootedCallback", declType)
   5520        declArgs = "cx"
   5521    else:
   5522        declArgs = None
   5523 
   5524    conversion = fill(
   5525        """
   5526        { // scope for tempRoot and tempGlobalRoot if needed
   5527          $*{rootArgs}
   5528          $${declName} = new ${name}(${args});
   5529        }
   5530        """,
   5531        rootArgs=rootArgs,
   5532        name=name,
   5533        args=args,
   5534    )
   5535    return (declType, declArgs, conversion)
   5536 
   5537 
   5538 class JSToNativeConversionInfo:
   5539    """
   5540    An object representing information about a JS-to-native conversion.
   5541    """
   5542 
   5543    def __init__(
   5544        self,
   5545        template,
   5546        declType=None,
   5547        holderType=None,
   5548        dealWithOptional=False,
   5549        declArgs=None,
   5550        holderArgs=None,
   5551    ):
   5552        """
   5553        template: A string representing the conversion code.  This will have
   5554                  template substitution performed on it as follows:
   5555 
   5556          ${val} is a handle to the JS::Value in question
   5557          ${maybeMutableVal} May be a mutable handle to the JS::Value in
   5558                             question. This is only OK to use if ${val} is
   5559                             known to not be undefined.
   5560          ${holderName} replaced by the holder's name, if any
   5561          ${declName} replaced by the declaration's name
   5562          ${haveValue} replaced by an expression that evaluates to a boolean
   5563                       for whether we have a JS::Value.  Only used when
   5564                       defaultValue is not None or when True is passed for
   5565                       checkForValue to instantiateJSToNativeConversion.
   5566                       This expression may not be already-parenthesized, so if
   5567                       you use it with && or || make sure to put parens
   5568                       around it.
   5569          ${passedToJSImpl} replaced by an expression that evaluates to a boolean
   5570                            for whether this value is being passed to a JS-
   5571                            implemented interface.
   5572 
   5573        declType: A CGThing representing the native C++ type we're converting
   5574                  to.  This is allowed to be None if the conversion code is
   5575                  supposed to be used as-is.
   5576 
   5577        holderType: A CGThing representing the type of a "holder" which will
   5578                    hold a possible reference to the C++ thing whose type we
   5579                    returned in declType, or  None if no such holder is needed.
   5580 
   5581        dealWithOptional: A boolean indicating whether the caller has to do
   5582                          optional-argument handling.  This should only be set
   5583                          to true if the JS-to-native conversion is being done
   5584                          for an optional argument or dictionary member with no
   5585                          default value and if the returned template expects
   5586                          both declType and holderType to be wrapped in
   5587                          Optional<>, with ${declName} and ${holderName}
   5588                          adjusted to point to the Value() of the Optional, and
   5589                          Construct() calls to be made on the Optional<>s as
   5590                          needed.
   5591 
   5592        declArgs: If not None, the arguments to pass to the ${declName}
   5593                  constructor.  These will have template substitution performed
   5594                  on them so you can use things like ${val}.  This is a
   5595                  single string, not a list of strings.
   5596 
   5597        holderArgs: If not None, the arguments to pass to the ${holderName}
   5598                    constructor.  These will have template substitution
   5599                    performed on them so you can use things like ${val}.
   5600                    This is a single string, not a list of strings.
   5601 
   5602        ${declName} must be in scope before the code from 'template' is entered.
   5603 
   5604        If holderType is not None then ${holderName} must be in scope before
   5605        the code from 'template' is entered.
   5606        """
   5607        assert isinstance(template, str)
   5608        assert declType is None or isinstance(declType, CGThing)
   5609        assert holderType is None or isinstance(holderType, CGThing)
   5610        self.template = template
   5611        self.declType = declType
   5612        self.holderType = holderType
   5613        self.dealWithOptional = dealWithOptional
   5614        self.declArgs = declArgs
   5615        self.holderArgs = holderArgs
   5616 
   5617 
   5618 def getHandleDefault(defaultValue):
   5619    tag = defaultValue.type.tag()
   5620    if tag in numericSuffixes:
   5621        # Some numeric literals require a suffix to compile without warnings
   5622        return numericValue(tag, defaultValue.value)
   5623    assert tag == IDLType.Tags.bool
   5624    return toStringBool(defaultValue.value)
   5625 
   5626 
   5627 def handleDefaultStringValue(defaultValue, method):
   5628    """
   5629    Returns a string which ends up calling 'method' with a (char_t*, length)
   5630    pair that sets this string default value.  This string is suitable for
   5631    passing as the second argument of handleDefault.
   5632    """
   5633    assert (
   5634        defaultValue.type.isDOMString()
   5635        or defaultValue.type.isUSVString()
   5636        or defaultValue.type.isUTF8String()
   5637        or defaultValue.type.isByteString()
   5638    )
   5639    # There shouldn't be any non-ASCII or embedded nulls in here; if
   5640    # it ever sneaks in we will need to think about how to properly
   5641    # represent that in the C++.
   5642    assert all(ord(c) < 128 and ord(c) > 0 for c in defaultValue.value)
   5643    if defaultValue.type.isByteString() or defaultValue.type.isUTF8String():
   5644        prefix = ""
   5645    else:
   5646        prefix = "u"
   5647    return fill(
   5648        """
   5649        ${method}(${prefix}"${value}");
   5650        """,
   5651        method=method,
   5652        prefix=prefix,
   5653        value=defaultValue.value,
   5654    )
   5655 
   5656 
   5657 def recordKeyType(recordType):
   5658    assert recordType.keyType.isString()
   5659    if recordType.keyType.isByteString() or recordType.keyType.isUTF8String():
   5660        return "nsCString"
   5661    return "nsString"
   5662 
   5663 
   5664 def recordKeyDeclType(recordType):
   5665    return CGGeneric(recordKeyType(recordType))
   5666 
   5667 
   5668 def initializerForType(type):
   5669    """
   5670    Get the right initializer for the given type for a data location where we
   5671    plan to then initialize it from a JS::Value.  Some types need to always be
   5672    initialized even before we start the JS::Value-to-IDL-value conversion.
   5673 
   5674    Returns a string or None if no initialization is needed.
   5675    """
   5676    if type.isObject():
   5677        return "nullptr"
   5678    # We could probably return CGDictionary.getNonInitializingCtorArg() for the
   5679    # dictionary case, but code outside DictionaryBase subclasses can't use
   5680    # that, so we can't do it across the board.
   5681    return None
   5682 
   5683 
   5684 # If this function is modified, modify CGNativeMember.getArg and
   5685 # CGNativeMember.getRetvalInfo accordingly.  The latter cares about the decltype
   5686 # and holdertype we end up using, because it needs to be able to return the code
   5687 # that will convert those to the actual return value of the callback function.
   5688 def getJSToNativeConversionInfo(
   5689    type,
   5690    descriptorProvider,
   5691    failureCode=None,
   5692    isDefinitelyObject=False,
   5693    isMember=False,
   5694    isOptional=False,
   5695    invalidEnumValueFatal=True,
   5696    defaultValue=None,
   5697    isNullOrUndefined=False,
   5698    isKnownMissing=False,
   5699    exceptionCode=None,
   5700    lenientFloatCode=None,
   5701    allowTreatNonCallableAsNull=False,
   5702    isCallbackReturnValue=False,
   5703    sourceDescription="value",
   5704    nestingLevel="",
   5705 ):
   5706    """
   5707    Get a template for converting a JS value to a native object based on the
   5708    given type and descriptor.  If failureCode is given, then we're actually
   5709    testing whether we can convert the argument to the desired type.  That
   5710    means that failures to convert due to the JS value being the wrong type of
   5711    value need to use failureCode instead of throwing exceptions.  Failures to
   5712    convert that are due to JS exceptions (from toString or valueOf methods) or
   5713    out of memory conditions need to throw exceptions no matter what
   5714    failureCode is.  However what actually happens when throwing an exception
   5715    can be controlled by exceptionCode.  The only requirement on that is that
   5716    exceptionCode must end up doing a return, and every return from this
   5717    function must happen via exceptionCode if exceptionCode is not None.
   5718 
   5719    If isDefinitelyObject is True, that means we have a value and the value
   5720    tests true for isObject(), so we have no need to recheck that.
   5721 
   5722    If isNullOrUndefined is True, that means we have a value and the value
   5723    tests true for isNullOrUndefined(), so we have no need to recheck that.
   5724 
   5725    If isKnownMissing is True, that means that we are known-missing, and for
   5726    cases when we have a default value we only need to output the default value.
   5727 
   5728    if isMember is not False, we're being converted from a property of some JS
   5729    object, not from an actual method argument, so we can't rely on our jsval
   5730    being rooted or outliving us in any way.  Callers can pass "Dictionary",
   5731    "Variadic", "Sequence", "Union", or "OwningUnion" to indicate that the conversion
   5732    is for something that is a dictionary member, a variadic argument, a sequence,
   5733    an union, or an owning union respectively.
   5734    XXX Once we swtich *Rooter to Rooted* for Record and Sequence type entirely,
   5735        we could remove "Union" from isMember.
   5736 
   5737    If isOptional is true, then we are doing conversion of an optional
   5738    argument with no default value.
   5739 
   5740    invalidEnumValueFatal controls whether an invalid enum value conversion
   5741    attempt will throw (if true) or simply return without doing anything (if
   5742    false).
   5743 
   5744    If defaultValue is not None, it's the IDL default value for this conversion
   5745 
   5746    If isEnforceRange is true, we're converting an integer and throwing if the
   5747    value is out of range.
   5748 
   5749    If isClamp is true, we're converting an integer and clamping if the
   5750    value is out of range.
   5751 
   5752    If isAllowShared is false, we're converting a buffer source and throwing if
   5753    it is a SharedArrayBuffer or backed by a SharedArrayBuffer.
   5754 
   5755    If lenientFloatCode is not None, it should be used in cases when
   5756    we're a non-finite float that's not unrestricted.
   5757 
   5758    If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and
   5759    [LegacyTreatNonObjectAsNull] extended attributes on nullable callback functions
   5760    will be honored.
   5761 
   5762    If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be
   5763    adjusted to make it easier to return from a callback.  Since that type is
   5764    never directly observable by any consumers of the callback code, this is OK.
   5765    Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior
   5766    of the FailureFatalCastableObjectUnwrapper conversion; this is used for
   5767    implementing auto-wrapping of JS-implemented return values from a
   5768    JS-implemented interface.
   5769 
   5770    sourceDescription is a description of what this JS value represents, to be
   5771    used in error reporting.  Callers should assume that it might get placed in
   5772    the middle of a sentence.  If it ends up at the beginning of a sentence, its
   5773    first character will be automatically uppercased.
   5774 
   5775    The return value from this function is a JSToNativeConversionInfo.
   5776    """
   5777    # If we have a defaultValue then we're not actually optional for
   5778    # purposes of what we need to be declared as.
   5779    assert defaultValue is None or not isOptional
   5780 
   5781    # Also, we should not have a defaultValue if we know we're an object
   5782    assert not isDefinitelyObject or defaultValue is None
   5783 
   5784    # And we can't both be an object and be null or undefined
   5785    assert not isDefinitelyObject or not isNullOrUndefined
   5786 
   5787    isClamp = type.hasClamp()
   5788    isEnforceRange = type.hasEnforceRange()
   5789    isAllowShared = type.hasAllowShared()
   5790 
   5791    # If exceptionCode is not set, we'll just rethrow the exception we got.
   5792    # Note that we can't just set failureCode to exceptionCode, because setting
   5793    # failureCode will prevent pending exceptions from being set in cases when
   5794    # they really should be!
   5795    if exceptionCode is None:
   5796        exceptionCode = "return false;\n"
   5797 
   5798    # Unfortunately, .capitalize() on a string will lowercase things inside the
   5799    # string, which we do not want.
   5800    def firstCap(string):
   5801        return string[0].upper() + string[1:]
   5802 
   5803    # Helper functions for dealing with failures due to the JS value being the
   5804    # wrong type of value
   5805    def onFailureNotAnObject(failureCode):
   5806        return CGGeneric(
   5807            failureCode
   5808            or (
   5809                'cx.ThrowErrorMessage<MSG_NOT_OBJECT>("%s");\n'
   5810                "%s" % (firstCap(sourceDescription), exceptionCode)
   5811            )
   5812        )
   5813 
   5814    def onFailureBadType(failureCode, typeName):
   5815        return CGGeneric(
   5816            failureCode
   5817            or (
   5818                'cx.ThrowErrorMessage<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("%s", "%s");\n'
   5819                "%s" % (firstCap(sourceDescription), typeName, exceptionCode)
   5820            )
   5821        )
   5822 
   5823    # It's a failure in the committed-to conversion, not a failure to match up
   5824    # to a type, so we don't want to use failureCode in here. We want to just
   5825    # throw an exception unconditionally.
   5826    def onFailureIsShared():
   5827        return CGGeneric(
   5828            'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_SHARED>("%s");\n'
   5829            "%s" % (firstCap(sourceDescription), exceptionCode)
   5830        )
   5831 
   5832    def onFailureIsLarge():
   5833        return CGGeneric(
   5834            'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_LARGE>("%s");\n'
   5835            "%s" % (firstCap(sourceDescription), exceptionCode)
   5836        )
   5837 
   5838    def onFailureIsResizable():
   5839        return CGGeneric(
   5840            'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_RESIZABLE>("%s");\n'
   5841            "%s" % (firstCap(sourceDescription), exceptionCode)
   5842        )
   5843 
   5844    def onFailureIsImmutable():
   5845        desc = firstCap(sourceDescription)
   5846        return CGGeneric(
   5847            f'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_IMMUTABLE>("{desc}");\n'
   5848            f"{exceptionCode}"
   5849        )
   5850 
   5851    def onFailureNotCallable(failureCode):
   5852        return CGGeneric(
   5853            failureCode
   5854            or (
   5855                'cx.ThrowErrorMessage<MSG_NOT_CALLABLE>("%s");\n'
   5856                "%s" % (firstCap(sourceDescription), exceptionCode)
   5857            )
   5858        )
   5859 
   5860    # A helper function for handling default values.  Takes a template
   5861    # body and the C++ code to set the default value and wraps the
   5862    # given template body in handling for the default value.
   5863    def handleDefault(template, setDefault):
   5864        if defaultValue is None:
   5865            return template
   5866        if isKnownMissing:
   5867            return fill(
   5868                """
   5869                {
   5870                  // scope for any temporaries our default value setting needs.
   5871                  $*{setDefault}
   5872                }
   5873                """,
   5874                setDefault=setDefault,
   5875            )
   5876        return fill(
   5877            """
   5878            if ($${haveValue}) {
   5879              $*{templateBody}
   5880            } else {
   5881              $*{setDefault}
   5882            }
   5883            """,
   5884            templateBody=template,
   5885            setDefault=setDefault,
   5886        )
   5887 
   5888    # A helper function for wrapping up the template body for
   5889    # possibly-nullable objecty stuff
   5890    def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None):
   5891        if isNullOrUndefined and type.nullable():
   5892            # Just ignore templateBody and set ourselves to null.
   5893            # Note that we don't have to worry about default values
   5894            # here either, since we already examined this value.
   5895            return codeToSetNull
   5896 
   5897        if not isDefinitelyObject:
   5898            # Handle the non-object cases by wrapping up the whole
   5899            # thing in an if cascade.
   5900            if type.nullable():
   5901                elifLine = "} else if (${val}.isNullOrUndefined()) {\n"
   5902                elifBody = codeToSetNull
   5903            else:
   5904                elifLine = ""
   5905                elifBody = ""
   5906 
   5907            # Note that $${val} below expands to ${val}. This string is
   5908            # used as a template later, and val will be filled in then.
   5909            templateBody = fill(
   5910                """
   5911                if ($${val}.isObject()) {
   5912                  $*{templateBody}
   5913                $*{elifLine}
   5914                  $*{elifBody}
   5915                } else {
   5916                  $*{failureBody}
   5917                }
   5918                """,
   5919                templateBody=templateBody,
   5920                elifLine=elifLine,
   5921                elifBody=elifBody,
   5922                failureBody=onFailureNotAnObject(failureCode).define(),
   5923            )
   5924 
   5925            if isinstance(defaultValue, IDLNullValue):
   5926                assert type.nullable()  # Parser should enforce this
   5927                templateBody = handleDefault(templateBody, codeToSetNull)
   5928            elif isinstance(defaultValue, IDLEmptySequenceValue):
   5929                # Our caller will handle it
   5930                pass
   5931            else:
   5932                assert defaultValue is None
   5933 
   5934        return templateBody
   5935 
   5936    # A helper function for converting things that look like a JSObject*.
   5937    def handleJSObjectType(
   5938        type, isMember, failureCode, exceptionCode, sourceDescription
   5939    ):
   5940        if not isMember or isMember == "Union":
   5941            if isOptional:
   5942                # We have a specialization of Optional that will use a
   5943                # Rooted for the storage here.
   5944                declType = CGGeneric("JS::Handle<JSObject*>")
   5945            else:
   5946                declType = CGGeneric("JS::Rooted<JSObject*>")
   5947            declArgs = "cx"
   5948        else:
   5949            assert isMember in (
   5950                "Sequence",
   5951                "Variadic",
   5952                "Dictionary",
   5953                "OwningUnion",
   5954                "Record",
   5955            )
   5956            # We'll get traced by the sequence or dictionary or union tracer
   5957            declType = CGGeneric("JSObject*")
   5958            declArgs = None
   5959        templateBody = "${declName} = &${val}.toObject();\n"
   5960 
   5961        # For JS-implemented APIs, we refuse to allow passing objects that the
   5962        # API consumer does not subsume. The extra parens around
   5963        # ($${passedToJSImpl}) suppress unreachable code warnings when
   5964        # $${passedToJSImpl} is the literal `false`.  But Apple is shipping a
   5965        # buggy clang (clang 3.9) in Xcode 8.3, so there even the parens are not
   5966        # enough.  So we manually disable some warnings in clang.
   5967        if (
   5968            not isinstance(descriptorProvider, Descriptor)
   5969            or descriptorProvider.interface.isJSImplemented()
   5970        ):
   5971            templateBody = (
   5972                fill(
   5973                    """
   5974                #ifdef __clang__
   5975                #pragma clang diagnostic push
   5976                #pragma clang diagnostic ignored "-Wunreachable-code"
   5977                #pragma clang diagnostic ignored "-Wunreachable-code-return"
   5978                #endif // __clang__
   5979                if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
   5980                  cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}");
   5981                  $*{exceptionCode}
   5982                }
   5983                #ifdef __clang__
   5984                #pragma clang diagnostic pop
   5985                #endif // __clang__
   5986                """,
   5987                    sourceDescription=sourceDescription,
   5988                    exceptionCode=exceptionCode,
   5989                )
   5990                + templateBody
   5991            )
   5992 
   5993        setToNullCode = "${declName} = nullptr;\n"
   5994        template = wrapObjectTemplate(templateBody, type, setToNullCode, failureCode)
   5995        return JSToNativeConversionInfo(
   5996            template, declType=declType, dealWithOptional=isOptional, declArgs=declArgs
   5997        )
   5998 
   5999    def incrementNestingLevel():
   6000        if nestingLevel == "":
   6001            return 1
   6002        return nestingLevel + 1
   6003 
   6004    assert not (isEnforceRange and isClamp)  # These are mutually exclusive
   6005 
   6006    if type.isSequence() or type.isObservableArray():
   6007        assert not isEnforceRange and not isClamp and not isAllowShared
   6008 
   6009        if failureCode is None:
   6010            notSequence = (
   6011                'cx.ThrowErrorMessage<MSG_CONVERSION_ERROR>("%s", "%s");\n'
   6012                "%s"
   6013                % (
   6014                    firstCap(sourceDescription),
   6015                    "sequence" if type.isSequence() else "observable array",
   6016                    exceptionCode,
   6017                )
   6018            )
   6019        else:
   6020            notSequence = failureCode
   6021 
   6022        nullable = type.nullable()
   6023        # Be very careful not to change "type": we need it later
   6024        if nullable:
   6025            elementType = type.inner.inner
   6026        else:
   6027            elementType = type.inner
   6028 
   6029        # We want to use auto arrays if we can, but we have to be careful with
   6030        # reallocation behavior for arrays.  In particular, if we use auto
   6031        # arrays for sequences and have a sequence of elements which are
   6032        # themselves sequences or have sequences as members, we have a problem.
   6033        # In that case, resizing the outermost AutoTArray to the right size
   6034        # will memmove its elements, but AutoTArrays are not memmovable and
   6035        # hence will end up with pointers to bogus memory, which is bad.  To
   6036        # deal with this, we typically map WebIDL sequences to our Sequence
   6037        # type, which is in fact memmovable.  The one exception is when we're
   6038        # passing in a sequence directly as an argument without any sort of
   6039        # optional or nullable complexity going on.  In that situation, we can
   6040        # use an AutoSequence instead.  We have to keep using Sequence in the
   6041        # nullable and optional cases because we don't want to leak the
   6042        # AutoSequence type to consumers, which would be unavoidable with
   6043        # Nullable<AutoSequence> or Optional<AutoSequence>.
   6044        if (
   6045            (isMember and isMember != "Union")
   6046            or isOptional
   6047            or nullable
   6048            or isCallbackReturnValue
   6049        ):
   6050            sequenceClass = "Sequence"
   6051        else:
   6052            sequenceClass = "binding_detail::AutoSequence"
   6053 
   6054        # XXXbz we can't include the index in the sourceDescription, because
   6055        # we don't really have a way to pass one in dynamically at runtime...
   6056        elementInfo = getJSToNativeConversionInfo(
   6057            elementType,
   6058            descriptorProvider,
   6059            isMember="Sequence",
   6060            exceptionCode=exceptionCode,
   6061            lenientFloatCode=lenientFloatCode,
   6062            isCallbackReturnValue=isCallbackReturnValue,
   6063            sourceDescription="element of %s" % sourceDescription,
   6064            nestingLevel=incrementNestingLevel(),
   6065        )
   6066        if elementInfo.dealWithOptional:
   6067            raise TypeError("Shouldn't have optional things in sequences")
   6068        if elementInfo.holderType is not None:
   6069            raise TypeError("Shouldn't need holders for sequences")
   6070 
   6071        typeName = CGTemplatedType(sequenceClass, elementInfo.declType)
   6072        sequenceType = typeName.define()
   6073 
   6074        if isMember == "Union" and typeNeedsRooting(type):
   6075            assert not nullable
   6076            typeName = CGTemplatedType(
   6077                "binding_detail::RootedAutoSequence", elementInfo.declType
   6078            )
   6079        elif nullable:
   6080            typeName = CGTemplatedType("Nullable", typeName)
   6081 
   6082        if nullable:
   6083            arrayRef = "${declName}.SetValue()"
   6084        else:
   6085            arrayRef = "${declName}"
   6086 
   6087        elementConversion = string.Template(elementInfo.template).substitute(
   6088            {
   6089                "val": "temp" + str(nestingLevel),
   6090                "maybeMutableVal": "&temp" + str(nestingLevel),
   6091                "declName": "slot" + str(nestingLevel),
   6092                # We only need holderName here to handle isExternal()
   6093                # interfaces, which use an internal holder for the
   6094                # conversion even when forceOwningType ends up true.
   6095                "holderName": "tempHolder" + str(nestingLevel),
   6096                "passedToJSImpl": "${passedToJSImpl}",
   6097            }
   6098        )
   6099 
   6100        elementInitializer = initializerForType(elementType)
   6101        if elementInitializer is None:
   6102            elementInitializer = ""
   6103        else:
   6104            elementInitializer = elementInitializer + ", "
   6105 
   6106        # NOTE: Keep this in sync with variadic conversions as needed
   6107        templateBody = fill(
   6108            """
   6109            JS::ForOfIterator iter${nestingLevel}(cx);
   6110            if (!iter${nestingLevel}.init($${val}, JS::ForOfIterator::AllowNonIterable)) {
   6111              $*{exceptionCode}
   6112            }
   6113            if (!iter${nestingLevel}.valueIsIterable()) {
   6114              $*{notSequence}
   6115            }
   6116            ${sequenceType} &arr${nestingLevel} = ${arrayRef};
   6117            JS::Rooted<JS::Value> temp${nestingLevel}(cx);
   6118            while (true) {
   6119              bool done${nestingLevel};
   6120              if (!iter${nestingLevel}.next(&temp${nestingLevel}, &done${nestingLevel})) {
   6121                $*{exceptionCode}
   6122              }
   6123              if (done${nestingLevel}) {
   6124                break;
   6125              }
   6126              ${elementType}* slotPtr${nestingLevel} = arr${nestingLevel}.AppendElement(${elementInitializer}mozilla::fallible);
   6127              if (!slotPtr${nestingLevel}) {
   6128                JS_ReportOutOfMemory(cx);
   6129                $*{exceptionCode}
   6130              }
   6131              ${elementType}& slot${nestingLevel} = *slotPtr${nestingLevel};
   6132              $*{elementConversion}
   6133            }
   6134            """,
   6135            exceptionCode=exceptionCode,
   6136            notSequence=notSequence,
   6137            sequenceType=sequenceType,
   6138            arrayRef=arrayRef,
   6139            elementType=elementInfo.declType.define(),
   6140            elementConversion=elementConversion,
   6141            elementInitializer=elementInitializer,
   6142            nestingLevel=str(nestingLevel),
   6143        )
   6144 
   6145        templateBody = wrapObjectTemplate(
   6146            templateBody, type, "${declName}.SetNull();\n", notSequence
   6147        )
   6148        if isinstance(defaultValue, IDLEmptySequenceValue):
   6149            if type.nullable():
   6150                codeToSetEmpty = "${declName}.SetValue();\n"
   6151            else:
   6152                codeToSetEmpty = (
   6153                    "/* ${declName} array is already empty; nothing to do */\n"
   6154                )
   6155            templateBody = handleDefault(templateBody, codeToSetEmpty)
   6156 
   6157        declArgs = None
   6158        holderType = None
   6159        holderArgs = None
   6160        # Sequence arguments that might contain traceable things need
   6161        # to get traced
   6162        if typeNeedsRooting(elementType):
   6163            if not isMember:
   6164                holderType = CGTemplatedType("SequenceRooter", elementInfo.declType)
   6165                # If our sequence is nullable, this will set the Nullable to be
   6166                # not-null, but that's ok because we make an explicit SetNull() call
   6167                # on it as needed if our JS value is actually null.
   6168                holderArgs = "cx, &%s" % arrayRef
   6169            elif isMember == "Union":
   6170                declArgs = "cx"
   6171 
   6172        return JSToNativeConversionInfo(
   6173            templateBody,
   6174            declType=typeName,
   6175            declArgs=declArgs,
   6176            holderType=holderType,
   6177            dealWithOptional=isOptional,
   6178            holderArgs=holderArgs,
   6179        )
   6180 
   6181    if type.isRecord():
   6182        assert not isEnforceRange and not isClamp and not isAllowShared
   6183        if failureCode is None:
   6184            notRecord = 'cx.ThrowErrorMessage<MSG_NOT_OBJECT>("%s");\n' "%s" % (
   6185                firstCap(sourceDescription),
   6186                exceptionCode,
   6187            )
   6188        else:
   6189            notRecord = failureCode
   6190 
   6191        nullable = type.nullable()
   6192        # Be very careful not to change "type": we need it later
   6193        if nullable:
   6194            recordType = type.inner
   6195        else:
   6196            recordType = type
   6197        valueType = recordType.inner
   6198 
   6199        valueInfo = getJSToNativeConversionInfo(
   6200            valueType,
   6201            descriptorProvider,
   6202            isMember="Record",
   6203            exceptionCode=exceptionCode,
   6204            lenientFloatCode=lenientFloatCode,
   6205            isCallbackReturnValue=isCallbackReturnValue,
   6206            sourceDescription="value in %s" % sourceDescription,
   6207            nestingLevel=incrementNestingLevel(),
   6208        )
   6209        if valueInfo.dealWithOptional:
   6210            raise TypeError("Shouldn't have optional things in record")
   6211        if valueInfo.holderType is not None:
   6212            raise TypeError("Shouldn't need holders for record")
   6213 
   6214        declType = CGTemplatedType(
   6215            "Record", [recordKeyDeclType(recordType), valueInfo.declType]
   6216        )
   6217        typeName = declType.define()
   6218 
   6219        if isMember == "Union" and typeNeedsRooting(type):
   6220            assert not nullable
   6221            declType = CGTemplatedType(
   6222                "RootedRecord", [recordKeyDeclType(recordType), valueInfo.declType]
   6223            )
   6224        elif nullable:
   6225            declType = CGTemplatedType("Nullable", declType)
   6226 
   6227        if nullable:
   6228            recordRef = "${declName}.SetValue()"
   6229        else:
   6230            recordRef = "${declName}"
   6231 
   6232        valueConversion = string.Template(valueInfo.template).substitute(
   6233            {
   6234                "val": "temp",
   6235                "maybeMutableVal": "&temp",
   6236                "declName": "slot",
   6237                # We only need holderName here to handle isExternal()
   6238                # interfaces, which use an internal holder for the
   6239                # conversion even when forceOwningType ends up true.
   6240                "holderName": "tempHolder",
   6241                "passedToJSImpl": "${passedToJSImpl}",
   6242            }
   6243        )
   6244 
   6245        keyType = recordKeyType(recordType)
   6246        if recordType.keyType.isJSString():
   6247            raise TypeError(
   6248                "Have do deal with JSString record type, but don't know how"
   6249            )
   6250        if recordType.keyType.isByteString() or recordType.keyType.isUTF8String():
   6251            hashKeyType = "nsCStringHashKey"
   6252            if recordType.keyType.isByteString():
   6253                keyConversionFunction = "ConvertJSValueToByteString"
   6254            else:
   6255                keyConversionFunction = "ConvertJSValueToString"
   6256 
   6257        else:
   6258            hashKeyType = "nsStringHashKey"
   6259            if recordType.keyType.isDOMString():
   6260                keyConversionFunction = "ConvertJSValueToString"
   6261            else:
   6262                assert recordType.keyType.isUSVString()
   6263                keyConversionFunction = "ConvertJSValueToUSVString"
   6264 
   6265        templateBody = fill(
   6266            """
   6267            auto& recordEntries = ${recordRef}.Entries();
   6268 
   6269            JS::Rooted<JSObject*> recordObj(cx, &$${val}.toObject());
   6270            JS::RootedVector<jsid> ids(cx);
   6271            if (!js::GetPropertyKeys(cx, recordObj,
   6272                                     JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &ids)) {
   6273              $*{exceptionCode}
   6274            }
   6275            if (!recordEntries.SetCapacity(ids.length(), mozilla::fallible)) {
   6276              JS_ReportOutOfMemory(cx);
   6277              $*{exceptionCode}
   6278            }
   6279            JS::Rooted<JS::Value> propNameValue(cx);
   6280            JS::Rooted<JS::Value> temp(cx);
   6281            JS::Rooted<jsid> curId(cx);
   6282            JS::Rooted<JS::Value> idVal(cx);
   6283            // Use a hashset to keep track of ids seen, to avoid
   6284            // introducing nasty O(N^2) behavior scanning for them all the
   6285            // time.  Ideally we'd use a data structure with O(1) lookup
   6286            // _and_ ordering for the MozMap, but we don't have one lying
   6287            // around.
   6288            nsTHashtable<${hashKeyType}> idsSeen;
   6289            for (size_t i = 0; i < ids.length(); ++i) {
   6290              curId = ids[i];
   6291 
   6292              JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc(cx);
   6293              if (!JS_GetOwnPropertyDescriptorById(cx, recordObj, curId,
   6294                                                   &desc)) {
   6295                $*{exceptionCode}
   6296              }
   6297 
   6298              if (desc.isNothing() || !desc->enumerable()) {
   6299                continue;
   6300              }
   6301 
   6302              idVal = js::IdToValue(curId);
   6303              ${keyType} propName;
   6304              // This will just throw if idVal is a Symbol, like the spec says
   6305              // to do.
   6306              if (!${keyConversionFunction}(cx, idVal, "key of ${sourceDescription}", propName)) {
   6307                $*{exceptionCode}
   6308              }
   6309 
   6310              if (!JS_GetPropertyById(cx, recordObj, curId, &temp)) {
   6311                $*{exceptionCode}
   6312              }
   6313 
   6314              ${typeName}::EntryType* entry;
   6315              if (!idsSeen.EnsureInserted(propName)) {
   6316                // Find the existing entry.
   6317                auto idx = recordEntries.IndexOf(propName);
   6318                MOZ_ASSERT(idx != recordEntries.NoIndex,
   6319                           "Why is it not found?");
   6320                // Now blow it away to make it look like it was just added
   6321                // to the array, because it's not obvious that it's
   6322                // safe to write to its already-initialized mValue via our
   6323                // normal codegen conversions.  For example, the value
   6324                // could be a union and this would change its type, but
   6325                // codegen assumes we won't do that.
   6326                entry = recordEntries.ReconstructElementAt(idx);
   6327              } else {
   6328                // Safe to do an infallible append here, because we did a
   6329                // SetCapacity above to the right capacity.
   6330                entry = recordEntries.AppendElement();
   6331              }
   6332              entry->mKey = propName;
   6333              ${valueType}& slot = entry->mValue;
   6334              $*{valueConversion}
   6335            }
   6336            """,
   6337            exceptionCode=exceptionCode,
   6338            recordRef=recordRef,
   6339            hashKeyType=hashKeyType,
   6340            keyType=keyType,
   6341            keyConversionFunction=keyConversionFunction,
   6342            sourceDescription=sourceDescription,
   6343            typeName=typeName,
   6344            valueType=valueInfo.declType.define(),
   6345            valueConversion=valueConversion,
   6346        )
   6347 
   6348        templateBody = wrapObjectTemplate(
   6349            templateBody, type, "${declName}.SetNull();\n", notRecord
   6350        )
   6351 
   6352        declArgs = None
   6353        holderType = None
   6354        holderArgs = None
   6355        # record arguments that might contain traceable things need
   6356        # to get traced
   6357        if not isMember and isCallbackReturnValue:
   6358            # Go ahead and just convert directly into our actual return value
   6359            declType = CGWrapper(declType, post="&")
   6360            declArgs = "aRetVal"
   6361        elif typeNeedsRooting(valueType):
   6362            if not isMember:
   6363                holderType = CGTemplatedType(
   6364                    "RecordRooter", [recordKeyDeclType(recordType), valueInfo.declType]
   6365                )
   6366                # If our record is nullable, this will set the Nullable to be
   6367                # not-null, but that's ok because we make an explicit SetNull() call
   6368                # on it as needed if our JS value is actually null.
   6369                holderArgs = "cx, &%s" % recordRef
   6370            elif isMember == "Union":
   6371                declArgs = "cx"
   6372 
   6373        return JSToNativeConversionInfo(
   6374            templateBody,
   6375            declType=declType,
   6376            declArgs=declArgs,
   6377            holderType=holderType,
   6378            dealWithOptional=isOptional,
   6379            holderArgs=holderArgs,
   6380        )
   6381 
   6382    if type.isUnion():
   6383        nullable = type.nullable()
   6384        if nullable:
   6385            type = type.inner
   6386 
   6387        isOwningUnion = (isMember and isMember != "Union") or isCallbackReturnValue
   6388        unionArgumentObj = "${declName}"
   6389        if nullable:
   6390            if isOptional and not isOwningUnion:
   6391                unionArgumentObj += ".Value()"
   6392            # If we're owning, we're a Nullable, which hasn't been told it has
   6393            # a value.  Otherwise we're an already-constructed Maybe.
   6394            unionArgumentObj += ".SetValue()"
   6395 
   6396        templateBody = CGIfWrapper(
   6397            CGGeneric(exceptionCode),
   6398            '!%s.Init(cx, ${val}, "%s", ${passedToJSImpl})'
   6399            % (unionArgumentObj, firstCap(sourceDescription)),
   6400        )
   6401 
   6402        if type.hasNullableType:
   6403            assert not nullable
   6404            # Make sure to handle a null default value here
   6405            if defaultValue and isinstance(defaultValue, IDLNullValue):
   6406                assert defaultValue.type == type
   6407                templateBody = CGIfElseWrapper(
   6408                    "!(${haveValue})",
   6409                    CGGeneric("%s.SetNull();\n" % unionArgumentObj),
   6410                    templateBody,
   6411                )
   6412 
   6413        typeName = CGUnionStruct.unionTypeDecl(type, isOwningUnion)
   6414        argumentTypeName = typeName + "Argument"
   6415        if nullable:
   6416            typeName = "Nullable<" + typeName + " >"
   6417 
   6418        declType = CGGeneric(typeName)
   6419        if isOwningUnion:
   6420            holderType = None
   6421        else:
   6422            holderType = CGGeneric(argumentTypeName)
   6423            if nullable:
   6424                holderType = CGTemplatedType("Maybe", holderType)
   6425 
   6426        # If we're isOptional and not nullable the normal optional handling will
   6427        # handle lazy construction of our holder.  If we're nullable and not
   6428        # owning we do it all by hand because we do not want our holder
   6429        # constructed if we're null.  But if we're owning we don't have a
   6430        # holder anyway, so we can do the normal Optional codepath.
   6431        declLoc = "${declName}"
   6432        constructDecl = None
   6433        if nullable:
   6434            if isOptional and not isOwningUnion:
   6435                declType = CGTemplatedType("Optional", declType)
   6436                constructDecl = CGGeneric("${declName}.Construct();\n")
   6437                declLoc = "${declName}.Value()"
   6438 
   6439        if not isMember and isCallbackReturnValue:
   6440            declType = CGWrapper(declType, post="&")
   6441            declArgs = "aRetVal"
   6442        else:
   6443            declArgs = None
   6444 
   6445        if (
   6446            defaultValue
   6447            and not isinstance(defaultValue, IDLNullValue)
   6448            and not isinstance(defaultValue, IDLDefaultDictionaryValue)
   6449        ):
   6450            tag = defaultValue.type.tag()
   6451 
   6452            if tag in numericSuffixes or tag is IDLType.Tags.bool:
   6453                defaultStr = getHandleDefault(defaultValue)
   6454                # Make sure we actually construct the thing inside the nullable.
   6455                value = declLoc + (".SetValue()" if nullable else "")
   6456                name = getUnionMemberName(defaultValue.type)
   6457                default = CGGeneric(
   6458                    "%s.RawSetAs%s() = %s;\n" % (value, name, defaultStr)
   6459                )
   6460            elif isinstance(defaultValue, IDLEmptySequenceValue):
   6461                name = getUnionMemberName(defaultValue.type)
   6462                # Make sure we actually construct the thing inside the nullable.
   6463                value = declLoc + (".SetValue()" if nullable else "")
   6464                if not isOwningUnion and typeNeedsRooting(defaultValue.type):
   6465                    ctorArgs = "cx"
   6466                else:
   6467                    ctorArgs = ""
   6468                # It's enough to set us to the right type; that will
   6469                # create an empty array, which is all we need here.
   6470                default = CGGeneric(
   6471                    "(void)%s.RawSetAs%s(%s);\n" % (value, name, ctorArgs)
   6472                )
   6473            elif defaultValue.type.isEnum():
   6474                name = getUnionMemberName(defaultValue.type)
   6475                # Make sure we actually construct the thing inside the nullable.
   6476                value = declLoc + (".SetValue()" if nullable else "")
   6477                default = CGGeneric(
   6478                    "%s.RawSetAs%s() = %s::%s;\n"
   6479                    % (
   6480                        value,
   6481                        name,
   6482                        defaultValue.type.inner.identifier.name,
   6483                        getEnumValueName(defaultValue.value),
   6484                    )
   6485                )
   6486            else:
   6487                default = CGGeneric(
   6488                    handleDefaultStringValue(
   6489                        defaultValue, "%s.SetStringLiteral" % unionArgumentObj
   6490                    )
   6491                )
   6492 
   6493            templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody)
   6494 
   6495        if nullable:
   6496            assert not type.hasNullableType
   6497            if defaultValue:
   6498                if isinstance(defaultValue, IDLNullValue):
   6499                    extraConditionForNull = "!(${haveValue}) || "
   6500                else:
   6501                    extraConditionForNull = "(${haveValue}) && "
   6502            else:
   6503                extraConditionForNull = ""
   6504 
   6505            hasUndefinedType = any(t.isUndefined() for t in type.flatMemberTypes)
   6506            assert not hasUndefinedType or defaultValue is None
   6507 
   6508            nullTest = (
   6509                "${val}.isNull()" if hasUndefinedType else "${val}.isNullOrUndefined()"
   6510            )
   6511            templateBody = CGIfElseWrapper(
   6512                extraConditionForNull + nullTest,
   6513                CGGeneric("%s.SetNull();\n" % declLoc),
   6514                templateBody,
   6515            )
   6516        elif (
   6517            not type.hasNullableType
   6518            and defaultValue
   6519            and isinstance(defaultValue, IDLDefaultDictionaryValue)
   6520        ):
   6521            assert type.hasDictionaryType()
   6522            assert defaultValue.type.isDictionary()
   6523            if not isOwningUnion and typeNeedsRooting(defaultValue.type):
   6524                ctorArgs = "cx"
   6525            else:
   6526                ctorArgs = ""
   6527            initDictionaryWithNull = CGIfWrapper(
   6528                CGGeneric("return false;\n"),
   6529                (
   6530                    '!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")'
   6531                    % (
   6532                        declLoc,
   6533                        getUnionMemberName(defaultValue.type),
   6534                        ctorArgs,
   6535                        type.prettyName(),
   6536                    )
   6537                ),
   6538            )
   6539            templateBody = CGIfElseWrapper(
   6540                "!(${haveValue})", initDictionaryWithNull, templateBody
   6541            )
   6542 
   6543        templateBody = CGList([constructDecl, templateBody])
   6544 
   6545        return JSToNativeConversionInfo(
   6546            templateBody.define(),
   6547            declType=declType,
   6548            declArgs=declArgs,
   6549            dealWithOptional=isOptional and (not nullable or isOwningUnion),
   6550        )
   6551 
   6552    if type.isPromise():
   6553        assert not type.nullable()
   6554        assert defaultValue is None
   6555 
   6556        # We always have to hold a strong ref to Promise here, because
   6557        # Promise::resolve returns an addrefed thing.
   6558        argIsPointer = isCallbackReturnValue
   6559        if argIsPointer:
   6560            declType = CGGeneric("RefPtr<Promise>")
   6561        else:
   6562            declType = CGGeneric("OwningNonNull<Promise>")
   6563 
   6564        # Per spec, what we're supposed to do is take the original
   6565        # Promise.resolve and call it with the original Promise as this
   6566        # value to make a Promise out of whatever value we actually have
   6567        # here.  The question is which global we should use.  There are
   6568        # several cases to consider:
   6569        #
   6570        # 1) Normal call to API with a Promise argument.  This is a case the
   6571        #    spec covers, and we should be using the current Realm's
   6572        #    Promise.  That means the current compartment.
   6573        # 2) Call to API with a Promise argument over Xrays.  In practice,
   6574        #    this sort of thing seems to be used for giving an API
   6575        #    implementation a way to wait for conclusion of an asyc
   6576        #    operation, _not_ to expose the Promise to content code.  So we
   6577        #    probably want to allow callers to use such an API in a
   6578        #    "natural" way, by passing chrome-side promises; indeed, that
   6579        #    may be all that the caller has to represent their async
   6580        #    operation.  That means we really need to do the
   6581        #    Promise.resolve() in the caller (chrome) compartment: if we do
   6582        #    it in the content compartment, we will try to call .then() on
   6583        #    the chrome promise while in the content compartment, which will
   6584        #    throw and we'll just get a rejected Promise.  Note that this is
   6585        #    also the reason why a caller who has a chrome Promise
   6586        #    representing an async operation can't itself convert it to a
   6587        #    content-side Promise (at least not without some serious
   6588        #    gyrations).
   6589        # 3) Promise return value from a callback or callback interface.
   6590        #    Per spec, this should use the Realm of the callback object.  In
   6591        #    our case, that's the compartment of the underlying callback,
   6592        #    not the current compartment (which may be the compartment of
   6593        #    some cross-compartment wrapper around said callback).
   6594        # 4) Return value from a JS-implemented interface.  In this case we
   6595        #    have a problem.  Our current compartment is the compartment of
   6596        #    the JS implementation.  But if the JS implementation returned
   6597        #    a page-side Promise (which is a totally sane thing to do, and
   6598        #    in fact the right thing to do given that this return value is
   6599        #    going right to content script) then we don't want to
   6600        #    Promise.resolve with our current compartment Promise, because
   6601        #    that will wrap it up in a chrome-side Promise, which is
   6602        #    decidedly _not_ what's desired here.  So in that case we
   6603        #    should really unwrap the return value and use the global of
   6604        #    the result.  CheckedUnwrapStatic should be good enough for that;
   6605        #    if it fails, then we're failing unwrap while in a
   6606        #    system-privileged compartment, so presumably we have a dead
   6607        #    object wrapper.  Just error out.  Do NOT fall back to using
   6608        #    the current compartment instead: that will return a
   6609        #    system-privileged rejected (because getting .then inside
   6610        #    resolve() failed) Promise to the caller, which they won't be
   6611        #    able to touch.  That's not helpful.  If we error out, on the
   6612        #    other hand, they will get a content-side rejected promise.
   6613        #    Same thing if the value returned is not even an object.
   6614        if isCallbackReturnValue == "JSImpl":
   6615            # Case 4 above.  Note that globalObj defaults to the current
   6616            # compartment global.  Note that we don't use $*{exceptionCode}
   6617            # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED)
   6618            # which we don't really want here.
   6619            assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n"
   6620            getPromiseGlobal = fill(
   6621                """
   6622                if (!$${val}.isObject()) {
   6623                  aRv.ThrowTypeError<MSG_NOT_OBJECT>("${sourceDescription}");
   6624                  return nullptr;
   6625                }
   6626                JSObject* unwrappedVal = js::CheckedUnwrapStatic(&$${val}.toObject());
   6627                if (!unwrappedVal) {
   6628                  // A slight lie, but not much of one, for a dead object wrapper.
   6629                  aRv.ThrowTypeError<MSG_NOT_OBJECT>("${sourceDescription}");
   6630                  return nullptr;
   6631                }
   6632                globalObj = JS::GetNonCCWObjectGlobal(unwrappedVal);
   6633                """,
   6634                sourceDescription=sourceDescription,
   6635            )
   6636        elif isCallbackReturnValue == "Callback":
   6637            getPromiseGlobal = dedent(
   6638                """
   6639                // We basically want our entry global here.  Play it safe
   6640                // and use GetEntryGlobal() to get it, with whatever
   6641                // principal-clamping it ends up doing.
   6642                globalObj = GetEntryGlobal()->GetGlobalJSObject();
   6643                """
   6644            )
   6645        else:
   6646            getPromiseGlobal = dedent(
   6647                """
   6648                globalObj = JS::CurrentGlobalOrNull(cx);
   6649                """
   6650            )
   6651 
   6652        templateBody = fill(
   6653            """
   6654            { // Scope for our GlobalObject, FastErrorResult, JSAutoRealm,
   6655              // etc.
   6656 
   6657              JS::Rooted<JSObject*> globalObj(cx);
   6658              $*{getPromiseGlobal}
   6659              JSAutoRealm ar(cx, globalObj);
   6660              GlobalObject promiseGlobal(cx, globalObj);
   6661              if (promiseGlobal.Failed()) {
   6662                $*{exceptionCode}
   6663              }
   6664 
   6665              JS::Rooted<JS::Value> valueToResolve(cx, $${val});
   6666              if (!JS_WrapValue(cx, &valueToResolve)) {
   6667                $*{exceptionCode}
   6668              }
   6669              binding_detail::FastErrorResult promiseRv;
   6670              nsCOMPtr<nsIGlobalObject> global =
   6671                do_QueryInterface(promiseGlobal.GetAsSupports());
   6672              if (!global) {
   6673                promiseRv.Throw(NS_ERROR_UNEXPECTED);
   6674                MOZ_ALWAYS_TRUE(promiseRv.MaybeSetPendingException(cx));
   6675                $*{exceptionCode}
   6676              }
   6677              $${declName} = Promise::Resolve(global, cx, valueToResolve,
   6678                                              promiseRv);
   6679              if (promiseRv.MaybeSetPendingException(cx)) {
   6680                $*{exceptionCode}
   6681              }
   6682            }
   6683            """,
   6684            getPromiseGlobal=getPromiseGlobal,
   6685            exceptionCode=exceptionCode,
   6686        )
   6687 
   6688        return JSToNativeConversionInfo(
   6689            templateBody, declType=declType, dealWithOptional=isOptional
   6690        )
   6691 
   6692    if type.isGeckoInterface():
   6693        assert not isEnforceRange and not isClamp and not isAllowShared
   6694 
   6695        descriptor = descriptorProvider.getDescriptor(
   6696            type.unroll().inner.identifier.name
   6697        )
   6698 
   6699        assert descriptor.nativeType != "JSObject"
   6700 
   6701        if descriptor.interface.isCallback():
   6702            (declType, declArgs, conversion) = getCallbackConversionInfo(
   6703                type, descriptor.interface, isMember, isCallbackReturnValue, isOptional
   6704            )
   6705            template = wrapObjectTemplate(
   6706                conversion, type, "${declName} = nullptr;\n", failureCode
   6707            )
   6708            return JSToNativeConversionInfo(
   6709                template,
   6710                declType=declType,
   6711                declArgs=declArgs,
   6712                dealWithOptional=isOptional,
   6713            )
   6714 
   6715        if descriptor.interface.identifier.name == "WindowProxy":
   6716            declType = CGGeneric("mozilla::dom::WindowProxyHolder")
   6717            if type.nullable():
   6718                declType = CGTemplatedType("Nullable", declType)
   6719                windowProxyHolderRef = "${declName}.SetValue()"
   6720            else:
   6721                windowProxyHolderRef = "${declName}"
   6722 
   6723            failureCode = onFailureBadType(
   6724                failureCode, descriptor.interface.identifier.name
   6725            ).define()
   6726            templateBody = fill(
   6727                """
   6728                JS::Rooted<JSObject*> source(cx, &$${val}.toObject());
   6729                if (NS_FAILED(UnwrapWindowProxyArg(cx, source, ${windowProxyHolderRef}))) {
   6730                    $*{onFailure}
   6731                }
   6732                """,
   6733                windowProxyHolderRef=windowProxyHolderRef,
   6734                onFailure=failureCode,
   6735            )
   6736            templateBody = wrapObjectTemplate(
   6737                templateBody, type, "${declName}.SetNull();\n", failureCode
   6738            )
   6739            return JSToNativeConversionInfo(
   6740                templateBody, declType=declType, dealWithOptional=isOptional
   6741            )
   6742 
   6743        # This is an interface that we implement as a concrete class
   6744        # or an XPCOM interface.
   6745 
   6746        # Allow null pointers for nullable types and old-binding classes, and
   6747        # use an RefPtr or raw pointer for callback return values to make
   6748        # them easier to return.
   6749        argIsPointer = (
   6750            type.nullable() or type.unroll().inner.isExternal() or isCallbackReturnValue
   6751        )
   6752 
   6753        # Sequence and dictionary members, as well as owning unions (which can
   6754        # appear here as return values in JS-implemented interfaces) have to
   6755        # hold a strong ref to the thing being passed down.  Those all set
   6756        # isMember.
   6757        #
   6758        # Also, callback return values always end up addrefing anyway, so there
   6759        # is no point trying to avoid it here and it makes other things simpler
   6760        # since we can assume the return value is a strong ref.
   6761        assert not descriptor.interface.isCallback()
   6762        forceOwningType = (isMember and isMember != "Union") or isCallbackReturnValue
   6763 
   6764        typeName = descriptor.nativeType
   6765        typePtr = typeName + "*"
   6766 
   6767        # Compute a few things:
   6768        #  - declType is the type we want to return as the first element of our
   6769        #    tuple.
   6770        #  - holderType is the type we want to return as the third element
   6771        #    of our tuple.
   6772 
   6773        # Set up some sensible defaults for these things insofar as we can.
   6774        holderType = None
   6775        if argIsPointer:
   6776            if forceOwningType:
   6777                declType = "RefPtr<" + typeName + ">"
   6778            else:
   6779                declType = typePtr
   6780        else:
   6781            if forceOwningType:
   6782                declType = "OwningNonNull<" + typeName + ">"
   6783            else:
   6784                declType = "NonNull<" + typeName + ">"
   6785 
   6786        templateBody = ""
   6787        if forceOwningType:
   6788            templateBody += fill(
   6789                """
   6790                static_assert(IsRefcounted<${typeName}>::value, "We can only store refcounted classes.");
   6791                """,
   6792                typeName=typeName,
   6793            )
   6794 
   6795        if not descriptor.interface.isExternal():
   6796            if failureCode is not None:
   6797                templateBody += str(
   6798                    CastableObjectUnwrapper(
   6799                        descriptor,
   6800                        "${val}",
   6801                        "${maybeMutableVal}",
   6802                        "${declName}",
   6803                        failureCode,
   6804                    )
   6805                )
   6806            else:
   6807                templateBody += str(
   6808                    FailureFatalCastableObjectUnwrapper(
   6809                        descriptor,
   6810                        "${val}",
   6811                        "${maybeMutableVal}",
   6812                        "${declName}",
   6813                        exceptionCode,
   6814                        isCallbackReturnValue,
   6815                        firstCap(sourceDescription),
   6816                    )
   6817                )
   6818        else:
   6819            # External interface.  We always have a holder for these, because we
   6820            # don't actually know whether we have to addref when unwrapping or not.
   6821            # So we just pass an getter_AddRefs(RefPtr) to XPConnect and if we'll
   6822            # need a release it'll put a non-null pointer in there.
   6823            if forceOwningType:
   6824                # Don't return a holderType in this case; our declName
   6825                # will just own stuff.
   6826                templateBody += "RefPtr<" + typeName + "> ${holderName};\n"
   6827            else:
   6828                holderType = "RefPtr<" + typeName + ">"
   6829            templateBody += (
   6830                "JS::Rooted<JSObject*> source(cx, &${val}.toObject());\n"
   6831                + "if (NS_FAILED(UnwrapArg<"
   6832                + typeName
   6833                + ">(cx, source, getter_AddRefs(${holderName})))) {\n"
   6834            )
   6835            templateBody += CGIndenter(
   6836                onFailureBadType(failureCode, descriptor.interface.identifier.name)
   6837            ).define()
   6838            templateBody += "}\n" "MOZ_ASSERT(${holderName});\n"
   6839 
   6840            # And store our value in ${declName}
   6841            templateBody += "${declName} = ${holderName};\n"
   6842 
   6843        # Just pass failureCode, not onFailureBadType, here, so we'll report
   6844        # the thing as not an object as opposed to not implementing whatever
   6845        # our interface is.
   6846        templateBody = wrapObjectTemplate(
   6847            templateBody, type, "${declName} = nullptr;\n", failureCode
   6848        )
   6849 
   6850        declType = CGGeneric(declType)
   6851        if holderType is not None:
   6852            holderType = CGGeneric(holderType)
   6853        return JSToNativeConversionInfo(
   6854            templateBody,
   6855            declType=declType,
   6856            holderType=holderType,
   6857            dealWithOptional=isOptional,
   6858        )
   6859 
   6860    if type.isSpiderMonkeyInterface():
   6861        assert not isEnforceRange and not isClamp
   6862        name = type.unroll().name  # unroll() because it may be nullable
   6863        interfaceType = CGGeneric(name)
   6864        declType = interfaceType
   6865        if type.nullable():
   6866            declType = CGTemplatedType("Nullable", declType)
   6867            objRef = "${declName}.SetValue()"
   6868        else:
   6869            objRef = "${declName}"
   6870 
   6871        # Again, this is a bit strange since we are actually building a
   6872        # template string here. ${objRef} and $*{badType} below are filled in
   6873        # right now; $${val} expands to ${val}, to be filled in later.
   6874        template = fill(
   6875            """
   6876            if (!${objRef}.Init(&$${val}.toObject())) {
   6877              $*{badType}
   6878            }
   6879            """,
   6880            objRef=objRef,
   6881            badType=onFailureBadType(failureCode, type.name).define(),
   6882        )
   6883        if type.isBufferSource():
   6884            if type.isArrayBuffer():
   6885                isSharedMethod = "JS::IsSharedArrayBufferObject"
   6886                isLargeMethod = "JS::IsLargeArrayBufferMaybeShared"
   6887                isResizableMethod = "JS::IsResizableArrayBufferMaybeShared"
   6888                isImmutableMethod = "JS::IsImmutableArrayBufferMaybeShared"
   6889            else:
   6890                assert type.isArrayBufferView() or type.isTypedArray()
   6891                isSharedMethod = "JS::IsArrayBufferViewShared"
   6892                isLargeMethod = "JS::IsLargeArrayBufferView"
   6893                isResizableMethod = "JS::IsResizableArrayBufferView"
   6894                isImmutableMethod = "JS::IsImmutableArrayBufferView"
   6895            if not isAllowShared:
   6896                template += fill(
   6897                    """
   6898                    if (${isSharedMethod}(${objRef}.Obj())) {
   6899                      $*{badType}
   6900                    }
   6901                    """,
   6902                    isSharedMethod=isSharedMethod,
   6903                    objRef=objRef,
   6904                    badType=onFailureIsShared().define(),
   6905                )
   6906            # For now reject large (> 2 GB) ArrayBuffers and ArrayBufferViews.
   6907            # Supporting this will require changing dom::TypedArray and
   6908            # consumers.
   6909            template += fill(
   6910                """
   6911                if (${isLargeMethod}(${objRef}.Obj())) {
   6912                  $*{badType}
   6913                }
   6914                """,
   6915                isLargeMethod=isLargeMethod,
   6916                objRef=objRef,
   6917                badType=onFailureIsLarge().define(),
   6918            )
   6919            # For now reject resizable ArrayBuffers and growable
   6920            # SharedArrayBuffers. Supporting this will require changing
   6921            # dom::TypedArray and consumers.
   6922            template += fill(
   6923                """
   6924                if (${isResizableMethod}(${objRef}.Obj())) {
   6925                  $*{badType}
   6926                }
   6927                """,
   6928                isResizableMethod=isResizableMethod,
   6929                objRef=objRef,
   6930                badType=onFailureIsResizable().define(),
   6931            )
   6932            # For now reject immutable ArrayBuffers. Supporting this will
   6933            # require changing dom::TypedArray and consumers.
   6934            template += fill(
   6935                """
   6936                if (${isImmutableMethod}(${objRef}.Obj())) {
   6937                  $*{badType}
   6938                }
   6939                """,
   6940                isImmutableMethod=isImmutableMethod,
   6941                objRef=objRef,
   6942                badType=onFailureIsImmutable().define(),
   6943            )
   6944        template = wrapObjectTemplate(
   6945            template, type, "${declName}.SetNull();\n", failureCode
   6946        )
   6947        if not isMember or isMember == "Union":
   6948            # This is a bit annoying.  In a union we don't want to have a
   6949            # holder, since unions don't support that.  But if we're optional we
   6950            # want to have a holder, so that the callee doesn't see
   6951            # Optional<RootedSpiderMonkeyInterface<InterfaceType>>.  So do a
   6952            # holder if we're optional and use a RootedSpiderMonkeyInterface
   6953            # otherwise.
   6954            if isOptional:
   6955                holderType = CGTemplatedType(
   6956                    "SpiderMonkeyInterfaceRooter", interfaceType
   6957                )
   6958                # If our SpiderMonkey interface is nullable, this will set the
   6959                # Nullable to be not-null, but that's ok because we make an
   6960                # explicit SetNull() call on it as needed if our JS value is
   6961                # actually null.  XXXbz Because "Maybe" takes const refs for
   6962                # constructor arguments, we can't pass a reference here; have
   6963                # to pass a pointer.
   6964                holderArgs = "cx, &%s" % objRef
   6965                declArgs = None
   6966            else:
   6967                holderType = None
   6968                holderArgs = None
   6969                declType = CGTemplatedType("RootedSpiderMonkeyInterface", declType)
   6970                declArgs = "cx"
   6971        else:
   6972            holderType = None
   6973            holderArgs = None
   6974            declArgs = None
   6975        return JSToNativeConversionInfo(
   6976            template,
   6977            declType=declType,
   6978            holderType=holderType,
   6979            dealWithOptional=isOptional,
   6980            declArgs=declArgs,
   6981            holderArgs=holderArgs,
   6982        )
   6983 
   6984    if type.isJSString():
   6985        assert not isEnforceRange and not isClamp and not isAllowShared
   6986        if type.nullable():
   6987            raise TypeError("Nullable JSString not supported")
   6988 
   6989        declArgs = "cx"
   6990        if isMember:
   6991            raise TypeError("JSString not supported as member")
   6992        else:
   6993            declType = "JS::Rooted<JSString*>"
   6994 
   6995        if isOptional:
   6996            raise TypeError("JSString not supported as optional")
   6997        templateBody = fill(
   6998            """
   6999                if (!($${declName} = ConvertJSValueToJSString(cx, $${val}))) {
   7000                  $*{exceptionCode}
   7001                }
   7002                """,
   7003            exceptionCode=exceptionCode,
   7004        )
   7005 
   7006        if defaultValue is not None:
   7007            assert not isinstance(defaultValue, IDLNullValue)
   7008            defaultCode = fill(
   7009                """
   7010                static const char data[] = { ${data} };
   7011                $${declName} = JS_NewStringCopyN(cx, data, std::size(data) - 1);
   7012                if (!$${declName}) {
   7013                    $*{exceptionCode}
   7014                }
   7015                """,
   7016                data=", ".join(
   7017                    ["'" + char + "'" for char in defaultValue.value] + ["0"]
   7018                ),
   7019                exceptionCode=exceptionCode,
   7020            )
   7021 
   7022            templateBody = handleDefault(templateBody, defaultCode)
   7023        return JSToNativeConversionInfo(
   7024            templateBody, declType=CGGeneric(declType), declArgs=declArgs
   7025        )
   7026 
   7027    if type.isDOMString() or type.isUSVString() or type.isUTF8String():
   7028        assert not isEnforceRange and not isClamp and not isAllowShared
   7029 
   7030        treatAs = {
   7031            "Default": "eStringify",
   7032            "EmptyString": "eEmpty",
   7033            "Null": "eNull",
   7034        }
   7035        if type.nullable():
   7036            # For nullable strings null becomes a null string.
   7037            treatNullAs = "Null"
   7038            # For nullable strings undefined also becomes a null string.
   7039            undefinedBehavior = "eNull"
   7040        else:
   7041            undefinedBehavior = "eStringify"
   7042            if type.legacyNullToEmptyString:
   7043                treatNullAs = "EmptyString"
   7044            else:
   7045                treatNullAs = "Default"
   7046        nullBehavior = treatAs[treatNullAs]
   7047 
   7048        def getConversionCode(varName):
   7049            normalizeCode = ""
   7050            if type.isUSVString():
   7051                normalizeCode = fill(
   7052                    """
   7053                    if (!NormalizeUSVString(${var})) {
   7054                      JS_ReportOutOfMemory(cx);
   7055                      $*{exceptionCode}
   7056                    }
   7057                    """,
   7058                    var=varName,
   7059                    exceptionCode=exceptionCode,
   7060                )
   7061 
   7062            conversionCode = fill(
   7063                """
   7064                if (!ConvertJSValueToString(cx, $${val}, ${nullBehavior}, ${undefinedBehavior}, ${varName})) {
   7065                  $*{exceptionCode}
   7066                }
   7067                $*{normalizeCode}
   7068                """,
   7069                nullBehavior=nullBehavior,
   7070                undefinedBehavior=undefinedBehavior,
   7071                varName=varName,
   7072                exceptionCode=exceptionCode,
   7073                normalizeCode=normalizeCode,
   7074            )
   7075 
   7076            if defaultValue is None:
   7077                return conversionCode
   7078 
   7079            if isinstance(defaultValue, IDLNullValue):
   7080                assert type.nullable()
   7081                defaultCode = "%s.SetIsVoid(true);\n" % varName
   7082            else:
   7083                defaultCode = handleDefaultStringValue(
   7084                    defaultValue, "%s.AssignLiteral" % varName
   7085                )
   7086            return handleDefault(conversionCode, defaultCode)
   7087 
   7088        if isMember and isMember != "Union":
   7089            # Convert directly into the ns[C]String member we have.
   7090            if type.isUTF8String():
   7091                declType = "nsCString"
   7092            else:
   7093                declType = "nsString"
   7094            return JSToNativeConversionInfo(
   7095                getConversionCode("${declName}"),
   7096                declType=CGGeneric(declType),
   7097                dealWithOptional=isOptional,
   7098            )
   7099 
   7100        if isOptional:
   7101            if type.isUTF8String():
   7102                declType = "Optional<nsACString>"
   7103                holderType = CGGeneric("binding_detail::FakeString<char>")
   7104            else:
   7105                declType = "Optional<nsAString>"
   7106                holderType = CGGeneric("binding_detail::FakeString<char16_t>")
   7107            conversionCode = "%s" "${declName} = &${holderName};\n" % getConversionCode(
   7108                "${holderName}"
   7109            )
   7110        else:
   7111            if type.isUTF8String():
   7112                declType = "binding_detail::FakeString<char>"
   7113            else:
   7114                declType = "binding_detail::FakeString<char16_t>"
   7115            holderType = None
   7116            conversionCode = getConversionCode("${declName}")
   7117 
   7118        # No need to deal with optional here; we handled it already
   7119        return JSToNativeConversionInfo(
   7120            conversionCode, declType=CGGeneric(declType), holderType=holderType
   7121        )
   7122 
   7123    if type.isByteString():
   7124        assert not isEnforceRange and not isClamp and not isAllowShared
   7125 
   7126        nullable = toStringBool(type.nullable())
   7127 
   7128        conversionCode = fill(
   7129            """
   7130            if (!ConvertJSValueToByteString(cx, $${val}, ${nullable}, "${sourceDescription}", $${declName})) {
   7131              $*{exceptionCode}
   7132            }
   7133            """,
   7134            nullable=nullable,
   7135            sourceDescription=sourceDescription,
   7136            exceptionCode=exceptionCode,
   7137        )
   7138 
   7139        if defaultValue is not None:
   7140            if isinstance(defaultValue, IDLNullValue):
   7141                assert type.nullable()
   7142                defaultCode = "${declName}.SetIsVoid(true);\n"
   7143            else:
   7144                defaultCode = handleDefaultStringValue(
   7145                    defaultValue, "${declName}.AssignLiteral"
   7146                )
   7147            conversionCode = handleDefault(conversionCode, defaultCode)
   7148 
   7149        return JSToNativeConversionInfo(
   7150            conversionCode, declType=CGGeneric("nsCString"), dealWithOptional=isOptional
   7151        )
   7152 
   7153    if type.isEnum():
   7154        assert not isEnforceRange and not isClamp and not isAllowShared
   7155 
   7156        enumName = type.unroll().inner.identifier.name
   7157        declType = CGGeneric(enumName)
   7158        if type.nullable():
   7159            declType = CGTemplatedType("Nullable", declType)
   7160            declType = declType.define()
   7161            enumLoc = "${declName}.SetValue()"
   7162        else:
   7163            enumLoc = "${declName}"
   7164            declType = declType.define()
   7165 
   7166        if invalidEnumValueFatal:
   7167            handleInvalidEnumValueCode = "MOZ_ASSERT(index >= 0);\n"
   7168        else:
   7169            # invalidEnumValueFatal is false only for attributes.  So we won't
   7170            # have a non-default exceptionCode here unless attribute "arg
   7171            # conversion" code starts passing in an exceptionCode.  At which
   7172            # point we'll need to figure out what that even means.
   7173            assert exceptionCode == "return false;\n"
   7174            handleInvalidEnumValueCode = dedent(
   7175                """
   7176                if (index < 0) {
   7177                  return true;
   7178                }
   7179                """
   7180            )
   7181 
   7182        template = fill(
   7183            """
   7184            {
   7185              int index;
   7186              if (!binding_detail::FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val},
   7187                                                                                 binding_detail::EnumStrings<${enumtype}>::Values,
   7188                                                                                 "${enumtype}", "${sourceDescription}",
   7189                                                                                 &index)) {
   7190                $*{exceptionCode}
   7191              }
   7192              $*{handleInvalidEnumValueCode}
   7193              ${enumLoc} = static_cast<${enumtype}>(index);
   7194            }
   7195            """,
   7196            enumtype=enumName,
   7197            invalidEnumValueFatal=toStringBool(invalidEnumValueFatal),
   7198            handleInvalidEnumValueCode=handleInvalidEnumValueCode,
   7199            exceptionCode=exceptionCode,
   7200            enumLoc=enumLoc,
   7201            sourceDescription=sourceDescription,
   7202        )
   7203 
   7204        setNull = "${declName}.SetNull();\n"
   7205 
   7206        if type.nullable():
   7207            template = CGIfElseWrapper(
   7208                "${val}.isNullOrUndefined()", CGGeneric(setNull), CGGeneric(template)
   7209            ).define()
   7210 
   7211        if defaultValue is not None:
   7212            if isinstance(defaultValue, IDLNullValue):
   7213                assert type.nullable()
   7214                template = handleDefault(template, setNull)
   7215            else:
   7216                assert defaultValue.type.tag() == IDLType.Tags.domstring
   7217                template = handleDefault(
   7218                    template,
   7219                    (
   7220                        "%s = %s::%s;\n"
   7221                        % (enumLoc, enumName, getEnumValueName(defaultValue.value))
   7222                    ),
   7223                )
   7224        return JSToNativeConversionInfo(
   7225            template, declType=CGGeneric(declType), dealWithOptional=isOptional
   7226        )
   7227 
   7228    if type.isCallback():
   7229        assert not isEnforceRange and not isClamp and not isAllowShared
   7230        assert not type.treatNonCallableAsNull() or type.nullable()
   7231        assert not type.treatNonObjectAsNull() or type.nullable()
   7232        assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
   7233 
   7234        callback = type.unroll().callback
   7235        name = callback.identifier.name
   7236        (declType, declArgs, conversion) = getCallbackConversionInfo(
   7237            type, callback, isMember, isCallbackReturnValue, isOptional
   7238        )
   7239 
   7240        if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
   7241            haveCallable = "JS::IsCallable(&${val}.toObject())"
   7242            if not isDefinitelyObject:
   7243                haveCallable = "${val}.isObject() && " + haveCallable
   7244            if defaultValue is not None:
   7245                assert isinstance(defaultValue, IDLNullValue)
   7246                haveCallable = "(${haveValue}) && " + haveCallable
   7247            template = (
   7248                ("if (%s) {\n" % haveCallable) + conversion + "} else {\n"
   7249                "  ${declName} = nullptr;\n"
   7250                "}\n"
   7251            )
   7252        elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull():
   7253            if not isDefinitelyObject:
   7254                haveObject = "${val}.isObject()"
   7255                if defaultValue is not None:
   7256                    assert isinstance(defaultValue, IDLNullValue)
   7257                    haveObject = "(${haveValue}) && " + haveObject
   7258                template = CGIfElseWrapper(
   7259                    haveObject,
   7260                    CGGeneric(conversion),
   7261                    CGGeneric("${declName} = nullptr;\n"),
   7262                ).define()
   7263            else:
   7264                template = conversion
   7265        else:
   7266            template = wrapObjectTemplate(
   7267                "if (JS::IsCallable(&${val}.toObject())) {\n"
   7268                + conversion
   7269                + "} else {\n"
   7270                + indent(onFailureNotCallable(failureCode).define())
   7271                + "}\n",
   7272                type,
   7273                "${declName} = nullptr;\n",
   7274                failureCode,
   7275            )
   7276        return JSToNativeConversionInfo(
   7277            template, declType=declType, declArgs=declArgs, dealWithOptional=isOptional
   7278        )
   7279 
   7280    if type.isAny():
   7281        assert not isEnforceRange and not isClamp and not isAllowShared
   7282 
   7283        declArgs = None
   7284        if isMember in ("Variadic", "Sequence", "Dictionary", "Record"):
   7285            # Rooting is handled by the sequence and dictionary tracers.
   7286            declType = "JS::Value"
   7287        else:
   7288            assert not isMember
   7289            declType = "JS::Rooted<JS::Value>"
   7290            declArgs = "cx"
   7291 
   7292        assert not isOptional
   7293        templateBody = "${declName} = ${val};\n"
   7294 
   7295        # For JS-implemented APIs, we refuse to allow passing objects that the
   7296        # API consumer does not subsume. The extra parens around
   7297        # ($${passedToJSImpl}) suppress unreachable code warnings when
   7298        # $${passedToJSImpl} is the literal `false`.  But Apple is shipping a
   7299        # buggy clang (clang 3.9) in Xcode 8.3, so there even the parens are not
   7300        # enough.  So we manually disable some warnings in clang.
   7301        if (
   7302            not isinstance(descriptorProvider, Descriptor)
   7303            or descriptorProvider.interface.isJSImplemented()
   7304        ):
   7305            templateBody = (
   7306                fill(
   7307                    """
   7308                #ifdef __clang__
   7309                #pragma clang diagnostic push
   7310                #pragma clang diagnostic ignored "-Wunreachable-code"
   7311                #pragma clang diagnostic ignored "-Wunreachable-code-return"
   7312                #endif // __clang__
   7313                if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
   7314                  cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}");
   7315                  $*{exceptionCode}
   7316                }
   7317                #ifdef __clang__
   7318                #pragma clang diagnostic pop
   7319                #endif // __clang__
   7320                """,
   7321                    sourceDescription=sourceDescription,
   7322                    exceptionCode=exceptionCode,
   7323                )
   7324                + templateBody
   7325            )
   7326 
   7327        # We may not have a default value if we're being converted for
   7328        # a setter, say.
   7329        if defaultValue:
   7330            if isinstance(defaultValue, IDLNullValue):
   7331                defaultHandling = "${declName} = JS::NullValue();\n"
   7332            else:
   7333                assert isinstance(defaultValue, IDLUndefinedValue)
   7334                defaultHandling = "${declName} = JS::UndefinedValue();\n"
   7335            templateBody = handleDefault(templateBody, defaultHandling)
   7336        return JSToNativeConversionInfo(
   7337            templateBody, declType=CGGeneric(declType), declArgs=declArgs
   7338        )
   7339 
   7340    if type.isObject():
   7341        assert not isEnforceRange and not isClamp and not isAllowShared
   7342        return handleJSObjectType(
   7343            type, isMember, failureCode, exceptionCode, sourceDescription
   7344        )
   7345 
   7346    if type.isDictionary():
   7347        # There are no nullable dictionary-typed arguments or dictionary-typed
   7348        # dictionary members.
   7349        assert (
   7350            not type.nullable()
   7351            or isCallbackReturnValue
   7352            or (isMember and isMember != "Dictionary")
   7353        )
   7354        # All optional dictionary-typed arguments always have default values,
   7355        # but dictionary-typed dictionary members can be optional.
   7356        assert not isOptional or isMember == "Dictionary"
   7357        # In the callback return value case we never have to worry
   7358        # about a default value; we always have a value.
   7359        assert not isCallbackReturnValue or defaultValue is None
   7360 
   7361        typeName = CGDictionary.makeDictionaryName(type.unroll().inner)
   7362        if (not isMember or isMember == "Union") and not isCallbackReturnValue:
   7363            # Since we're not a member and not nullable or optional, no one will
   7364            # see our real type, so we can do the fast version of the dictionary
   7365            # that doesn't pre-initialize members.
   7366            typeName = "binding_detail::Fast" + typeName
   7367 
   7368        declType = CGGeneric(typeName)
   7369 
   7370        # We do manual default value handling here, because we actually do want
   7371        # a jsval, and we only handle the default-dictionary case (which we map
   7372        # into initialization with the JS value `null`) anyway
   7373        # NOTE: if isNullOrUndefined or isDefinitelyObject are true,
   7374        # we know we have a value, so we don't have to worry about the
   7375        # default value.
   7376        if (
   7377            not isNullOrUndefined
   7378            and not isDefinitelyObject
   7379            and defaultValue is not None
   7380        ):
   7381            assert isinstance(defaultValue, IDLDefaultDictionaryValue)
   7382            # Initializing from JS null does the right thing to give
   7383            # us a default-initialized dictionary.
   7384            val = "(${haveValue}) ? ${val} : JS::NullHandleValue"
   7385        else:
   7386            val = "${val}"
   7387 
   7388        dictLoc = "${declName}"
   7389        if type.nullable():
   7390            dictLoc += ".SetValue()"
   7391 
   7392        if type.unroll().inner.needsConversionFromJS:
   7393            args = "cx, %s, " % val
   7394        else:
   7395            # We can end up in this case if a dictionary that does not need
   7396            # conversion from JS has a dictionary-typed member with a default
   7397            # value of {}.
   7398            args = ""
   7399        conversionCode = fill(
   7400            """
   7401            if (!${dictLoc}.Init(${args}"${desc}", $${passedToJSImpl})) {
   7402              $*{exceptionCode}
   7403            }
   7404            """,
   7405            dictLoc=dictLoc,
   7406            args=args,
   7407            desc=firstCap(sourceDescription),
   7408            exceptionCode=exceptionCode,
   7409        )
   7410 
   7411        if failureCode is not None:
   7412            # This means we're part of an overload or union conversion, and
   7413            # should simply skip stuff if our value is not convertible to
   7414            # dictionary, instead of trying and throwing.  If we're either
   7415            # isDefinitelyObject or isNullOrUndefined then we're convertible to
   7416            # dictionary and don't need to check here.
   7417            if isDefinitelyObject or isNullOrUndefined:
   7418                template = conversionCode
   7419            else:
   7420                template = fill(
   7421                    """
   7422                    if (!IsConvertibleToDictionary(${val})) {
   7423                      $*{failureCode}
   7424                    }
   7425                    $*{conversionCode}
   7426                    """,
   7427                    val=val,
   7428                    failureCode=failureCode,
   7429                    conversionCode=conversionCode,
   7430                )
   7431        else:
   7432            template = conversionCode
   7433 
   7434        if type.nullable():
   7435            declType = CGTemplatedType("Nullable", declType)
   7436            template = CGIfElseWrapper(
   7437                "${val}.isNullOrUndefined()",
   7438                CGGeneric("${declName}.SetNull();\n"),
   7439                CGGeneric(template),
   7440            ).define()
   7441 
   7442        # Dictionary arguments that might contain traceable things need to get
   7443        # traced
   7444        if (not isMember or isMember == "Union") and isCallbackReturnValue:
   7445            # Go ahead and just convert directly into our actual return value
   7446            declType = CGWrapper(declType, post="&")
   7447            declArgs = "aRetVal"
   7448        elif (not isMember or isMember == "Union") and typeNeedsRooting(type):
   7449            declType = CGTemplatedType("RootedDictionary", declType)
   7450            declArgs = "cx"
   7451        else:
   7452            declArgs = None
   7453 
   7454        return JSToNativeConversionInfo(
   7455            template, declType=declType, declArgs=declArgs, dealWithOptional=isOptional
   7456        )
   7457 
   7458    if type.isUndefined():
   7459        assert not isOptional
   7460        # This one only happens for return values, and its easy: Just
   7461        # ignore the jsval.
   7462        return JSToNativeConversionInfo("")
   7463 
   7464    if not type.isPrimitive():
   7465        raise TypeError("Need conversion for argument type '%s'" % str(type))
   7466 
   7467    typeName = builtinNames[type.tag()]
   7468 
   7469    conversionBehavior = "eDefault"
   7470    if isEnforceRange:
   7471        assert type.isInteger()
   7472        conversionBehavior = "eEnforceRange"
   7473    elif isClamp:
   7474        assert type.isInteger()
   7475        conversionBehavior = "eClamp"
   7476 
   7477    alwaysNull = False
   7478    if type.nullable():
   7479        declType = CGGeneric("Nullable<" + typeName + ">")
   7480        writeLoc = "${declName}.SetValue()"
   7481        readLoc = "${declName}.Value()"
   7482        nullCondition = "${val}.isNullOrUndefined()"
   7483        if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
   7484            nullCondition = "!(${haveValue}) || " + nullCondition
   7485            if isKnownMissing:
   7486                alwaysNull = True
   7487                template = dedent(
   7488                    """
   7489                    ${declName}.SetNull();
   7490                    """
   7491                )
   7492        if not alwaysNull:
   7493            template = fill(
   7494                """
   7495                if (${nullCondition}) {
   7496                  $${declName}.SetNull();
   7497                } else if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, "${sourceDescription}", &${writeLoc})) {
   7498                  $*{exceptionCode}
   7499                }
   7500                """,
   7501                nullCondition=nullCondition,
   7502                typeName=typeName,
   7503                conversionBehavior=conversionBehavior,
   7504                sourceDescription=firstCap(sourceDescription),
   7505                writeLoc=writeLoc,
   7506                exceptionCode=exceptionCode,
   7507            )
   7508    else:
   7509        assert defaultValue is None or not isinstance(defaultValue, IDLNullValue)
   7510        writeLoc = "${declName}"
   7511        readLoc = writeLoc
   7512        template = fill(
   7513            """
   7514            if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, "${sourceDescription}", &${writeLoc})) {
   7515              $*{exceptionCode}
   7516            }
   7517            """,
   7518            typeName=typeName,
   7519            conversionBehavior=conversionBehavior,
   7520            sourceDescription=firstCap(sourceDescription),
   7521            writeLoc=writeLoc,
   7522            exceptionCode=exceptionCode,
   7523        )
   7524        declType = CGGeneric(typeName)
   7525 
   7526    if type.isFloat() and not type.isUnrestricted() and not alwaysNull:
   7527        if lenientFloatCode is not None:
   7528            nonFiniteCode = lenientFloatCode
   7529        else:
   7530            nonFiniteCode = 'cx.ThrowErrorMessage<MSG_NOT_FINITE>("%s");\n' "%s" % (
   7531                firstCap(sourceDescription),
   7532                exceptionCode,
   7533            )
   7534 
   7535        # We're appending to an if-block brace, so strip trailing whitespace
   7536        # and add an extra space before the else.
   7537        template = template.rstrip()
   7538        template += fill(
   7539            """
   7540             else if (!std::isfinite(${readLoc})) {
   7541              $*{nonFiniteCode}
   7542            }
   7543            """,
   7544            readLoc=readLoc,
   7545            nonFiniteCode=nonFiniteCode,
   7546        )
   7547 
   7548    if (
   7549        defaultValue is not None
   7550        and
   7551        # We already handled IDLNullValue, so just deal with the other ones
   7552        not isinstance(defaultValue, IDLNullValue)
   7553    ):
   7554        tag = defaultValue.type.tag()
   7555        defaultStr = getHandleDefault(defaultValue)
   7556        template = handleDefault(template, "%s = %s;\n" % (writeLoc, defaultStr))
   7557 
   7558    return JSToNativeConversionInfo(
   7559        template, declType=declType, dealWithOptional=isOptional
   7560    )
   7561 
   7562 
   7563 def instantiateJSToNativeConversion(info, replacements, checkForValue=False):
   7564    """
   7565    Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo
   7566    and a set of replacements as required by the strings in such an object, and
   7567    generate code to convert into stack C++ types.
   7568 
   7569    If checkForValue is True, then the conversion will get wrapped in
   7570    a check for ${haveValue}.
   7571    """
   7572    templateBody, declType, holderType, dealWithOptional = (
   7573        info.template,
   7574        info.declType,
   7575        info.holderType,
   7576        info.dealWithOptional,
   7577    )
   7578 
   7579    if dealWithOptional and not checkForValue:
   7580        raise TypeError("Have to deal with optional things, but don't know how")
   7581    if checkForValue and declType is None:
   7582        raise TypeError(
   7583            "Need to predeclare optional things, so they will be "
   7584            "outside the check for big enough arg count!"
   7585        )
   7586 
   7587    # We can't precompute our holder constructor arguments, since
   7588    # those might depend on ${declName}, which we change below.  Just
   7589    # compute arguments at the point when we need them as we go.
   7590    def getArgsCGThing(args):
   7591        return CGGeneric(string.Template(args).substitute(replacements))
   7592 
   7593    result = CGList([])
   7594    # Make a copy of "replacements" since we may be about to start modifying it
   7595    replacements = dict(replacements)
   7596    originalDeclName = replacements["declName"]
   7597    if declType is not None:
   7598        if dealWithOptional:
   7599            replacements["declName"] = "%s.Value()" % originalDeclName
   7600            declType = CGTemplatedType("Optional", declType)
   7601            declCtorArgs = None
   7602        elif info.declArgs is not None:
   7603            declCtorArgs = CGWrapper(getArgsCGThing(info.declArgs), pre="(", post=")")
   7604        else:
   7605            declCtorArgs = None
   7606        result.append(
   7607            CGList(
   7608                [
   7609                    declType,
   7610                    CGGeneric(" "),
   7611                    CGGeneric(originalDeclName),
   7612                    declCtorArgs,
   7613                    CGGeneric(";\n"),
   7614                ]
   7615            )
   7616        )
   7617 
   7618    originalHolderName = replacements["holderName"]
   7619    if holderType is not None:
   7620        if dealWithOptional:
   7621            replacements["holderName"] = "%s.ref()" % originalHolderName
   7622            holderType = CGTemplatedType("Maybe", holderType)
   7623            holderCtorArgs = None
   7624        elif info.holderArgs is not None:
   7625            holderCtorArgs = CGWrapper(
   7626                getArgsCGThing(info.holderArgs), pre="(", post=")"
   7627            )
   7628        else:
   7629            holderCtorArgs = None
   7630        result.append(
   7631            CGList(
   7632                [
   7633                    holderType,
   7634                    CGGeneric(" "),
   7635                    CGGeneric(originalHolderName),
   7636                    holderCtorArgs,
   7637                    CGGeneric(";\n"),
   7638                ]
   7639            )
   7640        )
   7641 
   7642    if "maybeMutableVal" not in replacements:
   7643        replacements["maybeMutableVal"] = replacements["val"]
   7644 
   7645    conversion = CGGeneric(string.Template(templateBody).substitute(replacements))
   7646 
   7647    if checkForValue:
   7648        if dealWithOptional:
   7649            declConstruct = CGIndenter(
   7650                CGGeneric(
   7651                    "%s.Construct(%s);\n"
   7652                    % (
   7653                        originalDeclName,
   7654                        getArgsCGThing(info.declArgs).define() if info.declArgs else "",
   7655                    )
   7656                )
   7657            )
   7658            if holderType is not None:
   7659                holderConstruct = CGIndenter(
   7660                    CGGeneric(
   7661                        "%s.emplace(%s);\n"
   7662                        % (
   7663                            originalHolderName,
   7664                            (
   7665                                getArgsCGThing(info.holderArgs).define()
   7666                                if info.holderArgs
   7667                                else ""
   7668                            ),
   7669                        )
   7670                    )
   7671                )
   7672            else:
   7673                holderConstruct = None
   7674        else:
   7675            declConstruct = None
   7676            holderConstruct = None
   7677 
   7678        conversion = CGList(
   7679            [
   7680                CGGeneric(
   7681                    string.Template("if (${haveValue}) {\n").substitute(replacements)
   7682                ),
   7683                declConstruct,
   7684                holderConstruct,
   7685                CGIndenter(conversion),
   7686                CGGeneric("}\n"),
   7687            ]
   7688        )
   7689 
   7690    result.append(conversion)
   7691    return result
   7692 
   7693 
   7694 def convertConstIDLValueToJSVal(value):
   7695    if isinstance(value, IDLNullValue):
   7696        return "JS::NullValue()"
   7697    if isinstance(value, IDLUndefinedValue):
   7698        return "JS::UndefinedValue()"
   7699    tag = value.type.tag()
   7700    if tag in [
   7701        IDLType.Tags.int8,
   7702        IDLType.Tags.uint8,
   7703        IDLType.Tags.int16,
   7704        IDLType.Tags.uint16,
   7705        IDLType.Tags.int32,
   7706    ]:
   7707        return "JS::Int32Value(%s)" % (value.value)
   7708    if tag == IDLType.Tags.uint32:
   7709        return "JS::NumberValue(%sU)" % (value.value)
   7710    if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
   7711        return "JS::CanonicalizedDoubleValue(%s)" % numericValue(tag, value.value)
   7712    if tag == IDLType.Tags.bool:
   7713        return "JS::BooleanValue(%s)" % (toStringBool(value.value))
   7714    if tag in [IDLType.Tags.float, IDLType.Tags.double]:
   7715        return "JS::CanonicalizedDoubleValue(%s)" % (value.value)
   7716    raise TypeError("Const value of unhandled type: %s" % value.type)
   7717 
   7718 
   7719 class CGArgumentConverter(CGThing):
   7720    """
   7721    A class that takes an IDL argument object and its index in the
   7722    argument list and generates code to unwrap the argument to the
   7723    right native type.
   7724 
   7725    argDescription is a description of the argument for error-reporting
   7726    purposes.  Callers should assume that it might get placed in the middle of a
   7727    sentence.  If it ends up at the beginning of a sentence, its first character
   7728    will be automatically uppercased.
   7729    """
   7730 
   7731    def __init__(
   7732        self,
   7733        argument,
   7734        index,
   7735        descriptorProvider,
   7736        argDescription,
   7737        member,
   7738        invalidEnumValueFatal=True,
   7739        lenientFloatCode=None,
   7740    ):
   7741        CGThing.__init__(self)
   7742        self.argument = argument
   7743        self.argDescription = argDescription
   7744        assert not argument.defaultValue or argument.optional
   7745 
   7746        replacer = {"index": index, "argc": "args.length()"}
   7747        self.replacementVariables = {
   7748            "declName": "arg%d" % index,
   7749            "holderName": ("arg%d" % index) + "_holder",
   7750            "obj": "obj",
   7751            "passedToJSImpl": toStringBool(
   7752                isJSImplementedDescriptor(descriptorProvider)
   7753            ),
   7754        }
   7755        # If we have a method generated by the maplike/setlike portion of an
   7756        # interface, arguments can possibly be undefined, but will need to be
   7757        # converted to the key/value type of the backing object. In this case,
   7758        # use .get() instead of direct access to the argument. This won't
   7759        # matter for iterable since generated functions for those interface
   7760        # don't take arguments.
   7761        if member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod():
   7762            self.replacementVariables["val"] = string.Template(
   7763                "args.get(${index})"
   7764            ).substitute(replacer)
   7765            self.replacementVariables["maybeMutableVal"] = string.Template(
   7766                "args[${index}]"
   7767            ).substitute(replacer)
   7768        else:
   7769            self.replacementVariables["val"] = string.Template(
   7770                "args[${index}]"
   7771            ).substitute(replacer)
   7772        haveValueCheck = string.Template("args.hasDefined(${index})").substitute(
   7773            replacer
   7774        )
   7775        self.replacementVariables["haveValue"] = haveValueCheck
   7776        self.descriptorProvider = descriptorProvider
   7777        if self.argument.canHaveMissingValue():
   7778            self.argcAndIndex = replacer
   7779        else:
   7780            self.argcAndIndex = None
   7781        self.invalidEnumValueFatal = invalidEnumValueFatal
   7782        self.lenientFloatCode = lenientFloatCode
   7783 
   7784    def define(self):
   7785        typeConversion = getJSToNativeConversionInfo(
   7786            self.argument.type,
   7787            self.descriptorProvider,
   7788            isOptional=(self.argcAndIndex is not None and not self.argument.variadic),
   7789            invalidEnumValueFatal=self.invalidEnumValueFatal,
   7790            defaultValue=self.argument.defaultValue,
   7791            lenientFloatCode=self.lenientFloatCode,
   7792            isMember="Variadic" if self.argument.variadic else False,
   7793            allowTreatNonCallableAsNull=self.argument.allowTreatNonCallableAsNull(),
   7794            sourceDescription=self.argDescription,
   7795        )
   7796 
   7797        if not self.argument.variadic:
   7798            return instantiateJSToNativeConversion(
   7799                typeConversion, self.replacementVariables, self.argcAndIndex is not None
   7800            ).define()
   7801 
   7802        # Variadic arguments get turned into a sequence.
   7803        if typeConversion.dealWithOptional:
   7804            raise TypeError("Shouldn't have optional things in variadics")
   7805        if typeConversion.holderType is not None:
   7806            raise TypeError("Shouldn't need holders for variadics")
   7807 
   7808        replacer = dict(self.argcAndIndex, **self.replacementVariables)
   7809        replacer["seqType"] = CGTemplatedType(
   7810            "AutoSequence", typeConversion.declType
   7811        ).define()
   7812        if typeNeedsRooting(self.argument.type):
   7813            rooterDecl = (
   7814                "SequenceRooter<%s> ${holderName}(cx, &${declName});\n"
   7815                % typeConversion.declType.define()
   7816            )
   7817        else:
   7818            rooterDecl = ""
   7819        replacer["elemType"] = typeConversion.declType.define()
   7820 
   7821        replacer["elementInitializer"] = initializerForType(self.argument.type) or ""
   7822 
   7823        # NOTE: Keep this in sync with sequence conversions as needed
   7824        variadicConversion = string.Template(
   7825            "${seqType} ${declName};\n"
   7826            + rooterDecl
   7827            + dedent(
   7828                """
   7829                if (${argc} > ${index}) {
   7830                  if (!${declName}.SetCapacity(${argc} - ${index}, mozilla::fallible)) {
   7831                    JS_ReportOutOfMemory(cx);
   7832                    return false;
   7833                  }
   7834                  for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) {
   7835                    // OK to do infallible append here, since we ensured capacity already.
   7836                    ${elemType}& slot = *${declName}.AppendElement(${elementInitializer});
   7837                """
   7838            )
   7839        ).substitute(replacer)
   7840 
   7841        val = string.Template("args[variadicArg]").substitute(replacer)
   7842        variadicConversion += indent(
   7843            string.Template(typeConversion.template).substitute(
   7844                {
   7845                    "val": val,
   7846                    "maybeMutableVal": val,
   7847                    "declName": "slot",
   7848                    # We only need holderName here to handle isExternal()
   7849                    # interfaces, which use an internal holder for the
   7850                    # conversion even when forceOwningType ends up true.
   7851                    "holderName": "tempHolder",
   7852                    # Use the same ${obj} as for the variadic arg itself
   7853                    "obj": replacer["obj"],
   7854                    "passedToJSImpl": toStringBool(
   7855                        isJSImplementedDescriptor(self.descriptorProvider)
   7856                    ),
   7857                }
   7858            ),
   7859            4,
   7860        )
   7861 
   7862        variadicConversion += "  }\n" "}\n"
   7863        return variadicConversion
   7864 
   7865 
   7866 def getMaybeWrapValueFuncForType(type):
   7867    if type.isJSString():
   7868        return "MaybeWrapStringValue"
   7869    # Callbacks might actually be DOM objects; nothing prevents a page from
   7870    # doing that.
   7871    if type.isCallback() or type.isCallbackInterface() or type.isObject():
   7872        if type.nullable():
   7873            return "MaybeWrapObjectOrNullValue"
   7874        return "MaybeWrapObjectValue"
   7875    # SpiderMonkey interfaces are never DOM objects.  Neither are sequences or
   7876    # dictionaries, since those are always plain JS objects.
   7877    if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence():
   7878        if type.nullable():
   7879            return "MaybeWrapNonDOMObjectOrNullValue"
   7880        return "MaybeWrapNonDOMObjectValue"
   7881    if type.isAny():
   7882        return "MaybeWrapValue"
   7883 
   7884    # For other types, just go ahead an fall back on MaybeWrapValue for now:
   7885    # it's always safe to do, and shouldn't be particularly slow for any of
   7886    # them
   7887    return "MaybeWrapValue"
   7888 
   7889 
   7890 sequenceWrapLevel = 0
   7891 recordWrapLevel = 0
   7892 
   7893 
   7894 def getWrapTemplateForType(
   7895    type,
   7896    descriptorProvider,
   7897    result,
   7898    successCode,
   7899    returnsNewObject,
   7900    exceptionCode,
   7901    spiderMonkeyInterfacesAreStructs,
   7902    isConstructorRetval=False,
   7903 ):
   7904    """
   7905    Reflect a C++ value stored in "result", of IDL type "type" into JS.  The
   7906    "successCode" is the code to run once we have successfully done the
   7907    conversion and must guarantee that execution of the conversion template
   7908    stops once the successCode has executed (e.g. by doing a 'return', or by
   7909    doing a 'break' if the entire conversion template is inside a block that
   7910    the 'break' will exit).
   7911 
   7912    If spiderMonkeyInterfacesAreStructs is true, then if the type is a
   7913    SpiderMonkey interface, "result" is one of the
   7914    dom::SpiderMonkeyInterfaceObjectStorage subclasses, not a JSObject*.
   7915 
   7916    The resulting string should be used with string.Template.  It
   7917    needs the following keys when substituting:
   7918 
   7919      jsvalHandle: something that can be passed to methods taking a
   7920                   JS::MutableHandle<JS::Value>.  This can be a
   7921                   JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
   7922      jsvalRef: something that can have .address() called on it to get a
   7923                JS::Value* and .set() called on it to set it to a JS::Value.
   7924                This can be a JS::MutableHandle<JS::Value> or a
   7925                JS::Rooted<JS::Value>.
   7926      obj: a JS::Handle<JSObject*>.
   7927 
   7928    Returns (templateString, infallibility of conversion template)
   7929    """
   7930    if successCode is None:
   7931        successCode = "return true;\n"
   7932 
   7933    def setUndefined():
   7934        return _setValue("", setter="setUndefined")
   7935 
   7936    def setNull():
   7937        return _setValue("", setter="setNull")
   7938 
   7939    def setInt32(value):
   7940        return _setValue(value, setter="setInt32")
   7941 
   7942    def setString(value):
   7943        return _setValue(value, wrapAsType=type, setter="setString")
   7944 
   7945    def setObject(value, wrapAsType=None):
   7946        return _setValue(value, wrapAsType=wrapAsType, setter="setObject")
   7947 
   7948    def setObjectOrNull(value, wrapAsType=None):
   7949        return _setValue(value, wrapAsType=wrapAsType, setter="setObjectOrNull")
   7950 
   7951    def setUint32(value):
   7952        return _setValue(value, setter="setNumber")
   7953 
   7954    def setDouble(value):
   7955        return _setValue("JS_NumberValue(%s)" % value)
   7956 
   7957    def setBoolean(value):
   7958        return _setValue(value, setter="setBoolean")
   7959 
   7960    def _setValue(value, wrapAsType=None, setter="set"):
   7961        """
   7962        Returns the code to set the jsval to value.
   7963 
   7964        If wrapAsType is not None, then will wrap the resulting value using the
   7965        function that getMaybeWrapValueFuncForType(wrapAsType) returns.
   7966        Otherwise, no wrapping will be done.
   7967        """
   7968        if wrapAsType is None:
   7969            tail = successCode
   7970        else:
   7971            tail = fill(
   7972                """
   7973                if (!${maybeWrap}(cx, $${jsvalHandle})) {
   7974                  $*{exceptionCode}
   7975                }
   7976                $*{successCode}
   7977                """,
   7978                maybeWrap=getMaybeWrapValueFuncForType(wrapAsType),
   7979                exceptionCode=exceptionCode,
   7980                successCode=successCode,
   7981            )
   7982        return ("${jsvalRef}.%s(%s);\n" % (setter, value)) + tail
   7983 
   7984    def wrapAndSetPtr(wrapCall, failureCode=None):
   7985        """
   7986        Returns the code to set the jsval by calling "wrapCall". "failureCode"
   7987        is the code to run if calling "wrapCall" fails
   7988        """
   7989        if failureCode is None:
   7990            failureCode = exceptionCode
   7991        return fill(
   7992            """
   7993            if (!${wrapCall}) {
   7994              $*{failureCode}
   7995            }
   7996            $*{successCode}
   7997            """,
   7998            wrapCall=wrapCall,
   7999            failureCode=failureCode,
   8000            successCode=successCode,
   8001        )
   8002 
   8003    if type is None or type.isUndefined():
   8004        return (setUndefined(), True)
   8005 
   8006    if (type.isSequence() or type.isRecord()) and type.nullable():
   8007        # These are both wrapped in Nullable<>
   8008        recTemplate, recInfall = getWrapTemplateForType(
   8009            type.inner,
   8010            descriptorProvider,
   8011            "%s.Value()" % result,
   8012            successCode,
   8013            returnsNewObject,
   8014            exceptionCode,
   8015            spiderMonkeyInterfacesAreStructs,
   8016        )
   8017        code = fill(
   8018            """
   8019 
   8020            if (${result}.IsNull()) {
   8021              $*{setNull}
   8022            }
   8023            $*{recTemplate}
   8024            """,
   8025            result=result,
   8026            setNull=setNull(),
   8027            recTemplate=recTemplate,
   8028        )
   8029        return code, recInfall
   8030 
   8031    if type.isSequence():
   8032        # Now do non-nullable sequences.  Our success code is just to break to
   8033        # where we set the element in the array.  Note that we bump the
   8034        # sequenceWrapLevel around this call so that nested sequence conversions
   8035        # will use different iteration variables.
   8036        global sequenceWrapLevel
   8037        index = "sequenceIdx%d" % sequenceWrapLevel
   8038        sequenceWrapLevel += 1
   8039        innerTemplate = wrapForType(
   8040            type.inner,
   8041            descriptorProvider,
   8042            {
   8043                "result": "%s[%s]" % (result, index),
   8044                "successCode": "break;\n",
   8045                "jsvalRef": "tmp",
   8046                "jsvalHandle": "&tmp",
   8047                "returnsNewObject": returnsNewObject,
   8048                "exceptionCode": exceptionCode,
   8049                "obj": "returnArray",
   8050                "spiderMonkeyInterfacesAreStructs": spiderMonkeyInterfacesAreStructs,
   8051            },
   8052        )
   8053        sequenceWrapLevel -= 1
   8054        code = fill(
   8055            """
   8056 
   8057            uint32_t length = ${result}.Length();
   8058            JS::Rooted<JSObject*> returnArray(cx, JS::NewArrayObject(cx, length));
   8059            if (!returnArray) {
   8060              $*{exceptionCode}
   8061            }
   8062            // Scope for 'tmp'
   8063            {
   8064              JS::Rooted<JS::Value> tmp(cx);
   8065              for (uint32_t ${index} = 0; ${index} < length; ++${index}) {
   8066                // Control block to let us common up the JS_DefineElement calls when there
   8067                // are different ways to succeed at wrapping the object.
   8068                do {
   8069                  $*{innerTemplate}
   8070                } while (false);
   8071                if (!JS_DefineElement(cx, returnArray, ${index}, tmp,
   8072                                      JSPROP_ENUMERATE)) {
   8073                  $*{exceptionCode}
   8074                }
   8075              }
   8076            }
   8077            $*{set}
   8078            """,
   8079            result=result,
   8080            exceptionCode=exceptionCode,
   8081            index=index,
   8082            innerTemplate=innerTemplate,
   8083            set=setObject("*returnArray"),
   8084        )
   8085 
   8086        return (code, False)
   8087 
   8088    if type.isRecord():
   8089        # Now do non-nullable record.  Our success code is just to break to
   8090        # where we define the property on the object.  Note that we bump the
   8091        # recordWrapLevel around this call so that nested record conversions
   8092        # will use different temp value names.
   8093        global recordWrapLevel
   8094        valueName = "recordValue%d" % recordWrapLevel
   8095        recordWrapLevel += 1
   8096        innerTemplate = wrapForType(
   8097            type.inner,
   8098            descriptorProvider,
   8099            {
   8100                "result": valueName,
   8101                "successCode": "break;\n",
   8102                "jsvalRef": "tmp",
   8103                "jsvalHandle": "&tmp",
   8104                "returnsNewObject": returnsNewObject,
   8105                "exceptionCode": exceptionCode,
   8106                "obj": "returnObj",
   8107                "spiderMonkeyInterfacesAreStructs": spiderMonkeyInterfacesAreStructs,
   8108            },
   8109        )
   8110        recordWrapLevel -= 1
   8111        if type.keyType.isByteString():
   8112            # There is no length-taking JS_DefineProperty.  So to keep
   8113            # things sane with embedded nulls, we want to byte-inflate
   8114            # to an nsAString.  The only byte-inflation function we
   8115            # have around is AppendASCIItoUTF16, which luckily doesn't
   8116            # assert anything about the input being ASCII.
   8117            expandedKeyDecl = "NS_ConvertASCIItoUTF16 expandedKey(entry.mKey);\n"
   8118            keyName = "expandedKey"
   8119        elif type.keyType.isUTF8String():
   8120            # We do the same as above for utf8 strings. We could do better if
   8121            # we had a DefineProperty API that takes utf-8 property names.
   8122            expandedKeyDecl = "NS_ConvertUTF8toUTF16 expandedKey(entry.mKey);\n"
   8123            keyName = "expandedKey"
   8124        else:
   8125            expandedKeyDecl = ""
   8126            keyName = "entry.mKey"
   8127 
   8128        code = fill(
   8129            """
   8130 
   8131            JS::Rooted<JSObject*> returnObj(cx, JS_NewPlainObject(cx));
   8132            if (!returnObj) {
   8133              $*{exceptionCode}
   8134            }
   8135            // Scope for 'tmp'
   8136            {
   8137              JS::Rooted<JS::Value> tmp(cx);
   8138              for (auto& entry : ${result}.Entries()) {
   8139                auto& ${valueName} = entry.mValue;
   8140                // Control block to let us common up the JS_DefineUCProperty calls when there
   8141                // are different ways to succeed at wrapping the value.
   8142                do {
   8143                  $*{innerTemplate}
   8144                } while (false);
   8145                $*{expandedKeyDecl}
   8146                if (!JS_DefineUCProperty(cx, returnObj,
   8147                                         ${keyName}.BeginReading(),
   8148                                         ${keyName}.Length(), tmp,
   8149                                         JSPROP_ENUMERATE)) {
   8150                  $*{exceptionCode}
   8151                }
   8152              }
   8153            }
   8154            $*{set}
   8155            """,
   8156            result=result,
   8157            exceptionCode=exceptionCode,
   8158            valueName=valueName,
   8159            innerTemplate=innerTemplate,
   8160            expandedKeyDecl=expandedKeyDecl,
   8161            keyName=keyName,
   8162            set=setObject("*returnObj"),
   8163        )
   8164 
   8165        return (code, False)
   8166 
   8167    if type.isPromise():
   8168        assert not type.nullable()
   8169        # The use of ToJSValue here is a bit annoying because the Promise
   8170        # version is not inlined.  But we can't put an inline version in either
   8171        # ToJSValue.h or BindingUtils.h, because Promise.h includes ToJSValue.h
   8172        # and that includes BindingUtils.h, so we'd get an include loop if
   8173        # either of those headers included Promise.h.  And trying to write the
   8174        # conversion by hand here is pretty annoying because we have to handle
   8175        # the various RefPtr, rawptr, NonNull, etc cases, which ToJSValue will
   8176        # handle for us.  So just eat the cost of the function call.
   8177        return (wrapAndSetPtr("ToJSValue(cx, %s, ${jsvalHandle})" % result), False)
   8178 
   8179    if type.isGeckoInterface() and not type.isCallbackInterface():
   8180        descriptor = descriptorProvider.getDescriptor(
   8181            type.unroll().inner.identifier.name
   8182        )
   8183        if type.nullable():
   8184            if descriptor.interface.identifier.name == "WindowProxy":
   8185                template, infal = getWrapTemplateForType(
   8186                    type.inner,
   8187                    descriptorProvider,
   8188                    "%s.Value()" % result,
   8189                    successCode,
   8190                    returnsNewObject,
   8191                    exceptionCode,
   8192                    spiderMonkeyInterfacesAreStructs,
   8193                )
   8194                return (
   8195                    "if (%s.IsNull()) {\n" % result
   8196                    + indent(setNull())
   8197                    + "}\n"
   8198                    + template,
   8199                    infal,
   8200                )
   8201 
   8202            wrappingCode = "if (!%s) {\n" % (result) + indent(setNull()) + "}\n"
   8203        else:
   8204            wrappingCode = ""
   8205 
   8206        if not descriptor.interface.isExternal():
   8207            if descriptor.wrapperCache:
   8208                wrapMethod = "GetOrCreateDOMReflector"
   8209                wrapArgs = "cx, %s, ${jsvalHandle}" % result
   8210            else:
   8211                wrapMethod = "WrapNewBindingNonWrapperCachedObject"
   8212                wrapArgs = "cx, ${obj}, %s, ${jsvalHandle}" % result
   8213            if isConstructorRetval:
   8214                wrapArgs += ", desiredProto"
   8215            wrap = "%s(%s)" % (wrapMethod, wrapArgs)
   8216            # Can only fail to wrap as a new-binding object if they already
   8217            # threw an exception.
   8218            failed = "MOZ_ASSERT(JS_IsExceptionPending(cx));\n" + exceptionCode
   8219        else:
   8220            if descriptor.notflattened:
   8221                getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
   8222            else:
   8223                getIID = ""
   8224            wrap = "WrapObject(cx, %s, %s${jsvalHandle})" % (result, getIID)
   8225            failed = None
   8226 
   8227        wrappingCode += wrapAndSetPtr(wrap, failed)
   8228        return (wrappingCode, False)
   8229 
   8230    if type.isJSString():
   8231        return (setString(result), False)
   8232 
   8233    if type.isDOMString() or type.isUSVString():
   8234        if type.nullable():
   8235            return (
   8236                wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result),
   8237                False,
   8238            )
   8239        else:
   8240            return (
   8241                wrapAndSetPtr(
   8242                    "xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result
   8243                ),
   8244                False,
   8245            )
   8246 
   8247    if type.isByteString():
   8248        if type.nullable():
   8249            return (
   8250                wrapAndSetPtr("ByteStringToJsval(cx, %s, ${jsvalHandle})" % result),
   8251                False,
   8252            )
   8253        else:
   8254            return (
   8255                wrapAndSetPtr(
   8256                    "NonVoidByteStringToJsval(cx, %s, ${jsvalHandle})" % result
   8257                ),
   8258                False,
   8259            )
   8260 
   8261    if type.isUTF8String():
   8262        if type.nullable():
   8263            return (
   8264                wrapAndSetPtr("UTF8StringToJsval(cx, %s, ${jsvalHandle})" % result),
   8265                False,
   8266            )
   8267        else:
   8268            return (
   8269                wrapAndSetPtr(
   8270                    "NonVoidUTF8StringToJsval(cx, %s, ${jsvalHandle})" % result
   8271                ),
   8272                False,
   8273            )
   8274 
   8275    if type.isEnum():
   8276        if type.nullable():
   8277            resultLoc = "%s.Value()" % result
   8278        else:
   8279            resultLoc = result
   8280        conversion = fill(
   8281            """
   8282            if (!ToJSValue(cx, ${result}, $${jsvalHandle})) {
   8283              $*{exceptionCode}
   8284            }
   8285            $*{successCode}
   8286            """,
   8287            result=resultLoc,
   8288            exceptionCode=exceptionCode,
   8289            successCode=successCode,
   8290        )
   8291 
   8292        if type.nullable():
   8293            conversion = CGIfElseWrapper(
   8294                "%s.IsNull()" % result, CGGeneric(setNull()), CGGeneric(conversion)
   8295            ).define()
   8296        return conversion, False
   8297 
   8298    if type.isCallback() or type.isCallbackInterface():
   8299        # Callbacks can store null if we nuked the compartments their
   8300        # objects lived in.
   8301        wrapCode = setObjectOrNull(
   8302            "GetCallbackFromCallbackObject(cx, %(result)s)", wrapAsType=type
   8303        )
   8304        if type.nullable():
   8305            wrapCode = (
   8306                "if (%(result)s) {\n"
   8307                + indent(wrapCode)
   8308                + "} else {\n"
   8309                + indent(setNull())
   8310                + "}\n"
   8311            )
   8312        wrapCode = wrapCode % {"result": result}
   8313        return wrapCode, False
   8314 
   8315    if type.isAny():
   8316        # See comments in GetOrCreateDOMReflector explaining why we need
   8317        # to wrap here.
   8318        # NB: _setValue(..., type-that-is-any) calls JS_WrapValue(), so is fallible
   8319        head = "JS::ExposeValueToActiveJS(%s);\n" % result
   8320        return (head + _setValue(result, wrapAsType=type), False)
   8321 
   8322    if type.isObject() or (
   8323        type.isSpiderMonkeyInterface() and not spiderMonkeyInterfacesAreStructs
   8324    ):
   8325        # See comments in GetOrCreateDOMReflector explaining why we need
   8326        # to wrap here.
   8327        if type.nullable():
   8328            toValue = "%s"
   8329            setter = setObjectOrNull
   8330            head = """if (%s) {
   8331              JS::ExposeObjectToActiveJS(%s);
   8332            }
   8333            """ % (
   8334                result,
   8335                result,
   8336            )
   8337        else:
   8338            toValue = "*%s"
   8339            setter = setObject
   8340            head = "JS::ExposeObjectToActiveJS(%s);\n" % result
   8341        # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible
   8342        return (head + setter(toValue % result, wrapAsType=type), False)
   8343 
   8344    if type.isObservableArray():
   8345        # This first argument isn't used at all for now, the attribute getter
   8346        # for ObservableArray type are generated in getObservableArrayGetterBody
   8347        # instead.
   8348        return "", False
   8349 
   8350    if not (
   8351        type.isUnion()
   8352        or type.isPrimitive()
   8353        or type.isDictionary()
   8354        or (type.isSpiderMonkeyInterface() and spiderMonkeyInterfacesAreStructs)
   8355    ):
   8356        raise TypeError("Need to learn to wrap %s" % type)
   8357 
   8358    if type.nullable():
   8359        recTemplate, recInfal = getWrapTemplateForType(
   8360            type.inner,
   8361            descriptorProvider,
   8362            "%s.Value()" % result,
   8363            successCode,
   8364            returnsNewObject,
   8365            exceptionCode,
   8366            spiderMonkeyInterfacesAreStructs,
   8367        )
   8368        return (
   8369            "if (%s.IsNull()) {\n" % result + indent(setNull()) + "}\n" + recTemplate,
   8370            recInfal,
   8371        )
   8372 
   8373    if type.isSpiderMonkeyInterface():
   8374        assert spiderMonkeyInterfacesAreStructs
   8375        # See comments in GetOrCreateDOMReflector explaining why we need
   8376        # to wrap here.
   8377        # NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible
   8378        return (setObject("*%s.Obj()" % result, wrapAsType=type), False)
   8379 
   8380    if type.isUnion():
   8381        return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result), False)
   8382 
   8383    if type.isDictionary():
   8384        return (
   8385            wrapAndSetPtr("%s.ToObjectInternal(cx, ${jsvalHandle})" % result),
   8386            False,
   8387        )
   8388 
   8389    tag = type.tag()
   8390 
   8391    if tag in [
   8392        IDLType.Tags.int8,
   8393        IDLType.Tags.uint8,
   8394        IDLType.Tags.int16,
   8395        IDLType.Tags.uint16,
   8396        IDLType.Tags.int32,
   8397    ]:
   8398        return (setInt32("int32_t(%s)" % result), True)
   8399 
   8400    elif tag in [
   8401        IDLType.Tags.int64,
   8402        IDLType.Tags.uint64,
   8403        IDLType.Tags.unrestricted_float,
   8404        IDLType.Tags.float,
   8405        IDLType.Tags.unrestricted_double,
   8406        IDLType.Tags.double,
   8407    ]:
   8408        # XXXbz will cast to double do the "even significand" thing that webidl
   8409        # calls for for 64-bit ints?  Do we care?
   8410        return (setDouble("double(%s)" % result), True)
   8411 
   8412    elif tag == IDLType.Tags.uint32:
   8413        return (setUint32(result), True)
   8414 
   8415    elif tag == IDLType.Tags.bool:
   8416        return (setBoolean(result), True)
   8417 
   8418    else:
   8419        raise TypeError("Need to learn to wrap primitive: %s" % type)
   8420 
   8421 
   8422 def wrapForType(type, descriptorProvider, templateValues):
   8423    """
   8424    Reflect a C++ value of IDL type "type" into JS.  TemplateValues is a dict
   8425    that should contain:
   8426 
   8427      * 'jsvalRef': something that can have .address() called on it to get a
   8428                    JS::Value* and .set() called on it to set it to a JS::Value.
   8429                    This can be a JS::MutableHandle<JS::Value> or a
   8430                    JS::Rooted<JS::Value>.
   8431      * 'jsvalHandle': something that can be passed to methods taking a
   8432                       JS::MutableHandle<JS::Value>.  This can be a
   8433                       JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
   8434      * 'obj' (optional): the name of the variable that contains the JSObject to
   8435                          use as a scope when wrapping, if not supplied 'obj'
   8436                          will be used as the name
   8437      * 'result' (optional): the name of the variable in which the C++ value is
   8438                             stored, if not supplied 'result' will be used as
   8439                             the name
   8440      * 'successCode' (optional): the code to run once we have successfully
   8441                                  done the conversion, if not supplied 'return
   8442                                  true;' will be used as the code.  The
   8443                                  successCode must ensure that once it runs no
   8444                                  more of the conversion template will be
   8445                                  executed (e.g. by doing a 'return' or 'break'
   8446                                  as appropriate).
   8447      * 'returnsNewObject' (optional): If true, we're wrapping for the return
   8448                                       value of a [NewObject] method.  Assumed
   8449                                       false if not set.
   8450      * 'exceptionCode' (optional): Code to run when a JS exception is thrown.
   8451                                    The default is "return false;".  The code
   8452                                    passed here must return.
   8453      * 'isConstructorRetval' (optional): If true, we're wrapping a constructor
   8454                                          return value.
   8455    """
   8456    wrap = getWrapTemplateForType(
   8457        type,
   8458        descriptorProvider,
   8459        templateValues.get("result", "result"),
   8460        templateValues.get("successCode", None),
   8461        templateValues.get("returnsNewObject", False),
   8462        templateValues.get("exceptionCode", "return false;\n"),
   8463        templateValues.get("spiderMonkeyInterfacesAreStructs", False),
   8464        isConstructorRetval=templateValues.get("isConstructorRetval", False),
   8465    )[0]
   8466 
   8467    defaultValues = {"obj": "obj"}
   8468    return string.Template(wrap).substitute(defaultValues, **templateValues)
   8469 
   8470 
   8471 def infallibleForMember(member, type, descriptorProvider):
   8472    """
   8473    Determine the fallibility of changing a C++ value of IDL type "type" into
   8474    JS for the given attribute. Apart from returnsNewObject, all the defaults
   8475    are used, since the fallbility does not change based on the boolean values,
   8476    and the template will be discarded.
   8477 
   8478    CURRENT ASSUMPTIONS:
   8479        We assume that successCode for wrapping up return values cannot contain
   8480        failure conditions.
   8481    """
   8482    return getWrapTemplateForType(
   8483        type,
   8484        descriptorProvider,
   8485        "result",
   8486        None,
   8487        memberReturnsNewObject(member),
   8488        "return false;\n",
   8489        False,
   8490    )[1]
   8491 
   8492 
   8493 def leafTypeNeedsCx(type, retVal):
   8494    return (
   8495        type.isAny()
   8496        or type.isObject()
   8497        or type.isJSString()
   8498        or (retVal and type.isSpiderMonkeyInterface())
   8499    )
   8500 
   8501 
   8502 def leafTypeNeedsScopeObject(type, retVal):
   8503    return retVal and type.isSpiderMonkeyInterface()
   8504 
   8505 
   8506 def leafTypeNeedsRooting(type):
   8507    return leafTypeNeedsCx(type, False) or type.isSpiderMonkeyInterface()
   8508 
   8509 
   8510 def typeNeedsRooting(type):
   8511    return typeMatchesLambda(type, lambda t: leafTypeNeedsRooting(t))
   8512 
   8513 
   8514 def typeNeedsCx(type, retVal=False):
   8515    return typeMatchesLambda(type, lambda t: leafTypeNeedsCx(t, retVal))
   8516 
   8517 
   8518 def typeNeedsScopeObject(type, retVal=False):
   8519    return typeMatchesLambda(type, lambda t: leafTypeNeedsScopeObject(t, retVal))
   8520 
   8521 
   8522 def typeMatchesLambda(type, func):
   8523    if type is None:
   8524        return False
   8525    if type.nullable():
   8526        return typeMatchesLambda(type.inner, func)
   8527    if type.isSequence() or type.isRecord():
   8528        return typeMatchesLambda(type.inner, func)
   8529    if type.isUnion():
   8530        return any(typeMatchesLambda(t, func) for t in type.unroll().flatMemberTypes)
   8531    if type.isDictionary():
   8532        return dictionaryMatchesLambda(type.inner, func)
   8533    return func(type)
   8534 
   8535 
   8536 def dictionaryMatchesLambda(dictionary, func):
   8537    return any(typeMatchesLambda(m.type, func) for m in dictionary.members) or (
   8538        dictionary.parent and dictionaryMatchesLambda(dictionary.parent, func)
   8539    )
   8540 
   8541 
   8542 # Whenever this is modified, please update CGNativeMember.getRetvalInfo as
   8543 # needed to keep the types compatible.
   8544 def getRetvalDeclarationForType(returnType, descriptorProvider, isMember=False):
   8545    """
   8546    Returns a tuple containing five things:
   8547 
   8548    1) A CGThing for the type of the return value, or None if there is no need
   8549       for a return value.
   8550 
   8551    2) A value indicating the kind of ourparam to pass the value as.  Valid
   8552       options are None to not pass as an out param at all, "ref" (to pass a
   8553       reference as an out param), and "ptr" (to pass a pointer as an out
   8554       param).
   8555 
   8556    3) A CGThing for a tracer for the return value, or None if no tracing is
   8557       needed.
   8558 
   8559    4) An argument string to pass to the retval declaration
   8560       constructor or None if there are no arguments.
   8561 
   8562    5) The name of a function that needs to be called with the return value
   8563       before using it, or None if no function needs to be called.
   8564    """
   8565    if returnType is None or returnType.isUndefined():
   8566        # Nothing to declare
   8567        return None, None, None, None, None
   8568    if returnType.isPrimitive() and returnType.tag() in builtinNames:
   8569        result = CGGeneric(builtinNames[returnType.tag()])
   8570        if returnType.nullable():
   8571            result = CGTemplatedType("Nullable", result)
   8572        return result, None, None, None, None
   8573    if returnType.isJSString():
   8574        if isMember:
   8575            raise TypeError("JSString not supported as return type member")
   8576        return CGGeneric("JS::Rooted<JSString*>"), "ptr", None, "cx", None
   8577    if returnType.isDOMString() or returnType.isUSVString():
   8578        if isMember:
   8579            return CGGeneric("nsString"), "ref", None, None, None
   8580        return CGGeneric("DOMString"), "ref", None, None, None
   8581    if returnType.isByteString() or returnType.isUTF8String():
   8582        if isMember:
   8583            return CGGeneric("nsCString"), "ref", None, None, None
   8584        return CGGeneric("nsAutoCString"), "ref", None, None, None
   8585    if returnType.isEnum():
   8586        result = CGGeneric(returnType.unroll().inner.identifier.name)
   8587        if returnType.nullable():
   8588            result = CGTemplatedType("Nullable", result)
   8589        return result, None, None, None, None
   8590    if returnType.isGeckoInterface() or returnType.isPromise():
   8591        if returnType.isGeckoInterface():
   8592            typeName = returnType.unroll().inner.identifier.name
   8593            if typeName == "WindowProxy":
   8594                result = CGGeneric("WindowProxyHolder")
   8595                if returnType.nullable():
   8596                    result = CGTemplatedType("Nullable", result)
   8597                return result, None, None, None, None
   8598 
   8599            typeName = descriptorProvider.getDescriptor(typeName).nativeType
   8600        else:
   8601            typeName = "Promise"
   8602        if isMember:
   8603            conversion = None
   8604            result = CGGeneric("StrongPtrForMember<%s>" % typeName)
   8605        else:
   8606            conversion = CGGeneric("StrongOrRawPtr<%s>" % typeName)
   8607            result = CGGeneric("auto")
   8608        return result, None, None, None, conversion
   8609    if returnType.isCallback():
   8610        name = returnType.unroll().callback.identifier.name
   8611        return CGGeneric("RefPtr<%s>" % name), None, None, None, None
   8612    if returnType.isAny():
   8613        if isMember:
   8614            return CGGeneric("JS::Value"), None, None, None, None
   8615        return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx", None
   8616    if returnType.isObject() or returnType.isSpiderMonkeyInterface():
   8617        if isMember:
   8618            return CGGeneric("JSObject*"), None, None, None, None
   8619        return CGGeneric("JS::Rooted<JSObject*>"), "ptr", None, "cx", None
   8620    if returnType.isSequence():
   8621        nullable = returnType.nullable()
   8622        if nullable:
   8623            returnType = returnType.inner
   8624        result, _, _, _, _ = getRetvalDeclarationForType(
   8625            returnType.inner, descriptorProvider, isMember="Sequence"
   8626        )
   8627        # While we have our inner type, set up our rooter, if needed
   8628        if not isMember and typeNeedsRooting(returnType):
   8629            rooter = CGGeneric(
   8630                "SequenceRooter<%s > resultRooter(cx, &result);\n" % result.define()
   8631            )
   8632        else:
   8633            rooter = None
   8634        result = CGTemplatedType("nsTArray", result)
   8635        if nullable:
   8636            result = CGTemplatedType("Nullable", result)
   8637        return result, "ref", rooter, None, None
   8638    if returnType.isRecord():
   8639        nullable = returnType.nullable()
   8640        if nullable:
   8641            returnType = returnType.inner
   8642        result, _, _, _, _ = getRetvalDeclarationForType(
   8643            returnType.inner, descriptorProvider, isMember="Record"
   8644        )
   8645        # While we have our inner type, set up our rooter, if needed
   8646        if not isMember and typeNeedsRooting(returnType):
   8647            rooter = CGGeneric(
   8648                "RecordRooter<%s> resultRooter(cx, &result);\n"
   8649                % (recordKeyType(returnType) + ", " + result.define())
   8650            )
   8651        else:
   8652            rooter = None
   8653        result = CGTemplatedType("Record", [recordKeyDeclType(returnType), result])
   8654        if nullable:
   8655            result = CGTemplatedType("Nullable", result)
   8656        return result, "ref", rooter, None, None
   8657    if returnType.isDictionary():
   8658        nullable = returnType.nullable()
   8659        dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner)
   8660        result = CGGeneric(dictName)
   8661        if not isMember and typeNeedsRooting(returnType):
   8662            if nullable:
   8663                result = CGTemplatedType("NullableRootedDictionary", result)
   8664            else:
   8665                result = CGTemplatedType("RootedDictionary", result)
   8666            resultArgs = "cx"
   8667        else:
   8668            if nullable:
   8669                result = CGTemplatedType("Nullable", result)
   8670            resultArgs = None
   8671        return result, "ref", None, resultArgs, None
   8672    if returnType.isUnion():
   8673        result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True))
   8674        if not isMember and typeNeedsRooting(returnType):
   8675            if returnType.nullable():
   8676                result = CGTemplatedType("NullableRootedUnion", result)
   8677            else:
   8678                result = CGTemplatedType("RootedUnion", result)
   8679            resultArgs = "cx"
   8680        else:
   8681            if returnType.nullable():
   8682                result = CGTemplatedType("Nullable", result)
   8683            resultArgs = None
   8684        return result, "ref", None, resultArgs, None
   8685    raise TypeError("Don't know how to declare return value for %s" % returnType)
   8686 
   8687 
   8688 def needCx(returnType, arguments, extendedAttributes, considerTypes, static=False):
   8689    return (
   8690        not static
   8691        and considerTypes
   8692        and (
   8693            typeNeedsCx(returnType, True) or any(typeNeedsCx(a.type) for a in arguments)
   8694        )
   8695        or "implicitJSContext" in extendedAttributes
   8696    )
   8697 
   8698 
   8699 def needScopeObject(
   8700    returnType, arguments, extendedAttributes, isWrapperCached, considerTypes, isMember
   8701 ):
   8702    """
   8703    isMember should be true if we're dealing with an attribute
   8704    annotated as [StoreInSlot].
   8705    """
   8706    return (
   8707        considerTypes
   8708        and not isWrapperCached
   8709        and (
   8710            (not isMember and typeNeedsScopeObject(returnType, True))
   8711            or any(typeNeedsScopeObject(a.type) for a in arguments)
   8712        )
   8713    )
   8714 
   8715 
   8716 def callerTypeGetterForDescriptor(descriptor):
   8717    if descriptor.interface.isExposedInAnyWorker():
   8718        systemCallerGetter = "nsContentUtils::ThreadsafeIsSystemCaller"
   8719    else:
   8720        systemCallerGetter = "nsContentUtils::IsSystemCaller"
   8721    return "%s(cx) ? CallerType::System : CallerType::NonSystem" % systemCallerGetter
   8722 
   8723 
   8724 class CGCallGenerator(CGThing):
   8725    """
   8726    A class to generate an actual call to a C++ object.  Assumes that the C++
   8727    object is stored in a variable whose name is given by the |object| argument.
   8728 
   8729    needsCallerType is a boolean indicating whether the call should receive
   8730    a PrincipalType for the caller.
   8731 
   8732    needsErrorResult is a boolean indicating whether the call should be
   8733    fallible and thus needs ErrorResult parameter.
   8734 
   8735    resultVar: If the returnType is not void, then the result of the call is
   8736    stored in a C++ variable named by resultVar. The caller is responsible for
   8737    declaring the result variable. If the caller doesn't care about the result
   8738    value, resultVar can be omitted.
   8739 
   8740    context: The context string to pass to MaybeSetPendingException.
   8741    """
   8742 
   8743    def __init__(
   8744        self,
   8745        needsErrorResult,
   8746        needsCallerType,
   8747        isChromeOnly,
   8748        arguments,
   8749        argsPre,
   8750        returnType,
   8751        extendedAttributes,
   8752        descriptor,
   8753        nativeMethodName,
   8754        static,
   8755        object="self",
   8756        argsPost=[],
   8757        resultVar=None,
   8758        context="nullptr",
   8759    ):
   8760        CGThing.__init__(self)
   8761 
   8762        (
   8763            result,
   8764            resultOutParam,
   8765            resultRooter,
   8766            resultArgs,
   8767            resultConversion,
   8768        ) = getRetvalDeclarationForType(returnType, descriptor)
   8769 
   8770        args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
   8771        for a, name in arguments:
   8772            arg = CGGeneric(name)
   8773 
   8774            # Now constify the things that need it
   8775            def needsConst(a):
   8776                if a.type.isDictionary():
   8777                    return True
   8778                if a.type.isSequence():
   8779                    return True
   8780                if a.type.isRecord():
   8781                    return True
   8782                # isObject() types are always a JS::Rooted, whether
   8783                # nullable or not, and it turns out a const JS::Rooted
   8784                # is not very helpful at all (in particular, it won't
   8785                # even convert to a JS::Handle).
   8786                # XXX bz Well, why not???
   8787                if a.type.nullable() and not a.type.isObject():
   8788                    return True
   8789                if a.type.isString():
   8790                    return True
   8791                if a.canHaveMissingValue():
   8792                    # This will need an Optional or it's a variadic;
   8793                    # in both cases it should be const.
   8794                    return True
   8795                if a.type.isUnion():
   8796                    return True
   8797                if a.type.isSpiderMonkeyInterface():
   8798                    return True
   8799                return False
   8800 
   8801            if needsConst(a):
   8802                arg = CGWrapper(arg, pre="Constify(", post=")")
   8803            # And convert NonNull<T> to T&
   8804            if (
   8805                (a.type.isGeckoInterface() or a.type.isCallback() or a.type.isPromise())
   8806                and not a.type.nullable()
   8807            ) or a.type.isDOMString():
   8808                arg = CGWrapper(arg, pre="NonNullHelper(", post=")")
   8809 
   8810            # If it's a refcounted object, let the static analysis know it's
   8811            # alive for the duration of the call.
   8812            if a.type.isGeckoInterface() or a.type.isCallback():
   8813                arg = CGWrapper(arg, pre="MOZ_KnownLive(", post=")")
   8814 
   8815            args.append(arg)
   8816 
   8817        needResultDecl = False
   8818 
   8819        # Build up our actual call
   8820        self.cgRoot = CGList([])
   8821 
   8822        # Return values that go in outparams go here
   8823        if resultOutParam is not None:
   8824            if resultVar is None:
   8825                needResultDecl = True
   8826                resultVar = "result"
   8827            if resultOutParam == "ref":
   8828                args.append(CGGeneric(resultVar))
   8829            else:
   8830                assert resultOutParam == "ptr"
   8831                args.append(CGGeneric("&" + resultVar))
   8832 
   8833        needsSubjectPrincipal = "needsSubjectPrincipal" in extendedAttributes
   8834        if needsSubjectPrincipal:
   8835            needsNonSystemPrincipal = (
   8836                "needsNonSystemSubjectPrincipal" in extendedAttributes
   8837            )
   8838            if needsNonSystemPrincipal:
   8839                principalType = "nsIPrincipal*"
   8840                subjectPrincipalArg = "subjectPrincipal"
   8841                checkPrincipal = dedent(
   8842                    """
   8843                    if (principal->IsSystemPrincipal()) {
   8844                      principal = nullptr;
   8845                    }
   8846                    """
   8847                )
   8848            else:
   8849                principalType = "NonNull<nsIPrincipal>"
   8850                subjectPrincipalArg = "NonNullHelper(subjectPrincipal)"
   8851                checkPrincipal = ""
   8852 
   8853            self.cgRoot.append(
   8854                CGGeneric(
   8855                    fill(
   8856                        """
   8857                        ${principalType} subjectPrincipal;
   8858                        {
   8859                          JS::Realm* realm = js::GetContextRealm(cx);
   8860                          MOZ_ASSERT(realm);
   8861                          JSPrincipals* principals = JS::GetRealmPrincipals(realm);
   8862                          nsIPrincipal* principal = nsJSPrincipals::get(principals);
   8863                          ${checkPrincipal}
   8864                          subjectPrincipal = principal;
   8865                        }
   8866                        """,
   8867                        principalType=principalType,
   8868                        checkPrincipal=checkPrincipal,
   8869                    )
   8870                )
   8871            )
   8872 
   8873            args.append(CGGeneric("MOZ_KnownLive(%s)" % subjectPrincipalArg))
   8874 
   8875        if needsCallerType:
   8876            if isChromeOnly:
   8877                args.append(CGGeneric("SystemCallerGuarantee()"))
   8878            else:
   8879                args.append(CGGeneric(callerTypeGetterForDescriptor(descriptor)))
   8880 
   8881        canOOM = "canOOM" in extendedAttributes
   8882        if needsErrorResult:
   8883            args.append(CGGeneric("rv"))
   8884        elif canOOM:
   8885            args.append(CGGeneric("OOMReporter::From(rv)"))
   8886        args.extend(CGGeneric(arg) for arg in argsPost)
   8887 
   8888        call = CGGeneric(nativeMethodName)
   8889        if not static:
   8890            call = CGWrapper(call, pre="%s->" % object)
   8891        call = CGList([call, CGWrapper(args, pre="(", post=")")])
   8892        if returnType is None or returnType.isUndefined() or resultOutParam is not None:
   8893            assert resultConversion is None
   8894            call = CGList(
   8895                [
   8896                    CGWrapper(
   8897                        call,
   8898                        pre=(
   8899                            "// NOTE: This assert does NOT call the function.\n"
   8900                            "static_assert(std::is_void_v<decltype("
   8901                        ),
   8902                        post=')>, "Should be returning void here");',
   8903                    ),
   8904                    call,
   8905                ],
   8906                "\n",
   8907            )
   8908        elif resultConversion is not None:
   8909            call = CGList([resultConversion, CGWrapper(call, pre="(", post=")")])
   8910        if resultVar is None and result is not None:
   8911            needResultDecl = True
   8912            resultVar = "result"
   8913 
   8914        if needResultDecl:
   8915            if resultArgs is not None:
   8916                resultArgsStr = "(%s)" % resultArgs
   8917            else:
   8918                resultArgsStr = ""
   8919            result = CGWrapper(result, post=(" %s%s" % (resultVar, resultArgsStr)))
   8920            if resultOutParam is None and resultArgs is None:
   8921                call = CGList([result, CGWrapper(call, pre="(", post=")")])
   8922            else:
   8923                self.cgRoot.append(CGWrapper(result, post=";\n"))
   8924                if resultOutParam is None:
   8925                    call = CGWrapper(call, pre=resultVar + " = ")
   8926            if resultRooter is not None:
   8927                self.cgRoot.append(resultRooter)
   8928        elif result is not None:
   8929            assert resultOutParam is None
   8930            call = CGWrapper(call, pre=resultVar + " = ")
   8931 
   8932        call = CGWrapper(call, post=";\n")
   8933        self.cgRoot.append(call)
   8934 
   8935        if needsErrorResult or canOOM:
   8936            self.cgRoot.prepend(CGGeneric("FastErrorResult rv;\n"))
   8937            self.cgRoot.append(
   8938                CGGeneric(
   8939                    fill(
   8940                        """
   8941                if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx, ${context}))) {
   8942                  return false;
   8943                }
   8944                """,
   8945                        context=context,
   8946                    )
   8947                )
   8948            )
   8949 
   8950        self.cgRoot.append(CGGeneric("MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"))
   8951 
   8952    def define(self):
   8953        return self.cgRoot.define()
   8954 
   8955 
   8956 def getUnionMemberName(type):
   8957    # Promises can't be in unions, because they're not distinguishable
   8958    # from anything else.
   8959    assert not type.isPromise()
   8960    if type.isGeckoInterface():
   8961        return type.inner.identifier.name
   8962    if type.isEnum():
   8963        return type.inner.identifier.name
   8964    return type.name
   8965 
   8966 
   8967 # A counter for making sure that when we're wrapping up things in
   8968 # nested sequences we don't use the same variable name to iterate over
   8969 # different sequences.
   8970 sequenceWrapLevel = 0
   8971 recordWrapLevel = 0
   8972 
   8973 
   8974 def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
   8975    """
   8976    Take the thing named by "value" and if it contains "any",
   8977    "object", or spidermonkey-interface types inside return a CGThing
   8978    that will wrap them into the current compartment.
   8979    """
   8980    if type.isAny():
   8981        assert not type.nullable()
   8982        if isMember:
   8983            value = "JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" % value
   8984        else:
   8985            value = "&" + value
   8986        return CGGeneric(
   8987            "if (!JS_WrapValue(cx, %s)) {\n" "  return false;\n" "}\n" % value
   8988        )
   8989 
   8990    if type.isObject():
   8991        if isMember:
   8992            value = "JS::MutableHandle<JSObject*>::fromMarkedLocation(&%s)" % value
   8993        else:
   8994            value = "&" + value
   8995        return CGGeneric(
   8996            "if (!JS_WrapObject(cx, %s)) {\n" "  return false;\n" "}\n" % value
   8997        )
   8998 
   8999    if type.isSpiderMonkeyInterface():
   9000        origValue = value
   9001        if type.nullable():
   9002            value = "%s.Value()" % value
   9003        wrapCode = CGGeneric(
   9004            "if (!%s.WrapIntoNewCompartment(cx)) {\n" "  return false;\n" "}\n" % value
   9005        )
   9006        if type.nullable():
   9007            wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
   9008        return wrapCode
   9009 
   9010    if type.isSequence():
   9011        origValue = value
   9012        origType = type
   9013        if type.nullable():
   9014            type = type.inner
   9015            value = "%s.Value()" % value
   9016        global sequenceWrapLevel
   9017        index = "indexName%d" % sequenceWrapLevel
   9018        sequenceWrapLevel += 1
   9019        wrapElement = wrapTypeIntoCurrentCompartment(
   9020            type.inner, "%s[%s]" % (value, index)
   9021        )
   9022        sequenceWrapLevel -= 1
   9023        if not wrapElement:
   9024            return None
   9025        wrapCode = CGWrapper(
   9026            CGIndenter(wrapElement),
   9027            pre=(
   9028                "for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n"
   9029                % (index, index, value, index)
   9030            ),
   9031            post="}\n",
   9032        )
   9033        if origType.nullable():
   9034            wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
   9035        return wrapCode
   9036 
   9037    if type.isRecord():
   9038        origType = type
   9039        if type.nullable():
   9040            type = type.inner
   9041            recordRef = "%s.Value()" % value
   9042        else:
   9043            recordRef = value
   9044        global recordWrapLevel
   9045        entryRef = "mapEntry%d" % recordWrapLevel
   9046        recordWrapLevel += 1
   9047        wrapElement = wrapTypeIntoCurrentCompartment(type.inner, "%s.mValue" % entryRef)
   9048        recordWrapLevel -= 1
   9049        if not wrapElement:
   9050            return None
   9051        wrapCode = CGWrapper(
   9052            CGIndenter(wrapElement),
   9053            pre=("for (auto& %s : %s.Entries()) {\n" % (entryRef, recordRef)),
   9054            post="}\n",
   9055        )
   9056        if origType.nullable():
   9057            wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % value)
   9058        return wrapCode
   9059 
   9060    if type.isDictionary():
   9061        assert not type.nullable()
   9062        myDict = type.inner
   9063        memberWraps = []
   9064        while myDict:
   9065            for member in myDict.members:
   9066                memberWrap = wrapArgIntoCurrentCompartment(
   9067                    member,
   9068                    "%s.%s"
   9069                    % (value, CGDictionary.makeMemberName(member.identifier.name)),
   9070                )
   9071                if memberWrap:
   9072                    memberWraps.append(memberWrap)
   9073            myDict = myDict.parent
   9074        return CGList(memberWraps) if len(memberWraps) != 0 else None
   9075 
   9076    if type.isUnion():
   9077        origValue = value
   9078        origType = type
   9079        if type.nullable():
   9080            type = type.inner
   9081            value = "%s.Value()" % value
   9082        memberWraps = []
   9083        for member in type.flatMemberTypes:
   9084            memberName = getUnionMemberName(member)
   9085            memberWrap = wrapTypeIntoCurrentCompartment(
   9086                member, "%s.GetAs%s()" % (value, memberName)
   9087            )
   9088            if memberWrap:
   9089                memberWrap = CGIfWrapper(memberWrap, "%s.Is%s()" % (value, memberName))
   9090                memberWraps.append(memberWrap)
   9091        if len(memberWraps) == 0:
   9092            return None
   9093        wrapCode = CGList(memberWraps, "else ")
   9094        if origType.nullable():
   9095            wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
   9096        return wrapCode
   9097 
   9098    if (
   9099        type.isUndefined()
   9100        or type.isString()
   9101        or type.isPrimitive()
   9102        or type.isEnum()
   9103        or type.isGeckoInterface()
   9104        or type.isCallback()
   9105        or type.isPromise()
   9106    ):
   9107        # All of these don't need wrapping.
   9108        return None
   9109 
   9110    raise TypeError(
   9111        "Unknown type; we don't know how to wrap it in constructor "
   9112        "arguments: %s" % type
   9113    )
   9114 
   9115 
   9116 def wrapArgIntoCurrentCompartment(arg, value, isMember=True):
   9117    """
   9118    As wrapTypeIntoCurrentCompartment but handles things being optional
   9119    """
   9120    origValue = value
   9121    isOptional = arg.canHaveMissingValue()
   9122    if isOptional:
   9123        value = value + ".Value()"
   9124    wrap = wrapTypeIntoCurrentCompartment(arg.type, value, isMember)
   9125    if wrap and isOptional:
   9126        wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue)
   9127    return wrap
   9128 
   9129 
   9130 def needsCallerType(m):
   9131    return m.getExtendedAttribute("NeedsCallerType")
   9132 
   9133 
   9134 class CGPerSignatureCall(CGThing):
   9135    """
   9136    This class handles the guts of generating code for a particular
   9137    call signature.  A call signature consists of four things:
   9138 
   9139    1) A return type, which can be None to indicate that there is no
   9140       actual return value (e.g. this is an attribute setter) or an
   9141       IDLType if there's an IDL type involved (including |void|).
   9142    2) An argument list, which is allowed to be empty.
   9143    3) A name of a native method to call. It is ignored for methods
   9144       annotated with the "[WebExtensionStub=...]" extended attribute.
   9145    4) Whether or not this method is static. Note that this only controls how
   9146       the method is called (|self->nativeMethodName(...)| vs
   9147       |nativeMethodName(...)|).
   9148 
   9149    We also need to know whether this is a method or a getter/setter
   9150    to do error reporting correctly.
   9151 
   9152    The idlNode parameter can be either a method or an attr. We can query
   9153    |idlNode.identifier| in both cases, so we can be agnostic between the two.
   9154 
   9155    dontSetSlot should be set to True if the value should not be cached in a
   9156    slot (even if the attribute is marked as StoreInSlot or Cached in the
   9157    WebIDL).
   9158 
   9159    errorReportingLabel can contain a custom label to use for error reporting.
   9160    It will be inserted as is in the code, so if it needs to be a literal
   9161    string in C++ it should be quoted.
   9162 
   9163    additionalArgsPre contains additional arguments that are added after the
   9164    arguments that CGPerSignatureCall itself adds (JSContext, global, …), and
   9165    before the actual arguments.
   9166    """
   9167 
   9168    # XXXbz For now each entry in the argument list is either an
   9169    # IDLArgument or a FakeArgument, but longer-term we may want to
   9170    # have ways of flagging things like JSContext* or optional_argc in
   9171    # there.
   9172 
   9173    def __init__(
   9174        self,
   9175        returnType,
   9176        arguments,
   9177        nativeMethodName,
   9178        static,
   9179        descriptor,
   9180        idlNode,
   9181        argConversionStartsAt=0,
   9182        getter=False,
   9183        setter=False,
   9184        isConstructor=False,
   9185        useCounterName=None,
   9186        resultVar=None,
   9187        objectName="obj",
   9188        dontSetSlot=False,
   9189        extendedAttributes=None,
   9190        errorReportingLabel=None,
   9191        additionalArgsPre=[],
   9192    ):
   9193        assert idlNode.isMethod() == (not getter and not setter)
   9194        assert idlNode.isAttr() == (getter or setter)
   9195        # Constructors are always static
   9196        assert not isConstructor or static
   9197 
   9198        CGThing.__init__(self)
   9199        self.returnType = returnType
   9200        self.descriptor = descriptor
   9201        self.idlNode = idlNode
   9202        if extendedAttributes is None:
   9203            extendedAttributes = descriptor.getExtendedAttributes(
   9204                idlNode, getter=getter, setter=setter
   9205            )
   9206        self.extendedAttributes = extendedAttributes
   9207        self.arguments = arguments
   9208        self.argCount = len(arguments)
   9209        self.isConstructor = isConstructor
   9210        self.setSlot = (
   9211            not dontSetSlot and idlNode.isAttr() and idlNode.slotIndices is not None
   9212        )
   9213        cgThings = []
   9214 
   9215        deprecated = idlNode.getExtendedAttribute("Deprecated") or (
   9216            idlNode.isStatic()
   9217            and descriptor.interface.getExtendedAttribute("Deprecated")
   9218        )
   9219        if deprecated:
   9220            cgThings.append(
   9221                CGGeneric(
   9222                    dedent(
   9223                        """
   9224                DeprecationWarning(cx, obj, DeprecatedOperations::e%s);
   9225                """
   9226                        % deprecated[0]
   9227                    )
   9228                )
   9229            )
   9230 
   9231        lenientFloatCode = None
   9232        if idlNode.getExtendedAttribute("LenientFloat") is not None and (
   9233            setter or idlNode.isMethod()
   9234        ):
   9235            cgThings.append(
   9236                CGGeneric(
   9237                    dedent(
   9238                        """
   9239                bool foundNonFiniteFloat = false;
   9240                """
   9241                    )
   9242                )
   9243            )
   9244            lenientFloatCode = "foundNonFiniteFloat = true;\n"
   9245 
   9246        argsPre = []
   9247        if idlNode.isStatic():
   9248            # If we're a constructor, "obj" may not be a function, so calling
   9249            # XrayAwareCalleeGlobal() on it is not safe.  Of course in the
   9250            # constructor case either "obj" is an Xray or we're already in the
   9251            # content compartment, not the Xray compartment, so just
   9252            # constructing the GlobalObject from "obj" is fine.
   9253            if isConstructor:
   9254                objForGlobalObject = "obj"
   9255            else:
   9256                objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)"
   9257            cgThings.append(
   9258                CGGeneric(
   9259                    fill(
   9260                        """
   9261                GlobalObject global(cx, ${obj});
   9262                if (global.Failed()) {
   9263                  return false;
   9264                }
   9265 
   9266                """,
   9267                        obj=objForGlobalObject,
   9268                    )
   9269                )
   9270            )
   9271            argsPre.append("global")
   9272 
   9273        # For JS-implemented interfaces we do not want to base the
   9274        # needsCx decision on the types involved, just on our extended
   9275        # attributes. Also, JSContext is not needed for the static case
   9276        # since GlobalObject already contains the context.
   9277        needsCx = needCx(
   9278            returnType,
   9279            arguments,
   9280            self.extendedAttributes,
   9281            not descriptor.interface.isJSImplemented(),
   9282            static,
   9283        )
   9284        if needsCx:
   9285            argsPre.append("cx")
   9286 
   9287        needsUnwrap = False
   9288        argsPost = []
   9289        runConstructorInCallerCompartment = descriptor.interface.getExtendedAttribute(
   9290            "RunConstructorInCallerCompartment"
   9291        )
   9292        if isConstructor and not runConstructorInCallerCompartment:
   9293            needsUnwrap = True
   9294            needsUnwrappedVar = False
   9295            unwrappedVar = "obj"
   9296            if descriptor.interface.isJSImplemented():
   9297                # We need the desired proto in our constructor, because the
   9298                # constructor will actually construct our reflector.
   9299                argsPost.append("desiredProto")
   9300        elif descriptor.interface.isJSImplemented():
   9301            if not idlNode.isStatic():
   9302                needsUnwrap = True
   9303                needsUnwrappedVar = True
   9304                argsPost.append(
   9305                    "(unwrappedObj ? js::GetNonCCWObjectRealm(*unwrappedObj) : js::GetContextRealm(cx))"
   9306                )
   9307        elif needScopeObject(
   9308            returnType,
   9309            arguments,
   9310            self.extendedAttributes,
   9311            descriptor.wrapperCache,
   9312            True,
   9313            idlNode.getExtendedAttribute("StoreInSlot"),
   9314        ):
   9315            # If we ever end up with APIs like this on cross-origin objects,
   9316            # figure out how the CheckedUnwrapDynamic bits should work.  Chances
   9317            # are, just calling it with "cx" is fine...  For now, though, just
   9318            # assert that it does not matter.
   9319            assert not descriptor.isMaybeCrossOriginObject()
   9320            # The scope object should always be from the relevant
   9321            # global.  Make sure to unwrap it as needed.
   9322            cgThings.append(
   9323                CGGeneric(
   9324                    dedent(
   9325                        """
   9326                JS::Rooted<JSObject*> unwrappedObj(cx, js::CheckedUnwrapStatic(obj));
   9327                // Caller should have ensured that "obj" can be unwrapped already.
   9328                MOZ_DIAGNOSTIC_ASSERT(unwrappedObj);
   9329                """
   9330                    )
   9331                )
   9332            )
   9333            argsPre.append("unwrappedObj")
   9334 
   9335        if needsUnwrap and needsUnwrappedVar:
   9336            # We cannot assign into obj because it's a Handle, not a
   9337            # MutableHandle, so we need a separate Rooted.
   9338            cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;\n"))
   9339            unwrappedVar = "unwrappedObj.ref()"
   9340 
   9341        if idlNode.isMethod() and idlNode.isLegacycaller():
   9342            # If we can have legacycaller with identifier, we can't
   9343            # just use the idlNode to determine whether we're
   9344            # generating code for the legacycaller or not.
   9345            assert idlNode.isIdentifierLess()
   9346            # Pass in our thisVal
   9347            argsPre.append("args.thisv()")
   9348 
   9349        if idlNode.isMethod():
   9350            argDescription = "argument %(index)d"
   9351        elif setter:
   9352            argDescription = "value being assigned"
   9353        else:
   9354            assert self.argCount == 0
   9355 
   9356        if needsUnwrap:
   9357            # It's very important that we construct our unwrappedObj, if we need
   9358            # to do it, before we might start setting up Rooted things for our
   9359            # arguments, so that we don't violate the stack discipline Rooted
   9360            # depends on.
   9361            cgThings.append(
   9362                CGGeneric("bool objIsXray = xpc::WrapperFactory::IsXrayWrapper(obj);\n")
   9363            )
   9364            if needsUnwrappedVar:
   9365                cgThings.append(
   9366                    CGIfWrapper(
   9367                        CGGeneric("unwrappedObj.emplace(cx, obj);\n"), "objIsXray"
   9368                    )
   9369                )
   9370 
   9371        for i in range(argConversionStartsAt, self.argCount):
   9372            cgThings.append(
   9373                CGArgumentConverter(
   9374                    arguments[i],
   9375                    i,
   9376                    self.descriptor,
   9377                    argDescription % {"index": i + 1},
   9378                    idlNode,
   9379                    invalidEnumValueFatal=not setter,
   9380                    lenientFloatCode=lenientFloatCode,
   9381                )
   9382            )
   9383 
   9384        # Now that argument processing is done, enforce the LenientFloat stuff
   9385        if lenientFloatCode:
   9386            if setter:
   9387                foundNonFiniteFloatBehavior = "return true;\n"
   9388            else:
   9389                assert idlNode.isMethod()
   9390                foundNonFiniteFloatBehavior = dedent(
   9391                    """
   9392                    args.rval().setUndefined();
   9393                    return true;
   9394                    """
   9395                )
   9396            cgThings.append(
   9397                CGGeneric(
   9398                    fill(
   9399                        """
   9400                if (foundNonFiniteFloat) {
   9401                  $*{returnSteps}
   9402                }
   9403                """,
   9404                        returnSteps=foundNonFiniteFloatBehavior,
   9405                    )
   9406                )
   9407            )
   9408 
   9409        if needsUnwrap:
   9410            # Something depends on having the unwrapped object, so unwrap it now.
   9411            xraySteps = []
   9412            # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is
   9413            # not null.
   9414            xraySteps.append(
   9415                CGGeneric(
   9416                    fill(
   9417                        """
   9418                    // Since our object is an Xray, we can just CheckedUnwrapStatic:
   9419                    // we know Xrays have no dynamic unwrap behavior.
   9420                    ${obj} = js::CheckedUnwrapStatic(${obj});
   9421                    if (!${obj}) {
   9422                      return false;
   9423                    }
   9424                    """,
   9425                        obj=unwrappedVar,
   9426                    )
   9427                )
   9428            )
   9429            if isConstructor:
   9430                # If we're called via an xray, we need to enter the underlying
   9431                # object's compartment and then wrap up all of our arguments into
   9432                # that compartment as needed.  This is all happening after we've
   9433                # already done the conversions from JS values to WebIDL (C++)
   9434                # values, so we only need to worry about cases where there are 'any'
   9435                # or 'object' types, or other things that we represent as actual
   9436                # JSAPI types, present.  Effectively, we're emulating a
   9437                # CrossCompartmentWrapper, but working with the C++ types, not the
   9438                # original list of JS::Values.
   9439                cgThings.append(CGGeneric("Maybe<JSAutoRealm> ar;\n"))
   9440                xraySteps.append(CGGeneric("ar.emplace(cx, obj);\n"))
   9441                xraySteps.append(
   9442                    CGGeneric(
   9443                        dedent(
   9444                            """
   9445                    if (!JS_WrapObject(cx, &desiredProto)) {
   9446                      return false;
   9447                    }
   9448                    """
   9449                        )
   9450                    )
   9451                )
   9452                xraySteps.extend(
   9453                    wrapArgIntoCurrentCompartment(arg, argname, isMember=False)
   9454                    for arg, argname in self.getArguments()
   9455                )
   9456 
   9457            cgThings.append(CGIfWrapper(CGList(xraySteps), "objIsXray"))
   9458 
   9459        if idlNode.getExtendedAttribute("CEReactions") is not None and not getter:
   9460            cgThings.append(
   9461                CGGeneric(
   9462                    dedent(
   9463                        """
   9464                Maybe<AutoCEReaction> ceReaction;
   9465                DocGroup* docGroup = self->GetDocGroup();
   9466                if (docGroup) {
   9467                  ceReaction.emplace(docGroup->CustomElementReactionsStack(), cx);
   9468                }
   9469                """
   9470                    )
   9471                )
   9472            )
   9473 
   9474        # If this is a method that was generated by a maplike/setlike
   9475        # interface, use the maplike/setlike generator to fill in the body.
   9476        # Otherwise, use CGCallGenerator to call the native method.
   9477        if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
   9478            if (
   9479                idlNode.maplikeOrSetlikeOrIterable.isMaplike()
   9480                or idlNode.maplikeOrSetlikeOrIterable.isSetlike()
   9481            ):
   9482                cgThings.append(
   9483                    CGMaplikeOrSetlikeMethodGenerator(
   9484                        descriptor,
   9485                        idlNode.maplikeOrSetlikeOrIterable,
   9486                        idlNode.identifier.name,
   9487                    )
   9488                )
   9489            else:
   9490                cgThings.append(
   9491                    CGIterableMethodGenerator(
   9492                        descriptor,
   9493                        idlNode.identifier.name,
   9494                        self.getArgumentNames(),
   9495                    )
   9496                )
   9497        elif idlNode.isAttr() and idlNode.type.isObservableArray():
   9498            assert setter
   9499            cgThings.append(CGObservableArraySetterGenerator(descriptor, idlNode))
   9500        else:
   9501            if errorReportingLabel is None:
   9502                context = GetLabelForErrorReporting(descriptor, idlNode, isConstructor)
   9503                if getter:
   9504                    context = context + " getter"
   9505                elif setter:
   9506                    context = context + " setter"
   9507                # Callee expects a quoted string for the context if
   9508                # there's a context.
   9509                context = '"%s"' % context
   9510            else:
   9511                context = errorReportingLabel
   9512 
   9513            if idlNode.isMethod() and idlNode.getExtendedAttribute("WebExtensionStub"):
   9514                [
   9515                    nativeMethodName,
   9516                    argsPre,
   9517                    args,
   9518                ] = self.processWebExtensionStubAttribute(cgThings)
   9519            else:
   9520                args = self.getArguments()
   9521 
   9522            cgThings.append(
   9523                CGCallGenerator(
   9524                    self.needsErrorResult(),
   9525                    needsCallerType(idlNode),
   9526                    isChromeOnly(idlNode),
   9527                    args,
   9528                    argsPre + additionalArgsPre,
   9529                    returnType,
   9530                    self.extendedAttributes,
   9531                    descriptor,
   9532                    nativeMethodName,
   9533                    static,
   9534                    # We know our "self" must be being kept alive; otherwise we have
   9535                    # a serious problem.  In common cases it's just an argument and
   9536                    # we're MOZ_CAN_RUN_SCRIPT, but in some cases it's on the stack
   9537                    # and being kept alive via references from JS.
   9538                    object="MOZ_KnownLive(self)",
   9539                    argsPost=argsPost,
   9540                    resultVar=resultVar,
   9541                    context=context,
   9542                )
   9543            )
   9544 
   9545        if useCounterName:
   9546            # Generate a telemetry call for when [UseCounter] is used.
   9547            windowCode = fill(
   9548                """
   9549                SetUseCounter(obj, eUseCounter_${useCounterName});
   9550                """,
   9551                useCounterName=useCounterName,
   9552            )
   9553            workerCode = fill(
   9554                """
   9555                SetUseCounter(UseCounterWorker::${useCounterName});
   9556                """,
   9557                useCounterName=useCounterName,
   9558            )
   9559            code = ""
   9560            if idlNode.isExposedInWindow() and idlNode.isExposedInAnyWorker():
   9561                code += fill(
   9562                    """
   9563                    if (NS_IsMainThread()) {
   9564                      ${windowCode}
   9565                    } else {
   9566                      ${workerCode}
   9567                    }
   9568                    """,
   9569                    windowCode=windowCode,
   9570                    workerCode=workerCode,
   9571                )
   9572            elif idlNode.isExposedInWindow():
   9573                code += windowCode
   9574            elif idlNode.isExposedInAnyWorker():
   9575                code += workerCode
   9576 
   9577            cgThings.append(CGGeneric(code))
   9578 
   9579        self.cgRoot = CGList(cgThings)
   9580 
   9581    def getArgumentNames(self):
   9582        return ["arg" + str(i) for i in range(len(self.arguments))]
   9583 
   9584    def getArguments(self):
   9585        return list(zip(self.arguments, self.getArgumentNames()))
   9586 
   9587    def processWebExtensionStubAttribute(self, cgThings):
   9588        nativeMethodName = "CallWebExtMethod"
   9589        stubNameSuffix = self.idlNode.getExtendedAttribute("WebExtensionStub")
   9590        if isinstance(stubNameSuffix, list):
   9591            nativeMethodName += stubNameSuffix[0]
   9592 
   9593        argsLength = len(self.getArguments())
   9594        singleVariadicArg = argsLength == 1 and self.getArguments()[0][0].variadic
   9595 
   9596        # If the method signature does only include a single variadic arguments,
   9597        # then `arg0` is already a Sequence of JS values and we can pass that
   9598        # to the WebExtensions Stub method as is.
   9599        if singleVariadicArg:
   9600            argsPre = [
   9601                "cx",
   9602                'u"%s"_ns' % self.idlNode.identifier.name,
   9603                "Constify(%s)" % "arg0",
   9604            ]
   9605            args = []
   9606            return [nativeMethodName, argsPre, args]
   9607 
   9608        argsPre = [
   9609            "cx",
   9610            'u"%s"_ns' % self.idlNode.identifier.name,
   9611            "Constify(%s)" % "args_sequence",
   9612        ]
   9613        args = []
   9614 
   9615        # Determine the maximum number of elements of the js values sequence argument,
   9616        # skipping the last optional callback argument if any:
   9617        #
   9618        # if this WebExtensions API method does expect a last optional callback argument,
   9619        # then it is the callback parameter supported for chrome-compatibility
   9620        # reasons, and we want it as a separate argument passed to the WebExtension
   9621        # stub method and skip it from the js values sequence including all other
   9622        # arguments.
   9623        maxArgsSequenceLen = argsLength
   9624        if argsLength > 0:
   9625            lastArg = self.getArguments()[argsLength - 1]
   9626            isCallback = lastArg[0].type.tag() == IDLType.Tags.callback
   9627            if isCallback and lastArg[0].optional:
   9628                argsPre.append(
   9629                    "MOZ_KnownLive(NonNullHelper(Constify(%s)))" % lastArg[1]
   9630                )
   9631                maxArgsSequenceLen = argsLength - 1
   9632 
   9633        cgThings.append(
   9634            CGGeneric(
   9635                dedent(
   9636                    fill(
   9637                        """
   9638            // Collecting all args js values into the single sequence argument
   9639            // passed to the webextensions stub method.
   9640            //
   9641            // NOTE: The stub method will receive the original non-normalized js values,
   9642            // but those arguments will still be normalized on the main thread by the
   9643            // WebExtensions API request handler using the same JSONSchema defnition
   9644            // used by the non-webIDL webextensions API bindings.
   9645            AutoSequence<JS::Value> args_sequence;
   9646            SequenceRooter<JS::Value> args_sequence_holder(cx, &args_sequence);
   9647 
   9648            // maximum number of arguments expected by the WebExtensions API method
   9649            // excluding the last optional chrome-compatible callback argument (which
   9650            // is being passed to the stub method as a separate additional argument).
   9651            uint32_t maxArgsSequenceLen = ${maxArgsSequenceLen};
   9652 
   9653            uint32_t sequenceArgsLen = args.length() <= maxArgsSequenceLen ?
   9654              args.length() : maxArgsSequenceLen;
   9655 
   9656            if (sequenceArgsLen > 0) {
   9657              if (!args_sequence.SetCapacity(sequenceArgsLen, mozilla::fallible)) {
   9658                JS_ReportOutOfMemory(cx);
   9659                return false;
   9660              }
   9661              for (uint32_t argIdx = 0; argIdx < sequenceArgsLen; ++argIdx) {
   9662                // OK to do infallible append here, since we ensured capacity already.
   9663                JS::Value& slot = *args_sequence.AppendElement();
   9664                slot = args[argIdx];
   9665              }
   9666            }
   9667            """,
   9668                        maxArgsSequenceLen=maxArgsSequenceLen,
   9669                    )
   9670                )
   9671            )
   9672        )
   9673 
   9674        return [nativeMethodName, argsPre, args]
   9675 
   9676    def needsErrorResult(self):
   9677        return "needsErrorResult" in self.extendedAttributes
   9678 
   9679    def wrap_return_value(self):
   9680        wrapCode = ""
   9681 
   9682        returnsNewObject = memberReturnsNewObject(self.idlNode)
   9683        if returnsNewObject and (
   9684            self.returnType.isGeckoInterface() or self.returnType.isPromise()
   9685        ):
   9686            wrapCode += dedent(
   9687                """
   9688                static_assert(!std::is_pointer_v<decltype(result)>,
   9689                              "NewObject implies that we need to keep the object alive with a strong reference.");
   9690                """
   9691            )
   9692 
   9693        if self.setSlot:
   9694            # For attributes in slots, we want to do some
   9695            # post-processing once we've wrapped them.
   9696            successCode = "break;\n"
   9697        else:
   9698            successCode = None
   9699 
   9700        resultTemplateValues = {
   9701            "jsvalRef": "args.rval()",
   9702            "jsvalHandle": "args.rval()",
   9703            "returnsNewObject": returnsNewObject,
   9704            "isConstructorRetval": self.isConstructor,
   9705            "successCode": successCode,
   9706            # 'obj' in this dictionary is the thing whose compartment we are
   9707            # trying to do the to-JS conversion in.  We're going to put that
   9708            # thing in a variable named "conversionScope" if setSlot is true.
   9709            # Otherwise, just use "obj" for lack of anything better.
   9710            "obj": "conversionScope" if self.setSlot else "obj",
   9711        }
   9712 
   9713        wrapCode += wrapForType(self.returnType, self.descriptor, resultTemplateValues)
   9714 
   9715        if self.setSlot:
   9716            if self.idlNode.isStatic():
   9717                raise TypeError(
   9718                    "Attribute %s.%s is static, so we don't have a useful slot "
   9719                    "to cache it in, because we don't have support for that on "
   9720                    "interface objects.  See "
   9721                    "https://bugzilla.mozilla.org/show_bug.cgi?id=1363870"
   9722                    % (
   9723                        self.descriptor.interface.identifier.name,
   9724                        self.idlNode.identifier.name,
   9725                    )
   9726                )
   9727 
   9728            # When using a slot on the Xray expando, we need to make sure that
   9729            # our initial conversion to a JS::Value is done in the caller
   9730            # compartment.  When using a slot on our reflector, we want to do
   9731            # the conversion in the compartment of that reflector (that is,
   9732            # slotStorage).  In both cases we want to make sure that we finally
   9733            # set up args.rval() to be in the caller compartment.  We also need
   9734            # to make sure that the conversion steps happen inside a do/while
   9735            # that they can break out of on success.
   9736            #
   9737            # Of course we always have to wrap the value into the slotStorage
   9738            # compartment before we store it in slotStorage.
   9739 
   9740            # postConversionSteps are the steps that run while we're still in
   9741            # the compartment we do our conversion in but after we've finished
   9742            # the initial conversion into args.rval().
   9743            postConversionSteps = ""
   9744            if self.idlNode.getExtendedAttribute("Frozen"):
   9745                assert (
   9746                    self.idlNode.type.isSequence() or self.idlNode.type.isDictionary()
   9747                )
   9748                freezeValue = CGGeneric(
   9749                    "JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());\n"
   9750                    "if (!JS_FreezeObject(cx, rvalObj)) {\n"
   9751                    "  return false;\n"
   9752                    "}\n"
   9753                )
   9754                if self.idlNode.type.nullable():
   9755                    freezeValue = CGIfWrapper(freezeValue, "args.rval().isObject()")
   9756                postConversionSteps += freezeValue.define()
   9757 
   9758            # slotStorageSteps are steps that run once we have entered the
   9759            # slotStorage compartment.
   9760            if self.idlNode.getExtendedAttribute(
   9761                "ReflectedHTMLAttributeReturningFrozenArray"
   9762            ):
   9763                storeInSlot = fill(
   9764                    """
   9765                    array[${arrayIndex}] = storedVal;
   9766                    """,
   9767                    arrayIndex=reflectedHTMLAttributesArrayIndex(
   9768                        self.descriptor, self.idlNode
   9769                    ),
   9770                )
   9771            else:
   9772                storeInSlot = dedent(
   9773                    """
   9774                    JS::SetReservedSlot(slotStorage, slotIndex, storedVal);
   9775                    """
   9776                )
   9777 
   9778            slotStorageSteps = fill(
   9779                """
   9780                // Make a copy so that we don't do unnecessary wrapping on args.rval().
   9781                JS::Rooted<JS::Value> storedVal(cx, args.rval());
   9782                if (!${maybeWrap}(cx, &storedVal)) {
   9783                  return false;
   9784                }
   9785                $*{storeInSlot}
   9786                """,
   9787                maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type),
   9788                storeInSlot=storeInSlot,
   9789            )
   9790 
   9791            checkForXray = mayUseXrayExpandoSlots(self.descriptor, self.idlNode)
   9792 
   9793            # For the case of Cached attributes, go ahead and preserve our
   9794            # wrapper if needed.  We need to do this because otherwise the
   9795            # wrapper could get garbage-collected and the cached value would
   9796            # suddenly disappear, but the whole premise of cached values is that
   9797            # they never change without explicit action on someone's part.  We
   9798            # don't do this for StoreInSlot, since those get dealt with during
   9799            # wrapper setup, and failure would involve us trying to clear an
   9800            # already-preserved wrapper.
   9801            if (
   9802                self.idlNode.getExtendedAttribute("Cached")
   9803                or self.idlNode.getExtendedAttribute(
   9804                    "ReflectedHTMLAttributeReturningFrozenArray"
   9805                )
   9806            ) and self.descriptor.wrapperCache:
   9807                preserveWrapper = dedent(
   9808                    """
   9809                    PreserveWrapper(self);
   9810                    """
   9811                )
   9812                if checkForXray:
   9813                    preserveWrapper = fill(
   9814                        """
   9815                        if (!isXray) {
   9816                          // In the Xray case we don't need to do this, because getting the
   9817                          // expando object already preserved our wrapper.
   9818                          $*{preserveWrapper}
   9819                        }
   9820                        """,
   9821                        preserveWrapper=preserveWrapper,
   9822                    )
   9823                slotStorageSteps += preserveWrapper
   9824 
   9825            if checkForXray:
   9826                # In the Xray case we use the current global as conversion
   9827                # scope, as explained in the big compartment/conversion comment
   9828                # above.
   9829                conversionScope = "isXray ? JS::CurrentGlobalOrNull(cx) : slotStorage"
   9830            else:
   9831                conversionScope = "slotStorage"
   9832 
   9833            wrapCode = fill(
   9834                """
   9835                {
   9836                  JS::Rooted<JSObject*> conversionScope(cx, ${conversionScope});
   9837                  JSAutoRealm ar(cx, conversionScope);
   9838                  do { // block we break out of when done wrapping
   9839                    $*{wrapCode}
   9840                  } while (false);
   9841                  $*{postConversionSteps}
   9842                }
   9843                { // And now store things in the realm of our slotStorage.
   9844                  JSAutoRealm ar(cx, slotStorage);
   9845                  $*{slotStorageSteps}
   9846                }
   9847                // And now make sure args.rval() is in the caller realm.
   9848                return ${maybeWrap}(cx, args.rval());
   9849                """,
   9850                conversionScope=conversionScope,
   9851                wrapCode=wrapCode,
   9852                postConversionSteps=postConversionSteps,
   9853                slotStorageSteps=slotStorageSteps,
   9854                maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type),
   9855            )
   9856        return wrapCode
   9857 
   9858    def define(self):
   9859        return self.cgRoot.define() + self.wrap_return_value()
   9860 
   9861 
   9862 class CGSwitch(CGList):
   9863    """
   9864    A class to generate code for a switch statement.
   9865 
   9866    Takes three constructor arguments: an expression, a list of cases,
   9867    and an optional default.
   9868 
   9869    Each case is a CGCase.  The default is a CGThing for the body of
   9870    the default case, if any.
   9871    """
   9872 
   9873    def __init__(self, expression, cases, default=None):
   9874        CGList.__init__(self, [CGIndenter(c) for c in cases])
   9875        self.prepend(CGGeneric("switch (" + expression + ") {\n"))
   9876        if default is not None:
   9877            self.append(
   9878                CGIndenter(
   9879                    CGWrapper(CGIndenter(default), pre="default: {\n", post="}\n")
   9880                )
   9881            )
   9882 
   9883        self.append(CGGeneric("}\n"))
   9884 
   9885 
   9886 class CGCase(CGList):
   9887    """
   9888    A class to generate code for a case statement.
   9889 
   9890    Takes three constructor arguments: an expression, a CGThing for
   9891    the body (allowed to be None if there is no body), and an optional
   9892    argument for whether add a break, add fallthrough annotation or add nothing
   9893    (defaulting to add a break).
   9894    """
   9895 
   9896    ADD_BREAK = 0
   9897    ADD_FALLTHROUGH = 1
   9898    DONT_ADD_BREAK = 2
   9899 
   9900    def __init__(self, expression, body, breakOrFallthrough=ADD_BREAK):
   9901        CGList.__init__(self, [])
   9902 
   9903        assert (
   9904            breakOrFallthrough == CGCase.ADD_BREAK
   9905            or breakOrFallthrough == CGCase.ADD_FALLTHROUGH
   9906            or breakOrFallthrough == CGCase.DONT_ADD_BREAK
   9907        )
   9908 
   9909        self.append(CGGeneric("case " + expression + ": {\n"))
   9910        bodyList = CGList([body])
   9911        if breakOrFallthrough == CGCase.ADD_FALLTHROUGH:
   9912            bodyList.append(CGGeneric("[[fallthrough]];\n"))
   9913        elif breakOrFallthrough == CGCase.ADD_BREAK:
   9914            bodyList.append(CGGeneric("break;\n"))
   9915        self.append(CGIndenter(bodyList))
   9916        self.append(CGGeneric("}\n"))
   9917 
   9918 
   9919 class CGMethodCall(CGThing):
   9920    """
   9921    A class to generate selection of a method signature from a set of
   9922    signatures and generation of a call to that signature.
   9923    """
   9924 
   9925    def __init__(
   9926        self, nativeMethodName, static, descriptor, method, isConstructor=False
   9927    ):
   9928        CGThing.__init__(self)
   9929 
   9930        methodName = GetLabelForErrorReporting(descriptor, method, isConstructor)
   9931        argDesc = "argument %d"
   9932 
   9933        if method.getExtendedAttribute("UseCounter"):
   9934            useCounterName = methodName.replace(".", "_").replace(" ", "_")
   9935        else:
   9936            useCounterName = None
   9937 
   9938        if method.isStatic():
   9939            nativeType = descriptor.nativeType
   9940            staticTypeOverride = PropertyDefiner.getStringAttr(
   9941                method, "StaticClassOverride"
   9942            )
   9943            if staticTypeOverride:
   9944                nativeType = staticTypeOverride
   9945            nativeMethodName = "%s::%s" % (nativeType, nativeMethodName)
   9946 
   9947        def requiredArgCount(signature):
   9948            arguments = signature[1]
   9949            if len(arguments) == 0:
   9950                return 0
   9951            requiredArgs = len(arguments)
   9952            while requiredArgs and arguments[requiredArgs - 1].optional:
   9953                requiredArgs -= 1
   9954            return requiredArgs
   9955 
   9956        def getPerSignatureCall(signature, argConversionStartsAt=0):
   9957            return CGPerSignatureCall(
   9958                signature[0],
   9959                signature[1],
   9960                nativeMethodName,
   9961                static,
   9962                descriptor,
   9963                method,
   9964                argConversionStartsAt=argConversionStartsAt,
   9965                isConstructor=isConstructor,
   9966                useCounterName=useCounterName,
   9967            )
   9968 
   9969        signatures = method.signatures()
   9970        if len(signatures) == 1:
   9971            # Special case: we can just do a per-signature method call
   9972            # here for our one signature and not worry about switching
   9973            # on anything.
   9974            signature = signatures[0]
   9975            self.cgRoot = CGList([getPerSignatureCall(signature)])
   9976            requiredArgs = requiredArgCount(signature)
   9977 
   9978            # Skip required arguments check for maplike/setlike interfaces, as
   9979            # they can have arguments which are not passed, and are treated as
   9980            # if undefined had been explicitly passed.
   9981            if requiredArgs > 0 and not method.isMaplikeOrSetlikeOrIterableMethod():
   9982                code = fill(
   9983                    """
   9984                    if (!args.requireAtLeast(cx, "${methodName}", ${requiredArgs})) {
   9985                      return false;
   9986                    }
   9987                    """,
   9988                    requiredArgs=requiredArgs,
   9989                    methodName=methodName,
   9990                )
   9991                self.cgRoot.prepend(CGGeneric(code))
   9992            return
   9993 
   9994        # Need to find the right overload
   9995        maxArgCount = method.maxArgCount
   9996        allowedArgCounts = method.allowedArgCounts
   9997 
   9998        argCountCases = []
   9999        for argCountIdx, argCount in enumerate(allowedArgCounts):
  10000            possibleSignatures = method.signaturesForArgCount(argCount)
  10001 
  10002            # Try to optimize away cases when the next argCount in the list
  10003            # will have the same code as us; if it does, we can fall through to
  10004            # that case.
  10005            if argCountIdx + 1 < len(allowedArgCounts):
  10006                nextPossibleSignatures = method.signaturesForArgCount(
  10007                    allowedArgCounts[argCountIdx + 1]
  10008                )
  10009            else:
  10010                nextPossibleSignatures = None
  10011            if possibleSignatures == nextPossibleSignatures:
  10012                # Same set of signatures means we better have the same
  10013                # distinguishing index.  So we can in fact just fall through to
  10014                # the next case here.
  10015                assert len(possibleSignatures) == 1 or (
  10016                    method.distinguishingIndexForArgCount(argCount)
  10017                    == method.distinguishingIndexForArgCount(
  10018                        allowedArgCounts[argCountIdx + 1]
  10019                    )
  10020                )
  10021                argCountCases.append(
  10022                    CGCase(str(argCount), None, CGCase.ADD_FALLTHROUGH)
  10023                )
  10024                continue
  10025 
  10026            if len(possibleSignatures) == 1:
  10027                # easy case!
  10028                signature = possibleSignatures[0]
  10029                argCountCases.append(
  10030                    CGCase(str(argCount), getPerSignatureCall(signature))
  10031                )
  10032                continue
  10033 
  10034            distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
  10035 
  10036            def distinguishingArgument(signature):
  10037                args = signature[1]
  10038                if distinguishingIndex < len(args):
  10039                    return args[distinguishingIndex]
  10040                assert args[-1].variadic
  10041                return args[-1]
  10042 
  10043            def distinguishingType(signature):
  10044                return distinguishingArgument(signature).type
  10045 
  10046            for sig in possibleSignatures:
  10047                # We should not have "any" args at distinguishingIndex,
  10048                # since we have multiple possible signatures remaining,
  10049                # but "any" is never distinguishable from anything else.
  10050                assert not distinguishingType(sig).isAny()
  10051                # We can't handle unions at the distinguishing index.
  10052                if distinguishingType(sig).isUnion():
  10053                    raise TypeError(
  10054                        "No support for unions as distinguishing "
  10055                        "arguments yet: %s" % distinguishingArgument(sig).location
  10056                    )
  10057                # We don't support variadics as the distinguishingArgument yet.
  10058                # If you want to add support, consider this case:
  10059                #
  10060                #   undefined(long... foo);
  10061                #   undefined(long bar, Int32Array baz);
  10062                #
  10063                # in which we have to convert argument 0 to long before picking
  10064                # an overload... but all the variadic stuff needs to go into a
  10065                # single array in case we pick that overload, so we have to have
  10066                # machinery for converting argument 0 to long and then either
  10067                # placing it in the variadic bit or not.  Or something.  We may
  10068                # be able to loosen this restriction if the variadic arg is in
  10069                # fact at distinguishingIndex, perhaps.  Would need to
  10070                # double-check.
  10071                if distinguishingArgument(sig).variadic:
  10072                    raise TypeError(
  10073                        "No support for variadics as distinguishing "
  10074                        "arguments yet: %s" % distinguishingArgument(sig).location
  10075                    )
  10076 
  10077            # Convert all our arguments up to the distinguishing index.
  10078            # Doesn't matter which of the possible signatures we use, since
  10079            # they all have the same types up to that point; just use
  10080            # possibleSignatures[0]
  10081            caseBody = [
  10082                CGArgumentConverter(
  10083                    possibleSignatures[0][1][i],
  10084                    i,
  10085                    descriptor,
  10086                    argDesc % (i + 1),
  10087                    method,
  10088                )
  10089                for i in range(0, distinguishingIndex)
  10090            ]
  10091 
  10092            # Select the right overload from our set.
  10093            distinguishingArg = "args[%d]" % distinguishingIndex
  10094 
  10095            def tryCall(
  10096                signature, indent, isDefinitelyObject=False, isNullOrUndefined=False
  10097            ):
  10098                assert not isDefinitelyObject or not isNullOrUndefined
  10099                assert isDefinitelyObject or isNullOrUndefined
  10100                if isDefinitelyObject:
  10101                    failureCode = "break;\n"
  10102                else:
  10103                    failureCode = None
  10104                type = distinguishingType(signature)
  10105                # The argument at index distinguishingIndex can't possibly be
  10106                # unset here, because we've already checked that argc is large
  10107                # enough that we can examine this argument.  But note that we
  10108                # still want to claim that optional arguments are optional, in
  10109                # case undefined was passed in.
  10110                argIsOptional = distinguishingArgument(signature).canHaveMissingValue()
  10111                testCode = instantiateJSToNativeConversion(
  10112                    getJSToNativeConversionInfo(
  10113                        type,
  10114                        descriptor,
  10115                        failureCode=failureCode,
  10116                        isDefinitelyObject=isDefinitelyObject,
  10117                        isNullOrUndefined=isNullOrUndefined,
  10118                        isOptional=argIsOptional,
  10119                        sourceDescription=(argDesc % (distinguishingIndex + 1)),
  10120                    ),
  10121                    {
  10122                        "declName": "arg%d" % distinguishingIndex,
  10123                        "holderName": ("arg%d" % distinguishingIndex) + "_holder",
  10124                        "val": distinguishingArg,
  10125                        "obj": "obj",
  10126                        "haveValue": "args.hasDefined(%d)" % distinguishingIndex,
  10127                        "passedToJSImpl": toStringBool(
  10128                            isJSImplementedDescriptor(descriptor)
  10129                        ),
  10130                    },
  10131                    checkForValue=argIsOptional,
  10132                )
  10133                caseBody.append(CGIndenter(testCode, indent))
  10134 
  10135                # If we got this far, we know we unwrapped to the right
  10136                # C++ type, so just do the call.  Start conversion with
  10137                # distinguishingIndex + 1, since we already converted
  10138                # distinguishingIndex.
  10139                caseBody.append(
  10140                    CGIndenter(
  10141                        getPerSignatureCall(signature, distinguishingIndex + 1), indent
  10142                    )
  10143                )
  10144 
  10145            def hasConditionalConversion(type):
  10146                """
  10147                Return whether the argument conversion for this type will be
  10148                conditional on the type of incoming JS value.  For example, for
  10149                interface types the conversion is conditional on the incoming
  10150                value being isObject().
  10151 
  10152                For the types for which this returns false, we do not have to
  10153                output extra isUndefined() or isNullOrUndefined() cases, because
  10154                null/undefined values will just fall through into our
  10155                unconditional conversion.
  10156                """
  10157                if type.isString() or type.isEnum():
  10158                    return False
  10159                if type.isBoolean():
  10160                    distinguishingTypes = (
  10161                        distinguishingType(s) for s in possibleSignatures
  10162                    )
  10163                    return any(
  10164                        t.isString() or t.isEnum() or t.isNumeric()
  10165                        for t in distinguishingTypes
  10166                    )
  10167                if type.isNumeric():
  10168                    distinguishingTypes = (
  10169                        distinguishingType(s) for s in possibleSignatures
  10170                    )
  10171                    return any(t.isString() or t.isEnum() for t in distinguishingTypes)
  10172                return True
  10173 
  10174            def needsNullOrUndefinedCase(type):
  10175                """
  10176                Return true if the type needs a special isNullOrUndefined() case
  10177                """
  10178                return (
  10179                    type.nullable() and hasConditionalConversion(type)
  10180                ) or type.isDictionary()
  10181 
  10182            # First check for undefined and optional distinguishing arguments
  10183            # and output a special branch for that case.  Note that we don't
  10184            # use distinguishingArgument here because we actualy want to
  10185            # exclude variadic arguments.  Also note that we skip this check if
  10186            # we plan to output a isNullOrUndefined() special case for this
  10187            # argument anyway, since that will subsume our isUndefined() check.
  10188            # This is safe, because there can be at most one nullable
  10189            # distinguishing argument, so if we're it we'll definitely get
  10190            # picked up by the nullable handling.  Also, we can skip this check
  10191            # if the argument has an unconditional conversion later on.
  10192            undefSigs = [
  10193                s
  10194                for s in possibleSignatures
  10195                if distinguishingIndex < len(s[1])
  10196                and s[1][distinguishingIndex].optional
  10197                and hasConditionalConversion(s[1][distinguishingIndex].type)
  10198                and not needsNullOrUndefinedCase(s[1][distinguishingIndex].type)
  10199            ]
  10200            # Can't have multiple signatures with an optional argument at the
  10201            # same index.
  10202            assert len(undefSigs) < 2
  10203            if len(undefSigs) > 0:
  10204                caseBody.append(
  10205                    CGGeneric("if (%s.isUndefined()) {\n" % distinguishingArg)
  10206                )
  10207                tryCall(undefSigs[0], 2, isNullOrUndefined=True)
  10208                caseBody.append(CGGeneric("}\n"))
  10209 
  10210            # Next, check for null or undefined.  That means looking for
  10211            # nullable arguments at the distinguishing index and outputting a
  10212            # separate branch for them.  But if the nullable argument has an
  10213            # unconditional conversion, we don't need to do that.  The reason
  10214            # for that is that at most one argument at the distinguishing index
  10215            # is nullable (since two nullable arguments are not
  10216            # distinguishable), and null/undefined values will always fall
  10217            # through to the unconditional conversion we have, if any, since
  10218            # they will fail whatever the conditions on the input value are for
  10219            # our other conversions.
  10220            nullOrUndefSigs = [
  10221                s
  10222                for s in possibleSignatures
  10223                if needsNullOrUndefinedCase(distinguishingType(s))
  10224            ]
  10225            # Can't have multiple nullable types here
  10226            assert len(nullOrUndefSigs) < 2
  10227            if len(nullOrUndefSigs) > 0:
  10228                caseBody.append(
  10229                    CGGeneric("if (%s.isNullOrUndefined()) {\n" % distinguishingArg)
  10230                )
  10231                tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True)
  10232                caseBody.append(CGGeneric("}\n"))
  10233 
  10234            # Now check for distinguishingArg being various kinds of objects.
  10235            # The spec says to check for the following things in order:
  10236            # 1)  A platform object that's not a platform array object, being
  10237            #     passed to an interface or "object" arg.
  10238            # 2)  A callable object being passed to a callback or "object" arg.
  10239            # 3)  An iterable object being passed to a sequence arg.
  10240            # 4)  Any object being passed to a array or callback interface or
  10241            #     dictionary or "object" arg.
  10242 
  10243            # First grab all the overloads that have a non-callback interface
  10244            # (which includes SpiderMonkey interfaces) at the distinguishing
  10245            # index.  We can also include the ones that have an "object" here,
  10246            # since if those are present no other object-typed argument will
  10247            # be.
  10248            objectSigs = [
  10249                s
  10250                for s in possibleSignatures
  10251                if (
  10252                    distinguishingType(s).isObject()
  10253                    or distinguishingType(s).isNonCallbackInterface()
  10254                )
  10255            ]
  10256 
  10257            # And all the overloads that take callbacks
  10258            objectSigs.extend(
  10259                s for s in possibleSignatures if distinguishingType(s).isCallback()
  10260            )
  10261 
  10262            # And all the overloads that take sequences
  10263            objectSigs.extend(
  10264                s for s in possibleSignatures if distinguishingType(s).isSequence()
  10265            )
  10266 
  10267            # Now append all the overloads that take a dictionary or callback
  10268            # interface or record.  There should be only one of these!
  10269            genericObjectSigs = [
  10270                s
  10271                for s in possibleSignatures
  10272                if (
  10273                    distinguishingType(s).isDictionary()
  10274                    or distinguishingType(s).isRecord()
  10275                    or distinguishingType(s).isCallbackInterface()
  10276                )
  10277            ]
  10278            assert len(genericObjectSigs) <= 1
  10279            objectSigs.extend(genericObjectSigs)
  10280 
  10281            # There might be more than one thing in objectSigs; we need to check
  10282            # which ones we unwrap to.
  10283            if len(objectSigs) > 0:
  10284                # Here it's enough to guard on our argument being an object.
  10285                # The code for unwrapping non-callback interfaces, spiderMonkey
  10286                # interfaces, and sequences will just bail out and move
  10287                # on to the next overload if the object fails to unwrap
  10288                # correctly, while "object" accepts any object anyway.  We
  10289                # could even not do the isObject() check up front here, but in
  10290                # cases where we have multiple object overloads it makes sense
  10291                # to do it only once instead of for each overload.  That will
  10292                # also allow the unwrapping test to skip having to do codegen
  10293                # for the null-or-undefined case, which we already handled
  10294                # above.
  10295                caseBody.append(CGGeneric("if (%s.isObject()) {\n" % distinguishingArg))
  10296                for sig in objectSigs:
  10297                    caseBody.append(CGIndenter(CGGeneric("do {\n")))
  10298                    # Indent by 4, since we need to indent further
  10299                    # than our "do" statement
  10300                    tryCall(sig, 4, isDefinitelyObject=True)
  10301                    caseBody.append(CGIndenter(CGGeneric("} while (false);\n")))
  10302 
  10303                caseBody.append(CGGeneric("}\n"))
  10304 
  10305            # Now we only have to consider booleans, numerics, and strings.  If
  10306            # we only have one of them, then we can just output it.  But if not,
  10307            # then we need to output some of the cases conditionally: if we have
  10308            # a string overload, then boolean and numeric are conditional, and
  10309            # if not then boolean is conditional if we have a numeric overload.
  10310            def findUniqueSignature(filterLambda):
  10311                sigs = [s for s in possibleSignatures if filterLambda(s)]
  10312                assert len(sigs) < 2
  10313                if len(sigs) > 0:
  10314                    return sigs[0]
  10315                return None
  10316 
  10317            stringSignature = findUniqueSignature(
  10318                lambda s: (
  10319                    distinguishingType(s).isString() or distinguishingType(s).isEnum()
  10320                )
  10321            )
  10322            numericSignature = findUniqueSignature(
  10323                lambda s: distinguishingType(s).isNumeric()
  10324            )
  10325            booleanSignature = findUniqueSignature(
  10326                lambda s: distinguishingType(s).isBoolean()
  10327            )
  10328 
  10329            if stringSignature or numericSignature:
  10330                booleanCondition = "%s.isBoolean()"
  10331            else:
  10332                booleanCondition = None
  10333 
  10334            if stringSignature:
  10335                numericCondition = "%s.isNumber()"
  10336            else:
  10337                numericCondition = None
  10338 
  10339            def addCase(sig, condition):
  10340                sigCode = getPerSignatureCall(sig, distinguishingIndex)
  10341                if condition:
  10342                    sigCode = CGIfWrapper(sigCode, condition % distinguishingArg)
  10343                caseBody.append(sigCode)
  10344 
  10345            if booleanSignature:
  10346                addCase(booleanSignature, booleanCondition)
  10347            if numericSignature:
  10348                addCase(numericSignature, numericCondition)
  10349            if stringSignature:
  10350                addCase(stringSignature, None)
  10351 
  10352            if not booleanSignature and not numericSignature and not stringSignature:
  10353                # Just throw; we have no idea what we're supposed to
  10354                # do with this.
  10355                caseBody.append(
  10356                    CGGeneric(
  10357                        'return cx.ThrowErrorMessage<MSG_OVERLOAD_RESOLUTION_FAILED>("%d", "%d");\n'
  10358                        % (distinguishingIndex + 1, argCount)
  10359                    )
  10360                )
  10361 
  10362            argCountCases.append(CGCase(str(argCount), CGList(caseBody)))
  10363 
  10364        overloadCGThings = []
  10365        overloadCGThings.append(
  10366            CGGeneric(
  10367                "unsigned argcount = std::min(args.length(), %du);\n" % maxArgCount
  10368            )
  10369        )
  10370        overloadCGThings.append(
  10371            CGSwitch(
  10372                "argcount",
  10373                argCountCases,
  10374                CGGeneric(
  10375                    dedent(
  10376                        """
  10377                         // Using nsPrintfCString here would require including that
  10378                         // header.  Let's not worry about it.
  10379                         nsAutoCString argCountStr;
  10380                         argCountStr.AppendPrintf("%u", args.length());
  10381                         return cx.ThrowErrorMessage<MSG_INVALID_OVERLOAD_ARGCOUNT>(argCountStr.get());
  10382                         """
  10383                    )
  10384                ),
  10385            )
  10386        )
  10387        overloadCGThings.append(
  10388            CGGeneric(
  10389                'MOZ_CRASH("We have an always-returning default case");\n'
  10390                "return false;\n"
  10391            )
  10392        )
  10393        self.cgRoot = CGList(overloadCGThings)
  10394 
  10395    def define(self):
  10396        return self.cgRoot.define()
  10397 
  10398 
  10399 class CGGetterCall(CGPerSignatureCall):
  10400    """
  10401    A class to generate a native object getter call for a particular IDL
  10402    getter.
  10403    """
  10404 
  10405    def __init__(
  10406        self,
  10407        returnType,
  10408        nativeMethodName,
  10409        descriptor,
  10410        attr,
  10411        errorReportingLabel=None,
  10412        argsPre=[],
  10413        dontSetSlot=False,
  10414        extendedAttributes=None,
  10415        preConversionCode=None,
  10416    ):
  10417        self.preConversionCode = preConversionCode
  10418        if attr.getExtendedAttribute("UseCounter"):
  10419            useCounterName = "%s_%s_getter" % (
  10420                descriptor.interface.identifier.name,
  10421                attr.identifier.name,
  10422            )
  10423        else:
  10424            useCounterName = None
  10425        if attr.isStatic():
  10426            nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
  10427        CGPerSignatureCall.__init__(
  10428            self,
  10429            returnType,
  10430            [],
  10431            nativeMethodName,
  10432            attr.isStatic(),
  10433            descriptor,
  10434            attr,
  10435            getter=True,
  10436            useCounterName=useCounterName,
  10437            dontSetSlot=dontSetSlot,
  10438            extendedAttributes=extendedAttributes,
  10439            errorReportingLabel=errorReportingLabel,
  10440            additionalArgsPre=argsPre,
  10441        )
  10442 
  10443    def wrap_return_value(self):
  10444        wrap = CGPerSignatureCall.wrap_return_value(self)
  10445        if self.preConversionCode is not None:
  10446            wrap = self.preConversionCode + wrap
  10447        return wrap
  10448 
  10449 
  10450 class FakeIdentifier:
  10451    def __init__(self, name):
  10452        self.name = name
  10453 
  10454 
  10455 class FakeArgument:
  10456    """
  10457    A class that quacks like an IDLArgument.  This is used to make
  10458    setters look like method calls or for special operations.
  10459    """
  10460 
  10461    def __init__(self, type, name="arg", allowTreatNonCallableAsNull=False):
  10462        self.type = type
  10463        self.optional = False
  10464        self.variadic = False
  10465        self.defaultValue = None
  10466        self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull
  10467 
  10468        self.identifier = FakeIdentifier(name)
  10469 
  10470    def allowTreatNonCallableAsNull(self):
  10471        return self._allowTreatNonCallableAsNull
  10472 
  10473    def canHaveMissingValue(self):
  10474        return False
  10475 
  10476 
  10477 class CGSetterCall(CGPerSignatureCall):
  10478    """
  10479    A class to generate a native object setter call for a particular IDL
  10480    setter.
  10481    """
  10482 
  10483    def __init__(
  10484        self,
  10485        argType,
  10486        nativeMethodName,
  10487        descriptor,
  10488        attr,
  10489        errorReportingLabel=None,
  10490        argsPre=[],
  10491    ):
  10492        if attr.getExtendedAttribute("UseCounter"):
  10493            useCounterName = "%s_%s_setter" % (
  10494                descriptor.interface.identifier.name,
  10495                attr.identifier.name,
  10496            )
  10497        else:
  10498            useCounterName = None
  10499        if attr.isStatic():
  10500            nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
  10501        CGPerSignatureCall.__init__(
  10502            self,
  10503            None,
  10504            [FakeArgument(argType, allowTreatNonCallableAsNull=True)],
  10505            nativeMethodName,
  10506            attr.isStatic(),
  10507            descriptor,
  10508            attr,
  10509            setter=True,
  10510            useCounterName=useCounterName,
  10511            errorReportingLabel=errorReportingLabel,
  10512            additionalArgsPre=argsPre,
  10513        )
  10514 
  10515    def wrap_return_value(self):
  10516        attr = self.idlNode
  10517        clearSlot = ""
  10518        if self.descriptor.wrapperCache and attr.slotIndices is not None:
  10519            if attr.getExtendedAttribute("StoreInSlot"):
  10520                clearSlot = "%s(cx, self);\n" % MakeClearCachedValueNativeName(
  10521                    self.idlNode
  10522                )
  10523            elif attr.getExtendedAttribute("Cached"):
  10524                clearSlot = "%s(self);\n" % MakeClearCachedValueNativeName(self.idlNode)
  10525 
  10526        # We have no return value
  10527        return "\n" "%s" "return true;\n" % clearSlot
  10528 
  10529 
  10530 class CGAbstractBindingMethod(CGAbstractStaticMethod):
  10531    """
  10532    Common class to generate some of our class hooks.  This will generate the
  10533    function declaration, get a reference to the JS object for our binding
  10534    object (which might be an argument of the class hook or something we get
  10535    from a JS::CallArgs), and unwrap into the right C++ type. Subclasses are
  10536    expected to override the generate_code function to do the rest of the work.
  10537    This function should return a CGThing which is already properly indented.
  10538 
  10539    getThisObj should be code for getting a JSObject* for the binding
  10540    object.  "" can be passed in if the binding object is already stored in
  10541    'obj'.
  10542 
  10543    callArgs should be code for getting a JS::CallArgs into a variable
  10544    called 'args'.  This can be "" if there is already such a variable
  10545    around or if the body does not need a JS::CallArgs.
  10546 
  10547    """
  10548 
  10549    def __init__(
  10550        self,
  10551        descriptor,
  10552        name,
  10553        args,
  10554        getThisObj,
  10555        callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n",
  10556    ):
  10557        CGAbstractStaticMethod.__init__(
  10558            self, descriptor, name, "bool", args, canRunScript=True
  10559        )
  10560 
  10561        # This can't ever happen, because we only use this for class hooks.
  10562        self.unwrapFailureCode = fill(
  10563            """
  10564            MOZ_CRASH("Unexpected object in '${name}' hook");
  10565            return false;
  10566            """,
  10567            name=name,
  10568        )
  10569 
  10570        if getThisObj == "":
  10571            self.getThisObj = None
  10572        else:
  10573            self.getThisObj = CGGeneric(
  10574                "JS::Rooted<JSObject*> obj(cx, %s);\n" % getThisObj
  10575            )
  10576        self.callArgs = callArgs
  10577 
  10578    def definition_body(self):
  10579        body = self.callArgs
  10580        if self.getThisObj is not None:
  10581            body += self.getThisObj.define() + "\n"
  10582        body += "%s* self;\n" % self.descriptor.nativeType
  10583        body += dedent(
  10584            """
  10585            JS::Rooted<JS::Value> rootSelf(cx, JS::ObjectValue(*obj));
  10586            """
  10587        )
  10588 
  10589        body += str(
  10590            CastableObjectUnwrapper(
  10591                self.descriptor, "rootSelf", "&rootSelf", "self", self.unwrapFailureCode
  10592            )
  10593        )
  10594 
  10595        return body + self.generate_code().define()
  10596 
  10597    def generate_code(self):
  10598        assert False  # Override me
  10599 
  10600 
  10601 class CGAbstractStaticBindingMethod(CGAbstractStaticMethod):
  10602    """
  10603    Common class to generate the JSNatives for all our static methods, getters
  10604    and setters.  This will generate the function declaration and unwrap the
  10605    global object.  Subclasses are expected to override the generate_code
  10606    function to do the rest of the work.  This function should return a
  10607    CGThing which is already properly indented.
  10608    """
  10609 
  10610    def __init__(self, descriptor, name):
  10611        CGAbstractStaticMethod.__init__(
  10612            self, descriptor, name, "bool", JSNativeArguments(), canRunScript=True
  10613        )
  10614 
  10615    def definition_body(self):
  10616        # Make sure that "obj" is in the same compartment as "cx", since we'll
  10617        # later use it to wrap return values.
  10618        unwrap = dedent(
  10619            """
  10620            JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  10621            JS::Rooted<JSObject*> obj(cx, &args.callee());
  10622 
  10623            """
  10624        )
  10625        return unwrap + self.generate_code().define()
  10626 
  10627    def generate_code(self):
  10628        assert False  # Override me
  10629 
  10630 
  10631 def MakeNativeName(name):
  10632    return name[0].upper() + IDLToCIdentifier(name[1:])
  10633 
  10634 
  10635 def GetWebExposedName(idlObject, descriptor):
  10636    if idlObject == descriptor.operations["Stringifier"]:
  10637        return "toString"
  10638    name = idlObject.identifier.name
  10639    if name == "__namedsetter":
  10640        return "named setter"
  10641    if name == "__namedgetter":
  10642        return "named getter"
  10643    if name == "__indexedsetter":
  10644        return "indexed setter"
  10645    if name == "__indexedgetter":
  10646        return "indexed getter"
  10647    if name == "__legacycaller":
  10648        return "legacy caller"
  10649    return name
  10650 
  10651 
  10652 def GetConstructorNameForReporting(descriptor, ctor):
  10653    # Figure out the name of our constructor for reporting purposes.
  10654    # For unnamed webidl constructors, identifier.name is "constructor" but
  10655    # the name JS sees is the interface name; for legacy factory functions
  10656    # identifier.name is the actual name.
  10657    ctorName = ctor.identifier.name
  10658    if ctorName == "constructor":
  10659        return descriptor.interface.identifier.name
  10660    return ctorName
  10661 
  10662 
  10663 def GetLabelForErrorReporting(descriptor, idlObject, isConstructor):
  10664    """
  10665    descriptor is the descriptor for the interface involved
  10666 
  10667    idlObject is the method (regular or static), attribute (regular or
  10668    static), or constructor (named or not) involved.
  10669 
  10670    isConstructor is true if idlObject is a constructor and false otherwise.
  10671    """
  10672    if isConstructor:
  10673        return "%s constructor" % GetConstructorNameForReporting(descriptor, idlObject)
  10674 
  10675    namePrefix = descriptor.interface.identifier.name
  10676    name = GetWebExposedName(idlObject, descriptor)
  10677    if " " in name:
  10678        # It's got a space already, so just space-separate.
  10679        return "%s %s" % (namePrefix, name)
  10680 
  10681    return "%s.%s" % (namePrefix, name)
  10682 
  10683 
  10684 class CGSpecializedMethod(CGAbstractStaticMethod):
  10685    """
  10686    A class for generating the C++ code for a specialized method that the JIT
  10687    can call with lower overhead.
  10688    """
  10689 
  10690    def __init__(self, descriptor, method):
  10691        self.method = method
  10692        name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
  10693        args = [
  10694            Argument("JSContext*", "cx"),
  10695            Argument("JS::Handle<JSObject*>", "obj"),
  10696            Argument("void*", "void_self"),
  10697            Argument("const JSJitMethodCallArgs&", "args"),
  10698        ]
  10699        CGAbstractStaticMethod.__init__(
  10700            self, descriptor, name, "bool", args, canRunScript=True
  10701        )
  10702 
  10703    def definition_body(self):
  10704        nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method)
  10705        call = CGMethodCall(
  10706            nativeName, self.method.isStatic(), self.descriptor, self.method
  10707        ).define()
  10708        prefix = ""
  10709        if self.method.getExtendedAttribute("CrossOriginCallable"):
  10710            for signature in self.method.signatures():
  10711                # non-undefined signatures would require us to deal with remote proxies for the
  10712                # return value here.
  10713                if not signature[0].isUndefined():
  10714                    raise TypeError(
  10715                        "We don't support a method marked as CrossOriginCallable "
  10716                        "with non-undefined return type"
  10717                    )
  10718            prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
  10719            prefix = fill(
  10720                """
  10721                // CrossOriginThisPolicy::UnwrapThisObject stores a ${nativeType}::RemoteProxy in void_self
  10722                // if obj is a proxy with a RemoteObjectProxy handler for the right type, or else it stores
  10723                // a ${nativeType}. If we get here from the JIT (without going through UnwrapThisObject) we
  10724                // know void_self contains a ${nativeType}; we don't have special cases in the JIT to deal
  10725                // with remote object proxies.
  10726                if (IsRemoteObjectProxy(obj, ${prototypeID})) {
  10727                    auto* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
  10728                    $*{call}
  10729                }
  10730                """,
  10731                prototypeID=prototypeID,
  10732                nativeType=self.descriptor.nativeType,
  10733                call=call,
  10734            )
  10735        return prefix + fill(
  10736            """
  10737            auto* self = static_cast<${nativeType}*>(void_self);
  10738            $*{call}
  10739            """,
  10740            nativeType=self.descriptor.nativeType,
  10741            call=call,
  10742        )
  10743 
  10744    def auto_profiler_label(self):
  10745        interface_name = self.descriptor.interface.identifier.name
  10746        method_name = self.method.identifier.name
  10747        return fill(
  10748            """
  10749            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
  10750              "${interface_name}", "${method_name}", DOM, cx,
  10751              uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD) |
  10752              uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
  10753            """,
  10754            interface_name=interface_name,
  10755            method_name=method_name,
  10756        )
  10757 
  10758    @staticmethod
  10759    def should_have_method_description(descriptor, idlMethod):
  10760        """
  10761        Returns whether the given IDL method (static, non-static, constructor)
  10762        should have a method description declaration, for use in error
  10763        reporting.
  10764        """
  10765        # If a method has overloads, it needs a method description, because it
  10766        # can throw MSG_INVALID_OVERLOAD_ARGCOUNT at the very least.
  10767        if len(idlMethod.signatures()) != 1:
  10768            return True
  10769 
  10770        # Methods with only one signature need a method description if one of
  10771        # their args needs it.
  10772        sig = idlMethod.signatures()[0]
  10773        args = sig[1]
  10774        return any(
  10775            idlTypeNeedsCallContext(
  10776                arg.type,
  10777                descriptor,
  10778                allowTreatNonCallableAsNull=arg.allowTreatNonCallableAsNull(),
  10779            )
  10780            for arg in args
  10781        )
  10782 
  10783    @staticmethod
  10784    def error_reporting_label_helper(descriptor, idlMethod, isConstructor):
  10785        """
  10786        Returns the method description to use for error reporting for the given
  10787        IDL method.  Used to implement common error_reporting_label() functions
  10788        across different classes.
  10789        """
  10790        if not CGSpecializedMethod.should_have_method_description(
  10791            descriptor, idlMethod
  10792        ):
  10793            return None
  10794        return '"%s"' % GetLabelForErrorReporting(descriptor, idlMethod, isConstructor)
  10795 
  10796    def error_reporting_label(self):
  10797        return CGSpecializedMethod.error_reporting_label_helper(
  10798            self.descriptor, self.method, isConstructor=False
  10799        )
  10800 
  10801    @staticmethod
  10802    def makeNativeName(descriptor, method):
  10803        if method.underlyingAttr:
  10804            return CGSpecializedGetterCommon.makeNativeName(
  10805                descriptor, method.underlyingAttr
  10806            )
  10807        name = method.identifier.name
  10808        return MakeNativeName(descriptor.binaryNameFor(name, method.isStatic()))
  10809 
  10810 
  10811 class CGMethodPromiseWrapper(CGAbstractStaticMethod):
  10812    """
  10813    A class for generating a wrapper around another method that will
  10814    convert exceptions to promises.
  10815    """
  10816 
  10817    def __init__(self, descriptor, methodToWrap):
  10818        self.method = methodToWrap
  10819        name = self.makeName(methodToWrap.name)
  10820        args = list(methodToWrap.args)
  10821        CGAbstractStaticMethod.__init__(
  10822            self, descriptor, name, "bool", args, canRunScript=True
  10823        )
  10824 
  10825    def definition_body(self):
  10826        return fill(
  10827            """
  10828            bool ok = ${methodName}(${args});
  10829            if (ok) {
  10830              return true;
  10831            }
  10832            return ConvertExceptionToPromise(cx, args.rval());
  10833            """,
  10834            methodName=self.method.name,
  10835            args=", ".join(arg.name for arg in self.args),
  10836        )
  10837 
  10838    @staticmethod
  10839    def makeName(methodName):
  10840        return methodName + "_promiseWrapper"
  10841 
  10842 
  10843 class CGDefaultToJSONMethod(CGSpecializedMethod):
  10844    def __init__(self, descriptor, method):
  10845        assert method.isDefaultToJSON()
  10846        CGSpecializedMethod.__init__(self, descriptor, method)
  10847 
  10848    def definition_body(self):
  10849        ret = fill(
  10850            """
  10851            auto* self = static_cast<${nativeType}*>(void_self);
  10852            JS::Rooted<JSObject*> result(cx, JS_NewPlainObject(cx));
  10853            if (!result) {
  10854              return false;
  10855            }
  10856            """,
  10857            nativeType=self.descriptor.nativeType,
  10858        )
  10859 
  10860        jsonDescriptors = [self.descriptor]
  10861        interface = self.descriptor.interface.parent
  10862        while interface:
  10863            descriptor = self.descriptor.getDescriptor(interface.identifier.name)
  10864            if descriptor.hasDefaultToJSON:
  10865                jsonDescriptors.append(descriptor)
  10866            interface = interface.parent
  10867 
  10868        # Iterate the array in reverse: oldest ancestor first
  10869        for descriptor in jsonDescriptors[::-1]:
  10870            ret += fill(
  10871                """
  10872                if (!${parentclass}::CollectJSONAttributes(cx, obj, MOZ_KnownLive(self), result)) {
  10873                  return false;
  10874                }
  10875                """,
  10876                parentclass=toBindingNamespace(descriptor.name),
  10877            )
  10878        ret += "args.rval().setObject(*result);\n" "return true;\n"
  10879        return ret
  10880 
  10881 
  10882 class CGLegacyCallHook(CGAbstractBindingMethod):
  10883    """
  10884    Call hook for our object
  10885    """
  10886 
  10887    def __init__(self, descriptor):
  10888        self._legacycaller = descriptor.operations["LegacyCaller"]
  10889        # Our "self" is actually the callee in this case, not the thisval.
  10890        CGAbstractBindingMethod.__init__(
  10891            self,
  10892            descriptor,
  10893            LEGACYCALLER_HOOK_NAME,
  10894            JSNativeArguments(),
  10895            getThisObj="&args.callee()",
  10896        )
  10897 
  10898    def define(self):
  10899        if not self._legacycaller:
  10900            return ""
  10901        return CGAbstractBindingMethod.define(self)
  10902 
  10903    def generate_code(self):
  10904        name = self._legacycaller.identifier.name
  10905        nativeName = MakeNativeName(self.descriptor.binaryNameFor(name, False))
  10906        return CGMethodCall(nativeName, False, self.descriptor, self._legacycaller)
  10907 
  10908    def error_reporting_label(self):
  10909        # Should act like methods.
  10910        return CGSpecializedMethod.error_reporting_label_helper(
  10911            self.descriptor, self._legacycaller, isConstructor=False
  10912        )
  10913 
  10914 
  10915 class CGResolveHook(CGAbstractClassHook):
  10916    """
  10917    Resolve hook for objects that have the NeedResolve extended attribute.
  10918    """
  10919 
  10920    def __init__(self, descriptor):
  10921        assert descriptor.interface.getExtendedAttribute("NeedResolve")
  10922 
  10923        args = [
  10924            Argument("JSContext*", "cx"),
  10925            Argument("JS::Handle<JSObject*>", "obj"),
  10926            Argument("JS::Handle<jsid>", "id"),
  10927            Argument("bool*", "resolvedp"),
  10928        ]
  10929        CGAbstractClassHook.__init__(self, descriptor, RESOLVE_HOOK_NAME, "bool", args)
  10930 
  10931    def generate_code(self):
  10932        return dedent(
  10933            """
  10934            JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc(cx);
  10935            if (!self->DoResolve(cx, obj, id, &desc)) {
  10936              return false;
  10937            }
  10938            if (desc.isNothing()) {
  10939              return true;
  10940            }
  10941            // If desc.value() is undefined, then the DoResolve call
  10942            // has already defined it on the object.  Don't try to also
  10943            // define it.
  10944            MOZ_ASSERT(desc->isDataDescriptor());
  10945            if (!desc->value().isUndefined()) {
  10946              JS::Rooted<JS::PropertyDescriptor> defineDesc(cx, *desc);
  10947              defineDesc.setResolving(true);
  10948              if (!JS_DefinePropertyById(cx, obj, id, defineDesc)) {
  10949                return false;
  10950              }
  10951            }
  10952            *resolvedp = true;
  10953            return true;
  10954            """
  10955        )
  10956 
  10957    def definition_body(self):
  10958        if self.descriptor.isGlobal():
  10959            # Resolve standard classes
  10960            prefix = dedent(
  10961                """
  10962                if (!ResolveGlobal(cx, obj, id, resolvedp)) {
  10963                  return false;
  10964                }
  10965                if (*resolvedp) {
  10966                  return true;
  10967                }
  10968 
  10969                """
  10970            )
  10971        else:
  10972            prefix = ""
  10973        return prefix + CGAbstractClassHook.definition_body(self)
  10974 
  10975 
  10976 class CGMayResolveHook(CGAbstractStaticMethod):
  10977    """
  10978    Resolve hook for objects that have the NeedResolve extended attribute.
  10979    """
  10980 
  10981    def __init__(self, descriptor):
  10982        assert descriptor.interface.getExtendedAttribute("NeedResolve")
  10983 
  10984        args = [
  10985            Argument("const JSAtomState&", "names"),
  10986            Argument("jsid", "id"),
  10987            Argument("JSObject*", "maybeObj"),
  10988        ]
  10989        CGAbstractStaticMethod.__init__(
  10990            self, descriptor, MAY_RESOLVE_HOOK_NAME, "bool", args
  10991        )
  10992 
  10993    def definition_body(self):
  10994        if self.descriptor.isGlobal():
  10995            # Check whether this would resolve as a standard class.
  10996            prefix = dedent(
  10997                """
  10998                if (MayResolveGlobal(names, id, maybeObj)) {
  10999                  return true;
  11000                }
  11001 
  11002                """
  11003            )
  11004        else:
  11005            prefix = ""
  11006        return prefix + "return %s::MayResolve(id);\n" % self.descriptor.nativeType
  11007 
  11008 
  11009 class CGEnumerateHook(CGAbstractBindingMethod):
  11010    """
  11011    Enumerate hook for objects with custom hooks.
  11012    """
  11013 
  11014    def __init__(self, descriptor):
  11015        assert descriptor.interface.getExtendedAttribute("NeedResolve")
  11016 
  11017        args = [
  11018            Argument("JSContext*", "cx"),
  11019            Argument("JS::Handle<JSObject*>", "obj"),
  11020            Argument("JS::MutableHandleVector<jsid>", "properties"),
  11021            Argument("bool", "enumerableOnly"),
  11022        ]
  11023        # Our "self" is actually the "obj" argument in this case, not the thisval.
  11024        CGAbstractBindingMethod.__init__(
  11025            self, descriptor, NEW_ENUMERATE_HOOK_NAME, args, getThisObj="", callArgs=""
  11026        )
  11027 
  11028    def generate_code(self):
  11029        return CGGeneric(
  11030            dedent(
  11031                """
  11032            FastErrorResult rv;
  11033            self->GetOwnPropertyNames(cx, properties, enumerableOnly, rv);
  11034            if (rv.MaybeSetPendingException(cx)) {
  11035              return false;
  11036            }
  11037            return true;
  11038            """
  11039            )
  11040        )
  11041 
  11042    def definition_body(self):
  11043        if self.descriptor.isGlobal():
  11044            # Enumerate standard classes
  11045            prefix = dedent(
  11046                """
  11047                if (!EnumerateGlobal(cx, obj, properties, enumerableOnly)) {
  11048                  return false;
  11049                }
  11050 
  11051                """
  11052            )
  11053        else:
  11054            prefix = ""
  11055        return prefix + CGAbstractBindingMethod.definition_body(self)
  11056 
  11057 
  11058 class CppKeywords:
  11059    """
  11060    A class for checking if method names declared in webidl
  11061    are not in conflict with C++ keywords.
  11062    """
  11063 
  11064    keywords = frozenset(
  11065        [
  11066            "alignas",
  11067            "alignof",
  11068            "and",
  11069            "and_eq",
  11070            "asm",
  11071            "assert",
  11072            "auto",
  11073            "bitand",
  11074            "bitor",
  11075            "bool",
  11076            "break",
  11077            "case",
  11078            "catch",
  11079            "char",
  11080            "char16_t",
  11081            "char32_t",
  11082            "class",
  11083            "compl",
  11084            "const",
  11085            "constexpr",
  11086            "const_cast",
  11087            "continue",
  11088            "decltype",
  11089            "default",
  11090            "delete",
  11091            "do",
  11092            "double",
  11093            "dynamic_cast",
  11094            "else",
  11095            "enum",
  11096            "explicit",
  11097            "export",
  11098            "extern",
  11099            "false",
  11100            "final",
  11101            "float",
  11102            "for",
  11103            "friend",
  11104            "goto",
  11105            "if",
  11106            "inline",
  11107            "int",
  11108            "long",
  11109            "mutable",
  11110            "namespace",
  11111            "new",
  11112            "noexcept",
  11113            "not",
  11114            "not_eq",
  11115            "nullptr",
  11116            "operator",
  11117            "or",
  11118            "or_eq",
  11119            "override",
  11120            "private",
  11121            "protected",
  11122            "public",
  11123            "register",
  11124            "reinterpret_cast",
  11125            "return",
  11126            "short",
  11127            "signed",
  11128            "sizeof",
  11129            "static",
  11130            "static_assert",
  11131            "static_cast",
  11132            "struct",
  11133            "switch",
  11134            "template",
  11135            "this",
  11136            "thread_local",
  11137            "throw",
  11138            "true",
  11139            "try",
  11140            "typedef",
  11141            "typeid",
  11142            "typename",
  11143            "union",
  11144            "unsigned",
  11145            "using",
  11146            "virtual",
  11147            "void",
  11148            "volatile",
  11149            "wchar_t",
  11150            "while",
  11151            "xor",
  11152            "xor_eq",
  11153        ]
  11154    )
  11155 
  11156    @staticmethod
  11157    def checkMethodName(name):
  11158        # Double '_' because 'assert' and '_assert' cannot be used in MS2013 compiler.
  11159        # Bug 964892 and bug 963560.
  11160        if name in CppKeywords.keywords:
  11161            name = "_" + name + "_"
  11162        return name
  11163 
  11164 
  11165 class CGStaticMethod(CGAbstractStaticBindingMethod):
  11166    """
  11167    A class for generating the C++ code for an IDL static method.
  11168    """
  11169 
  11170    def __init__(self, descriptor, method):
  11171        self.method = method
  11172        name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
  11173        CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
  11174 
  11175    def generate_code(self):
  11176        nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method)
  11177        return CGMethodCall(nativeName, True, self.descriptor, self.method)
  11178 
  11179    def auto_profiler_label(self):
  11180        interface_name = self.descriptor.interface.identifier.name
  11181        method_name = self.method.identifier.name
  11182        return fill(
  11183            """
  11184            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
  11185              "${interface_name}", "${method_name}", DOM, cx,
  11186              uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD) |
  11187              uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
  11188            """,
  11189            interface_name=interface_name,
  11190            method_name=method_name,
  11191        )
  11192 
  11193    def error_reporting_label(self):
  11194        return CGSpecializedMethod.error_reporting_label_helper(
  11195            self.descriptor, self.method, isConstructor=False
  11196        )
  11197 
  11198 
  11199 class CGSpecializedGetterCommon(CGAbstractStaticMethod):
  11200    """
  11201    A class for generating the code for a specialized attribute getter
  11202    that the JIT can call with lower overhead.
  11203    """
  11204 
  11205    def __init__(
  11206        self,
  11207        descriptor,
  11208        name,
  11209        nativeName,
  11210        attr,
  11211        args,
  11212        errorReportingLabel=None,
  11213        additionalArg=None,
  11214    ):
  11215        self.nativeName = nativeName
  11216        self.errorReportingLabel = errorReportingLabel
  11217        self.additionalArgs = [] if additionalArg is None else [additionalArg]
  11218        # StoreInSlot attributes have their getters called from Wrap().  We
  11219        # really hope they can't run script, and don't want to annotate Wrap()
  11220        # methods as doing that anyway, so let's not annotate them as
  11221        # MOZ_CAN_RUN_SCRIPT.
  11222        CGAbstractStaticMethod.__init__(
  11223            self,
  11224            descriptor,
  11225            name,
  11226            "bool",
  11227            args + self.additionalArgs,
  11228            canRunScript=not attr.getExtendedAttribute("StoreInSlot"),
  11229        )
  11230 
  11231    def definition_body(self):
  11232        prefix = fill(
  11233            """
  11234            auto* self = static_cast<${nativeType}*>(void_self);
  11235            """,
  11236            nativeType=self.descriptor.nativeType,
  11237        )
  11238 
  11239        if self.attr.isMaplikeOrSetlikeAttr():
  11240            assert not self.attr.getExtendedAttribute("CrossOriginReadable")
  11241            # If the interface is maplike/setlike, there will be one getter
  11242            # method for the size property of the backing object. Due to having
  11243            # to unpack the backing object from the slot, this requires its own
  11244            # generator.
  11245            return prefix + getMaplikeOrSetlikeSizeGetterBody(
  11246                self.descriptor, self.attr
  11247            )
  11248 
  11249        if self.attr.type.isObservableArray():
  11250            assert not self.attr.getExtendedAttribute("CrossOriginReadable")
  11251            # If the attribute is observableArray, due to having to unpack the
  11252            # backing object from the slot, this requires its own generator.
  11253            return prefix + getObservableArrayGetterBody(self.descriptor, self.attr)
  11254 
  11255        if self.nativeName is None:
  11256            nativeName = CGSpecializedGetterCommon.makeNativeName(
  11257                self.descriptor, self.attr
  11258            )
  11259        else:
  11260            nativeName = self.nativeName
  11261 
  11262        type = self.attr.type
  11263        if self.attr.getExtendedAttribute("CrossOriginReadable"):
  11264            remoteType = type
  11265            extendedAttributes = self.descriptor.getExtendedAttributes(
  11266                self.attr, getter=True
  11267            )
  11268            if (
  11269                remoteType.isGeckoInterface()
  11270                and not remoteType.unroll().inner.isExternal()
  11271                and remoteType.unroll().inner.getExtendedAttribute("ChromeOnly") is None
  11272            ):
  11273                # We'll use a JSObject. It might make more sense to use remoteType's
  11274                # RemoteProxy, but it's not easy to construct a type for that from here.
  11275                remoteType = BuiltinTypes[IDLBuiltinType.Types.object]
  11276                if "needsErrorResult" not in extendedAttributes:
  11277                    extendedAttributes.append("needsErrorResult")
  11278            prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
  11279            prefix = (
  11280                fill(
  11281                    """
  11282                if (IsRemoteObjectProxy(obj, ${prototypeID})) {
  11283                    ${nativeType}::RemoteProxy* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
  11284                    $*{call}
  11285                }
  11286            """,
  11287                    prototypeID=prototypeID,
  11288                    nativeType=self.descriptor.nativeType,
  11289                    call=CGGetterCall(
  11290                        remoteType,
  11291                        nativeName,
  11292                        self.descriptor,
  11293                        self.attr,
  11294                        self.errorReportingLabel,
  11295                        argsPre=[a.name for a in self.additionalArgs],
  11296                        dontSetSlot=True,
  11297                        extendedAttributes=extendedAttributes,
  11298                    ).define(),
  11299                )
  11300                + prefix
  11301            )
  11302 
  11303        argsPre = [a.name for a in self.additionalArgs]
  11304        maybeReturnCachedVal = None
  11305        if self.attr.slotIndices is not None:
  11306            # We're going to store this return value in a slot on some object,
  11307            # to cache it.  The question is, which object?  For dictionary and
  11308            # sequence return values, we want to use a slot on the Xray expando
  11309            # if we're called via Xrays, and a slot on our reflector otherwise.
  11310            # On the other hand, when dealing with some interface types
  11311            # (e.g. window.document) we want to avoid calling the getter more
  11312            # than once.  In the case of window.document, it's because the
  11313            # getter can start returning null, which would get hidden in the
  11314            # non-Xray case by the fact that it's [StoreOnSlot], so the cached
  11315            # version is always around.
  11316            #
  11317            # The upshot is that we use the reflector slot for any getter whose
  11318            # type is a gecko interface, whether we're called via Xrays or not.
  11319            # Since [Cached] and [StoreInSlot] cannot be used with "NewObject",
  11320            # we know that in the interface type case the returned object is
  11321            # wrappercached.  So creating Xrays to it is reasonable.
  11322            if mayUseXrayExpandoSlots(self.descriptor, self.attr):
  11323                prefix += dedent(
  11324                    """
  11325                    // Have to either root across the getter call or reget after.
  11326                    bool isXray;
  11327                    JS::Rooted<JSObject*> slotStorage(cx, GetCachedSlotStorageObject(cx, obj, &isXray));
  11328                    if (!slotStorage) {
  11329                      return false;
  11330                    }
  11331                    """
  11332                )
  11333            else:
  11334                prefix += dedent(
  11335                    """
  11336                    // Have to either root across the getter call or reget after.
  11337                    JS::Rooted<JSObject*> slotStorage(cx, js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false));
  11338                    MOZ_ASSERT(IsDOMObject(slotStorage));
  11339                    """
  11340                )
  11341 
  11342            if self.attr.getExtendedAttribute(
  11343                "ReflectedHTMLAttributeReturningFrozenArray"
  11344            ):
  11345                argsPre.append("hasCachedValue ? &useCachedValue : nullptr")
  11346                isXray = (
  11347                    "isXray"
  11348                    if mayUseXrayExpandoSlots(self.descriptor, self.attr)
  11349                    else "false"
  11350                )
  11351                prefix += fill(
  11352                    """
  11353                    auto& array = ReflectedHTMLAttributeSlots::GetOrCreate(slotStorage, ${isXray});
  11354                    JS::Rooted<JS::Value> cachedVal(cx, array[${arrayIndex}]);
  11355                    bool hasCachedValue = !cachedVal.isUndefined();
  11356                    bool useCachedValue = false;
  11357                    """,
  11358                    isXray=isXray,
  11359                    arrayIndex=reflectedHTMLAttributesArrayIndex(
  11360                        self.descriptor, self.attr
  11361                    ),
  11362                )
  11363                maybeReturnCachedVal = fill(
  11364                    """
  11365                    MOZ_ASSERT_IF(useCachedValue, hasCachedValue);
  11366                    if (hasCachedValue && useCachedValue) {
  11367                      args.rval().set(cachedVal);
  11368                      // The cached value is in the compartment of slotStorage,
  11369                      // so wrap into the caller compartment as needed.
  11370                      return ${maybeWrap}(cx, args.rval());
  11371                    }
  11372 
  11373                    ${clearCachedValue}(self);
  11374 
  11375                    """,
  11376                    maybeWrap=getMaybeWrapValueFuncForType(self.attr.type),
  11377                    clearCachedValue=MakeClearCachedValueNativeName(self.attr),
  11378                )
  11379            else:
  11380                if mayUseXrayExpandoSlots(self.descriptor, self.attr):
  11381                    prefix += fill(
  11382                        """
  11383                        const size_t slotIndex = isXray ? ${xraySlotIndex} : ${slotIndex};
  11384                        """,
  11385                        xraySlotIndex=memberXrayExpandoReservedSlot(
  11386                            self.attr, self.descriptor
  11387                        ),
  11388                        slotIndex=memberReservedSlot(self.attr, self.descriptor),
  11389                    )
  11390                else:
  11391                    prefix += fill(
  11392                        """
  11393                        const size_t slotIndex = ${slotIndex};
  11394                        """,
  11395                        slotIndex=memberReservedSlot(self.attr, self.descriptor),
  11396                    )
  11397                prefix += fill(
  11398                    """
  11399                    MOZ_ASSERT(slotIndex < JSCLASS_RESERVED_SLOTS(JS::GetClass(slotStorage)));
  11400                    {
  11401                      // Scope for cachedVal
  11402                      JS::Value cachedVal = JS::GetReservedSlot(slotStorage, slotIndex);
  11403                      if (!cachedVal.isUndefined()) {
  11404                        args.rval().set(cachedVal);
  11405                        // The cached value is in the compartment of slotStorage,
  11406                        // so wrap into the caller compartment as needed.
  11407                        return ${maybeWrap}(cx, args.rval());
  11408                      }
  11409                    }
  11410 
  11411                    """,
  11412                    maybeWrap=getMaybeWrapValueFuncForType(self.attr.type),
  11413                )
  11414 
  11415        return (
  11416            prefix
  11417            + CGGetterCall(
  11418                type,
  11419                nativeName,
  11420                self.descriptor,
  11421                self.attr,
  11422                self.errorReportingLabel,
  11423                argsPre=argsPre,
  11424                preConversionCode=maybeReturnCachedVal,
  11425            ).define()
  11426        )
  11427 
  11428    def auto_profiler_label(self, profilerLabel=None):
  11429        if profilerLabel is None:
  11430            profilerLabel = '"' + self.attr.identifier.name + '"'
  11431        interface_name = self.descriptor.interface.identifier.name
  11432        return fill(
  11433            """
  11434            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
  11435              "${interface_name}", ${attr_name}, DOM, cx,
  11436              uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) |
  11437              uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
  11438            """,
  11439            interface_name=interface_name,
  11440            attr_name=profilerLabel,
  11441        )
  11442 
  11443    def error_reporting_label(self):
  11444        # Getters never need a BindingCallContext.
  11445        return None
  11446 
  11447    @staticmethod
  11448    def makeNativeName(descriptor, attr):
  11449        name = attr.identifier.name
  11450        nativeName = MakeNativeName(descriptor.binaryNameFor(name, attr.isStatic()))
  11451        _, resultOutParam, _, _, _ = getRetvalDeclarationForType(attr.type, descriptor)
  11452        extendedAttrs = descriptor.getExtendedAttributes(attr, getter=True)
  11453        canFail = "needsErrorResult" in extendedAttrs or "canOOM" in extendedAttrs
  11454        if resultOutParam or attr.type.nullable() or canFail:
  11455            nativeName = "Get" + nativeName
  11456        return nativeName
  11457 
  11458 
  11459 class CGSpecializedGetter(CGSpecializedGetterCommon):
  11460    """
  11461    A class for generating the code for a specialized attribute getter
  11462    that the JIT can call with lower overhead.
  11463    """
  11464 
  11465    def __init__(self, descriptor, attr):
  11466        self.attr = attr
  11467        name = "get_" + IDLToCIdentifier(attr.identifier.name)
  11468        args = [
  11469            Argument("JSContext*", "cx"),
  11470            Argument("JS::Handle<JSObject*>", "obj"),
  11471            Argument("void*", "void_self"),
  11472            Argument("JSJitGetterCallArgs", "args"),
  11473        ]
  11474        CGSpecializedGetterCommon.__init__(self, descriptor, name, None, attr, args)
  11475 
  11476 
  11477 class CGTemplateForSpecializedGetter(CGSpecializedGetterCommon):
  11478    """
  11479    A class for generating the code for a specialized attribute getter
  11480    that can be used as the common getter that templated attribute
  11481    getters can forward to.
  11482    """
  11483 
  11484    def __init__(self, descriptor, template):
  11485        self.attr = template.attr
  11486        self.attrNameString = template.attrNameString
  11487        args = [
  11488            Argument("JSContext*", "cx"),
  11489            Argument("JS::Handle<JSObject*>", "obj"),
  11490            Argument("void*", "void_self"),
  11491            Argument("JSJitGetterCallArgs", "args"),
  11492        ]
  11493        errorDescription = (
  11494            'ErrorDescriptionFor<ErrorFor::getter>{ "%s", attrName }'
  11495            % descriptor.interface.identifier.name
  11496        )
  11497        CGSpecializedGetterCommon.__init__(
  11498            self,
  11499            descriptor,
  11500            template.getter,
  11501            template.getter,
  11502            self.attr,
  11503            args,
  11504            errorReportingLabel=errorDescription,
  11505            additionalArg=Argument(template.argument.type, template.argument.name),
  11506        )
  11507 
  11508    def auto_profiler_label(self):
  11509        return (
  11510            fill(
  11511                """
  11512                const char* attrName = ${attrNameString};
  11513                """,
  11514                attrNameString=self.attrNameString,
  11515            )
  11516            + CGSpecializedGetterCommon.auto_profiler_label(self, "attrName")
  11517        )
  11518 
  11519 
  11520 class CGSpecializedTemplatedGetter(CGAbstractStaticMethod):
  11521    """
  11522    A class for generating the code for a specialized templated attribute
  11523    getter that forwards to a common template getter.
  11524    """
  11525 
  11526    def __init__(self, descriptor, attr, template, additionalArg):
  11527        self.attr = attr
  11528        self.template = template
  11529        self.additionalArg = additionalArg
  11530        name = "get_" + IDLToCIdentifier(attr.identifier.name)
  11531        args = [
  11532            Argument("JSContext*", "cx"),
  11533            Argument("JS::Handle<JSObject*>", "obj"),
  11534            Argument("void*", "void_self"),
  11535            Argument("JSJitGetterCallArgs", "args"),
  11536        ]
  11537        assert not attr.getExtendedAttribute("StoreInSlot")
  11538        CGAbstractStaticMethod.__init__(
  11539            self,
  11540            descriptor,
  11541            name,
  11542            "bool",
  11543            args,
  11544            canRunScript=True,
  11545        )
  11546 
  11547    def definition_body(self):
  11548        if self.additionalArg is None:
  11549            additionalArg = self.attr.identifier.name
  11550        else:
  11551            additionalArg = self.additionalArg
  11552 
  11553        return fill(
  11554            """
  11555            return ${namespace}::${getter}(cx, obj, void_self, args, ${additionalArg});
  11556            """,
  11557            namespace=toBindingNamespace(
  11558                self.template.descriptor.interface.identifier.name
  11559            ),
  11560            getter=self.template.getter,
  11561            additionalArg=additionalArg,
  11562        )
  11563 
  11564 
  11565 class CGGetterPromiseWrapper(CGAbstractStaticMethod):
  11566    """
  11567    A class for generating a wrapper around another getter that will
  11568    convert exceptions to promises.
  11569    """
  11570 
  11571    def __init__(self, descriptor, getterToWrap):
  11572        self.getter = getterToWrap
  11573        name = self.makeName(getterToWrap.name)
  11574        args = list(getterToWrap.args)
  11575        CGAbstractStaticMethod.__init__(
  11576            self, descriptor, name, "bool", args, canRunScript=True
  11577        )
  11578 
  11579    def definition_body(self):
  11580        return fill(
  11581            """
  11582            bool ok = ${getterName}(${args});
  11583            if (ok) {
  11584              return true;
  11585            }
  11586            return ConvertExceptionToPromise(cx, args.rval());
  11587            """,
  11588            getterName=self.getter.name,
  11589            args=", ".join(arg.name for arg in self.args),
  11590        )
  11591 
  11592    @staticmethod
  11593    def makeName(getterName):
  11594        return getterName + "_promiseWrapper"
  11595 
  11596 
  11597 class CGStaticGetter(CGAbstractStaticBindingMethod):
  11598    """
  11599    A class for generating the C++ code for an IDL static attribute getter.
  11600    """
  11601 
  11602    def __init__(self, descriptor, attr):
  11603        self.attr = attr
  11604        name = "get_" + IDLToCIdentifier(attr.identifier.name)
  11605        CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
  11606 
  11607    def generate_code(self):
  11608        nativeName = CGSpecializedGetterCommon.makeNativeName(
  11609            self.descriptor, self.attr
  11610        )
  11611        return CGGetterCall(self.attr.type, nativeName, self.descriptor, self.attr)
  11612 
  11613    def auto_profiler_label(self):
  11614        interface_name = self.descriptor.interface.identifier.name
  11615        attr_name = self.attr.identifier.name
  11616        return fill(
  11617            """
  11618            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
  11619              "${interface_name}", "${attr_name}", DOM, cx,
  11620              uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) |
  11621              uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
  11622            """,
  11623            interface_name=interface_name,
  11624            attr_name=attr_name,
  11625        )
  11626 
  11627    def error_reporting_label(self):
  11628        # Getters never need a BindingCallContext.
  11629        return None
  11630 
  11631 
  11632 class CGSpecializedSetterCommon(CGAbstractStaticMethod):
  11633    """
  11634    A class for generating the code for a specialized attribute setter
  11635    that the JIT can call with lower overhead.
  11636    """
  11637 
  11638    def __init__(
  11639        self,
  11640        descriptor,
  11641        name,
  11642        nativeName,
  11643        attr,
  11644        args,
  11645        errorReportingLabel=None,
  11646        additionalArg=None,
  11647    ):
  11648        self.nativeName = nativeName
  11649        self.errorReportingLabel = errorReportingLabel
  11650        self.additionalArgs = [] if additionalArg is None else [additionalArg]
  11651        CGAbstractStaticMethod.__init__(
  11652            self,
  11653            descriptor,
  11654            name,
  11655            "bool",
  11656            args + self.additionalArgs,
  11657            canRunScript=True,
  11658        )
  11659 
  11660    def definition_body(self):
  11661        type = self.attr.type
  11662        call = CGSetterCall(
  11663            type,
  11664            self.nativeName,
  11665            self.descriptor,
  11666            self.attr,
  11667            self.errorReportingLabel,
  11668            [a.name for a in self.additionalArgs],
  11669        ).define()
  11670        prefix = ""
  11671        if self.attr.getExtendedAttribute("CrossOriginWritable"):
  11672            if type.isGeckoInterface() and not type.unroll().inner.isExternal():
  11673                # a setter taking a Gecko interface would require us to deal with remote
  11674                # proxies for the value here.
  11675                raise TypeError(
  11676                    "We don't support the setter of %s marked as "
  11677                    "CrossOriginWritable because it takes a Gecko interface "
  11678                    "as the value",
  11679                    self.attr.identifier.name,
  11680                )
  11681            prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
  11682            prefix = fill(
  11683                """
  11684                if (IsRemoteObjectProxy(obj, ${prototypeID})) {
  11685                    auto* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
  11686                    $*{call}
  11687                }
  11688                """,
  11689                prototypeID=prototypeID,
  11690                nativeType=self.descriptor.nativeType,
  11691                call=call,
  11692            )
  11693 
  11694        return prefix + fill(
  11695            """
  11696            auto* self = static_cast<${nativeType}*>(void_self);
  11697            $*{call}
  11698            """,
  11699            nativeType=self.descriptor.nativeType,
  11700            call=call,
  11701        )
  11702 
  11703    def auto_profiler_label(self, profilerLabel=None):
  11704        interface_name = self.descriptor.interface.identifier.name
  11705        if profilerLabel is None:
  11706            profilerLabel = '"' + self.attr.identifier.name + '"'
  11707        return fill(
  11708            """
  11709            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
  11710              "${interface_name}", ${attr_name}, DOM, cx,
  11711              uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER) |
  11712              uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
  11713            """,
  11714            interface_name=interface_name,
  11715            attr_name=profilerLabel,
  11716        )
  11717 
  11718    @staticmethod
  11719    def error_reporting_label_helper(descriptor, attr):
  11720        # Setters need a BindingCallContext if the type of the attribute needs
  11721        # one.
  11722        if not idlTypeNeedsCallContext(
  11723            attr.type, descriptor, allowTreatNonCallableAsNull=True
  11724        ):
  11725            return None
  11726        return '"%s"' % (
  11727            GetLabelForErrorReporting(descriptor, attr, isConstructor=False) + " setter"
  11728        )
  11729 
  11730    def error_reporting_label(self):
  11731        errorReportingLabel = CGSpecializedSetterCommon.error_reporting_label_helper(
  11732            self.descriptor, self.attr
  11733        )
  11734        if errorReportingLabel is None:
  11735            return None
  11736        if self.errorReportingLabel:
  11737            return self.errorReportingLabel
  11738        return errorReportingLabel
  11739 
  11740    @staticmethod
  11741    def makeNativeName(descriptor, attr):
  11742        name = attr.identifier.name
  11743        return "Set" + MakeNativeName(descriptor.binaryNameFor(name, attr.isStatic()))
  11744 
  11745 
  11746 class CGSpecializedSetter(CGSpecializedSetterCommon):
  11747    """
  11748    A class for generating the code for a specialized attribute setter
  11749    that the JIT can call with lower overhead.
  11750    """
  11751 
  11752    def __init__(self, descriptor, attr):
  11753        self.attr = attr
  11754        name = "set_" + IDLToCIdentifier(attr.identifier.name)
  11755        args = [
  11756            Argument("JSContext*", "cx"),
  11757            Argument("JS::Handle<JSObject*>", "obj"),
  11758            Argument("void*", "void_self"),
  11759            Argument("JSJitSetterCallArgs", "args"),
  11760        ]
  11761        CGSpecializedSetterCommon.__init__(
  11762            self,
  11763            descriptor,
  11764            name,
  11765            CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
  11766            attr,
  11767            args,
  11768        )
  11769 
  11770 
  11771 class CGTemplateForSpecializedSetter(CGSpecializedSetterCommon):
  11772    """
  11773    A class for generating the code for a specialized attribute setter
  11774    that can be used as the common setter that templated attribute
  11775    setters can forward to.
  11776    """
  11777 
  11778    def __init__(self, descriptor, template):
  11779        self.attr = template.attr
  11780        self.attrNameString = template.attrNameString
  11781        args = [
  11782            Argument("JSContext*", "cx"),
  11783            Argument("JS::Handle<JSObject*>", "obj"),
  11784            Argument("void*", "void_self"),
  11785            Argument("JSJitSetterCallArgs", "args"),
  11786        ]
  11787        errorDescription = (
  11788            'ErrorDescriptionFor<ErrorFor::setter>{ "%s", attrName }'
  11789            % descriptor.interface.identifier.name
  11790        )
  11791        CGSpecializedSetterCommon.__init__(
  11792            self,
  11793            descriptor,
  11794            template.setter,
  11795            template.setter,
  11796            self.attr,
  11797            args,
  11798            errorReportingLabel=errorDescription,
  11799            additionalArg=Argument(template.argument.type, template.argument.name),
  11800        )
  11801 
  11802    def auto_profiler_label(self):
  11803        return (
  11804            fill(
  11805                """
  11806                const char* attrName = ${attrNameString};
  11807                """,
  11808                attrNameString=self.attrNameString,
  11809            )
  11810            + CGSpecializedSetterCommon.auto_profiler_label(self, "attrName")
  11811        )
  11812 
  11813 
  11814 class CGSpecializedTemplatedSetter(CGAbstractStaticMethod):
  11815    """
  11816    A class for generating the code for a specialized templated attribute
  11817    setter that forwards to a common template setter.
  11818    """
  11819 
  11820    def __init__(self, descriptor, attr, template, additionalArg):
  11821        self.attr = attr
  11822        self.template = template
  11823        self.additionalArg = additionalArg
  11824        name = "set_" + IDLToCIdentifier(attr.identifier.name)
  11825        args = [
  11826            Argument("JSContext*", "cx"),
  11827            Argument("JS::Handle<JSObject*>", "obj"),
  11828            Argument("void*", "void_self"),
  11829            Argument("JSJitSetterCallArgs", "args"),
  11830        ]
  11831        CGAbstractStaticMethod.__init__(
  11832            self, descriptor, name, "bool", args, canRunScript=True
  11833        )
  11834 
  11835    def definition_body(self):
  11836        additionalArgs = []
  11837        if self.additionalArg is None:
  11838            additionalArgs.append(self.attr.identifier.name)
  11839        else:
  11840            additionalArgs.append(self.additionalArg)
  11841 
  11842        return fill(
  11843            """
  11844            return ${namespace}::${setter}(cx, obj, void_self, args, ${additionalArgs});
  11845            """,
  11846            namespace=toBindingNamespace(
  11847                self.template.descriptor.interface.identifier.name
  11848            ),
  11849            setter=self.template.setter,
  11850            additionalArgs=", ".join(additionalArgs),
  11851        )
  11852 
  11853 
  11854 class CGStaticSetter(CGAbstractStaticBindingMethod):
  11855    """
  11856    A class for generating the C++ code for an IDL static attribute setter.
  11857    """
  11858 
  11859    def __init__(self, descriptor, attr):
  11860        self.attr = attr
  11861        name = "set_" + IDLToCIdentifier(attr.identifier.name)
  11862        CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
  11863 
  11864    def generate_code(self):
  11865        nativeName = CGSpecializedSetterCommon.makeNativeName(
  11866            self.descriptor, self.attr
  11867        )
  11868        checkForArg = CGGeneric(
  11869            fill(
  11870                """
  11871            if (!args.requireAtLeast(cx, "${name} setter", 1)) {
  11872              return false;
  11873            }
  11874            """,
  11875                name=self.attr.identifier.name,
  11876            )
  11877        )
  11878        call = CGSetterCall(self.attr.type, nativeName, self.descriptor, self.attr)
  11879        return CGList([checkForArg, call])
  11880 
  11881    def auto_profiler_label(self):
  11882        interface_name = self.descriptor.interface.identifier.name
  11883        attr_name = self.attr.identifier.name
  11884        return fill(
  11885            """
  11886            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
  11887              "${interface_name}", "${attr_name}", DOM, cx,
  11888              uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER) |
  11889              uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
  11890            """,
  11891            interface_name=interface_name,
  11892            attr_name=attr_name,
  11893        )
  11894 
  11895    def error_reporting_label(self):
  11896        return CGSpecializedSetterCommon.error_reporting_label_helper(
  11897            self.descriptor, self.attr
  11898        )
  11899 
  11900 
  11901 class CGSpecializedForwardingSetter(CGSpecializedSetter):
  11902    """
  11903    A class for generating the code for a specialized attribute setter with
  11904    PutForwards that the JIT can call with lower overhead.
  11905    """
  11906 
  11907    def __init__(self, descriptor, attr):
  11908        CGSpecializedSetter.__init__(self, descriptor, attr)
  11909 
  11910    def definition_body(self):
  11911        attrName = self.attr.identifier.name
  11912        forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0]
  11913        # JS_GetProperty and JS_SetProperty can only deal with ASCII
  11914        assert all(ord(c) < 128 for c in attrName)
  11915        assert all(ord(c) < 128 for c in forwardToAttrName)
  11916        return fill(
  11917            """
  11918            JS::Rooted<JS::Value> v(cx);
  11919            if (!JS_GetProperty(cx, obj, "${attr}", &v)) {
  11920              return false;
  11921            }
  11922 
  11923            if (!v.isObject()) {
  11924              return cx.ThrowErrorMessage<MSG_NOT_OBJECT>("${interface}.${attr}");
  11925            }
  11926 
  11927            JS::Rooted<JSObject*> targetObj(cx, &v.toObject());
  11928            return JS_SetProperty(cx, targetObj, "${forwardToAttrName}", args[0]);
  11929            """,
  11930            attr=attrName,
  11931            interface=self.descriptor.interface.identifier.name,
  11932            forwardToAttrName=forwardToAttrName,
  11933        )
  11934 
  11935    def error_reporting_label(self):
  11936        # We always need to be able to throw.
  11937        return '"%s"' % (
  11938            GetLabelForErrorReporting(self.descriptor, self.attr, isConstructor=False)
  11939            + " setter"
  11940        )
  11941 
  11942 
  11943 class CGSpecializedReplaceableSetter(CGSpecializedSetter):
  11944    """
  11945    A class for generating the code for a specialized attribute setter with
  11946    Replaceable that the JIT can call with lower overhead.
  11947    """
  11948 
  11949    def __init__(self, descriptor, attr):
  11950        CGSpecializedSetter.__init__(self, descriptor, attr)
  11951 
  11952    def definition_body(self):
  11953        attrName = self.attr.identifier.name
  11954        # JS_DefineProperty can only deal with ASCII
  11955        assert all(ord(c) < 128 for c in attrName)
  11956        return (
  11957            'return JS_DefineProperty(cx, obj, "%s", args[0], JSPROP_ENUMERATE);\n'
  11958            % attrName
  11959        )
  11960 
  11961    def error_reporting_label(self):
  11962        # We never throw directly.
  11963        return None
  11964 
  11965 
  11966 class CGSpecializedLenientSetter(CGSpecializedSetter):
  11967    """
  11968    A class for generating the code for a specialized attribute setter with
  11969    LenientSetter that the JIT can call with lower overhead.
  11970    """
  11971 
  11972    def __init__(self, descriptor, attr):
  11973        CGSpecializedSetter.__init__(self, descriptor, attr)
  11974 
  11975    def definition_body(self):
  11976        attrName = self.attr.identifier.name
  11977        # JS_DefineProperty can only deal with ASCII
  11978        assert all(ord(c) < 128 for c in attrName)
  11979        return dedent(
  11980            """
  11981            DeprecationWarning(cx, obj, DeprecatedOperations::eLenientSetter);
  11982            return true;
  11983            """
  11984        )
  11985 
  11986    def error_reporting_label(self):
  11987        # We never throw; that's the whole point.
  11988        return None
  11989 
  11990 
  11991 def memberReturnsNewObject(member):
  11992    return member.getExtendedAttribute("NewObject") is not None
  11993 
  11994 
  11995 class CGMemberJITInfo(CGThing):
  11996    """
  11997    A class for generating the JITInfo for a property that points to
  11998    our specialized getter and setter.
  11999    """
  12000 
  12001    def __init__(self, descriptor, member):
  12002        self.member = member
  12003        self.descriptor = descriptor
  12004 
  12005    def declare(self):
  12006        return ""
  12007 
  12008    def defineJitInfo(
  12009        self,
  12010        infoName,
  12011        opName,
  12012        opType,
  12013        infallible,
  12014        movable,
  12015        eliminatable,
  12016        aliasSet,
  12017        alwaysInSlot,
  12018        lazilyInSlot,
  12019        slotIndex,
  12020        returnTypes,
  12021        args,
  12022    ):
  12023        """
  12024        aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit.
  12025 
  12026        args is None if we don't want to output argTypes for some
  12027        reason (e.g. we have overloads or we're not a method) and
  12028        otherwise an iterable of the arguments for this method.
  12029        """
  12030        assert (
  12031            not movable or aliasSet != "AliasEverything"
  12032        )  # Can't move write-aliasing things
  12033        assert (
  12034            not alwaysInSlot or movable
  12035        )  # Things always in slots had better be movable
  12036        assert (
  12037            not eliminatable or aliasSet != "AliasEverything"
  12038        )  # Can't eliminate write-aliasing things
  12039        assert (
  12040            not alwaysInSlot or eliminatable
  12041        )  # Things always in slots had better be eliminatable
  12042 
  12043        def jitInfoInitializer(isTypedMethod):
  12044            initializer = fill(
  12045                """
  12046                {
  12047                  { ${opName} },
  12048                  { prototypes::id::${name} },
  12049                  { PrototypeTraits<prototypes::id::${name}>::Depth },
  12050                  JSJitInfo::${opType},
  12051                  JSJitInfo::${aliasSet}, /* aliasSet.  Not relevant for setters. */
  12052                  ${returnType},  /* returnType.  Not relevant for setters. */
  12053                  ${isInfallible},  /* isInfallible. False in setters. */
  12054                  ${isMovable},  /* isMovable.  Not relevant for setters. */
  12055                  ${isEliminatable}, /* isEliminatable.  Not relevant for setters. */
  12056                  ${isAlwaysInSlot}, /* isAlwaysInSlot.  Only relevant for getters. */
  12057                  ${isLazilyCachedInSlot}, /* isLazilyCachedInSlot.  Only relevant for getters. */
  12058                  ${isTypedMethod},  /* isTypedMethod.  Only relevant for methods. */
  12059                  ${slotIndex}   /* Reserved slot index, if we're stored in a slot, else 0. */
  12060                }
  12061                """,
  12062                opName=opName,
  12063                name=self.descriptor.name,
  12064                opType=opType,
  12065                aliasSet=aliasSet,
  12066                returnType=functools.reduce(
  12067                    CGMemberJITInfo.getSingleReturnType, returnTypes, ""
  12068                ),
  12069                isInfallible=toStringBool(infallible),
  12070                isMovable=toStringBool(movable),
  12071                isEliminatable=toStringBool(eliminatable),
  12072                isAlwaysInSlot=toStringBool(alwaysInSlot),
  12073                isLazilyCachedInSlot=toStringBool(lazilyInSlot),
  12074                isTypedMethod=toStringBool(isTypedMethod),
  12075                slotIndex="0" if slotIndex is None else slotIndex,
  12076            )
  12077            return initializer.rstrip()
  12078 
  12079        if slotIndex is not None:
  12080            slotAssert = fill(
  12081                """
  12082                static_assert(${slotIndex} <= JSJitInfo::maxSlotIndex, "We won't fit");
  12083                static_assert(${slotIndex} < ${classReservedSlots}, "There is no slot for us");
  12084                """,
  12085                slotIndex=slotIndex,
  12086                classReservedSlots=INSTANCE_RESERVED_SLOTS
  12087                + self.descriptor.interface.totalMembersInSlots,
  12088            )
  12089        else:
  12090            slotAssert = ""
  12091        if args is not None:
  12092            argTypes = "%s_argTypes" % infoName
  12093            args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
  12094            args.append("JSJitInfo::ArgTypeListEnd")
  12095            argTypesDecl = "static const JSJitInfo::ArgType %s[] = { %s };\n" % (
  12096                argTypes,
  12097                ", ".join(args),
  12098            )
  12099            return fill(
  12100                """
  12101                $*{argTypesDecl}
  12102                static const JSTypedMethodJitInfo ${infoName} = {
  12103                ${jitInfo},
  12104                  ${argTypes}
  12105                };
  12106                $*{slotAssert}
  12107                """,
  12108                argTypesDecl=argTypesDecl,
  12109                infoName=infoName,
  12110                jitInfo=indent(jitInfoInitializer(True)),
  12111                argTypes=argTypes,
  12112                slotAssert=slotAssert,
  12113            )
  12114 
  12115        # Unexposed things are meant to be used from C++ directly, so we make
  12116        # their jitinfo non-static.  That way C++ can get at it.
  12117        if self.member.getExtendedAttribute("Unexposed"):
  12118            storageClass = "extern"
  12119        else:
  12120            storageClass = "static"
  12121 
  12122        return fill(
  12123            """
  12124            ${storageClass} const JSJitInfo ${infoName} = ${jitInfo};
  12125            $*{slotAssert}
  12126            """,
  12127            storageClass=storageClass,
  12128            infoName=infoName,
  12129            jitInfo=jitInfoInitializer(False),
  12130            slotAssert=slotAssert,
  12131        )
  12132 
  12133    def define(self):
  12134        if self.member.isAttr():
  12135            getterinfo = "%s_getterinfo" % IDLToCIdentifier(self.member.identifier.name)
  12136            name = IDLToCIdentifier(self.member.identifier.name)
  12137            if self.member.type.isPromise():
  12138                name = CGGetterPromiseWrapper.makeName(name)
  12139            getter = "get_%s" % name
  12140            extendedAttrs = self.descriptor.getExtendedAttributes(
  12141                self.member, getter=True
  12142            )
  12143            getterinfal = "needsErrorResult" not in extendedAttrs
  12144 
  12145            # At this point getterinfal is true if our getter either can't throw
  12146            # at all, or can only throw OOM.  In both cases, it's safe to move,
  12147            # or dead-code-eliminate, the getter, because throwing OOM is not
  12148            # semantically meaningful, so code can't rely on it happening.  Note
  12149            # that this makes the behavior consistent for OOM thrown from the
  12150            # getter itself and OOM thrown from the to-JS conversion of the
  12151            # return value (see the "canOOM" and "infallibleForMember" checks
  12152            # below).
  12153            movable = self.mayBeMovable() and getterinfal
  12154            eliminatable = self.mayBeEliminatable() and getterinfal
  12155            aliasSet = self.aliasSet()
  12156 
  12157            # Now we have to set getterinfal to whether we can _really_ ever
  12158            # throw, from the point of view of the JS engine.
  12159            getterinfal = (
  12160                getterinfal
  12161                and "canOOM" not in extendedAttrs
  12162                and infallibleForMember(self.member, self.member.type, self.descriptor)
  12163            )
  12164            isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot")
  12165 
  12166            if self.member.slotIndices is not None:
  12167                assert (
  12168                    isAlwaysInSlot
  12169                    or self.member.getExtendedAttribute("Cached")
  12170                    or self.member.getExtendedAttribute(
  12171                        "ReflectedHTMLAttributeReturningFrozenArray"
  12172                    )
  12173                    or self.member.type.isObservableArray()
  12174                )
  12175                isLazilyCachedInSlot = (
  12176                    not isAlwaysInSlot
  12177                    and not self.member.getExtendedAttribute(
  12178                        "ReflectedHTMLAttributeReturningFrozenArray"
  12179                    )
  12180                )
  12181                slotIndex = memberReservedSlot(self.member, self.descriptor)
  12182                # We'll statically assert that this is not too big in
  12183                # CGUpdateMemberSlotsMethod, in the case when
  12184                # isAlwaysInSlot is true.
  12185            else:
  12186                isLazilyCachedInSlot = False
  12187                slotIndex = None
  12188 
  12189            result = self.defineJitInfo(
  12190                getterinfo,
  12191                getter,
  12192                "Getter",
  12193                getterinfal,
  12194                movable,
  12195                eliminatable,
  12196                aliasSet,
  12197                isAlwaysInSlot,
  12198                isLazilyCachedInSlot,
  12199                slotIndex,
  12200                [self.member.type],
  12201                None,
  12202            )
  12203            if (
  12204                not self.member.readonly
  12205                or self.member.getExtendedAttribute("PutForwards") is not None
  12206                or self.member.getExtendedAttribute("Replaceable") is not None
  12207                or self.member.getExtendedAttribute("LegacyLenientSetter") is not None
  12208            ):
  12209                setterinfo = "%s_setterinfo" % IDLToCIdentifier(
  12210                    self.member.identifier.name
  12211                )
  12212                # Actually a JSJitSetterOp, but JSJitGetterOp is first in the
  12213                # union.
  12214                setter = "(JSJitGetterOp)set_%s" % IDLToCIdentifier(
  12215                    self.member.identifier.name
  12216                )
  12217                # Setters are always fallible, since they have to do a typed unwrap.
  12218                result += self.defineJitInfo(
  12219                    setterinfo,
  12220                    setter,
  12221                    "Setter",
  12222                    False,
  12223                    False,
  12224                    False,
  12225                    "AliasEverything",
  12226                    False,
  12227                    False,
  12228                    None,
  12229                    [BuiltinTypes[IDLBuiltinType.Types.undefined]],
  12230                    None,
  12231                )
  12232            return result
  12233        if self.member.isMethod():
  12234            methodinfo = "%s_methodinfo" % IDLToCIdentifier(self.member.identifier.name)
  12235            name = CppKeywords.checkMethodName(
  12236                IDLToCIdentifier(self.member.identifier.name)
  12237            )
  12238            if self.member.returnsPromise():
  12239                name = CGMethodPromiseWrapper.makeName(name)
  12240            # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union.
  12241            method = "(JSJitGetterOp)%s" % name
  12242 
  12243            # Methods are infallible if they are infallible, have no arguments
  12244            # to unwrap, and have a return type that's infallible to wrap up for
  12245            # return.
  12246            sigs = self.member.signatures()
  12247            if len(sigs) != 1:
  12248                # Don't handle overloading.  If there's more than one signature,
  12249                # one of them must take arguments.
  12250                methodInfal = False
  12251                args = None
  12252                movable = False
  12253                eliminatable = False
  12254            else:
  12255                sig = sigs[0]
  12256                # For methods that affect nothing, it's OK to set movable to our
  12257                # notion of infallible on the C++ side, without considering
  12258                # argument conversions, since argument conversions that can
  12259                # reliably throw would be effectful anyway and the jit doesn't
  12260                # move effectful things.
  12261                extendedAttrs = self.descriptor.getExtendedAttributes(self.member)
  12262                hasInfallibleImpl = "needsErrorResult" not in extendedAttrs
  12263                # At this point hasInfallibleImpl is true if our method either
  12264                # can't throw at all, or can only throw OOM.  In both cases, it
  12265                # may be safe to move, or dead-code-eliminate, the method,
  12266                # because throwing OOM is not semantically meaningful, so code
  12267                # can't rely on it happening.  Note that this makes the behavior
  12268                # consistent for OOM thrown from the method itself and OOM
  12269                # thrown from the to-JS conversion of the return value (see the
  12270                # "canOOM" and "infallibleForMember" checks below).
  12271                movable = self.mayBeMovable() and hasInfallibleImpl
  12272                eliminatable = self.mayBeEliminatable() and hasInfallibleImpl
  12273                # XXXbz can we move the smarts about fallibility due to arg
  12274                # conversions into the JIT, using our new args stuff?
  12275                if len(sig[1]) != 0 or not infallibleForMember(
  12276                    self.member, sig[0], self.descriptor
  12277                ):
  12278                    # We have arguments or our return-value boxing can fail
  12279                    methodInfal = False
  12280                else:
  12281                    methodInfal = hasInfallibleImpl and "canOOM" not in extendedAttrs
  12282                # For now, only bother to output args if we're side-effect-free.
  12283                if self.member.affects == "Nothing":
  12284                    args = sig[1]
  12285                else:
  12286                    args = None
  12287 
  12288            aliasSet = self.aliasSet()
  12289            result = self.defineJitInfo(
  12290                methodinfo,
  12291                method,
  12292                "Method",
  12293                methodInfal,
  12294                movable,
  12295                eliminatable,
  12296                aliasSet,
  12297                False,
  12298                False,
  12299                None,
  12300                [s[0] for s in sigs],
  12301                args,
  12302            )
  12303            return result
  12304        raise TypeError("Illegal member type to CGPropertyJITInfo")
  12305 
  12306    def mayBeMovable(self):
  12307        """
  12308        Returns whether this attribute or method may be movable, just
  12309        based on Affects/DependsOn annotations.
  12310        """
  12311        affects = self.member.affects
  12312        dependsOn = self.member.dependsOn
  12313        assert affects in IDLInterfaceMember.AffectsValues
  12314        assert dependsOn in IDLInterfaceMember.DependsOnValues
  12315        # Things that are DependsOn=DeviceState are not movable, because we
  12316        # don't want them coalesced with each other or loop-hoisted, since
  12317        # their return value can change even if nothing is going on from our
  12318        # point of view.
  12319        return affects == "Nothing" and (
  12320            dependsOn != "Everything" and dependsOn != "DeviceState"
  12321        )
  12322 
  12323    def mayBeEliminatable(self):
  12324        """
  12325        Returns whether this attribute or method may be eliminatable, just
  12326        based on Affects/DependsOn annotations.
  12327        """
  12328        # dependsOn shouldn't affect this decision at all, except in jitinfo we
  12329        # have no way to express "Depends on everything, affects nothing",
  12330        # because we only have three alias set values: AliasNone ("depends on
  12331        # nothing, affects nothing"), AliasDOMSets ("depends on DOM sets,
  12332        # affects nothing"), AliasEverything ("depends on everything, affects
  12333        # everything").  So the [Affects=Nothing, DependsOn=Everything] case
  12334        # gets encoded as AliasEverything and defineJitInfo asserts that if our
  12335        # alias state is AliasEverything then we're not eliminatable (because it
  12336        # thinks we might have side-effects at that point).  Bug 1155796 is
  12337        # tracking possible solutions for this.
  12338        affects = self.member.affects
  12339        dependsOn = self.member.dependsOn
  12340        assert affects in IDLInterfaceMember.AffectsValues
  12341        assert dependsOn in IDLInterfaceMember.DependsOnValues
  12342        return affects == "Nothing" and dependsOn != "Everything"
  12343 
  12344    def aliasSet(self):
  12345        """
  12346        Returns the alias set to store in the jitinfo.  This may not be the
  12347        effective alias set the JIT uses, depending on whether we have enough
  12348        information about our args to allow the JIT to prove that effectful
  12349        argument conversions won't happen.
  12350        """
  12351        dependsOn = self.member.dependsOn
  12352        assert dependsOn in IDLInterfaceMember.DependsOnValues
  12353 
  12354        if dependsOn == "Nothing" or dependsOn == "DeviceState":
  12355            assert self.member.affects == "Nothing"
  12356            return "AliasNone"
  12357 
  12358        if dependsOn == "DOMState":
  12359            assert self.member.affects == "Nothing"
  12360            return "AliasDOMSets"
  12361 
  12362        return "AliasEverything"
  12363 
  12364    @staticmethod
  12365    def getJSReturnTypeTag(t):
  12366        if t.nullable():
  12367            # Sometimes it might return null, sometimes not
  12368            return "JSVAL_TYPE_UNKNOWN"
  12369        if t.isUndefined():
  12370            # No return, every time
  12371            return "JSVAL_TYPE_UNDEFINED"
  12372        if t.isSequence():
  12373            return "JSVAL_TYPE_OBJECT"
  12374        if t.isRecord():
  12375            return "JSVAL_TYPE_OBJECT"
  12376        if t.isPromise():
  12377            return "JSVAL_TYPE_OBJECT"
  12378        if t.isGeckoInterface():
  12379            return "JSVAL_TYPE_OBJECT"
  12380        if t.isString():
  12381            return "JSVAL_TYPE_STRING"
  12382        if t.isEnum():
  12383            return "JSVAL_TYPE_STRING"
  12384        if t.isCallback():
  12385            return "JSVAL_TYPE_OBJECT"
  12386        if t.isAny():
  12387            # The whole point is to return various stuff
  12388            return "JSVAL_TYPE_UNKNOWN"
  12389        if t.isObject():
  12390            return "JSVAL_TYPE_OBJECT"
  12391        if t.isSpiderMonkeyInterface():
  12392            return "JSVAL_TYPE_OBJECT"
  12393        if t.isUnion():
  12394            u = t.unroll()
  12395            if u.hasNullableType:
  12396                # Might be null or not
  12397                return "JSVAL_TYPE_UNKNOWN"
  12398            return functools.reduce(
  12399                CGMemberJITInfo.getSingleReturnType, u.flatMemberTypes, ""
  12400            )
  12401        if t.isDictionary():
  12402            return "JSVAL_TYPE_OBJECT"
  12403        if t.isObservableArray():
  12404            return "JSVAL_TYPE_OBJECT"
  12405        if not t.isPrimitive():
  12406            raise TypeError("No idea what type " + str(t) + " is.")
  12407        tag = t.tag()
  12408        if tag == IDLType.Tags.bool:
  12409            return "JSVAL_TYPE_BOOLEAN"
  12410        if tag in [
  12411            IDLType.Tags.int8,
  12412            IDLType.Tags.uint8,
  12413            IDLType.Tags.int16,
  12414            IDLType.Tags.uint16,
  12415            IDLType.Tags.int32,
  12416        ]:
  12417            return "JSVAL_TYPE_INT32"
  12418        if tag in [
  12419            IDLType.Tags.int64,
  12420            IDLType.Tags.uint64,
  12421            IDLType.Tags.unrestricted_float,
  12422            IDLType.Tags.float,
  12423            IDLType.Tags.unrestricted_double,
  12424            IDLType.Tags.double,
  12425        ]:
  12426            # These all use JS_NumberValue, which can return int or double.
  12427            # But TI treats "double" as meaning "int or double", so we're
  12428            # good to return JSVAL_TYPE_DOUBLE here.
  12429            return "JSVAL_TYPE_DOUBLE"
  12430        if tag != IDLType.Tags.uint32:
  12431            raise TypeError("No idea what type " + str(t) + " is.")
  12432        # uint32 is sometimes int and sometimes double.
  12433        return "JSVAL_TYPE_DOUBLE"
  12434 
  12435    @staticmethod
  12436    def getSingleReturnType(existingType, t):
  12437        type = CGMemberJITInfo.getJSReturnTypeTag(t)
  12438        if existingType == "":
  12439            # First element of the list; just return its type
  12440            return type
  12441 
  12442        if type == existingType:
  12443            return existingType
  12444        if (type == "JSVAL_TYPE_DOUBLE" and existingType == "JSVAL_TYPE_INT32") or (
  12445            existingType == "JSVAL_TYPE_DOUBLE" and type == "JSVAL_TYPE_INT32"
  12446        ):
  12447            # Promote INT32 to DOUBLE as needed
  12448            return "JSVAL_TYPE_DOUBLE"
  12449        # Different types
  12450        return "JSVAL_TYPE_UNKNOWN"
  12451 
  12452    @staticmethod
  12453    def getJSArgType(t):
  12454        assert not t.isUndefined()
  12455        if t.nullable():
  12456            # Sometimes it might return null, sometimes not
  12457            return (
  12458                "JSJitInfo::ArgType(JSJitInfo::Null | %s)"
  12459                % CGMemberJITInfo.getJSArgType(t.inner)
  12460            )
  12461        if t.isSequence():
  12462            return "JSJitInfo::Object"
  12463        if t.isPromise():
  12464            return "JSJitInfo::Object"
  12465        if t.isGeckoInterface():
  12466            return "JSJitInfo::Object"
  12467        if t.isString():
  12468            return "JSJitInfo::String"
  12469        if t.isEnum():
  12470            return "JSJitInfo::String"
  12471        if t.isCallback():
  12472            return "JSJitInfo::Object"
  12473        if t.isAny():
  12474            # The whole point is to return various stuff
  12475            return "JSJitInfo::Any"
  12476        if t.isObject():
  12477            return "JSJitInfo::Object"
  12478        if t.isSpiderMonkeyInterface():
  12479            return "JSJitInfo::Object"
  12480        if t.isUnion():
  12481            u = t.unroll()
  12482            type = "JSJitInfo::Null" if u.hasNullableType else ""
  12483            return "JSJitInfo::ArgType(%s)" % functools.reduce(
  12484                CGMemberJITInfo.getSingleArgType, u.flatMemberTypes, type
  12485            )
  12486        if t.isDictionary():
  12487            return "JSJitInfo::Object"
  12488        if not t.isPrimitive():
  12489            raise TypeError("No idea what type " + str(t) + " is.")
  12490        tag = t.tag()
  12491        if tag == IDLType.Tags.bool:
  12492            return "JSJitInfo::Boolean"
  12493        if tag in [
  12494            IDLType.Tags.int8,
  12495            IDLType.Tags.uint8,
  12496            IDLType.Tags.int16,
  12497            IDLType.Tags.uint16,
  12498            IDLType.Tags.int32,
  12499        ]:
  12500            return "JSJitInfo::Integer"
  12501        if tag in [
  12502            IDLType.Tags.int64,
  12503            IDLType.Tags.uint64,
  12504            IDLType.Tags.unrestricted_float,
  12505            IDLType.Tags.float,
  12506            IDLType.Tags.unrestricted_double,
  12507            IDLType.Tags.double,
  12508        ]:
  12509            # These all use JS_NumberValue, which can return int or double.
  12510            # But TI treats "double" as meaning "int or double", so we're
  12511            # good to return JSVAL_TYPE_DOUBLE here.
  12512            return "JSJitInfo::Double"
  12513        if tag != IDLType.Tags.uint32:
  12514            raise TypeError("No idea what type " + str(t) + " is.")
  12515        # uint32 is sometimes int and sometimes double.
  12516        return "JSJitInfo::Double"
  12517 
  12518    @staticmethod
  12519    def getSingleArgType(existingType, t):
  12520        type = CGMemberJITInfo.getJSArgType(t)
  12521        if existingType == "":
  12522            # First element of the list; just return its type
  12523            return type
  12524 
  12525        if type == existingType:
  12526            return existingType
  12527        return "%s | %s" % (existingType, type)
  12528 
  12529 
  12530 class CGStaticMethodJitinfo(CGGeneric):
  12531    """
  12532    A class for generating the JITInfo for a promise-returning static method.
  12533    """
  12534 
  12535    def __init__(self, method):
  12536        CGGeneric.__init__(
  12537            self,
  12538            "\n"
  12539            "static const JSJitInfo %s_methodinfo = {\n"
  12540            "  { (JSJitGetterOp)%s },\n"
  12541            "  { prototypes::id::_ID_Count }, { 0 }, JSJitInfo::StaticMethod,\n"
  12542            "  JSJitInfo::AliasEverything, JSVAL_TYPE_OBJECT, false, false,\n"
  12543            "  false, false, 0\n"
  12544            "};\n"
  12545            % (
  12546                IDLToCIdentifier(method.identifier.name),
  12547                CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name)),
  12548            ),
  12549        )
  12550 
  12551 
  12552 def getEnumValueName(value):
  12553    # Some enum values can be empty strings.  Others might have weird
  12554    # characters in them.  Deal with the former by returning "_empty",
  12555    # deal with possible name collisions from that by throwing if the
  12556    # enum value is actually "_empty", and throw on any value
  12557    # containing non-ASCII chars for now. Replace all chars other than
  12558    # [0-9A-Za-z_] with '_'.
  12559    if re.match("[^\x20-\x7E]", value):
  12560        raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
  12561    if re.match("^[0-9]", value):
  12562        value = "_" + value
  12563    value = re.sub(r"[^0-9A-Za-z_]", "_", value)
  12564    if re.match("^_[A-Z]|__", value):
  12565        raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
  12566    if value == "_empty":
  12567        raise SyntaxError('"_empty" is not an IDL enum value we support yet')
  12568    if value == "":
  12569        return "_empty"
  12570    return MakeNativeName(value)
  12571 
  12572 
  12573 class CGEnumToJSValue(CGAbstractMethod):
  12574    def __init__(self, enum):
  12575        enumType = enum.identifier.name
  12576        self.stringsArray = "binding_detail::EnumStrings<" + enumType + ">::Values"
  12577        CGAbstractMethod.__init__(
  12578            self,
  12579            None,
  12580            "ToJSValue",
  12581            "bool",
  12582            [
  12583                Argument("JSContext*", "aCx"),
  12584                Argument(enumType, "aArgument"),
  12585                Argument("JS::MutableHandle<JS::Value>", "aValue"),
  12586            ],
  12587        )
  12588 
  12589    def definition_body(self):
  12590        return fill(
  12591            """
  12592            MOZ_ASSERT(uint32_t(aArgument) < std::size(${strings}));
  12593            JSString* resultStr =
  12594              JS_NewStringCopyN(aCx, ${strings}[uint32_t(aArgument)].BeginReading(),
  12595                                ${strings}[uint32_t(aArgument)].Length());
  12596            if (!resultStr) {
  12597              return false;
  12598            }
  12599            aValue.setString(resultStr);
  12600            return true;
  12601            """,
  12602            strings=self.stringsArray,
  12603        )
  12604 
  12605 
  12606 class CGEnum(CGThing):
  12607    def __init__(self, enum):
  12608        CGThing.__init__(self)
  12609        self.enum = enum
  12610        strings = CGNamespace(
  12611            "binding_detail",
  12612            CGGeneric(
  12613                declare=fill(
  12614                    """
  12615                    template <> struct EnumStrings<${name}> {
  12616                      static constexpr nsLiteralCString Values[${count}] {
  12617                        $*{entries}
  12618                      };
  12619                    };
  12620                    """,
  12621                    name=self.enum.identifier.name,
  12622                    count=self.nEnumStrings(),
  12623                    entries="".join('"%s"_ns,\n' % val for val in self.enum.values()),
  12624                ),
  12625                define=fill(
  12626                    """
  12627                    constexpr nsLiteralCString EnumStrings<${name}>::Values[${count}];
  12628                    """,
  12629                    name=self.enum.identifier.name,
  12630                    count=self.nEnumStrings(),
  12631                ),
  12632            ),
  12633        )
  12634        toJSValue = CGEnumToJSValue(enum)
  12635        self.cgThings = CGList([strings, toJSValue], "\n")
  12636 
  12637    def nEnumStrings(self):
  12638        return len(self.enum.values())
  12639 
  12640    @staticmethod
  12641    def underlyingType(enum):
  12642        count = len(enum.values())
  12643        if count <= 256:
  12644            return "uint8_t"
  12645        if count <= 65536:
  12646            return "uint16_t"
  12647        raise ValueError("Enum " + enum.identifier.name + " has more than 65536 values")
  12648 
  12649    def declare(self):
  12650        decl = fill(
  12651            """
  12652            enum class ${name} : ${ty} {
  12653              $*{enums}
  12654            };
  12655            """,
  12656            name=self.enum.identifier.name,
  12657            ty=CGEnum.underlyingType(self.enum),
  12658            enums=",\n".join(map(getEnumValueName, self.enum.values())) + ",\n",
  12659        )
  12660 
  12661        return decl + "\n" + self.cgThings.declare()
  12662 
  12663    def define(self):
  12664        return self.cgThings.define()
  12665 
  12666    def deps(self):
  12667        return self.enum.getDeps()
  12668 
  12669 
  12670 class CGMaxContiguousEnumValue(CGThing):
  12671    def __init__(self, enum):
  12672        CGThing.__init__(self)
  12673        self.enum = enum
  12674 
  12675    def declare(self):
  12676        enumValues = self.enum.values()
  12677        return fill(
  12678            """
  12679            template <>
  12680            struct MaxContiguousEnumValue<dom::${name}>
  12681            {
  12682              static constexpr dom::${name} value = dom::${name}::${maxValue};
  12683 
  12684              static_assert(static_cast<${ty}>(dom::${name}::${minValue}) == 0,
  12685                            "We rely on this in ContiguousEnumValues");
  12686              static_assert(std::size(dom::binding_detail::EnumStrings<dom::${name}>::Values) - 1 == UnderlyingValue(value),
  12687                            "Mismatch between enum strings and enum count");
  12688            };
  12689            """,
  12690            name=self.enum.identifier.name,
  12691            ty=CGEnum.underlyingType(self.enum),
  12692            maxValue=getEnumValueName(enumValues[-1]),
  12693            minValue=getEnumValueName(enumValues[0]),
  12694        )
  12695 
  12696    def define(self):
  12697        return ""
  12698 
  12699    def deps(self):
  12700        return self.enum.getDeps()
  12701 
  12702 
  12703 class CGUnionTypedef(CGThing):
  12704    def __init__(self, typedef: IDLTypedef, config: Configuration):
  12705        assert typedef.innerType.isUnion, "only union typedefs are supported"
  12706        super().__init__()
  12707        self.typedef = typedef
  12708 
  12709        builder = ForwardDeclarationBuilder()
  12710        builder.forwardDeclareForType(typedef.innerType, config)
  12711 
  12712        name = self.typedef.identifier.name
  12713        innerName = self.typedef.innerType.name
  12714        declare = dedent(
  12715            f"""
  12716            using {name} = {innerName};
  12717            using Owning{name} = Owning{innerName};
  12718            """
  12719        )
  12720        self.root = CGList(
  12721            [
  12722                builder.build(),
  12723                CGNamespace.build(
  12724                    ["mozilla", "dom"], CGGeneric(forward_declare=declare)
  12725                ),
  12726            ],
  12727            joiner="\n",
  12728        )
  12729 
  12730    def declare(self):
  12731        return ""
  12732 
  12733    def define(self):
  12734        return ""
  12735 
  12736    def forward_declare(self):
  12737        return self.root.forward_declare()
  12738 
  12739    def deps(self):
  12740        return self.typedef.getDeps()
  12741 
  12742 
  12743 def getUnionAccessorSignatureType(type, descriptorProvider):
  12744    """
  12745    Returns the types that are used in the getter and setter signatures for
  12746    union types
  12747    """
  12748    # Flat member types have already unwrapped nullables.
  12749    assert not type.nullable()
  12750 
  12751    # Promise types can never appear in unions, because Promise is not
  12752    # distinguishable from anything.
  12753    assert not type.isPromise()
  12754 
  12755    if type.isSequence() or type.isRecord():
  12756        if type.isSequence():
  12757            wrapperType = "Sequence"
  12758        else:
  12759            wrapperType = "Record"
  12760        # We don't use the returned template here, so it's OK to just pass no
  12761        # sourceDescription.
  12762        elementInfo = getJSToNativeConversionInfo(
  12763            type.inner, descriptorProvider, isMember=wrapperType
  12764        )
  12765        if wrapperType == "Sequence":
  12766            innerType = elementInfo.declType
  12767        else:
  12768            innerType = [recordKeyDeclType(type), elementInfo.declType]
  12769 
  12770        return CGTemplatedType(wrapperType, innerType, isConst=True, isReference=True)
  12771 
  12772    # Nested unions are unwrapped automatically into our flatMemberTypes.
  12773    assert not type.isUnion()
  12774 
  12775    if type.isGeckoInterface():
  12776        descriptor = descriptorProvider.getDescriptor(
  12777            type.unroll().inner.identifier.name
  12778        )
  12779        typeName = CGGeneric(descriptor.nativeType)
  12780        if not type.unroll().inner.isExternal():
  12781            typeName = CGWrapper(typeName, post="&")
  12782        elif descriptor.interface.identifier.name == "WindowProxy":
  12783            typeName = CGGeneric("WindowProxyHolder const&")
  12784        else:
  12785            # Allow null pointers for old-binding classes.
  12786            typeName = CGWrapper(typeName, post="*")
  12787        return typeName
  12788 
  12789    if type.isSpiderMonkeyInterface():
  12790        typeName = CGGeneric(type.name)
  12791        return CGWrapper(typeName, post=" const &")
  12792 
  12793    if type.isJSString():
  12794        raise TypeError("JSString not supported in unions")
  12795 
  12796    if type.isDOMString() or type.isUSVString():
  12797        return CGGeneric("const nsAString&")
  12798 
  12799    if type.isUTF8String():
  12800        return CGGeneric("const nsACString&")
  12801 
  12802    if type.isByteString():
  12803        return CGGeneric("const nsCString&")
  12804 
  12805    if type.isEnum():
  12806        return CGGeneric(type.inner.identifier.name)
  12807 
  12808    if type.isCallback():
  12809        return CGGeneric("%s&" % type.unroll().callback.identifier.name)
  12810 
  12811    if type.isAny():
  12812        return CGGeneric("JS::Value")
  12813 
  12814    if type.isObject():
  12815        return CGGeneric("JSObject*")
  12816 
  12817    if type.isDictionary():
  12818        return CGGeneric("const %s&" % type.inner.identifier.name)
  12819 
  12820    if not type.isPrimitive():
  12821        raise TypeError("Need native type for argument type '%s'" % str(type))
  12822 
  12823    return CGGeneric(builtinNames[type.tag()])
  12824 
  12825 
  12826 def getUnionTypeTemplateVars(unionType, type, descriptorProvider, isMember=False):
  12827    assert not type.isUndefined()
  12828    assert not isMember or isMember in ("Union", "OwningUnion")
  12829 
  12830    ownsMembers = isMember == "OwningUnion"
  12831    name = getUnionMemberName(type)
  12832    holderName = "m" + name + "Holder"
  12833 
  12834    # By the time tryNextCode is invoked, we're guaranteed the union has been
  12835    # constructed as some type, since we've been trying to convert into the
  12836    # corresponding member.
  12837    tryNextCode = fill(
  12838        """
  12839        Destroy${name}();
  12840        tryNext = true;
  12841        return true;
  12842        """,
  12843        name=name,
  12844    )
  12845 
  12846    sourceDescription = "%s branch of %s" % (type.prettyName(), unionType.prettyName())
  12847 
  12848    conversionInfo = getJSToNativeConversionInfo(
  12849        type,
  12850        descriptorProvider,
  12851        failureCode=tryNextCode,
  12852        isDefinitelyObject=not type.isDictionary(),
  12853        isMember=isMember,
  12854        sourceDescription=sourceDescription,
  12855    )
  12856 
  12857    ctorNeedsCx = conversionInfo.declArgs == "cx"
  12858    ctorArgs = "cx" if ctorNeedsCx else ""
  12859 
  12860    structType = conversionInfo.declType.define()
  12861    externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
  12862 
  12863    if type.isObject():
  12864        if ownsMembers:
  12865            setValue = "mValue.mObject.SetValue(obj);"
  12866        else:
  12867            setValue = "mValue.mObject.SetValue(cx, obj);"
  12868 
  12869        body = fill(
  12870            """
  12871            MOZ_ASSERT(mType == eUninitialized);
  12872            ${setValue}
  12873            mType = eObject;
  12874            """,
  12875            setValue=setValue,
  12876        )
  12877 
  12878        # It's a bit sketchy to do the security check after setting the value,
  12879        # but it keeps the code cleaner and lets us avoid rooting |obj| over the
  12880        # call to CallerSubsumes().
  12881        body = body + fill(
  12882            """
  12883            if (passedToJSImpl && !CallerSubsumes(obj)) {
  12884              cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}");
  12885              return false;
  12886            }
  12887            return true;
  12888            """,
  12889            sourceDescription=sourceDescription,
  12890        )
  12891 
  12892        setters = [
  12893            ClassMethod(
  12894                "SetToObject",
  12895                "bool",
  12896                [
  12897                    Argument("BindingCallContext&", "cx"),
  12898                    Argument("JSObject*", "obj"),
  12899                    Argument("bool", "passedToJSImpl", default="false"),
  12900                ],
  12901                inline=True,
  12902                bodyInHeader=True,
  12903                body=body,
  12904            )
  12905        ]
  12906    elif type.isDictionary() and not type.inner.needsConversionFromJS:
  12907        # In this case we are never initialized from JS to start with
  12908        setters = None
  12909    else:
  12910        # Important: we need to not have our declName involve
  12911        # maybe-GCing operations.
  12912        jsConversion = fill(
  12913            conversionInfo.template,
  12914            val="value",
  12915            maybeMutableVal="value",
  12916            declName="memberSlot",
  12917            holderName=(holderName if ownsMembers else "%s.ref()" % holderName),
  12918            passedToJSImpl="passedToJSImpl",
  12919        )
  12920 
  12921        jsConversion = fill(
  12922            """
  12923            tryNext = false;
  12924            { // scope for memberSlot
  12925              ${structType}& memberSlot = RawSetAs${name}(${ctorArgs});
  12926              $*{jsConversion}
  12927            }
  12928            return true;
  12929            """,
  12930            structType=structType,
  12931            name=name,
  12932            ctorArgs=ctorArgs,
  12933            jsConversion=jsConversion,
  12934        )
  12935 
  12936        needCallContext = idlTypeNeedsCallContext(type)
  12937        if needCallContext:
  12938            cxType = "BindingCallContext&"
  12939        else:
  12940            cxType = "JSContext*"
  12941        setters = [
  12942            ClassMethod(
  12943                "TrySetTo" + name,
  12944                "bool",
  12945                [
  12946                    Argument(cxType, "cx"),
  12947                    Argument("JS::Handle<JS::Value>", "value"),
  12948                    Argument("bool&", "tryNext"),
  12949                    Argument("bool", "passedToJSImpl", default="false"),
  12950                ],
  12951                visibility="private",
  12952                body=jsConversion,
  12953            )
  12954        ]
  12955        if needCallContext:
  12956            # Add a method for non-binding uses of unions to allow them to set
  12957            # things in the union without providing a call context (though if
  12958            # they want good error reporting they'll provide one anyway).
  12959            shimBody = fill(
  12960                """
  12961                BindingCallContext cx(cx_, nullptr);
  12962                return TrySetTo${name}(cx, value, tryNext, passedToJSImpl);
  12963                """,
  12964                name=name,
  12965            )
  12966            setters.append(
  12967                ClassMethod(
  12968                    "TrySetTo" + name,
  12969                    "bool",
  12970                    [
  12971                        Argument("JSContext*", "cx_"),
  12972                        Argument("JS::Handle<JS::Value>", "value"),
  12973                        Argument("bool&", "tryNext"),
  12974                        Argument("bool", "passedToJSImpl", default="false"),
  12975                    ],
  12976                    visibility="private",
  12977                    body=shimBody,
  12978                )
  12979            )
  12980 
  12981    return {
  12982        "name": name,
  12983        "structType": structType,
  12984        "externalType": externalType,
  12985        "setters": setters,
  12986        "ctorArgs": ctorArgs,
  12987        "ctorArgList": [Argument("JSContext*", "cx")] if ctorNeedsCx else [],
  12988    }
  12989 
  12990 
  12991 def getUnionConversionTemplate(type):
  12992    assert type.isUnion()
  12993    assert not type.nullable()
  12994 
  12995    memberTypes = type.flatMemberTypes
  12996    prettyNames = []
  12997 
  12998    interfaceMemberTypes = [t for t in memberTypes if t.isNonCallbackInterface()]
  12999    if len(interfaceMemberTypes) > 0:
  13000        interfaceObject = []
  13001        for memberType in interfaceMemberTypes:
  13002            name = getUnionMemberName(memberType)
  13003            interfaceObject.append(
  13004                CGGeneric(
  13005                    "(failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext"
  13006                    % name
  13007                )
  13008            )
  13009            prettyNames.append(memberType.prettyName())
  13010        interfaceObject = CGWrapper(
  13011            CGList(interfaceObject, " ||\n"),
  13012            pre="done = ",
  13013            post=";\n",
  13014            reindent=True,
  13015        )
  13016    else:
  13017        interfaceObject = None
  13018 
  13019    sequenceObjectMemberTypes = [t for t in memberTypes if t.isSequence()]
  13020    if len(sequenceObjectMemberTypes) > 0:
  13021        assert len(sequenceObjectMemberTypes) == 1
  13022        memberType = sequenceObjectMemberTypes[0]
  13023        name = getUnionMemberName(memberType)
  13024        sequenceObject = CGGeneric(
  13025            "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
  13026            % name
  13027        )
  13028        prettyNames.append(memberType.prettyName())
  13029    else:
  13030        sequenceObject = None
  13031 
  13032    callbackMemberTypes = [
  13033        t for t in memberTypes if t.isCallback() or t.isCallbackInterface()
  13034    ]
  13035    if len(callbackMemberTypes) > 0:
  13036        assert len(callbackMemberTypes) == 1
  13037        memberType = callbackMemberTypes[0]
  13038        name = getUnionMemberName(memberType)
  13039        callbackObject = CGGeneric(
  13040            "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
  13041            % name
  13042        )
  13043        prettyNames.append(memberType.prettyName())
  13044    else:
  13045        callbackObject = None
  13046 
  13047    dictionaryMemberTypes = [t for t in memberTypes if t.isDictionary()]
  13048    if len(dictionaryMemberTypes) > 0:
  13049        assert len(dictionaryMemberTypes) == 1
  13050        memberType = dictionaryMemberTypes[0]
  13051        name = getUnionMemberName(memberType)
  13052        setDictionary = CGGeneric(
  13053            "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
  13054            % name
  13055        )
  13056        prettyNames.append(memberType.prettyName())
  13057    else:
  13058        setDictionary = None
  13059 
  13060    recordMemberTypes = [t for t in memberTypes if t.isRecord()]
  13061    if len(recordMemberTypes) > 0:
  13062        assert len(recordMemberTypes) == 1
  13063        memberType = recordMemberTypes[0]
  13064        name = getUnionMemberName(memberType)
  13065        recordObject = CGGeneric(
  13066            "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
  13067            % name
  13068        )
  13069        prettyNames.append(memberType.prettyName())
  13070    else:
  13071        recordObject = None
  13072 
  13073    objectMemberTypes = [t for t in memberTypes if t.isObject()]
  13074    if len(objectMemberTypes) > 0:
  13075        assert len(objectMemberTypes) == 1
  13076        # Very important to NOT construct a temporary Rooted here, since the
  13077        # SetToObject call can call a Rooted constructor and we need to keep
  13078        # stack discipline for Rooted.
  13079        object = CGGeneric(
  13080            "if (!SetToObject(cx, &${val}.toObject(), ${passedToJSImpl})) {\n"
  13081            "  return false;\n"
  13082            "}\n"
  13083            "done = true;\n"
  13084        )
  13085        prettyNames.append(objectMemberTypes[0].prettyName())
  13086    else:
  13087        object = None
  13088 
  13089    hasObjectTypes = (
  13090        interfaceObject or sequenceObject or callbackObject or object or recordObject
  13091    )
  13092    if hasObjectTypes:
  13093        # "object" is not distinguishable from other types
  13094        assert not object or not (
  13095            interfaceObject or sequenceObject or callbackObject or recordObject
  13096        )
  13097        if sequenceObject or callbackObject:
  13098            # An object can be both an sequence object and a callback or
  13099            # dictionary, but we shouldn't have both in the union's members
  13100            # because they are not distinguishable.
  13101            assert not (sequenceObject and callbackObject)
  13102            templateBody = CGElseChain([sequenceObject, callbackObject])
  13103        else:
  13104            templateBody = None
  13105        if interfaceObject:
  13106            assert not object
  13107            if templateBody:
  13108                templateBody = CGIfWrapper(templateBody, "!done")
  13109            templateBody = CGList([interfaceObject, templateBody])
  13110        else:
  13111            templateBody = CGList([templateBody, object])
  13112 
  13113        if recordObject:
  13114            templateBody = CGList([templateBody, CGIfWrapper(recordObject, "!done")])
  13115 
  13116        templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
  13117    else:
  13118        templateBody = CGGeneric()
  13119 
  13120    if setDictionary:
  13121        assert not object
  13122        templateBody = CGList([templateBody, CGIfWrapper(setDictionary, "!done")])
  13123 
  13124    stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
  13125    numericTypes = [t for t in memberTypes if t.isNumeric()]
  13126    booleanTypes = [t for t in memberTypes if t.isBoolean()]
  13127    if stringTypes or numericTypes or booleanTypes:
  13128        assert len(stringTypes) <= 1
  13129        assert len(numericTypes) <= 1
  13130        assert len(booleanTypes) <= 1
  13131 
  13132        # We will wrap all this stuff in a do { } while (0); so we
  13133        # can use "break" for flow control.
  13134        def getStringOrPrimitiveConversion(memberType):
  13135            name = getUnionMemberName(memberType)
  13136            return CGGeneric(
  13137                "done = (failed = !TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n"
  13138                "break;\n" % name
  13139            )
  13140 
  13141        other = CGList([])
  13142        stringConversion = [getStringOrPrimitiveConversion(t) for t in stringTypes]
  13143        numericConversion = [getStringOrPrimitiveConversion(t) for t in numericTypes]
  13144        booleanConversion = [getStringOrPrimitiveConversion(t) for t in booleanTypes]
  13145        if stringConversion:
  13146            if booleanConversion:
  13147                other.append(CGIfWrapper(booleanConversion[0], "${val}.isBoolean()"))
  13148            if numericConversion:
  13149                other.append(CGIfWrapper(numericConversion[0], "${val}.isNumber()"))
  13150            other.append(stringConversion[0])
  13151        elif numericConversion:
  13152            if booleanConversion:
  13153                other.append(CGIfWrapper(booleanConversion[0], "${val}.isBoolean()"))
  13154            other.append(numericConversion[0])
  13155        else:
  13156            assert booleanConversion
  13157            other.append(booleanConversion[0])
  13158 
  13159        other = CGWrapper(CGIndenter(other), pre="do {\n", post="} while (false);\n")
  13160        if hasObjectTypes or setDictionary:
  13161            other = CGWrapper(CGIndenter(other), "{\n", post="}\n")
  13162            if object:
  13163                templateBody = CGElseChain([templateBody, other])
  13164            else:
  13165                other = CGWrapper(other, pre="if (!done) ")
  13166                templateBody = CGList([templateBody, other])
  13167        else:
  13168            assert templateBody.define() == ""
  13169            templateBody = other
  13170    else:
  13171        other = None
  13172 
  13173    templateBody = CGWrapper(
  13174        templateBody, pre="bool done = false, failed = false, tryNext;\n"
  13175    )
  13176    throw = CGGeneric(
  13177        fill(
  13178            """
  13179            if (failed) {
  13180              return false;
  13181            }
  13182            if (!done) {
  13183              cx.ThrowErrorMessage<MSG_NOT_IN_UNION>(sourceDescription, "${names}");
  13184              return false;
  13185            }
  13186            """,
  13187            names=", ".join(prettyNames),
  13188        )
  13189    )
  13190 
  13191    templateBody = CGList([templateBody, throw])
  13192 
  13193    hasUndefinedType = any(t.isUndefined() for t in memberTypes)
  13194    elseChain = []
  13195 
  13196    # The spec does this before anything else, but we do it after checking
  13197    # for null in the case of a nullable union. In practice this shouldn't
  13198    # make a difference, but it makes things easier because we first need to
  13199    # call Construct on our Maybe<...>, before we can set the union type to
  13200    # undefined, and we do that below after checking for null (see the
  13201    # 'if nullable:' block below).
  13202    if hasUndefinedType:
  13203        elseChain.append(
  13204            CGIfWrapper(
  13205                CGGeneric("SetUndefined();\n"),
  13206                "${val}.isUndefined()",
  13207            )
  13208        )
  13209 
  13210    if type.hasNullableType:
  13211        nullTest = (
  13212            "${val}.isNull()" if hasUndefinedType else "${val}.isNullOrUndefined()"
  13213        )
  13214        elseChain.append(
  13215            CGIfWrapper(
  13216                CGGeneric("SetNull();\n"),
  13217                nullTest,
  13218            )
  13219        )
  13220 
  13221    if len(elseChain) > 0:
  13222        elseChain.append(CGWrapper(CGIndenter(templateBody), pre="{\n", post="}\n"))
  13223        templateBody = CGElseChain(elseChain)
  13224 
  13225    return templateBody
  13226 
  13227 
  13228 def getUnionInitMethods(type, isOwningUnion=False):
  13229    assert type.isUnion()
  13230 
  13231    template = getUnionConversionTemplate(type).define()
  13232 
  13233    replacements = {
  13234        "val": "value",
  13235        "passedToJSImpl": "passedToJSImpl",
  13236    }
  13237 
  13238    initBody = fill(
  13239        """
  13240        MOZ_ASSERT(mType == eUninitialized);
  13241 
  13242        $*{conversion}
  13243        return true;
  13244        """,
  13245        conversion=string.Template(template).substitute(replacements),
  13246    )
  13247 
  13248    return [
  13249        ClassMethod(
  13250            "Init",
  13251            "bool",
  13252            [
  13253                Argument("BindingCallContext&", "cx"),
  13254                Argument("JS::Handle<JS::Value>", "value"),
  13255                Argument("const char*", "sourceDescription", default='"Value"'),
  13256                Argument("bool", "passedToJSImpl", default="false"),
  13257            ],
  13258            visibility="public",
  13259            body=initBody,
  13260        ),
  13261        ClassMethod(
  13262            "Init",
  13263            "bool",
  13264            [
  13265                Argument("JSContext*", "cx_"),
  13266                Argument("JS::Handle<JS::Value>", "value"),
  13267                Argument("const char*", "sourceDescription", default='"Value"'),
  13268                Argument("bool", "passedToJSImpl", default="false"),
  13269            ],
  13270            visibility="public",
  13271            body=dedent(
  13272                """
  13273                BindingCallContext cx(cx_, nullptr);
  13274                return Init(cx, value, sourceDescription, passedToJSImpl);
  13275                """
  13276            ),
  13277        ),
  13278    ]
  13279 
  13280 
  13281 class CGUnionStruct(CGThing):
  13282    def __init__(self, type, descriptorProvider, ownsMembers=False):
  13283        CGThing.__init__(self)
  13284        self.type = type.unroll()
  13285        self.descriptorProvider = descriptorProvider
  13286        self.ownsMembers = ownsMembers
  13287        self.struct = self.getStruct()
  13288 
  13289    def declare(self):
  13290        return self.struct.declare()
  13291 
  13292    def define(self):
  13293        return self.struct.define()
  13294 
  13295    def deps(self):
  13296        return self.type.getDeps()
  13297 
  13298    def getStruct(self):
  13299        members = [
  13300            ClassMember("mType", "TypeOrUninit", body="eUninitialized"),
  13301            ClassMember("mValue", "Value"),
  13302        ]
  13303        ctor = ClassConstructor(
  13304            [], bodyInHeader=True, visibility="public", explicit=True
  13305        )
  13306 
  13307        methods = []
  13308        enumValues = ["eUninitialized"]
  13309        toJSValCases = [
  13310            CGCase(
  13311                "eUninitialized", CGGeneric("return false;\n"), CGCase.DONT_ADD_BREAK
  13312            )
  13313        ]
  13314        destructorCases = [CGCase("eUninitialized", None)]
  13315        assignmentCase = CGCase(
  13316            "eUninitialized",
  13317            CGGeneric(
  13318                "MOZ_ASSERT(mType == eUninitialized,\n"
  13319                '           "We need to destroy ourselves?");\n'
  13320            ),
  13321        )
  13322        assignmentCases = [assignmentCase]
  13323        moveCases = [assignmentCase]
  13324        traceCases = []
  13325        unionValues = []
  13326 
  13327        def addSpecialType(typename):
  13328            enumValue = "e" + typename
  13329            enumValues.append(enumValue)
  13330            methods.append(
  13331                ClassMethod(
  13332                    "Is" + typename,
  13333                    "bool",
  13334                    [],
  13335                    const=True,
  13336                    inline=True,
  13337                    body="return mType == %s;\n" % enumValue,
  13338                    bodyInHeader=True,
  13339                )
  13340            )
  13341            methods.append(
  13342                ClassMethod(
  13343                    "Set" + typename,
  13344                    "void",
  13345                    [],
  13346                    inline=True,
  13347                    body=fill(
  13348                        """
  13349                        Uninit();
  13350                        mType = ${enumValue};
  13351                        """,
  13352                        enumValue=enumValue,
  13353                    ),
  13354                    bodyInHeader=True,
  13355                )
  13356            )
  13357            destructorCases.append(CGCase(enumValue, None))
  13358            assignmentCase = CGCase(
  13359                enumValue,
  13360                CGGeneric(
  13361                    fill(
  13362                        """
  13363                            MOZ_ASSERT(mType == eUninitialized);
  13364                            mType = ${enumValue};
  13365                            """,
  13366                        enumValue=enumValue,
  13367                    )
  13368                ),
  13369            )
  13370            assignmentCases.append(assignmentCase)
  13371            moveCases.append(assignmentCase)
  13372            toJSValCases.append(
  13373                CGCase(
  13374                    enumValue,
  13375                    CGGeneric(
  13376                        fill(
  13377                            """
  13378                            rval.set${typename}();
  13379                            return true;
  13380                            """,
  13381                            typename=typename,
  13382                        )
  13383                    ),
  13384                    CGCase.DONT_ADD_BREAK,
  13385                )
  13386            )
  13387 
  13388        if self.type.hasNullableType:
  13389            addSpecialType("Null")
  13390 
  13391        hasObjectType = any(t.isObject() for t in self.type.flatMemberTypes)
  13392        skipToJSVal = False
  13393        for t in self.type.flatMemberTypes:
  13394            if t.isUndefined():
  13395                addSpecialType("Undefined")
  13396                continue
  13397 
  13398            vars = getUnionTypeTemplateVars(
  13399                self.type,
  13400                t,
  13401                self.descriptorProvider,
  13402                isMember="OwningUnion" if self.ownsMembers else "Union",
  13403            )
  13404            if vars["setters"]:
  13405                methods.extend(vars["setters"])
  13406            uninit = "Uninit();"
  13407            if hasObjectType and not self.ownsMembers:
  13408                uninit = (
  13409                    'MOZ_ASSERT(mType != eObject, "This will not play well with Rooted");\n'
  13410                    + uninit
  13411                )
  13412            if not t.isObject() or self.ownsMembers:
  13413                body = fill(
  13414                    """
  13415                    if (mType == e${name}) {
  13416                      return mValue.m${name}.Value();
  13417                    }
  13418                    %s
  13419                    mType = e${name};
  13420                    return mValue.m${name}.SetValue(${ctorArgs});
  13421                    """,
  13422                    **vars,
  13423                )
  13424 
  13425                # bodyInHeader must be false for return values because they own
  13426                # their union members and we don't want include headers in
  13427                # UnionTypes.h just to call Addref/Release
  13428                methods.append(
  13429                    ClassMethod(
  13430                        "RawSetAs" + vars["name"],
  13431                        vars["structType"] + "&",
  13432                        vars["ctorArgList"],
  13433                        bodyInHeader=not self.ownsMembers,
  13434                        body=body % "MOZ_ASSERT(mType == eUninitialized);",
  13435                        noDiscard=True,
  13436                    )
  13437                )
  13438                methods.append(
  13439                    ClassMethod(
  13440                        "SetAs" + vars["name"],
  13441                        vars["structType"] + "&",
  13442                        vars["ctorArgList"],
  13443                        bodyInHeader=not self.ownsMembers,
  13444                        body=body % uninit,
  13445                        noDiscard=True,
  13446                    )
  13447                )
  13448 
  13449                # Provide a SetStringLiteral() method to support string defaults.
  13450                if t.isByteString() or t.isUTF8String():
  13451                    charType = "const nsCString::char_type"
  13452                elif t.isString():
  13453                    charType = "const nsString::char_type"
  13454                else:
  13455                    charType = None
  13456 
  13457                if charType:
  13458                    methods.append(
  13459                        ClassMethod(
  13460                            "SetStringLiteral",
  13461                            "void",
  13462                            # Hack, but it works...
  13463                            [Argument(charType, "(&aData)[N]")],
  13464                            inline=True,
  13465                            bodyInHeader=True,
  13466                            templateArgs=["int N"],
  13467                            body="RawSetAs%s().AssignLiteral(aData);\n" % t.name,
  13468                        )
  13469                    )
  13470 
  13471            body = fill("return mType == e${name};\n", **vars)
  13472            methods.append(
  13473                ClassMethod(
  13474                    "Is" + vars["name"],
  13475                    "bool",
  13476                    [],
  13477                    const=True,
  13478                    bodyInHeader=True,
  13479                    body=body,
  13480                )
  13481            )
  13482 
  13483            body = fill(
  13484                """
  13485                MOZ_RELEASE_ASSERT(Is${name}(), "Wrong type!");
  13486                mValue.m${name}.Destroy();
  13487                mType = eUninitialized;
  13488                """,
  13489                **vars,
  13490            )
  13491            methods.append(
  13492                ClassMethod(
  13493                    "Destroy" + vars["name"],
  13494                    "void",
  13495                    [],
  13496                    visibility="private",
  13497                    bodyInHeader=not self.ownsMembers,
  13498                    body=body,
  13499                )
  13500            )
  13501 
  13502            body = fill(
  13503                """
  13504                MOZ_RELEASE_ASSERT(Is${name}(), "Wrong type!");
  13505                return mValue.m${name}.Value();
  13506                """,
  13507                **vars,
  13508            )
  13509            # The non-const version of GetAs* returns our internal type
  13510            getterReturnType = "%s&" % vars["structType"]
  13511            methods.append(
  13512                ClassMethod(
  13513                    "GetAs" + vars["name"],
  13514                    getterReturnType,
  13515                    [],
  13516                    bodyInHeader=True,
  13517                    body=body,
  13518                )
  13519            )
  13520            # The const version of GetAs* returns our internal type
  13521            # for owning unions, but our external type for non-owning
  13522            # ones.
  13523            if self.ownsMembers:
  13524                getterReturnType = "%s const &" % vars["structType"]
  13525            else:
  13526                getterReturnType = vars["externalType"]
  13527            methods.append(
  13528                ClassMethod(
  13529                    "GetAs" + vars["name"],
  13530                    getterReturnType,
  13531                    [],
  13532                    const=True,
  13533                    bodyInHeader=True,
  13534                    body=body,
  13535                )
  13536            )
  13537 
  13538            unionValues.append(fill("UnionMember<${structType} > m${name}", **vars))
  13539            destructorCases.append(
  13540                CGCase("e" + vars["name"], CGGeneric("Destroy%s();\n" % vars["name"]))
  13541            )
  13542 
  13543            enumValues.append("e" + vars["name"])
  13544 
  13545            conversionToJS = self.getConversionToJS(vars, t)
  13546            if conversionToJS:
  13547                toJSValCases.append(
  13548                    CGCase("e" + vars["name"], conversionToJS, CGCase.DONT_ADD_BREAK)
  13549                )
  13550            else:
  13551                skipToJSVal = True
  13552 
  13553            assignmentCases.append(
  13554                CGCase(
  13555                    "e" + vars["name"],
  13556                    CGGeneric(
  13557                        "SetAs%s() = aOther.GetAs%s();\n" % (vars["name"], vars["name"])
  13558                    ),
  13559                )
  13560            )
  13561            moveCases.append(
  13562                CGCase(
  13563                    "e" + vars["name"],
  13564                    CGGeneric(
  13565                        "mType = e%s;\n" % vars["name"]
  13566                        + "mValue.m%s.SetValue(std::move(aOther.mValue.m%s.Value()));\n"
  13567                        % (vars["name"], vars["name"])
  13568                    ),
  13569                )
  13570            )
  13571            if self.ownsMembers and typeNeedsRooting(t):
  13572                if t.isObject():
  13573                    traceCases.append(
  13574                        CGCase(
  13575                            "e" + vars["name"],
  13576                            CGGeneric(
  13577                                'JS::TraceRoot(trc, %s, "%s");\n'
  13578                                % (
  13579                                    "&mValue.m" + vars["name"] + ".Value()",
  13580                                    "mValue.m" + vars["name"],
  13581                                )
  13582                            ),
  13583                        )
  13584                    )
  13585                elif t.isDictionary():
  13586                    traceCases.append(
  13587                        CGCase(
  13588                            "e" + vars["name"],
  13589                            CGGeneric(
  13590                                "mValue.m%s.Value().TraceDictionary(trc);\n"
  13591                                % vars["name"]
  13592                            ),
  13593                        )
  13594                    )
  13595                elif t.isSequence():
  13596                    traceCases.append(
  13597                        CGCase(
  13598                            "e" + vars["name"],
  13599                            CGGeneric(
  13600                                "DoTraceSequence(trc, mValue.m%s.Value());\n"
  13601                                % vars["name"]
  13602                            ),
  13603                        )
  13604                    )
  13605                elif t.isRecord():
  13606                    traceCases.append(
  13607                        CGCase(
  13608                            "e" + vars["name"],
  13609                            CGGeneric(
  13610                                "TraceRecord(trc, mValue.m%s.Value());\n" % vars["name"]
  13611                            ),
  13612                        )
  13613                    )
  13614                else:
  13615                    assert t.isSpiderMonkeyInterface()
  13616                    traceCases.append(
  13617                        CGCase(
  13618                            "e" + vars["name"],
  13619                            CGGeneric(
  13620                                "mValue.m%s.Value().TraceSelf(trc);\n" % vars["name"]
  13621                            ),
  13622                        )
  13623                    )
  13624 
  13625        dtor = CGSwitch("mType", destructorCases).define()
  13626 
  13627        methods.extend(getUnionInitMethods(self.type, isOwningUnion=self.ownsMembers))
  13628        methods.append(
  13629            ClassMethod(
  13630                "Uninit",
  13631                "void",
  13632                [],
  13633                visibility="public",
  13634                body=dtor,
  13635                bodyInHeader=not self.ownsMembers,
  13636                inline=not self.ownsMembers,
  13637            )
  13638        )
  13639 
  13640        if not skipToJSVal:
  13641            methods.append(
  13642                ClassMethod(
  13643                    "ToJSVal",
  13644                    "bool",
  13645                    [
  13646                        Argument("JSContext*", "cx"),
  13647                        Argument("JS::Handle<JSObject*>", "scopeObj"),
  13648                        Argument("JS::MutableHandle<JS::Value>", "rval"),
  13649                    ],
  13650                    body=CGSwitch(
  13651                        "mType", toJSValCases, default=CGGeneric("return false;\n")
  13652                    ).define(),
  13653                    const=True,
  13654                )
  13655            )
  13656 
  13657        constructors = [ctor]
  13658        selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
  13659        if self.ownsMembers:
  13660            if traceCases:
  13661                traceBody = CGSwitch(
  13662                    "mType", traceCases, default=CGGeneric("")
  13663                ).define()
  13664                methods.append(
  13665                    ClassMethod(
  13666                        "TraceUnion",
  13667                        "void",
  13668                        [Argument("JSTracer*", "trc")],
  13669                        body=traceBody,
  13670                    )
  13671                )
  13672 
  13673            op_body = CGList([])
  13674            op_body.append(CGSwitch("aOther.mType", moveCases))
  13675            constructors.append(
  13676                ClassConstructor(
  13677                    [Argument("%s&&" % selfName, "aOther")],
  13678                    visibility="public",
  13679                    body=op_body.define(),
  13680                )
  13681            )
  13682 
  13683            methods.append(
  13684                ClassMethod(
  13685                    "operator=",
  13686                    "%s&" % selfName,
  13687                    [Argument("%s&&" % selfName, "aOther")],
  13688                    body="this->~%s();\nnew (this) %s (std::move(aOther));\nreturn *this;\n"
  13689                    % (selfName, selfName),
  13690                )
  13691            )
  13692 
  13693            body = dedent(
  13694                """
  13695                MOZ_RELEASE_ASSERT(mType != eUninitialized);
  13696                return static_cast<Type>(mType);
  13697                """
  13698            )
  13699            methods.append(
  13700                ClassMethod(
  13701                    "GetType",
  13702                    "Type",
  13703                    [],
  13704                    bodyInHeader=True,
  13705                    body=body,
  13706                    const=True,
  13707                )
  13708            )
  13709 
  13710            if CGUnionStruct.isUnionCopyConstructible(self.type):
  13711                constructors.append(
  13712                    ClassConstructor(
  13713                        [Argument("const %s&" % selfName, "aOther")],
  13714                        bodyInHeader=True,
  13715                        visibility="public",
  13716                        explicit=True,
  13717                        body="*this = aOther;\n",
  13718                    )
  13719                )
  13720                op_body = CGList([])
  13721                op_body.append(CGSwitch("aOther.mType", assignmentCases))
  13722                op_body.append(CGGeneric("return *this;\n"))
  13723                methods.append(
  13724                    ClassMethod(
  13725                        "operator=",
  13726                        "%s&" % selfName,
  13727                        [Argument("const %s&" % selfName, "aOther")],
  13728                        body=op_body.define(),
  13729                    )
  13730                )
  13731                disallowCopyConstruction = False
  13732            else:
  13733                disallowCopyConstruction = True
  13734        else:
  13735            disallowCopyConstruction = True
  13736 
  13737        if self.ownsMembers and idlTypeNeedsCycleCollection(self.type):
  13738            friend = (
  13739                "  friend void ImplCycleCollectionUnlink(%s& aUnion);\n"
  13740                % CGUnionStruct.unionTypeName(self.type, True)
  13741            )
  13742        else:
  13743            friend = ""
  13744 
  13745        enumValuesNoUninit = [x for x in enumValues if x != "eUninitialized"]
  13746 
  13747        enums = [
  13748            ClassGroup(
  13749                [
  13750                    ClassEnum("TypeOrUninit", enumValues, visibility="private"),
  13751                    ClassEnum(
  13752                        "Type",
  13753                        enumValuesNoUninit,
  13754                        visibility="public",
  13755                        enumClass=True,
  13756                        values=["TypeOrUninit::" + x for x in enumValuesNoUninit],
  13757                    ),
  13758                ]
  13759            )
  13760        ]
  13761 
  13762        bases = [
  13763            ClassBase("AllOwningUnionBase" if self.ownsMembers else "AllUnionBase")
  13764        ]
  13765 
  13766        typeAliases = []
  13767        bufferSourceTypes = [
  13768            t.name for t in self.type.flatMemberTypes if t.isBufferSource()
  13769        ]
  13770        if len(bufferSourceTypes) > 0:
  13771            bases.append(ClassBase("UnionWithTypedArraysBase"))
  13772            memberTypesCount = len(self.type.flatMemberTypes)
  13773            if self.type.hasNullableType:
  13774                memberTypesCount += 1
  13775 
  13776            typeAliases = [
  13777                ClassUsingDeclaration(
  13778                    "ApplyToTypedArrays",
  13779                    "binding_detail::ApplyToTypedArraysHelper<%s, %s, %s>"
  13780                    % (
  13781                        selfName,
  13782                        toStringBool(memberTypesCount > len(bufferSourceTypes)),
  13783                        ", ".join(bufferSourceTypes),
  13784                    ),
  13785                )
  13786            ]
  13787 
  13788        return CGClass(
  13789            selfName,
  13790            bases=bases,
  13791            typeAliases=typeAliases,
  13792            members=members,
  13793            constructors=constructors,
  13794            methods=methods,
  13795            disallowCopyConstruction=disallowCopyConstruction,
  13796            extradeclarations=friend,
  13797            destructor=ClassDestructor(
  13798                visibility="public", body="Uninit();\n", bodyInHeader=True
  13799            ),
  13800            enums=enums,
  13801            unions=[ClassUnion("Value", unionValues, visibility="private")],
  13802        )
  13803 
  13804    def getConversionToJS(self, templateVars, type):
  13805        if type.isDictionary() and not type.inner.needsConversionToJS:
  13806            # We won't be able to convert this dictionary to a JS value, nor
  13807            # will we need to, since we don't need a ToJSVal method at all.
  13808            return None
  13809 
  13810        assert not type.nullable()  # flatMemberTypes never has nullable types
  13811        val = "mValue.m%(name)s.Value()" % templateVars
  13812        wrapCode = wrapForType(
  13813            type,
  13814            self.descriptorProvider,
  13815            {
  13816                "jsvalRef": "rval",
  13817                "jsvalHandle": "rval",
  13818                "obj": "scopeObj",
  13819                "result": val,
  13820                "spiderMonkeyInterfacesAreStructs": True,
  13821            },
  13822        )
  13823        return CGGeneric(wrapCode)
  13824 
  13825    @staticmethod
  13826    def isUnionCopyConstructible(type):
  13827        return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes)
  13828 
  13829    @staticmethod
  13830    def unionTypeName(type, ownsMembers):
  13831        """
  13832        Returns a string name for this known union type.
  13833        """
  13834        assert type.isUnion() and not type.nullable()
  13835        return ("Owning" if ownsMembers else "") + type.name
  13836 
  13837    @staticmethod
  13838    def unionTypeDecl(type, ownsMembers):
  13839        """
  13840        Returns a string for declaring this possibly-nullable union type.
  13841        """
  13842        assert type.isUnion()
  13843        nullable = type.nullable()
  13844        if nullable:
  13845            type = type.inner
  13846        decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers))
  13847        if nullable:
  13848            decl = CGTemplatedType("Nullable", decl)
  13849        return decl.define()
  13850 
  13851 
  13852 class ClassItem:
  13853    """Use with CGClass"""
  13854 
  13855    def __init__(self, name, visibility):
  13856        self.name = name
  13857        self.visibility = visibility
  13858 
  13859    def declare(self, cgClass):
  13860        assert False
  13861 
  13862    def define(self, cgClass):
  13863        assert False
  13864 
  13865 
  13866 class ClassBase(ClassItem):
  13867    def __init__(self, name, visibility="public"):
  13868        ClassItem.__init__(self, name, visibility)
  13869 
  13870    def declare(self, cgClass):
  13871        return "%s %s" % (self.visibility, self.name)
  13872 
  13873    def define(self, cgClass):
  13874        # Only in the header
  13875        return ""
  13876 
  13877 
  13878 class ClassMethod(ClassItem):
  13879    def __init__(
  13880        self,
  13881        name,
  13882        returnType,
  13883        args,
  13884        inline=False,
  13885        static=False,
  13886        virtual=False,
  13887        const=False,
  13888        bodyInHeader=False,
  13889        templateArgs=None,
  13890        visibility="public",
  13891        body=None,
  13892        breakAfterReturnDecl="\n",
  13893        breakAfterSelf="\n",
  13894        override=False,
  13895        canRunScript=False,
  13896        noDiscard=False,
  13897        delete=False,
  13898    ):
  13899        """
  13900        override indicates whether to flag the method as override
  13901        """
  13902        assert not override or virtual
  13903        assert not (override and static)
  13904        assert not (delete and body)
  13905        self.returnType = returnType
  13906        self.args = args
  13907        self.inline = inline or bodyInHeader
  13908        self.static = static
  13909        self.virtual = virtual
  13910        self.const = const
  13911        self.bodyInHeader = bodyInHeader
  13912        self.templateArgs = templateArgs
  13913        self.body = body
  13914        self.breakAfterReturnDecl = breakAfterReturnDecl
  13915        self.breakAfterSelf = breakAfterSelf
  13916        self.override = override
  13917        self.canRunScript = canRunScript
  13918        self.noDiscard = noDiscard
  13919        self.delete = delete
  13920        ClassItem.__init__(self, name, visibility)
  13921 
  13922    def getDecorators(self, declaring):
  13923        decorators = []
  13924        if self.noDiscard:
  13925            decorators.append("[[nodiscard]]")
  13926        if self.canRunScript:
  13927            decorators.append("MOZ_CAN_RUN_SCRIPT")
  13928        if self.inline:
  13929            decorators.append("inline")
  13930        if declaring:
  13931            if self.static:
  13932                decorators.append("static")
  13933            if self.virtual and not self.override:
  13934                decorators.append("virtual")
  13935        if decorators:
  13936            return " ".join(decorators) + " "
  13937        return ""
  13938 
  13939    def getBody(self):
  13940        # Override me or pass a string to constructor
  13941        assert self.body is not None
  13942        return self.body
  13943 
  13944    def declare(self, cgClass):
  13945        templateClause = (
  13946            "template <%s>\n" % ", ".join(self.templateArgs)
  13947            if self.bodyInHeader and self.templateArgs
  13948            else ""
  13949        )
  13950        args = ", ".join([a.declare() for a in self.args])
  13951        if self.delete:
  13952            body = " = delete;\n"
  13953        elif self.bodyInHeader:
  13954            body = indent(self.getBody())
  13955            body = "\n{\n" + body + "}\n"
  13956        else:
  13957            body = ";\n"
  13958 
  13959        return fill(
  13960            "${templateClause}${decorators}${returnType}${breakAfterReturnDecl}"
  13961            "${name}(${args})${const}${override}${body}"
  13962            "${breakAfterSelf}",
  13963            templateClause=templateClause,
  13964            decorators=self.getDecorators(True),
  13965            returnType=self.returnType,
  13966            breakAfterReturnDecl=self.breakAfterReturnDecl,
  13967            name=self.name,
  13968            args=args,
  13969            const=" const" if self.const else "",
  13970            override=" override" if self.override else "",
  13971            body=body,
  13972            breakAfterSelf=self.breakAfterSelf,
  13973        )
  13974 
  13975    def define(self, cgClass):
  13976        if self.delete or self.bodyInHeader:
  13977            return ""
  13978 
  13979        templateArgs = cgClass.templateArgs
  13980        if templateArgs:
  13981            if cgClass.templateSpecialization:
  13982                templateArgs = templateArgs[len(cgClass.templateSpecialization) :]
  13983 
  13984        if templateArgs:
  13985            templateClause = "template <%s>\n" % ", ".join(
  13986                [str(a) for a in templateArgs]
  13987            )
  13988        else:
  13989            templateClause = ""
  13990 
  13991        return fill(
  13992            """
  13993            ${templateClause}${decorators}${returnType}
  13994            ${className}::${name}(${args})${const}
  13995            {
  13996              $*{body}
  13997            }
  13998            """,
  13999            templateClause=templateClause,
  14000            decorators=self.getDecorators(False),
  14001            returnType=self.returnType,
  14002            className=cgClass.getNameString(),
  14003            name=self.name,
  14004            args=", ".join([a.define() for a in self.args]),
  14005            const=" const" if self.const else "",
  14006            body=self.getBody(),
  14007        )
  14008 
  14009 
  14010 class ClassUsingDeclaration(ClassItem):
  14011    """
  14012    Used for declaring an alias for a type in a CGClass
  14013 
  14014    name is the name of the alias
  14015 
  14016    type is the type to declare an alias of
  14017 
  14018    visibility determines the visibility of the alias (public,
  14019    protected, private), defaults to public.
  14020    """
  14021 
  14022    def __init__(self, name, type, visibility="public"):
  14023        self.type = type
  14024        ClassItem.__init__(self, name, visibility)
  14025 
  14026    def declare(self, cgClass):
  14027        return "using %s = %s;\n\n" % (self.name, self.type)
  14028 
  14029    def define(self, cgClass):
  14030        return ""
  14031 
  14032 
  14033 class ClassUsingFromBaseDeclaration(ClassItem):
  14034    """
  14035    Used for importing a name from a base class into a CGClass
  14036 
  14037    baseClass is the name of the base class to import the name from
  14038 
  14039    name is the name to import
  14040 
  14041    visibility determines the visibility of the name (public,
  14042    protected, private), defaults to public.
  14043    """
  14044 
  14045    def __init__(self, baseClass, name, visibility="public"):
  14046        self.baseClass = baseClass
  14047        ClassItem.__init__(self, name, visibility)
  14048 
  14049    def declare(self, cgClass):
  14050        return "using %s::%s;\n\n" % (self.baseClass, self.name)
  14051 
  14052    def define(self, cgClass):
  14053        return ""
  14054 
  14055 
  14056 class ClassConstructor(ClassItem):
  14057    """
  14058    Used for adding a constructor to a CGClass.
  14059 
  14060    args is a list of Argument objects that are the arguments taken by the
  14061    constructor.
  14062 
  14063    inline should be True if the constructor should be marked inline.
  14064 
  14065    bodyInHeader should be True if the body should be placed in the class
  14066    declaration in the header.
  14067 
  14068    default should be True if the definition of the constructor should be
  14069    `= default;`.
  14070 
  14071    visibility determines the visibility of the constructor (public,
  14072    protected, private), defaults to private.
  14073 
  14074    explicit should be True if the constructor should be marked explicit.
  14075 
  14076    baseConstructors is a list of strings containing calls to base constructors,
  14077    defaults to None.
  14078 
  14079    body contains a string with the code for the constructor, defaults to empty.
  14080    """
  14081 
  14082    def __init__(
  14083        self,
  14084        args,
  14085        inline=False,
  14086        bodyInHeader=False,
  14087        default=False,
  14088        visibility="private",
  14089        explicit=False,
  14090        constexpr=False,
  14091        baseConstructors=None,
  14092        body="",
  14093    ):
  14094        assert not (inline and constexpr)
  14095        assert not (bodyInHeader and constexpr)
  14096        assert not (default and body)
  14097        self.args = args
  14098        self.inline = inline or bodyInHeader
  14099        self.bodyInHeader = bodyInHeader or constexpr or default
  14100        self.default = default
  14101        self.explicit = explicit
  14102        self.constexpr = constexpr
  14103        self.baseConstructors = baseConstructors or []
  14104        self.body = body
  14105        ClassItem.__init__(self, None, visibility)
  14106 
  14107    def getDecorators(self, declaring):
  14108        decorators = []
  14109        if declaring:
  14110            if self.explicit:
  14111                decorators.append("explicit")
  14112            if self.inline:
  14113                decorators.append("inline")
  14114            if self.constexpr:
  14115                decorators.append("constexpr")
  14116        if decorators:
  14117            return " ".join(decorators) + " "
  14118        return ""
  14119 
  14120    def getInitializationList(self, cgClass):
  14121        items = [str(c) for c in self.baseConstructors]
  14122        for m in cgClass.members:
  14123            if not m.static:
  14124                initialize = m.body
  14125                if initialize:
  14126                    items.append(m.name + "(" + initialize + ")")
  14127 
  14128        if len(items) > 0:
  14129            return "\n  : " + ",\n    ".join(items)
  14130        return ""
  14131 
  14132    def getBody(self):
  14133        return self.body
  14134 
  14135    def declare(self, cgClass):
  14136        args = ", ".join([a.declare() for a in self.args])
  14137        if self.bodyInHeader:
  14138            if self.default:
  14139                body = " = default;\n"
  14140            else:
  14141                body = (
  14142                    self.getInitializationList(cgClass)
  14143                    + "\n{\n"
  14144                    + indent(self.getBody())
  14145                    + "}\n"
  14146                )
  14147        else:
  14148            body = ";\n"
  14149 
  14150        return fill(
  14151            "${decorators}${className}(${args})${body}\n",
  14152            decorators=self.getDecorators(True),
  14153            className=cgClass.getNameString(),
  14154            args=args,
  14155            body=body,
  14156        )
  14157 
  14158    def define(self, cgClass):
  14159        if self.bodyInHeader:
  14160            return ""
  14161 
  14162        return fill(
  14163            """
  14164            ${decorators}
  14165            ${className}::${className}(${args})${initializationList}
  14166            {
  14167              $*{body}
  14168            }
  14169            """,
  14170            decorators=self.getDecorators(False),
  14171            className=cgClass.getNameString(),
  14172            args=", ".join([a.define() for a in self.args]),
  14173            initializationList=self.getInitializationList(cgClass),
  14174            body=self.getBody(),
  14175        )
  14176 
  14177 
  14178 class ClassDestructor(ClassItem):
  14179    """
  14180    Used for adding a destructor to a CGClass.
  14181 
  14182    inline should be True if the destructor should be marked inline.
  14183 
  14184    bodyInHeader should be True if the body should be placed in the class
  14185    declaration in the header.
  14186 
  14187    visibility determines the visibility of the destructor (public,
  14188    protected, private), defaults to private.
  14189 
  14190    body contains a string with the code for the destructor, defaults to empty.
  14191 
  14192    virtual determines whether the destructor is virtual, defaults to False.
  14193    """
  14194 
  14195    def __init__(
  14196        self,
  14197        inline=False,
  14198        bodyInHeader=False,
  14199        visibility="private",
  14200        body="",
  14201        virtual=False,
  14202    ):
  14203        self.inline = inline or bodyInHeader
  14204        self.bodyInHeader = bodyInHeader
  14205        self.body = body
  14206        self.virtual = virtual
  14207        ClassItem.__init__(self, None, visibility)
  14208 
  14209    def getDecorators(self, declaring):
  14210        decorators = []
  14211        if self.virtual and declaring:
  14212            decorators.append("virtual")
  14213        if self.inline and declaring:
  14214            decorators.append("inline")
  14215        if decorators:
  14216            return " ".join(decorators) + " "
  14217        return ""
  14218 
  14219    def getBody(self):
  14220        return self.body
  14221 
  14222    def declare(self, cgClass):
  14223        if self.bodyInHeader:
  14224            body = "\n{\n" + indent(self.getBody()) + "}\n"
  14225        else:
  14226            body = ";\n"
  14227 
  14228        return fill(
  14229            "${decorators}~${className}()${body}\n",
  14230            decorators=self.getDecorators(True),
  14231            className=cgClass.getNameString(),
  14232            body=body,
  14233        )
  14234 
  14235    def define(self, cgClass):
  14236        if self.bodyInHeader:
  14237            return ""
  14238        return fill(
  14239            """
  14240            ${decorators}
  14241            ${className}::~${className}()
  14242            {
  14243              $*{body}
  14244            }
  14245            """,
  14246            decorators=self.getDecorators(False),
  14247            className=cgClass.getNameString(),
  14248            body=self.getBody(),
  14249        )
  14250 
  14251 
  14252 class ClassMember(ClassItem):
  14253    def __init__(
  14254        self,
  14255        name,
  14256        type,
  14257        visibility="private",
  14258        static=False,
  14259        body=None,
  14260        hasIgnoreInitCheckFlag=False,
  14261    ):
  14262        self.type = type
  14263        self.static = static
  14264        self.body = body
  14265        self.hasIgnoreInitCheckFlag = hasIgnoreInitCheckFlag
  14266        ClassItem.__init__(self, name, visibility)
  14267 
  14268    def declare(self, cgClass):
  14269        return "%s%s%s %s;\n" % (
  14270            "static " if self.static else "",
  14271            "MOZ_INIT_OUTSIDE_CTOR " if self.hasIgnoreInitCheckFlag else "",
  14272            self.type,
  14273            self.name,
  14274        )
  14275 
  14276    def define(self, cgClass):
  14277        if not self.static:
  14278            return ""
  14279        if self.body:
  14280            body = " = " + self.body
  14281        else:
  14282            body = ""
  14283        return "%s %s::%s%s;\n" % (self.type, cgClass.getNameString(), self.name, body)
  14284 
  14285 
  14286 class ClassEnum(ClassItem):
  14287    def __init__(
  14288        self, name, entries, values=None, visibility="public", enumClass=False
  14289    ):
  14290        self.entries = entries
  14291        self.values = values
  14292        self.enumClass = enumClass
  14293        ClassItem.__init__(self, name, visibility)
  14294 
  14295    def declare(self, cgClass):
  14296        entries = []
  14297        for i in range(0, len(self.entries)):
  14298            if not self.values or i >= len(self.values):
  14299                entry = "%s" % self.entries[i]
  14300            else:
  14301                entry = "%s = %s" % (self.entries[i], self.values[i])
  14302            entries.append(entry)
  14303 
  14304        decl = ["enum"]
  14305        self.enumClass and decl.append("class")
  14306        self.name and decl.append(self.name)
  14307        decl = " ".join(decl)
  14308 
  14309        return "%s\n{\n%s\n};\n" % (decl, indent(",\n".join(entries)))
  14310 
  14311    def define(self, cgClass):
  14312        # Only goes in the header
  14313        return ""
  14314 
  14315 
  14316 class ClassUnion(ClassItem):
  14317    def __init__(self, name, entries, visibility="public"):
  14318        self.entries = [entry + ";\n" for entry in entries]
  14319        ClassItem.__init__(self, name, visibility)
  14320 
  14321    def declare(self, cgClass):
  14322        return "union %s\n{\n%s\n};\n" % (self.name, indent("".join(self.entries)))
  14323 
  14324    def define(self, cgClass):
  14325        # Only goes in the header
  14326        return ""
  14327 
  14328 
  14329 class ClassGroup(ClassItem):
  14330    def __init__(self, items):
  14331        self.items = items
  14332        ClassItem.__init__(self, "", items[0].visibility)
  14333 
  14334    def declare(self, cgClass):
  14335        assert False
  14336 
  14337    def define(self, cgClass):
  14338        assert False
  14339 
  14340 
  14341 class CGClass(CGThing):
  14342    def __init__(
  14343        self,
  14344        name,
  14345        bases=[],
  14346        typeAliases=[],
  14347        members=[],
  14348        constructors=[],
  14349        destructor=None,
  14350        methods=[],
  14351        enums=[],
  14352        unions=[],
  14353        templateArgs=[],
  14354        templateSpecialization=[],
  14355        isStruct=False,
  14356        disallowCopyConstruction=False,
  14357        indent="",
  14358        decorators="",
  14359        extradeclarations="",
  14360        extradefinitions="",
  14361    ):
  14362        CGThing.__init__(self)
  14363        self.name = name
  14364        self.bases = bases
  14365        self.typeAliases = typeAliases
  14366        self.members = members
  14367        self.constructors = constructors
  14368        # We store our single destructor in a list, since all of our
  14369        # code wants lists of members.
  14370        self.destructors = [destructor] if destructor else []
  14371        self.methods = methods
  14372        self.enums = enums
  14373        self.unions = unions
  14374        self.templateArgs = templateArgs
  14375        self.templateSpecialization = templateSpecialization
  14376        self.isStruct = isStruct
  14377        self.disallowCopyConstruction = disallowCopyConstruction
  14378        self.indent = indent
  14379        self.defaultVisibility = "public" if isStruct else "private"
  14380        self.decorators = decorators
  14381        self.extradeclarations = extradeclarations
  14382        self.extradefinitions = extradefinitions
  14383 
  14384    def getNameString(self):
  14385        className = self.name
  14386        if self.templateSpecialization:
  14387            className += "<%s>" % ", ".join(
  14388                [str(a) for a in self.templateSpecialization]
  14389            )
  14390        return className
  14391 
  14392    @staticmethod
  14393    def flattenClassItemLists(l):
  14394        for item in l:
  14395            if isinstance(item, ClassGroup):
  14396                for inner in CGClass.flattenClassItemLists(item.items):
  14397                    yield inner
  14398            else:
  14399                yield item
  14400 
  14401    def declare(self):
  14402        result = ""
  14403        if self.templateArgs:
  14404            templateArgs = [a.declare() for a in self.templateArgs]
  14405            templateArgs = templateArgs[len(self.templateSpecialization) :]
  14406            result += "template <%s>\n" % ",".join([str(a) for a in templateArgs])
  14407 
  14408        type = "struct" if self.isStruct else "class"
  14409 
  14410        if self.templateSpecialization:
  14411            specialization = "<%s>" % ", ".join(
  14412                [str(a) for a in self.templateSpecialization]
  14413            )
  14414        else:
  14415            specialization = ""
  14416 
  14417        myself = "%s %s%s" % (type, self.name, specialization)
  14418        if self.decorators != "":
  14419            myself += " " + self.decorators
  14420        result += myself
  14421 
  14422        if self.bases:
  14423            inherit = " : "
  14424            result += inherit
  14425            # Grab our first base
  14426            baseItems = [CGGeneric(b.declare(self)) for b in self.bases]
  14427            bases = baseItems[:1]
  14428            # Indent the rest
  14429            bases.extend(
  14430                CGIndenter(b, len(myself) + len(inherit)) for b in baseItems[1:]
  14431            )
  14432            result += ",\n".join(b.define() for b in bases)
  14433 
  14434        result += "\n{\n"
  14435 
  14436        result += self.extradeclarations
  14437 
  14438        def declareMembers(cgClass, memberList, defaultVisibility):
  14439            members = {"private": [], "protected": [], "public": []}
  14440 
  14441            for member in memberList:
  14442                members[member.visibility].append(member)
  14443 
  14444            if defaultVisibility == "public":
  14445                order = ["public", "protected", "private"]
  14446            else:
  14447                order = ["private", "protected", "public"]
  14448 
  14449            result = ""
  14450 
  14451            lastVisibility = defaultVisibility
  14452            for visibility in order:
  14453                list = members[visibility]
  14454                if list:
  14455                    for member in self.flattenClassItemLists(list):
  14456                        if member.visibility != lastVisibility:
  14457                            result += member.visibility + ":\n"
  14458                        result += indent(member.declare(cgClass))
  14459                        lastVisibility = member.visibility
  14460            return (result, lastVisibility)
  14461 
  14462        if self.disallowCopyConstruction:
  14463 
  14464            class DisallowedCopyConstructor(object):
  14465                def __init__(self):
  14466                    self.visibility = "private"
  14467 
  14468                def declare(self, cgClass):
  14469                    name = cgClass.getNameString()
  14470                    return (
  14471                        "%s(const %s&) = delete;\n"
  14472                        "%s& operator=(const %s&) = delete;\n"
  14473                        % (name, name, name, name)
  14474                    )
  14475 
  14476            disallowedCopyConstructors = [DisallowedCopyConstructor()]
  14477        else:
  14478            disallowedCopyConstructors = []
  14479 
  14480        order = [
  14481            self.typeAliases,
  14482            self.enums,
  14483            self.unions,
  14484            self.members,
  14485            self.constructors + disallowedCopyConstructors,
  14486            self.destructors,
  14487            self.methods,
  14488        ]
  14489 
  14490        lastVisibility = self.defaultVisibility
  14491        pieces = []
  14492        for memberList in order:
  14493            code, lastVisibility = declareMembers(self, memberList, lastVisibility)
  14494 
  14495            if code:
  14496                code = code.rstrip() + "\n"  # remove extra blank lines at the end
  14497                pieces.append(code)
  14498 
  14499        result += "\n".join(pieces)
  14500        result += "};\n"
  14501        result = indent(result, len(self.indent))
  14502        return result
  14503 
  14504    def define(self):
  14505        def defineMembers(cgClass, memberList, itemCount, separator=""):
  14506            result = ""
  14507            for member in self.flattenClassItemLists(memberList):
  14508                if itemCount != 0:
  14509                    result = result + separator
  14510                definition = member.define(cgClass)
  14511                if definition:
  14512                    # Member variables would only produce empty lines here.
  14513                    result += definition
  14514                    itemCount += 1
  14515            return (result, itemCount)
  14516 
  14517        order = [
  14518            (self.members, ""),
  14519            (self.constructors, "\n"),
  14520            (self.destructors, "\n"),
  14521            (self.methods, "\n"),
  14522        ]
  14523 
  14524        result = self.extradefinitions
  14525        itemCount = 0
  14526        for memberList, separator in order:
  14527            memberString, itemCount = defineMembers(
  14528                self, memberList, itemCount, separator
  14529            )
  14530            result = result + memberString
  14531        return result
  14532 
  14533 
  14534 class CGResolveOwnPropertyViaResolve(CGAbstractBindingMethod):
  14535    """
  14536    An implementation of Xray ResolveOwnProperty stuff for things that have a
  14537    resolve hook.
  14538    """
  14539 
  14540    def __init__(self, descriptor):
  14541        args = [
  14542            Argument("JSContext*", "cx"),
  14543            Argument("JS::Handle<JSObject*>", "wrapper"),
  14544            Argument("JS::Handle<JSObject*>", "obj"),
  14545            Argument("JS::Handle<jsid>", "id"),
  14546            Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"),
  14547        ]
  14548        CGAbstractBindingMethod.__init__(
  14549            self,
  14550            descriptor,
  14551            "ResolveOwnPropertyViaResolve",
  14552            args,
  14553            getThisObj="",
  14554            callArgs="",
  14555        )
  14556 
  14557    def generate_code(self):
  14558        return CGGeneric(
  14559            dedent(
  14560                """
  14561            {
  14562              // Since we're dealing with an Xray, do the resolve on the
  14563              // underlying object first.  That gives it a chance to
  14564              // define properties on the actual object as needed, and
  14565              // then use the fact that it created the objects as a flag
  14566              // to avoid re-resolving the properties if someone deletes
  14567              // them.
  14568              JSAutoRealm ar(cx, obj);
  14569              JS_MarkCrossZoneId(cx, id);
  14570              JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> objDesc(cx);
  14571              if (!self->DoResolve(cx, obj, id, &objDesc)) {
  14572                return false;
  14573              }
  14574              // If desc->value() is undefined, then the DoResolve call
  14575              // has already defined the property on the object.  Don't
  14576              // try to also define it.
  14577              if (objDesc.isSome() &&
  14578                  !objDesc->value().isUndefined()) {
  14579                JS::Rooted<JS::PropertyDescriptor> defineDesc(cx, *objDesc);
  14580                if (!JS_DefinePropertyById(cx, obj, id, defineDesc)) {
  14581                  return false;
  14582                }
  14583              }
  14584            }
  14585            return self->DoResolve(cx, wrapper, id, desc);
  14586            """
  14587            )
  14588        )
  14589 
  14590 
  14591 class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod):
  14592    """
  14593    An implementation of Xray EnumerateOwnProperties stuff for things
  14594    that have a resolve hook.
  14595    """
  14596 
  14597    def __init__(self, descriptor):
  14598        args = [
  14599            Argument("JSContext*", "cx"),
  14600            Argument("JS::Handle<JSObject*>", "wrapper"),
  14601            Argument("JS::Handle<JSObject*>", "obj"),
  14602            Argument("JS::MutableHandleVector<jsid>", "props"),
  14603        ]
  14604        CGAbstractBindingMethod.__init__(
  14605            self,
  14606            descriptor,
  14607            "EnumerateOwnPropertiesViaGetOwnPropertyNames",
  14608            args,
  14609            getThisObj="",
  14610            callArgs="",
  14611        )
  14612 
  14613    def generate_code(self):
  14614        return CGGeneric(
  14615            dedent(
  14616                """
  14617            FastErrorResult rv;
  14618            // This wants all own props, not just enumerable ones.
  14619            self->GetOwnPropertyNames(cx, props, false, rv);
  14620            if (rv.MaybeSetPendingException(cx)) {
  14621              return false;
  14622            }
  14623            return true;
  14624            """
  14625            )
  14626        )
  14627 
  14628 
  14629 class CGPrototypeTraitsClass(CGClass):
  14630    def __init__(self, descriptor, indent=""):
  14631        templateArgs = [Argument("prototypes::ID", "PrototypeID")]
  14632        templateSpecialization = ["prototypes::id::" + descriptor.name]
  14633        enums = [ClassEnum("", ["Depth"], [descriptor.interface.inheritanceDepth()])]
  14634        CGClass.__init__(
  14635            self,
  14636            "PrototypeTraits",
  14637            indent=indent,
  14638            templateArgs=templateArgs,
  14639            templateSpecialization=templateSpecialization,
  14640            enums=enums,
  14641            isStruct=True,
  14642        )
  14643 
  14644    def deps(self):
  14645        return set()
  14646 
  14647 
  14648 class CGClassForwardDeclare(CGThing):
  14649    def __init__(self, name, isStruct=False):
  14650        CGThing.__init__(self)
  14651        self.name = name
  14652        self.isStruct = isStruct
  14653 
  14654    def declare(self):
  14655        type = "struct" if self.isStruct else "class"
  14656        return "%s %s;\n" % (type, self.name)
  14657 
  14658    def define(self):
  14659        # Header only
  14660        return ""
  14661 
  14662    def forward_declare(self):
  14663        return self.declare()
  14664 
  14665    def deps(self):
  14666        return set()
  14667 
  14668 
  14669 class CGProxySpecialOperation(CGPerSignatureCall):
  14670    """
  14671    Base class for classes for calling an indexed or named special operation
  14672    (don't use this directly, use the derived classes below).
  14673 
  14674    If checkFound is False, will just assert that the prop is found instead of
  14675    checking that it is before wrapping the value.
  14676 
  14677    resultVar: See the docstring for CGCallGenerator.
  14678 
  14679    foundVar: For getters and deleters, the generated code can also set a bool
  14680    variable, declared by the caller, if the given indexed or named property
  14681    already existed. If the caller wants this, it should pass the name of the
  14682    bool variable as the foundVar keyword argument to the constructor. The
  14683    caller is responsible for declaring the variable and initializing it to
  14684    false.
  14685    """
  14686 
  14687    def __init__(
  14688        self,
  14689        descriptor,
  14690        operation,
  14691        checkFound=True,
  14692        argumentHandleValue=None,
  14693        resultVar=None,
  14694        foundVar=None,
  14695    ):
  14696        self.checkFound = checkFound
  14697        self.foundVar = foundVar or "found"
  14698 
  14699        nativeName = MakeNativeName(descriptor.binaryNameFor(operation, False))
  14700        operation = descriptor.operations[operation]
  14701        assert len(operation.signatures()) == 1
  14702        signature = operation.signatures()[0]
  14703 
  14704        returnType, arguments = signature
  14705 
  14706        # We pass len(arguments) as the final argument so that the
  14707        # CGPerSignatureCall won't do any argument conversion of its own.
  14708        CGPerSignatureCall.__init__(
  14709            self,
  14710            returnType,
  14711            arguments,
  14712            nativeName,
  14713            False,
  14714            descriptor,
  14715            operation,
  14716            len(arguments),
  14717            resultVar=resultVar,
  14718            objectName="proxy",
  14719        )
  14720 
  14721        if operation.isSetter():
  14722            # arguments[0] is the index or name of the item that we're setting.
  14723            argument = arguments[1]
  14724            info = getJSToNativeConversionInfo(
  14725                argument.type,
  14726                descriptor,
  14727                sourceDescription=(
  14728                    "value being assigned to %s setter"
  14729                    % descriptor.interface.identifier.name
  14730                ),
  14731            )
  14732            if argumentHandleValue is None:
  14733                argumentHandleValue = "desc.value()"
  14734            rootedValue = fill(
  14735                """
  14736                JS::Rooted<JS::Value> rootedValue(cx, ${argumentHandleValue});
  14737                """,
  14738                argumentHandleValue=argumentHandleValue,
  14739            )
  14740            templateValues = {
  14741                "declName": argument.identifier.name,
  14742                "holderName": argument.identifier.name + "_holder",
  14743                "val": argumentHandleValue,
  14744                "maybeMutableVal": "&rootedValue",
  14745                "obj": "obj",
  14746                "passedToJSImpl": "false",
  14747            }
  14748            self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
  14749            # rootedValue needs to come before the conversion, so we
  14750            # need to prepend it last.
  14751            self.cgRoot.prepend(CGGeneric(rootedValue))
  14752        elif operation.isGetter() or operation.isDeleter():
  14753            if foundVar is None:
  14754                self.cgRoot.prepend(CGGeneric("bool found = false;\n"))
  14755 
  14756    def getArguments(self):
  14757        args = [(a, a.identifier.name) for a in self.arguments]
  14758        if self.idlNode.isGetter() or self.idlNode.isDeleter():
  14759            args.append(
  14760                (
  14761                    FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean]),
  14762                    self.foundVar,
  14763                )
  14764            )
  14765        return args
  14766 
  14767    def wrap_return_value(self):
  14768        if not self.idlNode.isGetter() or self.templateValues is None:
  14769            return ""
  14770 
  14771        wrap = CGGeneric(
  14772            wrapForType(self.returnType, self.descriptor, self.templateValues)
  14773        )
  14774        if self.checkFound:
  14775            wrap = CGIfWrapper(wrap, self.foundVar)
  14776        else:
  14777            wrap = CGList([CGGeneric("MOZ_ASSERT(" + self.foundVar + ");\n"), wrap])
  14778        return "\n" + wrap.define()
  14779 
  14780 
  14781 class CGProxyIndexedOperation(CGProxySpecialOperation):
  14782    """
  14783    Class to generate a call to an indexed operation.
  14784 
  14785    If doUnwrap is False, the caller is responsible for making sure a variable
  14786    named 'self' holds the C++ object somewhere where the code we generate
  14787    will see it.
  14788 
  14789    If checkFound is False, will just assert that the prop is found instead of
  14790    checking that it is before wrapping the value.
  14791 
  14792    resultVar: See the docstring for CGCallGenerator.
  14793 
  14794    foundVar: See the docstring for CGProxySpecialOperation.
  14795    """
  14796 
  14797    def __init__(
  14798        self,
  14799        descriptor,
  14800        name,
  14801        doUnwrap=True,
  14802        checkFound=True,
  14803        argumentHandleValue=None,
  14804        resultVar=None,
  14805        foundVar=None,
  14806    ):
  14807        self.doUnwrap = doUnwrap
  14808        CGProxySpecialOperation.__init__(
  14809            self,
  14810            descriptor,
  14811            name,
  14812            checkFound,
  14813            argumentHandleValue=argumentHandleValue,
  14814            resultVar=resultVar,
  14815            foundVar=foundVar,
  14816        )
  14817 
  14818    def define(self):
  14819        # Our first argument is the id we're getting.
  14820        argName = self.arguments[0].identifier.name
  14821        if argName == "index":
  14822            # We already have our index in a variable with that name
  14823            setIndex = ""
  14824        else:
  14825            setIndex = "uint32_t %s = index;\n" % argName
  14826        if self.doUnwrap:
  14827            unwrap = "%s* self = UnwrapProxy(proxy);\n" % self.descriptor.nativeType
  14828        else:
  14829            unwrap = ""
  14830        return setIndex + unwrap + CGProxySpecialOperation.define(self)
  14831 
  14832 
  14833 class CGProxyIndexedGetter(CGProxyIndexedOperation):
  14834    """
  14835    Class to generate a call to an indexed getter. If templateValues is not None
  14836    the returned value will be wrapped with wrapForType using templateValues.
  14837 
  14838    If doUnwrap is False, the caller is responsible for making sure a variable
  14839    named 'self' holds the C++ object somewhere where the code we generate
  14840    will see it.
  14841 
  14842    If checkFound is False, will just assert that the prop is found instead of
  14843    checking that it is before wrapping the value.
  14844 
  14845    foundVar: See the docstring for CGProxySpecialOperation.
  14846    """
  14847 
  14848    def __init__(
  14849        self,
  14850        descriptor,
  14851        templateValues=None,
  14852        doUnwrap=True,
  14853        checkFound=True,
  14854        foundVar=None,
  14855    ):
  14856        self.templateValues = templateValues
  14857        CGProxyIndexedOperation.__init__(
  14858            self, descriptor, "IndexedGetter", doUnwrap, checkFound, foundVar=foundVar
  14859        )
  14860 
  14861 
  14862 class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter):
  14863    """
  14864    Class to generate a call that checks whether an indexed property exists.
  14865 
  14866    For now, we just delegate to CGProxyIndexedGetter
  14867 
  14868    foundVar: See the docstring for CGProxySpecialOperation.
  14869    """
  14870 
  14871    def __init__(self, descriptor, foundVar):
  14872        CGProxyIndexedGetter.__init__(self, descriptor, foundVar=foundVar)
  14873        self.cgRoot.append(CGGeneric("(void)result;\n"))
  14874 
  14875 
  14876 class CGProxyIndexedSetter(CGProxyIndexedOperation):
  14877    """
  14878    Class to generate a call to an indexed setter.
  14879    """
  14880 
  14881    def __init__(self, descriptor, argumentHandleValue=None):
  14882        CGProxyIndexedOperation.__init__(
  14883            self, descriptor, "IndexedSetter", argumentHandleValue=argumentHandleValue
  14884        )
  14885 
  14886 
  14887 class CGProxyNamedOperation(CGProxySpecialOperation):
  14888    """
  14889    Class to generate a call to a named operation.
  14890 
  14891    'value' is the jsval to use for the name; None indicates that it should be
  14892    gotten from the property id.
  14893 
  14894    resultVar: See the docstring for CGCallGenerator.
  14895 
  14896    foundVar: See the docstring for CGProxySpecialOperation.
  14897 
  14898    tailCode: if we end up with a non-symbol string id, run this code after
  14899              we do all our other work.
  14900    """
  14901 
  14902    def __init__(
  14903        self,
  14904        descriptor,
  14905        name,
  14906        value=None,
  14907        argumentHandleValue=None,
  14908        resultVar=None,
  14909        foundVar=None,
  14910        tailCode="",
  14911    ):
  14912        CGProxySpecialOperation.__init__(
  14913            self,
  14914            descriptor,
  14915            name,
  14916            argumentHandleValue=argumentHandleValue,
  14917            resultVar=resultVar,
  14918            foundVar=foundVar,
  14919        )
  14920        self.value = value
  14921        self.tailCode = tailCode
  14922 
  14923    def define(self):
  14924        # Our first argument is the id we're getting.
  14925        argName = self.arguments[0].identifier.name
  14926        if argName == "id":
  14927            # deal with the name collision
  14928            decls = "JS::Rooted<jsid> id_(cx, id);\n"
  14929            idName = "id_"
  14930        else:
  14931            decls = ""
  14932            idName = "id"
  14933 
  14934        decls += "FakeString<char16_t> %s;\n" % argName
  14935 
  14936        main = fill(
  14937            """
  14938            ${nativeType}* self = UnwrapProxy(proxy);
  14939            $*{op}
  14940            $*{tailCode}
  14941            """,
  14942            nativeType=self.descriptor.nativeType,
  14943            op=CGProxySpecialOperation.define(self),
  14944            tailCode=self.tailCode,
  14945        )
  14946 
  14947        if self.value is None:
  14948            return fill(
  14949                """
  14950                $*{decls}
  14951                bool isSymbol;
  14952                if (!ConvertIdToString(cx, ${idName}, ${argName}, isSymbol)) {
  14953                  return false;
  14954                }
  14955                if (!isSymbol) {
  14956                  $*{main}
  14957                }
  14958                """,
  14959                decls=decls,
  14960                idName=idName,
  14961                argName=argName,
  14962                main=main,
  14963            )
  14964 
  14965        # Sadly, we have to set up nameVal even if we have an atom id,
  14966        # because we don't know for sure, and we can end up needing it
  14967        # so it needs to be higher up the stack.  Using a Maybe here
  14968        # seems like probable overkill.
  14969        return fill(
  14970            """
  14971            $*{decls}
  14972            JS::Rooted<JS::Value> nameVal(cx, ${value});
  14973            if (!nameVal.isSymbol()) {
  14974              if (!ConvertJSValueToString(cx, nameVal, eStringify, eStringify,
  14975                                          ${argName})) {
  14976                return false;
  14977              }
  14978              $*{main}
  14979            }
  14980            """,
  14981            decls=decls,
  14982            value=self.value,
  14983            argName=argName,
  14984            main=main,
  14985        )
  14986 
  14987 
  14988 class CGProxyNamedGetter(CGProxyNamedOperation):
  14989    """
  14990    Class to generate a call to an named getter. If templateValues is not None
  14991    the returned value will be wrapped with wrapForType using templateValues.
  14992    'value' is the jsval to use for the name; None indicates that it should be
  14993    gotten from the property id.
  14994 
  14995    foundVar: See the docstring for CGProxySpecialOperation.
  14996    """
  14997 
  14998    def __init__(self, descriptor, templateValues=None, value=None, foundVar=None):
  14999        self.templateValues = templateValues
  15000        CGProxyNamedOperation.__init__(
  15001            self, descriptor, "NamedGetter", value, foundVar=foundVar
  15002        )
  15003 
  15004 
  15005 class CGProxyNamedPresenceChecker(CGProxyNamedGetter):
  15006    """
  15007    Class to generate a call that checks whether a named property exists.
  15008 
  15009    For now, we just delegate to CGProxyNamedGetter
  15010 
  15011    foundVar: See the docstring for CGProxySpecialOperation.
  15012    """
  15013 
  15014    def __init__(self, descriptor, foundVar=None):
  15015        CGProxyNamedGetter.__init__(self, descriptor, foundVar=foundVar)
  15016        self.cgRoot.append(CGGeneric("(void)result;\n"))
  15017 
  15018 
  15019 class CGProxyNamedSetter(CGProxyNamedOperation):
  15020    """
  15021    Class to generate a call to a named setter.
  15022    """
  15023 
  15024    def __init__(self, descriptor, tailCode, argumentHandleValue=None):
  15025        CGProxyNamedOperation.__init__(
  15026            self,
  15027            descriptor,
  15028            "NamedSetter",
  15029            argumentHandleValue=argumentHandleValue,
  15030            tailCode=tailCode,
  15031        )
  15032 
  15033 
  15034 class CGProxyNamedDeleter(CGProxyNamedOperation):
  15035    """
  15036    Class to generate a call to a named deleter.
  15037 
  15038    resultVar: See the docstring for CGCallGenerator.
  15039 
  15040    foundVar: See the docstring for CGProxySpecialOperation.
  15041    """
  15042 
  15043    def __init__(self, descriptor, resultVar=None, foundVar=None):
  15044        CGProxyNamedOperation.__init__(
  15045            self, descriptor, "NamedDeleter", resultVar=resultVar, foundVar=foundVar
  15046        )
  15047 
  15048 
  15049 class CGProxyIsProxy(CGAbstractMethod):
  15050    def __init__(self, descriptor):
  15051        args = [Argument("JSObject*", "obj")]
  15052        CGAbstractMethod.__init__(
  15053            self, descriptor, "IsProxy", "bool", args, alwaysInline=True
  15054        )
  15055 
  15056    def declare(self):
  15057        return ""
  15058 
  15059    def definition_body(self):
  15060        return "return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();\n"
  15061 
  15062 
  15063 class CGProxyUnwrap(CGAbstractMethod):
  15064    def __init__(self, descriptor):
  15065        args = [Argument("JSObject*", "obj")]
  15066        CGAbstractMethod.__init__(
  15067            self,
  15068            descriptor,
  15069            "UnwrapProxy",
  15070            descriptor.nativeType + "*",
  15071            args,
  15072            alwaysInline=True,
  15073        )
  15074 
  15075    def declare(self):
  15076        return ""
  15077 
  15078    def definition_body(self):
  15079        return fill(
  15080            """
  15081            MOZ_ASSERT(js::IsProxy(obj));
  15082            if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) {
  15083              MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj));
  15084              obj = js::UncheckedUnwrap(obj);
  15085            }
  15086            MOZ_ASSERT(IsProxy(obj));
  15087            return static_cast<${type}*>(js::GetProxyReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate());
  15088            """,
  15089            type=self.descriptor.nativeType,
  15090        )
  15091 
  15092 
  15093 MISSING_PROP_PREF = "dom.missing_prop_counters.enabled"
  15094 
  15095 
  15096 def missingPropUseCountersForDescriptor(desc):
  15097    if not desc.needsMissingPropUseCounters:
  15098        return ""
  15099 
  15100    return fill(
  15101        """
  15102        if (StaticPrefs::${pref}() && id.isAtom()) {
  15103          CountMaybeMissingProperty(proxy, id);
  15104        }
  15105 
  15106        """,
  15107        pref=prefIdentifier(MISSING_PROP_PREF),
  15108    )
  15109 
  15110 
  15111 def findAncestorWithInstrumentedProps(desc):
  15112    """
  15113    Find an ancestor of desc.interface (not including desc.interface
  15114    itself) that has instrumented properties on it.  May return None
  15115    if there is no such ancestor.
  15116    """
  15117    ancestor = desc.interface.parent
  15118    while ancestor:
  15119        if ancestor.getExtendedAttribute("InstrumentedProps"):
  15120            return ancestor
  15121        ancestor = ancestor.parent
  15122    return None
  15123 
  15124 
  15125 class CGAbstractPropertySwitchMethod(CGAbstractMethod):
  15126    def __init__(self, *args):
  15127        """
  15128        Create an optimized switch tree for matching (property) names.
  15129        """
  15130 
  15131        CGAbstractMethod.__init__(self, *args)
  15132 
  15133    def gen_switch(self, switchDecriptor):
  15134        """
  15135        Generate a switch from the switch descriptor.  The descriptor
  15136        dictionary must have the following properties:
  15137 
  15138        1) A "precondition" property that contains code to run before the
  15139           switch statement.  Its value is a string.
  15140        2) A "condition" property for the condition.  Its value is a string.
  15141        3) A "cases" property.  Its value is an object that has property names
  15142           corresponding to the case labels.  The values of those properties
  15143           are either new switch descriptor dictionaries (which will then
  15144           generate nested switches) or strings to use for case bodies.
  15145        """
  15146        cases = []
  15147        for label, body in sorted(switchDecriptor["cases"].items()):
  15148            if isinstance(body, dict):
  15149                body = self.gen_switch(body)
  15150            cases.append(
  15151                fill(
  15152                    """
  15153                case ${label}: {
  15154                  $*{body}
  15155                  break;
  15156                }
  15157                """,
  15158                    label=label,
  15159                    body=body,
  15160                )
  15161            )
  15162        return fill(
  15163            """
  15164            $*{precondition}
  15165            switch (${condition}) {
  15166              $*{cases}
  15167            }
  15168            """,
  15169            precondition=switchDecriptor["precondition"],
  15170            condition=switchDecriptor["condition"],
  15171            cases="".join(cases),
  15172        )
  15173 
  15174    def charSwitch(
  15175        self, props, charIndex, initializeChars, charIndexer, propertyMatcher
  15176    ):
  15177        """
  15178        Create a switch for the given props, based on the first char where
  15179        they start to differ at index charIndex or more.
  15180 
  15181        initializeChars is used for initializing the characters should be matched.
  15182        charIndexer is used for matching a specific character. Must return a single char.
  15183        propertyMatcher checks whether the given string really matches the property name.
  15184 
  15185        Incoming props should be a sorted list.
  15186        """
  15187        if len(props) == 1:
  15188            # We're down to one string: just check whether we match it.
  15189            return propertyMatcher(props[0])
  15190 
  15191        switch = dict()
  15192        if charIndex == 0:
  15193            switch["precondition"] = initializeChars
  15194        else:
  15195            switch["precondition"] = ""
  15196 
  15197        # Find the first place where we might actually have a difference.
  15198        while all(prop[charIndex] == props[0][charIndex] for prop in props):
  15199            charIndex += 1
  15200 
  15201        switch["condition"] = charIndexer(charIndex)
  15202        switch["cases"] = dict()
  15203        current_props = None
  15204        curChar = None
  15205        idx = 0
  15206        while idx < len(props):
  15207            nextChar = "'%s'" % props[idx][charIndex]
  15208            if nextChar != curChar:
  15209                if curChar:
  15210                    switch["cases"][curChar] = self.charSwitch(
  15211                        current_props,
  15212                        charIndex + 1,
  15213                        initializeChars,
  15214                        charIndexer,
  15215                        propertyMatcher,
  15216                    )
  15217                current_props = []
  15218                curChar = nextChar
  15219            current_props.append(props[idx])
  15220            idx += 1
  15221        switch["cases"][curChar] = self.charSwitch(
  15222            current_props, charIndex + 1, initializeChars, charIndexer, propertyMatcher
  15223        )
  15224        return switch
  15225 
  15226 
  15227 class CGCountMaybeMissingProperty(CGAbstractPropertySwitchMethod):
  15228    def __init__(self, descriptor):
  15229        """
  15230        Returns whether we counted the property involved.
  15231        """
  15232        CGAbstractPropertySwitchMethod.__init__(
  15233            self,
  15234            descriptor,
  15235            "CountMaybeMissingProperty",
  15236            "bool",
  15237            [
  15238                Argument("JS::Handle<JSObject*>", "proxy"),
  15239                Argument("JS::Handle<jsid>", "id"),
  15240            ],
  15241        )
  15242 
  15243    def definition_body(self):
  15244        ancestor = findAncestorWithInstrumentedProps(self.descriptor)
  15245 
  15246        if ancestor:
  15247            body = fill(
  15248                """
  15249                if (${ancestor}_Binding::CountMaybeMissingProperty(proxy, id)) {
  15250                  return true;
  15251                }
  15252 
  15253                """,
  15254                ancestor=ancestor.identifier.name,
  15255            )
  15256        else:
  15257            body = ""
  15258 
  15259        instrumentedProps = self.descriptor.instrumentedProps
  15260        if not instrumentedProps:
  15261            return body + dedent(
  15262                """
  15263                return false;
  15264                """
  15265            )
  15266 
  15267        initializeChars = "StringIdChars chars(nogc, str);\n"
  15268        charIndexer = lambda index: "chars[%d]" % index
  15269 
  15270        def property_matcher(name):
  15271            return fill(
  15272                """
  15273                if (JS_LinearStringEqualsLiteral(str, "${name}")) {
  15274                  counter.emplace(eUseCounter_${iface}_${name});
  15275                }
  15276                """,
  15277                iface=self.descriptor.name,
  15278                name=name,
  15279            )
  15280 
  15281        lengths = set(len(prop) for prop in instrumentedProps)
  15282        switchDesc = {"condition": "JS::GetLinearStringLength(str)", "precondition": ""}
  15283        switchDesc["cases"] = dict()
  15284        for length in sorted(lengths, key=int):
  15285            switchDesc["cases"][length] = self.charSwitch(
  15286                list(sorted(prop for prop in instrumentedProps if len(prop) == length)),
  15287                0,
  15288                initializeChars,
  15289                charIndexer,
  15290                property_matcher,
  15291            )
  15292 
  15293        return body + fill(
  15294            """
  15295            MOZ_ASSERT(StaticPrefs::${pref}() && id.isAtom());
  15296            Maybe<UseCounter> counter;
  15297            {
  15298              // Scope for our no-GC section, so we don't need to rely on SetUseCounter not GCing.
  15299              JS::AutoCheckCannotGC nogc;
  15300              JSLinearString* str = JS::AtomToLinearString(id.toAtom());
  15301              // Don't waste time fetching the chars until we've done the length switch.
  15302              $*{switch}
  15303            }
  15304            if (counter) {
  15305              SetUseCounter(proxy, *counter);
  15306              return true;
  15307            }
  15308 
  15309            return false;
  15310            """,
  15311            pref=prefIdentifier(MISSING_PROP_PREF),
  15312            switch=self.gen_switch(switchDesc),
  15313        )
  15314 
  15315 
  15316 class CGInterfaceHasProperty(CGAbstractPropertySwitchMethod):
  15317    def __init__(self, descriptor):
  15318        """
  15319        Returns whether the given string a property of this or any of its
  15320        ancestors interfaces.
  15321        """
  15322        CGAbstractPropertySwitchMethod.__init__(
  15323            self,
  15324            descriptor,
  15325            "InterfaceHasProperty",
  15326            "bool",
  15327            [
  15328                Argument("const nsAString&", "name"),
  15329            ],
  15330        )
  15331 
  15332    def definition_body(self):
  15333        # Include all properties of the Object.prototype.
  15334        names = set(JS_OBJECT_PROTOTYPE_PROPERTIES)
  15335 
  15336        iface = self.descriptor.interface
  15337        while iface:
  15338            for m in iface.members:
  15339                if isChromeOnly(m):
  15340                    continue
  15341 
  15342                names.add(m.identifier.name)
  15343 
  15344            iface = iface.parent
  15345 
  15346        initializeChars = ""
  15347        charIndexer = lambda index: "name[%d]" % index
  15348 
  15349        def property_matcher(name):
  15350            return fill(
  15351                """
  15352                if (name == u\"${name}\"_ns) {
  15353                  return true;
  15354                }
  15355                """,
  15356                name=name,
  15357            )
  15358 
  15359        lengths = set(len(name) for name in names)
  15360        switchDesc = {"condition": "name.Length()", "precondition": ""}
  15361        switchDesc["cases"] = dict()
  15362        for length in sorted(lengths):
  15363            switchDesc["cases"][length] = self.charSwitch(
  15364                list(sorted(name for name in names if len(name) == length)),
  15365                0,
  15366                initializeChars,
  15367                charIndexer,
  15368                property_matcher,
  15369            )
  15370 
  15371        return fill(
  15372            """
  15373            $*{switch}
  15374 
  15375            return false;
  15376            """,
  15377            switch=self.gen_switch(switchDesc),
  15378        )
  15379 
  15380 
  15381 class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
  15382    def __init__(self, descriptor):
  15383        args = [
  15384            Argument("JSContext*", "cx"),
  15385            Argument("JS::Handle<JSObject*>", "proxy"),
  15386            Argument("JS::Handle<jsid>", "id"),
  15387            Argument("bool", "ignoreNamedProps"),
  15388            Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"),
  15389        ]
  15390        ClassMethod.__init__(
  15391            self,
  15392            "getOwnPropDescriptor",
  15393            "bool",
  15394            args,
  15395            virtual=True,
  15396            override=True,
  15397            const=True,
  15398        )
  15399        self.descriptor = descriptor
  15400 
  15401    def getBody(self):
  15402        indexedSetter = self.descriptor.operations["IndexedSetter"]
  15403 
  15404        if self.descriptor.isMaybeCrossOriginObject():
  15405            xrayDecl = dedent(
  15406                """
  15407                MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
  15408                MOZ_ASSERT(IsPlatformObjectSameOrigin(cx, proxy),
  15409                           "getOwnPropertyDescriptor() and set() should have dealt");
  15410                MOZ_ASSERT(js::IsObjectInContextCompartment(proxy, cx),
  15411                           "getOwnPropertyDescriptor() and set() should have dealt");
  15412 
  15413                """
  15414            )
  15415            xrayCheck = ""
  15416        else:
  15417            xrayDecl = "bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);\n"
  15418            xrayCheck = "!isXray &&"
  15419 
  15420        if self.descriptor.supportsIndexedProperties():
  15421            attributes = [
  15422                "JS::PropertyAttribute::Configurable",
  15423                "JS::PropertyAttribute::Enumerable",
  15424            ]
  15425            if indexedSetter is not None:
  15426                attributes.append("JS::PropertyAttribute::Writable")
  15427            setDescriptor = (
  15428                "desc.set(mozilla::Some(JS::PropertyDescriptor::Data(value, { %s })));\nreturn true;\n"
  15429                % ", ".join(attributes)
  15430            )
  15431            templateValues = {
  15432                "jsvalRef": "value",
  15433                "jsvalHandle": "&value",
  15434                "obj": "proxy",
  15435                "successCode": setDescriptor,
  15436            }
  15437            getIndexed = fill(
  15438                """
  15439                uint32_t index = GetArrayIndexFromId(id);
  15440                if (IsArrayIndex(index)) {
  15441                  JS::Rooted<JS::Value> value(cx);
  15442                  $*{callGetter}
  15443                }
  15444 
  15445                """,
  15446                callGetter=CGProxyIndexedGetter(
  15447                    self.descriptor, templateValues
  15448                ).define(),
  15449            )
  15450        else:
  15451            getIndexed = ""
  15452 
  15453        missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor)
  15454 
  15455        if self.descriptor.supportsNamedProperties():
  15456            operations = self.descriptor.operations
  15457            attributes = ["JS::PropertyAttribute::Configurable"]
  15458            if self.descriptor.namedPropertiesEnumerable:
  15459                attributes.append("JS::PropertyAttribute::Enumerable")
  15460            if operations["NamedSetter"] is not None:
  15461                attributes.append("JS::PropertyAttribute::Writable")
  15462            setDescriptor = (
  15463                "desc.set(mozilla::Some(JS::PropertyDescriptor::Data(value, { %s })));\nreturn true;\n"
  15464                % ", ".join(attributes)
  15465            )
  15466            templateValues = {
  15467                "jsvalRef": "value",
  15468                "jsvalHandle": "&value",
  15469                "obj": "proxy",
  15470                "successCode": setDescriptor,
  15471            }
  15472 
  15473            computeCondition = dedent(
  15474                """
  15475                bool hasOnProto;
  15476                if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
  15477                  return false;
  15478                }
  15479                callNamedGetter = !hasOnProto;
  15480                """
  15481            )
  15482            if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
  15483                computeCondition = fill(
  15484                    """
  15485                    if (!isXray) {
  15486                      callNamedGetter = true;
  15487                    } else {
  15488                      $*{hasOnProto}
  15489                    }
  15490                    """,
  15491                    hasOnProto=computeCondition,
  15492                )
  15493 
  15494            outerCondition = "!ignoreNamedProps"
  15495            if self.descriptor.supportsIndexedProperties():
  15496                outerCondition = "!IsArrayIndex(index) && " + outerCondition
  15497 
  15498            namedGetCode = CGProxyNamedGetter(self.descriptor, templateValues).define()
  15499            namedGet = fill(
  15500                """
  15501                bool callNamedGetter = false;
  15502                if (${outerCondition}) {
  15503                  $*{computeCondition}
  15504                }
  15505                if (callNamedGetter) {
  15506                  JS::Rooted<JS::Value> value(cx);
  15507                  $*{namedGetCode}
  15508                }
  15509                """,
  15510                outerCondition=outerCondition,
  15511                computeCondition=computeCondition,
  15512                namedGetCode=namedGetCode,
  15513            )
  15514            namedGet += "\n"
  15515        else:
  15516            namedGet = ""
  15517 
  15518        return fill(
  15519            """
  15520            $*{xrayDecl}
  15521            $*{getIndexed}
  15522            $*{missingPropUseCounters}
  15523            JS::Rooted<JSObject*> expando(cx);
  15524            if (${xrayCheck}(expando = GetExpandoObject(proxy))) {
  15525              if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc)) {
  15526                return false;
  15527              }
  15528              if (desc.isSome()) {
  15529                return true;
  15530              }
  15531            }
  15532 
  15533            $*{namedGet}
  15534            desc.reset();
  15535            return true;
  15536            """,
  15537            xrayDecl=xrayDecl,
  15538            xrayCheck=xrayCheck,
  15539            getIndexed=getIndexed,
  15540            missingPropUseCounters=missingPropUseCounters,
  15541            namedGet=namedGet,
  15542        )
  15543 
  15544 
  15545 class CGDOMJSProxyHandler_defineProperty(ClassMethod):
  15546    def __init__(self, descriptor):
  15547        # The usual convention is to name the ObjectOpResult out-parameter
  15548        # `result`, but that name is a bit overloaded around here.
  15549        args = [
  15550            Argument("JSContext*", "cx_"),
  15551            Argument("JS::Handle<JSObject*>", "proxy"),
  15552            Argument("JS::Handle<jsid>", "id"),
  15553            Argument("JS::Handle<JS::PropertyDescriptor>", "desc"),
  15554            Argument("JS::ObjectOpResult&", "opresult"),
  15555            Argument("bool*", "done"),
  15556        ]
  15557        ClassMethod.__init__(
  15558            self,
  15559            "defineProperty",
  15560            "bool",
  15561            args,
  15562            virtual=True,
  15563            override=True,
  15564            const=True,
  15565        )
  15566        self.descriptor = descriptor
  15567 
  15568    def getBody(self):
  15569        set = ""
  15570 
  15571        indexedSetter = self.descriptor.operations["IndexedSetter"]
  15572        if indexedSetter:
  15573            error_label = CGSpecializedMethod.error_reporting_label_helper(
  15574                self.descriptor, indexedSetter, isConstructor=False
  15575            )
  15576            if error_label:
  15577                cxDecl = fill(
  15578                    """
  15579                    BindingCallContext cx(cx_, ${error_label});
  15580                    """,
  15581                    error_label=error_label,
  15582                )
  15583            else:
  15584                cxDecl = dedent(
  15585                    """
  15586                    JSContext* cx = cx_;
  15587                    """
  15588                )
  15589            set += fill(
  15590                """
  15591                uint32_t index = GetArrayIndexFromId(id);
  15592                if (IsArrayIndex(index)) {
  15593                  $*{cxDecl}
  15594                  *done = true;
  15595                  // https://webidl.spec.whatwg.org/#legacy-platform-object-defineownproperty
  15596                  // Step 1.1.  The no-indexed-setter case is handled by step 1.2.
  15597                  if (!desc.isDataDescriptor()) {
  15598                    return opresult.failNotDataDescriptor();
  15599                  }
  15600 
  15601                  $*{callSetter}
  15602                  return opresult.succeed();
  15603                }
  15604                """,
  15605                cxDecl=cxDecl,
  15606                callSetter=CGProxyIndexedSetter(self.descriptor).define(),
  15607            )
  15608        elif self.descriptor.supportsIndexedProperties():
  15609            # We allow untrusted content to prevent Xrays from setting a
  15610            # property if that property is an indexed property and we have no
  15611            # indexed setter.  That's how the object would normally behave if
  15612            # you tried to set the property on it.  That means we don't need to
  15613            # do anything special for Xrays here.
  15614            set += dedent(
  15615                """
  15616                if (IsArrayIndex(GetArrayIndexFromId(id))) {
  15617                  *done = true;
  15618                  return opresult.failNoIndexedSetter();
  15619                }
  15620                """
  15621            )
  15622 
  15623        namedSetter = self.descriptor.operations["NamedSetter"]
  15624        if namedSetter:
  15625            error_label = CGSpecializedMethod.error_reporting_label_helper(
  15626                self.descriptor, namedSetter, isConstructor=False
  15627            )
  15628            if error_label:
  15629                set += fill(
  15630                    """
  15631                    BindingCallContext cx(cx_, ${error_label});
  15632                    """,
  15633                    error_label=error_label,
  15634                )
  15635            else:
  15636                set += dedent(
  15637                    """
  15638                    JSContext* cx = cx_;
  15639                    """
  15640                )
  15641            if self.descriptor.hasLegacyUnforgeableMembers:
  15642                raise TypeError(
  15643                    "Can't handle a named setter on an interface "
  15644                    "that has unforgeables.  Figure out how that "
  15645                    "should work!"
  15646                )
  15647            set += dedent(
  15648                """
  15649                // https://webidl.spec.whatwg.org/#legacy-platform-object-defineownproperty
  15650                // Step 2.2.2.1.
  15651                if (!desc.isDataDescriptor()) {
  15652                  *done = true;
  15653                  return opresult.failNotDataDescriptor();
  15654                }
  15655                """
  15656            )
  15657            tailCode = dedent(
  15658                """
  15659                *done = true;
  15660                return opresult.succeed();
  15661                """
  15662            )
  15663            set += CGProxyNamedSetter(self.descriptor, tailCode).define()
  15664        else:
  15665            # We allow untrusted content to prevent Xrays from setting a
  15666            # property if that property is already a named property on the
  15667            # object and we have no named setter.  That's how the object would
  15668            # normally behave if you tried to set the property on it.  That
  15669            # means we don't need to do anything special for Xrays here.
  15670            if self.descriptor.supportsNamedProperties():
  15671                set += fill(
  15672                    """
  15673                    JSContext* cx = cx_;
  15674                    bool found = false;
  15675                    $*{presenceChecker}
  15676 
  15677                    if (found) {
  15678                      *done = true;
  15679                      return opresult.failNoNamedSetter();
  15680                    }
  15681                    """,
  15682                    presenceChecker=CGProxyNamedPresenceChecker(
  15683                        self.descriptor, foundVar="found"
  15684                    ).define(),
  15685                )
  15686            if self.descriptor.isMaybeCrossOriginObject():
  15687                set += dedent(
  15688                    """
  15689                    MOZ_ASSERT(IsPlatformObjectSameOrigin(cx_, proxy),
  15690                               "Why did the MaybeCrossOriginObject defineProperty override fail?");
  15691                    MOZ_ASSERT(js::IsObjectInContextCompartment(proxy, cx_),
  15692                               "Why did the MaybeCrossOriginObject defineProperty override fail?");
  15693                    """
  15694                )
  15695 
  15696        # In all cases we want to tail-call to our base class; we can
  15697        # always land here for symbols.
  15698        set += (
  15699            "return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n"
  15700            % ", ".join(a.name for a in self.args)
  15701        )
  15702        return set
  15703 
  15704 
  15705 def getDeleterBody(descriptor, type, foundVar=None):
  15706    """
  15707    type should be "Named" or "Indexed"
  15708 
  15709    The possible outcomes:
  15710    - an error happened                       (the emitted code returns false)
  15711    - own property not found                  (foundVar=false, deleteSucceeded=true)
  15712    - own property found and deleted          (foundVar=true,  deleteSucceeded=true)
  15713    - own property found but can't be deleted (foundVar=true,  deleteSucceeded=false)
  15714    """
  15715    assert type in ("Named", "Indexed")
  15716    deleter = descriptor.operations[type + "Deleter"]
  15717    if deleter:
  15718        assert type == "Named"
  15719        assert foundVar is not None
  15720        if descriptor.hasLegacyUnforgeableMembers:
  15721            raise TypeError(
  15722                "Can't handle a deleter on an interface "
  15723                "that has unforgeables.  Figure out how "
  15724                "that should work!"
  15725            )
  15726        # See if the deleter method is fallible.
  15727        t = deleter.signatures()[0][0]
  15728        if t.isPrimitive() and not t.nullable() and t.tag() == IDLType.Tags.bool:
  15729            # The deleter method has a boolean return value. When a
  15730            # property is found, the return value indicates whether it
  15731            # was successfully deleted.
  15732            setDS = fill(
  15733                """
  15734                if (!${foundVar}) {
  15735                  deleteSucceeded = true;
  15736                }
  15737                """,
  15738                foundVar=foundVar,
  15739            )
  15740        else:
  15741            # No boolean return value: if a property is found,
  15742            # deleting it always succeeds.
  15743            setDS = "deleteSucceeded = true;\n"
  15744 
  15745        body = (
  15746            CGProxyNamedDeleter(
  15747                descriptor, resultVar="deleteSucceeded", foundVar=foundVar
  15748            ).define()
  15749            + setDS
  15750        )
  15751    elif getattr(descriptor, "supports%sProperties" % type)():
  15752        presenceCheckerClass = globals()["CGProxy%sPresenceChecker" % type]
  15753        foundDecl = ""
  15754        if foundVar is None:
  15755            foundVar = "found"
  15756            foundDecl = "bool found = false;\n"
  15757        body = fill(
  15758            """
  15759            $*{foundDecl}
  15760            $*{presenceChecker}
  15761            deleteSucceeded = !${foundVar};
  15762            """,
  15763            foundDecl=foundDecl,
  15764            presenceChecker=presenceCheckerClass(
  15765                descriptor, foundVar=foundVar
  15766            ).define(),
  15767            foundVar=foundVar,
  15768        )
  15769    else:
  15770        body = None
  15771    return body
  15772 
  15773 
  15774 class CGDeleteNamedProperty(CGAbstractStaticMethod):
  15775    def __init__(self, descriptor):
  15776        args = [
  15777            Argument("JSContext*", "cx"),
  15778            Argument("JS::Handle<JSObject*>", "xray"),
  15779            Argument("JS::Handle<JSObject*>", "proxy"),
  15780            Argument("JS::Handle<jsid>", "id"),
  15781            Argument("JS::ObjectOpResult&", "opresult"),
  15782        ]
  15783        CGAbstractStaticMethod.__init__(
  15784            self, descriptor, "DeleteNamedProperty", "bool", args
  15785        )
  15786 
  15787    def definition_body(self):
  15788        return fill(
  15789            """
  15790            MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(xray));
  15791            MOZ_ASSERT(js::IsProxy(proxy));
  15792            MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
  15793            JSAutoRealm ar(cx, proxy);
  15794            bool deleteSucceeded = false;
  15795            bool found = false;
  15796            $*{namedBody}
  15797            if (!found || deleteSucceeded) {
  15798              return opresult.succeed();
  15799            }
  15800            return opresult.failCantDelete();
  15801            """,
  15802            namedBody=getDeleterBody(self.descriptor, "Named", foundVar="found"),
  15803        )
  15804 
  15805 
  15806 class CGDOMJSProxyHandler_delete(ClassMethod):
  15807    def __init__(self, descriptor):
  15808        args = [
  15809            Argument("JSContext*", "cx"),
  15810            Argument("JS::Handle<JSObject*>", "proxy"),
  15811            Argument("JS::Handle<jsid>", "id"),
  15812            Argument("JS::ObjectOpResult&", "opresult"),
  15813        ]
  15814        ClassMethod.__init__(
  15815            self, "delete_", "bool", args, virtual=True, override=True, const=True
  15816        )
  15817        self.descriptor = descriptor
  15818 
  15819    def getBody(self):
  15820        delete = dedent(
  15821            """
  15822            MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
  15823                      "Should not have a XrayWrapper here");
  15824 
  15825            """
  15826        )
  15827 
  15828        if self.descriptor.isMaybeCrossOriginObject():
  15829            delete += dedent(
  15830                """
  15831                if (!IsPlatformObjectSameOrigin(cx, proxy)) {
  15832                  return ReportCrossOriginDenial(cx, id, "delete"_ns);
  15833                }
  15834 
  15835                // Safe to enter the Realm of proxy now.
  15836                JSAutoRealm ar(cx, proxy);
  15837                JS_MarkCrossZoneId(cx, id);
  15838                """
  15839            )
  15840 
  15841        indexedBody = getDeleterBody(self.descriptor, "Indexed")
  15842        if indexedBody is not None:
  15843            # Can't handle cross-origin objects here.
  15844            assert not self.descriptor.isMaybeCrossOriginObject()
  15845            delete += fill(
  15846                """
  15847                uint32_t index = GetArrayIndexFromId(id);
  15848                if (IsArrayIndex(index)) {
  15849                  bool deleteSucceeded;
  15850                  $*{indexedBody}
  15851                  return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
  15852                }
  15853                """,
  15854                indexedBody=indexedBody,
  15855            )
  15856 
  15857        namedBody = getDeleterBody(self.descriptor, "Named", foundVar="found")
  15858        if namedBody is not None:
  15859            delete += dedent(
  15860                """
  15861                // Try named delete only if the named property visibility
  15862                // algorithm says the property is visible.
  15863                bool tryNamedDelete = true;
  15864                { // Scope for expando
  15865                  JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
  15866                  if (expando) {
  15867                    bool hasProp;
  15868                    if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
  15869                      return false;
  15870                    }
  15871                    tryNamedDelete = !hasProp;
  15872                  }
  15873                }
  15874                """
  15875            )
  15876 
  15877            if not self.descriptor.interface.getExtendedAttribute(
  15878                "LegacyOverrideBuiltIns"
  15879            ):
  15880                delete += dedent(
  15881                    """
  15882                    if (tryNamedDelete) {
  15883                      bool hasOnProto;
  15884                      if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
  15885                        return false;
  15886                      }
  15887                      tryNamedDelete = !hasOnProto;
  15888                    }
  15889                    """
  15890                )
  15891 
  15892            # We always return above for an index id in the case when we support
  15893            # indexed properties, so we can just treat the id as a name
  15894            # unconditionally here.
  15895            delete += fill(
  15896                """
  15897                if (tryNamedDelete) {
  15898                  bool found = false;
  15899                  bool deleteSucceeded;
  15900                  $*{namedBody}
  15901                  if (found) {
  15902                    return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
  15903                  }
  15904                }
  15905                """,
  15906                namedBody=namedBody,
  15907            )
  15908 
  15909        delete += dedent(
  15910            """
  15911 
  15912            return dom::DOMProxyHandler::delete_(cx, proxy, id, opresult);
  15913            """
  15914        )
  15915 
  15916        return delete
  15917 
  15918 
  15919 class CGDOMJSProxyHandler_ownPropNames(ClassMethod):
  15920    def __init__(
  15921        self,
  15922        descriptor,
  15923    ):
  15924        args = [
  15925            Argument("JSContext*", "cx"),
  15926            Argument("JS::Handle<JSObject*>", "proxy"),
  15927            Argument("unsigned", "flags"),
  15928            Argument("JS::MutableHandleVector<jsid>", "props"),
  15929        ]
  15930        ClassMethod.__init__(
  15931            self, "ownPropNames", "bool", args, virtual=True, override=True, const=True
  15932        )
  15933        self.descriptor = descriptor
  15934 
  15935    def getBody(self):
  15936        if self.descriptor.isMaybeCrossOriginObject():
  15937            xrayDecl = dedent(
  15938                """
  15939                MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
  15940                if (!IsPlatformObjectSameOrigin(cx, proxy)) {
  15941                  if (!(flags & JSITER_HIDDEN)) {
  15942                    // There are no enumerable cross-origin props, so we're done.
  15943                    return true;
  15944                  }
  15945 
  15946                  JS::Rooted<JSObject*> holder(cx);
  15947                  if (!EnsureHolder(cx, proxy, &holder)) {
  15948                    return false;
  15949                  }
  15950 
  15951                  if (!js::GetPropertyKeys(cx, holder, flags, props)) {
  15952                    return false;
  15953                  }
  15954 
  15955                  return xpc::AppendCrossOriginWhitelistedPropNames(cx, props);
  15956                }
  15957 
  15958                """
  15959            )
  15960            xrayCheck = ""
  15961        else:
  15962            xrayDecl = "bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);\n"
  15963            xrayCheck = "!isXray &&"
  15964 
  15965        # Per spec, we do indices, then named props, then everything else.
  15966        if self.descriptor.supportsIndexedProperties():
  15967            if self.descriptor.lengthNeedsCallerType():
  15968                callerType = callerTypeGetterForDescriptor(self.descriptor)
  15969            else:
  15970                callerType = ""
  15971            addIndices = fill(
  15972                """
  15973 
  15974                uint32_t length = UnwrapProxy(proxy)->Length(${callerType});
  15975                MOZ_ASSERT(int32_t(length) >= 0);
  15976                for (int32_t i = 0; i < int32_t(length); ++i) {
  15977                  if (!props.append(JS::PropertyKey::Int(i))) {
  15978                    return false;
  15979                  }
  15980                }
  15981                """,
  15982                callerType=callerType,
  15983            )
  15984        else:
  15985            addIndices = ""
  15986 
  15987        if self.descriptor.supportsNamedProperties():
  15988            if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
  15989                shadow = "!isXray"
  15990            else:
  15991                shadow = "false"
  15992 
  15993            if self.descriptor.supportedNamesNeedCallerType():
  15994                callerType = ", " + callerTypeGetterForDescriptor(self.descriptor)
  15995            else:
  15996                callerType = ""
  15997 
  15998            addNames = fill(
  15999                """
  16000                nsTArray<nsString> names;
  16001                UnwrapProxy(proxy)->GetSupportedNames(names${callerType});
  16002                if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) {
  16003                  return false;
  16004                }
  16005                """,
  16006                callerType=callerType,
  16007                shadow=shadow,
  16008            )
  16009            if not self.descriptor.namedPropertiesEnumerable:
  16010                addNames = CGIfWrapper(
  16011                    CGGeneric(addNames), "flags & JSITER_HIDDEN"
  16012                ).define()
  16013            addNames = "\n" + addNames
  16014        else:
  16015            addNames = ""
  16016 
  16017        addExpandoProps = fill(
  16018            """
  16019            JS::Rooted<JSObject*> expando(cx);
  16020            if (${xrayCheck}(expando = DOMProxyHandler::GetExpandoObject(proxy)) &&
  16021                !js::GetPropertyKeys(cx, expando, flags, props)) {
  16022              return false;
  16023            }
  16024            """,
  16025            xrayCheck=xrayCheck,
  16026        )
  16027 
  16028        if self.descriptor.isMaybeCrossOriginObject():
  16029            # We need to enter our compartment (which we might not be
  16030            # in right now) to get the expando props.
  16031            addExpandoProps = fill(
  16032                """
  16033                {  // Scope for accessing the expando.
  16034                  // Safe to enter our compartment, because IsPlatformObjectSameOrigin tested true.
  16035                  JSAutoRealm ar(cx, proxy);
  16036                  $*{addExpandoProps}
  16037                }
  16038                for (auto& id : props) {
  16039                  JS_MarkCrossZoneId(cx, id);
  16040                }
  16041                """,
  16042                addExpandoProps=addExpandoProps,
  16043            )
  16044 
  16045        return fill(
  16046            """
  16047            $*{xrayDecl}
  16048            $*{addIndices}
  16049            $*{addNames}
  16050 
  16051            $*{addExpandoProps}
  16052 
  16053            return true;
  16054            """,
  16055            xrayDecl=xrayDecl,
  16056            addIndices=addIndices,
  16057            addNames=addNames,
  16058            addExpandoProps=addExpandoProps,
  16059        )
  16060 
  16061 
  16062 class CGDOMJSProxyHandler_hasOwn(ClassMethod):
  16063    def __init__(self, descriptor):
  16064        args = [
  16065            Argument("JSContext*", "cx"),
  16066            Argument("JS::Handle<JSObject*>", "proxy"),
  16067            Argument("JS::Handle<jsid>", "id"),
  16068            Argument("bool*", "bp"),
  16069        ]
  16070        ClassMethod.__init__(
  16071            self, "hasOwn", "bool", args, virtual=True, override=True, const=True
  16072        )
  16073        self.descriptor = descriptor
  16074 
  16075    def getBody(self):
  16076        if self.descriptor.isMaybeCrossOriginObject():
  16077            maybeCrossOrigin = dedent(
  16078                """
  16079                if (!IsPlatformObjectSameOrigin(cx, proxy)) {
  16080                  // Just hand this off to BaseProxyHandler to do the slow-path thing.
  16081                  // The BaseProxyHandler code is OK with this happening without entering the
  16082                  // compartment of "proxy", which is important to get the right answers.
  16083                  return js::BaseProxyHandler::hasOwn(cx, proxy, id, bp);
  16084                }
  16085 
  16086                // Now safe to enter the Realm of proxy and do the rest of the work there.
  16087                JSAutoRealm ar(cx, proxy);
  16088                JS_MarkCrossZoneId(cx, id);
  16089                """
  16090            )
  16091        else:
  16092            maybeCrossOrigin = ""
  16093 
  16094        if self.descriptor.supportsIndexedProperties():
  16095            indexed = fill(
  16096                """
  16097                uint32_t index = GetArrayIndexFromId(id);
  16098                if (IsArrayIndex(index)) {
  16099                  bool found = false;
  16100                  $*{presenceChecker}
  16101 
  16102                  *bp = found;
  16103                  return true;
  16104                }
  16105 
  16106                """,
  16107                presenceChecker=CGProxyIndexedPresenceChecker(
  16108                    self.descriptor, foundVar="found"
  16109                ).define(),
  16110            )
  16111        else:
  16112            indexed = ""
  16113 
  16114        if self.descriptor.supportsNamedProperties():
  16115            # If we support indexed properties we always return above for index
  16116            # property names, so no need to check for those here.
  16117            named = fill(
  16118                """
  16119                bool found = false;
  16120                $*{presenceChecker}
  16121 
  16122                *bp = found;
  16123                """,
  16124                presenceChecker=CGProxyNamedPresenceChecker(
  16125                    self.descriptor, foundVar="found"
  16126                ).define(),
  16127            )
  16128            if not self.descriptor.interface.getExtendedAttribute(
  16129                "LegacyOverrideBuiltIns"
  16130            ):
  16131                named = fill(
  16132                    """
  16133                    bool hasOnProto;
  16134                    if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
  16135                      return false;
  16136                    }
  16137                    if (!hasOnProto) {
  16138                      $*{protoLacksProperty}
  16139                      return true;
  16140                    }
  16141                    """,
  16142                    protoLacksProperty=named,
  16143                )
  16144                named += "*bp = false;\n"
  16145            else:
  16146                named += "\n"
  16147        else:
  16148            named = "*bp = false;\n"
  16149 
  16150        missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor)
  16151 
  16152        return fill(
  16153            """
  16154            MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
  16155                      "Should not have a XrayWrapper here");
  16156            $*{maybeCrossOrigin}
  16157            $*{indexed}
  16158 
  16159            $*{missingPropUseCounters}
  16160            JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy));
  16161            if (expando) {
  16162              bool b = true;
  16163              bool ok = JS_HasPropertyById(cx, expando, id, &b);
  16164              *bp = !!b;
  16165              if (!ok || *bp) {
  16166                return ok;
  16167              }
  16168            }
  16169 
  16170            $*{named}
  16171            return true;
  16172            """,
  16173            maybeCrossOrigin=maybeCrossOrigin,
  16174            indexed=indexed,
  16175            missingPropUseCounters=missingPropUseCounters,
  16176            named=named,
  16177        )
  16178 
  16179 
  16180 class CGDOMJSProxyHandler_get(ClassMethod):
  16181    def __init__(self, descriptor):
  16182        args = [
  16183            Argument("JSContext*", "cx"),
  16184            Argument("JS::Handle<JSObject*>", "proxy"),
  16185            Argument("JS::Handle<JS::Value>", "receiver"),
  16186            Argument("JS::Handle<jsid>", "id"),
  16187            Argument("JS::MutableHandle<JS::Value>", "vp"),
  16188        ]
  16189        ClassMethod.__init__(
  16190            self, "get", "bool", args, virtual=True, override=True, const=True
  16191        )
  16192        self.descriptor = descriptor
  16193 
  16194    def getBody(self):
  16195        missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor)
  16196 
  16197        getUnforgeableOrExpando = dedent(
  16198            """
  16199            bool expandoHasProp = false;
  16200            { // Scope for expando
  16201              JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
  16202              if (expando) {
  16203                if (!JS_HasPropertyById(cx, expando, id, &expandoHasProp)) {
  16204                  return false;
  16205                }
  16206 
  16207                if (expandoHasProp) {
  16208                  // Forward the get to the expando object, but our receiver is whatever our
  16209                  // receiver is.
  16210                  if (!JS_ForwardGetPropertyTo(cx, expando, id, ${receiver}, vp)) {
  16211                    return false;
  16212                  }
  16213                }
  16214              }
  16215            }
  16216            """
  16217        )
  16218 
  16219        getOnPrototype = dedent(
  16220            """
  16221            bool foundOnPrototype;
  16222            if (!GetPropertyOnPrototype(cx, proxy, ${receiver}, id, &foundOnPrototype, vp)) {
  16223              return false;
  16224            }
  16225            """
  16226        )
  16227 
  16228        if self.descriptor.isMaybeCrossOriginObject():
  16229            # We can't handle these for cross-origin objects
  16230            assert not self.descriptor.supportsIndexedProperties()
  16231            assert not self.descriptor.supportsNamedProperties()
  16232 
  16233            return fill(
  16234                """
  16235                MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
  16236                            "Should not have a XrayWrapper here");
  16237 
  16238                if (!IsPlatformObjectSameOrigin(cx, proxy)) {
  16239                  return CrossOriginGet(cx, proxy, receiver, id, vp);
  16240                }
  16241 
  16242                $*{missingPropUseCounters}
  16243                { // Scope for the JSAutoRealm accessing expando and prototype.
  16244                  JSAutoRealm ar(cx, proxy);
  16245                  JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
  16246                  if (!MaybeWrapValue(cx, &wrappedReceiver)) {
  16247                    return false;
  16248                  }
  16249                  JS_MarkCrossZoneId(cx, id);
  16250 
  16251                  $*{getUnforgeableOrExpando}
  16252                  if (!expandoHasProp) {
  16253                    $*{getOnPrototype}
  16254                    if (!foundOnPrototype) {
  16255                      MOZ_ASSERT(vp.isUndefined());
  16256                      return true;
  16257                    }
  16258                  }
  16259                }
  16260 
  16261                return MaybeWrapValue(cx, vp);
  16262                """,
  16263                missingPropUseCounters=missingPropUseCountersForDescriptor(
  16264                    self.descriptor
  16265                ),
  16266                getUnforgeableOrExpando=fill(
  16267                    getUnforgeableOrExpando, receiver="wrappedReceiver"
  16268                ),
  16269                getOnPrototype=fill(getOnPrototype, receiver="wrappedReceiver"),
  16270            )
  16271 
  16272        templateValues = {"jsvalRef": "vp", "jsvalHandle": "vp", "obj": "proxy"}
  16273 
  16274        getUnforgeableOrExpando = fill(
  16275            getUnforgeableOrExpando, receiver="receiver"
  16276        ) + dedent(
  16277            """
  16278 
  16279            if (expandoHasProp) {
  16280              return true;
  16281            }
  16282            """
  16283        )
  16284        if self.descriptor.supportsIndexedProperties():
  16285            getIndexedOrExpando = fill(
  16286                """
  16287                uint32_t index = GetArrayIndexFromId(id);
  16288                if (IsArrayIndex(index)) {
  16289                  $*{callGetter}
  16290                  // Even if we don't have this index, we don't forward the
  16291                  // get on to our expando object.
  16292                } else {
  16293                  $*{getUnforgeableOrExpando}
  16294                }
  16295                """,
  16296                callGetter=CGProxyIndexedGetter(
  16297                    self.descriptor, templateValues
  16298                ).define(),
  16299                getUnforgeableOrExpando=getUnforgeableOrExpando,
  16300            )
  16301        else:
  16302            getIndexedOrExpando = getUnforgeableOrExpando
  16303 
  16304        if self.descriptor.supportsNamedProperties():
  16305            getNamed = CGProxyNamedGetter(self.descriptor, templateValues)
  16306            if self.descriptor.supportsIndexedProperties():
  16307                getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
  16308            getNamed = getNamed.define() + "\n"
  16309        else:
  16310            getNamed = ""
  16311 
  16312        getOnPrototype = fill(getOnPrototype, receiver="receiver") + dedent(
  16313            """
  16314 
  16315            if (foundOnPrototype) {
  16316              return true;
  16317            }
  16318 
  16319            MOZ_ASSERT(vp.isUndefined());
  16320            """
  16321        )
  16322 
  16323        if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
  16324            getNamedOrOnPrototype = getNamed + getOnPrototype
  16325        else:
  16326            getNamedOrOnPrototype = getOnPrototype + getNamed
  16327 
  16328        return fill(
  16329            """
  16330            MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
  16331                        "Should not have a XrayWrapper here");
  16332 
  16333            $*{missingPropUseCounters}
  16334            $*{indexedOrExpando}
  16335 
  16336            $*{namedOrOnPropotype}
  16337            return true;
  16338            """,
  16339            missingPropUseCounters=missingPropUseCounters,
  16340            indexedOrExpando=getIndexedOrExpando,
  16341            namedOrOnPropotype=getNamedOrOnPrototype,
  16342        )
  16343 
  16344 
  16345 class CGDOMJSProxyHandler_setCustom(ClassMethod):
  16346    def __init__(self, descriptor):
  16347        args = [
  16348            Argument("JSContext*", "cx_"),
  16349            Argument("JS::Handle<JSObject*>", "proxy"),
  16350            Argument("JS::Handle<jsid>", "id"),
  16351            Argument("JS::Handle<JS::Value>", "v"),
  16352            Argument("bool*", "done"),
  16353        ]
  16354        ClassMethod.__init__(
  16355            self, "setCustom", "bool", args, virtual=True, override=True, const=True
  16356        )
  16357        self.descriptor = descriptor
  16358 
  16359    def getBody(self):
  16360        assertion = (
  16361            "MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n"
  16362            '           "Should not have a XrayWrapper here");\n'
  16363        )
  16364 
  16365        # Correctness first. If we have a NamedSetter and [LegacyOverrideBuiltIns],
  16366        # always call the NamedSetter and never do anything else.
  16367        namedSetter = self.descriptor.operations["NamedSetter"]
  16368        if namedSetter is not None and self.descriptor.interface.getExtendedAttribute(
  16369            "LegacyOverrideBuiltIns"
  16370        ):
  16371            # Check assumptions.
  16372            if self.descriptor.supportsIndexedProperties():
  16373                raise ValueError(
  16374                    "In interface "
  16375                    + self.descriptor.name
  16376                    + ": "
  16377                    + "Can't cope with [LegacyOverrideBuiltIns] and an indexed getter"
  16378                )
  16379            if self.descriptor.hasLegacyUnforgeableMembers:
  16380                raise ValueError(
  16381                    "In interface "
  16382                    + self.descriptor.name
  16383                    + ": "
  16384                    + "Can't cope with [LegacyOverrideBuiltIns] and unforgeable members"
  16385                )
  16386 
  16387            tailCode = dedent(
  16388                """
  16389                *done = true;
  16390                return true;
  16391                """
  16392            )
  16393            callSetter = CGProxyNamedSetter(
  16394                self.descriptor, tailCode, argumentHandleValue="v"
  16395            )
  16396            error_label = CGSpecializedMethod.error_reporting_label_helper(
  16397                self.descriptor, namedSetter, isConstructor=False
  16398            )
  16399            if error_label:
  16400                cxDecl = fill(
  16401                    """
  16402                    BindingCallContext cx(cx_, ${error_label});
  16403                    """,
  16404                    error_label=error_label,
  16405                )
  16406            else:
  16407                cxDecl = dedent(
  16408                    """
  16409                    JSContext* cx = cx_;
  16410                    """
  16411                )
  16412            return fill(
  16413                """
  16414                $*{assertion}
  16415                $*{cxDecl}
  16416                $*{callSetter}
  16417                *done = false;
  16418                return true;
  16419                """,
  16420                assertion=assertion,
  16421                cxDecl=cxDecl,
  16422                callSetter=callSetter.define(),
  16423            )
  16424 
  16425        # As an optimization, if we are going to call an IndexedSetter, go
  16426        # ahead and call it and have done.
  16427        indexedSetter = self.descriptor.operations["IndexedSetter"]
  16428        if indexedSetter is not None:
  16429            error_label = CGSpecializedMethod.error_reporting_label_helper(
  16430                self.descriptor, indexedSetter, isConstructor=False
  16431            )
  16432            if error_label:
  16433                cxDecl = fill(
  16434                    """
  16435                    BindingCallContext cx(cx_, ${error_label});
  16436                    """,
  16437                    error_label=error_label,
  16438                )
  16439            else:
  16440                cxDecl = dedent(
  16441                    """
  16442                    JSContext* cx = cx_;
  16443                    """
  16444                )
  16445            setIndexed = fill(
  16446                """
  16447                uint32_t index = GetArrayIndexFromId(id);
  16448                if (IsArrayIndex(index)) {
  16449                  $*{cxDecl}
  16450                  $*{callSetter}
  16451                  *done = true;
  16452                  return true;
  16453                }
  16454 
  16455                """,
  16456                cxDecl=cxDecl,
  16457                callSetter=CGProxyIndexedSetter(
  16458                    self.descriptor, argumentHandleValue="v"
  16459                ).define(),
  16460            )
  16461        else:
  16462            setIndexed = ""
  16463 
  16464        return assertion + setIndexed + "*done = false;\n" "return true;\n"
  16465 
  16466 
  16467 class CGDOMJSProxyHandler_className(ClassMethod):
  16468    def __init__(self, descriptor):
  16469        args = [
  16470            Argument("JSContext*", "cx"),
  16471            Argument("JS::Handle<JSObject*>", "proxy"),
  16472        ]
  16473        ClassMethod.__init__(
  16474            self,
  16475            "className",
  16476            "const char*",
  16477            args,
  16478            virtual=True,
  16479            override=True,
  16480            const=True,
  16481        )
  16482        self.descriptor = descriptor
  16483 
  16484    def getBody(self):
  16485        if self.descriptor.isMaybeCrossOriginObject():
  16486            crossOrigin = dedent(
  16487                """
  16488                if (!IsPlatformObjectSameOrigin(cx, proxy)) {
  16489                  return "Object";
  16490                }
  16491 
  16492                """
  16493            )
  16494        else:
  16495            crossOrigin = ""
  16496        return fill(
  16497            """
  16498            $*{crossOrigin}
  16499            return "${name}";
  16500            """,
  16501            crossOrigin=crossOrigin,
  16502            name=self.descriptor.name,
  16503        )
  16504 
  16505 
  16506 class CGDOMJSProxyHandler_trace(ClassMethod):
  16507    def __init__(self, descriptor):
  16508        args = [Argument("JSTracer*", "trc"), Argument("JSObject*", "proxy")]
  16509        ClassMethod.__init__(
  16510            self,
  16511            "trace",
  16512            "void",
  16513            args,
  16514            virtual=True,
  16515            override=True,
  16516            const=True,
  16517        )
  16518        self.descriptor = descriptor
  16519 
  16520    def getBody(self):
  16521        iface = getReflectedHTMLAttributesIface(self.descriptor)
  16522        return fill(
  16523            """
  16524            ${reflectedAttributesBase}::ReflectedHTMLAttributeSlots::Trace(${trc}, ${proxy});
  16525            return Base::trace(${trc}, ${proxy});
  16526            """,
  16527            reflectedAttributesBase=toBindingNamespace(iface.identifier.name),
  16528            trc=self.args[0].name,
  16529            proxy=self.args[1].name,
  16530        )
  16531 
  16532 
  16533 class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
  16534    def __init__(self, descriptor):
  16535        args = [Argument("const JS::Value&", "priv")]
  16536        ClassMethod.__init__(
  16537            self,
  16538            "finalizeInBackground",
  16539            "bool",
  16540            args,
  16541            virtual=True,
  16542            override=True,
  16543            const=True,
  16544        )
  16545        self.descriptor = descriptor
  16546 
  16547    def getBody(self):
  16548        return "return false;\n"
  16549 
  16550 
  16551 class CGDOMJSProxyHandler_finalize(ClassMethod):
  16552    def __init__(self, descriptor):
  16553        args = [Argument("JS::GCContext*", "gcx"), Argument("JSObject*", "proxy")]
  16554        ClassMethod.__init__(
  16555            self, "finalize", "void", args, virtual=True, override=True, const=True
  16556        )
  16557        self.descriptor = descriptor
  16558 
  16559    def getBody(self):
  16560        return (
  16561            "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(proxy);\n"
  16562            % (self.descriptor.nativeType, self.descriptor.nativeType)
  16563        ) + finalizeHook(
  16564            self.descriptor,
  16565            self.args[0].name,
  16566            self.args[1].name,
  16567        ).define()
  16568 
  16569 
  16570 class CGDOMJSProxyHandler_objectMoved(ClassMethod):
  16571    def __init__(self, descriptor):
  16572        args = [Argument("JSObject*", "obj"), Argument("JSObject*", "old")]
  16573        ClassMethod.__init__(
  16574            self, "objectMoved", "size_t", args, virtual=True, override=True, const=True
  16575        )
  16576        self.descriptor = descriptor
  16577 
  16578    def getBody(self):
  16579        return (
  16580            "return NativeTypeHelpers<%s>::ObjectMoved(obj, old);\n"
  16581            % self.descriptor.nativeType
  16582        )
  16583 
  16584 
  16585 class CGDOMJSProxyHandler_getElements(ClassMethod):
  16586    def __init__(self, descriptor):
  16587        assert descriptor.supportsIndexedProperties()
  16588 
  16589        args = [
  16590            Argument("JSContext*", "cx"),
  16591            Argument("JS::Handle<JSObject*>", "proxy"),
  16592            Argument("uint32_t", "begin"),
  16593            Argument("uint32_t", "end"),
  16594            Argument("js::ElementAdder*", "adder"),
  16595        ]
  16596        ClassMethod.__init__(
  16597            self, "getElements", "bool", args, virtual=True, override=True, const=True
  16598        )
  16599        self.descriptor = descriptor
  16600 
  16601    def getBody(self):
  16602        # Just like ownPropertyKeys we'll assume that we have no holes, so
  16603        # we have all properties from 0 to length.  If that ever changes
  16604        # (unlikely), we'll need to do something a bit more clever with how we
  16605        # forward on to our ancestor.
  16606 
  16607        templateValues = {
  16608            "jsvalRef": "temp",
  16609            "jsvalHandle": "&temp",
  16610            "obj": "proxy",
  16611            "successCode": (
  16612                "if (!adder->append(cx, temp)) return false;\n" "continue;\n"
  16613            ),
  16614        }
  16615        get = CGProxyIndexedGetter(
  16616            self.descriptor, templateValues, False, False
  16617        ).define()
  16618 
  16619        if self.descriptor.lengthNeedsCallerType():
  16620            callerType = callerTypeGetterForDescriptor(self.descriptor)
  16621        else:
  16622            callerType = ""
  16623 
  16624        return fill(
  16625            """
  16626            JS::Rooted<JS::Value> temp(cx);
  16627            MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
  16628                       "Should not have a XrayWrapper here");
  16629 
  16630            ${nativeType}* self = UnwrapProxy(proxy);
  16631            uint32_t length = self->Length(${callerType});
  16632            // Compute the end of the indices we'll get ourselves
  16633            uint32_t ourEnd = std::clamp(length, begin, end);
  16634 
  16635            for (uint32_t index = begin; index < ourEnd; ++index) {
  16636              $*{get}
  16637            }
  16638 
  16639            if (end > ourEnd) {
  16640              JS::Rooted<JSObject*> proto(cx);
  16641              if (!js::GetObjectProto(cx, proxy, &proto)) {
  16642                return false;
  16643              }
  16644              return js::GetElementsWithAdder(cx, proto, proxy, ourEnd, end, adder);
  16645            }
  16646 
  16647            return true;
  16648            """,
  16649            nativeType=self.descriptor.nativeType,
  16650            callerType=callerType,
  16651            get=get,
  16652        )
  16653 
  16654 
  16655 class CGJSProxyHandler_getInstance(ClassMethod):
  16656    def __init__(self, type):
  16657        self.type = type
  16658        ClassMethod.__init__(
  16659            self, "getInstance", "const %s*" % self.type, [], static=True
  16660        )
  16661 
  16662    def getBody(self):
  16663        return fill(
  16664            """
  16665            static const ${type} instance;
  16666            return &instance;
  16667            """,
  16668            type=self.type,
  16669        )
  16670 
  16671 
  16672 class CGDOMJSProxyHandler_call(ClassMethod):
  16673    def __init__(self):
  16674        args = [
  16675            Argument("JSContext*", "cx"),
  16676            Argument("JS::Handle<JSObject*>", "proxy"),
  16677            Argument("const JS::CallArgs&", "args"),
  16678        ]
  16679 
  16680        ClassMethod.__init__(
  16681            self, "call", "bool", args, virtual=True, override=True, const=True
  16682        )
  16683 
  16684    def getBody(self):
  16685        return fill(
  16686            """
  16687            return js::ForwardToNative(cx, ${legacyCaller}, args);
  16688            """,
  16689            legacyCaller=LEGACYCALLER_HOOK_NAME,
  16690        )
  16691 
  16692 
  16693 class CGDOMJSProxyHandler_isCallable(ClassMethod):
  16694    def __init__(self):
  16695        ClassMethod.__init__(
  16696            self,
  16697            "isCallable",
  16698            "bool",
  16699            [Argument("JSObject*", "obj")],
  16700            virtual=True,
  16701            override=True,
  16702            const=True,
  16703        )
  16704 
  16705    def getBody(self):
  16706        return dedent(
  16707            """
  16708            return true;
  16709        """
  16710        )
  16711 
  16712 
  16713 class CGDOMJSProxyHandler_canNurseryAllocate(ClassMethod):
  16714    """
  16715    Override the default canNurseryAllocate in BaseProxyHandler, for cases when
  16716    we should be nursery-allocated.
  16717    """
  16718 
  16719    def __init__(self):
  16720        ClassMethod.__init__(
  16721            self,
  16722            "canNurseryAllocate",
  16723            "bool",
  16724            [],
  16725            virtual=True,
  16726            override=True,
  16727            const=True,
  16728        )
  16729 
  16730    def getBody(self):
  16731        return dedent(
  16732            """
  16733            return true;
  16734        """
  16735        )
  16736 
  16737 
  16738 class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod):
  16739    """
  16740    Implementation of getOwnPropertyDescriptor.  We only use this for
  16741    cross-origin objects.
  16742    """
  16743 
  16744    def __init__(self, descriptor):
  16745        assert descriptor.isMaybeCrossOriginObject()
  16746 
  16747        args = [
  16748            Argument("JSContext*", "cx"),
  16749            Argument("JS::Handle<JSObject*>", "proxy"),
  16750            Argument("JS::Handle<jsid>", "id"),
  16751            Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"),
  16752        ]
  16753        ClassMethod.__init__(
  16754            self,
  16755            "getOwnPropertyDescriptor",
  16756            "bool",
  16757            args,
  16758            virtual=True,
  16759            override=True,
  16760            const=True,
  16761        )
  16762        self.descriptor = descriptor
  16763 
  16764    def getBody(self):
  16765        return dedent(
  16766            """
  16767            // Implementation of <https://html.spec.whatwg.org/multipage/history.html#location-getownproperty>.
  16768            MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
  16769 
  16770            // Step 1.
  16771            if (IsPlatformObjectSameOrigin(cx, proxy)) {
  16772              { // Scope so we can wrap our PropertyDescriptor back into
  16773                // the caller compartment.
  16774                // Enter the Realm of "proxy" so we can work with it.
  16775                JSAutoRealm ar(cx, proxy);
  16776 
  16777                JS_MarkCrossZoneId(cx, id);
  16778 
  16779                // The spec messes around with configurability of the returned
  16780                // descriptor here, but it's not clear what should actually happen
  16781                // here.  See <https://github.com/whatwg/html/issues/4157>.  For
  16782                // now, keep our old behavior and don't do any magic.
  16783                if (!dom::DOMProxyHandler::getOwnPropertyDescriptor(cx, proxy, id, desc)) {
  16784                  return false;
  16785                }
  16786              }
  16787              return JS_WrapPropertyDescriptor(cx, desc);
  16788            }
  16789 
  16790            // Step 2.
  16791            if (!CrossOriginGetOwnPropertyHelper(cx, proxy, id, desc)) {
  16792              return false;
  16793            }
  16794 
  16795            // Step 3.
  16796            if (desc.isSome()) {
  16797              return true;
  16798            }
  16799 
  16800            // And step 4.
  16801            return CrossOriginPropertyFallback(cx, proxy, id, desc);
  16802            """
  16803        )
  16804 
  16805 
  16806 class CGDOMJSProxyHandler_getSameOriginPrototype(ClassMethod):
  16807    """
  16808    Implementation of getSameOriginPrototype.  We only use this for
  16809    cross-origin objects.
  16810    """
  16811 
  16812    def __init__(self, descriptor):
  16813        assert descriptor.isMaybeCrossOriginObject()
  16814 
  16815        args = [Argument("JSContext*", "cx")]
  16816        ClassMethod.__init__(
  16817            self,
  16818            "getSameOriginPrototype",
  16819            "JSObject*",
  16820            args,
  16821            virtual=True,
  16822            override=True,
  16823            const=True,
  16824        )
  16825        self.descriptor = descriptor
  16826 
  16827    def getBody(self):
  16828        return dedent(
  16829            """
  16830            return GetProtoObjectHandle(cx);
  16831            """
  16832        )
  16833 
  16834 
  16835 class CGDOMJSProxyHandler_definePropertySameOrigin(ClassMethod):
  16836    """
  16837    Implementation of definePropertySameOrigin.  We only use this for
  16838    cross-origin objects.
  16839    """
  16840 
  16841    def __init__(self, descriptor):
  16842        assert descriptor.isMaybeCrossOriginObject()
  16843 
  16844        args = [
  16845            Argument("JSContext*", "cx"),
  16846            Argument("JS::Handle<JSObject*>", "proxy"),
  16847            Argument("JS::Handle<jsid>", "id"),
  16848            Argument("JS::Handle<JS::PropertyDescriptor>", "desc"),
  16849            Argument("JS::ObjectOpResult&", "result"),
  16850        ]
  16851        ClassMethod.__init__(
  16852            self,
  16853            "definePropertySameOrigin",
  16854            "bool",
  16855            args,
  16856            virtual=True,
  16857            override=True,
  16858            const=True,
  16859        )
  16860        self.descriptor = descriptor
  16861 
  16862    def getBody(self):
  16863        return dedent(
  16864            """
  16865            return dom::DOMProxyHandler::defineProperty(cx, proxy, id, desc, result);
  16866            """
  16867        )
  16868 
  16869 
  16870 class CGDOMJSProxyHandler_set(ClassMethod):
  16871    """
  16872    Implementation of set().  We only use this for cross-origin objects.
  16873    """
  16874 
  16875    def __init__(self, descriptor):
  16876        assert descriptor.isMaybeCrossOriginObject()
  16877 
  16878        args = [
  16879            Argument("JSContext*", "cx"),
  16880            Argument("JS::Handle<JSObject*>", "proxy"),
  16881            Argument("JS::Handle<jsid>", "id"),
  16882            Argument("JS::Handle<JS::Value>", "v"),
  16883            Argument("JS::Handle<JS::Value>", "receiver"),
  16884            Argument("JS::ObjectOpResult&", "result"),
  16885        ]
  16886        ClassMethod.__init__(
  16887            self, "set", "bool", args, virtual=True, override=True, const=True
  16888        )
  16889        self.descriptor = descriptor
  16890 
  16891    def getBody(self):
  16892        return dedent(
  16893            """
  16894            if (!IsPlatformObjectSameOrigin(cx, proxy)) {
  16895              return CrossOriginSet(cx, proxy, id, v, receiver, result);
  16896            }
  16897 
  16898            // Safe to enter the Realm of proxy now, since it's same-origin with us.
  16899            JSAutoRealm ar(cx, proxy);
  16900            JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
  16901            if (!MaybeWrapValue(cx, &wrappedReceiver)) {
  16902              return false;
  16903            }
  16904 
  16905            JS::Rooted<JS::Value> wrappedValue(cx, v);
  16906            if (!MaybeWrapValue(cx, &wrappedValue)) {
  16907              return false;
  16908            }
  16909 
  16910            JS_MarkCrossZoneId(cx, id);
  16911 
  16912            return dom::DOMProxyHandler::set(cx, proxy, id, wrappedValue, wrappedReceiver, result);
  16913            """
  16914        )
  16915 
  16916 
  16917 class CGDOMJSProxyHandler_EnsureHolder(ClassMethod):
  16918    """
  16919    Implementation of EnsureHolder().  We only use this for cross-origin objects.
  16920    """
  16921 
  16922    def __init__(self, descriptor):
  16923        args = [
  16924            Argument("JSContext*", "cx"),
  16925            Argument("JS::Handle<JSObject*>", "proxy"),
  16926            Argument("JS::MutableHandle<JSObject*>", "holder"),
  16927        ]
  16928        ClassMethod.__init__(
  16929            self, "EnsureHolder", "bool", args, virtual=True, override=True, const=True
  16930        )
  16931        self.descriptor = descriptor
  16932 
  16933    def getBody(self):
  16934        return dedent(
  16935            """
  16936            return EnsureHolder(cx, proxy,
  16937                                JSCLASS_RESERVED_SLOTS(JS::GetClass(proxy)) - 1,
  16938                                sCrossOriginProperties, holder);
  16939            """
  16940        )
  16941 
  16942 
  16943 class CGDOMJSProxyHandler(CGClass):
  16944    def __init__(self, descriptor):
  16945        assert (
  16946            descriptor.supportsIndexedProperties()
  16947            or descriptor.supportsNamedProperties()
  16948            or descriptor.isMaybeCrossOriginObject()
  16949        )
  16950 
  16951        if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
  16952            assert not descriptor.isMaybeCrossOriginObject()
  16953            parentClass = "ShadowingDOMProxyHandler"
  16954        elif descriptor.isMaybeCrossOriginObject():
  16955            parentClass = "MaybeCrossOriginObject<mozilla::dom::DOMProxyHandler>"
  16956        else:
  16957            parentClass = "mozilla::dom::DOMProxyHandler"
  16958 
  16959        typeAliases = [
  16960            ClassUsingDeclaration("Base", parentClass),
  16961        ]
  16962        methods = [
  16963            CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor),
  16964            CGDOMJSProxyHandler_defineProperty(descriptor),
  16965            ClassUsingFromBaseDeclaration(
  16966                "mozilla::dom::DOMProxyHandler", "defineProperty"
  16967            ),
  16968            CGDOMJSProxyHandler_ownPropNames(descriptor),
  16969            CGDOMJSProxyHandler_hasOwn(descriptor),
  16970            CGDOMJSProxyHandler_get(descriptor),
  16971            CGDOMJSProxyHandler_className(descriptor),
  16972            CGDOMJSProxyHandler_finalizeInBackground(descriptor),
  16973            CGDOMJSProxyHandler_finalize(descriptor),
  16974            CGJSProxyHandler_getInstance("DOMProxyHandler"),
  16975            CGDOMJSProxyHandler_delete(descriptor),
  16976        ]
  16977        if getReflectedHTMLAttributesIface(descriptor):
  16978            methods.append(CGDOMJSProxyHandler_trace(descriptor))
  16979        constructors = [
  16980            ClassConstructor([], constexpr=True, visibility="public", explicit=True)
  16981        ]
  16982 
  16983        if descriptor.supportsIndexedProperties():
  16984            methods.append(CGDOMJSProxyHandler_getElements(descriptor))
  16985        if descriptor.operations["IndexedSetter"] is not None or (
  16986            descriptor.operations["NamedSetter"] is not None
  16987            and descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns")
  16988        ):
  16989            methods.append(CGDOMJSProxyHandler_setCustom(descriptor))
  16990        if descriptor.operations["LegacyCaller"]:
  16991            methods.append(CGDOMJSProxyHandler_call())
  16992            methods.append(CGDOMJSProxyHandler_isCallable())
  16993        if descriptor.interface.hasProbablyShortLivingWrapper():
  16994            if not descriptor.wrapperCache:
  16995                raise TypeError(
  16996                    "Need a wrapper cache to support nursery "
  16997                    "allocation of DOM objects"
  16998                )
  16999            methods.append(CGDOMJSProxyHandler_canNurseryAllocate())
  17000        if descriptor.wrapperCache:
  17001            methods.append(CGDOMJSProxyHandler_objectMoved(descriptor))
  17002 
  17003        if descriptor.isMaybeCrossOriginObject():
  17004            methods.extend(
  17005                [
  17006                    CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor),
  17007                    CGDOMJSProxyHandler_getSameOriginPrototype(descriptor),
  17008                    CGDOMJSProxyHandler_definePropertySameOrigin(descriptor),
  17009                    CGDOMJSProxyHandler_set(descriptor),
  17010                    CGDOMJSProxyHandler_EnsureHolder(descriptor),
  17011                    ClassUsingFromBaseDeclaration(
  17012                        "MaybeCrossOriginObjectMixins", "EnsureHolder"
  17013                    ),
  17014                ]
  17015            )
  17016 
  17017        CGClass.__init__(
  17018            self,
  17019            "DOMProxyHandler",
  17020            bases=[ClassBase(parentClass)],
  17021            typeAliases=typeAliases,
  17022            constructors=constructors,
  17023            methods=methods,
  17024        )
  17025 
  17026 
  17027 class CGDOMJSProxyHandlerDeclarer(CGThing):
  17028    """
  17029    A class for declaring a DOMProxyHandler.
  17030    """
  17031 
  17032    def __init__(self, handlerThing):
  17033        self.handlerThing = handlerThing
  17034 
  17035    def declare(self):
  17036        # Our class declaration should happen when we're defining
  17037        return ""
  17038 
  17039    def define(self):
  17040        return self.handlerThing.declare()
  17041 
  17042 
  17043 class CGDOMJSProxyHandlerDefiner(CGThing):
  17044    """
  17045    A class for defining a DOMProxyHandler.
  17046    """
  17047 
  17048    def __init__(self, handlerThing):
  17049        self.handlerThing = handlerThing
  17050 
  17051    def declare(self):
  17052        return ""
  17053 
  17054    def define(self):
  17055        return self.handlerThing.define()
  17056 
  17057 
  17058 def stripTrailingWhitespace(text):
  17059    tail = "\n" if text.endswith("\n") else ""
  17060    lines = text.splitlines()
  17061    return "\n".join(line.rstrip() for line in lines) + tail
  17062 
  17063 
  17064 class MemberProperties:
  17065    def __init__(self):
  17066        self.isCrossOriginMethod = False
  17067        self.isCrossOriginGetter = False
  17068        self.isCrossOriginSetter = False
  17069 
  17070 
  17071 def memberProperties(m, descriptor):
  17072    props = MemberProperties()
  17073    if m.isMethod():
  17074        if not m.isIdentifierLess() or m == descriptor.operations["Stringifier"]:
  17075            if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
  17076                if m.getExtendedAttribute("CrossOriginCallable"):
  17077                    props.isCrossOriginMethod = True
  17078    elif m.isAttr():
  17079        if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
  17080            if m.getExtendedAttribute("CrossOriginReadable"):
  17081                props.isCrossOriginGetter = True
  17082        if not m.readonly:
  17083            if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
  17084                if m.getExtendedAttribute("CrossOriginWritable"):
  17085                    props.isCrossOriginSetter = True
  17086        elif m.getExtendedAttribute("PutForwards"):
  17087            if m.getExtendedAttribute("CrossOriginWritable"):
  17088                props.isCrossOriginSetter = True
  17089        elif m.getExtendedAttribute("Replaceable") or m.getExtendedAttribute(
  17090            "LegacyLenientSetter"
  17091        ):
  17092            if m.getExtendedAttribute("CrossOriginWritable"):
  17093                props.isCrossOriginSetter = True
  17094 
  17095    return props
  17096 
  17097 
  17098 class CGDescriptor(CGThing):
  17099    def __init__(self, descriptor: Descriptor, attributeTemplates):
  17100        CGThing.__init__(self)
  17101 
  17102        assert (
  17103            not descriptor.concrete
  17104            or descriptor.interface.hasInterfacePrototypeObject()
  17105            or descriptor.hasOrdinaryObjectPrototype()
  17106        )
  17107 
  17108        self.name = descriptor.interface.getClassName()
  17109        self._deps = descriptor.interface.getDeps()
  17110 
  17111        iteratorCGThings = None
  17112        if (
  17113            descriptor.interface.isIterable()
  17114            and descriptor.interface.maplikeOrSetlikeOrIterable.isPairIterator()
  17115        ) or descriptor.interface.isAsyncIterable():
  17116            # We need the Wrap function when using the [Async]IterableIterator type, so we want to declare it before we need it. We don't really want to expose it in the header file, so we make it static too.
  17117            iteratorCGThings = []
  17118            itr_iface = (
  17119                descriptor.interface.maplikeOrSetlikeOrIterable.iteratorType.inner
  17120            )
  17121            iteratorDescriptor = descriptor.getDescriptor(itr_iface.identifier.name)
  17122            iteratorCGThings.append(
  17123                CGWrapNonWrapperCacheMethod(
  17124                    iteratorDescriptor, static=True, signatureOnly=True
  17125                )
  17126            )
  17127            iteratorCGThings = CGList(
  17128                (CGIndenter(t, declareOnly=True) for t in iteratorCGThings), "\n"
  17129            )
  17130            iteratorCGThings = CGWrapper(iteratorCGThings, pre="\n", post="\n")
  17131            iteratorCGThings = CGWrapper(
  17132                CGNamespace(
  17133                    toBindingNamespace(iteratorDescriptor.name), iteratorCGThings
  17134                ),
  17135                post="\n",
  17136            )
  17137 
  17138        cgThings = []
  17139 
  17140        isIteratorInterface = (
  17141            descriptor.interface.isIteratorInterface()
  17142            or descriptor.interface.isAsyncIteratorInterface()
  17143        )
  17144        if not isIteratorInterface:
  17145            cgThings.append(
  17146                CGGeneric(declare="typedef %s NativeType;\n" % descriptor.nativeType)
  17147            )
  17148            parent = descriptor.interface.parent
  17149            if parent:
  17150                cgThings.append(
  17151                    CGGeneric(
  17152                        "static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n"
  17153                        '              "Can\'t inherit from an interface with a different ownership model.");\n'
  17154                        % toBindingNamespace(descriptor.parentPrototypeName)
  17155                    )
  17156                )
  17157 
  17158        defaultToJSONMethod = None
  17159        needCrossOriginPropertyArrays = False
  17160        unscopableNames = list()
  17161 
  17162        for n in descriptor.interface.legacyFactoryFunctions:
  17163            cgThings.append(
  17164                CGClassConstructor(descriptor, n, LegacyFactoryFunctionName(n))
  17165            )
  17166 
  17167        if descriptor.attributeTemplates is not None:
  17168            for template in descriptor.attributeTemplates:
  17169                if template.getter is not None:
  17170                    cgThings.append(
  17171                        CGTemplateForSpecializedGetter(descriptor, template)
  17172                    )
  17173                if template.setter is not None:
  17174                    cgThings.append(
  17175                        CGTemplateForSpecializedSetter(descriptor, template)
  17176                    )
  17177 
  17178        if descriptor.interface.reflectedHTMLAttributesReturningFrozenArray:
  17179            cgThings.append(CGDefineHTMLAttributeSlots(descriptor))
  17180 
  17181        for m in descriptor.interface.members:
  17182            if m.isMethod() and m.identifier.name == "QueryInterface":
  17183                continue
  17184 
  17185            props = memberProperties(m, descriptor)
  17186 
  17187            if m.isMethod():
  17188                if m.getExtendedAttribute("Unscopable"):
  17189                    assert not m.isStatic()
  17190                    unscopableNames.append(m.identifier.name)
  17191                if m.isDefaultToJSON():
  17192                    defaultToJSONMethod = m
  17193                elif (
  17194                    not m.isIdentifierLess()
  17195                    or m == descriptor.operations["Stringifier"]
  17196                ):
  17197                    if m.isStatic():
  17198                        assert descriptor.interface.hasInterfaceObject()
  17199                        cgThings.append(CGStaticMethod(descriptor, m))
  17200                        if m.returnsPromise():
  17201                            cgThings.append(CGStaticMethodJitinfo(m))
  17202                    elif descriptor.interface.hasInterfacePrototypeObject():
  17203                        specializedMethod = CGSpecializedMethod(descriptor, m)
  17204                        cgThings.append(specializedMethod)
  17205                        if m.returnsPromise():
  17206                            cgThings.append(
  17207                                CGMethodPromiseWrapper(descriptor, specializedMethod)
  17208                            )
  17209                        cgThings.append(CGMemberJITInfo(descriptor, m))
  17210                        if props.isCrossOriginMethod:
  17211                            needCrossOriginPropertyArrays = True
  17212            # If we've hit the maplike/setlike member itself, go ahead and
  17213            # generate its convenience functions.
  17214            elif m.isMaplikeOrSetlike():
  17215                cgThings.append(CGMaplikeOrSetlikeHelperGenerator(descriptor, m))
  17216            elif m.isAttr():
  17217                if m.type.isObservableArray():
  17218                    cgThings.append(
  17219                        CGObservableArrayProxyHandlerGenerator(descriptor, m)
  17220                    )
  17221                    cgThings.append(CGObservableArrayHelperGenerator(descriptor, m))
  17222                if m.getExtendedAttribute("Unscopable"):
  17223                    assert not m.isStatic()
  17224                    unscopableNames.append(m.identifier.name)
  17225                if m.isStatic():
  17226                    assert descriptor.interface.hasInterfaceObject()
  17227                    cgThings.append(CGStaticGetter(descriptor, m))
  17228                elif descriptor.interface.hasInterfacePrototypeObject():
  17229                    template = m.getExtendedAttribute("BindingTemplate")
  17230                    if template is not None:
  17231                        templateName = template[0][0]
  17232                        additionalArg = template[0][1]
  17233                        if not (m.type.isPrimitive() or m.type.isString()):
  17234                            raise TypeError(
  17235                                "We only support primitives or strings on templated attributes. "
  17236                                "Attribute '%s' on interface '%s' has type '%s' but tries to "
  17237                                "use template '%s'"
  17238                                % (
  17239                                    m.identifier.name,
  17240                                    descriptor.interface.identifier.name,
  17241                                    m.type,
  17242                                    templateName,
  17243                                )
  17244                            )
  17245                        template = attributeTemplates.get(templateName)
  17246                        specializedGetter = CGSpecializedTemplatedGetter(
  17247                            descriptor, m, template, additionalArg
  17248                        )
  17249                    else:
  17250                        specializedGetter = CGSpecializedGetter(descriptor, m)
  17251                    cgThings.append(specializedGetter)
  17252                    if m.type.isPromise():
  17253                        cgThings.append(
  17254                            CGGetterPromiseWrapper(descriptor, specializedGetter)
  17255                        )
  17256                    if props.isCrossOriginGetter:
  17257                        needCrossOriginPropertyArrays = True
  17258                if not m.readonly:
  17259                    if m.isStatic():
  17260                        assert descriptor.interface.hasInterfaceObject()
  17261                        cgThings.append(CGStaticSetter(descriptor, m))
  17262                    elif descriptor.interface.hasInterfacePrototypeObject():
  17263                        template = m.getExtendedAttribute("BindingTemplate")
  17264                        if template is not None:
  17265                            if isinstance(template[0], list):
  17266                                templateName = template[0][0]
  17267                                additionalArg = template[0][1]
  17268                            else:
  17269                                templateName = template[0]
  17270                                additionalArg = None
  17271                            template = attributeTemplates.get(templateName)
  17272                            specializedSetter = CGSpecializedTemplatedSetter(
  17273                                descriptor, m, template, additionalArg
  17274                            )
  17275                        else:
  17276                            specializedSetter = CGSpecializedSetter(descriptor, m)
  17277                        cgThings.append(specializedSetter)
  17278                        if props.isCrossOriginSetter:
  17279                            needCrossOriginPropertyArrays = True
  17280                elif m.getExtendedAttribute("PutForwards"):
  17281                    cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
  17282                    if props.isCrossOriginSetter:
  17283                        needCrossOriginPropertyArrays = True
  17284                elif m.getExtendedAttribute("Replaceable"):
  17285                    cgThings.append(CGSpecializedReplaceableSetter(descriptor, m))
  17286                elif m.getExtendedAttribute("LegacyLenientSetter"):
  17287                    # XXX In this case, we need to add an include for mozilla/dom/Document.h to the generated cpp file.
  17288                    cgThings.append(CGSpecializedLenientSetter(descriptor, m))
  17289                if (
  17290                    not m.isStatic()
  17291                    and descriptor.interface.hasInterfacePrototypeObject()
  17292                ):
  17293                    cgThings.append(CGMemberJITInfo(descriptor, m))
  17294            if m.isConst() and m.type.isPrimitive():
  17295                cgThings.append(CGConstDefinition(m))
  17296 
  17297        if defaultToJSONMethod:
  17298            cgThings.append(CGDefaultToJSONMethod(descriptor, defaultToJSONMethod))
  17299            cgThings.append(CGMemberJITInfo(descriptor, defaultToJSONMethod))
  17300 
  17301        if descriptor.concrete and not descriptor.proxy:
  17302            # Always have a finalize hook, regardless of whether the class
  17303            # wants a custom hook.
  17304            cgThings.append(CGClassFinalizeHook(descriptor))
  17305 
  17306        properties = PropertyArrays(descriptor)
  17307        cgThings.append(CGGeneric(define=str(properties)))
  17308        cgThings.append(CGNativeProperties(descriptor, properties))
  17309 
  17310        if defaultToJSONMethod:
  17311            # Now that we know about our property arrays, we can
  17312            # output our "collect attribute values" method, which uses those.
  17313            cgThings.append(
  17314                CGCollectJSONAttributesMethod(descriptor, defaultToJSONMethod)
  17315            )
  17316 
  17317        # Declare our DOMProxyHandler.
  17318        if descriptor.concrete and descriptor.proxy:
  17319            cgThings.append(
  17320                CGGeneric(
  17321                    fill(
  17322                        """
  17323                        static_assert(std::is_base_of_v<nsISupports, ${nativeType}>,
  17324                                      "We don't support non-nsISupports native classes for "
  17325                                      "proxy-based bindings yet");
  17326 
  17327                        """,
  17328                        nativeType=descriptor.nativeType,
  17329                    )
  17330                )
  17331            )
  17332            if not descriptor.wrapperCache:
  17333                raise TypeError(
  17334                    "We need a wrappercache to support expandos for proxy-based "
  17335                    "bindings (" + descriptor.name + ")"
  17336                )
  17337            handlerThing = CGDOMJSProxyHandler(descriptor)
  17338            cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing))
  17339            cgThings.append(CGProxyIsProxy(descriptor))
  17340            cgThings.append(CGProxyUnwrap(descriptor))
  17341 
  17342        # Set up our Xray callbacks as needed.  This needs to come
  17343        # after we have our DOMProxyHandler defined.
  17344        if descriptor.wantsXrays:
  17345            if descriptor.concrete and descriptor.proxy:
  17346                if descriptor.needsXrayNamedDeleterHook():
  17347                    cgThings.append(CGDeleteNamedProperty(descriptor))
  17348            elif descriptor.needsXrayResolveHooks():
  17349                cgThings.append(CGResolveOwnPropertyViaResolve(descriptor))
  17350                cgThings.append(
  17351                    CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor)
  17352                )
  17353            if descriptor.wantsXrayExpandoClass:
  17354                cgThings.append(CGXrayExpandoJSClass(descriptor))
  17355 
  17356            # Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff
  17357            # done, set up our NativePropertyHooks.
  17358            cgThings.append(CGNativePropertyHooks(descriptor, properties))
  17359 
  17360        if descriptor.interface.isNamespace():
  17361            cgThings.append(CGNamespaceObjectJSClass(descriptor))
  17362        elif descriptor.interface.hasInterfaceObject():
  17363            cgThings.append(CGClassConstructor(descriptor, descriptor.interface.ctor()))
  17364            cgThings.append(CGInterfaceObjectInfo(descriptor))
  17365            cgThings.append(CGLegacyFactoryFunctions(descriptor))
  17366 
  17367        cgThings.append(CGLegacyCallHook(descriptor))
  17368        if descriptor.interface.getExtendedAttribute("NeedResolve"):
  17369            cgThings.append(CGResolveHook(descriptor))
  17370            cgThings.append(CGMayResolveHook(descriptor))
  17371            cgThings.append(CGEnumerateHook(descriptor))
  17372 
  17373        if descriptor.hasNamedPropertiesObject:
  17374            cgThings.append(CGGetNamedPropertiesObjectMethod(descriptor))
  17375 
  17376        if descriptor.interface.hasInterfacePrototypeObject():
  17377            cgThings.append(CGPrototypeJSClass(descriptor, properties))
  17378 
  17379        if (
  17380            descriptor.interface.hasInterfaceObject()
  17381            and not descriptor.interface.isExternal()
  17382            and descriptor.isExposedConditionally()
  17383        ):
  17384            cgThings.append(CGConstructorEnabled(descriptor))
  17385 
  17386        if (
  17387            descriptor.interface.hasMembersInSlots()
  17388            and descriptor.interface.hasChildInterfaces()
  17389        ):
  17390            raise TypeError(
  17391                "We don't support members in slots on "
  17392                "non-leaf interfaces like %s" % descriptor.interface.identifier.name
  17393            )
  17394 
  17395        if descriptor.needsMissingPropUseCounters:
  17396            cgThings.append(CGCountMaybeMissingProperty(descriptor))
  17397 
  17398        if descriptor.interface.identifier.name == "HTMLDocument":
  17399            cgThings.append(CGInterfaceHasProperty(descriptor))
  17400 
  17401        # CGDOMProxyJSClass/CGDOMJSClass need GetProtoObjectHandle, but we don't
  17402        # want to export it for the iterator interfaces, or if we don't need it
  17403        # for child interfaces or for the named properties object.
  17404        protoObjectHandleGetterIsStatic = descriptor.concrete and (
  17405            isIteratorInterface
  17406            or (
  17407                descriptor.interface.hasInterfacePrototypeObject()
  17408                and not descriptor.interface.hasChildInterfaces()
  17409                and not descriptor.hasNamedPropertiesObject
  17410            )
  17411        )
  17412        if descriptor.concrete:
  17413            if descriptor.interface.isSerializable():
  17414                cgThings.append(CGSerializer(descriptor))
  17415                cgThings.append(CGDeserializer(descriptor))
  17416 
  17417            if protoObjectHandleGetterIsStatic:
  17418                cgThings.append(
  17419                    CGGetProtoObjectHandleMethod(
  17420                        descriptor, static=True, signatureOnly=True
  17421                    )
  17422                )
  17423 
  17424            if descriptor.proxy:
  17425                cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing))
  17426                cgThings.append(CGDOMProxyJSClass(descriptor))
  17427            else:
  17428                cgThings.append(CGDOMJSClass(descriptor))
  17429 
  17430            if descriptor.interface.hasMembersInSlots():
  17431                cgThings.append(CGUpdateMemberSlotsMethod(descriptor))
  17432 
  17433            if descriptor.isGlobal():
  17434                assert descriptor.wrapperCache
  17435                cgThings.append(CGWrapGlobalMethod(descriptor, properties))
  17436            elif descriptor.wrapperCache:
  17437                cgThings.append(CGWrapWithCacheMethod(descriptor))
  17438                cgThings.append(CGWrapMethod(descriptor))
  17439            else:
  17440                cgThings.append(
  17441                    CGWrapNonWrapperCacheMethod(descriptor, static=isIteratorInterface)
  17442                )
  17443 
  17444        # If we're not wrappercached, we don't know how to clear our
  17445        # cached values, since we can't get at the JSObject.
  17446        if descriptor.wrapperCache:
  17447            cgThings.extend(
  17448                CGClearCachedValueMethod(descriptor, m)
  17449                for m in clearableCachedAttrs(descriptor)
  17450            )
  17451 
  17452        haveUnscopables = (
  17453            len(unscopableNames) != 0
  17454            and descriptor.interface.hasInterfacePrototypeObject()
  17455        )
  17456        if haveUnscopables:
  17457            cgThings.append(
  17458                CGList(
  17459                    [
  17460                        CGGeneric("static const char* const unscopableNames[] = {"),
  17461                        CGIndenter(
  17462                            CGList(
  17463                                [CGGeneric('"%s"' % name) for name in unscopableNames]
  17464                                + [CGGeneric("nullptr")],
  17465                                ",\n",
  17466                            )
  17467                        ),
  17468                        CGGeneric("};\n"),
  17469                    ],
  17470                    "\n",
  17471                )
  17472            )
  17473 
  17474        legacyWindowAliases = descriptor.interface.legacyWindowAliases
  17475        haveLegacyWindowAliases = len(legacyWindowAliases) != 0
  17476        if haveLegacyWindowAliases:
  17477            cgThings.append(
  17478                CGList(
  17479                    [
  17480                        CGGeneric("static const char* const legacyWindowAliases[] = {"),
  17481                        CGIndenter(
  17482                            CGList(
  17483                                [
  17484                                    CGGeneric('"%s"' % name)
  17485                                    for name in legacyWindowAliases
  17486                                ]
  17487                                + [CGGeneric("nullptr")],
  17488                                ",\n",
  17489                            )
  17490                        ),
  17491                        CGGeneric("};\n"),
  17492                    ],
  17493                    "\n",
  17494                )
  17495            )
  17496 
  17497        if not descriptor.hasOrdinaryObjectPrototype():
  17498            # CGCreateInterfaceObjectsMethod needs to come after our
  17499            # CGDOMJSClass and unscopables, if any.
  17500            cgThings.append(
  17501                CGCreateInterfaceObjectsMethod(
  17502                    descriptor,
  17503                    properties,
  17504                    haveUnscopables,
  17505                    haveLegacyWindowAliases,
  17506                    static=isIteratorInterface,
  17507                )
  17508            )
  17509 
  17510            # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need
  17511            # to come after CGCreateInterfaceObjectsMethod.
  17512            if descriptor.interface.hasInterfacePrototypeObject():
  17513                cgThings.append(
  17514                    CGGetProtoObjectHandleMethod(
  17515                        descriptor, static=protoObjectHandleGetterIsStatic
  17516                    )
  17517                )
  17518                if descriptor.interface.hasChildInterfaces():
  17519                    assert not isIteratorInterface
  17520                    cgThings.append(CGGetProtoObjectMethod(descriptor))
  17521            if descriptor.interface.hasInterfaceObject():
  17522                cgThings.append(CGGetConstructorObjectHandleMethod(descriptor))
  17523                cgThings.append(
  17524                    CGCreateAndDefineOnGlobalMethod(
  17525                        descriptor,
  17526                    )
  17527                )
  17528 
  17529        # See whether we need to generate cross-origin property arrays.
  17530        if needCrossOriginPropertyArrays:
  17531            cgThings.append(CGCrossOriginProperties(descriptor))
  17532 
  17533        cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
  17534        cgThings = CGWrapper(cgThings, pre="\n", post="\n")
  17535        cgThings = CGWrapper(
  17536            CGNamespace(toBindingNamespace(descriptor.name), cgThings), post="\n"
  17537        )
  17538        self.cgRoot = CGList([iteratorCGThings, cgThings], "\n")
  17539 
  17540    def declare(self):
  17541        return self.cgRoot.declare()
  17542 
  17543    def define(self):
  17544        return self.cgRoot.define()
  17545 
  17546    def forward_declare(self):
  17547        return f"class {self.name};"
  17548 
  17549    def deps(self):
  17550        return self._deps
  17551 
  17552 
  17553 class CGNamespacedEnum(CGThing):
  17554    def __init__(self, namespace, enumName, names, values, comment=""):
  17555        if not values:
  17556            values = []
  17557 
  17558        # Account for explicit enum values.
  17559        entries = []
  17560        for i in range(0, len(names)):
  17561            if len(values) > i and values[i] is not None:
  17562                entry = "%s = %s" % (names[i], values[i])
  17563            else:
  17564                entry = names[i]
  17565            entries.append(entry)
  17566 
  17567        # Append a Count.
  17568        entries.append("_" + enumName + "_Count")
  17569 
  17570        # Indent.
  17571        entries = ["  " + e for e in entries]
  17572 
  17573        # Build the enum body.
  17574        enumstr = comment + "enum %s : uint16_t\n{\n%s\n};\n" % (
  17575            enumName,
  17576            ",\n".join(entries),
  17577        )
  17578        curr = CGGeneric(declare=enumstr)
  17579 
  17580        # Add some whitespace padding.
  17581        curr = CGWrapper(curr, pre="\n", post="\n")
  17582 
  17583        # Add the namespace.
  17584        curr = CGNamespace(namespace, curr)
  17585 
  17586        # Add the typedef
  17587        typedef = "\ntypedef %s::%s %s;\n\n" % (namespace, enumName, enumName)
  17588        curr = CGList([curr, CGGeneric(declare=typedef)])
  17589 
  17590        # Save the result.
  17591        self.node = curr
  17592 
  17593    def declare(self):
  17594        return self.node.declare()
  17595 
  17596    def define(self):
  17597        return ""
  17598 
  17599 
  17600 def initIdsClassMethod(identifiers, atomCacheName):
  17601    idinit = [
  17602        '!atomsCache->%s.init(cx, "%s")' % (CGDictionary.makeIdName(id), id)
  17603        for id in identifiers
  17604    ]
  17605    idinit.reverse()
  17606    body = fill(
  17607        """
  17608        MOZ_ASSERT(reinterpret_cast<jsid*>(atomsCache)->isVoid());
  17609 
  17610        // Initialize these in reverse order so that any failure leaves the first one
  17611        // uninitialized.
  17612        if (${idinit}) {
  17613          return false;
  17614        }
  17615        return true;
  17616        """,
  17617        idinit=" ||\n    ".join(idinit),
  17618    )
  17619    return ClassMethod(
  17620        "InitIds",
  17621        "bool",
  17622        [Argument("JSContext*", "cx"), Argument("%s*" % atomCacheName, "atomsCache")],
  17623        static=True,
  17624        body=body,
  17625        visibility="private",
  17626    )
  17627 
  17628 
  17629 class CGDictionary(CGThing):
  17630    def __init__(self, dictionary: IDLDictionary, descriptorProvider):
  17631        self.dictionary = dictionary
  17632        self.descriptorProvider = descriptorProvider
  17633        self.needToInitIds = len(dictionary.members) > 0
  17634        self.memberInfo = [
  17635            (
  17636                member,
  17637                getJSToNativeConversionInfo(
  17638                    member.type,
  17639                    descriptorProvider,
  17640                    isMember="Dictionary",
  17641                    isOptional=member.canHaveMissingValue(),
  17642                    isKnownMissing=not dictionary.needsConversionFromJS,
  17643                    defaultValue=member.defaultValue,
  17644                    sourceDescription=self.getMemberSourceDescription(member),
  17645                ),
  17646            )
  17647            for member in dictionary.members
  17648        ]
  17649 
  17650        # If we have a union member which is going to be declared in a different
  17651        # header but contains something that will be declared in the same header
  17652        # as us, bail: the C++ includes won't work out.
  17653        for member in dictionary.members:
  17654            type = member.type.unroll()
  17655            if type.isUnion() and CGHeaders.getUnionDeclarationFilename(
  17656                descriptorProvider.getConfig(), type
  17657            ) != CGHeaders.getDeclarationFilename(dictionary):
  17658                for t in type.flatMemberTypes:
  17659                    if t.isDictionary() and CGHeaders.getDeclarationFilename(
  17660                        t.inner
  17661                    ) == CGHeaders.getDeclarationFilename(dictionary):
  17662                        raise TypeError(
  17663                            "Dictionary contains a union that will live in a different "
  17664                            "header that contains a dictionary from the same header as "
  17665                            "the original dictionary.  This won't compile.  Move the "
  17666                            "inner dictionary to a different Web IDL file to move it "
  17667                            "to a different header.\n%s\n%s"
  17668                            % (t.location, t.inner.location)
  17669                        )
  17670        self.structs = self.getStructs()
  17671 
  17672    def declare(self):
  17673        return self.structs.declare()
  17674 
  17675    def define(self):
  17676        return self.structs.define()
  17677 
  17678    def forward_declare(self):
  17679        return f"struct {self.dictionary.identifier.name};"
  17680 
  17681    def base(self):
  17682        if self.dictionary.parent:
  17683            return self.makeClassName(self.dictionary.parent)
  17684        return "DictionaryBase"
  17685 
  17686    def initMethod(self):
  17687        """
  17688        This function outputs the body of the Init() method for the dictionary.
  17689 
  17690        For the most part, this is some bookkeeping for our atoms so
  17691        we can avoid atomizing strings all the time, then we just spit
  17692        out the getMemberConversion() output for each member,
  17693        separated by newlines.
  17694 
  17695        """
  17696        body = dedent(
  17697            """
  17698            // Passing a null JSContext is OK only if we're initing from null,
  17699            // Since in that case we will not have to do any property gets
  17700            // Also evaluate isNullOrUndefined in order to avoid false-positive
  17701            // checkers by static analysis tools
  17702            MOZ_ASSERT_IF(!cx, val.isNull() && val.isNullOrUndefined());
  17703            """
  17704        )
  17705 
  17706        if self.needToInitIds:
  17707            body += fill(
  17708                """
  17709                ${dictName}Atoms* atomsCache = nullptr;
  17710                if (cx) {
  17711                  atomsCache = GetAtomCache<${dictName}Atoms>(cx);
  17712                  if (reinterpret_cast<jsid*>(atomsCache)->isVoid() &&
  17713                      !InitIds(cx, atomsCache)) {
  17714                    return false;
  17715                  }
  17716                }
  17717 
  17718                """,
  17719                dictName=self.makeClassName(self.dictionary),
  17720            )
  17721 
  17722        if self.dictionary.parent:
  17723            body += fill(
  17724                """
  17725                // Per spec, we init the parent's members first
  17726                if (!${dictName}::Init(cx, val)) {
  17727                  return false;
  17728                }
  17729 
  17730                """,
  17731                dictName=self.makeClassName(self.dictionary.parent),
  17732            )
  17733        else:
  17734            body += dedent(
  17735                """
  17736                if (!IsConvertibleToDictionary(val)) {
  17737                  return cx.ThrowErrorMessage<MSG_CONVERSION_ERROR>(sourceDescription, "dictionary");
  17738                }
  17739 
  17740                """
  17741            )
  17742 
  17743        memberInits = [self.getMemberConversion(m).define() for m in self.memberInfo]
  17744        if memberInits:
  17745            body += fill(
  17746                """
  17747                bool isNull = val.isNullOrUndefined();
  17748                // We only need these if !isNull, in which case we have |cx|.
  17749                Maybe<JS::Rooted<JSObject *> > object;
  17750                Maybe<JS::Rooted<JS::Value> > temp;
  17751                if (!isNull) {
  17752                  MOZ_ASSERT(cx);
  17753                  object.emplace(cx, &val.toObject());
  17754                  temp.emplace(cx);
  17755                }
  17756                $*{memberInits}
  17757                """,
  17758                memberInits="\n".join(memberInits),
  17759            )
  17760 
  17761        body += "return true;\n"
  17762 
  17763        return ClassMethod(
  17764            "Init",
  17765            "bool",
  17766            [
  17767                Argument("BindingCallContext&", "cx"),
  17768                Argument("JS::Handle<JS::Value>", "val"),
  17769                Argument("const char*", "sourceDescription", default='"Value"'),
  17770                Argument("bool", "passedToJSImpl", default="false"),
  17771            ],
  17772            body=body,
  17773        )
  17774 
  17775    def initWithoutCallContextMethod(self):
  17776        """
  17777        This function outputs the body of an Init() method for the dictionary
  17778        that takes just a JSContext*.  This is needed for non-binding consumers.
  17779        """
  17780        body = dedent(
  17781            """
  17782            // We don't want to use sourceDescription for our context here;
  17783            // that's not really what it's formatted for.
  17784            BindingCallContext cx(cx_, nullptr);
  17785            return Init(cx, val, sourceDescription, passedToJSImpl);
  17786            """
  17787        )
  17788        return ClassMethod(
  17789            "Init",
  17790            "bool",
  17791            [
  17792                Argument("JSContext*", "cx_"),
  17793                Argument("JS::Handle<JS::Value>", "val"),
  17794                Argument("const char*", "sourceDescription", default='"Value"'),
  17795                Argument("bool", "passedToJSImpl", default="false"),
  17796            ],
  17797            body=body,
  17798        )
  17799 
  17800    def simpleInitMethod(self):
  17801        """
  17802        This function outputs the body of the Init() method for the dictionary,
  17803        for cases when we are just default-initializing it.
  17804 
  17805        """
  17806        relevantMembers = [
  17807            m
  17808            for m in self.memberInfo
  17809            # We only need to init the things that can have
  17810            # default values.
  17811            if m[0].optional and m[0].defaultValue
  17812        ]
  17813 
  17814        # We mostly avoid outputting code that uses cx in our native-to-JS
  17815        # conversions, but there is one exception: we may have a
  17816        # dictionary-typed member that _does_ generally support conversion from
  17817        # JS.  If we have such a thing, we can pass it a null JSContext and
  17818        # JS::NullHandleValue to default-initialize it, but since the
  17819        # native-to-JS templates hardcode `cx` as the JSContext value, we're
  17820        # going to need to provide that.
  17821        haveMemberThatNeedsCx = any(
  17822            m[0].type.isDictionary() and m[0].type.unroll().inner.needsConversionFromJS
  17823            for m in relevantMembers
  17824        )
  17825        if haveMemberThatNeedsCx:
  17826            body = dedent(
  17827                """
  17828                JSContext* cx = nullptr;
  17829                """
  17830            )
  17831        else:
  17832            body = ""
  17833 
  17834        if self.dictionary.parent:
  17835            if self.dictionary.parent.needsConversionFromJS:
  17836                args = "nullptr, JS::NullHandleValue"
  17837            else:
  17838                args = ""
  17839            body += fill(
  17840                """
  17841                // We init the parent's members first
  17842                if (!${dictName}::Init(${args})) {
  17843                  return false;
  17844                }
  17845 
  17846                """,
  17847                dictName=self.makeClassName(self.dictionary.parent),
  17848                args=args,
  17849            )
  17850 
  17851        memberInits = [
  17852            self.getMemberConversion(m, isKnownMissing=True).define()
  17853            for m in relevantMembers
  17854        ]
  17855        if memberInits:
  17856            body += fill(
  17857                """
  17858                $*{memberInits}
  17859                """,
  17860                memberInits="\n".join(memberInits),
  17861            )
  17862 
  17863        body += "return true;\n"
  17864 
  17865        return ClassMethod(
  17866            "Init",
  17867            "bool",
  17868            [
  17869                Argument("const char*", "sourceDescription", default='"Value"'),
  17870                Argument("bool", "passedToJSImpl", default="false"),
  17871            ],
  17872            body=body,
  17873        )
  17874 
  17875    def initFromJSONMethod(self):
  17876        return ClassMethod(
  17877            "Init",
  17878            "bool",
  17879            [Argument("const nsAString&", "aJSON")],
  17880            body=dedent(
  17881                """
  17882                AutoJSAPI jsapi;
  17883                JSObject* cleanGlobal = SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::BindingDetail);
  17884                if (!cleanGlobal) {
  17885                  return false;
  17886                }
  17887                if (!jsapi.Init(cleanGlobal)) {
  17888                  return false;
  17889                }
  17890                JSContext* cx = jsapi.cx();
  17891                JS::Rooted<JS::Value> json(cx);
  17892                bool ok = ParseJSON(cx, aJSON, &json);
  17893                NS_ENSURE_TRUE(ok, false);
  17894                return Init(cx, json);
  17895                """
  17896            ),
  17897        )
  17898 
  17899    def toJSONMethod(self):
  17900        return ClassMethod(
  17901            "ToJSON",
  17902            "bool",
  17903            [Argument("nsAString&", "aJSON")],
  17904            body=dedent(
  17905                """
  17906                AutoJSAPI jsapi;
  17907                jsapi.Init();
  17908                JSContext *cx = jsapi.cx();
  17909                // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here
  17910                // because we'll only be creating objects, in ways that have no
  17911                // side-effects, followed by a call to JS::ToJSONMaybeSafely,
  17912                // which likewise guarantees no side-effects for the sorts of
  17913                // things we will pass it.
  17914                JSObject* scope = UnprivilegedJunkScopeOrWorkerGlobal(fallible);
  17915                if (!scope) {
  17916                  JS_ReportOutOfMemory(cx);
  17917                  return false;
  17918                }
  17919                JSAutoRealm ar(cx, scope);
  17920                JS::Rooted<JS::Value> val(cx);
  17921                if (!ToObjectInternal(cx, &val)) {
  17922                  return false;
  17923                }
  17924                JS::Rooted<JSObject*> obj(cx, &val.toObject());
  17925                return StringifyToJSON(cx, obj, aJSON);
  17926            """
  17927            ),
  17928            const=True,
  17929        )
  17930 
  17931    def toObjectInternalMethod(self):
  17932        body = ""
  17933        if self.needToInitIds:
  17934            body += fill(
  17935                """
  17936                ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx);
  17937                if (reinterpret_cast<jsid*>(atomsCache)->isVoid() &&
  17938                    !InitIds(cx, atomsCache)) {
  17939                  return false;
  17940                }
  17941 
  17942                """,
  17943                dictName=self.makeClassName(self.dictionary),
  17944            )
  17945 
  17946        if self.dictionary.parent:
  17947            body += fill(
  17948                """
  17949                // Per spec, we define the parent's members first
  17950                if (!${dictName}::ToObjectInternal(cx, rval)) {
  17951                  return false;
  17952                }
  17953                JS::Rooted<JSObject*> obj(cx, &rval.toObject());
  17954 
  17955                """,
  17956                dictName=self.makeClassName(self.dictionary.parent),
  17957            )
  17958        else:
  17959            body += dedent(
  17960                """
  17961                JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
  17962                if (!obj) {
  17963                  return false;
  17964                }
  17965                rval.set(JS::ObjectValue(*obj));
  17966 
  17967                """
  17968            )
  17969 
  17970        if self.memberInfo:
  17971            body += "\n".join(
  17972                self.getMemberDefinition(m).define() for m in self.memberInfo
  17973            )
  17974        body += "\nreturn true;\n"
  17975 
  17976        return ClassMethod(
  17977            "ToObjectInternal",
  17978            "bool",
  17979            [
  17980                Argument("JSContext*", "cx"),
  17981                Argument("JS::MutableHandle<JS::Value>", "rval"),
  17982            ],
  17983            const=True,
  17984            body=body,
  17985        )
  17986 
  17987    def initIdsMethod(self):
  17988        assert self.needToInitIds
  17989        return initIdsClassMethod(
  17990            [m.identifier.name for m in self.dictionary.members],
  17991            "%sAtoms" % self.makeClassName(self.dictionary),
  17992        )
  17993 
  17994    def traceDictionaryMethod(self):
  17995        body = ""
  17996        if self.dictionary.parent:
  17997            cls = self.makeClassName(self.dictionary.parent)
  17998            body += "%s::TraceDictionary(trc);\n" % cls
  17999 
  18000        memberTraces = [
  18001            self.getMemberTrace(m)
  18002            for m in self.dictionary.members
  18003            if typeNeedsRooting(m.type)
  18004        ]
  18005 
  18006        if memberTraces:
  18007            body += "\n".join(memberTraces)
  18008 
  18009        return ClassMethod(
  18010            "TraceDictionary",
  18011            "void",
  18012            [
  18013                Argument("JSTracer*", "trc"),
  18014            ],
  18015            body=body,
  18016        )
  18017 
  18018    @staticmethod
  18019    def dictionaryNeedsCycleCollection(dictionary):
  18020        return any(idlTypeNeedsCycleCollection(m.type) for m in dictionary.members) or (
  18021            dictionary.parent
  18022            and CGDictionary.dictionaryNeedsCycleCollection(dictionary.parent)
  18023        )
  18024 
  18025    def traverseForCCMethod(self):
  18026        body = ""
  18027        if self.dictionary.parent and self.dictionaryNeedsCycleCollection(
  18028            self.dictionary.parent
  18029        ):
  18030            cls = self.makeClassName(self.dictionary.parent)
  18031            body += "%s::TraverseForCC(aCallback, aFlags);\n" % cls
  18032 
  18033        for m, _ in self.memberInfo:
  18034            if idlTypeNeedsCycleCollection(m.type):
  18035                memberName = self.makeMemberName(m.identifier.name)
  18036                body += (
  18037                    'ImplCycleCollectionTraverse(aCallback, %s, "%s", aFlags);\n'
  18038                    % (memberName, memberName)
  18039                )
  18040 
  18041        return ClassMethod(
  18042            "TraverseForCC",
  18043            "void",
  18044            [
  18045                Argument("nsCycleCollectionTraversalCallback&", "aCallback"),
  18046                Argument("uint32_t", "aFlags"),
  18047            ],
  18048            body=body,
  18049            # Inline so we don't pay a codesize hit unless someone actually uses
  18050            # this traverse method.
  18051            inline=True,
  18052            bodyInHeader=True,
  18053        )
  18054 
  18055    def unlinkForCCMethod(self):
  18056        body = ""
  18057        if self.dictionary.parent and self.dictionaryNeedsCycleCollection(
  18058            self.dictionary.parent
  18059        ):
  18060            cls = self.makeClassName(self.dictionary.parent)
  18061            body += "%s::UnlinkForCC();\n" % cls
  18062 
  18063        for m, _ in self.memberInfo:
  18064            if idlTypeNeedsCycleCollection(m.type):
  18065                memberName = self.makeMemberName(m.identifier.name)
  18066                body += "ImplCycleCollectionUnlink(%s);\n" % memberName
  18067 
  18068        return ClassMethod(
  18069            "UnlinkForCC",
  18070            "void",
  18071            [],
  18072            body=body,
  18073            # Inline so we don't pay a codesize hit unless someone actually uses
  18074            # this unlink method.
  18075            inline=True,
  18076            bodyInHeader=True,
  18077        )
  18078 
  18079    def assignmentOperator(self):
  18080        body = CGList([])
  18081        body.append(CGGeneric("%s::operator=(aOther);\n" % self.base()))
  18082 
  18083        for m, _ in self.memberInfo:
  18084            memberName = self.makeMemberName(m.identifier.name)
  18085            if m.canHaveMissingValue():
  18086                memberAssign = CGGeneric(
  18087                    fill(
  18088                        """
  18089                    ${name}.Reset();
  18090                    if (aOther.${name}.WasPassed()) {
  18091                      ${name}.Construct(aOther.${name}.Value());
  18092                    }
  18093                    """,
  18094                        name=memberName,
  18095                    )
  18096                )
  18097            else:
  18098                memberAssign = CGGeneric("%s = aOther.%s;\n" % (memberName, memberName))
  18099            body.append(memberAssign)
  18100        body.append(CGGeneric("return *this;\n"))
  18101        return ClassMethod(
  18102            "operator=",
  18103            "%s&" % self.makeClassName(self.dictionary),
  18104            [Argument("const %s&" % self.makeClassName(self.dictionary), "aOther")],
  18105            body=body.define(),
  18106        )
  18107 
  18108    def equalityOperator(self):
  18109        # For now we only allow equality operators if our members have a string
  18110        # type, a primitive type or an enum type.
  18111        if not all(
  18112            m.type.isString() or m.type.isPrimitive() or m.type.isEnum()
  18113            for m in self.dictionary.members
  18114        ):
  18115            err = (
  18116                "[GenerateEqualityOperator] set on %s, but it"
  18117                % self.dictionary.needsEqualityOperator.identifier.name
  18118            )
  18119            if self.dictionary.needsEqualityOperator != self.dictionary:
  18120                err += "s ancestor %s" % self.dictionary.identifier.name
  18121            err += " contains types other than string, primitive or enum types."
  18122            raise TypeError(err)
  18123 
  18124        body = CGList([])
  18125 
  18126        if self.dictionary.parent:
  18127            # If we have a parent dictionary we have to call its equals
  18128            # operator.
  18129            parentTest = CGGeneric(
  18130                fill(
  18131                    """
  18132                    if (!${base}::operator==(aOther)) {
  18133                        return false;
  18134                    }
  18135                    """,
  18136                    base=self.makeClassName(self.dictionary.parent),
  18137                )
  18138            )
  18139            body.append(parentTest)
  18140 
  18141        for m, _ in self.memberInfo:
  18142            memberName = self.makeMemberName(m.identifier.name)
  18143            memberTest = CGGeneric(
  18144                fill(
  18145                    """
  18146                    if (${memberName} != aOther.${memberName}) {
  18147                        return false;
  18148                    }
  18149                    """,
  18150                    memberName=memberName,
  18151                )
  18152            )
  18153            body.append(memberTest)
  18154        body.append(CGGeneric("return true;\n"))
  18155        return ClassMethod(
  18156            "operator==",
  18157            "bool",
  18158            [Argument("const %s&" % self.makeClassName(self.dictionary), "aOther")],
  18159            const=True,
  18160            body=body.define(),
  18161        )
  18162 
  18163    def getStructs(self):
  18164        d = self.dictionary
  18165        selfName = self.makeClassName(d)
  18166        members = [
  18167            ClassMember(
  18168                self.makeMemberName(m[0].identifier.name),
  18169                self.getMemberType(m),
  18170                visibility="public",
  18171                body=self.getMemberInitializer(m),
  18172                hasIgnoreInitCheckFlag=True,
  18173            )
  18174            for m in self.memberInfo
  18175        ]
  18176        if d.parent:
  18177            # We always want to init our parent with our non-initializing
  18178            # constructor arg, because either we're about to init ourselves (and
  18179            # hence our parent) or we don't want any init happening.
  18180            baseConstructors = [
  18181                "%s(%s)"
  18182                % (self.makeClassName(d.parent), self.getNonInitializingCtorArg())
  18183            ]
  18184        else:
  18185            baseConstructors = None
  18186 
  18187        if d.needsConversionFromJS:
  18188            initArgs = "nullptr, JS::NullHandleValue"
  18189        else:
  18190            initArgs = ""
  18191        ctors = [
  18192            ClassConstructor(
  18193                [],
  18194                visibility="public",
  18195                baseConstructors=baseConstructors,
  18196                body=(
  18197                    "// Safe to pass a null context if we pass a null value\n"
  18198                    "Init(%s);\n" % initArgs
  18199                ),
  18200            ),
  18201            ClassConstructor(
  18202                [Argument("const FastDictionaryInitializer&", "")],
  18203                visibility="public",
  18204                baseConstructors=baseConstructors,
  18205                explicit=True,
  18206                bodyInHeader=True,
  18207                body='// Do nothing here; this is used by our "Fast" subclass\n',
  18208            ),
  18209        ]
  18210        methods = []
  18211 
  18212        if self.needToInitIds:
  18213            methods.append(self.initIdsMethod())
  18214 
  18215        if d.needsConversionFromJS:
  18216            methods.append(self.initMethod())
  18217            methods.append(self.initWithoutCallContextMethod())
  18218        else:
  18219            methods.append(self.simpleInitMethod())
  18220 
  18221        canBeRepresentedAsJSON = self.dictionarySafeToJSONify(d)
  18222        if canBeRepresentedAsJSON and d.getExtendedAttribute("GenerateInitFromJSON"):
  18223            methods.append(self.initFromJSONMethod())
  18224 
  18225        if d.needsConversionToJS:
  18226            methods.append(self.toObjectInternalMethod())
  18227 
  18228        if canBeRepresentedAsJSON and d.getExtendedAttribute("GenerateToJSON"):
  18229            methods.append(self.toJSONMethod())
  18230 
  18231        methods.append(self.traceDictionaryMethod())
  18232 
  18233        try:
  18234            if self.dictionaryNeedsCycleCollection(d):
  18235                methods.append(self.traverseForCCMethod())
  18236                methods.append(self.unlinkForCCMethod())
  18237        except CycleCollectionUnsupported:
  18238            # We have some member that we don't know how to CC.  Don't output
  18239            # our cycle collection overloads, so attempts to CC us will fail to
  18240            # compile instead of misbehaving.
  18241            pass
  18242 
  18243        ctors.append(
  18244            ClassConstructor(
  18245                [Argument("%s&&" % selfName, "aOther")],
  18246                default=True,
  18247                visibility="public",
  18248                baseConstructors=baseConstructors,
  18249            )
  18250        )
  18251 
  18252        if CGDictionary.isDictionaryCopyConstructible(d):
  18253            disallowCopyConstruction = False
  18254            # Note: gcc's -Wextra has a warning against not initializng our
  18255            # base explicitly. If we have one. Use our non-initializing base
  18256            # constructor to get around that.
  18257            ctors.append(
  18258                ClassConstructor(
  18259                    [Argument("const %s&" % selfName, "aOther")],
  18260                    bodyInHeader=True,
  18261                    visibility="public",
  18262                    baseConstructors=baseConstructors,
  18263                    explicit=True,
  18264                    body="*this = aOther;\n",
  18265                )
  18266            )
  18267            methods.append(self.assignmentOperator())
  18268        else:
  18269            disallowCopyConstruction = True
  18270 
  18271        if d.needsEqualityOperator:
  18272            methods.append(self.equalityOperator())
  18273        elif d.parent and d.parent.needsEqualityOperator:
  18274            methods.append(
  18275                ClassMethod(
  18276                    "operator==",
  18277                    "bool",
  18278                    [
  18279                        Argument(
  18280                            "const %s&" % self.makeClassName(self.dictionary), "aOther"
  18281                        )
  18282                    ],
  18283                    visibility="public",
  18284                    delete=True,
  18285                )
  18286            )
  18287 
  18288        struct = CGClass(
  18289            selfName,
  18290            bases=[ClassBase(self.base())],
  18291            members=members,
  18292            constructors=ctors,
  18293            methods=methods,
  18294            isStruct=True,
  18295            disallowCopyConstruction=disallowCopyConstruction,
  18296        )
  18297 
  18298        fastDictionaryCtor = ClassConstructor(
  18299            [],
  18300            visibility="public",
  18301            bodyInHeader=True,
  18302            baseConstructors=["%s(%s)" % (selfName, self.getNonInitializingCtorArg())],
  18303            body="// Doesn't matter what int we pass to the parent constructor\n",
  18304        )
  18305 
  18306        fastStruct = CGClass(
  18307            "Fast" + selfName,
  18308            bases=[ClassBase(selfName)],
  18309            constructors=[fastDictionaryCtor],
  18310            isStruct=True,
  18311        )
  18312 
  18313        return CGList([struct, CGNamespace("binding_detail", fastStruct)], "\n")
  18314 
  18315    def deps(self):
  18316        return self.dictionary.getDeps()
  18317 
  18318    @staticmethod
  18319    def makeDictionaryName(dictionary):
  18320        return dictionary.identifier.name
  18321 
  18322    def makeClassName(self, dictionary):
  18323        return self.makeDictionaryName(dictionary)
  18324 
  18325    @staticmethod
  18326    def makeMemberName(name):
  18327        return "m" + name[0].upper() + IDLToCIdentifier(name[1:])
  18328 
  18329    def getMemberType(self, memberInfo):
  18330        member, conversionInfo = memberInfo
  18331        # We can't handle having a holderType here
  18332        assert conversionInfo.holderType is None
  18333 
  18334        if member.getExtendedAttribute("BinaryType"):
  18335            return member.getExtendedAttribute("BinaryType")[0]
  18336 
  18337        declType = conversionInfo.declType
  18338        if conversionInfo.dealWithOptional:
  18339            declType = CGTemplatedType("Optional", declType)
  18340        return declType.define()
  18341 
  18342    def getMemberConversion(self, memberInfo, isKnownMissing=False):
  18343        """
  18344        A function that outputs the initialization of a single dictionary
  18345        member from the given dictionary value.
  18346 
  18347        We start with our conversionInfo, which tells us how to
  18348        convert a JS::Value to whatever type this member is.  We
  18349        substiture the template from the conversionInfo with values
  18350        that point to our "temp" JS::Value and our member (which is
  18351        the C++ value we want to produce).  The output is a string of
  18352        code to do the conversion.  We store this string in
  18353        conversionReplacements["convert"].
  18354 
  18355        Now we have three different ways we might use (or skip) this
  18356        string of code, depending on whether the value is required,
  18357        optional with default value, or optional without default
  18358        value.  We set up a template in the 'conversion' variable for
  18359        exactly how to do this, then substitute into it from the
  18360        conversionReplacements dictionary.
  18361        """
  18362        member, conversionInfo = memberInfo
  18363 
  18364        # We should only be initializing things with default values if
  18365        # we're always-missing.
  18366        assert not isKnownMissing or (member.optional and member.defaultValue)
  18367 
  18368        replacements = {
  18369            "declName": self.makeMemberName(member.identifier.name),
  18370            # We need a holder name for external interfaces, but
  18371            # it's scoped down to the conversion so we can just use
  18372            # anything we want.
  18373            "holderName": "holder",
  18374            "passedToJSImpl": "passedToJSImpl",
  18375        }
  18376 
  18377        if isKnownMissing:
  18378            replacements["val"] = "(JS::NullHandleValue)"
  18379        else:
  18380            replacements["val"] = "temp.ref()"
  18381            replacements["maybeMutableVal"] = "temp.ptr()"
  18382 
  18383        # We can't handle having a holderType here
  18384        assert conversionInfo.holderType is None
  18385        if conversionInfo.dealWithOptional:
  18386            replacements["declName"] = "(" + replacements["declName"] + ".Value())"
  18387        if member.defaultValue:
  18388            if isKnownMissing:
  18389                replacements["haveValue"] = "false"
  18390            else:
  18391                replacements["haveValue"] = "!isNull && !temp->isUndefined()"
  18392 
  18393        propId = self.makeIdName(member.identifier.name)
  18394        propGet = "JS_GetPropertyById(cx, *object, atomsCache->%s, temp.ptr())" % propId
  18395 
  18396        conversionReplacements = {
  18397            "prop": self.makeMemberName(member.identifier.name),
  18398            "convert": string.Template(conversionInfo.template).substitute(
  18399                replacements
  18400            ),
  18401            "propGet": propGet,
  18402        }
  18403        # The conversion code will only run where a default value or a value passed
  18404        # by the author needs to get converted, so we can remember if we have any
  18405        # members present here.
  18406        conversionReplacements["convert"] += "mIsAnyMemberPresent = true;\n"
  18407        if isKnownMissing:
  18408            conversion = ""
  18409        else:
  18410            setTempValue = CGGeneric(
  18411                dedent(
  18412                    """
  18413                if (!${propGet}) {
  18414                  return false;
  18415                }
  18416                """
  18417                )
  18418            )
  18419            conditions = getConditionList(member, "cx", "*object")
  18420            if len(conditions) != 0:
  18421                setTempValue = CGIfElseWrapper(
  18422                    conditions.define(),
  18423                    setTempValue,
  18424                    CGGeneric("temp->setUndefined();\n"),
  18425                )
  18426            setTempValue = CGIfWrapper(setTempValue, "!isNull")
  18427            conversion = setTempValue.define()
  18428 
  18429        if member.defaultValue:
  18430            if member.type.isUnion() and (
  18431                not member.type.nullable()
  18432                or not isinstance(member.defaultValue, IDLNullValue)
  18433            ):
  18434                # Since this has a default value, it might have been initialized
  18435                # already.  Go ahead and uninit it before we try to init it
  18436                # again.
  18437                memberName = self.makeMemberName(member.identifier.name)
  18438                if member.type.nullable():
  18439                    conversion += fill(
  18440                        """
  18441                        if (!${memberName}.IsNull()) {
  18442                          ${memberName}.Value().Uninit();
  18443                        }
  18444                        """,
  18445                        memberName=memberName,
  18446                    )
  18447                else:
  18448                    conversion += "%s.Uninit();\n" % memberName
  18449            conversion += "${convert}"
  18450        elif not conversionInfo.dealWithOptional:
  18451            # We're required, but have no default value.  Make sure
  18452            # that we throw if we have no value provided.
  18453            conversion += dedent(
  18454                """
  18455                if (!isNull && !temp->isUndefined()) {
  18456                ${convert}
  18457                } else if (cx) {
  18458                  // Don't error out if we have no cx.  In that
  18459                  // situation the caller is default-constructing us and we'll
  18460                  // just assume they know what they're doing.
  18461                  return cx.ThrowErrorMessage<MSG_MISSING_REQUIRED_DICTIONARY_MEMBER>("%s");
  18462                }
  18463                """
  18464                % self.getMemberSourceDescription(member)
  18465            )
  18466            conversionReplacements["convert"] = indent(
  18467                conversionReplacements["convert"]
  18468            ).rstrip()
  18469        else:
  18470            conversion += (
  18471                "if (!isNull && !temp->isUndefined()) {\n"
  18472                "  ${prop}.Construct();\n"
  18473                "${convert}"
  18474                "}\n"
  18475            )
  18476            conversionReplacements["convert"] = indent(
  18477                conversionReplacements["convert"]
  18478            )
  18479 
  18480        return CGGeneric(string.Template(conversion).substitute(conversionReplacements))
  18481 
  18482    def getMemberDefinition(self, memberInfo):
  18483        member = memberInfo[0]
  18484        declType = memberInfo[1].declType
  18485        memberLoc = self.makeMemberName(member.identifier.name)
  18486        if not member.canHaveMissingValue():
  18487            memberData = memberLoc
  18488        else:
  18489            # The data is inside the Optional<>
  18490            memberData = "%s.InternalValue()" % memberLoc
  18491 
  18492        if member.identifier.name in JS_OBJECT_PROTOTYPE_PROPERTIES:
  18493            raise TypeError(
  18494                "'%s' member of %s dictionary shadows "
  18495                "a property of Object.prototype, and Xrays to "
  18496                "Object can't handle that.\n"
  18497                "%s"
  18498                % (
  18499                    member.identifier.name,
  18500                    self.dictionary.identifier.name,
  18501                    member.location,
  18502                )
  18503            )
  18504 
  18505        propDef = (
  18506            "JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, JSPROP_ENUMERATE)"
  18507            % self.makeIdName(member.identifier.name)
  18508        )
  18509 
  18510        innerTemplate = wrapForType(
  18511            member.type,
  18512            self.descriptorProvider,
  18513            {
  18514                "result": "currentValue",
  18515                "successCode": (
  18516                    "if (!%s) {\n" "  return false;\n" "}\n" "break;\n" % propDef
  18517                ),
  18518                "jsvalRef": "temp",
  18519                "jsvalHandle": "&temp",
  18520                "returnsNewObject": False,
  18521                # 'obj' can just be allowed to be the string "obj", since that
  18522                # will be our dictionary object, which is presumably itself in
  18523                # the right scope.
  18524                "spiderMonkeyInterfacesAreStructs": True,
  18525            },
  18526        )
  18527        conversion = CGGeneric(innerTemplate)
  18528        conversion = CGWrapper(
  18529            conversion,
  18530            pre=(
  18531                "JS::Rooted<JS::Value> temp(cx);\n"
  18532                "%s const & currentValue = %s;\n" % (declType.define(), memberData)
  18533            ),
  18534        )
  18535 
  18536        # Now make sure that our successCode can actually break out of the
  18537        # conversion.  This incidentally gives us a scope for 'temp' and
  18538        # 'currentValue'.
  18539        conversion = CGWrapper(
  18540            CGIndenter(conversion),
  18541            pre=(
  18542                "do {\n"
  18543                "  // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"
  18544            ),
  18545            post="} while(false);\n",
  18546        )
  18547        if member.canHaveMissingValue():
  18548            # Only do the conversion if we have a value
  18549            conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc)
  18550        conditions = getConditionList(member, "cx", "obj")
  18551        if len(conditions) != 0:
  18552            conversion = CGIfWrapper(conversion, conditions.define())
  18553        return conversion
  18554 
  18555    def getMemberTrace(self, member):
  18556        type = member.type
  18557        assert typeNeedsRooting(type)
  18558        memberLoc = self.makeMemberName(member.identifier.name)
  18559        if not member.canHaveMissingValue():
  18560            memberData = memberLoc
  18561        else:
  18562            # The data is inside the Optional<>
  18563            memberData = "%s.Value()" % memberLoc
  18564 
  18565        memberName = "%s.%s" % (self.makeClassName(self.dictionary), memberLoc)
  18566 
  18567        if type.isObject():
  18568            trace = CGGeneric(
  18569                'JS::TraceRoot(trc, %s, "%s");\n' % ("&" + memberData, memberName)
  18570            )
  18571            if type.nullable():
  18572                trace = CGIfWrapper(trace, memberData)
  18573        elif type.isAny():
  18574            trace = CGGeneric(
  18575                'JS::TraceRoot(trc, %s, "%s");\n' % ("&" + memberData, memberName)
  18576            )
  18577        elif (
  18578            type.isSequence()
  18579            or type.isDictionary()
  18580            or type.isSpiderMonkeyInterface()
  18581            or type.isUnion()
  18582            or type.isRecord()
  18583        ):
  18584            if type.nullable():
  18585                memberNullable = memberData
  18586                memberData = "%s.Value()" % memberData
  18587            if type.isSequence():
  18588                trace = CGGeneric("DoTraceSequence(trc, %s);\n" % memberData)
  18589            elif type.isDictionary():
  18590                trace = CGGeneric("%s.TraceDictionary(trc);\n" % memberData)
  18591            elif type.isUnion():
  18592                trace = CGGeneric("%s.TraceUnion(trc);\n" % memberData)
  18593            elif type.isRecord():
  18594                trace = CGGeneric("TraceRecord(trc, %s);\n" % memberData)
  18595            else:
  18596                assert type.isSpiderMonkeyInterface()
  18597                trace = CGGeneric("%s.TraceSelf(trc);\n" % memberData)
  18598            if type.nullable():
  18599                trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable)
  18600        else:
  18601            assert False  # unknown type
  18602 
  18603        if member.canHaveMissingValue():
  18604            trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc)
  18605 
  18606        return trace.define()
  18607 
  18608    def getMemberInitializer(self, memberInfo):
  18609        """
  18610        Get the right initializer for the member.  Most members don't need one,
  18611        but we need to pre-initialize 'object' that have a default value or are
  18612        required (and hence are not inside Optional), so they're safe to trace
  18613        at all times.  And we can optimize a bit for dictionary-typed members.
  18614        """
  18615        member, _ = memberInfo
  18616        if member.canHaveMissingValue():
  18617            # Allowed missing value means no need to set it up front, since it's
  18618            # inside an Optional and won't get traced until it's actually set
  18619            # up.
  18620            return None
  18621        type = member.type
  18622        if type.isDictionary():
  18623            # When we construct ourselves, we don't want to init our member
  18624            # dictionaries.  Either we're being constructed-but-not-initialized
  18625            # ourselves (and then we don't want to init them) or we're about to
  18626            # init ourselves and then we'll init them anyway.
  18627            return CGDictionary.getNonInitializingCtorArg()
  18628        return initializerForType(type)
  18629 
  18630    def getMemberSourceDescription(self, member):
  18631        return "'%s' member of %s" % (
  18632            member.identifier.name,
  18633            self.dictionary.identifier.name,
  18634        )
  18635 
  18636    @staticmethod
  18637    def makeIdName(name):
  18638        return IDLToCIdentifier(name) + "_id"
  18639 
  18640    @staticmethod
  18641    def getNonInitializingCtorArg():
  18642        return "FastDictionaryInitializer()"
  18643 
  18644    @staticmethod
  18645    def isDictionaryCopyConstructible(dictionary):
  18646        if dictionary.parent and not CGDictionary.isDictionaryCopyConstructible(
  18647            dictionary.parent
  18648        ):
  18649            return False
  18650        return all(isTypeCopyConstructible(m.type) for m in dictionary.members)
  18651 
  18652    @staticmethod
  18653    def typeSafeToJSONify(type):
  18654        """
  18655        Determine whether the given type is safe to convert to JSON.  The
  18656        restriction is that this needs to be safe while in a global controlled
  18657        by an adversary, and "safe" means no side-effects when the JS
  18658        representation of this type is converted to JSON.  That means that we
  18659        have to be pretty restrictive about what things we can allow.  For
  18660        example, "object" is out, because it may have accessor properties on it.
  18661        """
  18662        if type.nullable():
  18663            # Converting null to JSON is always OK.
  18664            return CGDictionary.typeSafeToJSONify(type.inner)
  18665 
  18666        if type.isSequence():
  18667            # Sequences are arrays we create ourselves, with no holes.  They
  18668            # should be safe if their contents are safe, as long as we suppress
  18669            # invocation of .toJSON on objects.
  18670            return CGDictionary.typeSafeToJSONify(type.inner)
  18671 
  18672        if type.isUnion():
  18673            # OK if everything in it is ok.
  18674            return all(CGDictionary.typeSafeToJSONify(t) for t in type.flatMemberTypes)
  18675 
  18676        if type.isDictionary():
  18677            # OK if the dictionary is OK
  18678            return CGDictionary.dictionarySafeToJSONify(type.inner)
  18679 
  18680        if type.isUndefined() or type.isString() or type.isEnum():
  18681            # Strings are always OK.
  18682            return True
  18683 
  18684        if type.isPrimitive():
  18685            # Primitives (numbers and booleans) are ok, as long as
  18686            # they're not unrestricted float/double.
  18687            return not type.isFloat() or not type.isUnrestricted()
  18688 
  18689        if type.isRecord():
  18690            # Records are okay, as long as the value type is.
  18691            # Per spec, only strings are allowed as keys.
  18692            return CGDictionary.typeSafeToJSONify(type.inner)
  18693 
  18694        return False
  18695 
  18696    @staticmethod
  18697    def dictionarySafeToJSONify(dictionary):
  18698        # The dictionary itself is OK, so we're good if all our types are.
  18699        return all(CGDictionary.typeSafeToJSONify(m.type) for m in dictionary.members)
  18700 
  18701 
  18702 def RegisterNonWindowBindings(descriptors):
  18703    conditions = []
  18704    for desc in descriptors:
  18705        bindingNS = toBindingNamespace(desc.name)
  18706        condition = "!%s::CreateAndDefineOnGlobal(aCx)" % bindingNS
  18707        if desc.isExposedConditionally():
  18708            condition = "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
  18709        conditions.append(condition)
  18710    lines = [
  18711        CGIfWrapper(CGGeneric("return false;\n"), condition) for condition in conditions
  18712    ]
  18713    lines.append(CGGeneric("return true;\n"))
  18714    return CGList(lines, "\n").define()
  18715 
  18716 
  18717 class CGRegisterWorkerBindings(CGAbstractMethod):
  18718    def __init__(self, config):
  18719        CGAbstractMethod.__init__(
  18720            self,
  18721            None,
  18722            "RegisterWorkerBindings",
  18723            "bool",
  18724            [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
  18725        )
  18726        self.config = config
  18727 
  18728    def definition_body(self):
  18729        return RegisterNonWindowBindings(
  18730            self.config.getDescriptors(
  18731                hasInterfaceObject=True, isExposedInAnyWorker=True, register=True
  18732            )
  18733        )
  18734 
  18735 
  18736 class CGRegisterWorkerDebuggerBindings(CGAbstractMethod):
  18737    def __init__(self, config):
  18738        CGAbstractMethod.__init__(
  18739            self,
  18740            None,
  18741            "RegisterWorkerDebuggerBindings",
  18742            "bool",
  18743            [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
  18744        )
  18745        self.config = config
  18746 
  18747    def definition_body(self):
  18748        return RegisterNonWindowBindings(
  18749            self.config.getDescriptors(
  18750                hasInterfaceObject=True, isExposedInWorkerDebugger=True, register=True
  18751            )
  18752        )
  18753 
  18754 
  18755 class CGRegisterWorkletBindings(CGAbstractMethod):
  18756    def __init__(self, config):
  18757        CGAbstractMethod.__init__(
  18758            self,
  18759            None,
  18760            "RegisterWorkletBindings",
  18761            "bool",
  18762            [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
  18763        )
  18764        self.config = config
  18765 
  18766    def definition_body(self):
  18767        return RegisterNonWindowBindings(
  18768            self.config.getDescriptors(
  18769                hasInterfaceObject=True, isExposedInAnyWorklet=True, register=True
  18770            )
  18771        )
  18772 
  18773 
  18774 class CGRegisterShadowRealmBindings(CGAbstractMethod):
  18775    def __init__(self, config):
  18776        CGAbstractMethod.__init__(
  18777            self,
  18778            None,
  18779            "RegisterShadowRealmBindings",
  18780            "bool",
  18781            [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
  18782        )
  18783        self.config = config
  18784 
  18785    def definition_body(self):
  18786        return RegisterNonWindowBindings(
  18787            self.config.getDescriptors(
  18788                hasInterfaceObject=True, isExposedInShadowRealms=True, register=True
  18789            )
  18790        )
  18791 
  18792 
  18793 def BindingNamesOffsetEnum(name):
  18794    return CppKeywords.checkMethodName(name.replace(" ", "_"))
  18795 
  18796 
  18797 class CGGlobalNames(CGGeneric):
  18798    def __init__(self, names):
  18799        """
  18800        names is expected to be a list of tuples of the name and the descriptor it refers to.
  18801        """
  18802 
  18803        strings = []
  18804        entries = []
  18805        for name, desc in names:
  18806            # Generate the entry declaration
  18807            # XXX(nika): mCreate & mEnabled require relocations. If we want to
  18808            # reduce those, we could move them into separate tables.
  18809            nativeEntry = fill(
  18810                """
  18811                {
  18812                  /* mNameOffset */ BindingNamesOffset::${nameOffset},
  18813                  /* mNameLength */ ${nameLength},
  18814                  /* mConstructorId */ constructors::id::${realname},
  18815                  /* mCreate */ ${realname}_Binding::CreateInterfaceObjects,
  18816                  /* mEnabled */ ${enabled}
  18817                }
  18818                """,
  18819                nameOffset=BindingNamesOffsetEnum(name),
  18820                nameLength=len(name),
  18821                name=name,
  18822                realname=desc.name,
  18823                enabled=(
  18824                    "%s_Binding::ConstructorEnabled" % desc.name
  18825                    if desc.isExposedConditionally()
  18826                    else "nullptr"
  18827                ),
  18828            )
  18829 
  18830            entries.append((name.encode(), nativeEntry))
  18831 
  18832        # Unfortunately, when running tests, we may have no entries.
  18833        # PerfectHash will assert if we give it an empty set of entries, so we
  18834        # just generate a dummy value.
  18835        if len(entries) == 0:
  18836            CGGeneric.__init__(
  18837                self,
  18838                define=dedent(
  18839                    """
  18840                static_assert(false, "No WebIDL global name entries!");
  18841                """
  18842                ),
  18843            )
  18844            return
  18845 
  18846        # Build the perfect hash function.
  18847        phf = PerfectHash(entries, GLOBAL_NAMES_PHF_SIZE)
  18848 
  18849        # Generate code for the PHF
  18850        phfCodegen = phf.codegen(
  18851            "WebIDLGlobalNameHash::sEntries", "WebIDLNameTableEntry"
  18852        )
  18853        entries = phfCodegen.gen_entries(lambda e: e[1])
  18854        getter = phfCodegen.gen_jslinearstr_getter(
  18855            name="WebIDLGlobalNameHash::GetEntry",
  18856            return_type="const WebIDLNameTableEntry*",
  18857            return_entry=dedent(
  18858                """
  18859                if (JS_LinearStringEqualsAscii(aKey, BindingName(entry.mNameOffset), entry.mNameLength)) {
  18860                  return &entry;
  18861                }
  18862                return nullptr;
  18863                """
  18864            ),
  18865        )
  18866 
  18867        define = fill(
  18868            """
  18869            const uint32_t WebIDLGlobalNameHash::sCount = ${count};
  18870 
  18871            $*{entries}
  18872 
  18873            $*{getter}
  18874            """,
  18875            count=len(phf.entries),
  18876            strings="\n".join(strings) + ";\n",
  18877            entries=entries,
  18878            getter=getter,
  18879        )
  18880        CGGeneric.__init__(self, define=define)
  18881 
  18882 
  18883 def dependencySortObjects(objects, dependencyGetter, nameGetter):
  18884    """
  18885    Sort IDL objects with dependencies on each other such that if A
  18886    depends on B then B will come before A.  This is needed for
  18887    declaring C++ classes in the right order, for example.  Objects
  18888    that have no dependencies are just sorted by name.
  18889 
  18890    objects should be something that can produce a set of objects
  18891    (e.g. a set, iterator, list, etc).
  18892 
  18893    dependencyGetter is something that, given an object, should return
  18894    the set of objects it depends on.
  18895    """
  18896    # XXXbz this will fail if we have two webidl files F1 and F2 such that F1
  18897    # declares an object which depends on an object in F2, and F2 declares an
  18898    # object (possibly a different one!) that depends on an object in F1.  The
  18899    # good news is that I expect this to never happen.
  18900    sortedObjects = []
  18901    objects = set(objects)
  18902    while len(objects) != 0:
  18903        # Find the dictionaries that don't depend on anything else
  18904        # anymore and move them over.
  18905        toMove = [o for o in objects if len(dependencyGetter(o) & objects) == 0]
  18906        if len(toMove) == 0:
  18907            raise TypeError(
  18908                "Loop in dependency graph\n" + "\n".join(o.location for o in objects)
  18909            )
  18910        objects = objects - set(toMove)
  18911        sortedObjects.extend(sorted(toMove, key=nameGetter))
  18912    return sortedObjects
  18913 
  18914 
  18915 class ForwardDeclarationBuilder:
  18916    """
  18917    Create a canonical representation of a set of namespaced forward
  18918    declarations.
  18919    """
  18920 
  18921    def __init__(self):
  18922        """
  18923        The set of declarations is represented as a tree of nested namespaces.
  18924        Each tree node has a set of declarations |decls| and a dict |children|.
  18925        Each declaration is a pair consisting of the class name and a boolean
  18926        that is true iff the class is really a struct. |children| maps the
  18927        names of inner namespaces to the declarations in that namespace.
  18928        """
  18929        self.decls = set()
  18930        self.children = {}
  18931 
  18932    def _ensureNonTemplateType(self, type):
  18933        if "<" in type:
  18934            # This is a templated type.  We don't really know how to
  18935            # forward-declare those, and trying to do it naively is not going to
  18936            # go well (e.g. we may have :: characters inside the type we're
  18937            # templated on!).  Just bail out.
  18938            raise TypeError(
  18939                "Attempt to use ForwardDeclarationBuilder on "
  18940                "templated type %s.  We don't know how to do that "
  18941                "yet." % type
  18942            )
  18943 
  18944    def _listAdd(self, namespaces, name, isStruct=False):
  18945        """
  18946        Add a forward declaration, where |namespaces| is a list of namespaces.
  18947        |name| should not contain any other namespaces.
  18948        """
  18949        if namespaces:
  18950            child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder())
  18951            child._listAdd(namespaces[1:], name, isStruct)
  18952        else:
  18953            assert "::" not in name
  18954            self.decls.add((name, isStruct))
  18955 
  18956    def addInMozillaDom(self, name, isStruct=False):
  18957        """
  18958        Add a forward declaration to the mozilla::dom:: namespace. |name| should not
  18959        contain any other namespaces.
  18960        """
  18961        self._ensureNonTemplateType(name)
  18962        self._listAdd(["mozilla", "dom"], name, isStruct)
  18963 
  18964    def add(self, nativeType, isStruct=False):
  18965        """
  18966        Add a forward declaration, where |nativeType| is a string containing
  18967        the type and its namespaces, in the usual C++ way.
  18968        """
  18969        self._ensureNonTemplateType(nativeType)
  18970        components = nativeType.split("::")
  18971        self._listAdd(components[:-1], components[-1], isStruct)
  18972 
  18973    def _build(self, atTopLevel):
  18974        """
  18975        Return a codegenerator for the forward declarations.
  18976        """
  18977        decls = []
  18978        if self.decls:
  18979            decls.append(
  18980                CGList(
  18981                    [
  18982                        CGClassForwardDeclare(cname, isStruct)
  18983                        for cname, isStruct in sorted(self.decls)
  18984                    ]
  18985                )
  18986            )
  18987        for namespace, child in sorted(self.children.items()):
  18988            decls.append(CGNamespace(namespace, child._build(atTopLevel=False)))
  18989 
  18990        cg = CGList(decls, "\n")
  18991        if not atTopLevel and len(decls) + len(self.decls) > 1:
  18992            cg = CGWrapper(cg, pre="\n", post="\n")
  18993        return cg
  18994 
  18995    def build(self):
  18996        return self._build(atTopLevel=True)
  18997 
  18998    def forwardDeclareForType(self, t, config):
  18999        t = t.unroll()
  19000        if t.isGeckoInterface():
  19001            name = t.inner.identifier.name
  19002            try:
  19003                desc = config.getDescriptor(name)
  19004                self.add(desc.nativeType)
  19005            except NoSuchDescriptorError:
  19006                pass
  19007 
  19008        # Note: SpiderMonkey interfaces are typedefs, so can't be
  19009        # forward-declared
  19010        elif t.isPromise():
  19011            self.addInMozillaDom("Promise")
  19012        elif t.isCallback():
  19013            self.addInMozillaDom(t.callback.identifier.name)
  19014        elif t.isDictionary():
  19015            self.addInMozillaDom(t.inner.identifier.name, isStruct=True)
  19016        elif t.isCallbackInterface():
  19017            self.addInMozillaDom(t.inner.identifier.name)
  19018        elif t.isUnion():
  19019            # Forward declare both the owning and non-owning version,
  19020            # since we don't know which one we might want
  19021            self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
  19022            self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
  19023        elif t.isRecord():
  19024            self.forwardDeclareForType(t.inner, config)
  19025        # Don't need to do anything for void, primitive, string, any or object.
  19026        # There may be some other cases we are missing.
  19027 
  19028 
  19029 class CGForwardDeclarations(CGWrapper):
  19030    """
  19031    Code generate the forward declarations for a header file.
  19032    additionalDeclarations is a list of tuples containing a classname and a
  19033    boolean. If the boolean is true we will declare a struct, otherwise we'll
  19034    declare a class.
  19035    """
  19036 
  19037    def __init__(
  19038        self,
  19039        config,
  19040        descriptors,
  19041        callbacks,
  19042        dictionaries,
  19043        callbackInterfaces,
  19044        additionalDeclarations=[],
  19045    ):
  19046        builder = ForwardDeclarationBuilder()
  19047 
  19048        # Needed for at least Wrap.
  19049        for d in descriptors:
  19050            # If this is a generated iterator interface, we only create these
  19051            # in the generated bindings, and don't need to forward declare.
  19052            if (
  19053                d.interface.isIteratorInterface()
  19054                or d.interface.isAsyncIteratorInterface()
  19055            ):
  19056                continue
  19057            builder.add(d.nativeType)
  19058            if d.interface.isSerializable():
  19059                builder.add("nsIGlobalObject")
  19060            # If we're an interface and we have a maplike/setlike declaration,
  19061            # we'll have helper functions exposed to the native side of our
  19062            # bindings, which will need to show up in the header. If either of
  19063            # our key/value types are interfaces, they'll be passed as
  19064            # arguments to helper functions, and they'll need to be forward
  19065            # declared in the header.
  19066            if d.interface.maplikeOrSetlikeOrIterable:
  19067                if d.interface.maplikeOrSetlikeOrIterable.hasKeyType():
  19068                    builder.forwardDeclareForType(
  19069                        d.interface.maplikeOrSetlikeOrIterable.keyType, config
  19070                    )
  19071                if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
  19072                    builder.forwardDeclareForType(
  19073                        d.interface.maplikeOrSetlikeOrIterable.valueType, config
  19074                    )
  19075 
  19076            for m in d.interface.members:
  19077                if m.isAttr() and m.type.isObservableArray():
  19078                    builder.forwardDeclareForType(m.type, config)
  19079 
  19080        # We just about always need NativePropertyHooks
  19081        builder.addInMozillaDom("NativePropertyHooks", isStruct=True)
  19082        builder.addInMozillaDom("ProtoAndIfaceCache")
  19083 
  19084        for callback in callbacks:
  19085            builder.addInMozillaDom(callback.identifier.name)
  19086            for t in getTypesFromCallback(callback):
  19087                builder.forwardDeclareForType(t, config)
  19088 
  19089        for d in callbackInterfaces:
  19090            builder.add(d.nativeType)
  19091            builder.add(d.nativeType + "Atoms", isStruct=True)
  19092            for t in getTypesFromDescriptor(d):
  19093                builder.forwardDeclareForType(t, config)
  19094            if d.hasCEReactions():
  19095                builder.addInMozillaDom("DocGroup")
  19096 
  19097        for d in dictionaries:
  19098            if len(d.members) > 0:
  19099                builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True)
  19100            for t in getTypesFromDictionary(d):
  19101                builder.forwardDeclareForType(t, config)
  19102 
  19103        for className, isStruct in additionalDeclarations:
  19104            builder.add(className, isStruct=isStruct)
  19105 
  19106        CGWrapper.__init__(self, builder.build())
  19107 
  19108    def forward_declare(self):
  19109        return ""
  19110 
  19111 
  19112 def dependencySortDictionariesAndUnionsAndCallbacks(types):
  19113    def getDependenciesFromType(type):
  19114        if type.isDictionary():
  19115            return set([type.unroll().inner])
  19116        if type.isSequence():
  19117            return getDependenciesFromType(type.unroll())
  19118        if type.isUnion():
  19119            return set([type.unroll()])
  19120        if type.isRecord():
  19121            return set([type.unroll().inner])
  19122        if type.isCallback():
  19123            return set([type.unroll()])
  19124        return set()
  19125 
  19126    def getDependencies(unionTypeOrDictionaryOrCallback):
  19127        if isinstance(unionTypeOrDictionaryOrCallback, IDLDictionary):
  19128            deps = set()
  19129            if unionTypeOrDictionaryOrCallback.parent:
  19130                deps.add(unionTypeOrDictionaryOrCallback.parent)
  19131            for member in unionTypeOrDictionaryOrCallback.members:
  19132                deps |= getDependenciesFromType(member.type)
  19133            return deps
  19134 
  19135        if (
  19136            unionTypeOrDictionaryOrCallback.isType()
  19137            and unionTypeOrDictionaryOrCallback.isUnion()
  19138        ):
  19139            deps = set()
  19140            for member in unionTypeOrDictionaryOrCallback.flatMemberTypes:
  19141                deps |= getDependenciesFromType(member)
  19142            return deps
  19143 
  19144        assert unionTypeOrDictionaryOrCallback.isCallback()
  19145        return set()
  19146 
  19147    def getName(unionTypeOrDictionaryOrCallback):
  19148        if isinstance(unionTypeOrDictionaryOrCallback, IDLDictionary):
  19149            return unionTypeOrDictionaryOrCallback.identifier.name
  19150 
  19151        if (
  19152            unionTypeOrDictionaryOrCallback.isType()
  19153            and unionTypeOrDictionaryOrCallback.isUnion()
  19154        ):
  19155            return unionTypeOrDictionaryOrCallback.name
  19156 
  19157        assert unionTypeOrDictionaryOrCallback.isCallback()
  19158        return unionTypeOrDictionaryOrCallback.identifier.name
  19159 
  19160    return dependencySortObjects(types, getDependencies, getName)
  19161 
  19162 
  19163 class CGBindingRoot(CGThing):
  19164    """
  19165    Root codegen class for binding generation. Instantiate the class, and call
  19166    declare or define to generate header or cpp code (respectively).
  19167    """
  19168 
  19169    def __init__(self, config: Configuration, prefix, webIDLFile):
  19170        bindingHeaders = dict.fromkeys(
  19171            ("mozilla/dom/NonRefcountedDOMObject.h", "MainThreadUtils.h"), True
  19172        )
  19173        bindingDeclareHeaders = dict.fromkeys(
  19174            (
  19175                "mozilla/dom/BindingDeclarations.h",
  19176                "mozilla/dom/Nullable.h",
  19177            ),
  19178            True,
  19179        )
  19180 
  19181        descriptors = config.getDescriptors(
  19182            webIDLFile=webIDLFile, hasInterfaceOrInterfacePrototypeObject=True
  19183        )
  19184        descriptors.extend(
  19185            config.getDescriptors(
  19186                webIDLFile=webIDLFile, hasOrdinaryObjectPrototype=True
  19187            )
  19188        )
  19189 
  19190        unionTypes = UnionsForFile(config, webIDLFile)
  19191 
  19192        (
  19193            unionHeaders,
  19194            unionImplheaders,
  19195            unionDeclarations,
  19196            traverseMethods,
  19197            unlinkMethods,
  19198            unionStructs,
  19199        ) = UnionTypes(unionTypes, config)
  19200 
  19201        bindingDeclareHeaders.update(dict.fromkeys(unionHeaders, True))
  19202        bindingHeaders.update(dict.fromkeys(unionImplheaders, True))
  19203        bindingDeclareHeaders["mozilla/dom/UnionMember.h"] = len(unionStructs) > 0
  19204        bindingDeclareHeaders["mozilla/dom/FakeString.h"] = len(unionStructs) > 0
  19205        # BindingUtils.h is only needed for SetToObject.
  19206        # If it stops being inlined or stops calling CallerSubsumes
  19207        # both this bit and the bit in UnionTypes can be removed.
  19208        bindingDeclareHeaders["mozilla/dom/BindingUtils.h"] = any(
  19209            d.isObject() for t in unionTypes for d in t.flatMemberTypes
  19210        )
  19211        bindingDeclareHeaders["mozilla/dom/JSSlots.h"] = any(
  19212            d.interface.reflectedHTMLAttributesReturningFrozenArray for d in descriptors
  19213        )
  19214        bindingHeaders["mozilla/dom/IterableIterator.h"] = any(
  19215            (
  19216                d.interface.isIteratorInterface()
  19217                and d.interface.maplikeOrSetlikeOrIterable.isPairIterator()
  19218            )
  19219            or d.interface.isAsyncIteratorInterface()
  19220            or d.interface.isIterable()
  19221            or d.interface.isAsyncIterable()
  19222            for d in descriptors
  19223        )
  19224 
  19225        def memberNeedsSubjectPrincipal(d, m):
  19226            if m.isAttr():
  19227                return (
  19228                    "needsSubjectPrincipal" in d.getExtendedAttributes(m, getter=True)
  19229                ) or (
  19230                    not m.readonly
  19231                    and "needsSubjectPrincipal"
  19232                    in d.getExtendedAttributes(m, setter=True)
  19233                )
  19234            return m.isMethod() and "needsSubjectPrincipal" in d.getExtendedAttributes(
  19235                m
  19236            )
  19237 
  19238        if any(
  19239            memberNeedsSubjectPrincipal(d, m)
  19240            for d in descriptors
  19241            for m in d.interface.members
  19242        ):
  19243            bindingHeaders["mozilla/BasePrincipal.h"] = True
  19244            bindingHeaders["nsJSPrincipals.h"] = True
  19245 
  19246        # The conditions for which we generate profiler labels are fairly
  19247        # complicated. The check below is a little imprecise to make it simple.
  19248        # It includes the profiler header in all cases where it is necessary and
  19249        # generates only a few false positives.
  19250        bindingHeaders["mozilla/ProfilerLabels.h"] = any(
  19251            # constructor profiler label
  19252            d.interface.legacyFactoryFunctions
  19253            or (d.interface.hasInterfaceObject() and d.interface.ctor())
  19254            or any(
  19255                # getter/setter profiler labels
  19256                m.isAttr()
  19257                # method profiler label
  19258                or m.isMethod()
  19259                for m in d.interface.members
  19260            )
  19261            for d in descriptors
  19262        )
  19263 
  19264        def descriptorHasCrossOriginProperties(desc):
  19265            def hasCrossOriginProperty(m):
  19266                props = memberProperties(m, desc)
  19267                return (
  19268                    props.isCrossOriginMethod
  19269                    or props.isCrossOriginGetter
  19270                    or props.isCrossOriginSetter
  19271                )
  19272 
  19273            return any(hasCrossOriginProperty(m) for m in desc.interface.members)
  19274 
  19275        def descriptorHasObservableArrayTypes(desc):
  19276            def hasObservableArrayTypes(m):
  19277                return m.isAttr() and m.type.isObservableArray()
  19278 
  19279            return any(hasObservableArrayTypes(m) for m in desc.interface.members)
  19280 
  19281        bindingDeclareHeaders["mozilla/dom/RemoteObjectProxy.h"] = any(
  19282            descriptorHasCrossOriginProperties(d) for d in descriptors
  19283        )
  19284        bindingDeclareHeaders["jsapi.h"] = any(
  19285            descriptorHasCrossOriginProperties(d)
  19286            or descriptorHasObservableArrayTypes(d)
  19287            for d in descriptors
  19288        )
  19289        bindingHeaders["mozilla/dom/ToJSValue.h"] = any(
  19290            descriptorHasObservableArrayTypes(d) for d in descriptors
  19291        )
  19292        bindingDeclareHeaders["js/TypeDecls.h"] = not bindingDeclareHeaders["jsapi.h"]
  19293        bindingDeclareHeaders["js/RootingAPI.h"] = not bindingDeclareHeaders["jsapi.h"]
  19294 
  19295        # JS::IsCallable
  19296        bindingDeclareHeaders["js/CallAndConstruct.h"] = True
  19297 
  19298        def descriptorHasIteratorAlias(desc):
  19299            def hasIteratorAlias(m):
  19300                return m.isMethod() and (
  19301                    ("@@iterator" in m.aliases) or ("@@asyncIterator" in m.aliases)
  19302                )
  19303 
  19304            return any(hasIteratorAlias(m) for m in desc.interface.members)
  19305 
  19306        bindingHeaders["js/Symbol.h"] = any(
  19307            descriptorHasIteratorAlias(d) for d in descriptors
  19308        )
  19309 
  19310        bindingHeaders["js/shadow/Object.h"] = any(
  19311            d.interface.hasMembersInSlots() for d in descriptors
  19312        )
  19313 
  19314        # The symbols supplied by this header are used so ubiquitously it's not
  19315        # worth the effort delineating the exact dependency, if it can't be done
  19316        # *at* the places where their definitions are required.
  19317        bindingHeaders["js/experimental/JitInfo.h"] = True
  19318 
  19319        # JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, and
  19320        # JS::SetReservedSlot are also used too many places to restate
  19321        # dependency logic.
  19322        bindingHeaders["js/Object.h"] = True
  19323 
  19324        # JS::IsCallable, JS::Call, JS::Construct
  19325        bindingHeaders["js/CallAndConstruct.h"] = True
  19326 
  19327        # JS_IsExceptionPending
  19328        bindingHeaders["js/Exception.h"] = True
  19329 
  19330        # JS::Map{Clear, Delete, Has, Get, Set}
  19331        bindingHeaders["js/MapAndSet.h"] = True
  19332 
  19333        # JS_DefineElement, JS_DefineProperty, JS_DefinePropertyById,
  19334        # JS_DefineUCProperty, JS_ForwardGetPropertyTo, JS_GetProperty,
  19335        # JS_GetPropertyById, JS_HasPropertyById, JS_SetProperty,
  19336        # JS_SetPropertyById
  19337        bindingHeaders["js/PropertyAndElement.h"] = True
  19338 
  19339        # JS_GetOwnPropertyDescriptorById
  19340        bindingHeaders["js/PropertyDescriptor.h"] = True
  19341 
  19342        def descriptorDeprecated(desc):
  19343            iface = desc.interface
  19344            return any(
  19345                m.getExtendedAttribute("Deprecated") for m in iface.members + [iface]
  19346            )
  19347 
  19348        bindingHeaders["mozilla/dom/Document.h"] = any(
  19349            descriptorDeprecated(d) for d in descriptors
  19350        )
  19351 
  19352        bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any(
  19353            d.concrete and d.proxy for d in descriptors
  19354        )
  19355 
  19356        bindingHeaders["mozilla/dom/ProxyHandlerUtils.h"] = any(
  19357            d.concrete and d.proxy for d in descriptors
  19358        )
  19359 
  19360        bindingHeaders["js/String.h"] = any(
  19361            d.needsMissingPropUseCounters for d in descriptors
  19362        )
  19363 
  19364        hasCrossOriginObjects = any(
  19365            d.concrete and d.isMaybeCrossOriginObject() for d in descriptors
  19366        )
  19367        bindingHeaders["mozilla/dom/MaybeCrossOriginObject.h"] = hasCrossOriginObjects
  19368        bindingHeaders["AccessCheck.h"] = hasCrossOriginObjects
  19369        hasCEReactions = any(d.hasCEReactions() for d in descriptors)
  19370        bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = hasCEReactions
  19371        bindingHeaders["mozilla/dom/DocGroup.h"] = hasCEReactions
  19372 
  19373        def descriptorHasChromeOnly(desc):
  19374            ctor = desc.interface.ctor()
  19375 
  19376            return (
  19377                any(
  19378                    isChromeOnly(a) or needsCallerType(a)
  19379                    for a in desc.interface.members
  19380                )
  19381                or desc.interface.getExtendedAttribute("ChromeOnly") is not None
  19382                or
  19383                # JS-implemented interfaces with an interface object get a
  19384                # chromeonly _create method.  And interfaces with an
  19385                # interface object might have a ChromeOnly constructor.
  19386                (
  19387                    desc.interface.hasInterfaceObject()
  19388                    and (
  19389                        desc.interface.isJSImplemented()
  19390                        or (ctor and isChromeOnly(ctor))
  19391                    )
  19392                )
  19393            )
  19394 
  19395        # XXXkhuey ugly hack but this is going away soon.
  19396        bindingHeaders["xpcprivate.h"] = webIDLFile.endswith("EventTarget.webidl")
  19397 
  19398        hasThreadChecks = any(d.hasThreadChecks() for d in descriptors)
  19399        bindingHeaders["nsThreadUtils.h"] = hasThreadChecks
  19400 
  19401        dictionaries = config.getDictionaries(webIDLFile)
  19402 
  19403        def dictionaryHasChromeOnly(dictionary):
  19404            while dictionary:
  19405                if any(isChromeOnly(m) for m in dictionary.members):
  19406                    return True
  19407                dictionary = dictionary.parent
  19408            return False
  19409 
  19410        def needsNonSystemPrincipal(member):
  19411            return (
  19412                member.getExtendedAttribute("NeedsSubjectPrincipal") == ["NonSystem"]
  19413                or member.getExtendedAttribute("SetterNeedsSubjectPrincipal")
  19414                == ["NonSystem"]
  19415                or member.getExtendedAttribute("GetterNeedsSubjectPrincipal")
  19416                == ["NonSystem"]
  19417            )
  19418 
  19419        def descriptorNeedsNonSystemPrincipal(d):
  19420            return any(needsNonSystemPrincipal(m) for m in d.interface.members)
  19421 
  19422        def descriptorHasPrefDisabler(desc):
  19423            iface = desc.interface
  19424            return any(
  19425                PropertyDefiner.getControllingCondition(m, desc).hasDisablers()
  19426                for m in iface.members
  19427                if (m.isMethod() or m.isAttr() or m.isConst())
  19428            )
  19429 
  19430        def addPrefHeaderForObject(bindingHeaders, obj):
  19431            """
  19432            obj might be a dictionary member or an interface.
  19433            """
  19434            if obj is not None:
  19435                pref = PropertyDefiner.getStringAttr(obj, "Pref")
  19436                if pref:
  19437                    bindingHeaders[prefHeader(pref)] = True
  19438 
  19439        def addPrefHeadersForDictionary(bindingHeaders, dictionary):
  19440            while dictionary:
  19441                for m in dictionary.members:
  19442                    addPrefHeaderForObject(bindingHeaders, m)
  19443                dictionary = dictionary.parent
  19444 
  19445        for d in dictionaries:
  19446            addPrefHeadersForDictionary(bindingHeaders, d)
  19447            try:
  19448                if CGDictionary.dictionaryNeedsCycleCollection(d):
  19449                    bindingDeclareHeaders["nsCycleCollectionParticipant.h"] = True
  19450            except CycleCollectionUnsupported:
  19451                # We have some member that we don't know how to CC.  Don't output
  19452                # our cycle collection overloads, so attempts to CC us will fail to
  19453                # compile instead of misbehaving.
  19454                pass
  19455 
  19456        for d in descriptors:
  19457            interface = d.interface
  19458            addPrefHeaderForObject(bindingHeaders, interface)
  19459            addPrefHeaderForObject(bindingHeaders, interface.ctor())
  19460 
  19461        bindingHeaders["mozilla/dom/WebIDLPrefs.h"] = any(
  19462            descriptorHasPrefDisabler(d) for d in descriptors
  19463        )
  19464        bindingHeaders["nsContentUtils.h"] = (
  19465            any(descriptorHasChromeOnly(d) for d in descriptors)
  19466            or any(descriptorNeedsNonSystemPrincipal(d) for d in descriptors)
  19467            or any(dictionaryHasChromeOnly(d) for d in dictionaries)
  19468        )
  19469        hasNonEmptyDictionaries = any(len(dict.members) > 0 for dict in dictionaries)
  19470        callbacks = config.getCallbacks(webIDLFile)
  19471        callbackDescriptors = config.getDescriptors(
  19472            webIDLFile=webIDLFile, isCallback=True
  19473        )
  19474        jsImplemented = config.getDescriptors(
  19475            webIDLFile=webIDLFile, isJSImplemented=True
  19476        )
  19477        bindingDeclareHeaders["nsWeakReference.h"] = jsImplemented
  19478        bindingDeclareHeaders["mozilla/dom/PrototypeList.h"] = descriptors
  19479        bindingHeaders["nsIGlobalObject.h"] = jsImplemented
  19480        bindingHeaders["AtomList.h"] = (
  19481            hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
  19482        )
  19483 
  19484        if callbackDescriptors:
  19485            bindingDeclareHeaders["mozilla/ErrorResult.h"] = True
  19486 
  19487        def descriptorClearsPropsInSlots(descriptor):
  19488            if not descriptor.wrapperCache:
  19489                return False
  19490            return any(
  19491                m.isAttr() and m.getExtendedAttribute("StoreInSlot")
  19492                for m in descriptor.interface.members
  19493            )
  19494 
  19495        bindingHeaders["nsJSUtils.h"] = any(
  19496            descriptorClearsPropsInSlots(d) for d in descriptors
  19497        )
  19498 
  19499        # Make sure we can sanely use binding_detail in generated code.
  19500        cgthings = [
  19501            CGGeneric(
  19502                dedent(
  19503                    """
  19504            namespace binding_detail {}; // Just to make sure it's known as a namespace
  19505            using namespace mozilla::dom::binding_detail;
  19506            """
  19507                )
  19508            )
  19509        ]
  19510 
  19511        # Do codegen for all the enums
  19512        enums = config.getEnums(webIDLFile)
  19513        cgthings.extend(CGEnum(e) for e in enums)
  19514        maxEnumValues = CGList([CGMaxContiguousEnumValue(e) for e in enums], "\n")
  19515 
  19516        bindingDeclareHeaders["mozilla/Span.h"] = enums
  19517        bindingDeclareHeaders["mozilla/ArrayUtils.h"] = enums
  19518        bindingDeclareHeaders["mozilla/EnumTypeTraits.h"] = enums
  19519 
  19520        hasCode = descriptors or callbackDescriptors or dictionaries or callbacks
  19521        bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode
  19522        bindingHeaders["mozilla/OwningNonNull.h"] = hasCode
  19523        bindingHeaders["<type_traits>"] = hasCode
  19524        bindingHeaders["mozilla/dom/BindingDeclarations.h"] = not hasCode and enums
  19525 
  19526        bindingHeaders["WrapperFactory.h"] = descriptors
  19527        bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
  19528        bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries  # AutoJSAPI
  19529        # Ensure we see our enums in the generated .cpp file, for the ToJSValue
  19530        # method body.  Also ensure that we see jsapi.h.
  19531        if enums:
  19532            bindingHeaders[CGHeaders.getDeclarationFilename(enums[0])] = True
  19533            bindingHeaders["jsapi.h"] = True
  19534 
  19535        # For things that have [UseCounter] or [InstrumentedProps] or [Trial]
  19536        for d in descriptors:
  19537            if d.concrete:
  19538                if d.instrumentedProps:
  19539                    bindingHeaders["mozilla/UseCounter.h"] = True
  19540                if d.needsMissingPropUseCounters:
  19541                    bindingHeaders[prefHeader(MISSING_PROP_PREF)] = True
  19542            if d.interface.isSerializable():
  19543                bindingHeaders["mozilla/dom/StructuredCloneTags.h"] = True
  19544            if d.wantsXrays:
  19545                bindingHeaders["mozilla/Atomics.h"] = True
  19546                bindingHeaders["mozilla/dom/XrayExpandoClass.h"] = True
  19547                if d.wantsXrayExpandoClass:
  19548                    bindingHeaders["XrayWrapper.h"] = True
  19549            for m in d.interface.members:
  19550                if m.getExtendedAttribute("UseCounter"):
  19551                    bindingHeaders["mozilla/UseCounter.h"] = True
  19552                if m.getExtendedAttribute("Trial"):
  19553                    bindingHeaders["mozilla/OriginTrials.h"] = True
  19554 
  19555        bindingHeaders["mozilla/dom/SimpleGlobalObject.h"] = any(
  19556            CGDictionary.dictionarySafeToJSONify(d) for d in dictionaries
  19557        )
  19558 
  19559        for ancestor in (findAncestorWithInstrumentedProps(d) for d in descriptors):
  19560            if not ancestor:
  19561                continue
  19562            bindingHeaders[CGHeaders.getDeclarationFilename(ancestor)] = True
  19563 
  19564        for d in descriptors:
  19565            ancestor = d.interface.parent
  19566            while ancestor:
  19567                if ancestor.reflectedHTMLAttributesReturningFrozenArray:
  19568                    bindingHeaders[CGHeaders.getDeclarationFilename(ancestor)] = True
  19569                    break
  19570                ancestor = ancestor.parent
  19571 
  19572        cgthings.extend(traverseMethods)
  19573        cgthings.extend(unlinkMethods)
  19574 
  19575        # Do codegen for all the dictionaries.  We have to be a bit careful
  19576        # here, because we have to generate these in order from least derived
  19577        # to most derived so that class inheritance works out.  We also have to
  19578        # generate members before the dictionary that contains them.
  19579 
  19580        for t in dependencySortDictionariesAndUnionsAndCallbacks(
  19581            dictionaries + unionStructs + callbacks
  19582        ):
  19583            if t.isDictionary():
  19584                cgthings.append(CGDictionary(t, config))
  19585            elif t.isUnion():
  19586                cgthings.append(CGUnionStruct(t, config))
  19587                cgthings.append(CGUnionStruct(t, config, True))
  19588            else:
  19589                assert t.isCallback()
  19590                cgthings.append(CGCallbackFunction(t, config))
  19591                cgthings.append(CGNamespace("binding_detail", CGFastCallback(t)))
  19592 
  19593        # Do codegen for all the descriptors
  19594        cgthings.extend(
  19595            [CGDescriptor(x, config.attributeTemplates) for x in descriptors]
  19596        )
  19597 
  19598        # Do codegen for all the callback interfaces.
  19599        cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors])
  19600 
  19601        cgthings.extend(
  19602            [
  19603                CGNamespace("binding_detail", CGFastCallback(x.interface))
  19604                for x in callbackDescriptors
  19605            ]
  19606        )
  19607 
  19608        # Do codegen for JS implemented classes
  19609        def getParentDescriptor(desc):
  19610            if not desc.interface.parent:
  19611                return set()
  19612            return {desc.getDescriptor(desc.interface.parent.identifier.name)}
  19613 
  19614        for x in dependencySortObjects(
  19615            jsImplemented, getParentDescriptor, lambda d: d.interface.identifier.name
  19616        ):
  19617            cgthings.append(
  19618                CGCallbackInterface(x, spiderMonkeyInterfacesAreStructs=True)
  19619            )
  19620            cgthings.append(CGJSImplClass(x))
  19621 
  19622        # And make sure we have the right number of newlines at the end
  19623        curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
  19624 
  19625        # Wrap all of that in our namespaces.
  19626 
  19627        if len(maxEnumValues) > 0:
  19628            curr = CGNamespace("dom", CGWrapper(curr, pre="\n"))
  19629            curr = CGWrapper(CGList([curr, maxEnumValues], "\n\n"), post="\n\n")
  19630            curr = CGNamespace("mozilla", CGWrapper(curr, pre="\n"))
  19631        else:
  19632            curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, pre="\n"))
  19633 
  19634        curr = CGList(
  19635            [
  19636                CGForwardDeclarations(
  19637                    config,
  19638                    descriptors,
  19639                    callbacks,
  19640                    dictionaries,
  19641                    callbackDescriptors + jsImplemented,
  19642                    additionalDeclarations=unionDeclarations,
  19643                ),
  19644                curr,
  19645            ],
  19646            "\n",
  19647        )
  19648 
  19649        unionTypedefs = config.getUnionTypedefs(webIDLFile)
  19650        cgUnionTypedefs = CGList(
  19651            [CGUnionTypedef(t, config) for t in unionTypedefs], joiner="\n"
  19652        )
  19653        curr = CGList([cgUnionTypedefs, curr], joiner="\n")
  19654 
  19655        # Add header includes.
  19656        bindingHeaders = [
  19657            header for header, include in bindingHeaders.items() if include
  19658        ]
  19659        bindingDeclareHeaders = [
  19660            header for header, include in bindingDeclareHeaders.items() if include
  19661        ]
  19662 
  19663        curr = CGHeaders(
  19664            descriptors,
  19665            dictionaries,
  19666            callbacks,
  19667            callbackDescriptors,
  19668            bindingDeclareHeaders,
  19669            bindingHeaders,
  19670            prefix,
  19671            curr,
  19672            config,
  19673            jsImplemented,
  19674        )
  19675 
  19676        # Add include guards.
  19677        curr = CGIncludeGuard(prefix, curr)
  19678 
  19679        # Add the auto-generated comment.
  19680        curr = CGWrapper(
  19681            curr,
  19682            pre=(
  19683                AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT % os.path.basename(webIDLFile)
  19684            ),
  19685        )
  19686 
  19687        # Store the final result.
  19688        self.root = curr
  19689 
  19690    def declare(self):
  19691        return stripTrailingWhitespace(self.root.declare())
  19692 
  19693    def define(self):
  19694        return stripTrailingWhitespace(self.root.define())
  19695 
  19696    def forward_declare(self):
  19697        return stripTrailingWhitespace(self.root.forward_declare())
  19698 
  19699    def deps(self):
  19700        return self.root.deps()
  19701 
  19702 
  19703 class CGNativeMember(ClassMethod):
  19704    def __init__(
  19705        self,
  19706        descriptorProvider,
  19707        member,
  19708        name,
  19709        signature,
  19710        extendedAttrs,
  19711        breakAfter=True,
  19712        passJSBitsAsNeeded=True,
  19713        visibility="public",
  19714        spiderMonkeyInterfacesAreStructs=True,
  19715        variadicIsSequence=False,
  19716        resultNotAddRefed=False,
  19717        virtual=False,
  19718        override=False,
  19719        canRunScript=False,
  19720    ):
  19721        """
  19722        If spiderMonkeyInterfacesAreStructs is false, SpiderMonkey interfaces
  19723        will be passed as JS::Handle<JSObject*>.  If it's true they will be
  19724        passed as one of the dom::SpiderMonkeyInterfaceObjectStorage subclasses.
  19725 
  19726        If passJSBitsAsNeeded is false, we don't automatically pass in a
  19727        JSContext* or a JSObject* based on the return and argument types.  We
  19728        can still pass it based on 'implicitJSContext' annotations.
  19729        """
  19730        self.descriptorProvider = descriptorProvider
  19731        self.member = member
  19732        self.extendedAttrs = extendedAttrs
  19733        self.resultAlreadyAddRefed = not resultNotAddRefed
  19734        self.passJSBitsAsNeeded = passJSBitsAsNeeded
  19735        self.spiderMonkeyInterfacesAreStructs = spiderMonkeyInterfacesAreStructs
  19736        self.variadicIsSequence = variadicIsSequence
  19737        breakAfterSelf = "\n" if breakAfter else ""
  19738        ClassMethod.__init__(
  19739            self,
  19740            name,
  19741            self.getReturnType(signature[0], False),
  19742            self.getArgs(signature[0], signature[1]),
  19743            static=member.isStatic(),
  19744            # Mark our getters, which are attrs that
  19745            # have a non-void return type, as const.
  19746            const=(
  19747                not member.isStatic()
  19748                and member.isAttr()
  19749                and not signature[0].isUndefined()
  19750            ),
  19751            breakAfterReturnDecl=" ",
  19752            breakAfterSelf=breakAfterSelf,
  19753            visibility=visibility,
  19754            virtual=virtual,
  19755            override=override,
  19756            canRunScript=canRunScript,
  19757        )
  19758 
  19759    def getReturnType(self, type, isMember):
  19760        return self.getRetvalInfo(type, isMember)[0]
  19761 
  19762    def getRetvalInfo(self, type, isMember):
  19763        """
  19764        Returns a tuple:
  19765 
  19766        The first element is the type declaration for the retval
  19767 
  19768        The second element is a default value that can be used on error returns.
  19769        For cases whose behavior depends on isMember, the second element will be
  19770        None if isMember is true.
  19771 
  19772        The third element is a template for actually returning a value stored in
  19773        "${declName}" and "${holderName}".  This means actually returning it if
  19774        we're not outparam, else assigning to the "retval" outparam.  If
  19775        isMember is true, this can be None, since in that case the caller will
  19776        never examine this value.
  19777        """
  19778        if type.isUndefined():
  19779            return "void", "", ""
  19780        if type.isPrimitive() and type.tag() in builtinNames:
  19781            result = CGGeneric(builtinNames[type.tag()])
  19782            defaultReturnArg = "0"
  19783            if type.nullable():
  19784                result = CGTemplatedType("Nullable", result)
  19785                defaultReturnArg = ""
  19786            return (
  19787                result.define(),
  19788                "%s(%s)" % (result.define(), defaultReturnArg),
  19789                "return ${declName};\n",
  19790            )
  19791        if type.isJSString():
  19792            if isMember:
  19793                raise TypeError("JSString not supported as return type member")
  19794            # Outparam
  19795            return "void", "", "aRetVal.set(${declName});\n"
  19796        if type.isDOMString() or type.isUSVString():
  19797            if isMember:
  19798                # No need for a third element in the isMember case
  19799                return "nsString", None, None
  19800            # Outparam
  19801            return "void", "", "aRetVal = ${declName};\n"
  19802        if type.isByteString() or type.isUTF8String():
  19803            if isMember:
  19804                # No need for a third element in the isMember case
  19805                return "nsCString", None, None
  19806            # Outparam
  19807            return "void", "", "aRetVal = ${declName};\n"
  19808        if type.isEnum():
  19809            enumName = type.unroll().inner.identifier.name
  19810            if type.nullable():
  19811                enumName = CGTemplatedType("Nullable", CGGeneric(enumName)).define()
  19812                defaultValue = "%s()" % enumName
  19813            else:
  19814                defaultValue = "%s(0)" % enumName
  19815            return enumName, defaultValue, "return ${declName};\n"
  19816        if type.isGeckoInterface() or type.isPromise():
  19817            if type.isGeckoInterface():
  19818                iface = type.unroll().inner
  19819                result = CGGeneric(
  19820                    self.descriptorProvider.getDescriptor(
  19821                        iface.identifier.name
  19822                    ).prettyNativeType
  19823                )
  19824            else:
  19825                result = CGGeneric("Promise")
  19826            if self.resultAlreadyAddRefed:
  19827                if isMember:
  19828                    holder = "RefPtr"
  19829                else:
  19830                    holder = "already_AddRefed"
  19831                if memberReturnsNewObject(self.member) or isMember:
  19832                    warning = ""
  19833                else:
  19834                    warning = "// Return a raw pointer here to avoid refcounting, but make sure it's safe (the object should be kept alive by the callee).\n"
  19835                result = CGWrapper(result, pre=("%s%s<" % (warning, holder)), post=">")
  19836            else:
  19837                result = CGWrapper(result, post="*")
  19838            # Since we always force an owning type for callback return values,
  19839            # our ${declName} is an OwningNonNull or RefPtr.  So we can just
  19840            # .forget() to get our already_AddRefed.
  19841            return result.define(), "nullptr", "return ${declName}.forget();\n"
  19842        if type.isCallback():
  19843            return (
  19844                "already_AddRefed<%s>" % type.unroll().callback.identifier.name,
  19845                "nullptr",
  19846                "return ${declName}.forget();\n",
  19847            )
  19848        if type.isAny():
  19849            if isMember:
  19850                # No need for a third element in the isMember case
  19851                return "JS::Value", None, None
  19852            # Outparam
  19853            return "void", "", "aRetVal.set(${declName});\n"
  19854 
  19855        if type.isObject():
  19856            if isMember:
  19857                # No need for a third element in the isMember case
  19858                return "JSObject*", None, None
  19859            return "void", "", "aRetVal.set(${declName});\n"
  19860        if type.isSpiderMonkeyInterface():
  19861            if isMember:
  19862                # No need for a third element in the isMember case
  19863                return "JSObject*", None, None
  19864            if type.nullable():
  19865                returnCode = (
  19866                    "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj()"
  19867                )
  19868            else:
  19869                returnCode = "${declName}.Obj()"
  19870            return "void", "", "aRetVal.set(%s);\n" % returnCode
  19871        if type.isSequence():
  19872            # If we want to handle sequence-of-sequences return values, we're
  19873            # going to need to fix example codegen to not produce nsTArray<void>
  19874            # for the relevant argument...
  19875            assert not isMember
  19876            # Outparam.
  19877            if type.nullable():
  19878                returnCode = dedent(
  19879                    """
  19880                    if (${declName}.IsNull()) {
  19881                      aRetVal.SetNull();
  19882                    } else {
  19883                      aRetVal.SetValue() = std::move(${declName}.Value());
  19884                    }
  19885                    """
  19886                )
  19887            else:
  19888                returnCode = "aRetVal = std::move(${declName});\n"
  19889            return "void", "", returnCode
  19890        if type.isRecord():
  19891            # If we want to handle record-of-record return values, we're
  19892            # going to need to fix example codegen to not produce record<void>
  19893            # for the relevant argument...
  19894            assert not isMember
  19895            # In this case we convert directly into our outparam to start with
  19896            return "void", "", ""
  19897        if type.isDictionary():
  19898            if isMember:
  19899                # Only the first member of the tuple matters here, but return
  19900                # bogus values for the others in case someone decides to use
  19901                # them.
  19902                return CGDictionary.makeDictionaryName(type.inner), None, None
  19903            # In this case we convert directly into our outparam to start with
  19904            return "void", "", ""
  19905        if type.isUnion():
  19906            if isMember:
  19907                # Only the first member of the tuple matters here, but return
  19908                # bogus values for the others in case someone decides to use
  19909                # them.
  19910                return CGUnionStruct.unionTypeDecl(type, True), None, None
  19911            # In this case we convert directly into our outparam to start with
  19912            return "void", "", ""
  19913 
  19914        raise TypeError("Don't know how to declare return value for %s" % type)
  19915 
  19916    def getArgs(self, returnType, argList):
  19917        args = [self.getArg(arg) for arg in argList]
  19918        # Now the outparams
  19919        if returnType.isJSString():
  19920            args.append(Argument("JS::MutableHandle<JSString*>", "aRetVal"))
  19921        elif returnType.isDOMString() or returnType.isUSVString():
  19922            args.append(Argument("nsString&", "aRetVal"))
  19923        elif returnType.isByteString() or returnType.isUTF8String():
  19924            args.append(Argument("nsCString&", "aRetVal"))
  19925        elif returnType.isSequence():
  19926            nullable = returnType.nullable()
  19927            if nullable:
  19928                returnType = returnType.inner
  19929            # And now the actual underlying type
  19930            elementDecl = self.getReturnType(returnType.inner, True)
  19931            type = CGTemplatedType("nsTArray", CGGeneric(elementDecl))
  19932            if nullable:
  19933                type = CGTemplatedType("Nullable", type)
  19934            args.append(Argument("%s&" % type.define(), "aRetVal"))
  19935        elif returnType.isRecord():
  19936            nullable = returnType.nullable()
  19937            if nullable:
  19938                returnType = returnType.inner
  19939            # And now the actual underlying type
  19940            elementDecl = self.getReturnType(returnType.inner, True)
  19941            type = CGTemplatedType(
  19942                "Record", [recordKeyDeclType(returnType), CGGeneric(elementDecl)]
  19943            )
  19944            if nullable:
  19945                type = CGTemplatedType("Nullable", type)
  19946            args.append(Argument("%s&" % type.define(), "aRetVal"))
  19947        elif returnType.isDictionary():
  19948            nullable = returnType.nullable()
  19949            if nullable:
  19950                returnType = returnType.inner
  19951            dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner))
  19952            if nullable:
  19953                dictType = CGTemplatedType("Nullable", dictType)
  19954            args.append(Argument("%s&" % dictType.define(), "aRetVal"))
  19955        elif returnType.isUnion():
  19956            args.append(
  19957                Argument(
  19958                    "%s&" % CGUnionStruct.unionTypeDecl(returnType, True), "aRetVal"
  19959                )
  19960            )
  19961        elif returnType.isAny():
  19962            args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
  19963        elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
  19964            args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
  19965 
  19966        # And the nsIPrincipal
  19967        if "needsSubjectPrincipal" in self.extendedAttrs:
  19968            if "needsNonSystemSubjectPrincipal" in self.extendedAttrs:
  19969                args.append(Argument("nsIPrincipal*", "aPrincipal"))
  19970            else:
  19971                args.append(Argument("nsIPrincipal&", "aPrincipal"))
  19972        # And the caller type, if desired.
  19973        if needsCallerType(self.member):
  19974            args.append(Argument("CallerType", "aCallerType"))
  19975        # And the ErrorResult or OOMReporter
  19976        if "needsErrorResult" in self.extendedAttrs:
  19977            # Use aRv so it won't conflict with local vars named "rv"
  19978            args.append(Argument("ErrorResult&", "aRv"))
  19979        elif "canOOM" in self.extendedAttrs:
  19980            args.append(Argument("OOMReporter&", "aRv"))
  19981 
  19982        # The legacycaller thisval
  19983        if self.member.isMethod() and self.member.isLegacycaller():
  19984            # If it has an identifier, we can't deal with it yet
  19985            assert self.member.isIdentifierLess()
  19986            args.insert(0, Argument("const JS::Value&", "aThisVal"))
  19987        # And jscontext bits.
  19988        if needCx(
  19989            returnType,
  19990            argList,
  19991            self.extendedAttrs,
  19992            self.passJSBitsAsNeeded,
  19993            self.member.isStatic(),
  19994        ):
  19995            args.insert(0, Argument("JSContext*", "cx"))
  19996            if needScopeObject(
  19997                returnType,
  19998                argList,
  19999                self.extendedAttrs,
  20000                self.descriptorProvider.wrapperCache,
  20001                self.passJSBitsAsNeeded,
  20002                self.member.getExtendedAttribute("StoreInSlot"),
  20003            ):
  20004                args.insert(1, Argument("JS::Handle<JSObject*>", "obj"))
  20005        # And if we're static, a global
  20006        if self.member.isStatic():
  20007            args.insert(0, Argument("const GlobalObject&", "global"))
  20008        return args
  20009 
  20010    def doGetArgType(self, type, optional, isMember):
  20011        """
  20012        The main work of getArgType.  Returns a string type decl, whether this
  20013        is a const ref, as well as whether the type should be wrapped in
  20014        Nullable as needed.
  20015 
  20016        isMember can be false or one of the strings "Sequence", "Variadic",
  20017                 "Record"
  20018        """
  20019        if type.isSequence():
  20020            nullable = type.nullable()
  20021            if nullable:
  20022                type = type.inner
  20023            elementType = type.inner
  20024            argType = self.getArgType(elementType, False, "Sequence")[0]
  20025            decl = CGTemplatedType("Sequence", argType)
  20026            return decl.define(), True, True
  20027 
  20028        if type.isRecord():
  20029            nullable = type.nullable()
  20030            if nullable:
  20031                type = type.inner
  20032            elementType = type.inner
  20033            argType = self.getArgType(elementType, False, "Record")[0]
  20034            decl = CGTemplatedType("Record", [recordKeyDeclType(type), argType])
  20035            return decl.define(), True, True
  20036 
  20037        if type.isUnion():
  20038            # unionTypeDecl will handle nullable types, so return False for
  20039            # auto-wrapping in Nullable
  20040            return CGUnionStruct.unionTypeDecl(type, isMember), True, False
  20041 
  20042        if type.isPromise():
  20043            assert not type.nullable()
  20044            if optional or isMember:
  20045                typeDecl = "OwningNonNull<Promise>"
  20046            else:
  20047                typeDecl = "Promise&"
  20048            return (typeDecl, False, False)
  20049 
  20050        if type.isGeckoInterface() and not type.isCallbackInterface():
  20051            iface = type.unroll().inner
  20052            if iface.identifier.name == "WindowProxy":
  20053                return "WindowProxyHolder", True, False
  20054 
  20055            argIsPointer = type.nullable() or iface.isExternal()
  20056            forceOwningType = iface.isCallback() or isMember
  20057            if argIsPointer:
  20058                if (optional or isMember) and forceOwningType:
  20059                    typeDecl = "RefPtr<%s>"
  20060                else:
  20061                    typeDecl = "%s*"
  20062            else:
  20063                if optional or isMember:
  20064                    if forceOwningType:
  20065                        typeDecl = "OwningNonNull<%s>"
  20066                    else:
  20067                        typeDecl = "NonNull<%s>"
  20068                else:
  20069                    typeDecl = "%s&"
  20070            return (
  20071                (
  20072                    typeDecl
  20073                    % self.descriptorProvider.getDescriptor(
  20074                        iface.identifier.name
  20075                    ).prettyNativeType
  20076                ),
  20077                False,
  20078                False,
  20079            )
  20080 
  20081        if type.isSpiderMonkeyInterface():
  20082            if not self.spiderMonkeyInterfacesAreStructs:
  20083                return "JS::Handle<JSObject*>", False, False
  20084 
  20085            # Unroll for the name, in case we're nullable.
  20086            return type.unroll().name, True, True
  20087 
  20088        if type.isJSString():
  20089            if isMember:
  20090                raise TypeError("JSString not supported as member")
  20091            return "JS::Handle<JSString*>", False, False
  20092 
  20093        if type.isDOMString() or type.isUSVString():
  20094            if isMember:
  20095                declType = "nsString"
  20096            else:
  20097                declType = "nsAString"
  20098            return declType, True, False
  20099 
  20100        if type.isByteString() or type.isUTF8String():
  20101            # TODO(emilio): Maybe bytestrings could benefit from nsAutoCString
  20102            # or such too.
  20103            if type.isUTF8String() and not isMember:
  20104                declType = "nsACString"
  20105            else:
  20106                declType = "nsCString"
  20107            return declType, True, False
  20108 
  20109        if type.isEnum():
  20110            return type.unroll().inner.identifier.name, False, True
  20111 
  20112        if type.isCallback() or type.isCallbackInterface():
  20113            forceOwningType = optional or isMember
  20114            if type.nullable():
  20115                if forceOwningType:
  20116                    declType = "RefPtr<%s>"
  20117                else:
  20118                    declType = "%s*"
  20119            else:
  20120                if forceOwningType:
  20121                    declType = "OwningNonNull<%s>"
  20122                else:
  20123                    declType = "%s&"
  20124            if type.isCallback():
  20125                name = type.unroll().callback.identifier.name
  20126            else:
  20127                name = type.unroll().inner.identifier.name
  20128            return declType % name, False, False
  20129 
  20130        if type.isAny():
  20131            # Don't do the rooting stuff for variadics for now
  20132            if isMember:
  20133                declType = "JS::Value"
  20134            else:
  20135                declType = "JS::Handle<JS::Value>"
  20136            return declType, False, False
  20137 
  20138        if type.isObject():
  20139            if isMember:
  20140                declType = "JSObject*"
  20141            else:
  20142                declType = "JS::Handle<JSObject*>"
  20143            return declType, False, False
  20144 
  20145        if type.isDictionary():
  20146            typeName = CGDictionary.makeDictionaryName(type.inner)
  20147            return typeName, True, True
  20148 
  20149        assert type.isPrimitive()
  20150 
  20151        return builtinNames[type.tag()], False, True
  20152 
  20153    def getArgType(self, type, optional, isMember):
  20154        """
  20155        Get the type of an argument declaration.  Returns the type CGThing, and
  20156        whether this should be a const ref.
  20157 
  20158        isMember can be False, "Sequence", or "Variadic"
  20159        """
  20160        decl, ref, handleNullable = self.doGetArgType(type, optional, isMember)
  20161        decl = CGGeneric(decl)
  20162        if handleNullable and type.nullable():
  20163            decl = CGTemplatedType("Nullable", decl)
  20164            ref = True
  20165        if isMember == "Variadic":
  20166            arrayType = "Sequence" if self.variadicIsSequence else "nsTArray"
  20167            decl = CGTemplatedType(arrayType, decl)
  20168            ref = True
  20169        elif optional:
  20170            # Note: All variadic args claim to be optional, but we can just use
  20171            # empty arrays to represent them not being present.
  20172            decl = CGTemplatedType("Optional", decl)
  20173            ref = True
  20174        return (decl, ref)
  20175 
  20176    def getArg(self, arg):
  20177        """
  20178        Get the full argument declaration for an argument
  20179        """
  20180        decl, ref = self.getArgType(
  20181            arg.type, arg.canHaveMissingValue(), "Variadic" if arg.variadic else False
  20182        )
  20183        if ref:
  20184            decl = CGWrapper(decl, pre="const ", post="&")
  20185 
  20186        return Argument(decl.define(), arg.identifier.name)
  20187 
  20188    def arguments(self):
  20189        return self.member.signatures()[0][1]
  20190 
  20191 
  20192 class CGExampleMethod(CGNativeMember):
  20193    def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
  20194        CGNativeMember.__init__(
  20195            self,
  20196            descriptor,
  20197            method,
  20198            CGSpecializedMethod.makeNativeName(descriptor, method),
  20199            signature,
  20200            descriptor.getExtendedAttributes(method),
  20201            breakAfter=breakAfter,
  20202            variadicIsSequence=True,
  20203        )
  20204 
  20205    def declare(self, cgClass):
  20206        assert self.member.isMethod()
  20207        # We skip declaring ourselves if this is a maplike/setlike/iterable
  20208        # method, because those get implemented automatically by the binding
  20209        # machinery, so the implementor of the interface doesn't have to worry
  20210        # about it.
  20211        if self.member.isMaplikeOrSetlikeOrIterableMethod():
  20212            return ""
  20213        return CGNativeMember.declare(self, cgClass)
  20214 
  20215    def define(self, cgClass):
  20216        return ""
  20217 
  20218 
  20219 class CGExampleGetter(CGNativeMember):
  20220    def __init__(self, descriptor, attr):
  20221        CGNativeMember.__init__(
  20222            self,
  20223            descriptor,
  20224            attr,
  20225            CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
  20226            (attr.type, []),
  20227            descriptor.getExtendedAttributes(attr, getter=True),
  20228        )
  20229 
  20230    def getArgs(self, returnType, argList):
  20231        args = CGNativeMember.getArgs(self, returnType, argList)
  20232        if self.member.getExtendedAttribute(
  20233            "ReflectedHTMLAttributeReturningFrozenArray"
  20234        ):
  20235            args.insert(0, Argument("bool*", "aUseCachedValue"))
  20236        return args
  20237 
  20238    def declare(self, cgClass):
  20239        assert self.member.isAttr()
  20240        # We skip declaring ourselves if this is a maplike/setlike attr (in
  20241        # practice, "size"), because those get implemented automatically by the
  20242        # binding machinery, so the implementor of the interface doesn't have to
  20243        # worry about it.
  20244        if self.member.isMaplikeOrSetlikeAttr():
  20245            return ""
  20246        return CGNativeMember.declare(self, cgClass)
  20247 
  20248    def define(self, cgClass):
  20249        return ""
  20250 
  20251 
  20252 class CGExampleSetter(CGNativeMember):
  20253    def __init__(self, descriptor, attr):
  20254        CGNativeMember.__init__(
  20255            self,
  20256            descriptor,
  20257            attr,
  20258            CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
  20259            (
  20260                BuiltinTypes[IDLBuiltinType.Types.undefined],
  20261                [FakeArgument(attr.type)],
  20262            ),
  20263            descriptor.getExtendedAttributes(attr, setter=True),
  20264        )
  20265 
  20266    def define(self, cgClass):
  20267        return ""
  20268 
  20269 
  20270 class CGBindingImplClass(CGClass):
  20271    """
  20272    Common codegen for generating a C++ implementation of a WebIDL interface
  20273    """
  20274 
  20275    def __init__(
  20276        self,
  20277        descriptor,
  20278        cgMethod,
  20279        cgGetter,
  20280        cgSetter,
  20281        wantGetParent=True,
  20282        wrapMethodName="WrapObject",
  20283        skipStaticMethods=False,
  20284    ):
  20285        """
  20286        cgMethod, cgGetter and cgSetter are classes used to codegen methods,
  20287        getters and setters.
  20288        """
  20289        self.descriptor = descriptor
  20290        self._deps = descriptor.interface.getDeps()
  20291 
  20292        iface = descriptor.interface
  20293 
  20294        self.methodDecls = []
  20295 
  20296        def appendMethod(m, isConstructor=False):
  20297            sigs = m.signatures()
  20298            for s in sigs[:-1]:
  20299                # Don't put a blank line after overloads, until we
  20300                # get to the last one.
  20301                self.methodDecls.append(
  20302                    cgMethod(descriptor, m, s, isConstructor, breakAfter=False)
  20303                )
  20304            self.methodDecls.append(cgMethod(descriptor, m, sigs[-1], isConstructor))
  20305 
  20306        if iface.ctor():
  20307            appendMethod(iface.ctor(), isConstructor=True)
  20308        for n in iface.legacyFactoryFunctions:
  20309            appendMethod(n, isConstructor=True)
  20310        for m in iface.members:
  20311            if m.isMethod():
  20312                if m.isIdentifierLess():
  20313                    continue
  20314                if m.isMaplikeOrSetlikeOrIterableMethod():
  20315                    # Handled by generated code already
  20316                    continue
  20317                if not m.isStatic() or not skipStaticMethods:
  20318                    appendMethod(m)
  20319            elif m.isAttr():
  20320                if m.isMaplikeOrSetlikeAttr() or m.type.isObservableArray():
  20321                    # Handled by generated code already
  20322                    continue
  20323                self.methodDecls.append(cgGetter(descriptor, m))
  20324                if not m.readonly:
  20325                    self.methodDecls.append(cgSetter(descriptor, m))
  20326 
  20327        # Now do the special operations
  20328        def appendSpecialOperation(name, op):
  20329            if op is None:
  20330                return
  20331            assert len(op.signatures()) == 1
  20332            returnType, args = op.signatures()[0]
  20333            # Make a copy of the args, since we plan to modify them.
  20334            args = list(args)
  20335            if op.isGetter() or op.isDeleter():
  20336                # This is a total hack.  The '&' belongs with the
  20337                # type, not the name!  But it works, and is simpler
  20338                # than trying to somehow make this pretty.
  20339                args.append(
  20340                    FakeArgument(
  20341                        BuiltinTypes[IDLBuiltinType.Types.boolean], name="&found"
  20342                    )
  20343                )
  20344            if name == "Stringifier":
  20345                if op.isIdentifierLess():
  20346                    # XXXbz I wish we were consistent about our renaming here.
  20347                    name = "Stringify"
  20348                else:
  20349                    # We already added this method
  20350                    return
  20351            if name == "LegacyCaller":
  20352                if op.isIdentifierLess():
  20353                    # XXXbz I wish we were consistent about our renaming here.
  20354                    name = "LegacyCall"
  20355                else:
  20356                    # We already added this method
  20357                    return
  20358            self.methodDecls.append(
  20359                CGNativeMember(
  20360                    descriptor,
  20361                    op,
  20362                    name,
  20363                    (returnType, args),
  20364                    descriptor.getExtendedAttributes(op),
  20365                )
  20366            )
  20367 
  20368        # Sort things by name so we get stable ordering in the output.
  20369        ops = sorted(descriptor.operations.items(), key=lambda x: x[0])
  20370        for name, op in ops:
  20371            appendSpecialOperation(name, op)
  20372        # If we support indexed properties, then we need a Length()
  20373        # method so we know which indices are supported.
  20374        if descriptor.supportsIndexedProperties():
  20375            # But we don't need it if we already have an infallible
  20376            # "length" attribute, which we often do.
  20377            haveLengthAttr = any(
  20378                m
  20379                for m in iface.members
  20380                if m.isAttr()
  20381                and CGSpecializedGetterCommon.makeNativeName(descriptor, m) == "Length"
  20382            )
  20383            if not haveLengthAttr:
  20384                self.methodDecls.append(
  20385                    CGNativeMember(
  20386                        descriptor,
  20387                        FakeMember(),
  20388                        "Length",
  20389                        (BuiltinTypes[IDLBuiltinType.Types.unsigned_long], []),
  20390                        [],
  20391                    ),
  20392                )
  20393        # And if we support named properties we need to be able to
  20394        # enumerate the supported names.
  20395        if descriptor.supportsNamedProperties():
  20396            self.methodDecls.append(
  20397                CGNativeMember(
  20398                    descriptor,
  20399                    FakeMember(),
  20400                    "GetSupportedNames",
  20401                    (
  20402                        IDLSequenceType(
  20403                            None, BuiltinTypes[IDLBuiltinType.Types.domstring]
  20404                        ),
  20405                        [],
  20406                    ),
  20407                    [],
  20408                )
  20409            )
  20410 
  20411        if descriptor.concrete:
  20412            wrapArgs = [
  20413                Argument("JSContext*", "aCx"),
  20414                Argument("JS::Handle<JSObject*>", "aGivenProto"),
  20415            ]
  20416            if not descriptor.wrapperCache:
  20417                wrapReturnType = "bool"
  20418                wrapArgs.append(Argument("JS::MutableHandle<JSObject*>", "aReflector"))
  20419            else:
  20420                wrapReturnType = "JSObject*"
  20421            self.methodDecls.insert(
  20422                0,
  20423                ClassMethod(
  20424                    wrapMethodName,
  20425                    wrapReturnType,
  20426                    wrapArgs,
  20427                    virtual=descriptor.wrapperCache,
  20428                    breakAfterReturnDecl=" ",
  20429                    override=descriptor.wrapperCache,
  20430                    body=self.getWrapObjectBody(),
  20431                ),
  20432            )
  20433        if descriptor.hasCEReactions():
  20434            self.methodDecls.insert(
  20435                0,
  20436                ClassMethod(
  20437                    "GetDocGroup",
  20438                    "DocGroup*",
  20439                    [],
  20440                    const=True,
  20441                    breakAfterReturnDecl=" ",
  20442                    body=self.getGetDocGroupBody(),
  20443                ),
  20444            )
  20445        if wantGetParent:
  20446            self.methodDecls.insert(
  20447                0,
  20448                ClassMethod(
  20449                    "GetParentObject",
  20450                    self.getGetParentObjectReturnType(),
  20451                    [],
  20452                    const=True,
  20453                    breakAfterReturnDecl=" ",
  20454                    body=self.getGetParentObjectBody(),
  20455                ),
  20456            )
  20457 
  20458        # Invoke  CGClass.__init__ in any subclasses afterwards to do the actual codegen.
  20459 
  20460    def getWrapObjectBody(self):
  20461        return None
  20462 
  20463    def getGetParentObjectReturnType(self):
  20464        # The lack of newline before the end of the string is on purpose.
  20465        return dedent(
  20466            """
  20467            // This should return something that eventually allows finding a
  20468            // path to the global this object is associated with.  Most simply,
  20469            // returning an actual global works.
  20470            nsIGlobalObject*"""
  20471        )
  20472 
  20473    def getGetParentObjectBody(self):
  20474        return None
  20475 
  20476    def getGetDocGroupBody(self):
  20477        return None
  20478 
  20479    def deps(self):
  20480        return self._deps
  20481 
  20482 
  20483 class CGExampleObservableArrayCallback(CGNativeMember):
  20484    def __init__(self, descriptor, attr, callbackName):
  20485        assert attr.isAttr()
  20486        assert attr.type.isObservableArray()
  20487        CGNativeMember.__init__(
  20488            self,
  20489            descriptor,
  20490            attr,
  20491            self.makeNativeName(attr, callbackName),
  20492            (
  20493                BuiltinTypes[IDLBuiltinType.Types.undefined],
  20494                [
  20495                    FakeArgument(attr.type.inner, "aValue"),
  20496                    FakeArgument(
  20497                        BuiltinTypes[IDLBuiltinType.Types.unsigned_long], "aIndex"
  20498                    ),
  20499                ],
  20500            ),
  20501            ["needsErrorResult"],
  20502        )
  20503 
  20504    def declare(self, cgClass):
  20505        assert self.member.isAttr()
  20506        assert self.member.type.isObservableArray()
  20507        return CGNativeMember.declare(self, cgClass)
  20508 
  20509    def define(self, cgClass):
  20510        return ""
  20511 
  20512    @staticmethod
  20513    def makeNativeName(attr, callbackName):
  20514        assert attr.isAttr()
  20515        nativeName = MakeNativeName(attr.identifier.name)
  20516        return "On" + callbackName + nativeName
  20517 
  20518 
  20519 class CGExampleClass(CGBindingImplClass):
  20520    """
  20521    Codegen for the actual example class implementation for this descriptor
  20522    """
  20523 
  20524    def __init__(self, descriptor):
  20525        CGBindingImplClass.__init__(
  20526            self,
  20527            descriptor,
  20528            CGExampleMethod,
  20529            CGExampleGetter,
  20530            CGExampleSetter,
  20531            wantGetParent=descriptor.wrapperCache,
  20532        )
  20533 
  20534        self.parentIface = descriptor.interface.parent
  20535        if self.parentIface:
  20536            self.parentDesc = descriptor.getDescriptor(self.parentIface.identifier.name)
  20537            bases = [ClassBase(self.nativeLeafName(self.parentDesc))]
  20538        else:
  20539            bases = [
  20540                ClassBase(
  20541                    "nsISupports /* or NonRefcountedDOMObject if this is a non-refcounted object */"
  20542                )
  20543            ]
  20544            if descriptor.wrapperCache:
  20545                bases.append(
  20546                    ClassBase(
  20547                        "nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */"
  20548                    )
  20549                )
  20550 
  20551        destructorVisibility = "protected"
  20552        if self.parentIface:
  20553            extradeclarations = (
  20554                "public:\n"
  20555                "  NS_DECL_ISUPPORTS_INHERITED\n"
  20556                "  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n"
  20557                "\n"
  20558                % (
  20559                    self.nativeLeafName(descriptor),
  20560                    self.nativeLeafName(self.parentDesc),
  20561                )
  20562            )
  20563        else:
  20564            extradeclarations = (
  20565                "public:\n"
  20566                "  NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
  20567                "  NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(%s)\n"
  20568                "\n" % self.nativeLeafName(descriptor)
  20569            )
  20570 
  20571        if descriptor.interface.hasChildInterfaces():
  20572            decorators = ""
  20573        else:
  20574            decorators = "final"
  20575 
  20576        for m in descriptor.interface.members:
  20577            if m.isAttr() and m.type.isObservableArray():
  20578                self.methodDecls.append(
  20579                    CGExampleObservableArrayCallback(descriptor, m, "Set")
  20580                )
  20581                self.methodDecls.append(
  20582                    CGExampleObservableArrayCallback(descriptor, m, "Delete")
  20583                )
  20584 
  20585        CGClass.__init__(
  20586            self,
  20587            self.nativeLeafName(descriptor),
  20588            bases=bases,
  20589            constructors=[ClassConstructor([], visibility="public")],
  20590            destructor=ClassDestructor(visibility=destructorVisibility),
  20591            methods=self.methodDecls,
  20592            decorators=decorators,
  20593            extradeclarations=extradeclarations,
  20594        )
  20595 
  20596    def define(self):
  20597        # Just override CGClass and do our own thing
  20598        nativeType = self.nativeLeafName(self.descriptor)
  20599 
  20600        ctordtor = fill(
  20601            """
  20602            ${nativeType}::${nativeType}()
  20603            {
  20604                // Add |MOZ_COUNT_CTOR(${nativeType});| for a non-refcounted object.
  20605            }
  20606 
  20607            ${nativeType}::~${nativeType}()
  20608            {
  20609                // Add |MOZ_COUNT_DTOR(${nativeType});| for a non-refcounted object.
  20610            }
  20611            """,
  20612            nativeType=nativeType,
  20613        )
  20614 
  20615        if self.parentIface:
  20616            ccImpl = fill(
  20617                """
  20618 
  20619                // Only needed for refcounted objects.
  20620                # error "If you don't have members that need cycle collection,
  20621                # then remove all the cycle collection bits from this
  20622                # implementation and the corresponding header.  If you do, you
  20623                # want NS_IMPL_CYCLE_COLLECTION_INHERITED(${nativeType},
  20624                # ${parentType}, your, members, here)"
  20625                NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
  20626                NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
  20627                NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
  20628                NS_INTERFACE_MAP_END_INHERITING(${parentType})
  20629 
  20630                """,
  20631                nativeType=nativeType,
  20632                parentType=self.nativeLeafName(self.parentDesc),
  20633            )
  20634        else:
  20635            ccImpl = fill(
  20636                """
  20637 
  20638                // Only needed for refcounted objects.
  20639                NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType})
  20640                NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType})
  20641                NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType})
  20642                NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
  20643                  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  20644                  NS_INTERFACE_MAP_ENTRY(nsISupports)
  20645                NS_INTERFACE_MAP_END
  20646 
  20647                """,
  20648                nativeType=nativeType,
  20649            )
  20650 
  20651        classImpl = ccImpl + ctordtor + "\n"
  20652        if self.descriptor.concrete:
  20653            if self.descriptor.wrapperCache:
  20654                reflectorArg = ""
  20655                reflectorPassArg = ""
  20656                returnType = "JSObject*"
  20657            else:
  20658                reflectorArg = ", JS::MutableHandle<JSObject*> aReflector"
  20659                reflectorPassArg = ", aReflector"
  20660                returnType = "bool"
  20661            classImpl += fill(
  20662                """
  20663                ${returnType}
  20664                ${nativeType}::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto${reflectorArg})
  20665                {
  20666                  return ${ifaceName}_Binding::Wrap(aCx, this, aGivenProto${reflectorPassArg});
  20667                }
  20668 
  20669                """,
  20670                returnType=returnType,
  20671                nativeType=nativeType,
  20672                reflectorArg=reflectorArg,
  20673                ifaceName=self.descriptor.name,
  20674                reflectorPassArg=reflectorPassArg,
  20675            )
  20676 
  20677        return classImpl
  20678 
  20679    @staticmethod
  20680    def nativeLeafName(descriptor):
  20681        return descriptor.nativeType.split("::")[-1]
  20682 
  20683 
  20684 class CGExampleRoot(CGThing):
  20685    """
  20686    Root codegen class for example implementation generation.  Instantiate the
  20687    class and call declare or define to generate header or cpp code,
  20688    respectively.
  20689    """
  20690 
  20691    def __init__(self, config, interfaceName):
  20692        descriptor = config.getDescriptor(interfaceName)
  20693 
  20694        self.root = CGWrapper(CGExampleClass(descriptor), pre="\n", post="\n")
  20695 
  20696        self.root = CGNamespace.build(["mozilla", "dom"], self.root)
  20697 
  20698        builder = ForwardDeclarationBuilder()
  20699        if descriptor.hasCEReactions():
  20700            builder.addInMozillaDom("DocGroup")
  20701        for member in descriptor.interface.members:
  20702            if not member.isAttr() and not member.isMethod():
  20703                continue
  20704            if member.isStatic():
  20705                builder.addInMozillaDom("GlobalObject")
  20706            if member.isAttr():
  20707                if not member.isMaplikeOrSetlikeAttr():
  20708                    builder.forwardDeclareForType(member.type, config)
  20709            else:
  20710                assert member.isMethod()
  20711                if not member.isMaplikeOrSetlikeOrIterableMethod():
  20712                    for sig in member.signatures():
  20713                        builder.forwardDeclareForType(sig[0], config)
  20714                        for arg in sig[1]:
  20715                            builder.forwardDeclareForType(arg.type, config)
  20716 
  20717        self.root = CGList([builder.build(), self.root], "\n")
  20718 
  20719        # Throw in our #includes
  20720        self.root = CGHeaders(
  20721            [],
  20722            [],
  20723            [],
  20724            [],
  20725            [
  20726                "nsWrapperCache.h",
  20727                "nsCycleCollectionParticipant.h",
  20728                "mozilla/Attributes.h",
  20729                "mozilla/ErrorResult.h",
  20730                "mozilla/dom/BindingDeclarations.h",
  20731                "js/TypeDecls.h",
  20732            ],
  20733            [
  20734                "mozilla/dom/%s.h" % interfaceName,
  20735                (
  20736                    "mozilla/dom/%s"
  20737                    % CGHeaders.getDeclarationFilename(descriptor.interface)
  20738                ),
  20739            ],
  20740            "",
  20741            self.root,
  20742        )
  20743 
  20744        # And now some include guards
  20745        self.root = CGIncludeGuard(interfaceName, self.root)
  20746 
  20747        # And our license block comes before everything else
  20748        self.root = CGWrapper(
  20749            self.root,
  20750            pre=dedent(
  20751                """
  20752            /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  20753            /* vim:set ts=2 sw=2 sts=2 et cindent: */
  20754            /* This Source Code Form is subject to the terms of the Mozilla Public
  20755             * License, v. 2.0. If a copy of the MPL was not distributed with this
  20756             * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  20757 
  20758            """
  20759            ),
  20760        )
  20761 
  20762    def declare(self):
  20763        return self.root.declare()
  20764 
  20765    def define(self):
  20766        return self.root.define()
  20767 
  20768 
  20769 def jsImplName(name):
  20770    return name + "JSImpl"
  20771 
  20772 
  20773 class CGJSImplMember(CGNativeMember):
  20774    """
  20775    Base class for generating code for the members of the implementation class
  20776    for a JS-implemented WebIDL interface.
  20777    """
  20778 
  20779    def __init__(
  20780        self,
  20781        descriptorProvider,
  20782        member,
  20783        name,
  20784        signature,
  20785        extendedAttrs,
  20786        breakAfter=True,
  20787        passJSBitsAsNeeded=True,
  20788        visibility="public",
  20789        variadicIsSequence=False,
  20790        virtual=False,
  20791        override=False,
  20792    ):
  20793        CGNativeMember.__init__(
  20794            self,
  20795            descriptorProvider,
  20796            member,
  20797            name,
  20798            signature,
  20799            extendedAttrs,
  20800            breakAfter=breakAfter,
  20801            passJSBitsAsNeeded=passJSBitsAsNeeded,
  20802            visibility=visibility,
  20803            variadicIsSequence=variadicIsSequence,
  20804            virtual=virtual,
  20805            override=override,
  20806        )
  20807        self.body = self.getImpl()
  20808 
  20809    def getArgs(self, returnType, argList):
  20810        args = CGNativeMember.getArgs(self, returnType, argList)
  20811        args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
  20812        return args
  20813 
  20814 
  20815 class CGJSImplMethod(CGJSImplMember):
  20816    """
  20817    Class for generating code for the methods for a JS-implemented WebIDL
  20818    interface.
  20819    """
  20820 
  20821    def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
  20822        self.signature = signature
  20823        self.descriptor = descriptor
  20824        self.isConstructor = isConstructor
  20825        CGJSImplMember.__init__(
  20826            self,
  20827            descriptor,
  20828            method,
  20829            CGSpecializedMethod.makeNativeName(descriptor, method),
  20830            signature,
  20831            descriptor.getExtendedAttributes(method),
  20832            breakAfter=breakAfter,
  20833            variadicIsSequence=True,
  20834            passJSBitsAsNeeded=False,
  20835        )
  20836 
  20837    def getArgs(self, returnType, argList):
  20838        if self.isConstructor:
  20839            # Skip the JS::Compartment bits for constructors; it's handled
  20840            # manually in getImpl.  But we do need our aGivenProto argument.  We
  20841            # allow it to be omitted if the default proto is desired.
  20842            return CGNativeMember.getArgs(self, returnType, argList) + [
  20843                Argument("JS::Handle<JSObject*>", "aGivenProto", "nullptr")
  20844            ]
  20845        return CGJSImplMember.getArgs(self, returnType, argList)
  20846 
  20847    def getImpl(self):
  20848        args = self.getArgs(self.signature[0], self.signature[1])
  20849        if not self.isConstructor:
  20850            return "return mImpl->%s(%s);\n" % (
  20851                self.name,
  20852                ", ".join(arg.name for arg in args),
  20853            )
  20854 
  20855        assert self.descriptor.interface.isJSImplemented()
  20856        if self.name != "Constructor":
  20857            raise TypeError(
  20858                "Legacy factory functions are not supported for JS implemented WebIDL."
  20859            )
  20860        if len(self.signature[1]) != 0:
  20861            # The first two arguments to the constructor implementation are not
  20862            # arguments to the WebIDL constructor, so don't pass them to
  20863            # __Init().  The last argument is the prototype we're supposed to
  20864            # use, and shouldn't get passed to __Init() either.
  20865            assert args[0].argType == "const GlobalObject&"
  20866            assert args[1].argType == "JSContext*"
  20867            assert args[-1].argType == "JS::Handle<JSObject*>"
  20868            assert args[-1].name == "aGivenProto"
  20869            constructorArgs = [arg.name for arg in args[2:-1]]
  20870            constructorArgs.append("js::GetNonCCWObjectRealm(scopeObj)")
  20871            initCall = fill(
  20872                """
  20873                // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
  20874                JS::Rooted<JSObject*> scopeObj(cx, global.Get());
  20875                MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
  20876                JS::Rooted<JS::Value> wrappedVal(cx);
  20877                if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal, aGivenProto)) {
  20878                  MOZ_ASSERT(JS_IsExceptionPending(cx));
  20879                  aRv.Throw(NS_ERROR_UNEXPECTED);
  20880                  return nullptr;
  20881                }
  20882                // Initialize the object with the constructor arguments.
  20883                impl->mImpl->__Init(${args});
  20884                if (aRv.Failed()) {
  20885                  return nullptr;
  20886                }
  20887                """,
  20888                args=", ".join(constructorArgs),
  20889            )
  20890        else:
  20891            initCall = ""
  20892        return fill(
  20893            """
  20894            RefPtr<${implClass}> impl =
  20895              ConstructJSImplementation<${implClass}>("${contractId}", global, aRv);
  20896            if (aRv.Failed()) {
  20897              return nullptr;
  20898            }
  20899            $*{initCall}
  20900            return impl.forget();
  20901            """,
  20902            contractId=self.descriptor.interface.getJSImplementation(),
  20903            implClass=self.descriptor.name,
  20904            initCall=initCall,
  20905        )
  20906 
  20907 
  20908 # We're always fallible
  20909 def callbackGetterName(attr, descriptor):
  20910    return "Get" + MakeNativeName(
  20911        descriptor.binaryNameFor(attr.identifier.name, attr.isStatic())
  20912    )
  20913 
  20914 
  20915 def callbackSetterName(attr, descriptor):
  20916    return "Set" + MakeNativeName(
  20917        descriptor.binaryNameFor(attr.identifier.name, attr.isStatic())
  20918    )
  20919 
  20920 
  20921 class CGJSImplGetter(CGJSImplMember):
  20922    """
  20923    Class for generating code for the getters of attributes for a JS-implemented
  20924    WebIDL interface.
  20925    """
  20926 
  20927    def __init__(self, descriptor, attr):
  20928        CGJSImplMember.__init__(
  20929            self,
  20930            descriptor,
  20931            attr,
  20932            CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
  20933            (attr.type, []),
  20934            descriptor.getExtendedAttributes(attr, getter=True),
  20935            passJSBitsAsNeeded=False,
  20936        )
  20937 
  20938    def getImpl(self):
  20939        callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])]
  20940        return "return mImpl->%s(%s);\n" % (
  20941            callbackGetterName(self.member, self.descriptorProvider),
  20942            ", ".join(callbackArgs),
  20943        )
  20944 
  20945 
  20946 class CGJSImplSetter(CGJSImplMember):
  20947    """
  20948    Class for generating code for the setters of attributes for a JS-implemented
  20949    WebIDL interface.
  20950    """
  20951 
  20952    def __init__(self, descriptor, attr):
  20953        CGJSImplMember.__init__(
  20954            self,
  20955            descriptor,
  20956            attr,
  20957            CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
  20958            (
  20959                BuiltinTypes[IDLBuiltinType.Types.undefined],
  20960                [FakeArgument(attr.type)],
  20961            ),
  20962            descriptor.getExtendedAttributes(attr, setter=True),
  20963            passJSBitsAsNeeded=False,
  20964        )
  20965 
  20966    def getImpl(self):
  20967        callbackArgs = [
  20968            arg.name
  20969            for arg in self.getArgs(
  20970                BuiltinTypes[IDLBuiltinType.Types.undefined],
  20971                [FakeArgument(self.member.type)],
  20972            )
  20973        ]
  20974        return "mImpl->%s(%s);\n" % (
  20975            callbackSetterName(self.member, self.descriptorProvider),
  20976            ", ".join(callbackArgs),
  20977        )
  20978 
  20979 
  20980 class CGJSImplClass(CGBindingImplClass):
  20981    def __init__(self, descriptor):
  20982        CGBindingImplClass.__init__(
  20983            self,
  20984            descriptor,
  20985            CGJSImplMethod,
  20986            CGJSImplGetter,
  20987            CGJSImplSetter,
  20988            skipStaticMethods=True,
  20989        )
  20990 
  20991        if descriptor.interface.parent:
  20992            parentClass = descriptor.getDescriptor(
  20993                descriptor.interface.parent.identifier.name
  20994            ).jsImplParent
  20995            baseClasses = [ClassBase(parentClass)]
  20996            isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n"
  20997            ccDecl = "NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" % (
  20998                descriptor.name,
  20999                parentClass,
  21000            )
  21001            extradefinitions = fill(
  21002                """
  21003                NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent)
  21004                NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass})
  21005                NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass})
  21006                NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
  21007                NS_INTERFACE_MAP_END_INHERITING(${parentClass})
  21008                """,
  21009                ifaceName=self.descriptor.name,
  21010                parentClass=parentClass,
  21011            )
  21012        else:
  21013            baseClasses = [
  21014                ClassBase("nsSupportsWeakReference"),
  21015                ClassBase("nsWrapperCache"),
  21016            ]
  21017            isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
  21018            ccDecl = (
  21019                "NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(%s)\n" % descriptor.name
  21020            )
  21021            extradefinitions = fill(
  21022                """
  21023                NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(${ifaceName})
  21024                NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(${ifaceName})
  21025                  NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl)
  21026                  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
  21027                  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
  21028                  tmp->ClearWeakReferences();
  21029                NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  21030                NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName})
  21031                  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl)
  21032                  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
  21033                NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  21034                NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})
  21035                NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})
  21036                NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
  21037                  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  21038                  NS_INTERFACE_MAP_ENTRY(nsISupports)
  21039                  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  21040                NS_INTERFACE_MAP_END
  21041                """,
  21042                ifaceName=self.descriptor.name,
  21043            )
  21044 
  21045        extradeclarations = fill(
  21046            """
  21047            public:
  21048              $*{isupportsDecl}
  21049              $*{ccDecl}
  21050 
  21051            private:
  21052              RefPtr<${jsImplName}> mImpl;
  21053              nsCOMPtr<nsIGlobalObject> mParent;
  21054 
  21055            """,
  21056            isupportsDecl=isupportsDecl,
  21057            ccDecl=ccDecl,
  21058            jsImplName=jsImplName(descriptor.name),
  21059        )
  21060 
  21061        if descriptor.interface.getExtendedAttribute("WantsEventListenerHooks"):
  21062            # No need to do too much sanity checking here; the
  21063            # generated code will fail to compile if the methods we
  21064            # try to overrid aren't on a superclass.
  21065            self.methodDecls.extend(
  21066                self.getEventHookMethod(parentClass, "EventListenerAdded")
  21067            )
  21068            self.methodDecls.extend(
  21069                self.getEventHookMethod(parentClass, "EventListenerRemoved")
  21070            )
  21071 
  21072        if descriptor.interface.hasChildInterfaces():
  21073            decorators = ""
  21074            # We need a protected virtual destructor our subclasses can use
  21075            destructor = ClassDestructor(virtual=True, visibility="protected")
  21076        else:
  21077            decorators = "final"
  21078            destructor = ClassDestructor(virtual=False, visibility="private")
  21079 
  21080        baseConstructors = [
  21081            (
  21082                "mImpl(new %s(nullptr, aJSImplObject, aJSImplGlobal, /* aIncumbentGlobal = */ nullptr))"
  21083                % jsImplName(descriptor.name)
  21084            ),
  21085            "mParent(aParent)",
  21086        ]
  21087        parentInterface = descriptor.interface.parent
  21088        while parentInterface:
  21089            if parentInterface.isJSImplemented():
  21090                baseConstructors.insert(
  21091                    0, "%s(aJSImplObject, aJSImplGlobal, aParent)" % parentClass
  21092                )
  21093                break
  21094            parentInterface = parentInterface.parent
  21095        if not parentInterface and descriptor.interface.parent:
  21096            # We only have C++ ancestors, so only pass along the window
  21097            baseConstructors.insert(0, "%s(aParent)" % parentClass)
  21098 
  21099        constructor = ClassConstructor(
  21100            [
  21101                Argument("JS::Handle<JSObject*>", "aJSImplObject"),
  21102                Argument("JS::Handle<JSObject*>", "aJSImplGlobal"),
  21103                Argument("nsIGlobalObject*", "aParent"),
  21104            ],
  21105            visibility="public",
  21106            baseConstructors=baseConstructors,
  21107        )
  21108 
  21109        self.methodDecls.append(
  21110            ClassMethod(
  21111                "_Create",
  21112                "bool",
  21113                JSNativeArguments(),
  21114                static=True,
  21115                body=self.getCreateFromExistingBody(),
  21116            )
  21117        )
  21118 
  21119        CGClass.__init__(
  21120            self,
  21121            descriptor.name,
  21122            bases=baseClasses,
  21123            constructors=[constructor],
  21124            destructor=destructor,
  21125            methods=self.methodDecls,
  21126            decorators=decorators,
  21127            extradeclarations=extradeclarations,
  21128            extradefinitions=extradefinitions,
  21129        )
  21130 
  21131    def getWrapObjectBody(self):
  21132        return fill(
  21133            """
  21134            JS::Rooted<JSObject*> obj(aCx, ${name}_Binding::Wrap(aCx, this, aGivenProto));
  21135            if (!obj) {
  21136              return nullptr;
  21137            }
  21138 
  21139            // Now define it on our chrome object
  21140            JSAutoRealm ar(aCx, mImpl->CallbackGlobalOrNull());
  21141            if (!JS_WrapObject(aCx, &obj)) {
  21142              return nullptr;
  21143            }
  21144            JS::Rooted<JSObject*> callback(aCx, mImpl->CallbackOrNull());
  21145            if (!JS_DefineProperty(aCx, callback, "__DOM_IMPL__", obj, 0)) {
  21146              return nullptr;
  21147            }
  21148            return obj;
  21149            """,
  21150            name=self.descriptor.name,
  21151        )
  21152 
  21153    def getGetParentObjectReturnType(self):
  21154        return "nsISupports*"
  21155 
  21156    def getGetParentObjectBody(self):
  21157        return "return mParent;\n"
  21158 
  21159    def getGetDocGroupBody(self):
  21160        return dedent(
  21161            """
  21162            nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent);
  21163            if (!window) {
  21164              return nullptr;
  21165            }
  21166            return window->GetDocGroup();
  21167            """
  21168        )
  21169 
  21170    def getCreateFromExistingBody(self):
  21171        # XXXbz we could try to get parts of this (e.g. the argument
  21172        # conversions) auto-generated by somehow creating an IDLMethod and
  21173        # adding it to our interface, but we'd still need to special-case the
  21174        # implementation slightly to have it not try to forward to the JS
  21175        # object...
  21176        return fill(
  21177            """
  21178            JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  21179            if (!args.requireAtLeast(cx, "${ifaceName}._create", 2)) {
  21180              return false;
  21181            }
  21182            BindingCallContext callCx(cx, "${ifaceName}._create");
  21183            if (!args[0].isObject()) {
  21184              return callCx.ThrowErrorMessage<MSG_NOT_OBJECT>("Argument 1");
  21185            }
  21186            if (!args[1].isObject()) {
  21187              return callCx.ThrowErrorMessage<MSG_NOT_OBJECT>("Argument 2");
  21188            }
  21189 
  21190            // GlobalObject will go through wrappers as needed for us, and
  21191            // is simpler than the right UnwrapArg incantation.
  21192            GlobalObject global(cx, &args[0].toObject());
  21193            if (global.Failed()) {
  21194              return false;
  21195            }
  21196            nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(global.GetAsSupports());
  21197            MOZ_ASSERT(globalHolder);
  21198            JS::Rooted<JSObject*> arg(cx, &args[1].toObject());
  21199            JS::Rooted<JSObject*> argGlobal(cx, JS::CurrentGlobalOrNull(cx));
  21200            RefPtr<${implName}> impl = new ${implName}(arg, argGlobal, globalHolder);
  21201            MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx));
  21202            return GetOrCreateDOMReflector(cx, impl, args.rval());
  21203            """,
  21204            ifaceName=self.descriptor.interface.identifier.name,
  21205            implName=self.descriptor.name,
  21206        )
  21207 
  21208    def getEventHookMethod(self, parentClass, methodName):
  21209        body = fill(
  21210            """
  21211            ${parentClass}::${methodName}(aType);
  21212            mImpl->${methodName}(Substring(nsDependentAtomString(aType), 2), IgnoreErrors());
  21213            """,
  21214            parentClass=parentClass,
  21215            methodName=methodName,
  21216        )
  21217        return [
  21218            ClassMethod(
  21219                methodName,
  21220                "void",
  21221                [Argument("nsAtom*", "aType")],
  21222                virtual=True,
  21223                override=True,
  21224                body=body,
  21225            ),
  21226            ClassUsingFromBaseDeclaration(parentClass, methodName),
  21227        ]
  21228 
  21229 
  21230 def isJSImplementedDescriptor(descriptorProvider):
  21231    return (
  21232        isinstance(descriptorProvider, Descriptor)
  21233        and descriptorProvider.interface.isJSImplemented()
  21234    )
  21235 
  21236 
  21237 class CGCallback(CGClass):
  21238    def __init__(
  21239        self, idlObject, descriptorProvider, baseName, methods, getters=[], setters=[]
  21240    ):
  21241        self.baseName = baseName
  21242        self._deps = idlObject.getDeps()
  21243        self.idlObject = idlObject
  21244        self.name = idlObject.identifier.name
  21245        if isJSImplementedDescriptor(descriptorProvider):
  21246            self.name = jsImplName(self.name)
  21247        # For our public methods that needThisHandling we want most of the
  21248        # same args and the same return type as what CallbackMember
  21249        # generates.  So we want to take advantage of all its
  21250        # CGNativeMember infrastructure, but that infrastructure can't deal
  21251        # with templates and most especially template arguments.  So just
  21252        # cheat and have CallbackMember compute all those things for us.
  21253        realMethods = []
  21254        for method in methods:
  21255            if not isinstance(method, CallbackMember) or not method.needThisHandling:
  21256                realMethods.append(method)
  21257            else:
  21258                realMethods.extend(self.getMethodImpls(method))
  21259        realMethods.append(
  21260            ClassMethod(
  21261                "operator==",
  21262                "bool",
  21263                [Argument("const %s&" % self.name, "aOther")],
  21264                inline=True,
  21265                bodyInHeader=True,
  21266                const=True,
  21267                body=("return %s::operator==(aOther);\n" % baseName),
  21268            )
  21269        )
  21270        CGClass.__init__(
  21271            self,
  21272            self.name,
  21273            bases=[ClassBase(baseName)],
  21274            constructors=self.getConstructors(),
  21275            methods=realMethods + getters + setters,
  21276        )
  21277 
  21278    def getConstructors(self):
  21279        if (
  21280            not self.idlObject.isInterface()
  21281            and not self.idlObject._treatNonObjectAsNull
  21282        ):
  21283            body = "MOZ_ASSERT(JS::IsCallable(mCallback));\n"
  21284        else:
  21285            # Not much we can assert about it, other than not being null, and
  21286            # CallbackObject does that already.
  21287            body = ""
  21288        return [
  21289            ClassConstructor(
  21290                [
  21291                    Argument("JSContext*", "aCx"),
  21292                    Argument("JS::Handle<JSObject*>", "aCallback"),
  21293                    Argument("JS::Handle<JSObject*>", "aCallbackGlobal"),
  21294                    Argument("nsIGlobalObject*", "aIncumbentGlobal"),
  21295                ],
  21296                bodyInHeader=True,
  21297                visibility="public",
  21298                explicit=True,
  21299                baseConstructors=[
  21300                    "%s(aCx, aCallback, aCallbackGlobal, aIncumbentGlobal)"
  21301                    % self.baseName,
  21302                ],
  21303                body=body,
  21304            ),
  21305            ClassConstructor(
  21306                [
  21307                    Argument("JSObject*", "aCallback"),
  21308                    Argument("JSObject*", "aCallbackGlobal"),
  21309                    Argument("const FastCallbackConstructor&", ""),
  21310                ],
  21311                bodyInHeader=True,
  21312                visibility="public",
  21313                explicit=True,
  21314                baseConstructors=[
  21315                    "%s(aCallback, aCallbackGlobal, FastCallbackConstructor())"
  21316                    % self.baseName,
  21317                ],
  21318                body=body,
  21319            ),
  21320            ClassConstructor(
  21321                [
  21322                    Argument("JSObject*", "aCallback"),
  21323                    Argument("JSObject*", "aCallbackGlobal"),
  21324                    Argument("JSObject*", "aAsyncStack"),
  21325                    Argument("nsIGlobalObject*", "aIncumbentGlobal"),
  21326                ],
  21327                bodyInHeader=True,
  21328                visibility="public",
  21329                explicit=True,
  21330                baseConstructors=[
  21331                    "%s(aCallback, aCallbackGlobal, aAsyncStack, aIncumbentGlobal)"
  21332                    % self.baseName,
  21333                ],
  21334                body=body,
  21335            ),
  21336        ]
  21337 
  21338    def getMethodImpls(self, method):
  21339        assert method.needThisHandling
  21340        args = list(method.args)
  21341        # Strip out the BindingCallContext&/JSObject* args
  21342        # that got added.
  21343        assert args[0].name == "cx" and args[0].argType == "BindingCallContext&"
  21344        assert args[1].name == "aThisVal" and args[1].argType == "JS::Handle<JS::Value>"
  21345        args = args[2:]
  21346 
  21347        # Now remember which index the ErrorResult argument is at;
  21348        # we'll need this below.
  21349        assert args[-1].name == "aRv" and args[-1].argType == "ErrorResult&"
  21350        rvIndex = len(args) - 1
  21351        assert rvIndex >= 0
  21352 
  21353        # Record the names of all the arguments, so we can use them when we call
  21354        # the private method.
  21355        argnames = [arg.name for arg in args]
  21356        argnamesWithThis = ["s.GetCallContext()", "thisValJS"] + argnames
  21357        argnamesWithoutThis = [
  21358            "s.GetCallContext()",
  21359            "JS::UndefinedHandleValue",
  21360        ] + argnames
  21361        # Now that we've recorded the argnames for our call to our private
  21362        # method, insert our optional argument for the execution reason.
  21363        args.append(Argument("const char*", "aExecutionReason", "nullptr"))
  21364 
  21365        # Make copies of the arg list for the two "without rv" overloads.  Note
  21366        # that those don't need aExceptionHandling or aRealm arguments because
  21367        # those would make not sense anyway: the only sane thing to do with
  21368        # exceptions in the "without rv" cases is to report them.
  21369        argsWithoutRv = list(args)
  21370        argsWithoutRv.pop(rvIndex)
  21371        argsWithoutThisAndRv = list(argsWithoutRv)
  21372 
  21373        # Add the potional argument for deciding whether the CallSetup should
  21374        # re-throw exceptions on aRv.
  21375        args.append(
  21376            Argument("ExceptionHandling", "aExceptionHandling", "eReportExceptions")
  21377        )
  21378        # And the argument for communicating when exceptions should really be
  21379        # rethrown.  In particular, even when aExceptionHandling is
  21380        # eRethrowExceptions they won't get rethrown if aRealm is provided
  21381        # and its principal doesn't subsume either the callback or the
  21382        # exception.
  21383        args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
  21384        # And now insert our template argument.
  21385        argsWithoutThis = list(args)
  21386        args.insert(0, Argument("const T&", "thisVal"))
  21387        argsWithoutRv.insert(0, Argument("const T&", "thisVal"))
  21388 
  21389        argnamesWithoutThisAndRv = [arg.name for arg in argsWithoutThisAndRv]
  21390        argnamesWithoutThisAndRv.insert(rvIndex, "IgnoreErrors()")
  21391        # If we just leave things like that, and have no actual arguments in the
  21392        # IDL, we will end up trying to call the templated "without rv" overload
  21393        # with "rv" as the thisVal.  That's no good.  So explicitly append the
  21394        # aExceptionHandling and aRealm values we need to end up matching the
  21395        # signature of our non-templated "with rv" overload.
  21396        argnamesWithoutThisAndRv.extend(["eReportExceptions", "nullptr"])
  21397 
  21398        argnamesWithoutRv = [arg.name for arg in argsWithoutRv]
  21399        # Note that we need to insert at rvIndex + 1, since we inserted a
  21400        # thisVal arg at the start.
  21401        argnamesWithoutRv.insert(rvIndex + 1, "IgnoreErrors()")
  21402 
  21403        errorReturn = method.getDefaultRetval()
  21404 
  21405        setupCall = fill(
  21406            """
  21407            MOZ_ASSERT(!aRv.Failed(), "Don't pass an already-failed ErrorResult to a callback!");
  21408            if (!aExecutionReason) {
  21409              aExecutionReason = "${executionReason}";
  21410            }
  21411            CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aRealm);
  21412            if (!s.GetContext()) {
  21413              MOZ_ASSERT(aRv.Failed());
  21414              return${errorReturn};
  21415            }
  21416            """,
  21417            errorReturn=errorReturn,
  21418            executionReason=method.getPrettyName(),
  21419        )
  21420 
  21421        bodyWithThis = fill(
  21422            """
  21423            $*{setupCall}
  21424            JS::Rooted<JS::Value> thisValJS(s.GetContext());
  21425            if (!ToJSValue(s.GetContext(), thisVal, &thisValJS)) {
  21426              aRv.Throw(NS_ERROR_FAILURE);
  21427              return${errorReturn};
  21428            }
  21429            return ${methodName}(${callArgs});
  21430            """,
  21431            setupCall=setupCall,
  21432            errorReturn=errorReturn,
  21433            methodName=method.name,
  21434            callArgs=", ".join(argnamesWithThis),
  21435        )
  21436        bodyWithoutThis = fill(
  21437            """
  21438            $*{setupCall}
  21439            return ${methodName}(${callArgs});
  21440            """,
  21441            setupCall=setupCall,
  21442            errorReturn=errorReturn,
  21443            methodName=method.name,
  21444            callArgs=", ".join(argnamesWithoutThis),
  21445        )
  21446        bodyWithThisWithoutRv = fill(
  21447            """
  21448            return ${methodName}(${callArgs});
  21449            """,
  21450            methodName=method.name,
  21451            callArgs=", ".join(argnamesWithoutRv),
  21452        )
  21453        bodyWithoutThisAndRv = fill(
  21454            """
  21455            return ${methodName}(${callArgs});
  21456            """,
  21457            methodName=method.name,
  21458            callArgs=", ".join(argnamesWithoutThisAndRv),
  21459        )
  21460 
  21461        return [
  21462            ClassMethod(
  21463                method.name,
  21464                method.returnType,
  21465                args,
  21466                bodyInHeader=True,
  21467                templateArgs=["typename T"],
  21468                body=bodyWithThis,
  21469                canRunScript=method.canRunScript,
  21470            ),
  21471            ClassMethod(
  21472                method.name,
  21473                method.returnType,
  21474                argsWithoutThis,
  21475                bodyInHeader=True,
  21476                body=bodyWithoutThis,
  21477                canRunScript=method.canRunScript,
  21478            ),
  21479            ClassMethod(
  21480                method.name,
  21481                method.returnType,
  21482                argsWithoutRv,
  21483                bodyInHeader=True,
  21484                templateArgs=["typename T"],
  21485                body=bodyWithThisWithoutRv,
  21486                canRunScript=method.canRunScript,
  21487            ),
  21488            ClassMethod(
  21489                method.name,
  21490                method.returnType,
  21491                argsWithoutThisAndRv,
  21492                bodyInHeader=True,
  21493                body=bodyWithoutThisAndRv,
  21494                canRunScript=method.canRunScript,
  21495            ),
  21496            method,
  21497        ]
  21498 
  21499    def deps(self):
  21500        return self._deps
  21501 
  21502 
  21503 class CGCallbackFunction(CGCallback):
  21504    def __init__(self, callback, descriptorProvider):
  21505        self.callback = callback
  21506        if callback.isConstructor():
  21507            methods = [ConstructCallback(callback, descriptorProvider)]
  21508        else:
  21509            methods = [CallCallback(callback, descriptorProvider)]
  21510        CGCallback.__init__(
  21511            self, callback, descriptorProvider, "CallbackFunction", methods
  21512        )
  21513 
  21514    def getConstructors(self):
  21515        return CGCallback.getConstructors(self) + [
  21516            ClassConstructor(
  21517                [Argument("CallbackFunction*", "aOther")],
  21518                bodyInHeader=True,
  21519                visibility="public",
  21520                explicit=True,
  21521                baseConstructors=["CallbackFunction(aOther)"],
  21522            )
  21523        ]
  21524 
  21525 
  21526 class CGFastCallback(CGClass):
  21527    def __init__(self, idlObject):
  21528        self._deps = idlObject.getDeps()
  21529        baseName = idlObject.identifier.name
  21530        constructor = ClassConstructor(
  21531            [
  21532                Argument("JSObject*", "aCallback"),
  21533                Argument("JSObject*", "aCallbackGlobal"),
  21534            ],
  21535            bodyInHeader=True,
  21536            visibility="public",
  21537            explicit=True,
  21538            baseConstructors=[
  21539                "%s(aCallback, aCallbackGlobal, FastCallbackConstructor())" % baseName,
  21540            ],
  21541            body="",
  21542        )
  21543 
  21544        traceMethod = ClassMethod(
  21545            "Trace",
  21546            "void",
  21547            [Argument("JSTracer*", "aTracer")],
  21548            inline=True,
  21549            bodyInHeader=True,
  21550            visibility="public",
  21551            body="%s::Trace(aTracer);\n" % baseName,
  21552        )
  21553        holdMethod = ClassMethod(
  21554            "FinishSlowJSInitIfMoreThanOneOwner",
  21555            "void",
  21556            [Argument("JSContext*", "aCx")],
  21557            inline=True,
  21558            bodyInHeader=True,
  21559            visibility="public",
  21560            body=("%s::FinishSlowJSInitIfMoreThanOneOwner(aCx);\n" % baseName),
  21561        )
  21562 
  21563        CGClass.__init__(
  21564            self,
  21565            "Fast%s" % baseName,
  21566            bases=[ClassBase(baseName)],
  21567            constructors=[constructor],
  21568            methods=[traceMethod, holdMethod],
  21569        )
  21570 
  21571    def deps(self):
  21572        return self._deps
  21573 
  21574 
  21575 class CGCallbackInterface(CGCallback):
  21576    def __init__(self, descriptor, spiderMonkeyInterfacesAreStructs=False):
  21577        iface = descriptor.interface
  21578        attrs = [
  21579            m
  21580            for m in iface.members
  21581            if (
  21582                m.isAttr()
  21583                and not m.isStatic()
  21584                and (not m.isMaplikeOrSetlikeAttr() or not iface.isJSImplemented())
  21585            )
  21586        ]
  21587        getters = [
  21588            CallbackGetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
  21589            for a in attrs
  21590        ]
  21591        setters = [
  21592            CallbackSetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
  21593            for a in attrs
  21594            if not a.readonly
  21595        ]
  21596        methods = [
  21597            m
  21598            for m in iface.members
  21599            if (
  21600                m.isMethod()
  21601                and not m.isStatic()
  21602                and not m.isIdentifierLess()
  21603                and (
  21604                    not m.isMaplikeOrSetlikeOrIterableMethod()
  21605                    or not iface.isJSImplemented()
  21606                )
  21607            )
  21608        ]
  21609        methods = [
  21610            CallbackOperation(m, sig, descriptor, spiderMonkeyInterfacesAreStructs)
  21611            for m in methods
  21612            for sig in m.signatures()
  21613        ]
  21614 
  21615        needInitId = False
  21616        if iface.isJSImplemented() and iface.ctor():
  21617            sigs = descriptor.interface.ctor().signatures()
  21618            if len(sigs) != 1:
  21619                raise TypeError("We only handle one constructor.  See bug 869268.")
  21620            methods.append(CGJSImplInitOperation(sigs[0], descriptor))
  21621            needInitId = True
  21622 
  21623        idlist = [
  21624            descriptor.binaryNameFor(m.identifier.name, m.isStatic())
  21625            for m in iface.members
  21626            if m.isAttr() or m.isMethod()
  21627        ]
  21628        if needInitId:
  21629            idlist.append("__init")
  21630 
  21631        if iface.isJSImplemented() and iface.getExtendedAttribute(
  21632            "WantsEventListenerHooks"
  21633        ):
  21634            methods.append(CGJSImplEventHookOperation(descriptor, "eventListenerAdded"))
  21635            methods.append(
  21636                CGJSImplEventHookOperation(descriptor, "eventListenerRemoved")
  21637            )
  21638            idlist.append("eventListenerAdded")
  21639            idlist.append("eventListenerRemoved")
  21640 
  21641        if len(idlist) != 0:
  21642            methods.append(initIdsClassMethod(idlist, iface.identifier.name + "Atoms"))
  21643        CGCallback.__init__(
  21644            self,
  21645            iface,
  21646            descriptor,
  21647            "CallbackInterface",
  21648            methods,
  21649            getters=getters,
  21650            setters=setters,
  21651        )
  21652 
  21653 
  21654 class FakeMember:
  21655    def __init__(self, name=None):
  21656        if name is not None:
  21657            self.identifier = FakeIdentifier(name)
  21658 
  21659    def isStatic(self):
  21660        return False
  21661 
  21662    def isAttr(self):
  21663        return False
  21664 
  21665    def isMethod(self):
  21666        return False
  21667 
  21668    def getExtendedAttribute(self, name):
  21669        # Claim to be a [NewObject] so we can avoid the "return a raw pointer"
  21670        # comments CGNativeMember codegen would otherwise stick in.
  21671        if name == "NewObject":
  21672            return True
  21673        return None
  21674 
  21675 
  21676 class CallbackMember(CGNativeMember):
  21677    # XXXbz It's OK to use CallbackKnownNotGray for wrapScope because
  21678    # CallSetup already handled the unmark-gray bits for us. we don't have
  21679    # anything better to use for 'obj', really...
  21680    def __init__(
  21681        self,
  21682        sig,
  21683        name,
  21684        descriptorProvider,
  21685        needThisHandling,
  21686        rethrowContentException=False,
  21687        spiderMonkeyInterfacesAreStructs=False,
  21688        wrapScope=None,
  21689        canRunScript=False,
  21690        passJSBitsAsNeeded=False,
  21691    ):
  21692        """
  21693        needThisHandling is True if we need to be able to accept a specified
  21694        thisObj, False otherwise.
  21695        """
  21696        assert not rethrowContentException or not needThisHandling
  21697 
  21698        self.retvalType = sig[0]
  21699        self.originalSig = sig
  21700        args = sig[1]
  21701        self.argCount = len(args)
  21702        if self.argCount > 0:
  21703            # Check for variadic arguments
  21704            lastArg = args[self.argCount - 1]
  21705            if lastArg.variadic:
  21706                self.argCountStr = "(%d - 1) + %s.Length()" % (
  21707                    self.argCount,
  21708                    lastArg.identifier.name,
  21709                )
  21710            else:
  21711                self.argCountStr = "%d" % self.argCount
  21712        self.needThisHandling = needThisHandling
  21713        # If needThisHandling, we generate ourselves as private and the caller
  21714        # will handle generating public versions that handle the "this" stuff.
  21715        visibility = "private" if needThisHandling else "public"
  21716        self.rethrowContentException = rethrowContentException
  21717 
  21718        self.wrapScope = wrapScope
  21719        # We don't care, for callback codegen, whether our original member was
  21720        # a method or attribute or whatnot.  Just always pass FakeMember()
  21721        # here.
  21722        CGNativeMember.__init__(
  21723            self,
  21724            descriptorProvider,
  21725            FakeMember(),
  21726            name,
  21727            (self.retvalType, args),
  21728            extendedAttrs=["needsErrorResult"],
  21729            passJSBitsAsNeeded=passJSBitsAsNeeded,
  21730            visibility=visibility,
  21731            spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
  21732            canRunScript=canRunScript,
  21733        )
  21734        # We have to do all the generation of our body now, because
  21735        # the caller relies on us throwing if we can't manage it.
  21736        self.body = self.getImpl()
  21737 
  21738    def getImpl(self):
  21739        setupCall = self.getCallSetup()
  21740        declRval = self.getRvalDecl()
  21741        if self.argCount > 0:
  21742            argvDecl = fill(
  21743                """
  21744                JS::RootedVector<JS::Value> argv(cx);
  21745                if (!argv.resize(${argCount})) {
  21746                  $*{failureCode}
  21747                  return${errorReturn};
  21748                }
  21749                """,
  21750                argCount=self.argCountStr,
  21751                failureCode=self.getArgvDeclFailureCode(),
  21752                errorReturn=self.getDefaultRetval(),
  21753            )
  21754        else:
  21755            # Avoid weird 0-sized arrays
  21756            argvDecl = ""
  21757        convertArgs = self.getArgConversions()
  21758        doCall = self.getCall()
  21759        returnResult = self.getResultConversion()
  21760 
  21761        body = declRval + argvDecl + convertArgs + doCall
  21762        if self.needsScopeBody():
  21763            body = "{\n" + indent(body) + "}\n"
  21764        return setupCall + body + returnResult
  21765 
  21766    def needsScopeBody(self):
  21767        return False
  21768 
  21769    def getArgvDeclFailureCode(self):
  21770        return dedent(
  21771            """
  21772            // That threw an exception on the JSContext, and our CallSetup will do
  21773            // the right thing with that.
  21774            """
  21775        )
  21776 
  21777    def getExceptionCode(self, forResult):
  21778        return fill(
  21779            """
  21780            aRv.Throw(NS_ERROR_UNEXPECTED);
  21781            return${defaultRetval};
  21782            """,
  21783            defaultRetval=self.getDefaultRetval(),
  21784        )
  21785 
  21786    def getResultConversion(
  21787        self, val="rval", failureCode=None, isDefinitelyObject=False, exceptionCode=None
  21788    ):
  21789        replacements = {
  21790            "val": val,
  21791            "holderName": "rvalHolder",
  21792            "declName": "rvalDecl",
  21793            # We actually want to pass in a null scope object here, because
  21794            # wrapping things into our current compartment (that of mCallback)
  21795            # is what we want.
  21796            "obj": "nullptr",
  21797            "passedToJSImpl": "false",
  21798        }
  21799 
  21800        if isJSImplementedDescriptor(self.descriptorProvider):
  21801            isCallbackReturnValue = "JSImpl"
  21802        else:
  21803            isCallbackReturnValue = "Callback"
  21804        sourceDescription = "return value of %s" % self.getPrettyName()
  21805        convertType = instantiateJSToNativeConversion(
  21806            getJSToNativeConversionInfo(
  21807                self.retvalType,
  21808                self.descriptorProvider,
  21809                failureCode=failureCode,
  21810                isDefinitelyObject=isDefinitelyObject,
  21811                exceptionCode=exceptionCode or self.getExceptionCode(forResult=True),
  21812                isCallbackReturnValue=isCallbackReturnValue,
  21813                # Allow returning a callback type that
  21814                # allows non-callable objects.
  21815                allowTreatNonCallableAsNull=True,
  21816                sourceDescription=sourceDescription,
  21817            ),
  21818            replacements,
  21819        )
  21820        assignRetval = string.Template(
  21821            self.getRetvalInfo(self.retvalType, False)[2]
  21822        ).substitute(replacements)
  21823        type = convertType.define()
  21824        return type + assignRetval
  21825 
  21826    def getArgConversions(self):
  21827        # Just reget the arglist from self.originalSig, because our superclasses
  21828        # just have way to many members they like to clobber, so I can't find a
  21829        # safe member name to store it in.
  21830        argConversions = [
  21831            self.getArgConversion(i, arg) for i, arg in enumerate(self.originalSig[1])
  21832        ]
  21833        if not argConversions:
  21834            return "\n"
  21835 
  21836        # Do them back to front, so our argc modifications will work
  21837        # correctly, because we examine trailing arguments first.
  21838        argConversions.reverse()
  21839        # Wrap each one in a scope so that any locals it has don't leak out, and
  21840        # also so that we can just "break;" for our successCode.
  21841        argConversions = [
  21842            CGWrapper(CGIndenter(CGGeneric(c)), pre="do {\n", post="} while (false);\n")
  21843            for c in argConversions
  21844        ]
  21845        if self.argCount > 0:
  21846            argConversions.insert(0, self.getArgcDecl())
  21847        # And slap them together.
  21848        return CGList(argConversions, "\n").define() + "\n"
  21849 
  21850    def getArgConversion(self, i, arg):
  21851        argval = arg.identifier.name
  21852 
  21853        if arg.variadic:
  21854            argval = argval + "[idx]"
  21855            jsvalIndex = "%d + idx" % i
  21856        else:
  21857            jsvalIndex = "%d" % i
  21858            if arg.canHaveMissingValue():
  21859                argval += ".Value()"
  21860 
  21861        prepend = ""
  21862 
  21863        wrapScope = self.wrapScope
  21864        if arg.type.isUnion() and wrapScope is None:
  21865            prepend += (
  21866                "JS::Rooted<JSObject*> callbackObj(cx, CallbackKnownNotGray());\n"
  21867            )
  21868            wrapScope = "callbackObj"
  21869 
  21870        conversion = prepend + wrapForType(
  21871            arg.type,
  21872            self.descriptorProvider,
  21873            {
  21874                "result": argval,
  21875                "successCode": "continue;\n" if arg.variadic else "break;\n",
  21876                "jsvalRef": "argv[%s]" % jsvalIndex,
  21877                "jsvalHandle": "argv[%s]" % jsvalIndex,
  21878                "obj": wrapScope,
  21879                "returnsNewObject": False,
  21880                "exceptionCode": self.getExceptionCode(forResult=False),
  21881                "spiderMonkeyInterfacesAreStructs": self.spiderMonkeyInterfacesAreStructs,
  21882            },
  21883        )
  21884 
  21885        if arg.variadic:
  21886            conversion = fill(
  21887                """
  21888                for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {
  21889                  $*{conversion}
  21890                }
  21891                break;
  21892                """,
  21893                arg=arg.identifier.name,
  21894                conversion=conversion,
  21895            )
  21896        elif arg.canHaveMissingValue():
  21897            conversion = fill(
  21898                """
  21899                if (${argName}.WasPassed()) {
  21900                  $*{conversion}
  21901                } else if (argc == ${iPlus1}) {
  21902                  // This is our current trailing argument; reduce argc
  21903                  --argc;
  21904                } else {
  21905                  argv[${i}].setUndefined();
  21906                }
  21907                """,
  21908                argName=arg.identifier.name,
  21909                conversion=conversion,
  21910                iPlus1=i + 1,
  21911                i=i,
  21912            )
  21913        return conversion
  21914 
  21915    def getDefaultRetval(self):
  21916        default = self.getRetvalInfo(self.retvalType, False)[1]
  21917        if len(default) != 0:
  21918            default = " " + default
  21919        return default
  21920 
  21921    def getArgs(self, returnType, argList):
  21922        args = CGNativeMember.getArgs(self, returnType, argList)
  21923        if not self.needThisHandling:
  21924            # Since we don't need this handling, we're the actual method that
  21925            # will be called, so we need an aRethrowExceptions argument.
  21926            if not self.rethrowContentException:
  21927                args.append(Argument("const char*", "aExecutionReason", "nullptr"))
  21928                args.append(
  21929                    Argument(
  21930                        "ExceptionHandling", "aExceptionHandling", "eReportExceptions"
  21931                    )
  21932                )
  21933            args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
  21934            return args
  21935        # We want to allow the caller to pass in a "this" value, as
  21936        # well as a BindingCallContext.
  21937        return [
  21938            Argument("BindingCallContext&", "cx"),
  21939            Argument("JS::Handle<JS::Value>", "aThisVal"),
  21940        ] + args
  21941 
  21942    def getCallSetup(self):
  21943        if self.needThisHandling:
  21944            # It's been done for us already
  21945            return ""
  21946        callSetup = "CallSetup s(this, aRv"
  21947        if self.rethrowContentException:
  21948            # getArgs doesn't add the aExceptionHandling argument but does add
  21949            # aRealm for us.
  21950            callSetup += (
  21951                ', "%s", eRethrowContentExceptions, aRealm, /* aIsJSImplementedWebIDL = */ '
  21952                % self.getPrettyName()
  21953            )
  21954            callSetup += toStringBool(
  21955                isJSImplementedDescriptor(self.descriptorProvider)
  21956            )
  21957        else:
  21958            callSetup += ', "%s", aExceptionHandling, aRealm' % self.getPrettyName()
  21959        callSetup += ");\n"
  21960        return fill(
  21961            """
  21962            $*{callSetup}
  21963            if (aRv.Failed()) {
  21964              return${errorReturn};
  21965            }
  21966            MOZ_ASSERT(s.GetContext());
  21967            BindingCallContext& cx = s.GetCallContext();
  21968 
  21969            """,
  21970            callSetup=callSetup,
  21971            errorReturn=self.getDefaultRetval(),
  21972        )
  21973 
  21974    def getArgcDecl(self):
  21975        return CGGeneric("unsigned argc = %s;\n" % self.argCountStr)
  21976 
  21977    @staticmethod
  21978    def ensureASCIIName(idlObject):
  21979        type = "attribute" if idlObject.isAttr() else "operation"
  21980        if re.match("[^\x20-\x7E]", idlObject.identifier.name):
  21981            raise SyntaxError(
  21982                'Callback %s name "%s" contains non-ASCII '
  21983                "characters.  We can't handle that.  %s"
  21984                % (type, idlObject.identifier.name, idlObject.location)
  21985            )
  21986        if re.match('"', idlObject.identifier.name):
  21987            raise SyntaxError(
  21988                "Callback %s name '%s' contains "
  21989                "double-quote character.  We can't handle "
  21990                "that.  %s" % (type, idlObject.identifier.name, idlObject.location)
  21991            )
  21992 
  21993 
  21994 class ConstructCallback(CallbackMember):
  21995    def __init__(self, callback, descriptorProvider):
  21996        self.callback = callback
  21997        CallbackMember.__init__(
  21998            self,
  21999            callback.signatures()[0],
  22000            "Construct",
  22001            descriptorProvider,
  22002            needThisHandling=False,
  22003            canRunScript=True,
  22004        )
  22005 
  22006    def getRvalDecl(self):
  22007        # Box constructedObj for getJSToNativeConversionInfo().
  22008        return "JS::Rooted<JS::Value> rval(cx);\n"
  22009 
  22010    def getCall(self):
  22011        if self.argCount > 0:
  22012            args = "JS::HandleValueArray::subarray(argv, 0, argc)"
  22013        else:
  22014            args = "JS::HandleValueArray::empty()"
  22015 
  22016        return fill(
  22017            """
  22018            JS::Rooted<JS::Value> constructor(cx, JS::ObjectValue(*mCallback));
  22019            JS::Rooted<JSObject*> constructedObj(cx);
  22020            if (!JS::Construct(cx, constructor,
  22021                          ${args}, &constructedObj)) {
  22022              aRv.NoteJSContextException(cx);
  22023              return${errorReturn};
  22024            }
  22025            rval.setObject(*constructedObj);
  22026            """,
  22027            args=args,
  22028            errorReturn=self.getDefaultRetval(),
  22029        )
  22030 
  22031    def getResultConversion(self):
  22032        return CallbackMember.getResultConversion(self, isDefinitelyObject=True)
  22033 
  22034    def getPrettyName(self):
  22035        return self.callback.identifier.name
  22036 
  22037 
  22038 class CallbackMethod(CallbackMember):
  22039    def __init__(
  22040        self,
  22041        sig,
  22042        name,
  22043        descriptorProvider,
  22044        needThisHandling,
  22045        rethrowContentException=False,
  22046        spiderMonkeyInterfacesAreStructs=False,
  22047        canRunScript=False,
  22048    ):
  22049        CallbackMember.__init__(
  22050            self,
  22051            sig,
  22052            name,
  22053            descriptorProvider,
  22054            needThisHandling,
  22055            rethrowContentException,
  22056            spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
  22057            canRunScript=canRunScript,
  22058        )
  22059 
  22060    def getRvalDecl(self):
  22061        return "JS::Rooted<JS::Value> rval(cx);\n"
  22062 
  22063    def getNoteCallFailed(self):
  22064        return fill(
  22065            """
  22066                aRv.NoteJSContextException(cx);
  22067                return${errorReturn};
  22068                """,
  22069            errorReturn=self.getDefaultRetval(),
  22070        )
  22071 
  22072    def getCall(self):
  22073        if self.argCount > 0:
  22074            args = "JS::HandleValueArray::subarray(argv, 0, argc)"
  22075        else:
  22076            args = "JS::HandleValueArray::empty()"
  22077 
  22078        return fill(
  22079            """
  22080            $*{declCallable}
  22081            $*{declThis}
  22082            if (${callGuard}!JS::Call(cx, ${thisVal}, callable,
  22083                          ${args}, &rval)) {
  22084              $*{noteError}
  22085            }
  22086            """,
  22087            declCallable=self.getCallableDecl(),
  22088            declThis=self.getThisDecl(),
  22089            callGuard=self.getCallGuard(),
  22090            thisVal=self.getThisVal(),
  22091            args=args,
  22092            noteError=self.getNoteCallFailed(),
  22093        )
  22094 
  22095 
  22096 class CallCallback(CallbackMethod):
  22097    def __init__(self, callback, descriptorProvider):
  22098        self.callback = callback
  22099        CallbackMethod.__init__(
  22100            self,
  22101            callback.signatures()[0],
  22102            "Call",
  22103            descriptorProvider,
  22104            needThisHandling=True,
  22105            canRunScript=not callback.isRunScriptBoundary(),
  22106        )
  22107 
  22108    def getNoteCallFailed(self):
  22109        if self.retvalType.isPromise():
  22110            return dedent(
  22111                """
  22112                // Convert exception to a rejected promise.
  22113                // See https://heycam.github.io/webidl/#call-a-user-objects-operation
  22114                // step 12 and step 15.5.
  22115                return CreateRejectedPromiseFromThrownException(cx, aRv);
  22116                """
  22117            )
  22118        return CallbackMethod.getNoteCallFailed(self)
  22119 
  22120    def getExceptionCode(self, forResult):
  22121        # If the result value is a promise, and conversion
  22122        # to the promise throws an exception we shouldn't
  22123        # try to convert that exception to a promise again.
  22124        if self.retvalType.isPromise() and not forResult:
  22125            return dedent(
  22126                """
  22127                // Convert exception to a rejected promise.
  22128                // See https://heycam.github.io/webidl/#call-a-user-objects-operation
  22129                // step 10 and step 15.5.
  22130                return CreateRejectedPromiseFromThrownException(cx, aRv);
  22131                """
  22132            )
  22133        return CallbackMethod.getExceptionCode(self, forResult)
  22134 
  22135    def getThisDecl(self):
  22136        return ""
  22137 
  22138    def getThisVal(self):
  22139        return "aThisVal"
  22140 
  22141    def getCallableDecl(self):
  22142        return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n"
  22143 
  22144    def getPrettyName(self):
  22145        return self.callback.identifier.name
  22146 
  22147    def getCallGuard(self):
  22148        if self.callback._treatNonObjectAsNull:
  22149            return "JS::IsCallable(mCallback) && "
  22150        return ""
  22151 
  22152 
  22153 class CallbackOperationBase(CallbackMethod):
  22154    """
  22155    Common class for implementing various callback operations.
  22156    """
  22157 
  22158    def __init__(
  22159        self,
  22160        signature,
  22161        jsName,
  22162        nativeName,
  22163        descriptor,
  22164        singleOperation,
  22165        rethrowContentException=False,
  22166        spiderMonkeyInterfacesAreStructs=False,
  22167    ):
  22168        self.singleOperation = singleOperation
  22169        self.methodName = descriptor.binaryNameFor(jsName, False)
  22170        CallbackMethod.__init__(
  22171            self,
  22172            signature,
  22173            nativeName,
  22174            descriptor,
  22175            singleOperation,
  22176            rethrowContentException,
  22177            spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
  22178        )
  22179 
  22180    def getThisDecl(self):
  22181        if not self.singleOperation:
  22182            return "JS::Rooted<JS::Value> thisValue(cx, JS::ObjectValue(*mCallback));\n"
  22183        # This relies on getCallableDecl declaring a boolean
  22184        # isCallable in the case when we're a single-operation
  22185        # interface.
  22186        return dedent(
  22187            """
  22188            JS::Rooted<JS::Value> thisValue(cx, isCallable ? aThisVal.get()
  22189                                                           : JS::ObjectValue(*mCallback));
  22190            """
  22191        )
  22192 
  22193    def getThisVal(self):
  22194        return "thisValue"
  22195 
  22196    def getCallableDecl(self):
  22197        getCallableFromProp = fill(
  22198            """
  22199            ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
  22200            if ((reinterpret_cast<jsid*>(atomsCache)->isVoid() &&
  22201                 !InitIds(cx, atomsCache)) ||
  22202                !GetCallableProperty(cx, atomsCache->${methodAtomName}, &callable)) {
  22203              aRv.Throw(NS_ERROR_UNEXPECTED);
  22204              return${errorReturn};
  22205            }
  22206            """,
  22207            methodAtomName=CGDictionary.makeIdName(self.methodName),
  22208            atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
  22209            errorReturn=self.getDefaultRetval(),
  22210        )
  22211        if not self.singleOperation:
  22212            return "JS::Rooted<JS::Value> callable(cx);\n" + getCallableFromProp
  22213        return fill(
  22214            """
  22215            bool isCallable = JS::IsCallable(mCallback);
  22216            JS::Rooted<JS::Value> callable(cx);
  22217            if (isCallable) {
  22218              callable = JS::ObjectValue(*mCallback);
  22219            } else {
  22220              $*{getCallableFromProp}
  22221            }
  22222            """,
  22223            getCallableFromProp=getCallableFromProp,
  22224        )
  22225 
  22226    def getCallGuard(self):
  22227        return ""
  22228 
  22229 
  22230 class CallbackOperation(CallbackOperationBase):
  22231    """
  22232    Codegen actual WebIDL operations on callback interfaces.
  22233    """
  22234 
  22235    def __init__(self, method, signature, descriptor, spiderMonkeyInterfacesAreStructs):
  22236        self.ensureASCIIName(method)
  22237        self.method = method
  22238        jsName = method.identifier.name
  22239        CallbackOperationBase.__init__(
  22240            self,
  22241            signature,
  22242            jsName,
  22243            MakeNativeName(descriptor.binaryNameFor(jsName, False)),
  22244            descriptor,
  22245            descriptor.interface.isSingleOperationInterface(),
  22246            rethrowContentException=descriptor.interface.isJSImplemented(),
  22247            spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
  22248        )
  22249 
  22250    def getPrettyName(self):
  22251        return "%s.%s" % (
  22252            self.descriptorProvider.interface.identifier.name,
  22253            self.method.identifier.name,
  22254        )
  22255 
  22256 
  22257 class CallbackAccessor(CallbackMember):
  22258    """
  22259    Shared superclass for CallbackGetter and CallbackSetter.
  22260    """
  22261 
  22262    def __init__(self, attr, sig, name, descriptor, spiderMonkeyInterfacesAreStructs):
  22263        self.ensureASCIIName(attr)
  22264        self.attrName = attr.identifier.name
  22265        CallbackMember.__init__(
  22266            self,
  22267            sig,
  22268            name,
  22269            descriptor,
  22270            needThisHandling=False,
  22271            rethrowContentException=descriptor.interface.isJSImplemented(),
  22272            spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
  22273        )
  22274 
  22275    def getPrettyName(self):
  22276        return "%s.%s" % (
  22277            self.descriptorProvider.interface.identifier.name,
  22278            self.attrName,
  22279        )
  22280 
  22281 
  22282 class CallbackGetter(CallbackAccessor):
  22283    def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
  22284        CallbackAccessor.__init__(
  22285            self,
  22286            attr,
  22287            (attr.type, []),
  22288            callbackGetterName(attr, descriptor),
  22289            descriptor,
  22290            spiderMonkeyInterfacesAreStructs,
  22291        )
  22292 
  22293    def getRvalDecl(self):
  22294        return "JS::Rooted<JS::Value> rval(cx);\n"
  22295 
  22296    def getCall(self):
  22297        return fill(
  22298            """
  22299            JS::Rooted<JSObject *> callback(cx, mCallback);
  22300            ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
  22301            if ((reinterpret_cast<jsid*>(atomsCache)->isVoid()
  22302                 && !InitIds(cx, atomsCache)) ||
  22303                !JS_GetPropertyById(cx, callback, atomsCache->${attrAtomName}, &rval)) {
  22304              aRv.Throw(NS_ERROR_UNEXPECTED);
  22305              return${errorReturn};
  22306            }
  22307            """,
  22308            atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
  22309            attrAtomName=CGDictionary.makeIdName(
  22310                self.descriptorProvider.binaryNameFor(self.attrName, False)
  22311            ),
  22312            errorReturn=self.getDefaultRetval(),
  22313        )
  22314 
  22315 
  22316 class CallbackSetter(CallbackAccessor):
  22317    def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
  22318        CallbackAccessor.__init__(
  22319            self,
  22320            attr,
  22321            (
  22322                BuiltinTypes[IDLBuiltinType.Types.undefined],
  22323                [FakeArgument(attr.type)],
  22324            ),
  22325            callbackSetterName(attr, descriptor),
  22326            descriptor,
  22327            spiderMonkeyInterfacesAreStructs,
  22328        )
  22329 
  22330    def getRvalDecl(self):
  22331        # We don't need an rval
  22332        return ""
  22333 
  22334    def getCall(self):
  22335        return fill(
  22336            """
  22337            MOZ_ASSERT(argv.length() == 1);
  22338            JS::Rooted<JSObject*> callback(cx, CallbackKnownNotGray());
  22339            ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
  22340            if ((reinterpret_cast<jsid*>(atomsCache)->isVoid() &&
  22341                 !InitIds(cx, atomsCache)) ||
  22342                !JS_SetPropertyById(cx, callback, atomsCache->${attrAtomName}, argv[0])) {
  22343              aRv.Throw(NS_ERROR_UNEXPECTED);
  22344              return${errorReturn};
  22345            }
  22346            """,
  22347            atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
  22348            attrAtomName=CGDictionary.makeIdName(
  22349                self.descriptorProvider.binaryNameFor(self.attrName, False)
  22350            ),
  22351            errorReturn=self.getDefaultRetval(),
  22352        )
  22353 
  22354    def getArgcDecl(self):
  22355        return None
  22356 
  22357 
  22358 class CGJSImplInitOperation(CallbackOperationBase):
  22359    """
  22360    Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL.
  22361    """
  22362 
  22363    def __init__(self, sig, descriptor):
  22364        assert sig in descriptor.interface.ctor().signatures()
  22365        CallbackOperationBase.__init__(
  22366            self,
  22367            (BuiltinTypes[IDLBuiltinType.Types.undefined], sig[1]),
  22368            "__init",
  22369            "__Init",
  22370            descriptor,
  22371            singleOperation=False,
  22372            rethrowContentException=True,
  22373            spiderMonkeyInterfacesAreStructs=True,
  22374        )
  22375 
  22376    def getPrettyName(self):
  22377        return "__init"
  22378 
  22379 
  22380 class CGJSImplEventHookOperation(CallbackOperationBase):
  22381    """
  22382    Codegen the hooks on a JS impl for adding/removing event listeners.
  22383    """
  22384 
  22385    def __init__(self, descriptor, name):
  22386        self.name = name
  22387 
  22388        CallbackOperationBase.__init__(
  22389            self,
  22390            (
  22391                BuiltinTypes[IDLBuiltinType.Types.undefined],
  22392                [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.domstring], "aType")],
  22393            ),
  22394            name,
  22395            MakeNativeName(name),
  22396            descriptor,
  22397            singleOperation=False,
  22398            rethrowContentException=False,
  22399            spiderMonkeyInterfacesAreStructs=True,
  22400        )
  22401 
  22402    def getPrettyName(self):
  22403        return self.name
  22404 
  22405 
  22406 def getMaplikeOrSetlikeErrorReturn(helperImpl):
  22407    """
  22408    Generate return values based on whether a maplike or setlike generated
  22409    method is an interface method (which returns bool) or a helper function
  22410    (which uses ErrorResult).
  22411    """
  22412    if helperImpl:
  22413        return dedent(
  22414            """
  22415            aRv.Throw(NS_ERROR_UNEXPECTED);
  22416            return%s;
  22417            """
  22418            % helperImpl.getDefaultRetval()
  22419        )
  22420    return "return false;\n"
  22421 
  22422 
  22423 def getMaplikeOrSetlikeBackingObject(descriptor, maplikeOrSetlike, helperImpl=None):
  22424    """
  22425    Generate code to get/create a JS backing object for a maplike/setlike
  22426    declaration from the declaration slot.
  22427    """
  22428    func_prefix = maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title()
  22429    ret = fill(
  22430        """
  22431        JS::Rooted<JSObject*> backingObj(cx);
  22432        bool created = false;
  22433        if (!Get${func_prefix}BackingObject(cx, obj, ${slot}, &backingObj, &created)) {
  22434          $*{errorReturn}
  22435        }
  22436        if (created) {
  22437          PreserveWrapper<${selfType}>(self);
  22438        }
  22439        """,
  22440        slot=memberReservedSlot(maplikeOrSetlike, descriptor),
  22441        func_prefix=func_prefix,
  22442        errorReturn=getMaplikeOrSetlikeErrorReturn(helperImpl),
  22443        selfType=descriptor.nativeType,
  22444    )
  22445    return ret
  22446 
  22447 
  22448 def getMaplikeOrSetlikeSizeGetterBody(descriptor, attr):
  22449    """
  22450    Creates the body for the size getter method of maplike/setlike interfaces.
  22451    """
  22452    # We should only have one declaration attribute currently
  22453    assert attr.identifier.name == "size"
  22454    assert attr.isMaplikeOrSetlikeAttr()
  22455    return fill(
  22456        """
  22457        $*{getBackingObj}
  22458        uint32_t result = JS::${funcPrefix}Size(cx, backingObj);
  22459        MOZ_ASSERT(!JS_IsExceptionPending(cx));
  22460        args.rval().setNumber(result);
  22461        return true;
  22462        """,
  22463        getBackingObj=getMaplikeOrSetlikeBackingObject(
  22464            descriptor, attr.maplikeOrSetlike
  22465        ),
  22466        funcPrefix=attr.maplikeOrSetlike.prefix,
  22467    )
  22468 
  22469 
  22470 class CGMaplikeOrSetlikeMethodGenerator(CGThing):
  22471    """
  22472    Creates methods for maplike/setlike interfaces. It is expected that all
  22473    methods will be have a maplike/setlike object attached. Unwrapping/wrapping
  22474    will be taken care of by the usual method generation machinery in
  22475    CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
  22476    using CGCallGenerator.
  22477    """
  22478 
  22479    def __init__(
  22480        self,
  22481        descriptor,
  22482        maplikeOrSetlike,
  22483        methodName,
  22484        needsValueTypeReturn=False,
  22485        helperImpl=None,
  22486    ):
  22487        CGThing.__init__(self)
  22488        # True if this will be the body of a C++ helper function.
  22489        self.helperImpl = helperImpl
  22490        self.descriptor = descriptor
  22491        self.maplikeOrSetlike = maplikeOrSetlike
  22492        self.cgRoot = CGList([])
  22493        impl_method_name = methodName
  22494        if impl_method_name[0] == "_":
  22495            # double underscore means this is a js-implemented chrome only rw
  22496            # function. Truncate the double underscore so calling the right
  22497            # underlying JSAPI function still works.
  22498            impl_method_name = impl_method_name[2:]
  22499        self.cgRoot.append(
  22500            CGGeneric(
  22501                getMaplikeOrSetlikeBackingObject(
  22502                    self.descriptor, self.maplikeOrSetlike, self.helperImpl
  22503                )
  22504            )
  22505        )
  22506        self.returnStmt = getMaplikeOrSetlikeErrorReturn(self.helperImpl)
  22507 
  22508        # Generates required code for the method. Method descriptions included
  22509        # in definitions below. Throw if we don't have a method to fill in what
  22510        # we're looking for.
  22511        try:
  22512            methodGenerator = getattr(self, impl_method_name)
  22513        except AttributeError:
  22514            raise TypeError(
  22515                "Missing %s method definition '%s'"
  22516                % (self.maplikeOrSetlike.maplikeOrSetlikeType, methodName)
  22517            )
  22518        # Method generator returns tuple, containing:
  22519        #
  22520        # - a list of CGThings representing setup code for preparing to call
  22521        #   the JS API function
  22522        # - a list of arguments needed for the JS API function we're calling
  22523        # - list of code CGThings needed for return value conversion.
  22524        (setupCode, arguments, setResult) = methodGenerator()
  22525 
  22526        # Create the actual method call, and then wrap it with the code to
  22527        # return the value if needed.
  22528        funcName = self.maplikeOrSetlike.prefix + MakeNativeName(impl_method_name)
  22529        # Append the list of setup code CGThings
  22530        self.cgRoot.append(CGList(setupCode))
  22531        # Create the JS API call
  22532        code = dedent(
  22533            """
  22534            if (!JS::${funcName}(${args})) {
  22535              $*{errorReturn}
  22536            }
  22537            """
  22538        )
  22539 
  22540        if needsValueTypeReturn:
  22541            assert self.helperImpl and impl_method_name == "get"
  22542            code += fill(
  22543                """
  22544                if (result.isUndefined()) {
  22545                  aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  22546                  return${retval};
  22547                }
  22548                """,
  22549                retval=self.helperImpl.getDefaultRetval(),
  22550            )
  22551 
  22552        self.cgRoot.append(
  22553            CGWrapper(
  22554                CGGeneric(
  22555                    fill(
  22556                        code,
  22557                        funcName=funcName,
  22558                        args=", ".join(["cx", "backingObj"] + arguments),
  22559                        errorReturn=self.returnStmt,
  22560                    )
  22561                )
  22562            )
  22563        )
  22564        # Append result conversion
  22565        self.cgRoot.append(CGList(setResult))
  22566 
  22567    def mergeTuples(self, a, b):
  22568        """
  22569        Expecting to take 2 tuples were all elements are lists, append the lists in
  22570        the second tuple to the lists in the first.
  22571        """
  22572        return tuple([x + y for x, y in zip(a, b)])
  22573 
  22574    def appendArgConversion(self, name):
  22575        """
  22576        Generate code to convert arguments to JS::Values, so they can be
  22577        passed into JSAPI functions.
  22578        """
  22579        return CGGeneric(
  22580            fill(
  22581                """
  22582            JS::Rooted<JS::Value> ${name}Val(cx);
  22583            if (!ToJSValue(cx, ${name}, &${name}Val)) {
  22584              $*{errorReturn}
  22585            }
  22586            """,
  22587                name=name,
  22588                errorReturn=self.returnStmt,
  22589            )
  22590        )
  22591 
  22592    def appendKeyArgConversion(self):
  22593        """
  22594        Generates the key argument for methods. Helper functions will use
  22595        a RootedVector<JS::Value>, while interface methods have separate JS::Values.
  22596        """
  22597        if self.helperImpl:
  22598            return ([], ["argv[0]"], [])
  22599        return ([self.appendArgConversion("arg0")], ["arg0Val"], [])
  22600 
  22601    def appendKeyAndValueArgConversion(self):
  22602        """
  22603        Generates arguments for methods that require a key and value. Helper
  22604        functions will use a RootedVector<JS::Value>, while interface methods have
  22605        separate JS::Values.
  22606        """
  22607        r = self.appendKeyArgConversion()
  22608        if self.helperImpl:
  22609            return self.mergeTuples(r, ([], ["argv[1]"], []))
  22610        return self.mergeTuples(
  22611            r, ([self.appendArgConversion("arg1")], ["arg1Val"], [])
  22612        )
  22613 
  22614    def appendIteratorResult(self):
  22615        """
  22616        Generate code to output JSObject* return values, needed for functions that
  22617        return iterators. Iterators cannot currently be wrapped via Xrays. If
  22618        something that would return an iterator is called via Xray, fail early.
  22619        """
  22620        # TODO: Bug 1173651 - Remove check once bug 1023984 is fixed.
  22621        code = CGGeneric(
  22622            dedent(
  22623                """
  22624            // TODO (Bug 1173651): Xrays currently cannot wrap iterators. Change
  22625            // after bug 1023984 is fixed.
  22626            if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
  22627              JS_ReportErrorASCII(cx, "Xray wrapping of iterators not supported.");
  22628              return false;
  22629            }
  22630            JS::Rooted<JSObject*> result(cx);
  22631            JS::Rooted<JS::Value> v(cx);
  22632            """
  22633            )
  22634        )
  22635        arguments = "&v"
  22636        setResult = CGGeneric(
  22637            dedent(
  22638                """
  22639            result = &v.toObject();
  22640            """
  22641            )
  22642        )
  22643        return ([code], [arguments], [setResult])
  22644 
  22645    def appendSelfResult(self):
  22646        """
  22647        Generate code to return the interface object itself.
  22648        """
  22649        code = CGGeneric(
  22650            dedent(
  22651                """
  22652            JS::Rooted<JSObject*> result(cx);
  22653            """
  22654            )
  22655        )
  22656        setResult = CGGeneric(
  22657            dedent(
  22658                """
  22659            result = obj;
  22660            """
  22661            )
  22662        )
  22663        return ([code], [], [setResult])
  22664 
  22665    def appendBoolResult(self):
  22666        if self.helperImpl:
  22667            return ([CGGeneric("bool retVal;\n")], ["&retVal"], [])
  22668        return ([CGGeneric("bool result;\n")], ["&result"], [])
  22669 
  22670    def forEach(self):
  22671        """
  22672        void forEach(callback c, any thisval);
  22673 
  22674        ForEach takes a callback, and a possible value to use as 'this'. The
  22675        callback needs to take value, key, and the interface object
  22676        implementing maplike/setlike. In order to make sure that the third arg
  22677        is our interface object instead of the map/set backing object, we
  22678        create a js function with the callback and original object in its
  22679        storage slots, then use a helper function in BindingUtils to make sure
  22680        the callback is called correctly.
  22681        """
  22682        assert not self.helperImpl
  22683        code = [
  22684            CGGeneric(
  22685                dedent(
  22686                    """
  22687            // Create a wrapper function.
  22688            JSFunction* func = js::NewFunctionWithReserved(cx, ForEachHandler, 3, 0, nullptr);
  22689            if (!func) {
  22690              return false;
  22691            }
  22692            JS::Rooted<JSObject*> funcObj(cx, JS_GetFunctionObject(func));
  22693            JS::Rooted<JS::Value> funcVal(cx, JS::ObjectValue(*funcObj));
  22694            js::SetFunctionNativeReserved(funcObj, FOREACH_CALLBACK_SLOT,
  22695                                          JS::ObjectValue(*arg0));
  22696            js::SetFunctionNativeReserved(funcObj, FOREACH_MAPLIKEORSETLIKEOBJ_SLOT,
  22697                                          JS::ObjectValue(*obj));
  22698            """
  22699                )
  22700            )
  22701        ]
  22702        arguments = ["funcVal", "arg1"]
  22703        return (code, arguments, [])
  22704 
  22705    def set(self):
  22706        """
  22707        object set(key, value);
  22708 
  22709        Maplike only function, takes key and sets value to it, returns
  22710        interface object unless being called from a C++ helper.
  22711        """
  22712        assert self.maplikeOrSetlike.isMaplike()
  22713        r = self.appendKeyAndValueArgConversion()
  22714        if self.helperImpl:
  22715            return r
  22716        return self.mergeTuples(r, self.appendSelfResult())
  22717 
  22718    def add(self):
  22719        """
  22720        object add(value);
  22721 
  22722        Setlike only function, adds value to set, returns interface object
  22723        unless being called from a C++ helper
  22724        """
  22725        assert self.maplikeOrSetlike.isSetlike()
  22726        r = self.appendKeyArgConversion()
  22727        if self.helperImpl:
  22728            return r
  22729        return self.mergeTuples(r, self.appendSelfResult())
  22730 
  22731    def get(self):
  22732        """
  22733        type? get(key);
  22734 
  22735        Retrieves a value from a backing object based on the key. Returns value
  22736        if key is in backing object, undefined otherwise.
  22737        """
  22738        assert self.maplikeOrSetlike.isMaplike()
  22739        r = self.appendKeyArgConversion()
  22740 
  22741        code = []
  22742        # We don't need to create the result variable because it'll be created elsewhere
  22743        # for JSObject Get method
  22744        if not self.helperImpl or not self.helperImpl.needsScopeBody():
  22745            code = [
  22746                CGGeneric(
  22747                    dedent(
  22748                        """
  22749                        JS::Rooted<JS::Value> result(cx);
  22750                        """
  22751                    )
  22752                )
  22753            ]
  22754 
  22755        arguments = ["&result"]
  22756        return self.mergeTuples(r, (code, arguments, []))
  22757 
  22758    def has(self):
  22759        """
  22760        bool has(key);
  22761 
  22762        Check if an entry exists in the backing object. Returns true if value
  22763        exists in backing object, false otherwise.
  22764        """
  22765        return self.mergeTuples(self.appendKeyArgConversion(), self.appendBoolResult())
  22766 
  22767    def keys(self):
  22768        """
  22769        object keys();
  22770 
  22771        Returns new object iterator with all keys from backing object.
  22772        """
  22773        return self.appendIteratorResult()
  22774 
  22775    def values(self):
  22776        """
  22777        object values();
  22778 
  22779        Returns new object iterator with all values from backing object.
  22780        """
  22781        return self.appendIteratorResult()
  22782 
  22783    def entries(self):
  22784        """
  22785        object entries();
  22786 
  22787        Returns new object iterator with all keys and values from backing
  22788        object. Keys will be null for set.
  22789        """
  22790        return self.appendIteratorResult()
  22791 
  22792    def clear(self):
  22793        """
  22794        void clear();
  22795 
  22796        Removes all entries from map/set.
  22797        """
  22798        return ([], [], [])
  22799 
  22800    def delete(self):
  22801        """
  22802        bool delete(key);
  22803 
  22804        Deletes an entry from the backing object. Returns true if value existed
  22805        in backing object, false otherwise.
  22806        """
  22807        return self.mergeTuples(self.appendKeyArgConversion(), self.appendBoolResult())
  22808 
  22809    def define(self):
  22810        return self.cgRoot.define()
  22811 
  22812 
  22813 class CGHelperFunctionGenerator(CallbackMember):
  22814    """
  22815    Generates code to allow C++ to perform operations. Gets a context from the
  22816    binding wrapper, turns arguments into JS::Values (via
  22817    CallbackMember/CGNativeMember argument conversion), then uses
  22818    getCall to generate the body for getting result, and maybe convert the
  22819    result into return type (via CallbackMember/CGNativeMember result
  22820    conversion)
  22821    """
  22822 
  22823    class HelperFunction(CGAbstractMethod):
  22824        """
  22825        Generates context retrieval code and rooted JSObject for interface for
  22826        method generator to use
  22827        """
  22828 
  22829        def __init__(self, descriptor, name, args, code, returnType):
  22830            self.code = code
  22831            CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
  22832 
  22833        def definition_body(self):
  22834            return self.code
  22835 
  22836    def __init__(
  22837        self,
  22838        descriptor,
  22839        name,
  22840        args,
  22841        returnType=BuiltinTypes[IDLBuiltinType.Types.undefined],
  22842        needsResultConversion=True,
  22843    ):
  22844        assert returnType.isType()
  22845        self.needsResultConversion = needsResultConversion
  22846 
  22847        # Run CallbackMember init function to generate argument conversion code.
  22848        # wrapScope is set to 'obj' when generating maplike or setlike helper
  22849        # functions, as we don't have access to the CallbackPreserveColor
  22850        # method.
  22851        CallbackMember.__init__(
  22852            self,
  22853            [returnType, args],
  22854            name,
  22855            descriptor,
  22856            False,
  22857            wrapScope="obj",
  22858            passJSBitsAsNeeded=typeNeedsCx(returnType),
  22859        )
  22860 
  22861        # Wrap CallbackMember body code into a CGAbstractMethod to make
  22862        # generation easier.
  22863        self.implMethod = CGHelperFunctionGenerator.HelperFunction(
  22864            descriptor, name, self.args, self.body, self.returnType
  22865        )
  22866 
  22867    def getCallSetup(self):
  22868        # If passJSBitsAsNeeded is true, it means the caller will provide a
  22869        # JSContext, so we don't need to create JSContext and enter
  22870        # UnprivilegedJunkScopeOrWorkerGlobal here.
  22871        code = "MOZ_ASSERT(self);\n"
  22872        if not self.passJSBitsAsNeeded:
  22873            code += dedent(
  22874                """
  22875                AutoJSAPI jsapi;
  22876                jsapi.Init();
  22877                JSContext* cx = jsapi.cx();
  22878                // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here because
  22879                // all we want is to wrap into _some_ scope and then unwrap to find
  22880                // the reflector, and wrapping has no side-effects.
  22881                JSObject* scope = UnprivilegedJunkScopeOrWorkerGlobal(fallible);
  22882                if (!scope) {
  22883                  aRv.Throw(NS_ERROR_UNEXPECTED);
  22884                  return%s;
  22885                }
  22886                JSAutoRealm tempRealm(cx, scope);
  22887                """
  22888                % self.getDefaultRetval()
  22889            )
  22890 
  22891        code += dedent(
  22892            """
  22893            JS::Rooted<JS::Value> v(cx);
  22894            if(!ToJSValue(cx, self, &v)) {
  22895              aRv.Throw(NS_ERROR_UNEXPECTED);
  22896              return%s;
  22897            }
  22898            // This is a reflector, but due to trying to name things
  22899            // similarly across method generators, it's called obj here.
  22900            JS::Rooted<JSObject*> obj(cx);
  22901            obj = js::UncheckedUnwrap(&v.toObject(), /* stopAtWindowProxy = */ false);
  22902            """
  22903            % self.getDefaultRetval()
  22904        )
  22905 
  22906        # We'd like wrap the inner code in a scope such that the code can use the
  22907        # same realm. So here we are creating the result variable outside of the
  22908        # scope.
  22909        if self.needsScopeBody():
  22910            code += "JS::Rooted<JS::Value> result(cx);\n"
  22911 
  22912        return code
  22913 
  22914    def getArgs(self, returnType, argList):
  22915        # We don't need the context or the value. We'll generate those instead.
  22916        args = CGNativeMember.getArgs(self, returnType, argList)
  22917        # Prepend a pointer to the binding object onto the arguments
  22918        return [Argument(self.descriptorProvider.nativeType + "*", "self")] + args
  22919 
  22920    def needsScopeBody(self):
  22921        return self.passJSBitsAsNeeded
  22922 
  22923    def getArgvDeclFailureCode(self):
  22924        return "aRv.Throw(NS_ERROR_UNEXPECTED);\n"
  22925 
  22926    def getResultConversion(self):
  22927        if self.needsResultConversion:
  22928            code = ""
  22929            if self.needsScopeBody():
  22930                code = dedent(
  22931                    """
  22932                    if (!JS_WrapValue(cx, &result)) {
  22933                      aRv.NoteJSContextException(cx);
  22934                      return;
  22935                    }
  22936                    """
  22937                )
  22938 
  22939            failureCode = dedent("aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n")
  22940 
  22941            exceptionCode = None
  22942            if self.retvalType.isPrimitive():
  22943                exceptionCode = dedent(
  22944                    "aRv.NoteJSContextException(cx);\nreturn%s;\n"
  22945                    % self.getDefaultRetval()
  22946                )
  22947 
  22948            return code + CallbackMember.getResultConversion(
  22949                self,
  22950                "result",
  22951                failureCode=failureCode,
  22952                isDefinitelyObject=True,
  22953                exceptionCode=exceptionCode,
  22954            )
  22955 
  22956        assignRetval = string.Template(
  22957            self.getRetvalInfo(self.retvalType, False)[2]
  22958        ).substitute(
  22959            {
  22960                "declName": "retVal",
  22961            }
  22962        )
  22963        return assignRetval
  22964 
  22965    def getRvalDecl(self):
  22966        # hack to make sure we put JSAutoRealm inside the body scope
  22967        return "JSAutoRealm reflectorRealm(cx, obj);\n"
  22968 
  22969    def getArgcDecl(self):
  22970        # Don't need argc for anything.
  22971        return None
  22972 
  22973    def getCall(self):
  22974        assert False  # Override me!
  22975 
  22976    def getPrettyName(self):
  22977        return self.name
  22978 
  22979    def declare(self):
  22980        return self.implMethod.declare()
  22981 
  22982    def define(self):
  22983        return self.implMethod.define()
  22984 
  22985 
  22986 class CGMaplikeOrSetlikeHelperFunctionGenerator(CGHelperFunctionGenerator):
  22987    """
  22988    Generates code to allow C++ to perform operations on backing objects. Gets
  22989    a context from the binding wrapper, turns arguments into JS::Values (via
  22990    CallbackMember/CGNativeMember argument conversion), then uses
  22991    CGMaplikeOrSetlikeMethodGenerator to generate the body.
  22992    """
  22993 
  22994    def __init__(
  22995        self,
  22996        descriptor,
  22997        maplikeOrSetlike,
  22998        name,
  22999        needsKeyArg=False,
  23000        needsValueArg=False,
  23001        needsValueTypeReturn=False,
  23002        needsBoolReturn=False,
  23003        needsResultConversion=True,
  23004    ):
  23005        self.maplikeOrSetlike = maplikeOrSetlike
  23006        self.needsValueTypeReturn = needsValueTypeReturn
  23007 
  23008        args = []
  23009        if needsKeyArg:
  23010            args.append(FakeArgument(maplikeOrSetlike.keyType, "aKey"))
  23011        if needsValueArg:
  23012            assert needsKeyArg
  23013            assert not needsValueTypeReturn
  23014            args.append(FakeArgument(maplikeOrSetlike.valueType, "aValue"))
  23015 
  23016        returnType = BuiltinTypes[IDLBuiltinType.Types.undefined]
  23017        if needsBoolReturn:
  23018            returnType = BuiltinTypes[IDLBuiltinType.Types.boolean]
  23019        elif needsValueTypeReturn:
  23020            returnType = maplikeOrSetlike.valueType
  23021 
  23022        CGHelperFunctionGenerator.__init__(
  23023            self,
  23024            descriptor,
  23025            name,
  23026            args,
  23027            returnType,
  23028            needsResultConversion,
  23029        )
  23030 
  23031    def getCall(self):
  23032        return CGMaplikeOrSetlikeMethodGenerator(
  23033            self.descriptorProvider,
  23034            self.maplikeOrSetlike,
  23035            self.name.lower(),
  23036            self.needsValueTypeReturn,
  23037            helperImpl=self,
  23038        ).define()
  23039 
  23040 
  23041 class CGMaplikeOrSetlikeHelperGenerator(CGNamespace):
  23042    """
  23043    Declares and defines convenience methods for accessing backing objects on
  23044    setlike/maplike interface. Generates function signatures, un/packs
  23045    backing objects from slot, etc.
  23046    """
  23047 
  23048    def __init__(self, descriptor, maplikeOrSetlike):
  23049        self.descriptor = descriptor
  23050        # Since iterables are folded in with maplike/setlike, make sure we've
  23051        # got the right type here.
  23052        assert maplikeOrSetlike.isMaplike() or maplikeOrSetlike.isSetlike()
  23053        self.maplikeOrSetlike = maplikeOrSetlike
  23054        self.namespace = "%sHelpers" % (
  23055            self.maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title()
  23056        )
  23057        self.helpers = [
  23058            CGMaplikeOrSetlikeHelperFunctionGenerator(
  23059                descriptor, maplikeOrSetlike, "Clear"
  23060            ),
  23061            CGMaplikeOrSetlikeHelperFunctionGenerator(
  23062                descriptor,
  23063                maplikeOrSetlike,
  23064                "Delete",
  23065                needsKeyArg=True,
  23066                needsBoolReturn=True,
  23067                needsResultConversion=False,
  23068            ),
  23069            CGMaplikeOrSetlikeHelperFunctionGenerator(
  23070                descriptor,
  23071                maplikeOrSetlike,
  23072                "Has",
  23073                needsKeyArg=True,
  23074                needsBoolReturn=True,
  23075                needsResultConversion=False,
  23076            ),
  23077        ]
  23078        if self.maplikeOrSetlike.isMaplike():
  23079            self.helpers.append(
  23080                CGMaplikeOrSetlikeHelperFunctionGenerator(
  23081                    descriptor,
  23082                    maplikeOrSetlike,
  23083                    "Set",
  23084                    needsKeyArg=True,
  23085                    needsValueArg=True,
  23086                )
  23087            )
  23088            self.helpers.append(
  23089                CGMaplikeOrSetlikeHelperFunctionGenerator(
  23090                    descriptor,
  23091                    maplikeOrSetlike,
  23092                    "Get",
  23093                    needsKeyArg=True,
  23094                    needsValueTypeReturn=True,
  23095                )
  23096            )
  23097        else:
  23098            assert self.maplikeOrSetlike.isSetlike()
  23099            self.helpers.append(
  23100                CGMaplikeOrSetlikeHelperFunctionGenerator(
  23101                    descriptor, maplikeOrSetlike, "Add", needsKeyArg=True
  23102                )
  23103            )
  23104        CGNamespace.__init__(self, self.namespace, CGList(self.helpers))
  23105 
  23106 
  23107 class CGIterableMethodGenerator(CGGeneric):
  23108    """
  23109    Creates methods for iterable interfaces. Unwrapping/wrapping
  23110    will be taken care of by the usual method generation machinery in
  23111    CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
  23112    using CGCallGenerator.
  23113    """
  23114 
  23115    def __init__(self, descriptor, methodName, args):
  23116        if methodName == "forEach":
  23117            assert len(args) == 2
  23118 
  23119            CGGeneric.__init__(
  23120                self,
  23121                fill(
  23122                    """
  23123                if (!JS::IsCallable(arg0)) {
  23124                  cx.ThrowErrorMessage<MSG_NOT_CALLABLE>("Argument 1");
  23125                  return false;
  23126                }
  23127                JS::RootedValueArray<3> callArgs(cx);
  23128                callArgs[2].setObject(*obj);
  23129                JS::Rooted<JS::Value> ignoredReturnVal(cx);
  23130                auto GetKeyAtIndex = &${selfType}::GetKeyAtIndex;
  23131                auto GetValueAtIndex = &${selfType}::GetValueAtIndex;
  23132                for (size_t i = 0; i < self->GetIterableLength(); ++i) {
  23133                  if (!CallIterableGetter(cx, GetValueAtIndex, self, i,
  23134                                          callArgs[0])) {
  23135                    return false;
  23136                  }
  23137                  if (!CallIterableGetter(cx, GetKeyAtIndex, self, i,
  23138                                          callArgs[1])) {
  23139                    return false;
  23140                  }
  23141                  if (!JS::Call(cx, arg1, arg0, JS::HandleValueArray(callArgs),
  23142                                &ignoredReturnVal)) {
  23143                    return false;
  23144                  }
  23145                }
  23146                """,
  23147                    ifaceName=descriptor.interface.identifier.name,
  23148                    selfType=descriptor.nativeType,
  23149                ),
  23150            )
  23151            return
  23152 
  23153        if descriptor.interface.isIterable():
  23154            assert descriptor.interface.maplikeOrSetlikeOrIterable.isPairIterator()
  23155            assert len(args) == 0
  23156 
  23157            wrap = f"{descriptor.interface.identifier.name}Iterator_Binding::Wrap"
  23158            iterClass = f"mozilla::dom::binding_detail::WrappableIterableIterator<{descriptor.nativeType}, &{wrap}>"
  23159        else:
  23160            needReturnMethod = toStringBool(
  23161                descriptor.interface.maplikeOrSetlikeOrIterable.getExtendedAttribute(
  23162                    "GenerateReturnMethod"
  23163                )
  23164                is not None
  23165            )
  23166            wrap = f"{descriptor.interface.identifier.name}AsyncIterator_Binding::Wrap"
  23167            iterClass = f"mozilla::dom::binding_detail::WrappableAsyncIterableIterator<{descriptor.nativeType}, {needReturnMethod}, &{wrap}>"
  23168 
  23169        createIterator = fill(
  23170            """
  23171            typedef ${iterClass} itrType;
  23172            RefPtr<itrType> result(new itrType(self,
  23173                                               itrType::IteratorType::${itrMethod}));
  23174            """,
  23175            iterClass=iterClass,
  23176            itrMethod=methodName.title(),
  23177        )
  23178 
  23179        if descriptor.interface.isAsyncIterable():
  23180            args.append("initError")
  23181            createIterator = fill(
  23182                """
  23183                $*{createIterator}
  23184                {
  23185                  ErrorResult initError;
  23186                  self->InitAsyncIteratorData(result->Data(), itrType::IteratorType::${itrMethod}, ${args});
  23187                  if (initError.MaybeSetPendingException(cx, "Asynchronous iterator initialization steps for ${ifaceName} failed")) {
  23188                    return false;
  23189                  }
  23190                }
  23191                """,
  23192                createIterator=createIterator,
  23193                itrMethod=methodName.title(),
  23194                args=", ".join(args),
  23195                ifaceName=descriptor.interface.identifier.name,
  23196            )
  23197 
  23198        CGGeneric.__init__(self, createIterator)
  23199 
  23200 
  23201 def getObservableArrayBackingObject(descriptor, attr, errorReturn="return false;\n"):
  23202    """
  23203    Generate code to get/create a JS backing list for an observableArray attribute
  23204    from the declaration slot.
  23205    """
  23206    assert attr.isAttr()
  23207    assert attr.type.isObservableArray()
  23208 
  23209    # GetObservableArrayBackingObject may return a wrapped object for Xrays, so
  23210    # when we create it we need to unwrap it to store the interface in the
  23211    # reserved slot.
  23212    return fill(
  23213        """
  23214        JS::Rooted<JSObject*> backingObj(cx);
  23215        bool created = false;
  23216        if (!GetObservableArrayBackingObject(cx, obj, ${slot},
  23217                &backingObj, &created, ${namespace}::ObservableArrayProxyHandler::getInstance(),
  23218                self)) {
  23219          $*{errorReturn}
  23220        }
  23221        if (created) {
  23222          PreserveWrapper(self);
  23223        }
  23224        """,
  23225        namespace=toBindingNamespace(MakeNativeName(attr.identifier.name)),
  23226        slot=memberReservedSlot(attr, descriptor),
  23227        errorReturn=errorReturn,
  23228        selfType=descriptor.nativeType,
  23229    )
  23230 
  23231 
  23232 def getObservableArrayGetterBody(descriptor, attr):
  23233    """
  23234    Creates the body for the getter method of an observableArray attribute.
  23235    """
  23236    assert attr.type.isObservableArray()
  23237    return fill(
  23238        """
  23239        $*{getBackingObj}
  23240        MOZ_ASSERT(!JS_IsExceptionPending(cx));
  23241        args.rval().setObject(*backingObj);
  23242        return true;
  23243        """,
  23244        getBackingObj=getObservableArrayBackingObject(descriptor, attr),
  23245    )
  23246 
  23247 
  23248 class CGObservableArrayProxyHandler_callback(ClassMethod):
  23249    """
  23250    Base class for declaring and defining the hook methods for ObservableArrayProxyHandler
  23251    subclasses to get the interface native object from backing object and calls
  23252    its On{Set|Delete}* callback.
  23253 
  23254     * 'callbackType': "Set" or "Delete".
  23255     * 'invalidTypeFatal' (optional): If True, we don't expect the type
  23256                                      conversion would fail, so generate the
  23257                                      assertion code if type conversion fails.
  23258    """
  23259 
  23260    def __init__(
  23261        self, descriptor, attr, name, args, callbackType, invalidTypeFatal=False
  23262    ):
  23263        assert attr.isAttr()
  23264        assert attr.type.isObservableArray()
  23265        assert callbackType in ["Set", "Delete"]
  23266        self.descriptor = descriptor
  23267        self.attr = attr
  23268        self.innertype = attr.type.inner
  23269        self.callbackType = callbackType
  23270        self.invalidTypeFatal = invalidTypeFatal
  23271        ClassMethod.__init__(
  23272            self,
  23273            name,
  23274            "bool",
  23275            args,
  23276            visibility="protected",
  23277            virtual=True,
  23278            override=True,
  23279            const=True,
  23280        )
  23281 
  23282    def preConversion(self):
  23283        """
  23284        The code to run before the conversion steps.
  23285        """
  23286        return ""
  23287 
  23288    def preCallback(self):
  23289        """
  23290        The code to run before calling the callback.
  23291        """
  23292        return ""
  23293 
  23294    def postCallback(self):
  23295        """
  23296        The code to run after calling the callback, all subclasses should override
  23297        this to generate the return values.
  23298        """
  23299        assert False  # Override me!
  23300 
  23301    def getBody(self):
  23302        exceptionCode = (
  23303            fill(
  23304                """
  23305                MOZ_ASSERT_UNREACHABLE("The item in ObservableArray backing list is not ${innertype}?");
  23306                return false;
  23307                """,
  23308                innertype=self.innertype,
  23309            )
  23310            if self.invalidTypeFatal
  23311            else None
  23312        )
  23313        convertType = instantiateJSToNativeConversion(
  23314            getJSToNativeConversionInfo(
  23315                self.innertype,
  23316                self.descriptor,
  23317                sourceDescription="Element in ObservableArray backing list",
  23318                exceptionCode=exceptionCode,
  23319            ),
  23320            {
  23321                "declName": "decl",
  23322                "holderName": "holder",
  23323                "val": "aValue",
  23324            },
  23325        )
  23326        callbackArgs = ["decl", "aIndex", "rv"]
  23327        if typeNeedsCx(self.innertype):
  23328            callbackArgs.insert(0, "cx")
  23329        return fill(
  23330            """
  23331            MOZ_ASSERT(IsObservableArrayProxy(aProxy));
  23332            $*{preConversion}
  23333 
  23334            BindingCallContext cx(aCx, "ObservableArray ${name}");
  23335            $*{convertType}
  23336 
  23337            $*{preCallback}
  23338            JS::Value val = js::GetProxyReservedSlot(aProxy, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT);
  23339            auto* interface = static_cast<${ifaceType}*>(val.toPrivate());
  23340            MOZ_ASSERT(interface);
  23341 
  23342            ErrorResult rv;
  23343            MOZ_KnownLive(interface)->${methodName}(${callbackArgs});
  23344            $*{postCallback}
  23345            """,
  23346            preConversion=self.preConversion(),
  23347            name=self.name,
  23348            convertType=convertType.define(),
  23349            preCallback=self.preCallback(),
  23350            ifaceType=self.descriptor.nativeType,
  23351            methodName="On%s%s"
  23352            % (self.callbackType, MakeNativeName(self.attr.identifier.name)),
  23353            callbackArgs=", ".join(callbackArgs),
  23354            postCallback=self.postCallback(),
  23355        )
  23356 
  23357 
  23358 class CGObservableArrayProxyHandler_OnDeleteItem(
  23359    CGObservableArrayProxyHandler_callback
  23360 ):
  23361    """
  23362    Declares and defines the hook methods for ObservableArrayProxyHandler
  23363    subclasses to get the interface native object from backing object and calls
  23364    its OnDelete* callback.
  23365    """
  23366 
  23367    def __init__(self, descriptor, attr):
  23368        args = [
  23369            Argument("JSContext*", "aCx"),
  23370            Argument("JS::Handle<JSObject*>", "aProxy"),
  23371            Argument("JS::Handle<JS::Value>", "aValue"),
  23372            Argument("uint32_t", "aIndex"),
  23373        ]
  23374        CGObservableArrayProxyHandler_callback.__init__(
  23375            self,
  23376            descriptor,
  23377            attr,
  23378            "OnDeleteItem",
  23379            args,
  23380            "Delete",
  23381            True,
  23382        )
  23383 
  23384    def postCallback(self):
  23385        return dedent(
  23386            """
  23387            return !rv.MaybeSetPendingException(cx);
  23388            """
  23389        )
  23390 
  23391 
  23392 class CGObservableArrayProxyHandler_SetIndexedValue(
  23393    CGObservableArrayProxyHandler_callback
  23394 ):
  23395    """
  23396    Declares and defines the hook methods for ObservableArrayProxyHandler
  23397    subclasses to run the setting the indexed value steps.
  23398    """
  23399 
  23400    def __init__(self, descriptor, attr):
  23401        args = [
  23402            Argument("JSContext*", "aCx"),
  23403            Argument("JS::Handle<JSObject*>", "aProxy"),
  23404            Argument("JS::Handle<JSObject*>", "aBackingList"),
  23405            Argument("uint32_t", "aIndex"),
  23406            Argument("JS::Handle<JS::Value>", "aValue"),
  23407            Argument("JS::ObjectOpResult&", "aResult"),
  23408        ]
  23409        CGObservableArrayProxyHandler_callback.__init__(
  23410            self,
  23411            descriptor,
  23412            attr,
  23413            "SetIndexedValue",
  23414            args,
  23415            "Set",
  23416        )
  23417 
  23418    def preConversion(self):
  23419        return dedent(
  23420            """
  23421            uint32_t oldLen;
  23422            if (!JS::GetArrayLength(aCx, aBackingList, &oldLen)) {
  23423              return false;
  23424            }
  23425 
  23426            if (aIndex > oldLen) {
  23427              return aResult.failBadIndex();
  23428            }
  23429            """
  23430        )
  23431 
  23432    def preCallback(self):
  23433        return dedent(
  23434            """
  23435            if (aIndex < oldLen) {
  23436              JS::Rooted<JS::Value> value(aCx);
  23437              if (!JS_GetElement(aCx, aBackingList, aIndex, &value)) {
  23438                return false;
  23439              }
  23440 
  23441              if (!OnDeleteItem(aCx, aProxy, value, aIndex)) {
  23442                return false;
  23443              }
  23444            }
  23445 
  23446            """
  23447        )
  23448 
  23449    def postCallback(self):
  23450        return dedent(
  23451            """
  23452            if (rv.MaybeSetPendingException(cx)) {
  23453              return false;
  23454            }
  23455 
  23456            if (!JS_SetElement(aCx, aBackingList, aIndex, aValue)) {
  23457              return false;
  23458            }
  23459 
  23460            return aResult.succeed();
  23461            """
  23462        )
  23463 
  23464 
  23465 class CGObservableArrayProxyHandler(CGThing):
  23466    """
  23467    A class for declaring a ObservableArrayProxyHandler.
  23468    """
  23469 
  23470    def __init__(self, descriptor, attr):
  23471        assert attr.isAttr()
  23472        assert attr.type.isObservableArray()
  23473        methods = [
  23474            # The item stored in backing object should always be converted successfully.
  23475            CGObservableArrayProxyHandler_OnDeleteItem(descriptor, attr),
  23476            CGObservableArrayProxyHandler_SetIndexedValue(descriptor, attr),
  23477            CGJSProxyHandler_getInstance("ObservableArrayProxyHandler"),
  23478        ]
  23479        parentClass = "mozilla::dom::ObservableArrayProxyHandler"
  23480        self.proxyHandler = CGClass(
  23481            "ObservableArrayProxyHandler",
  23482            bases=[ClassBase(parentClass)],
  23483            constructors=[],
  23484            methods=methods,
  23485        )
  23486 
  23487    def declare(self):
  23488        # Our class declaration should happen when we're defining
  23489        return ""
  23490 
  23491    def define(self):
  23492        return self.proxyHandler.declare() + "\n" + self.proxyHandler.define()
  23493 
  23494 
  23495 class CGObservableArrayProxyHandlerGenerator(CGNamespace):
  23496    """
  23497    Declares and defines convenience methods for accessing backing list objects
  23498    for observable array attribute. Generates function signatures, un/packs
  23499    backing list objects from slot, etc.
  23500    """
  23501 
  23502    def __init__(self, descriptor, attr):
  23503        assert attr.isAttr()
  23504        assert attr.type.isObservableArray()
  23505        namespace = toBindingNamespace(MakeNativeName(attr.identifier.name))
  23506        proxyHandler = CGObservableArrayProxyHandler(descriptor, attr)
  23507        CGNamespace.__init__(self, namespace, proxyHandler)
  23508 
  23509 
  23510 class CGObservableArraySetterGenerator(CGGeneric):
  23511    """
  23512    Creates setter for an observableArray attributes.
  23513    """
  23514 
  23515    def __init__(self, descriptor, attr):
  23516        assert attr.isAttr()
  23517        assert attr.type.isObservableArray()
  23518        getBackingObject = getObservableArrayBackingObject(descriptor, attr)
  23519        setElement = dedent(
  23520            """
  23521            if (!JS_SetElement(cx, backingObj, i, val)) {
  23522              return false;
  23523            }
  23524            """
  23525        )
  23526        conversion = wrapForType(
  23527            attr.type.inner,
  23528            descriptor,
  23529            {
  23530                "result": "arg0.ElementAt(i)",
  23531                "successCode": setElement,
  23532                "jsvalRef": "val",
  23533                "jsvalHandle": "&val",
  23534            },
  23535        )
  23536        CGGeneric.__init__(
  23537            self,
  23538            fill(
  23539                """
  23540                if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
  23541                  JS_ReportErrorASCII(cx, "Accessing from Xray wrapper is not supported.");
  23542                  return false;
  23543                }
  23544 
  23545                ${getBackingObject}
  23546                const ObservableArrayProxyHandler* handler = GetObservableArrayProxyHandler(backingObj);
  23547                if (!handler->SetLength(cx, backingObj, 0)) {
  23548                  return false;
  23549                }
  23550 
  23551                JS::Rooted<JS::Value> val(cx);
  23552                for (size_t i = 0; i < arg0.Length(); i++) {
  23553                  $*{conversion}
  23554                }
  23555                """,
  23556                conversion=conversion,
  23557                getBackingObject=getBackingObject,
  23558            ),
  23559        )
  23560 
  23561 
  23562 class CGObservableArrayHelperFunctionGenerator(CGHelperFunctionGenerator):
  23563    """
  23564    Generates code to allow C++ to perform operations on backing objects. Gets
  23565    a context from the binding wrapper, turns arguments into JS::Values (via
  23566    CallbackMember/CGNativeMember argument conversion), then uses
  23567    MethodBodyGenerator to generate the body.
  23568    """
  23569 
  23570    class MethodBodyGenerator(CGThing):
  23571        """
  23572        Creates methods body for observable array attribute. It is expected that all
  23573        methods will be have a maplike/setlike object attached. Unwrapping/wrapping
  23574        will be taken care of by the usual method generation machinery in
  23575        CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
  23576        using CGCallGenerator.
  23577        """
  23578 
  23579        def __init__(
  23580            self,
  23581            descriptor,
  23582            attr,
  23583            methodName,
  23584            helperGenerator,
  23585            needsIndexArg,
  23586        ):
  23587            assert attr.isAttr()
  23588            assert attr.type.isObservableArray()
  23589 
  23590            CGThing.__init__(self)
  23591            self.helperGenerator = helperGenerator
  23592            self.cgRoot = CGList([])
  23593 
  23594            self.cgRoot.append(
  23595                CGGeneric(
  23596                    getObservableArrayBackingObject(
  23597                        descriptor,
  23598                        attr,
  23599                        dedent(
  23600                            """
  23601                            aRv.Throw(NS_ERROR_UNEXPECTED);
  23602                            return%s;
  23603                            """
  23604                            % helperGenerator.getDefaultRetval()
  23605                        ),
  23606                    )
  23607                )
  23608            )
  23609 
  23610            # Generates required code for the method. Method descriptions included
  23611            # in definitions below. Throw if we don't have a method to fill in what
  23612            # we're looking for.
  23613            try:
  23614                methodGenerator = getattr(self, methodName)
  23615            except AttributeError:
  23616                raise TypeError(
  23617                    "Missing observable array method definition '%s'" % methodName
  23618                )
  23619            # Method generator returns tuple, containing:
  23620            #
  23621            # - a list of CGThings representing setup code for preparing to call
  23622            #   the JS API function
  23623            # - JS API function name
  23624            # - a list of arguments needed for the JS API function we're calling
  23625            # - a list of CGThings representing code needed before return.
  23626            (setupCode, funcName, arguments, returnCode) = methodGenerator()
  23627 
  23628            # Append the list of setup code CGThings
  23629            self.cgRoot.append(CGList(setupCode))
  23630            # Create the JS API call
  23631            if needsIndexArg:
  23632                arguments.insert(0, "aIndex")
  23633            self.cgRoot.append(
  23634                CGWrapper(
  23635                    CGGeneric(
  23636                        fill(
  23637                            """
  23638                            aRv.MightThrowJSException();
  23639                            if (!${funcName}(${args})) {
  23640                              aRv.StealExceptionFromJSContext(cx);
  23641                              return${retval};
  23642                            }
  23643                            """,
  23644                            funcName=funcName,
  23645                            args=", ".join(["cx", "backingObj"] + arguments),
  23646                            retval=helperGenerator.getDefaultRetval(),
  23647                        )
  23648                    )
  23649                )
  23650            )
  23651            # Append code before return
  23652            self.cgRoot.append(CGList(returnCode))
  23653 
  23654        def elementat(self):
  23655            setupCode = []
  23656            if not self.helperGenerator.needsScopeBody():
  23657                setupCode.append(CGGeneric("JS::Rooted<JS::Value> result(cx);\n"))
  23658            returnCode = [
  23659                CGGeneric(
  23660                    fill(
  23661                        """
  23662                        if (result.isUndefined()) {
  23663                          aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  23664                          return${retval};
  23665                        }
  23666                        """,
  23667                        retval=self.helperGenerator.getDefaultRetval(),
  23668                    )
  23669                )
  23670            ]
  23671            return (setupCode, "JS_GetElement", ["&result"], returnCode)
  23672 
  23673        def replaceelementat(self):
  23674            setupCode = [
  23675                CGGeneric(
  23676                    fill(
  23677                        """
  23678                        uint32_t length;
  23679                        aRv.MightThrowJSException();
  23680                        if (!JS::GetArrayLength(cx, backingObj, &length)) {
  23681                          aRv.StealExceptionFromJSContext(cx);
  23682                          return${retval};
  23683                        }
  23684                        if (aIndex > length) {
  23685                          aRv.ThrowRangeError("Invalid index");
  23686                          return${retval};
  23687                        }
  23688                        """,
  23689                        retval=self.helperGenerator.getDefaultRetval(),
  23690                    )
  23691                )
  23692            ]
  23693            return (setupCode, "JS_SetElement", ["argv[0]"], [])
  23694 
  23695        def appendelement(self):
  23696            setupCode = [
  23697                CGGeneric(
  23698                    fill(
  23699                        """
  23700                        uint32_t length;
  23701                        aRv.MightThrowJSException();
  23702                        if (!JS::GetArrayLength(cx, backingObj, &length)) {
  23703                          aRv.StealExceptionFromJSContext(cx);
  23704                          return${retval};
  23705                        }
  23706                        """,
  23707                        retval=self.helperGenerator.getDefaultRetval(),
  23708                    )
  23709                )
  23710            ]
  23711            return (setupCode, "JS_SetElement", ["length", "argv[0]"], [])
  23712 
  23713        def removelastelement(self):
  23714            setupCode = [
  23715                CGGeneric(
  23716                    fill(
  23717                        """
  23718                        uint32_t length;
  23719                        aRv.MightThrowJSException();
  23720                        if (!JS::GetArrayLength(cx, backingObj, &length)) {
  23721                          aRv.StealExceptionFromJSContext(cx);
  23722                          return${retval};
  23723                        }
  23724                        if (length == 0) {
  23725                          aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  23726                          return${retval};
  23727                        }
  23728                        """,
  23729                        retval=self.helperGenerator.getDefaultRetval(),
  23730                    )
  23731                )
  23732            ]
  23733            return (setupCode, "JS::SetArrayLength", ["length - 1"], [])
  23734 
  23735        def length(self):
  23736            return (
  23737                [CGGeneric("uint32_t retVal;\n")],
  23738                "JS::GetArrayLength",
  23739                ["&retVal"],
  23740                [],
  23741            )
  23742 
  23743        def define(self):
  23744            return self.cgRoot.define()
  23745 
  23746    def __init__(
  23747        self,
  23748        descriptor,
  23749        attr,
  23750        name,
  23751        returnType=BuiltinTypes[IDLBuiltinType.Types.undefined],
  23752        needsResultConversion=True,
  23753        needsIndexArg=False,
  23754        needsValueArg=False,
  23755    ):
  23756        assert attr.isAttr()
  23757        assert attr.type.isObservableArray()
  23758        self.attr = attr
  23759        self.needsIndexArg = needsIndexArg
  23760 
  23761        args = []
  23762        if needsValueArg:
  23763            args.append(FakeArgument(attr.type.inner, "aValue"))
  23764 
  23765        CGHelperFunctionGenerator.__init__(
  23766            self,
  23767            descriptor,
  23768            name,
  23769            args,
  23770            returnType,
  23771            needsResultConversion,
  23772        )
  23773 
  23774    def getArgs(self, returnType, argList):
  23775        if self.needsIndexArg:
  23776            argList = [
  23777                FakeArgument(BuiltinTypes[IDLBuiltinType.Types.unsigned_long], "aIndex")
  23778            ] + argList
  23779        return CGHelperFunctionGenerator.getArgs(self, returnType, argList)
  23780 
  23781    def getCall(self):
  23782        return CGObservableArrayHelperFunctionGenerator.MethodBodyGenerator(
  23783            self.descriptorProvider,
  23784            self.attr,
  23785            self.name.lower(),
  23786            self,
  23787            self.needsIndexArg,
  23788        ).define()
  23789 
  23790 
  23791 class CGObservableArrayHelperGenerator(CGNamespace):
  23792    """
  23793    Declares and defines convenience methods for accessing backing object for
  23794    observable array type. Generates function signatures, un/packs
  23795    backing objects from slot, etc.
  23796    """
  23797 
  23798    def __init__(self, descriptor, attr):
  23799        assert attr.isAttr()
  23800        assert attr.type.isObservableArray()
  23801 
  23802        namespace = "%sHelpers" % MakeNativeName(attr.identifier.name)
  23803        helpers = [
  23804            CGObservableArrayHelperFunctionGenerator(
  23805                descriptor,
  23806                attr,
  23807                "ElementAt",
  23808                returnType=attr.type.inner,
  23809                needsIndexArg=True,
  23810            ),
  23811            CGObservableArrayHelperFunctionGenerator(
  23812                descriptor,
  23813                attr,
  23814                "ReplaceElementAt",
  23815                needsIndexArg=True,
  23816                needsValueArg=True,
  23817            ),
  23818            CGObservableArrayHelperFunctionGenerator(
  23819                descriptor,
  23820                attr,
  23821                "AppendElement",
  23822                needsValueArg=True,
  23823            ),
  23824            CGObservableArrayHelperFunctionGenerator(
  23825                descriptor,
  23826                attr,
  23827                "RemoveLastElement",
  23828            ),
  23829            CGObservableArrayHelperFunctionGenerator(
  23830                descriptor,
  23831                attr,
  23832                "Length",
  23833                returnType=BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
  23834                needsResultConversion=False,
  23835            ),
  23836        ]
  23837        CGNamespace.__init__(self, namespace, CGList(helpers, "\n"))
  23838 
  23839 
  23840 class GlobalGenRoots:
  23841    """
  23842    Roots for global codegen.
  23843 
  23844    To generate code, call the method associated with the target, and then
  23845    call the appropriate define/declare method.
  23846    """
  23847 
  23848    @staticmethod
  23849    def GeneratedAtomList(config):
  23850        # Atom enum
  23851        dictionaries = config.dictionaries
  23852 
  23853        structs = []
  23854 
  23855        def memberToAtomCacheMember(binaryNameFor, m):
  23856            binaryMemberName = binaryNameFor(m)
  23857            return ClassMember(
  23858                CGDictionary.makeIdName(binaryMemberName),
  23859                "PinnedStringId",
  23860                visibility="public",
  23861            )
  23862 
  23863        def buildAtomCacheStructure(idlobj, binaryNameFor, members):
  23864            classMembers = [memberToAtomCacheMember(binaryNameFor, m) for m in members]
  23865            structName = idlobj.identifier.name + "Atoms"
  23866            return (
  23867                structName,
  23868                CGWrapper(
  23869                    CGClass(
  23870                        structName, bases=None, isStruct=True, members=classMembers
  23871                    ),
  23872                    post="\n",
  23873                ),
  23874            )
  23875 
  23876        for dict in dictionaries:
  23877            if len(dict.members) == 0:
  23878                continue
  23879 
  23880            structs.append(
  23881                buildAtomCacheStructure(dict, lambda m: m.identifier.name, dict.members)
  23882            )
  23883 
  23884        for d in config.getDescriptors(isJSImplemented=True) + config.getDescriptors(
  23885            isCallback=True
  23886        ):
  23887            members = [m for m in d.interface.members if m.isAttr() or m.isMethod()]
  23888            if d.interface.isJSImplemented() and d.interface.ctor():
  23889                # We'll have an __init() method.
  23890                members.append(FakeMember("__init"))
  23891            if d.interface.isJSImplemented() and d.interface.getExtendedAttribute(
  23892                "WantsEventListenerHooks"
  23893            ):
  23894                members.append(FakeMember("eventListenerAdded"))
  23895                members.append(FakeMember("eventListenerRemoved"))
  23896            if len(members) == 0:
  23897                continue
  23898 
  23899            structs.append(
  23900                buildAtomCacheStructure(
  23901                    d.interface,
  23902                    lambda m: d.binaryNameFor(m.identifier.name, m.isStatic()),
  23903                    members,
  23904                )
  23905            )
  23906 
  23907        structs.sort()
  23908        generatedStructs = [struct for structName, struct in structs]
  23909        structNames = [structName for structName, struct in structs]
  23910 
  23911        mainStruct = CGWrapper(
  23912            CGClass(
  23913                "PerThreadAtomCache",
  23914                bases=[ClassBase(structName) for structName in structNames],
  23915                isStruct=True,
  23916            ),
  23917            post="\n",
  23918        )
  23919 
  23920        structs = CGList(generatedStructs + [mainStruct])
  23921 
  23922        # Wrap all of that in our namespaces.
  23923        curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(structs, pre="\n"))
  23924        curr = CGWrapper(curr, post="\n")
  23925 
  23926        # Add include statement for PinnedStringId.
  23927        declareIncludes = ["mozilla/dom/PinnedStringId.h"]
  23928        curr = CGHeaders([], [], [], [], declareIncludes, [], "GeneratedAtomList", curr)
  23929 
  23930        # Add include guards.
  23931        curr = CGIncludeGuard("GeneratedAtomList", curr)
  23932 
  23933        # Add the auto-generated comment.
  23934        curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
  23935 
  23936        # Done.
  23937        return curr
  23938 
  23939    @staticmethod
  23940    def GeneratedEventList(config):
  23941        eventList = CGList([])
  23942        for generatedEvent in config.generatedEvents:
  23943            eventList.append(
  23944                CGGeneric(declare=("GENERATED_EVENT(%s)\n" % generatedEvent))
  23945            )
  23946        return eventList
  23947 
  23948    @staticmethod
  23949    def PrototypeList(config):
  23950        # Prototype ID enum.
  23951        descriptorsWithPrototype = config.getDescriptors(
  23952            hasInterfacePrototypeObject=True
  23953        )
  23954        descriptorsWithPrototype.extend(
  23955            config.getDescriptors(hasOrdinaryObjectPrototype=True)
  23956        )
  23957        descriptorsWithPrototype.sort(key=attrgetter("name"))
  23958        protos = [d.name for d in descriptorsWithPrototype]
  23959        idEnum = CGNamespacedEnum("id", "ID", ["_ID_Start"] + protos, [0, "_ID_Start"])
  23960        idEnum = CGList([idEnum])
  23961 
  23962        def fieldSizeAssert(amount, jitInfoField, message):
  23963            maxFieldValue = (
  23964                "(uint64_t(1) << (sizeof(std::declval<JSJitInfo>().%s) * 8))"
  23965                % jitInfoField
  23966            )
  23967            return CGGeneric(
  23968                define='static_assert(%s < %s, "%s");\n\n'
  23969                % (amount, maxFieldValue, message)
  23970            )
  23971 
  23972        idEnum.append(
  23973            fieldSizeAssert("id::_ID_Count", "protoID", "Too many prototypes!")
  23974        )
  23975 
  23976        # Wrap all of that in our namespaces.
  23977        idEnum = CGNamespace.build(
  23978            ["mozilla", "dom", "prototypes"], CGWrapper(idEnum, pre="\n")
  23979        )
  23980        idEnum = CGWrapper(idEnum, post="\n")
  23981 
  23982        curr = CGList(
  23983            [
  23984                CGGeneric(define="#include <stdint.h>\n"),
  23985                CGGeneric(define="#include <type_traits>\n\n"),
  23986                CGGeneric(define='#include "js/experimental/JitInfo.h"\n\n'),
  23987                CGGeneric(define='#include "mozilla/dom/BindingNames.h"\n\n'),
  23988                CGGeneric(define='#include "mozilla/dom/PrototypeList.h"\n\n'),
  23989                idEnum,
  23990            ]
  23991        )
  23992 
  23993        # Let things know the maximum length of the prototype chain.
  23994        maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH"
  23995        maxMacro = CGGeneric(
  23996            declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength)
  23997        )
  23998        curr.append(CGWrapper(maxMacro, post="\n\n"))
  23999        curr.append(
  24000            fieldSizeAssert(
  24001                maxMacroName, "depth", "Some inheritance chain is too long!"
  24002            )
  24003        )
  24004 
  24005        # Constructor ID enum.
  24006        constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)]
  24007        idEnum = CGNamespacedEnum(
  24008            "id",
  24009            "ID",
  24010            ["_ID_Start"] + constructors,
  24011            ["prototypes::id::_ID_Count", "_ID_Start"],
  24012        )
  24013 
  24014        # Wrap all of that in our namespaces.
  24015        idEnum = CGNamespace.build(
  24016            ["mozilla", "dom", "constructors"], CGWrapper(idEnum, pre="\n")
  24017        )
  24018        idEnum = CGWrapper(idEnum, post="\n")
  24019 
  24020        curr.append(idEnum)
  24021 
  24022        # Named properties object enum.
  24023        namedPropertiesObjects = [
  24024            d.name for d in config.getDescriptors(hasNamedPropertiesObject=True)
  24025        ]
  24026        idEnum = CGNamespacedEnum(
  24027            "id",
  24028            "ID",
  24029            ["_ID_Start"] + namedPropertiesObjects,
  24030            ["constructors::id::_ID_Count", "_ID_Start"],
  24031        )
  24032 
  24033        # Wrap all of that in our namespaces.
  24034        idEnum = CGNamespace.build(
  24035            ["mozilla", "dom", "namedpropertiesobjects"], CGWrapper(idEnum, pre="\n")
  24036        )
  24037        idEnum = CGWrapper(idEnum, post="\n")
  24038 
  24039        curr.append(idEnum)
  24040 
  24041        traitsDecls = [
  24042            CGGeneric(
  24043                declare=dedent(
  24044                    """
  24045            template <prototypes::ID PrototypeID>
  24046            struct PrototypeTraits;
  24047            """
  24048                )
  24049            )
  24050        ]
  24051        traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype)
  24052 
  24053        ifaceNamesWithProto = [
  24054            d.interface.getClassName() for d in descriptorsWithPrototype
  24055        ]
  24056        traitsDecls.append(
  24057            CGStringTable("NamesOfInterfacesWithProtos", ifaceNamesWithProto)
  24058        )
  24059 
  24060        traitsDecl = CGNamespace.build(["mozilla", "dom"], CGList(traitsDecls))
  24061 
  24062        curr.append(traitsDecl)
  24063 
  24064        # Add include guards.
  24065        curr = CGIncludeGuard("PrototypeList", curr)
  24066 
  24067        # Add the auto-generated comment.
  24068        curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
  24069 
  24070        # Done.
  24071        return curr
  24072 
  24073    @staticmethod
  24074    def BindingNames(config):
  24075        declare = fill(
  24076            """
  24077            enum class BindingNamesOffset : uint16_t {
  24078              $*{enumValues}
  24079            };
  24080 
  24081            namespace binding_detail {
  24082            extern const char sBindingNames[];
  24083            }  // namespace binding_detail
  24084 
  24085            MOZ_ALWAYS_INLINE const char* BindingName(BindingNamesOffset aOffset) {
  24086              return binding_detail::sBindingNames + static_cast<size_t>(aOffset);
  24087            }
  24088            """,
  24089            enumValues="".join(
  24090                "%s = %i,\n" % (BindingNamesOffsetEnum(n), o)
  24091                for (n, o) in config.namesStringOffsets
  24092            ),
  24093        )
  24094        define = fill(
  24095            """
  24096            namespace binding_detail {
  24097 
  24098            const char sBindingNames[] = {
  24099              $*{namesString}
  24100            };
  24101 
  24102            }  // namespace binding_detail
  24103 
  24104            // Making this enum bigger than a uint16_t has consequences on the size
  24105            // of some structs (eg. WebIDLNameTableEntry) and tables. We should try
  24106            // to avoid that.
  24107            static_assert(EnumTypeFitsWithin<BindingNamesOffset, uint16_t>::value,
  24108                          "Size increase");
  24109            """,
  24110            namesString=' "\\0"\n'.join(
  24111                '/* %5i */ "%s"' % (o, n) for (n, o) in config.namesStringOffsets
  24112            )
  24113            + "\n",
  24114        )
  24115 
  24116        curr = CGGeneric(declare=declare, define=define)
  24117        curr = CGWrapper(curr, pre="\n", post="\n")
  24118 
  24119        curr = CGNamespace.build(["mozilla", "dom"], curr)
  24120        curr = CGWrapper(curr, post="\n")
  24121 
  24122        curr = CGHeaders(
  24123            [],
  24124            [],
  24125            [],
  24126            [],
  24127            ["<stddef.h>", "<stdint.h>", "mozilla/Attributes.h"],
  24128            ["mozilla/dom/BindingNames.h", "mozilla/EnumTypeTraits.h"],
  24129            "BindingNames",
  24130            curr,
  24131        )
  24132 
  24133        # Add include guards.
  24134        curr = CGIncludeGuard("BindingNames", curr)
  24135 
  24136        # Done.
  24137        return curr
  24138 
  24139    @staticmethod
  24140    def RegisterBindings(config):
  24141        curr = CGNamespace.build(
  24142            ["mozilla", "dom"], CGGlobalNames(config.windowGlobalNames)
  24143        )
  24144        curr = CGWrapper(curr, post="\n")
  24145 
  24146        # Add the includes
  24147        defineIncludes = [
  24148            CGHeaders.getDeclarationFilename(desc.interface)
  24149            for desc in config.getDescriptors(
  24150                hasInterfaceObject=True, isExposedInWindow=True, register=True
  24151            )
  24152        ]
  24153        defineIncludes.append("mozilla/dom/BindingNames.h")
  24154        defineIncludes.append("mozilla/dom/WebIDLGlobalNameHash.h")
  24155        defineIncludes.append("mozilla/dom/PrototypeList.h")
  24156        defineIncludes.append("mozilla/PerfectHash.h")
  24157        defineIncludes.append("js/String.h")
  24158        curr = CGHeaders([], [], [], [], [], defineIncludes, "RegisterBindings", curr)
  24159 
  24160        # Add include guards.
  24161        curr = CGIncludeGuard("RegisterBindings", curr)
  24162 
  24163        # Done.
  24164        return curr
  24165 
  24166    @staticmethod
  24167    def RegisterWorkerBindings(config):
  24168        curr = CGRegisterWorkerBindings(config)
  24169 
  24170        # Wrap all of that in our namespaces.
  24171        curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
  24172        curr = CGWrapper(curr, post="\n")
  24173 
  24174        # Add the includes
  24175        defineIncludes = [
  24176            CGHeaders.getDeclarationFilename(desc.interface)
  24177            for desc in config.getDescriptors(
  24178                hasInterfaceObject=True, register=True, isExposedInAnyWorker=True
  24179            )
  24180        ]
  24181 
  24182        curr = CGHeaders(
  24183            [], [], [], [], [], defineIncludes, "RegisterWorkerBindings", curr
  24184        )
  24185 
  24186        # Add include guards.
  24187        curr = CGIncludeGuard("RegisterWorkerBindings", curr)
  24188 
  24189        # Done.
  24190        return curr
  24191 
  24192    @staticmethod
  24193    def RegisterWorkerDebuggerBindings(config):
  24194        curr = CGRegisterWorkerDebuggerBindings(config)
  24195 
  24196        # Wrap all of that in our namespaces.
  24197        curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
  24198        curr = CGWrapper(curr, post="\n")
  24199 
  24200        # Add the includes
  24201        defineIncludes = [
  24202            CGHeaders.getDeclarationFilename(desc.interface)
  24203            for desc in config.getDescriptors(
  24204                hasInterfaceObject=True, register=True, isExposedInWorkerDebugger=True
  24205            )
  24206        ]
  24207 
  24208        curr = CGHeaders(
  24209            [], [], [], [], [], defineIncludes, "RegisterWorkerDebuggerBindings", curr
  24210        )
  24211 
  24212        # Add include guards.
  24213        curr = CGIncludeGuard("RegisterWorkerDebuggerBindings", curr)
  24214 
  24215        # Done.
  24216        return curr
  24217 
  24218    @staticmethod
  24219    def RegisterWorkletBindings(config):
  24220        curr = CGRegisterWorkletBindings(config)
  24221 
  24222        # Wrap all of that in our namespaces.
  24223        curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
  24224        curr = CGWrapper(curr, post="\n")
  24225 
  24226        # Add the includes
  24227        defineIncludes = [
  24228            CGHeaders.getDeclarationFilename(desc.interface)
  24229            for desc in config.getDescriptors(
  24230                hasInterfaceObject=True, register=True, isExposedInAnyWorklet=True
  24231            )
  24232        ]
  24233 
  24234        curr = CGHeaders(
  24235            [], [], [], [], [], defineIncludes, "RegisterWorkletBindings", curr
  24236        )
  24237 
  24238        # Add include guards.
  24239        curr = CGIncludeGuard("RegisterWorkletBindings", curr)
  24240 
  24241        # Done.
  24242        return curr
  24243 
  24244    @staticmethod
  24245    def RegisterShadowRealmBindings(config):
  24246        curr = CGRegisterShadowRealmBindings(config)
  24247 
  24248        # Wrap all of that in our namespaces.
  24249        curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
  24250        curr = CGWrapper(curr, post="\n")
  24251 
  24252        # Add the includes
  24253        defineIncludes = [
  24254            CGHeaders.getDeclarationFilename(desc.interface)
  24255            for desc in config.getDescriptors(
  24256                hasInterfaceObject=True, register=True, isExposedInShadowRealms=True
  24257            )
  24258        ]
  24259 
  24260        curr = CGHeaders(
  24261            [], [], [], [], [], defineIncludes, "RegisterShadowRealmBindings", curr
  24262        )
  24263 
  24264        # Add include guards.
  24265        curr = CGIncludeGuard("RegisterShadowRealmBindings", curr)
  24266 
  24267        # Done.
  24268        return curr
  24269 
  24270    @staticmethod
  24271    def UnionTypes(config):
  24272        unionTypes = UnionsForFile(config, None)
  24273        (
  24274            includes,
  24275            implincludes,
  24276            declarations,
  24277            traverseMethods,
  24278            unlinkMethods,
  24279            unionStructs,
  24280        ) = UnionTypes(unionTypes, config)
  24281 
  24282        unionStructs = dependencySortDictionariesAndUnionsAndCallbacks(unionStructs)
  24283 
  24284        unions = CGList(
  24285            traverseMethods
  24286            + unlinkMethods
  24287            + [CGUnionStruct(t, config) for t in unionStructs]
  24288            + [CGUnionStruct(t, config, True) for t in unionStructs],
  24289            "\n",
  24290        )
  24291 
  24292        includes.add("mozilla/OwningNonNull.h")
  24293        includes.add("mozilla/dom/UnionMember.h")
  24294        includes.add("mozilla/dom/BindingDeclarations.h")
  24295        # BindingUtils.h is only needed for SetToObject.
  24296        # If it stops being inlined or stops calling CallerSubsumes
  24297        # both this bit and the bit in CGBindingRoot can be removed.
  24298        includes.add("mozilla/dom/BindingUtils.h")
  24299 
  24300        # Wrap all of that in our namespaces.
  24301        curr = CGNamespace.build(["mozilla", "dom"], unions)
  24302 
  24303        curr = CGWrapper(curr, post="\n")
  24304 
  24305        builder = ForwardDeclarationBuilder()
  24306        for className, isStruct in declarations:
  24307            builder.add(className, isStruct=isStruct)
  24308 
  24309        curr = CGList([builder.build(), curr], "\n")
  24310 
  24311        curr = CGHeaders([], [], [], [], includes, implincludes, "UnionTypes", curr)
  24312 
  24313        # Add include guards.
  24314        curr = CGIncludeGuard("UnionTypes", curr)
  24315 
  24316        # Done.
  24317        return curr
  24318 
  24319    @staticmethod
  24320    def WebIDLPrefs(config):
  24321        prefs = set()
  24322        headers = set(["mozilla/dom/WebIDLPrefs.h"])
  24323        for d in config.getDescriptors(hasInterfaceOrInterfacePrototypeObject=True):
  24324            for m in d.interface.members:
  24325                pref = PropertyDefiner.getStringAttr(m, "Pref")
  24326                if pref:
  24327                    headers.add(prefHeader(pref))
  24328                    prefs.add((pref, prefIdentifier(pref)))
  24329        prefs = sorted(prefs)
  24330        declare = fill(
  24331            """
  24332            enum class WebIDLPrefIndex : uint8_t {
  24333              NoPref,
  24334              $*{prefs}
  24335            };
  24336            typedef bool (*WebIDLPrefFunc)();
  24337            extern const WebIDLPrefFunc sWebIDLPrefs[${len}];
  24338            """,
  24339            prefs=",\n".join(map(lambda p: "// " + p[0] + "\n" + p[1], prefs)) + "\n",
  24340            len=len(prefs) + 1,
  24341        )
  24342        define = fill(
  24343            """
  24344            const WebIDLPrefFunc sWebIDLPrefs[] = {
  24345              nullptr,
  24346              $*{prefs}
  24347            };
  24348            """,
  24349            prefs=",\n".join(
  24350                map(lambda p: "// " + p[0] + "\nStaticPrefs::" + p[1], prefs)
  24351            )
  24352            + "\n",
  24353        )
  24354        prefFunctions = CGGeneric(declare=declare, define=define)
  24355 
  24356        # Wrap all of that in our namespaces.
  24357        curr = CGNamespace.build(["mozilla", "dom"], prefFunctions)
  24358 
  24359        curr = CGWrapper(curr, post="\n")
  24360 
  24361        curr = CGHeaders([], [], [], [], [], headers, "WebIDLPrefs", curr)
  24362 
  24363        # Add include guards.
  24364        curr = CGIncludeGuard("WebIDLPrefs", curr)
  24365 
  24366        # Done.
  24367        return curr
  24368 
  24369    @staticmethod
  24370    def WebIDLSerializable(config):
  24371        # We need a declaration of StructuredCloneTags in the header.
  24372        declareIncludes = set(
  24373            [
  24374                "mozilla/dom/DOMJSClass.h",
  24375                "mozilla/dom/StructuredCloneTags.h",
  24376                "js/TypeDecls.h",
  24377            ]
  24378        )
  24379        defineIncludes = set(
  24380            ["mozilla/dom/WebIDLSerializable.h", "mozilla/PerfectHash.h"]
  24381        )
  24382        names = list()
  24383        for d in config.getDescriptors(isSerializable=True):
  24384            names.append(d.name)
  24385            defineIncludes.add(CGHeaders.getDeclarationFilename(d.interface))
  24386 
  24387        if len(names) == 0:
  24388            # We can't really create a PerfectHash out of this, but also there's
  24389            # not much point to this file if we have no [Serializable] objects.
  24390            # Just spit out an empty file.
  24391            return CGIncludeGuard("WebIDLSerializable", CGGeneric(""))
  24392 
  24393        # If we had a lot of serializable things, it might be worth it to use a
  24394        # PerfectHash here, or an array ordered by sctag value and binary
  24395        # search.  But setting those up would require knowing in this python
  24396        # code the values of the various SCTAG_DOM_*.  We could hardcode them
  24397        # here and add static asserts that the values are right, or switch to
  24398        # code-generating StructuredCloneTags.h or something.  But in practice,
  24399        # there's a pretty small number of serializable interfaces, and just
  24400        # doing a linear walk is fine.  It's not obviously worse than the
  24401        # if-cascade we used to have.  Let's just make sure we notice if we do
  24402        # end up with a lot of serializable things here.
  24403        #
  24404        # Also, in practice it looks like compilers compile this linear walk to
  24405        # an out-of-bounds check followed by a direct index into an array, by
  24406        # basically making a second copy of this array ordered by tag, with the
  24407        # holes filled in.  Again, worth checking whether this still happens if
  24408        # we have too many serializable things.
  24409        if len(names) > 20:
  24410            raise TypeError(
  24411                "We now have %s serializable interfaces.  "
  24412                "Double-check that the compiler is still "
  24413                "generating a jump table." % len(names)
  24414            )
  24415 
  24416        entries = list()
  24417        # Make sure we have stable ordering.
  24418        for name in sorted(names):
  24419            exposedGlobals = computeGlobalNamesFromExposureSet(d.interface.exposureSet)
  24420            # Strip off trailing newline to make our formatting look right.
  24421            entries.append(
  24422                fill(
  24423                    """
  24424                {
  24425                  /* mTag */ ${tag},
  24426                  /* mDeserialize */ ${name}_Binding::Deserialize,
  24427                  /* mExposedGlobals */ ${exposedGlobals},
  24428                }
  24429                """,
  24430                    tag=StructuredCloneTag(name),
  24431                    name=name,
  24432                    exposedGlobals=exposedGlobals,
  24433                )[:-1]
  24434            )
  24435 
  24436        declare = dedent(
  24437            """
  24438            Maybe<std::pair<uint16_t, WebIDLDeserializer>> LookupDeserializer(StructuredCloneTags aTag);
  24439            """
  24440        )
  24441        define = fill(
  24442            """
  24443            struct WebIDLSerializableEntry {
  24444              StructuredCloneTags mTag;
  24445              WebIDLDeserializer mDeserialize;
  24446              uint16_t mExposedGlobals;
  24447            };
  24448 
  24449            static const WebIDLSerializableEntry sEntries[] = {
  24450              $*{entries}
  24451            };
  24452 
  24453            Maybe<std::pair<uint16_t, WebIDLDeserializer>> LookupDeserializer(StructuredCloneTags aTag) {
  24454              for (auto& entry : sEntries) {
  24455                if (entry.mTag == aTag) {
  24456                  return Some(std::pair(entry.mExposedGlobals, entry.mDeserialize));
  24457                }
  24458              }
  24459              return Nothing();
  24460            }
  24461            """,
  24462            entries=",\n".join(entries) + "\n",
  24463        )
  24464 
  24465        code = CGGeneric(declare=declare, define=define)
  24466 
  24467        # Wrap all of that in our namespaces.
  24468        curr = CGNamespace.build(["mozilla", "dom"], code)
  24469 
  24470        curr = CGWrapper(curr, post="\n")
  24471 
  24472        curr = CGHeaders(
  24473            [], [], [], [], declareIncludes, defineIncludes, "WebIDLSerializable", curr
  24474        )
  24475 
  24476        # Add include guards.
  24477        curr = CGIncludeGuard("WebIDLSerializable", curr)
  24478 
  24479        # Done.
  24480        return curr
  24481 
  24482 
  24483 # Code generator for simple events
  24484 class CGEventGetter(CGNativeMember):
  24485    def __init__(self, descriptor, attr):
  24486        ea = descriptor.getExtendedAttributes(attr, getter=True)
  24487        CGNativeMember.__init__(
  24488            self,
  24489            descriptor,
  24490            attr,
  24491            CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
  24492            (attr.type, []),
  24493            ea,
  24494            resultNotAddRefed=not attr.type.isSequence(),
  24495        )
  24496        self.body = self.getMethodBody()
  24497 
  24498    def getArgs(self, returnType, argList):
  24499        if "needsErrorResult" in self.extendedAttrs:
  24500            raise TypeError("Event code generator does not support [Throws]!")
  24501        if "canOOM" in self.extendedAttrs:
  24502            raise TypeError("Event code generator does not support [CanOOM]!")
  24503        if not self.member.isAttr():
  24504            raise TypeError("Event code generator does not support methods")
  24505        if self.member.isStatic():
  24506            raise TypeError("Event code generators does not support static attributes")
  24507        return CGNativeMember.getArgs(self, returnType, argList)
  24508 
  24509    def getMethodBody(self):
  24510        type = self.member.type
  24511        memberName = CGDictionary.makeMemberName(self.member.identifier.name)
  24512        if (
  24513            (type.isPrimitive() and type.tag() in builtinNames)
  24514            or type.isEnum()
  24515            or type.isPromise()
  24516            or type.isGeckoInterface()
  24517        ):
  24518            return "return " + memberName + ";\n"
  24519        if type.isJSString():
  24520            # https://bugzilla.mozilla.org/show_bug.cgi?id=1580167
  24521            raise TypeError("JSString not supported as member of a generated event")
  24522        if (
  24523            type.isDOMString()
  24524            or type.isByteString()
  24525            or type.isUSVString()
  24526            or type.isUTF8String()
  24527        ):
  24528            return "aRetVal = " + memberName + ";\n"
  24529        if type.isSpiderMonkeyInterface() or type.isObject():
  24530            return fill(
  24531                """
  24532                if (${memberName}) {
  24533                  JS::ExposeObjectToActiveJS(${memberName});
  24534                }
  24535                aRetVal.set(${memberName});
  24536                return;
  24537                """,
  24538                memberName=memberName,
  24539            )
  24540        if type.isAny():
  24541            return fill(
  24542                """
  24543                ${selfName}(aRetVal);
  24544                """,
  24545                selfName=self.name,
  24546            )
  24547        if type.isUnion():
  24548            return "aRetVal = " + memberName + ";\n"
  24549        if type.isSequence():
  24550            if type.nullable():
  24551                return (
  24552                    "if ("
  24553                    + memberName
  24554                    + ".IsNull()) { aRetVal.SetNull(); } else { aRetVal.SetValue("
  24555                    + memberName
  24556                    + ".Value().Clone()); }\n"
  24557                )
  24558            else:
  24559                return "aRetVal = " + memberName + ".Clone();\n"
  24560        if type.isDictionary():
  24561            return "aRetVal = " + memberName + ";\n"
  24562        raise TypeError("Event code generator does not support this type!")
  24563 
  24564    def declare(self, cgClass):
  24565        if (
  24566            getattr(self.member, "originatingInterface", cgClass.descriptor.interface)
  24567            != cgClass.descriptor.interface
  24568        ):
  24569            return ""
  24570        return CGNativeMember.declare(self, cgClass)
  24571 
  24572    def define(self, cgClass):
  24573        if (
  24574            getattr(self.member, "originatingInterface", cgClass.descriptor.interface)
  24575            != cgClass.descriptor.interface
  24576        ):
  24577            return ""
  24578        return CGNativeMember.define(self, cgClass)
  24579 
  24580 
  24581 class CGEventSetter(CGNativeMember):
  24582    def __init__(self):
  24583        raise TypeError("Event code generator does not support setters!")
  24584 
  24585 
  24586 class CGEventMethod(CGNativeMember):
  24587    def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
  24588        self.isInit = False
  24589 
  24590        CGNativeMember.__init__(
  24591            self,
  24592            descriptor,
  24593            method,
  24594            CGSpecializedMethod.makeNativeName(descriptor, method),
  24595            signature,
  24596            descriptor.getExtendedAttributes(method),
  24597            breakAfter=breakAfter,
  24598            variadicIsSequence=True,
  24599        )
  24600        self.originalArgs = list(self.args)
  24601 
  24602        iface = descriptor.interface
  24603        allowed = isConstructor
  24604        if not allowed and iface.getExtendedAttribute("LegacyEventInit"):
  24605            # Allow it, only if it fits the initFooEvent profile exactly
  24606            # We could check the arg types but it's not worth the effort.
  24607            if (
  24608                method.identifier.name == "init" + iface.identifier.name
  24609                and signature[1][0].type.isDOMString()
  24610                and signature[1][1].type.isBoolean()
  24611                and signature[1][2].type.isBoolean()
  24612                and
  24613                # -3 on the left to ignore the type, bubbles, and cancelable parameters
  24614                # -1 on the right to ignore the .trusted property which bleeds through
  24615                # here because it is [Unforgeable].
  24616                len(signature[1]) - 3
  24617                == len([x for x in iface.members if x.isAttr()]) - 1
  24618            ):
  24619                allowed = True
  24620                self.isInit = True
  24621 
  24622        if not allowed:
  24623            raise TypeError("Event code generator does not support methods!")
  24624 
  24625    def getArgs(self, returnType, argList):
  24626        args = [self.getArg(arg) for arg in argList]
  24627        return args
  24628 
  24629    def getArg(self, arg):
  24630        decl, ref = self.getArgType(
  24631            arg.type, arg.canHaveMissingValue(), "Variadic" if arg.variadic else False
  24632        )
  24633        if ref:
  24634            decl = CGWrapper(decl, pre="const ", post="&")
  24635 
  24636        name = arg.identifier.name
  24637        name = "a" + name[0].upper() + name[1:]
  24638        return Argument(decl.define(), name)
  24639 
  24640    def declare(self, cgClass):
  24641        if self.isInit:
  24642            constructorForNativeCaller = ""
  24643        else:
  24644            self.args = list(self.originalArgs)
  24645            self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
  24646            constructorForNativeCaller = CGNativeMember.declare(self, cgClass)
  24647 
  24648        self.args = list(self.originalArgs)
  24649        if needCx(None, self.arguments(), [], considerTypes=True, static=True):
  24650            self.args.insert(0, Argument("JSContext*", "aCx"))
  24651        if not self.isInit:
  24652            self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
  24653 
  24654        return constructorForNativeCaller + CGNativeMember.declare(self, cgClass)
  24655 
  24656    def defineInit(self, cgClass):
  24657        iface = self.descriptorProvider.interface
  24658        members = ""
  24659        while iface.identifier.name != "Event":
  24660            i = 3  # Skip the boilerplate args: type, bubble,s cancelable.
  24661            for m in iface.members:
  24662                if m.isAttr():
  24663                    # We need to initialize all the member variables that do
  24664                    # not come from Event.
  24665                    if (
  24666                        getattr(m, "originatingInterface", iface).identifier.name
  24667                        == "Event"
  24668                    ):
  24669                        continue
  24670                    name = CGDictionary.makeMemberName(m.identifier.name)
  24671                    members += "%s = %s;\n" % (name, self.args[i].name)
  24672                    i += 1
  24673            iface = iface.parent
  24674 
  24675        self.body = fill(
  24676            """
  24677            InitEvent(${typeArg}, ${bubblesArg}, ${cancelableArg});
  24678            ${members}
  24679            """,
  24680            typeArg=self.args[0].name,
  24681            bubblesArg=self.args[1].name,
  24682            cancelableArg=self.args[2].name,
  24683            members=members,
  24684        )
  24685 
  24686        return CGNativeMember.define(self, cgClass)
  24687 
  24688    def define(self, cgClass):
  24689        self.args = list(self.originalArgs)
  24690        if self.isInit:
  24691            return self.defineInit(cgClass)
  24692        members = ""
  24693        holdJS = ""
  24694        iface = self.descriptorProvider.interface
  24695        while iface.identifier.name != "Event":
  24696            for m in self.descriptorProvider.getDescriptor(
  24697                iface.identifier.name
  24698            ).interface.members:
  24699                if m.isAttr():
  24700                    # We initialize all the other member variables in the
  24701                    # Constructor except those ones coming from the Event.
  24702                    if (
  24703                        getattr(
  24704                            m, "originatingInterface", cgClass.descriptor.interface
  24705                        ).identifier.name
  24706                        == "Event"
  24707                    ):
  24708                        continue
  24709                    name = CGDictionary.makeMemberName(m.identifier.name)
  24710                    if m.type.isSequence():
  24711                        # For sequences we may not be able to do a simple
  24712                        # assignment because the underlying types may not match.
  24713                        # For example, the argument can be a
  24714                        # Sequence<OwningNonNull<SomeInterface>> while our
  24715                        # member is an nsTArray<RefPtr<SomeInterface>>.  So
  24716                        # use AppendElements, which is actually a template on
  24717                        # the incoming type on nsTArray and does the right thing
  24718                        # for this case.
  24719                        target = name
  24720                        source = "%s.%s" % (self.args[1].name, name)
  24721                        sequenceCopy = "e->%s.AppendElements(%s);\n"
  24722                        if m.type.nullable():
  24723                            sequenceCopy = CGIfWrapper(
  24724                                CGGeneric(sequenceCopy), "!%s.IsNull()" % source
  24725                            ).define()
  24726                            target += ".SetValue()"
  24727                            source += ".Value()"
  24728                        members += sequenceCopy % (target, source)
  24729                    elif m.type.isSpiderMonkeyInterface():
  24730                        srcname = "%s.%s" % (self.args[1].name, name)
  24731                        if m.type.nullable():
  24732                            members += fill(
  24733                                """
  24734                                if (${srcname}.IsNull()) {
  24735                                  e->${varname} = nullptr;
  24736                                } else {
  24737                                  e->${varname} = ${srcname}.Value().Obj();
  24738                                }
  24739                                """,
  24740                                varname=name,
  24741                                srcname=srcname,
  24742                            )
  24743                        else:
  24744                            members += fill(
  24745                                """
  24746                                e->${varname}.set(${srcname}.Obj());
  24747                                """,
  24748                                varname=name,
  24749                                srcname=srcname,
  24750                            )
  24751                    else:
  24752                        members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name)
  24753                    if (
  24754                        m.type.isAny()
  24755                        or m.type.isObject()
  24756                        or m.type.isSpiderMonkeyInterface()
  24757                    ):
  24758                        holdJS = "mozilla::HoldJSObjects(e.get());\n"
  24759            iface = iface.parent
  24760 
  24761        self.body = fill(
  24762            """
  24763            RefPtr<${nativeType}> e = new ${nativeType}(aOwner);
  24764            bool trusted = e->Init(aOwner);
  24765            e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable);
  24766            $*{members}
  24767            e->SetTrusted(trusted);
  24768            e->SetComposed(${eventInit}.mComposed);
  24769            $*{holdJS}
  24770            return e.forget();
  24771            """,
  24772            nativeType=self.descriptorProvider.nativeType.split("::")[-1],
  24773            eventType=self.args[0].name,
  24774            eventInit=self.args[1].name,
  24775            members=members,
  24776            holdJS=holdJS,
  24777        )
  24778 
  24779        self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
  24780        constructorForNativeCaller = CGNativeMember.define(self, cgClass) + "\n"
  24781        self.args = list(self.originalArgs)
  24782        self.body = fill(
  24783            """
  24784            nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
  24785            return Constructor(owner, ${arg0}, ${arg1});
  24786            """,
  24787            arg0=self.args[0].name,
  24788            arg1=self.args[1].name,
  24789        )
  24790        if needCx(None, self.arguments(), [], considerTypes=True, static=True):
  24791            self.args.insert(0, Argument("JSContext*", "aCx"))
  24792        self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
  24793        return constructorForNativeCaller + CGNativeMember.define(self, cgClass)
  24794 
  24795 
  24796 class CGEventClass(CGBindingImplClass):
  24797    """
  24798    Codegen for the actual Event class implementation for this descriptor
  24799    """
  24800 
  24801    def __init__(self, descriptor):
  24802        CGBindingImplClass.__init__(
  24803            self,
  24804            descriptor,
  24805            CGEventMethod,
  24806            CGEventGetter,
  24807            CGEventSetter,
  24808            False,
  24809            "WrapObjectInternal",
  24810        )
  24811        members = []
  24812        extraMethods = []
  24813        self.membersNeedingCC = []
  24814        self.membersNeedingTrace = []
  24815 
  24816        for m in descriptor.interface.members:
  24817            if (
  24818                getattr(m, "originatingInterface", descriptor.interface)
  24819                != descriptor.interface
  24820            ):
  24821                continue
  24822 
  24823            if m.isAttr():
  24824                if m.type.isAny():
  24825                    self.membersNeedingTrace.append(m)
  24826                    # Add a getter that doesn't need a JSContext.  Note that we
  24827                    # don't need to do this if our originating interface is not
  24828                    # the descriptor's interface, because in that case we
  24829                    # wouldn't generate the getter that _does_ need a JSContext
  24830                    # either.
  24831                    extraMethods.append(
  24832                        ClassMethod(
  24833                            CGSpecializedGetterCommon.makeNativeName(descriptor, m),
  24834                            "void",
  24835                            [Argument("JS::MutableHandle<JS::Value>", "aRetVal")],
  24836                            const=True,
  24837                            body=fill(
  24838                                """
  24839                                JS::ExposeValueToActiveJS(${memberName});
  24840                                aRetVal.set(${memberName});
  24841                                """,
  24842                                memberName=CGDictionary.makeMemberName(
  24843                                    m.identifier.name
  24844                                ),
  24845                            ),
  24846                        )
  24847                    )
  24848                elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
  24849                    self.membersNeedingTrace.append(m)
  24850                elif typeNeedsRooting(m.type):
  24851                    raise TypeError(
  24852                        "Need to implement tracing for event member of type %s" % m.type
  24853                    )
  24854                elif idlTypeNeedsCycleCollection(m.type):
  24855                    self.membersNeedingCC.append(m)
  24856 
  24857                nativeType = self.getNativeTypeForIDLType(m.type).define()
  24858                members.append(
  24859                    ClassMember(
  24860                        CGDictionary.makeMemberName(m.identifier.name),
  24861                        nativeType,
  24862                        visibility="private",
  24863                        body="body",
  24864                    )
  24865                )
  24866 
  24867        parent = self.descriptor.interface.parent
  24868        self.parentType = self.descriptor.getDescriptor(
  24869            parent.identifier.name
  24870        ).nativeType.split("::")[-1]
  24871        self.nativeType = self.descriptor.nativeType.split("::")[-1]
  24872 
  24873        if self.needCC():
  24874            isupportsDecl = fill(
  24875                """
  24876                NS_DECL_ISUPPORTS_INHERITED
  24877                NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(${nativeType}, ${parentType})
  24878                """,
  24879                nativeType=self.nativeType,
  24880                parentType=self.parentType,
  24881            )
  24882        else:
  24883            isupportsDecl = fill(
  24884                """
  24885                NS_INLINE_DECL_REFCOUNTING_INHERITED(${nativeType}, ${parentType})
  24886                """,
  24887                nativeType=self.nativeType,
  24888                parentType=self.parentType,
  24889            )
  24890 
  24891        baseDeclarations = fill(
  24892            """
  24893            public:
  24894              $*{isupportsDecl}
  24895 
  24896            protected:
  24897              virtual ~${nativeType}();
  24898              explicit ${nativeType}(mozilla::dom::EventTarget* aOwner);
  24899 
  24900            """,
  24901            isupportsDecl=isupportsDecl,
  24902            nativeType=self.nativeType,
  24903            parentType=self.parentType,
  24904        )
  24905 
  24906        className = self.nativeType
  24907        asConcreteTypeMethod = ClassMethod(
  24908            "As%s" % className,
  24909            "%s*" % className,
  24910            [],
  24911            virtual=True,
  24912            body="return this;\n",
  24913            breakAfterReturnDecl=" ",
  24914            override=True,
  24915        )
  24916        extraMethods.append(asConcreteTypeMethod)
  24917 
  24918        CGClass.__init__(
  24919            self,
  24920            className,
  24921            bases=[ClassBase(self.parentType)],
  24922            methods=extraMethods + self.methodDecls,
  24923            members=members,
  24924            extradeclarations=baseDeclarations,
  24925        )
  24926 
  24927    def getWrapObjectBody(self):
  24928        return (
  24929            "return %s_Binding::Wrap(aCx, this, aGivenProto);\n" % self.descriptor.name
  24930        )
  24931 
  24932    def needCC(self):
  24933        return len(self.membersNeedingCC) != 0 or len(self.membersNeedingTrace) != 0
  24934 
  24935    def implTraverse(self):
  24936        retVal = ""
  24937        for m in self.membersNeedingCC:
  24938            retVal += (
  24939                "  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(%s)\n"
  24940                % CGDictionary.makeMemberName(m.identifier.name)
  24941            )
  24942        return retVal
  24943 
  24944    def implUnlink(self):
  24945        retVal = ""
  24946        for m in self.membersNeedingCC:
  24947            retVal += (
  24948                "  NS_IMPL_CYCLE_COLLECTION_UNLINK(%s)\n"
  24949                % CGDictionary.makeMemberName(m.identifier.name)
  24950            )
  24951        for m in self.membersNeedingTrace:
  24952            name = CGDictionary.makeMemberName(m.identifier.name)
  24953            if m.type.isAny():
  24954                retVal += "  tmp->" + name + ".setUndefined();\n"
  24955            elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
  24956                retVal += "  tmp->" + name + " = nullptr;\n"
  24957            else:
  24958                raise TypeError("Unknown traceable member type %s" % m.type)
  24959        return retVal
  24960 
  24961    def implTrace(self):
  24962        retVal = ""
  24963        for m in self.membersNeedingTrace:
  24964            retVal += (
  24965                "  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(%s)\n"
  24966                % CGDictionary.makeMemberName(m.identifier.name)
  24967            )
  24968        return retVal
  24969 
  24970    def define(self):
  24971        for m in self.membersNeedingTrace:
  24972            if not (
  24973                m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface()
  24974            ):
  24975                raise TypeError("Unknown traceable member type %s" % m.type)
  24976 
  24977        if len(self.membersNeedingTrace) > 0:
  24978            dropJS = "mozilla::DropJSObjects(this);\n"
  24979        else:
  24980            dropJS = ""
  24981        # Just override CGClass and do our own thing
  24982        ctorParams = (
  24983            "aOwner, nullptr, nullptr" if self.parentType == "Event" else "aOwner"
  24984        )
  24985 
  24986        if self.needCC():
  24987            classImpl = fill(
  24988                """
  24989 
  24990                NS_IMPL_CYCLE_COLLECTION_CLASS(${nativeType})
  24991 
  24992                NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
  24993                NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
  24994 
  24995                NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(${nativeType}, ${parentType})
  24996                $*{traverse}
  24997                NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  24998 
  24999                NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(${nativeType}, ${parentType})
  25000                $*{trace}
  25001                NS_IMPL_CYCLE_COLLECTION_TRACE_END
  25002 
  25003                NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(${nativeType}, ${parentType})
  25004                $*{unlink}
  25005                NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  25006 
  25007                NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
  25008                NS_INTERFACE_MAP_END_INHERITING(${parentType})
  25009                """,
  25010                nativeType=self.nativeType,
  25011                parentType=self.parentType,
  25012                traverse=self.implTraverse(),
  25013                unlink=self.implUnlink(),
  25014                trace=self.implTrace(),
  25015            )
  25016        else:
  25017            classImpl = ""
  25018 
  25019        classImpl += fill(
  25020            """
  25021 
  25022            ${nativeType}::${nativeType}(mozilla::dom::EventTarget* aOwner)
  25023              : ${parentType}(${ctorParams})
  25024            {
  25025            }
  25026 
  25027            ${nativeType}::~${nativeType}()
  25028            {
  25029              $*{dropJS}
  25030            }
  25031 
  25032            """,
  25033            nativeType=self.nativeType,
  25034            ctorParams=ctorParams,
  25035            parentType=self.parentType,
  25036            dropJS=dropJS,
  25037        )
  25038 
  25039        return classImpl + CGBindingImplClass.define(self)
  25040 
  25041    def getNativeTypeForIDLType(self, type):
  25042        if type.isPrimitive() and type.tag() in builtinNames:
  25043            nativeType = CGGeneric(builtinNames[type.tag()])
  25044            if type.nullable():
  25045                nativeType = CGTemplatedType("Nullable", nativeType)
  25046        elif type.isEnum():
  25047            nativeType = CGGeneric(type.unroll().inner.identifier.name)
  25048            if type.nullable():
  25049                nativeType = CGTemplatedType("Nullable", nativeType)
  25050        elif type.isJSString():
  25051            nativeType = CGGeneric("JS::Heap<JSString*>")
  25052        elif type.isDOMString() or type.isUSVString():
  25053            nativeType = CGGeneric("nsString")
  25054        elif type.isByteString() or type.isUTF8String():
  25055            nativeType = CGGeneric("nsCString")
  25056        elif type.isPromise():
  25057            nativeType = CGGeneric("RefPtr<Promise>")
  25058        elif type.isDictionary():
  25059            if typeNeedsRooting(type):
  25060                raise TypeError(
  25061                    "We don't support event members that are dictionary types "
  25062                    "that need rooting (%s)" % type
  25063                )
  25064            nativeType = CGGeneric(CGDictionary.makeDictionaryName(type.unroll().inner))
  25065            if type.nullable():
  25066                nativeType = CGTemplatedType("Nullable", nativeType)
  25067        elif type.isGeckoInterface():
  25068            iface = type.unroll().inner
  25069            nativeType = self.descriptor.getDescriptor(iface.identifier.name).nativeType
  25070            # Now trim off unnecessary namespaces
  25071            nativeType = nativeType.split("::")
  25072            if nativeType[0] == "mozilla":
  25073                nativeType.pop(0)
  25074                if nativeType[0] == "dom":
  25075                    nativeType.pop(0)
  25076            nativeType = CGWrapper(
  25077                CGGeneric("::".join(nativeType)), pre="RefPtr<", post=">"
  25078            )
  25079        elif type.isAny():
  25080            nativeType = CGGeneric("JS::Heap<JS::Value>")
  25081        elif type.isObject() or type.isSpiderMonkeyInterface():
  25082            nativeType = CGGeneric("JS::Heap<JSObject*>")
  25083        elif type.isUnion():
  25084            nativeType = CGGeneric(CGUnionStruct.unionTypeDecl(type, True))
  25085        elif type.isSequence():
  25086            if type.nullable():
  25087                innerType = type.inner.inner
  25088            else:
  25089                innerType = type.inner
  25090            if (
  25091                not innerType.isPrimitive()
  25092                and not innerType.isEnum()
  25093                and not innerType.isDOMString()
  25094                and not innerType.isByteString()
  25095                and not innerType.isUTF8String()
  25096                and not innerType.isPromise()
  25097                and not innerType.isGeckoInterface()
  25098            ):
  25099                raise TypeError(
  25100                    "Don't know how to properly manage GC/CC for "
  25101                    "event member of type %s" % type
  25102                )
  25103            nativeType = CGTemplatedType(
  25104                "nsTArray", self.getNativeTypeForIDLType(innerType)
  25105            )
  25106            if type.nullable():
  25107                nativeType = CGTemplatedType("Nullable", nativeType)
  25108        else:
  25109            raise TypeError("Don't know how to declare event member of type %s" % type)
  25110        return nativeType
  25111 
  25112 
  25113 class CGEventRoot(CGThing):
  25114    def __init__(self, config, interfaceName):
  25115        descriptor = config.getDescriptor(interfaceName)
  25116 
  25117        self.root = CGWrapper(CGEventClass(descriptor), pre="\n", post="\n")
  25118 
  25119        self.root = CGNamespace.build(["mozilla", "dom"], self.root)
  25120 
  25121        self.root = CGList(
  25122            [CGClassForwardDeclare("JSContext", isStruct=True), self.root]
  25123        )
  25124 
  25125        parent = descriptor.interface.parent.identifier.name
  25126 
  25127        # Throw in our #includes
  25128        self.root = CGHeaders(
  25129            [descriptor],
  25130            [],
  25131            [],
  25132            [],
  25133            [
  25134                config.getDescriptor(parent).headerFile,
  25135                "mozilla/Attributes.h",
  25136                "mozilla/dom/%sBinding.h" % interfaceName,
  25137                "mozilla/dom/BindingUtils.h",
  25138            ],
  25139            [
  25140                "%s.h" % interfaceName,
  25141                "js/GCAPI.h",
  25142                "mozilla/HoldDropJSObjects.h",
  25143                "mozilla/dom/Nullable.h",
  25144            ],
  25145            "",
  25146            self.root,
  25147            config,
  25148        )
  25149 
  25150        # And now some include guards
  25151        self.root = CGIncludeGuard(interfaceName, self.root)
  25152 
  25153        self.root = CGWrapper(
  25154            self.root,
  25155            pre=(
  25156                AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT
  25157                % os.path.basename(descriptor.interface.filename)
  25158            ),
  25159        )
  25160 
  25161        self.root = CGWrapper(
  25162            self.root,
  25163            pre=dedent(
  25164                """
  25165            /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  25166            /* vim:set ts=2 sw=2 sts=2 et cindent: */
  25167            /* This Source Code Form is subject to the terms of the Mozilla Public
  25168             * License, v. 2.0. If a copy of the MPL was not distributed with this
  25169             * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  25170 
  25171            """
  25172            ),
  25173        )
  25174 
  25175    def declare(self):
  25176        return self.root.declare()
  25177 
  25178    def define(self):
  25179        return self.root.define()