dom/bindings/Codegen.py
author Oana Pop Rus <opoprus@mozilla.com>
Mon, 12 Aug 2019 13:37:03 +0300
changeset 487436 9f551fcee77ecd379e7df7d85102f72b6e9ec8ab
parent 487433 9fd7bea2b512cce296dd2d93f024143d40b9d2af
child 487443 65655ca92cb8d1f14248416181911a3441e607e1
permissions -rw-r--r--
Backed out 4 changesets (bug 1572782) for build bustages at build/src/obj-firefox/dist/include/nsIXPCScriptable.h on a CLOSED TREE Backed out changeset ec9d15c69bc8 (bug 1572782) Backed out changeset 8239e4baa0f4 (bug 1572782) Backed out changeset 9fd7bea2b512 (bug 1572782) Backed out changeset 11d750555fe1 (bug 1572782)

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

# Common codegen classes.

import os
import re
import string
import math
import textwrap
import functools

from perfecthash import PerfectHash

from WebIDL import BuiltinTypes, IDLBuiltinType, IDLDefaultDictionaryValue, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute, IDLInterfaceMember, IDLUndefinedValue, IDLEmptySequenceValue, IDLDictionary
from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, getAllTypes, Descriptor, MemberIsUnforgeable, iteratorNativeType

AUTOGENERATED_WARNING_COMMENT = \
    "/* THIS FILE IS AUTOGENERATED BY Codegen.py - DO NOT EDIT */\n\n"
AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT = \
    "/* THIS FILE IS AUTOGENERATED FROM %s BY Codegen.py - DO NOT EDIT */\n\n"
ADDPROPERTY_HOOK_NAME = '_addProperty'
FINALIZE_HOOK_NAME = '_finalize'
OBJECT_MOVED_HOOK_NAME = '_objectMoved'
CONSTRUCT_HOOK_NAME = '_constructor'
LEGACYCALLER_HOOK_NAME = '_legacycaller'
RESOLVE_HOOK_NAME = '_resolve'
MAY_RESOLVE_HOOK_NAME = '_mayResolve'
NEW_ENUMERATE_HOOK_NAME = '_newEnumerate'
ENUM_ENTRY_VARIABLE_NAME = 'strings'
INSTANCE_RESERVED_SLOTS = 1

# This size is arbitrary. It is a power of 2 to make using it as a modulo
# operand cheap, and is usually around 1/3-1/5th of the set size (sometimes
# smaller for very large sets).
GLOBAL_NAMES_PHF_SIZE=256


def memberReservedSlot(member, descriptor):
    return ("(DOM_INSTANCE_RESERVED_SLOTS + %d)" %
            member.slotIndices[descriptor.interface.identifier.name])


def memberXrayExpandoReservedSlot(member, descriptor):
    return ("(xpc::JSSLOT_EXPANDO_COUNT + %d)" %
            member.slotIndices[descriptor.interface.identifier.name])


def mayUseXrayExpandoSlots(descriptor, attr):
    assert not attr.getExtendedAttribute("NewObject")
    # For attributes whose type is a Gecko interface we always use
    # slots on the reflector for caching.  Also, for interfaces that
    # don't want Xrays we obviously never use the Xray expando slot.
    return descriptor.wantsXrays and not attr.type.isGeckoInterface()


def toStringBool(arg):
    """
    Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false)
    """
    return str(not not arg).lower()


def toBindingNamespace(arg):
    return arg + "_Binding"


def isTypeCopyConstructible(type):
    # Nullable and sequence stuff doesn't affect copy-constructibility
    type = type.unroll()
    return (type.isPrimitive() or type.isString() or type.isEnum() or
            (type.isUnion() and
             CGUnionStruct.isUnionCopyConstructible(type)) or
            (type.isDictionary() and
             CGDictionary.isDictionaryCopyConstructible(type.inner)) or
            # Interface types are only copy-constructible if they're Gecko
            # interfaces.  SpiderMonkey interfaces are not copy-constructible
            # because of rooting issues.
            (type.isInterface() and type.isGeckoInterface()))


class CycleCollectionUnsupported(TypeError):
    def __init__(self, message):
        TypeError.__init__(self, message)


def idlTypeNeedsCycleCollection(type):
    type = type.unroll()  # Takes care of sequences and nullables
    if ((type.isPrimitive() and type.tag() in builtinNames) or
        type.isEnum() or
        type.isString() or
        type.isAny() or
        type.isObject() or
        type.isSpiderMonkeyInterface()):
        return False
    elif type.isCallback() or type.isPromise() or type.isGeckoInterface():
        return True
    elif type.isUnion():
        return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
    elif type.isRecord():
        if idlTypeNeedsCycleCollection(type.inner):
            raise CycleCollectionUnsupported("Cycle collection for type %s is not supported" %
                                             type)
        return False
    elif type.isDictionary():
        return CGDictionary.dictionaryNeedsCycleCollection(type.inner)
    else:
        raise CycleCollectionUnsupported("Don't know whether to cycle-collect type %s" % type)


# TryPreserveWrapper uses the addProperty hook to preserve the wrapper of
# non-nsISupports cycle collected objects, so if wantsAddProperty is changed
# to not cover that case then TryPreserveWrapper will need to be changed.
def wantsAddProperty(desc):
    return (desc.concrete and desc.wrapperCache and not desc.isGlobal())


# We'll want to insert the indent at the beginnings of lines, but we
# don't want to indent empty lines.  So only indent lines that have a
# non-newline character on them.
lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)


def indent(s, indentLevel=2):
    """
    Indent C++ code.

    Weird secret feature: this doesn't indent lines that start with # (such as
    #include lines or #ifdef/#endif).
    """
    if s == "":
        return s
    return re.sub(lineStartDetector, indentLevel * " ", s)


# dedent() and fill() are often called on the same string multiple
# times.  We want to memoize their return values so we don't keep
# recomputing them all the time.
def memoize(fn):
    """
    Decorator to memoize a function of one argument.  The cache just
    grows without bound.
    """
    cache = {}

    @functools.wraps(fn)
    def wrapper(arg):
        retval = cache.get(arg)
        if retval is None:
            retval = cache[arg] = fn(arg)
        return retval
    return wrapper


@memoize
def dedent(s):
    """
    Remove all leading whitespace from s, and remove a blank line
    at the beginning.
    """
    if s.startswith('\n'):
        s = s[1:]
    return textwrap.dedent(s)


# This works by transforming the fill()-template to an equivalent
# string.Template.
fill_multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?")


find_substitutions = re.compile(r"\${")


@memoize
def compile_fill_template(template):
    """
    Helper function for fill().  Given the template string passed to fill(),
    do the reusable part of template processing and return a pair (t,
    argModList) that can be used every time fill() is called with that
    template argument.

    argsModList is list of tuples that represent modifications to be
    made to args.  Each modification has, in order: i) the arg name,
    ii) the modified name, iii) the indent depth.
    """
    t = dedent(template)
    assert t.endswith("\n") or "\n" not in t
    argModList = []

    def replace(match):
        """
        Replaces a line like '  $*{xyz}\n' with '${xyz_n}',
        where n is the indent depth, and add a corresponding entry to
        argModList.

        Note that this needs to close over argModList, so it has to be
        defined inside compile_fill_template().
        """
        indentation, name, nl = match.groups()
        depth = len(indentation)

        # Check that $*{xyz} appears by itself on a line.
        prev = match.string[:match.start()]
        if (prev and not prev.endswith("\n")) or nl is None:
            raise ValueError("Invalid fill() template: $*{%s} must appear by itself on a line" % name)

        # Now replace this whole line of template with the indented equivalent.
        modified_name = name + "_" + str(depth)
        argModList.append((name, modified_name, depth))
        return "${" + modified_name + "}"

    t = re.sub(fill_multiline_substitution_re, replace, t)
    if not re.search(find_substitutions, t):
        raise TypeError("Using fill() when dedent() would do.")
    return (string.Template(t), argModList)


def fill(template, **args):
    """
    Convenience function for filling in a multiline template.

    `fill(template, name1=v1, name2=v2)` is a lot like
    `string.Template(template).substitute({"name1": v1, "name2": v2})`.

    However, it's shorter, and has a few nice features:

      * If `template` is indented, fill() automatically dedents it!
        This makes code using fill() with Python's multiline strings
        much nicer to look at.

      * If `template` starts with a blank line, fill() strips it off.
        (Again, convenient with multiline strings.)

      * fill() recognizes a special kind of substitution
        of the form `$*{name}`.

        Use this to paste in, and automatically indent, multiple lines.
        (Mnemonic: The `*` is for "multiple lines").

        A `$*` substitution must appear by itself on a line, with optional
        preceding indentation (spaces only). The whole line is replaced by the
        corresponding keyword argument, indented appropriately.  If the
        argument is an empty string, no output is generated, not even a blank
        line.
    """

    t, argModList = compile_fill_template(template)
    # Now apply argModList to args
    for (name, modified_name, depth) in argModList:
        if not (args[name] == "" or args[name].endswith("\n")):
            raise ValueError("Argument %s with value %r is missing a newline" % (name, args[name]))
        args[modified_name] = indent(args[name], depth)

    return t.substitute(args)


class CGThing():
    """
    Abstract base class for things that spit out code.
    """
    def __init__(self):
        pass  # Nothing for now

    def declare(self):
        """Produce code for a header file."""
        assert False  # Override me!

    def define(self):
        """Produce code for a cpp file."""
        assert False  # Override me!

    def deps(self):
        """Produce the deps for a pp file"""
        assert False  # Override me!


class CGStringTable(CGThing):
    """
    Generate a string table for the given strings with a function accessor:

    const char *accessorName(unsigned int index) {
      static const char table[] = "...";
      static const uint16_t indices = { ... };
      return &table[indices[index]];
    }

    This is more efficient than the more natural:

    const char *table[] = {
      ...
    };

    The uint16_t indices are smaller than the pointer equivalents, and the
    string table requires no runtime relocations.
    """
    def __init__(self, accessorName, strings, static=False):
        CGThing.__init__(self)
        self.accessorName = accessorName
        self.strings = strings
        self.static = static

    def declare(self):
        if self.static:
            return ""
        return "extern const char *%s(unsigned int aIndex);\n" % self.accessorName

    def define(self):
        table = ' "\\0" '.join('"%s"' % s for s in self.strings)
        indices = []
        currentIndex = 0
        for s in self.strings:
            indices.append(currentIndex)
            currentIndex += len(s) + 1  # for the null terminator
        return fill(
            """
            ${static}const char *${name}(unsigned int aIndex)
            {
              static const char table[] = ${table};
              static const uint16_t indices[] = { ${indices} };
              static_assert(${currentIndex} <= UINT16_MAX, "string table overflow!");
              return &table[indices[aIndex]];
            }
            """,
            static="static " if self.static else "",
            name=self.accessorName,
            table=table,
            indices=", ".join("%d" % index for index in indices),
            currentIndex=currentIndex)


class CGNativePropertyHooks(CGThing):
    """
    Generate a NativePropertyHooks for a given descriptor
    """
    def __init__(self, descriptor, properties):
        CGThing.__init__(self)
        self.descriptor = descriptor
        self.properties = properties

    def declare(self):
        if not self.descriptor.wantsXrays:
            return ""
        return dedent("""
            // We declare this as an array so that retrieving a pointer to this
            // binding's property hooks only requires compile/link-time resolvable
            // address arithmetic.  Declaring it as a pointer instead would require
            // doing a run-time load to fetch a pointer to this binding's property
            // hooks.  And then structures which embedded a pointer to this structure
            // would require a run-time load for proper initialization, which would
            // then induce static constructors.  Lots of static constructors.
            extern const NativePropertyHooks sNativePropertyHooks[];
            """)

    def define(self):
        if not self.descriptor.wantsXrays:
            return ""
        deleteNamedProperty = "nullptr"
        if (self.descriptor.concrete and
            self.descriptor.proxy and
            not self.descriptor.isMaybeCrossOriginObject()):
            resolveOwnProperty = "ResolveOwnProperty"
            enumerateOwnProperties = "EnumerateOwnProperties"
            if self.descriptor.needsXrayNamedDeleterHook():
                deleteNamedProperty = "DeleteNamedProperty"
        elif self.descriptor.needsXrayResolveHooks():
            resolveOwnProperty = "ResolveOwnPropertyViaResolve"
            enumerateOwnProperties = "EnumerateOwnPropertiesViaGetOwnPropertyNames"
        else:
            resolveOwnProperty = "nullptr"
            enumerateOwnProperties = "nullptr"
        if self.properties.hasNonChromeOnly():
            regular = "sNativeProperties.Upcast()"
        else:
            regular = "nullptr"
        if self.properties.hasChromeOnly():
            chrome = "sChromeOnlyNativeProperties.Upcast()"
        else:
            chrome = "nullptr"
        constructorID = "constructors::id::"
        if self.descriptor.interface.hasInterfaceObject():
            constructorID += self.descriptor.name
        else:
            constructorID += "_ID_Count"
        prototypeID = "prototypes::id::"
        if self.descriptor.interface.hasInterfacePrototypeObject():
            prototypeID += self.descriptor.name
        else:
            prototypeID += "_ID_Count"
        parentProtoName = self.descriptor.parentPrototypeName
        parentHooks = (toBindingNamespace(parentProtoName) + "::sNativePropertyHooks"
                       if parentProtoName else 'nullptr')

        if self.descriptor.wantsXrayExpandoClass:
            expandoClass = "&sXrayExpandoObjectClass"
        else:
            expandoClass = "&DefaultXrayExpandoObjectClass"

        return fill(
            """
            const NativePropertyHooks sNativePropertyHooks[] = { {
              ${resolveOwnProperty},
              ${enumerateOwnProperties},
              ${deleteNamedProperty},
              { ${regular}, ${chrome} },
              ${prototypeID},
              ${constructorID},
              ${parentHooks},
              ${expandoClass}
            } };
            """,
            resolveOwnProperty=resolveOwnProperty,
            enumerateOwnProperties=enumerateOwnProperties,
            deleteNamedProperty=deleteNamedProperty,
            regular=regular,
            chrome=chrome,
            prototypeID=prototypeID,
            constructorID=constructorID,
            parentHooks=parentHooks,
            expandoClass=expandoClass)


def NativePropertyHooks(descriptor):
    return "&sEmptyNativePropertyHooks" if not descriptor.wantsXrays else "sNativePropertyHooks"


def DOMClass(descriptor):
    protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeNameChain]
    # Pad out the list to the right length with _ID_Count so we
    # guarantee that all the lists are the same length.  _ID_Count
    # is never the ID of any prototype, so it's safe to use as
    # padding.
    protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))

    if descriptor.interface.isSerializable():
        serializer = "Serialize"
    else:
        serializer = "nullptr"

    return fill(
        """
          { ${protoChain} },
          IsBaseOf<nsISupports, ${nativeType} >::value,
          ${hooks},
          FindAssociatedGlobalForNative<${nativeType}>::Get,
          GetProtoObjectHandle,
          GetCCParticipant<${nativeType}>::Get(),
          ${serializer}
        """,
        protoChain=', '.join(protoList),
        nativeType=descriptor.nativeType,
        hooks=NativePropertyHooks(descriptor),
        serializer=serializer)


def InstanceReservedSlots(descriptor):
    slots = INSTANCE_RESERVED_SLOTS + descriptor.interface.totalMembersInSlots
    if descriptor.isMaybeCrossOriginObject():
        # We need a slot for the cross-origin holder too.
        if descriptor.interface.hasChildInterfaces():
            raise TypeError("We don't support non-leaf cross-origin interfaces "
                            "like %s" % descriptor.interface.identifier.name)
        slots += 1
    return slots


class CGDOMJSClass(CGThing):
    """
    Generate a DOMJSClass for a given descriptor
    """
    def __init__(self, descriptor):
        CGThing.__init__(self)
        self.descriptor = descriptor

    def declare(self):
        return ""

    def define(self):
        callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
        objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
        slotCount = InstanceReservedSlots(self.descriptor)
        classFlags = "JSCLASS_IS_DOMJSCLASS | JSCLASS_FOREGROUND_FINALIZE | "
        if self.descriptor.isGlobal():
            classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)"
            traceHook = "JS_GlobalObjectTraceHook"
            reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
        else:
            classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
            traceHook = 'nullptr'
            reservedSlots = slotCount
        if self.descriptor.interface.hasProbablyShortLivingWrapper():
            if not self.descriptor.wrapperCache:
                raise TypeError("Need a wrapper cache to support nursery "
                                "allocation of DOM objects")
            classFlags += " | JSCLASS_SKIP_NURSERY_FINALIZE"

        if self.descriptor.interface.getExtendedAttribute("NeedResolve"):
            resolveHook = RESOLVE_HOOK_NAME
            mayResolveHook = MAY_RESOLVE_HOOK_NAME
            newEnumerateHook = NEW_ENUMERATE_HOOK_NAME
        elif self.descriptor.isGlobal():
            resolveHook = "mozilla::dom::ResolveGlobal"
            mayResolveHook = "mozilla::dom::MayResolveGlobal"
            newEnumerateHook = "mozilla::dom::EnumerateGlobal"
        else:
            resolveHook = "nullptr"
            mayResolveHook = "nullptr"
            newEnumerateHook = "nullptr"

        return fill(
            """
            static const js::ClassOps sClassOps = {
              ${addProperty}, /* addProperty */
              nullptr,               /* delProperty */
              nullptr,               /* enumerate */
              ${newEnumerate}, /* newEnumerate */
              ${resolve}, /* resolve */
              ${mayResolve}, /* mayResolve */
              ${finalize}, /* finalize */
              ${call}, /* call */
              nullptr,               /* hasInstance */
              nullptr,               /* construct */
              ${trace}, /* trace */
            };

            static const js::ClassExtension sClassExtension = {
              ${objectMoved} /* objectMovedOp */
            };

            static const DOMJSClass sClass = {
              { "${name}",
                ${flags},
                &sClassOps,
                JS_NULL_CLASS_SPEC,
                &sClassExtension,
                JS_NULL_OBJECT_OPS
              },
              $*{descriptor}
            };
            static_assert(${instanceReservedSlots} == DOM_INSTANCE_RESERVED_SLOTS,
                          "Must have the right minimal number of reserved slots.");
            static_assert(${reservedSlots} >= ${slotCount},
                          "Must have enough reserved slots.");
            """,
            name=self.descriptor.interface.getClassName(),
            flags=classFlags,
            addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'nullptr',
            newEnumerate=newEnumerateHook,
            resolve=resolveHook,
            mayResolve=mayResolveHook,
            finalize=FINALIZE_HOOK_NAME,
            call=callHook,
            trace=traceHook,
            objectMoved=objectMovedHook,
            descriptor=DOMClass(self.descriptor),
            instanceReservedSlots=INSTANCE_RESERVED_SLOTS,
            reservedSlots=reservedSlots,
            slotCount=slotCount)


class CGDOMProxyJSClass(CGThing):
    """
    Generate a DOMJSClass for a given proxy descriptor
    """
    def __init__(self, descriptor):
        CGThing.__init__(self)
        self.descriptor = descriptor

    def declare(self):
        return ""

    def define(self):
        slotCount = InstanceReservedSlots(self.descriptor)
        # We need one reserved slot (DOM_OBJECT_SLOT).
        flags = ["JSCLASS_IS_DOMJSCLASS",
                 "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount]
        # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
        # we don't want people ever adding that to any interface other than
        # HTMLAllCollection.  So just hardcode it here.
        if self.descriptor.interface.identifier.name == "HTMLAllCollection":
            flags.append("JSCLASS_EMULATES_UNDEFINED")
        return fill(
            """
            static const DOMJSClass sClass = {
              PROXY_CLASS_DEF("${name}",
                              ${flags}),
              $*{descriptor}
            };
            """,
            name=self.descriptor.interface.identifier.name,
            flags=" | ".join(flags),
            descriptor=DOMClass(self.descriptor))


class CGXrayExpandoJSClass(CGThing):
    """
    Generate a JSClass for an Xray expando object.  This is only
    needed if we have members in slots (for [Cached] or [StoreInSlot]
    stuff).
    """
    def __init__(self, descriptor):
        assert descriptor.interface.totalMembersInSlots != 0
        assert descriptor.wantsXrays
        assert descriptor.wantsXrayExpandoClass
        CGThing.__init__(self)
        self.descriptor = descriptor;

    def declare(self):
        return ""

    def define(self):
        return fill(
            """
            // This may allocate too many slots, because we only really need
            // slots for our non-interface-typed members that we cache.  But
            // allocating slots only for those would make the slot index
            // computations much more complicated, so let's do this the simple
            // way for now.
            DEFINE_XRAY_EXPANDO_CLASS(static, sXrayExpandoObjectClass, ${memberSlots});
            """,
            memberSlots=self.descriptor.interface.totalMembersInSlots)


def PrototypeIDAndDepth(descriptor):
    prototypeID = "prototypes::id::"
    if descriptor.interface.hasInterfacePrototypeObject():
        prototypeID += descriptor.interface.identifier.name
        depth = "PrototypeTraits<%s>::Depth" % prototypeID
    else:
        prototypeID += "_ID_Count"
        depth = "0"
    return (prototypeID, depth)


def InterfacePrototypeObjectProtoGetter(descriptor):
    """
    Returns a tuple with two elements:

        1) The name of the function to call to get the prototype to use for the
           interface prototype object as a JSObject*.

        2) The name of the function to call to get the prototype to use for the
           interface prototype object as a JS::Handle<JSObject*> or None if no
           such function exists.
    """
    parentProtoName = descriptor.parentPrototypeName
    if descriptor.hasNamedPropertiesObject:
        protoGetter = "GetNamedPropertiesObject"
        protoHandleGetter = None
    elif parentProtoName is None:
        if descriptor.interface.getExtendedAttribute("ExceptionClass"):
            protoGetter = "JS::GetRealmErrorPrototype"
        elif descriptor.interface.isIteratorInterface():
            protoGetter = "JS::GetRealmIteratorPrototype"
        else:
            protoGetter = "JS::GetRealmObjectPrototype"
        protoHandleGetter = None
    else:
        prefix = toBindingNamespace(parentProtoName)
        protoGetter = prefix + "::GetProtoObject"
        protoHandleGetter = prefix + "::GetProtoObjectHandle"

    return (protoGetter, protoHandleGetter)


class CGPrototypeJSClass(CGThing):
    def __init__(self, descriptor, properties):
        CGThing.__init__(self)
        self.descriptor = descriptor
        self.properties = properties

    def declare(self):
        # We're purely for internal consumption
        return ""

    def define(self):
        prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
        slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
        # Globals handle unforgeables directly in Wrap() instead of
        # via a holder.
        if (self.descriptor.hasUnforgeableMembers and
            not self.descriptor.isGlobal()):
            slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */"
        (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor)
        type = "eGlobalInterfacePrototype" if self.descriptor.isGlobal() else "eInterfacePrototype"
        return fill(
            """
            static const DOMIfaceAndProtoJSClass sPrototypeClass = {
              {
                "${name}Prototype",
                JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                JS_NULL_CLASS_OPS,
                JS_NULL_CLASS_SPEC,
                JS_NULL_CLASS_EXT,
                JS_NULL_OBJECT_OPS
              },
              ${type},
              false,
              ${prototypeID},
              ${depth},
              ${hooks},
              "[object ${name}Prototype]",
              ${protoGetter}
            };
            """,
            name=self.descriptor.interface.getClassName(),
            slotCount=slotCount,
            type=type,
            hooks=NativePropertyHooks(self.descriptor),
            prototypeID=prototypeID,
            depth=depth,
            protoGetter=protoGetter)


def InterfaceObjectProtoGetter(descriptor, forXrays=False):
    """
    Returns a tuple with two elements:

        1) The name of the function to call to get the prototype to use for the
           interface object as a JSObject*.

        2) The name of the function to call to get the prototype to use for the
           interface prototype as a JS::Handle<JSObject*> or None if no such
           function exists.
    """
    parentInterface = descriptor.interface.parent
    if parentInterface:
        assert not descriptor.interface.isNamespace()
        parentIfaceName = parentInterface.identifier.name
        parentDesc = descriptor.getDescriptor(parentIfaceName)
        prefix = toBindingNamespace(parentDesc.name)
        protoGetter = prefix + "::GetConstructorObject"
        protoHandleGetter = prefix + "::GetConstructorObjectHandle"
    elif descriptor.interface.isNamespace():
        if (forXrays or
            not descriptor.interface.getExtendedAttribute("ProtoObjectHack")):
            protoGetter = "JS::GetRealmObjectPrototype"
        else:
            protoGetter = "GetHackedNamespaceProtoObject"
        protoHandleGetter = None
    else:
        protoGetter = "JS::GetRealmFunctionPrototype"
        protoHandleGetter = None
    return (protoGetter, protoHandleGetter)


class CGInterfaceObjectJSClass(CGThing):
    def __init__(self, descriptor, properties):
        CGThing.__init__(self)
        self.descriptor = descriptor
        self.properties = properties

    def declare(self):
        # We're purely for internal consumption
        return ""

    def define(self):
        if self.descriptor.interface.ctor():
            assert not self.descriptor.interface.isNamespace()
            ctorname = CONSTRUCT_HOOK_NAME
        elif self.descriptor.interface.isNamespace():
            ctorname = "nullptr"
        else:
            ctorname = "ThrowingConstructor"
        needsHasInstance = self.descriptor.interface.hasInterfacePrototypeObject()

        prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
        slotCount = "DOM_INTERFACE_SLOTS_BASE"
        if len(self.descriptor.interface.namedConstructors) > 0:
            slotCount += (" + %i /* slots for the named constructors */" %
                          len(self.descriptor.interface.namedConstructors))
        (protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor,
                                                      forXrays=True)

        if ctorname == "ThrowingConstructor":
            ret = ""
            classOpsPtr = "&sBoringInterfaceObjectClassClassOps"
        elif ctorname == "nullptr":
            ret = ""
            classOpsPtr = "JS_NULL_CLASS_OPS"
        else:
            ret = fill(
                """
                static const js::ClassOps sInterfaceObjectClassOps = {
                    nullptr,               /* addProperty */
                    nullptr,               /* delProperty */
                    nullptr,               /* enumerate */
                    nullptr,               /* newEnumerate */
                    nullptr,               /* resolve */
                    nullptr,               /* mayResolve */
                    nullptr,               /* finalize */
                    ${ctorname}, /* call */
                    nullptr,               /* hasInstance */
                    ${ctorname}, /* construct */
                    nullptr,               /* trace */
                };

                """,
                ctorname=ctorname)
            classOpsPtr = "&sInterfaceObjectClassOps"

        if self.descriptor.interface.isNamespace():
            classString = self.descriptor.interface.getExtendedAttribute("ClassString")
            if classString is None:
                classString = "Object"
            else:
                classString = classString[0]
            toStringResult = "[object %s]" % classString
            objectOps = "JS_NULL_OBJECT_OPS"
        else:
            classString = "Function"
            toStringResult = ("function %s() {\\n    [native code]\\n}" %
                              self.descriptor.interface.identifier.name)
            # We need non-default ObjectOps so we can actually make
            # use of our toStringResult.
            objectOps = "&sInterfaceObjectClassObjectOps"

        ret = ret + fill(
            """
            static const DOMIfaceAndProtoJSClass sInterfaceObjectClass = {
              {
                "${classString}",
                JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                ${classOpsPtr},
                JS_NULL_CLASS_SPEC,
                JS_NULL_CLASS_EXT,
                ${objectOps}
              },
              eInterface,
              ${needsHasInstance},
              ${prototypeID},
              ${depth},
              ${hooks},
              "${toStringResult}",
              ${protoGetter}
            };
            """,
            classString=classString,
            slotCount=slotCount,
            classOpsPtr=classOpsPtr,
            hooks=NativePropertyHooks(self.descriptor),
            objectOps=objectOps,
            needsHasInstance=toStringBool(needsHasInstance),
            prototypeID=prototypeID,
            depth=depth,
            toStringResult=toStringResult,
            protoGetter=protoGetter)
        return ret

class CGList(CGThing):
    """
    Generate code for a list of GCThings.  Just concatenates them together, with
    an optional joiner string.  "\n" is a common joiner.
    """
    def __init__(self, children, joiner=""):
        CGThing.__init__(self)
        # Make a copy of the kids into a list, because if someone passes in a
        # generator we won't be able to both declare and define ourselves, or
        # define ourselves more than once!
        self.children = list(children)
        self.joiner = joiner

    def append(self, child):
        self.children.append(child)

    def prepend(self, child):
        self.children.insert(0, child)

    def extend(self, kids):
        self.children.extend(kids)

    def join(self, iterable):
        return self.joiner.join(s for s in iterable if len(s) > 0)

    def declare(self):
        return self.join(child.declare() for child in self.children if child is not None)

    def define(self):
        return self.join(child.define() for child in self.children if child is not None)

    def deps(self):
        deps = set()
        for child in self.children:
            if child is None:
                continue
            deps = deps.union(child.deps())
        return deps

    def __len__(self):
        return len(self.children)


class CGGeneric(CGThing):
    """
    A class that spits out a fixed string into the codegen.  Can spit out a
    separate string for the declaration too.
    """
    def __init__(self, define="", declare=""):
        self.declareText = declare
        self.defineText = define

    def declare(self):
        return self.declareText

    def define(self):
        return self.defineText

    def deps(self):
        return set()


class CGIndenter(CGThing):
    """
    A class that takes another CGThing and generates code that indents that
    CGThing by some number of spaces.  The default indent is two spaces.
    """
    def __init__(self, child, indentLevel=2, declareOnly=False):
        assert isinstance(child, CGThing)
        CGThing.__init__(self)
        self.child = child
        self.indentLevel = indentLevel
        self.declareOnly = declareOnly

    def declare(self):
        return indent(self.child.declare(), self.indentLevel)

    def define(self):
        defn = self.child.define()
        if self.declareOnly:
            return defn
        else:
            return indent(defn, self.indentLevel)


class CGWrapper(CGThing):
    """
    Generic CGThing that wraps other CGThings with pre and post text.
    """
    def __init__(self, child, pre="", post="", declarePre=None,
                 declarePost=None, definePre=None, definePost=None,
                 declareOnly=False, defineOnly=False, reindent=False):
        CGThing.__init__(self)
        self.child = child
        self.declarePre = declarePre or pre
        self.declarePost = declarePost or post
        self.definePre = definePre or pre
        self.definePost = definePost or post
        self.declareOnly = declareOnly
        self.defineOnly = defineOnly
        self.reindent = reindent

    def declare(self):
        if self.defineOnly:
            return ''
        decl = self.child.declare()
        if self.reindent:
            decl = self.reindentString(decl, self.declarePre)
        return self.declarePre + decl + self.declarePost

    def define(self):
        if self.declareOnly:
            return ''
        defn = self.child.define()
        if self.reindent:
            defn = self.reindentString(defn, self.definePre)
        return self.definePre + defn + self.definePost

    @staticmethod
    def reindentString(stringToIndent, widthString):
        # We don't use lineStartDetector because we don't want to
        # insert whitespace at the beginning of our _first_ line.
        # Use the length of the last line of width string, in case
        # it is a multiline string.
        lastLineWidth = len(widthString.splitlines()[-1])
        return stripTrailingWhitespace(
            stringToIndent.replace("\n", "\n" + (" " * lastLineWidth)))

    def deps(self):
        return self.child.deps()


class CGIfWrapper(CGList):
    def __init__(self, child, condition):
        CGList.__init__(self, [
            CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True),
            CGIndenter(child),
            CGGeneric("}\n")
        ])


class CGIfElseWrapper(CGList):
    def __init__(self, condition, ifTrue, ifFalse):
        CGList.__init__(self, [
            CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True),
            CGIndenter(ifTrue),
            CGGeneric("} else {\n"),
            CGIndenter(ifFalse),
            CGGeneric("}\n")
        ])


class CGElseChain(CGThing):
    """
    Concatenate if statements in an if-else-if-else chain.
    """
    def __init__(self, children):
        self.children = [c for c in children if c is not None]

    def declare(self):
        assert False

    def define(self):
        if not self.children:
            return ""
        s = self.children[0].define()
        assert s.endswith("\n")
        for child in self.children[1:]:
            code = child.define()
            assert code.startswith("if") or code.startswith("{")
            assert code.endswith("\n")
            s = s.rstrip() + " else " + code
        return s


class CGTemplatedType(CGWrapper):
    def __init__(self, templateName, child, isConst=False, isReference=False):
        if isinstance(child, list):
            child = CGList(child, ", ")
        const = "const " if isConst else ""
        pre = "%s%s<" % (const, templateName)
        ref = "&" if isReference else ""
        post = ">%s" % ref
        CGWrapper.__init__(self, child, pre=pre, post=post)


class CGNamespace(CGWrapper):
    def __init__(self, namespace, child, declareOnly=False):
        pre = "namespace %s {\n" % namespace
        post = "} // namespace %s\n" % namespace
        CGWrapper.__init__(self, child, pre=pre, post=post,
                           declareOnly=declareOnly)

    @staticmethod
    def build(namespaces, child, declareOnly=False):
        """
        Static helper method to build multiple wrapped namespaces.
        """
        if not namespaces:
            return CGWrapper(child, declareOnly=declareOnly)
        inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly)
        return CGNamespace(namespaces[0], inner, declareOnly=declareOnly)


class CGIncludeGuard(CGWrapper):
    """
    Generates include guards for a header.
    """
    def __init__(self, prefix, child):
        """|prefix| is the filename without the extension."""
        define = 'mozilla_dom_%s_h' % prefix
        CGWrapper.__init__(self, child,
                           declarePre='#ifndef %s\n#define %s\n\n' % (define, define),
                           declarePost='\n#endif // %s\n' % define)


class CGHeaders(CGWrapper):
    """
    Generates the appropriate include statements.
    """
    def __init__(self, descriptors, dictionaries, callbacks,
                 callbackDescriptors,
                 declareIncludes, defineIncludes, prefix, child,
                 config=None, jsImplementedDescriptors=[]):
        """
        Builds a set of includes to cover |descriptors|.

        Also includes the files in |declareIncludes| in the header
        file and the files in |defineIncludes| in the .cpp.

        |prefix| contains the basename of the file that we generate include
        statements for.
        """

        # Determine the filenames for which we need headers.
        interfaceDeps = [d.interface for d in descriptors]
        ancestors = []
        for iface in interfaceDeps:
            if iface.parent:
                # We're going to need our parent's prototype, to use as the
                # prototype of our prototype object.
                ancestors.append(iface.parent)
                # And if we have an interface object, we'll need the nearest
                # ancestor with an interface object too, so we can use its
                # interface object as the proto of our interface object.
                if iface.hasInterfaceObject():
                    parent = iface.parent
                    while parent and not parent.hasInterfaceObject():
                        parent = parent.parent
                    if parent:
                        ancestors.append(parent)
        interfaceDeps.extend(ancestors)

        # Include parent interface headers needed for default toJSON code.
        jsonInterfaceParents = []
        for desc in descriptors:
            if not desc.hasDefaultToJSON:
                continue
            parent = desc.interface.parent
            while parent:
                parentDesc = desc.getDescriptor(parent.identifier.name)
                if parentDesc.hasDefaultToJSON:
                    jsonInterfaceParents.append(parentDesc.interface)
                parent = parent.parent
        interfaceDeps.extend(jsonInterfaceParents)

        bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)

        # Grab all the implementation declaration files we need.
        implementationIncludes = set(d.headerFile for d in descriptors if d.needsHeaderInclude())

        # Grab the includes for checking hasInstance
        interfacesImplementingSelf = set()
        for d in descriptors:
            interfacesImplementingSelf |= d.interface.interfacesImplementingSelf
        implementationIncludes |= set(self.getDeclarationFilename(i) for i in
                                      interfacesImplementingSelf)

        # Now find all the things we'll need as arguments because we
        # need to wrap or unwrap them.
        bindingHeaders = set()
        declareIncludes = set(declareIncludes)

        def addHeadersForType(typeAndPossibleDictionary):
            """
            Add the relevant headers for this type.  We use dictionary, if
            passed, to decide what to do with interface types.
            """
            t, dictionary = typeAndPossibleDictionary
            # Dictionaries have members that need to be actually
            # declared, not just forward-declared.
            if dictionary:
                headerSet = declareIncludes
            else:
                headerSet = bindingHeaders
            # Strip off outer layers and add headers they might (conservatively:
            # only nullable non-pointer types need Nullable.h, and only
            # sequences outside unions require ForOfIterator.h) require.
            unrolled = t
            while True:
                if unrolled.nullable():
                    headerSet.add("mozilla/dom/Nullable.h")
                elif unrolled.isSequence():
                    bindingHeaders.add("js/ForOfIterator.h")
                else:
                    break
                unrolled = unrolled.inner
            if unrolled.isUnion():
                headerSet.add(self.getUnionDeclarationFilename(config, unrolled))
                bindingHeaders.add("mozilla/dom/UnionConversions.h")
            elif unrolled.isDate():
                if dictionary or jsImplementedDescriptors:
                    declareIncludes.add("mozilla/dom/Date.h")
                else:
                    bindingHeaders.add("mozilla/dom/Date.h")
            elif unrolled.isPromise():
                # See comment in the isInterface() case for why we add
                # Promise.h to headerSet, not bindingHeaders.
                headerSet.add("mozilla/dom/Promise.h")
                # We need ToJSValue to do the Promise to JS conversion.
                bindingHeaders.add("mozilla/dom/ToJSValue.h")
            elif unrolled.isInterface():
                if unrolled.isSpiderMonkeyInterface():
                    bindingHeaders.add("jsfriendapi.h")
                    if jsImplementedDescriptors:
                        # Since we can't forward-declare typed array types
                        # (because they're typedefs), we have to go ahead and
                        # just include their header if we need to have functions
                        # taking references to them declared in that header.
                        headerSet = declareIncludes
                    if unrolled.isReadableStream():
                        headerSet.add("mozilla/dom/ReadableStream.h")
                    else:
                        headerSet.add("mozilla/dom/TypedArray.h")
                else:
                    try:
                        typeDesc = config.getDescriptor(unrolled.inner.identifier.name)
                    except NoSuchDescriptorError:
                        return
                    # Dictionaries with interface members rely on the
                    # actual class definition of that interface member
                    # being visible in the binding header, because they
                    # store them in RefPtr and have inline
                    # constructors/destructors.
                    #
                    # XXXbz maybe dictionaries with interface members
                    # should just have out-of-line constructors and
                    # destructors?
                    headerSet.add(typeDesc.headerFile)
            elif unrolled.isDictionary():
                headerSet.add(self.getDeclarationFilename(unrolled.inner))
            elif unrolled.isCallback():
                headerSet.add(self.getDeclarationFilename(unrolled.callback))
            elif unrolled.isFloat() and not unrolled.isUnrestricted():
                # Restricted floats are tested for finiteness
                bindingHeaders.add("mozilla/FloatingPoint.h")
                bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
            elif unrolled.isEnum():
                filename = self.getDeclarationFilename(unrolled.inner)
                declareIncludes.add(filename)
            elif unrolled.isPrimitive():
                bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
            elif unrolled.isRecord():
                if dictionary or jsImplementedDescriptors:
                    declareIncludes.add("mozilla/dom/Record.h")
                else:
                    bindingHeaders.add("mozilla/dom/Record.h")
                # Also add headers for the type the record is
                # parametrized over, if needed.
                addHeadersForType((t.inner, dictionary))

        map(addHeadersForType,
            getAllTypes(descriptors + callbackDescriptors, dictionaries,
                        callbacks))

        # Now make sure we're not trying to include the header from inside itself
        declareIncludes.discard(prefix + ".h")

        def addHeaderForFunc(func, desc):
            if func is None:
                return
            # Include the right class header, which we can only do
            # if this is a class member function.
            if desc is not None and not desc.headerIsDefault:
                # An explicit header file was provided, assume that we know
                # what we're doing.
                return

            if "::" in func:
                # Strip out the function name and convert "::" to "/"
                bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")

        # Now for non-callback descriptors make sure we include any
        # headers needed by Func declarations and other things like that.
        for desc in descriptors:
            # If this is an iterator interface generated for a separate
            # iterable interface, skip generating type includes, as we have
            # what we need in IterableIterator.h
            if desc.interface.isExternal() or desc.interface.isIteratorInterface():
                continue

            for m in desc.interface.members:
                addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), desc)
                staticTypeOverride = PropertyDefiner.getStringAttr(m, "StaticClassOverride")
                if staticTypeOverride:
                    bindingHeaders.add("/".join(staticTypeOverride.split("::")) + ".h")
            # getExtendedAttribute() returns a list, extract the entry.
            funcList = desc.interface.getExtendedAttribute("Func")
            if funcList is not None:
                addHeaderForFunc(funcList[0], desc)

            if desc.interface.maplikeOrSetlikeOrIterable:
                # We need ToJSValue.h for maplike/setlike type conversions
                bindingHeaders.add("mozilla/dom/ToJSValue.h")
                # Add headers for the key and value types of the
                # maplike/setlike/iterable, since they'll be needed for
                # convenience functions
                if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType():
                    addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
                                       None))
                if desc.interface.maplikeOrSetlikeOrIterable.hasValueType():
                    addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.valueType,
                                       None))

        for d in dictionaries:
            if d.parent:
                declareIncludes.add(self.getDeclarationFilename(d.parent))
            bindingHeaders.add(self.getDeclarationFilename(d))
            for m in d.members:
                addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"),
                                 None)
            # No need to worry about Func on members of ancestors, because that
            # will happen automatically in whatever files those ancestors live
            # in.

        for c in callbacks:
            bindingHeaders.add(self.getDeclarationFilename(c))

        for c in callbackDescriptors:
            bindingHeaders.add(self.getDeclarationFilename(c.interface))

        if len(callbacks) != 0:
            # We need CallbackFunction to serve as our parent class
            declareIncludes.add("mozilla/dom/CallbackFunction.h")
            # And we need ToJSValue.h so we can wrap "this" objects
            declareIncludes.add("mozilla/dom/ToJSValue.h")

        if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0:
            # We need CallbackInterface to serve as our parent class
            declareIncludes.add("mozilla/dom/CallbackInterface.h")
            # And we need ToJSValue.h so we can wrap "this" objects
            declareIncludes.add("mozilla/dom/ToJSValue.h")

        # Also need to include the headers for ancestors of
        # JS-implemented interfaces.
        for jsImplemented in jsImplementedDescriptors:
            jsParent = jsImplemented.interface.parent
            if jsParent:
                parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name)
                declareIncludes.add(parentDesc.jsImplParentHeader)

        # Let the machinery do its thing.
        def _includeString(includes):
            return ''.join(['#include "%s"\n' % i for i in includes]) + '\n'
        CGWrapper.__init__(self, child,
                           declarePre=_includeString(sorted(declareIncludes)),
                           definePre=_includeString(sorted(set(defineIncludes) |
                                                           bindingIncludes |
                                                           bindingHeaders |
                                                           implementationIncludes)))

    @staticmethod
    def getDeclarationFilename(decl):
        # Use our local version of the header, not the exported one, so that
        # test bindings, which don't export, will work correctly.
        basename = os.path.basename(decl.filename())
        return basename.replace('.webidl', 'Binding.h')

    @staticmethod
    def getUnionDeclarationFilename(config, unionType):
        assert unionType.isUnion()
        assert unionType.unroll() == unionType
        # If a union is "defined" in multiple files, it goes in UnionTypes.h.
        if len(config.filenamesPerUnion[unionType.name]) > 1:
            return "mozilla/dom/UnionTypes.h"
        # If a union is defined by a built-in typedef, it also goes in
        # UnionTypes.h.
        assert len(config.filenamesPerUnion[unionType.name]) == 1
        if "<unknown>" in config.filenamesPerUnion[unionType.name]:
            return "mozilla/dom/UnionTypes.h"
        return CGHeaders.getDeclarationFilename(unionType)


def SortedDictValues(d):
    """
    Returns a list of values from the dict sorted by key.
    """
    return [v for k, v in sorted(d.items())]


def UnionsForFile(config, webIDLFile):
    """
    Returns a list of union types for all union types that are only used in
    webIDLFile. If webIDLFile is None this will return the list of tuples for
    union types that are used in more than one WebIDL file.
    """
    return config.unionsPerFilename.get(webIDLFile, [])


def UnionTypes(unionTypes, config):
    """
    The unionTypes argument should be a list of union types. This is typically
    the list generated by UnionsForFile.

    Returns a tuple containing a set of header filenames to include in
    the header for the types in unionTypes, a set of header filenames to
    include in the implementation file for the types in unionTypes, a set
    of tuples containing a type declaration and a boolean if the type is a
    struct for member types of the union, a list of traverse methods,
    unlink methods and a list of union types. These last three lists only
    contain unique union types.
    """

    headers = set()
    implheaders = set()
    declarations = set()
    unionStructs = dict()
    traverseMethods = dict()
    unlinkMethods = dict()

    for t in unionTypes:
        name = str(t)
        if name not in unionStructs:
            unionStructs[name] = t

            def addHeadersForType(f):
                if f.nullable():
                    headers.add("mozilla/dom/Nullable.h")
                isSequence = f.isSequence()
                if isSequence:
                    # Dealing with sequences requires for-of-compatible
                    # iteration.
                    implheaders.add("js/ForOfIterator.h")
                f = f.unroll()
                if f.isPromise():
                    headers.add("mozilla/dom/Promise.h")
                    # We need ToJSValue to do the Promise to JS conversion.
                    headers.add("mozilla/dom/ToJSValue.h")
                elif f.isInterface():
                    if f.isSpiderMonkeyInterface():
                        headers.add("jsfriendapi.h")
                        if f.isReadableStream():
                            headers.add("mozilla/dom/ReadableStream.h")
                        else:
                            headers.add("mozilla/dom/TypedArray.h")
                    else:
                        try:
                            typeDesc = config.getDescriptor(f.inner.identifier.name)
                        except NoSuchDescriptorError:
                            return
                        if typeDesc.interface.isCallback() or isSequence:
                            # Callback interfaces always use strong refs, so
                            # we need to include the right header to be able
                            # to Release() in our inlined code.
                            #
                            # Similarly, sequences always contain strong
                            # refs, so we'll need the header to handler
                            # those.
                            headers.add(typeDesc.headerFile)
                        elif typeDesc.interface.identifier.name == "WindowProxy":
                            # In UnionTypes.h we need to see the declaration of the
                            # WindowProxyHolder that we use to store the WindowProxy, so
                            # we have its sizeof and know how big to make our union.
                            headers.add(typeDesc.headerFile)
                        else:
                            declarations.add((typeDesc.nativeType, False))
                            implheaders.add(typeDesc.headerFile)
                elif f.isDictionary():
                    # For a dictionary, we need to see its declaration in
                    # UnionTypes.h so we have its sizeof and know how big to
                    # make our union.
                    headers.add(CGHeaders.getDeclarationFilename(f.inner))
                    # And if it needs rooting, we need RootedDictionary too
                    if typeNeedsRooting(f):
                        headers.add("mozilla/dom/RootedDictionary.h")
                elif f.isFloat() and not f.isUnrestricted():
                    # Restricted floats are tested for finiteness
                    implheaders.add("mozilla/FloatingPoint.h")
                    implheaders.add("mozilla/dom/PrimitiveConversions.h")
                elif f.isEnum():
                    # Need to see the actual definition of the enum,
                    # unfortunately.
                    headers.add(CGHeaders.getDeclarationFilename(f.inner))
                elif f.isPrimitive():
                    implheaders.add("mozilla/dom/PrimitiveConversions.h")
                elif f.isCallback():
                    # Callbacks always use strong refs, so we need to include
                    # the right header to be able to Release() in our inlined
                    # code.
                    headers.add(CGHeaders.getDeclarationFilename(f.callback))
                elif f.isRecord():
                    headers.add("mozilla/dom/Record.h")
                    # And add headers for the type we're parametrized over
                    addHeadersForType(f.inner)

            implheaders.add(CGHeaders.getUnionDeclarationFilename(config, t))
            for f in t.flatMemberTypes:
                assert not f.nullable()
                addHeadersForType(f)

            if idlTypeNeedsCycleCollection(t):
                declarations.add(("mozilla::dom::%s" % CGUnionStruct.unionTypeName(t, True), False))
                traverseMethods[name] = CGCycleCollectionTraverseForOwningUnionMethod(t)
                unlinkMethods[name] = CGCycleCollectionUnlinkForOwningUnionMethod(t)

    # The order of items in CGList is important.
    # Since the union structs friend the unlinkMethods, the forward-declaration
    # for these methods should come before the class declaration. Otherwise
    # some compilers treat the friend declaration as a forward-declaration in
    # the class scope.
    return (headers, implheaders, declarations,
            SortedDictValues(traverseMethods), SortedDictValues(unlinkMethods),
            SortedDictValues(unionStructs))


def UnionConversions(unionTypes, config):
    """
    The unionTypes argument should be a list of tuples, each containing two
    elements: a union type and a descriptor. This is typically the list
    generated by UnionsForFile.

    Returns a tuple containing a list of headers and a CGThing to declare all
    union argument conversion helper structs.
    """
    headers = set()
    unionConversions = dict()

    for t in unionTypes:
        name = str(t)
        if name not in unionConversions:
            unionConversions[name] = CGUnionConversionStruct(t, config)

            def addHeadersForType(f):
                if f.isSequence():
                    # Sequences require JSAPI C++ for-of iteration code to fill
                    # them.
                    headers.add("js/ForOfIterator.h")
                f = f.unroll()
                if f.isPromise():
                    headers.add("mozilla/dom/Promise.h")
                    # We need ToJSValue to do the Promise to JS conversion.
                    headers.add("mozilla/dom/ToJSValue.h")
                elif f.isInterface():
                    if f.isSpiderMonkeyInterface():
                        headers.add("jsfriendapi.h")
                        if f.isReadableStream():
                            headers.add("mozilla/dom/ReadableStream.h")
                        else:
                            headers.add("mozilla/dom/TypedArray.h")
                    elif f.inner.isExternal():
                        try:
                            typeDesc = config.getDescriptor(f.inner.identifier.name)
                        except NoSuchDescriptorError:
                            return
                        headers.add(typeDesc.headerFile)
                    else:
                        headers.add(CGHeaders.getDeclarationFilename(f.inner))
                elif f.isDictionary():
                    headers.add(CGHeaders.getDeclarationFilename(f.inner))
                elif f.isFloat() and not f.isUnrestricted():
                    # Restricted floats are tested for finiteness
                    headers.add("mozilla/FloatingPoint.h")
                    headers.add("mozilla/dom/PrimitiveConversions.h")
                elif f.isPrimitive():
                    headers.add("mozilla/dom/PrimitiveConversions.h")
                elif f.isRecord():
                    headers.add("mozilla/dom/Record.h")
                    # And the internal type of the record
                    addHeadersForType(f.inner)

            # We plan to include UnionTypes.h no matter what, so it's
            # OK if we throw it into the set here.
            headers.add(CGHeaders.getUnionDeclarationFilename(config, t))

            for f in t.flatMemberTypes:
                addHeadersForType(f)

    return (headers,
            CGWrapper(CGList(SortedDictValues(unionConversions), "\n"),
                      post="\n\n"))


class Argument():
    """
    A class for outputting the type and name of an argument
    """
    def __init__(self, argType, name, default=None):
        self.argType = argType
        self.name = name
        self.default = default

    def declare(self):
        string = self.argType + ' ' + self.name
        if self.default is not None:
            string += " = " + self.default
        return string

    def define(self):
        return self.argType + ' ' + self.name


class CGAbstractMethod(CGThing):
    """
    An abstract class for generating code for a method.  Subclasses
    should override definition_body to create the actual code.

    descriptor is the descriptor for the interface the method is associated with

    name is the name of the method as a string

    returnType is the IDLType of the return value

    args is a list of Argument objects

    inline should be True to generate an inline method, whose body is
    part of the declaration.

    alwaysInline should be True to generate an inline method annotated with
    MOZ_ALWAYS_INLINE.

    static should be True to generate a static method, which only has
    a definition.

    If templateArgs is not None it should be a list of strings containing
    template arguments, and the function will be templatized using those
    arguments.

    canRunScript should be True to generate a MOZ_CAN_RUN_SCRIPT annotation.
    """
    def __init__(self, descriptor, name, returnType, args, inline=False,
                 alwaysInline=False, static=False, templateArgs=None,
                 canRunScript=False):
        CGThing.__init__(self)
        self.descriptor = descriptor
        self.name = name
        self.returnType = returnType
        self.args = args
        self.inline = inline
        self.alwaysInline = alwaysInline
        self.static = static
        self.templateArgs = templateArgs
        self.canRunScript = canRunScript

    def _argstring(self, declare):
        return ', '.join([a.declare() if declare else a.define() for a in self.args])

    def _template(self):
        if self.templateArgs is None:
            return ''
        return 'template <%s>\n' % ', '.join(self.templateArgs)

    def _decorators(self):
        decorators = []
        if self.canRunScript:
            decorators.append('MOZ_CAN_RUN_SCRIPT');
        if self.alwaysInline:
            decorators.append('MOZ_ALWAYS_INLINE')
        elif self.inline:
            decorators.append('inline')
        if self.static:
            decorators.append('static')
        decorators.append(self.returnType)
        maybeNewline = " " if self.inline else "\n"
        return ' '.join(decorators) + maybeNewline

    def declare(self):
        if self.inline:
            return self._define(True)
        return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring(True))

    def indent_body(self, body):
        """
        Indent the code returned by self.definition_body(). Most classes
        simply indent everything two spaces. This is here for
        CGRegisterProtos, which needs custom indentation.
        """
        return indent(body)

    def _define(self, fromDeclare=False):
        return (self.definition_prologue(fromDeclare) +
                self.indent_body(self.definition_body()) +
                self.definition_epilogue())

    def define(self):
        return "" if self.inline else self._define()

    def definition_prologue(self, fromDeclare):
        prologue = "%s%s%s(%s)\n{\n" % (self._template(), self._decorators(),
                                        self.name, self._argstring(fromDeclare))
        profiler_label = self.auto_profiler_label()
        if profiler_label:
            prologue += indent(profiler_label) + "\n"

        return prologue

    def definition_epilogue(self):
        return "}\n"

    def definition_body(self):
        assert False  # Override me!

    """
    Override this method to return a pair of (descriptive string, name of a
    JSContext* variable) in order to generate a profiler label for this method.
    """
    def auto_profiler_label(self):
        return None # Override me!

class CGAbstractStaticMethod(CGAbstractMethod):
    """
    Abstract base class for codegen of implementation-only (no
    declaration) static methods.
    """
    def __init__(self, descriptor, name, returnType, args, canRunScript=False):
        CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
                                  inline=False, static=True,
                                  canRunScript=canRunScript)

    def declare(self):
        # We only have implementation
        return ""


class CGAbstractClassHook(CGAbstractStaticMethod):
    """
    Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
    'this' unwrapping as it assumes that the unwrapped type is always known.
    """
    def __init__(self, descriptor, name, returnType, args):
        CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
                                        args)

    def definition_body_prologue(self):
        return ("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" %
                (self.descriptor.nativeType, self.descriptor.nativeType))

    def definition_body(self):
        return self.definition_body_prologue() + self.generate_code()

    def generate_code(self):
        assert False  # Override me!


class CGGetJSClassMethod(CGAbstractMethod):
    def __init__(self, descriptor):
        CGAbstractMethod.__init__(self, descriptor, 'GetJSClass', 'const JSClass*',
                                  [])

    def definition_body(self):
        return "return sClass.ToJSClass();\n"


class CGAddPropertyHook(CGAbstractClassHook):
    """
    A hook for addProperty, used to preserve our wrapper from GC.
    """
    def __init__(self, descriptor):
        args = [Argument('JSContext*', 'cx'),
                Argument('JS::Handle<JSObject*>', 'obj'),
                Argument('JS::Handle<jsid>', 'id'),
                Argument('JS::Handle<JS::Value>', 'val')]
        CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME,
                                     'bool', args)

    def generate_code(self):
        assert self.descriptor.wrapperCache
        # This hook is also called by TryPreserveWrapper on non-nsISupports
        # cycle collected objects, so if addProperty is ever changed to do
        # anything more or less than preserve the wrapper, TryPreserveWrapper
        # will need to be changed.
        return dedent("""
            // We don't want to preserve if we don't have a wrapper, and we
            // obviously can't preserve if we're not initialized.
            if (self && self->GetWrapperPreserveColor()) {
              PreserveWrapper(self);
            }
            return true;
            """)


def finalizeHook(descriptor, hookName, freeOp, obj):
    finalize = "js::SetReservedSlot(%s, DOM_OBJECT_SLOT, JS::UndefinedValue());\n" % obj
    if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
        finalize += fill(
            """
            // Either our proxy created an expando object or not.  If it did,
            // then we would have preserved ourselves, and hence if we're going
            // away so is our C++ object and we should reset its expando value.
            // It's possible that in this situation the C++ object's reflector
            // pointer has been nulled out, but if not it's pointing to us.  If
            // our proxy did _not_ create an expando object then it's possible
            // that we're no longer the reflector for our C++ object (and
            // incremental finalization is finally getting to us), and that in
            // the meantime the new reflector has created an expando object.
            // In that case we do NOT want to clear the expando pointer in the
            // C++ object.
            //
            // It's important to do this before we ClearWrapper, of course.
            JSObject* reflector = self->GetWrapperMaybeDead();
            if (!reflector || reflector == ${obj}) {
              self->mExpandoAndGeneration.expando = JS::UndefinedValue();
            }
            """,
            obj=obj)
    if descriptor.wrapperCache:
        finalize += "ClearWrapper(self, self, %s);\n" % obj
    if descriptor.isGlobal():
        finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), %s);\n" % (freeOp, obj)
    finalize += fill(
        """
        if (size_t mallocBytes = BindingJSObjectMallocBytes(self)) {
          JS::RemoveAssociatedMemory(${obj}, mallocBytes,
                                     JS::MemoryUse::DOMBinding);
        }
        """,
        obj=obj)
    finalize += ("AddForDeferredFinalization<%s>(self);\n" %
                 descriptor.nativeType)
    return CGIfWrapper(CGGeneric(finalize), "self")


class CGClassFinalizeHook(CGAbstractClassHook):
    """
    A hook for finalize, used to release our native object.
    """
    def __init__(self, descriptor):
        args = [Argument('js::FreeOp*', 'fop'), Argument('JSObject*', 'obj')]
        CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
                                     'void', args)

    def generate_code(self):
        return finalizeHook(self.descriptor, self.name,
                            self.args[0].name, self.args[1].name).define()


def objectMovedHook(descriptor, hookName, obj, old):
    assert descriptor.wrapperCache
    return fill("""
        if (self) {
          UpdateWrapper(self, self, ${obj}, ${old});
        }

        return 0;
        """,
        obj=obj,
        old=old)


class CGClassObjectMovedHook(CGAbstractClassHook):
    """
    A hook for objectMovedOp, used to update the wrapper cache when an object it
    is holding moves.
    """
    def __init__(self, descriptor):
        args = [Argument('JSObject*', 'obj'), Argument('JSObject*', 'old')]
        CGAbstractClassHook.__init__(self, descriptor, OBJECT_MOVED_HOOK_NAME,
                                     'size_t', args)

    def generate_code(self):
        return objectMovedHook(self.descriptor, self.name,
                               self.args[0].name, self.args[1].name)


def JSNativeArguments():
    return [Argument('JSContext*', 'cx'),
            Argument('unsigned', 'argc'),
            Argument('JS::Value*', 'vp')]


class CGClassConstructor(CGAbstractStaticMethod):
    """
    JS-visible constructor for our objects
    """
    def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
        CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool',
                                        JSNativeArguments())
        self._ctor = ctor

    def define(self):
        if not self._ctor:
            return ""
        return CGAbstractStaticMethod.define(self)

    def definition_body(self):
        return self.generate_code()

    def generate_code(self):
        if self._ctor.isHTMLConstructor():
            # We better have a prototype object.  Otherwise our proto
            # id won't make sense.
            assert self.descriptor.interface.hasInterfacePrototypeObject()
            # We also better have a constructor object, if this is
            # getting called!
            assert self.descriptor.interface.hasInterfaceObject()
            # We can't just pass null for the CreateInterfaceObjects callback,
            # because our newTarget might be in a different compartment, in
            # which case we'll need to look up constructor objects in that
            # compartment.
            return fill(
                """
                return HTMLConstructor(cx, argc, vp,
                                       constructors::id::${name},
                                       prototypes::id::${name},
                                       CreateInterfaceObjects);
                """,
                name=self.descriptor.name)

        # [ChromeOnly] interfaces may only be constructed by chrome.
        chromeOnlyCheck = ""
        if isChromeOnly(self._ctor):
            chromeOnlyCheck = dedent("""
                if (!nsContentUtils::ThreadsafeIsSystemCaller(cx)) {
                  return ThrowingConstructor(cx, argc, vp);
                }

                """)

        # Additionally, we want to throw if a caller does a bareword invocation
        # of a constructor without |new|.
        #
        # Figure out the name of our constructor for error reporting purposes.
        # For unnamed webidl constructors, identifier.name is "constructor" but
        # the name JS sees is the interface name; for named constructors
        # identifier.name is the actual name.
        name = self._ctor.identifier.name
        if name != "constructor":
            ctorName = name
        else:
            ctorName = self.descriptor.interface.identifier.name

        preamble = fill(
            """
            JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
            JS::Rooted<JSObject*> obj(cx, &args.callee());
            $*{chromeOnlyCheck}
            if (!args.isConstructing()) {
              return ThrowConstructorWithoutNew(cx, "${ctorName}");
            }

            JS::Rooted<JSObject*> desiredProto(cx);
            if (!GetDesiredProto(cx, args,
                                 prototypes::id::${name},
                                 CreateInterfaceObjects,
                                 &desiredProto)) {
              return false;
            }
            """,
            chromeOnlyCheck=chromeOnlyCheck,
            ctorName=ctorName,
            name=self.descriptor.name)

        name = self._ctor.identifier.name
        nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
        callGenerator = CGMethodCall(nativeName, True, self.descriptor,
                                     self._ctor, isConstructor=True,
                                     constructorName=ctorName)
        return preamble + "\n" + callGenerator.define()

    def auto_profiler_label(self):
        name = self._ctor.identifier.name
        if name != "constructor":
            ctorName = name
        else:
            ctorName = self.descriptor.interface.identifier.name
        return fill(
            """
            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
              "${ctorName}", "constructor", DOM, cx,
              uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
            """,
            ctorName=ctorName)

# Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod.
class CGConstructNavigatorObject(CGAbstractMethod):
    """
    Construct a new JS-implemented WebIDL DOM object, for use on navigator.
    """
    def __init__(self, descriptor):
        args = [Argument('JSContext*', 'cx'),
                Argument('JS::Handle<JSObject*>', 'obj'),
                Argument('ErrorResult&', 'aRv')]
        rtype = 'already_AddRefed<%s>' % descriptor.name
        CGAbstractMethod.__init__(self, descriptor, "ConstructNavigatorObject",
                                  rtype, args)

    def definition_body(self):
        if not self.descriptor.interface.isJSImplemented():
            raise TypeError("Only JS-implemented classes are currently supported "
                            "on navigator. See bug 856820.")

        return dedent(
            """
            GlobalObject global(cx, obj);
            if (global.Failed()) {
              aRv.Throw(NS_ERROR_FAILURE);
              return nullptr;
            }
            """) + genConstructorBody(self.descriptor)


def NamedConstructorName(m):
    return '_' + m.identifier.name


class CGNamedConstructors(CGThing):
    def __init__(self, descriptor):
        self.descriptor = descriptor
        CGThing.__init__(self)

    def declare(self):
        return ""

    def define(self):
        if len(self.descriptor.interface.namedConstructors) == 0:
            return ""

        constructorID = "constructors::id::"
        if self.descriptor.interface.hasInterfaceObject():
            constructorID += self.descriptor.name
        else:
            constructorID += "_ID_Count"

        namedConstructors = ""
        for n in self.descriptor.interface.namedConstructors:
            namedConstructors += (
                "{ \"%s\", { %s, &sNamedConstructorNativePropertyHooks }, %i },\n" %
                (n.identifier.name, NamedConstructorName(n), methodLength(n)))

        return fill(
            """
            const NativePropertyHooks sNamedConstructorNativePropertyHooks = {
                nullptr,
                nullptr,
                nullptr,
                { nullptr, nullptr },
                prototypes::id::${name},
                ${constructorID},
                nullptr
            };

            static const NamedConstructor namedConstructors[] = {
              $*{namedConstructors}
              { nullptr, { nullptr, nullptr }, 0 }
            };
            """,
            name=self.descriptor.name,
            constructorID=constructorID,
            namedConstructors=namedConstructors)


def isChromeOnly(m):
    return m.getExtendedAttribute("ChromeOnly")


def prefIdentifier(pref):
    return pref.replace(".", "_").replace("-", "_")

def prefHeader(pref):
    return "mozilla/StaticPrefs_%s.h" % pref.partition(".")[0]

class MemberCondition:
    """
    An object representing the condition for a member to actually be
    exposed.  Any of the arguments can be None.  If not
    None, they should have the following types:

    pref: The name of the preference.
    func: The name of the function.
    secureContext: A bool indicating whether a secure context is required.
    nonExposedGlobals: A set of names of globals.  Can be empty, in which case
                       it's treated the same way as None.
    """
    def __init__(self, pref=None, func=None, secureContext=False,
                 nonExposedGlobals=None):
        assert pref is None or isinstance(pref, str)
        assert func is None or isinstance(func, str)
        assert isinstance(secureContext, bool)
        assert nonExposedGlobals is None or isinstance(nonExposedGlobals, set)
        self.pref = pref
        if self.pref:
            identifier = prefIdentifier(self.pref)
            self.prefFuncIndex = "WebIDLPrefIndex::" + identifier
        else:
            self.prefFuncIndex = "WebIDLPrefIndex::NoPref"

        self.secureContext = secureContext

        def toFuncPtr(val):
            if val is None:
                return "nullptr"
            return "&" + val
        self.func = toFuncPtr(func)

        if nonExposedGlobals:
            # Nonempty set
            self.nonExposedGlobals = " | ".join(
                map(lambda g: "GlobalNames::%s" % g,
                    sorted(nonExposedGlobals)))
        else:
            self.nonExposedGlobals = "0"

    def __eq__(self, other):
        return (self.pref == other.pref and self.func == other.func and
                self.secureContext == other.secureContext and
                self.nonExposedGlobals == other.nonExposedGlobals)

    def __ne__(self, other):
        return not self.__eq__(other)

    def hasDisablers(self):
        return (self.pref is not None or
                self.secureContext or
                self.func != "nullptr" or
                self.nonExposedGlobals != "0")


class PropertyDefiner:
    """
    A common superclass for defining things on prototype objects.

    Subclasses should implement generateArray to generate the actual arrays of
    things we're defining.  They should also set self.chrome to the list of
    things only exposed to chrome and self.regular to the list of things exposed
    to both chrome and web pages.
    """
    def __init__(self, descriptor, name):
        self.descriptor = descriptor
        self.name = name

    def hasChromeOnly(self):
        return len(self.chrome) > 0

    def hasNonChromeOnly(self):
        return len(self.regular) > 0

    def variableName(self, chrome):
        if chrome:
            if self.hasChromeOnly():
                return "sChrome" + self.name
        else:
            if self.hasNonChromeOnly():
                return "s" + self.name
        return "nullptr"

    def usedForXrays(self):
        return self.descriptor.wantsXrays

    def length(self, chrome):
        return len(self.chrome) if chrome else len(self.regular)

    def __str__(self):
        # We only need to generate id arrays for things that will end
        # up used via ResolveProperty or EnumerateProperties.
        str = self.generateArray(self.regular, self.variableName(False))
        if self.hasChromeOnly():
            str += self.generateArray(self.chrome, self.variableName(True))
        return str

    @staticmethod
    def getStringAttr(member, name):
        attr = member.getExtendedAttribute(name)
        if attr is None:
            return None
        # It's a list of strings
        assert len(attr) == 1
        assert attr[0] is not None
        return attr[0]

    @staticmethod
    def getControllingCondition(interfaceMember, descriptor):
        interface = descriptor.interface
        nonExposureSet = interface.exposureSet - interfaceMember.exposureSet

        return MemberCondition(
            PropertyDefiner.getStringAttr(interfaceMember,
                                          "Pref"),
            PropertyDefiner.getStringAttr(interfaceMember,
                                          "Func"),
            interfaceMember.getExtendedAttribute("SecureContext") is not None,
            nonExposureSet)

    @staticmethod
    def generatePrefableArrayValues(array, descriptor, specFormatter, specTerminator,
                                    getCondition, getDataTuple,
                                    switchToCondition=None):
        """
        This method generates an array of spec entries for interface members. It returns
          a tuple containing the array of spec entries and the maximum of the number of
          spec entries per condition.

        array is an array of interface members.

        descriptor is the descriptor for the interface that array contains members of.

        specFormatter is a function that takes a single argument, a tuple,
          and returns a string, a spec array entry.

        specTerminator is a terminator for the spec array (inserted every time
          our controlling pref changes and at the end of the array).

        getCondition is a callback function that takes an array entry and
          returns the corresponding MemberCondition.

        getDataTuple is a callback function that takes an array entry and
          returns a tuple suitable to be passed to specFormatter.

        switchToCondition is a function that takes a MemberCondition and an array of
          previously generated spec entries. If None is passed for this function then all
          the interface members should return the same value from getCondition.
        """
        def unsupportedSwitchToCondition(condition, specs):
            # If no specs have been added yet then this is just the first call to
            # switchToCondition that we call to avoid putting a specTerminator at the
            # front of the list.
            if len(specs) == 0:
                return
            raise "Not supported"

        if switchToCondition is None:
            switchToCondition = unsupportedSwitchToCondition

        specs = []
        numSpecsInCurPrefable = 0
        maxNumSpecsInPrefable = 0

        # So we won't put a specTerminator at the very front of the list:
        lastCondition = getCondition(array[0], descriptor)

        switchToCondition(lastCondition, specs)

        for member in array:
            curCondition = getCondition(member, descriptor)
            if lastCondition != curCondition:
                # Terminate previous list
                specs.append(specTerminator)
                if numSpecsInCurPrefable > maxNumSpecsInPrefable:
                    maxNumSpecsInPrefable = numSpecsInCurPrefable
                numSpecsInCurPrefable = 0
                # And switch to our new condition
                switchToCondition(curCondition, specs)
                lastCondition = curCondition
            # And the actual spec
            specs.append(specFormatter(getDataTuple(member, descriptor)))
            numSpecsInCurPrefable += 1
        if numSpecsInCurPrefable > maxNumSpecsInPrefable:
            maxNumSpecsInPrefable = numSpecsInCurPrefable
        specs.append(specTerminator)

        return (specs, maxNumSpecsInPrefable)

    def generatePrefableArray(self, array, name, specFormatter, specTerminator,
                              specType, getCondition, getDataTuple):
        """
        This method generates our various arrays.

        array is an array of interface members as passed to generateArray

        name is the name as passed to generateArray

        specFormatter is a function that takes a single argument, a tuple,
          and returns a string, a spec array entry

        specTerminator is a terminator for the spec array (inserted every time
          our controlling pref changes and at the end of the array)

        specType is the actual typename of our spec

        getCondition is a callback function that takes an array entry and
          returns the corresponding MemberCondition.

        getDataTuple is a callback function that takes an array entry and
          returns a tuple suitable to be passed to specFormatter.
        """

        # We want to generate a single list of specs, but with specTerminator
        # inserted at every point where the pref name controlling the member
        # changes.  That will make sure the order of the properties as exposed
        # on the interface and interface prototype objects does not change when
        # pref control is added to members while still allowing us to define all
        # the members in the smallest number of JSAPI calls.
        assert len(array) != 0

        disablers = []
        prefableSpecs = []

        disablersTemplate = dedent(
            """
            static const PrefableDisablers %s_disablers%d = {
              %s, %s, %s, %s
            };
            """)
        prefableWithDisablersTemplate = '  { &%s_disablers%d, &%s_specs[%d] }'
        prefableWithoutDisablersTemplate = '  { nullptr, &%s_specs[%d] }'
        prefCacheTemplate = '&%s[%d].disablers->enabled'

        def switchToCondition(condition, specs):
            # Set up pointers to the new sets of specs inside prefableSpecs
            if condition.hasDisablers():
                prefableSpecs.append(prefableWithDisablersTemplate %
                                     (name, len(specs), name, len(specs)))
                disablers.append(disablersTemplate %
                                 (name, len(specs),
                                  condition.prefFuncIndex,
                                  toStringBool(condition.secureContext),
                                  condition.nonExposedGlobals,
                                  condition.func))
            else:
                prefableSpecs.append(prefableWithoutDisablersTemplate %
                                     (name, len(specs)))

        specs, maxNumSpecsInPrefable = self.generatePrefableArrayValues(
            array, self.descriptor, specFormatter, specTerminator, getCondition,
            getDataTuple, switchToCondition)
        prefableSpecs.append("  { nullptr, nullptr }")

        specType = "const " + specType
        arrays = fill(
            """
            // We deliberately use brace-elision to make Visual Studio produce better initalization code.
            #if defined(__clang__)
            #pragma clang diagnostic push
            #pragma clang diagnostic ignored "-Wmissing-braces"
            #endif
            static ${specType} ${name}_specs[] = {
            ${specs}
            };
            #if defined(__clang__)
            #pragma clang diagnostic pop
            #endif

            ${disablers}
            static const Prefable<${specType}> ${name}[] = {
            ${prefableSpecs}
            };

            """,
            specType=specType,
            name=name,
            disablers='\n'.join(disablers),
            specs=',\n'.join(specs),
            prefableSpecs=',\n'.join(prefableSpecs))

        if self.usedForXrays():
            arrays = fill(
                """
                $*{arrays}
                static_assert(${numPrefableSpecs} <= 1ull << NUM_BITS_PROPERTY_INFO_PREF_INDEX,
                    "We have a prefable index that is >= (1 << NUM_BITS_PROPERTY_INFO_PREF_INDEX)");
                static_assert(${maxNumSpecsInPrefable} <= 1ull << NUM_BITS_PROPERTY_INFO_SPEC_INDEX,
                    "We have a spec index that is >= (1 << NUM_BITS_PROPERTY_INFO_SPEC_INDEX)");

                """,
                arrays=arrays,
                # Minus 1 because there's a list terminator in prefableSpecs.
                numPrefableSpecs=len(prefableSpecs)-1,
                maxNumSpecsInPrefable=maxNumSpecsInPrefable)

        return arrays


# The length of a method is the minimum of the lengths of the
# argument lists of all its overloads.
def overloadLength(arguments):
    i = len(arguments)
    while i > 0 and arguments[i - 1].optional:
        i -= 1
    return i


def methodLength(method):
    signatures = method.signatures()
    return min(overloadLength(arguments) for retType, arguments in signatures)


def clearableCachedAttrs(descriptor):
    return (m for m in descriptor.interface.members if
            m.isAttr() and
            # Constants should never need clearing!
            m.dependsOn != "Nothing" and
            m.slotIndices is not None)


def MakeClearCachedValueNativeName(member):
    return "ClearCached%sValue" % MakeNativeName(member.identifier.name)


def IDLToCIdentifier(name):
    return name.replace("-", "_")


def EnumerabilityFlags(member):
    if member.getExtendedAttribute("NonEnumerable"):
        return "0"
    return "JSPROP_ENUMERATE"


class MethodDefiner(PropertyDefiner):
    """
    A class for defining methods on a prototype object.
    """
    def __init__(self, descriptor, name, crossOriginOnly, static, unforgeable=False):
        assert not (static and unforgeable)
        PropertyDefiner.__init__(self, descriptor, name)

        # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
        #       We should be able to check for special operations without an
        #       identifier. For now we check if the name starts with __

        # Ignore non-static methods for interfaces without a proto object
        if descriptor.interface.hasInterfacePrototypeObject() or static:
            methods = [m for m in descriptor.interface.members if
                       m.isMethod() and m.isStatic() == static and
                       MemberIsUnforgeable(m, descriptor) == unforgeable and
                       (not crossOriginOnly or m.getExtendedAttribute("CrossOriginCallable")) and
                       not m.isIdentifierLess() and
                       not m.getExtendedAttribute("Unexposed")]
        else:
            methods = []
        self.chrome = []
        self.regular = []
        for m in methods:
            if m.identifier.name == 'QueryInterface':
                # QueryInterface is special, because instead of generating an
                # impl we just call out directly to our shared one.
                if m.isStatic():
                    raise TypeError("Legacy QueryInterface member shouldn't be static")
                signatures = m.signatures()

                if (len(signatures) > 1 or len(signatures[0][1]) > 1 or
                    not signatures[0][1][0].type.isAny()):
                    raise TypeError("There should be only one QueryInterface method with 1 argument of type any")

                # Make sure to not stick QueryInterface on abstract interfaces.
                if (not self.descriptor.interface.hasInterfacePrototypeObject() or
                    not self.descriptor.concrete):
                    raise TypeError("QueryInterface is only supported on "
                                    "interfaces that are concrete: " +
                                    self.descriptor.name)

                if not isChromeOnly(m):
                    raise TypeError("QueryInterface must be ChromeOnly")

                self.chrome.append({
                    "name": 'QueryInterface',
                    "methodInfo": False,
                    "length": 1,
                    "flags": "0",
                    "condition": PropertyDefiner.getControllingCondition(m, descriptor)
                })
                continue

            method = self.methodData(m, descriptor)

            if m.isStatic():
                method["nativeName"] = CppKeywords.checkMethodName(IDLToCIdentifier(m.identifier.name))

            if isChromeOnly(m):
                self.chrome.append(method)
            else:
                self.regular.append(method)

        # TODO: Once iterable is implemented, use tiebreak rules instead of
        # failing. Also, may be more tiebreak rules to implement once spec bug
        # is resolved.
        # https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592
        def hasIterator(methods, regular):
            return (any("@@iterator" in m.aliases for m in methods) or
                    any("@@iterator" == r["name"] for r in regular))

        # Check whether we need to output an @@iterator due to having an indexed
        # getter.  We only do this while outputting non-static and
        # non-unforgeable methods, since the @@iterator function will be
        # neither.
        if (not static and
            not unforgeable and
            descriptor.supportsIndexedProperties()):
            if hasIterator(methods, self.regular):
                raise TypeError("Cannot have indexed getter/attr on "
                                "interface %s with other members "
                                "that generate @@iterator, such as "
                                "maplike/setlike or aliased functions." %
                                self.descriptor.interface.identifier.name)
            self.regular.append({
                "name": "@@iterator",
                "methodInfo": False,
                "selfHostedName": "$ArrayValues",
                "length": 0,
                "flags": "0", # Not enumerable, per spec.
                "condition": MemberCondition()
            })

        # Generate the keys/values/entries aliases for value iterables.
        maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
        if (not static and
            not unforgeable and
            maplikeOrSetlikeOrIterable and
            maplikeOrSetlikeOrIterable.isIterable() and
            maplikeOrSetlikeOrIterable.isValueIterator()):
            # Add our keys/values/entries/forEach
            self.regular.append({
                "name": "keys",
                "methodInfo": False,
                "selfHostedName": "ArrayKeys",
                "length": 0,
                "flags": "JSPROP_ENUMERATE",
                "condition": PropertyDefiner.getControllingCondition(m,
                                                                     descriptor)
            })
            self.regular.append({
                "name": "values",
                "methodInfo": False,
                "selfHostedName": "$ArrayValues",
                "length": 0,
                "flags": "JSPROP_ENUMERATE",
                "condition": PropertyDefiner.getControllingCondition(m,
                                                                     descriptor)
            })
            self.regular.append({
                "name": "entries",
                "methodInfo": False,
                "selfHostedName": "ArrayEntries",
                "length": 0,
                "flags": "JSPROP_ENUMERATE",
                "condition": PropertyDefiner.getControllingCondition(m,
                                                                     descriptor)
            })
            self.regular.append({
                "name": "forEach",
                "methodInfo": False,
                "selfHostedName": "ArrayForEach",
                "length": 1,
                "flags": "JSPROP_ENUMERATE",
                "condition": PropertyDefiner.getControllingCondition(m,
                                                                     descriptor)
            })

        if not static:
            stringifier = descriptor.operations['Stringifier']
            if (stringifier and
                unforgeable == MemberIsUnforgeable(stringifier, descriptor)):
                toStringDesc = {
                    "name": "toString",
                    "nativeName": stringifier.identifier.name,
                    "length": 0,
                    "flags": "JSPROP_ENUMERATE",
                    "condition": PropertyDefiner.getControllingCondition(stringifier, descriptor)
                }
                if isChromeOnly(stringifier):
                    self.chrome.append(toStringDesc)
                else:
                    self.regular.append(toStringDesc)
            if (unforgeable and
                descriptor.interface.getExtendedAttribute("Unforgeable")):
                # Synthesize our valueOf method
                self.regular.append({
                    "name": 'valueOf',
                    "selfHostedName": "Object_valueOf",
                    "methodInfo": False,
                    "length": 0,
                    "flags": "0",  # readonly/permanent added automatically.
                    "condition": MemberCondition()
                })

        if descriptor.interface.isJSImplemented():
            if static:
                if descriptor.interface.hasInterfaceObject():
                    self.chrome.append({
                        "name": '_create',
                        "nativeName": ("%s::_Create" % descriptor.name),
                        "methodInfo": False,
                        "length": 2,
                        "flags": "0",
                        "condition": MemberCondition()
                    })

        self.unforgeable = unforgeable

        if static:
            if not descriptor.interface.hasInterfaceObject():
                # static methods go on the interface object
                assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
        else:
            if not descriptor.interface.hasInterfacePrototypeObject():
                # non-static methods go on the interface prototype object
                assert not self.hasChromeOnly() and not self.hasNonChromeOnly()

    @staticmethod
    def methodData(m, descriptor, overrideFlags=None):
        return {
                "name": m.identifier.name,
                "methodInfo": not m.isStatic(),
                "length": methodLength(m),
                "flags": EnumerabilityFlags(m) if (overrideFlags is None) else overrideFlags,
                "condition": PropertyDefiner.getControllingCondition(m, descriptor),
                "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
                "returnsPromise": m.returnsPromise(),
                "hasIteratorAlias": "@@iterator" in m.aliases
            }

    @staticmethod
    def formatSpec(fields):
        if fields[0].startswith("@@"):
            fields = (fields[0][2:],) + fields[1:]
            return '  JS_SYM_FNSPEC(%s, %s, %s, %s, %s, %s)' % fields
        return '  JS_FNSPEC("%s", %s, %s, %s, %s, %s)' % fields

    @staticmethod
    def specData(m, descriptor, unforgeable=False):
        def flags(m, unforgeable):
            unforgeable = " | JSPROP_PERMANENT | JSPROP_READONLY" if unforgeable else ""
            return m["flags"] + unforgeable

        if "selfHostedName" in m:
            selfHostedName = '"%s"' % m["selfHostedName"]
            assert not m.get("methodInfo", True)
            accessor = "nullptr"
            jitinfo = "nullptr"
        else:
            selfHostedName = "nullptr"
            # When defining symbols, function name may not match symbol name
            methodName = m.get("methodName", m["name"])
            accessor = m.get("nativeName", IDLToCIdentifier(methodName))
            if m.get("methodInfo", True):
                if m.get("returnsPromise", False):
                    exceptionPolicy = "ConvertExceptionsToPromises"
                else:
                    exceptionPolicy = "ThrowExceptions"

                # Cast this in case the methodInfo is a
                # JSTypedMethodJitInfo.
                jitinfo = ("reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor)
                if m.get("allowCrossOriginThis", False):
                    accessor = ("(GenericMethod<CrossOriginThisPolicy, %s>)" %
                                exceptionPolicy)
                elif descriptor.interface.isOnGlobalProtoChain():
                    accessor = ("(GenericMethod<MaybeGlobalThisPolicy, %s>)" %
                                exceptionPolicy)
                else:
                    accessor = ("(GenericMethod<NormalThisPolicy, %s>)" %
                                exceptionPolicy)
            else:
                if m.get("returnsPromise", False):
                    jitinfo = "&%s_methodinfo" % accessor
                    accessor = "StaticMethodPromiseWrapper"
                else:
                    jitinfo = "nullptr"

        return (m["name"], accessor, jitinfo, m["length"], flags(m, unforgeable), selfHostedName)

    @staticmethod
    def condition(m, d):
        return m["condition"]

    def generateArray(self, array, name):
        if len(array) == 0:
            return ""

        return self.generatePrefableArray(
            array, name,
            self.formatSpec,
            '  JS_FS_END',
            'JSFunctionSpec',
            self.condition, functools.partial(self.specData, unforgeable=self.unforgeable))


def isNonExposedNavigatorObjectGetter(attr, descriptor):
    return (attr.navigatorObjectGetter and
            not descriptor.getDescriptor(attr.type.inner.identifier.name).register)

class AttrDefiner(PropertyDefiner):
    def __init__(self, descriptor, name, crossOriginOnly, static, unforgeable=False):
        assert not (static and unforgeable)
        PropertyDefiner.__init__(self, descriptor, name)
        self.name = name
        # Ignore non-static attributes for interfaces without a proto object
        if descriptor.interface.hasInterfacePrototypeObject() or static:
            idlAttrs = [m for m in descriptor.interface.members if
                        m.isAttr() and m.isStatic() == static and
                        MemberIsUnforgeable(m, descriptor) == unforgeable and
                        (not crossOriginOnly or m.getExtendedAttribute("CrossOriginReadable") or
                         m.getExtendedAttribute("CrossOriginWritable")) and
                        not isNonExposedNavigatorObjectGetter(m, descriptor)]
        else:
            idlAttrs = []

        attributes = []
        for attr in idlAttrs:
            attributes.extend(self.attrData(attr, unforgeable))
        self.chrome = [m for m in attributes if isChromeOnly(m["attr"])]
        self.regular = [m for m in attributes if not isChromeOnly(m["attr"])]
        self.static = static

        if static:
            if not descriptor.interface.hasInterfaceObject():
                # static attributes go on the interface object
                assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
        else:
            if not descriptor.interface.hasInterfacePrototypeObject():
                # non-static attributes go on the interface prototype object
                assert not self.hasChromeOnly() and not self.hasNonChromeOnly()

    @staticmethod
    def attrData(attr, unforgeable=False, overrideFlags=None):
        if overrideFlags is None:
            permanent = " | JSPROP_PERMANENT" if unforgeable else ""
            flags = EnumerabilityFlags(attr) + permanent
        else:
            flags = overrideFlags
        return ({"name": name, "attr": attr, "flags": flags} for name in [attr.identifier.name] + attr.bindingAliases)

    @staticmethod
    def condition(m, d):
        return PropertyDefiner.getControllingCondition(m["attr"], d)

    @staticmethod
    def specData(entry, descriptor, static=False, crossOriginOnly=False):
        def getter(attr):
            if crossOriginOnly and not attr.getExtendedAttribute("CrossOriginReadable"):
                return "nullptr, nullptr"
            if static:
                if attr.type.isPromise():
                    raise TypeError("Don't know how to handle "
                                    "static Promise-returning "
                                    "attribute %s.%s" %
                                    (self.descriptor.name,
                                     attr.identifier.name))
                accessor = 'get_' + IDLToCIdentifier(attr.identifier.name)
                jitinfo = "nullptr"
            else:
                if attr.type.isPromise():
                    exceptionPolicy = "ConvertExceptionsToPromises"
                else:
                    exceptionPolicy = "ThrowExceptions"

                if attr.hasLenientThis():
                    if attr.getExtendedAttribute("CrossOriginReadable"):
                        raise TypeError("Can't handle lenient cross-origin "
                                        "readable attribute %s.%s" %
                                        (self.descriptor.name,
                                         attr.identifier.name))
                    accessor = ("GenericGetter<LenientThisPolicy, %s>" %
                                exceptionPolicy)
                elif attr.getExtendedAttribute("CrossOriginReadable"):
                    accessor = ("GenericGetter<CrossOriginThisPolicy, %s>" %
                                exceptionPolicy)
                elif descriptor.interface.isOnGlobalProtoChain():
                    accessor = ("GenericGetter<MaybeGlobalThisPolicy, %s>" %
                                exceptionPolicy)
                else:
                    accessor = ("GenericGetter<NormalThisPolicy, %s>" %
                                exceptionPolicy)
                jitinfo = ("&%s_getterinfo" %
                           IDLToCIdentifier(attr.identifier.name))
            return "%s, %s" % \
                   (accessor, jitinfo)

        def setter(attr):
            if (attr.readonly and
                attr.getExtendedAttribute("PutForwards") is None and
                attr.getExtendedAttribute("Replaceable") is None and
                attr.getExtendedAttribute("LenientSetter") is None):
                return "nullptr, nullptr"
            if crossOriginOnly and not attr.getExtendedAttribute("CrossOriginWritable"):
                return "nullptr, nullptr"
            if static:
                accessor = 'set_' + IDLToCIdentifier(attr.identifier.name)
                jitinfo = "nullptr"
            else:
                if attr.hasLenientThis():
                    if attr.getExtendedAttribute("CrossOriginWritable"):
                        raise TypeError("Can't handle lenient cross-origin "
                                        "writable attribute %s.%s" %
                                        (descriptor.name,
                                         attr.identifier.name))
                    accessor = "GenericSetter<LenientThisPolicy>"
                elif attr.getExtendedAttribute("CrossOriginWritable"):
                    accessor = "GenericSetter<CrossOriginThisPolicy>"
                elif descriptor.interface.isOnGlobalProtoChain():
                    accessor = "GenericSetter<MaybeGlobalThisPolicy>"
                else:
                    accessor = "GenericSetter<NormalThisPolicy>"
                jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name)
            return "%s, %s" % \
                   (accessor, jitinfo)

        name, attr, flags = entry["name"], entry["attr"], entry["flags"]
        return (name, flags, getter(attr), setter(attr))

    @staticmethod
    def formatSpec(fields):
        return '  JSPropertySpec::nativeAccessors("%s", %s, %s, %s)' % fields

    def generateArray(self, array, name):
        if len(array) == 0:
            return ""

        return self.generatePrefableArray(
            array, name, self.formatSpec,
            '  JS_PS_END',
            'JSPropertySpec',
            self.condition, functools.partial(self.specData, static=self.static))


class ConstDefiner(PropertyDefiner):
    """
    A class for definining constants on the interface object
    """
    def __init__(self, descriptor, name):
        PropertyDefiner.__init__(self, descriptor, name)
        self.name = name
        constants = [m for m in descriptor.interface.members if m.isConst()]
        self.chrome = [m for m in constants if isChromeOnly(m)]
        self.regular = [m for m in constants if not isChromeOnly(m)]

    def generateArray(self, array, name):
        if len(array) == 0:
            return ""

        def specData(const, descriptor):
            return (const.identifier.name,
                    convertConstIDLValueToJSVal(const.value))

        return self.generatePrefableArray(
            array, name,
            lambda fields: '  { "%s", %s }' % fields,
            '  { 0, JS::UndefinedValue() }',
            'ConstantSpec',
            PropertyDefiner.getControllingCondition, specData)


class PropertyArrays():
    def __init__(self, descriptor, crossOriginOnly=False):
        self.staticMethods = MethodDefiner(descriptor, "StaticMethods", crossOriginOnly,
                                           static=True)
        self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes", crossOriginOnly,
                                       static=True)
        self.methods = MethodDefiner(descriptor, "Methods", crossOriginOnly, static=False)
        self.attrs = AttrDefiner(descriptor, "Attributes", crossOriginOnly, static=False)
        self.unforgeableMethods = MethodDefiner(descriptor, "UnforgeableMethods",
                                                crossOriginOnly, static=False,
                                                unforgeable=True)
        self.unforgeableAttrs = AttrDefiner(descriptor, "UnforgeableAttributes",
                                            crossOriginOnly, static=False,
                                            unforgeable=True)
        self.consts = ConstDefiner(descriptor, "Constants")

    @staticmethod
    def arrayNames():
        return ["staticMethods", "staticAttrs", "methods", "attrs",
                "unforgeableMethods", "unforgeableAttrs", "consts"]

    def hasChromeOnly(self):
        return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())

    def hasNonChromeOnly(self):
        return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())

    def __str__(self):
        define = ""
        for array in self.arrayNames():
            define += str(getattr(self, array))
        return define


class CGConstDefinition(CGThing):
    """
    Given a const member of an interface, return the C++ static const definition
    for the member. Should be part of the interface namespace in the header
    file.
    """
    def __init__(self, member):
        assert (member.isConst() and
                member.value.type.isPrimitive() and
                not member.value.type.nullable())

        name = CppKeywords.checkMethodName(IDLToCIdentifier(member.identifier.name))
        tag = member.value.type.tag()
        value = member.value.value
        if tag == IDLType.Tags.bool:
            value = toStringBool(member.value.value)
        self.const = "static const %s %s = %s;" % (builtinNames[tag],
                                                   name,
                                                   value)
        if member.getExtendedAttribute("NeedsWindowsUndef"):
            self.const = fill(
                """
                #ifdef XP_WIN
                #undef ${name}
                #endif // XP_WIN
                ${constDecl}
                """,
                name=name,
                constDecl=self.const)

    def declare(self):
        return self.const

    def define(self):
        return ""

    def deps(self):
        return []


class CGNativeProperties(CGList):
    def __init__(self, descriptor, properties):
        def generateNativeProperties(name, chrome):
            def check(p):
                return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()

            nativePropsInts = []
            nativePropsPtrs = []
            nativePropsDuos = []

            duosOffset = 0
            idsOffset = 0
            for array in properties.arrayNames():
                propertyArray = getattr(properties, array)
                if check(propertyArray):
                    varName = propertyArray.variableName(chrome)
                    bitfields = "true,  %d /* %s */" % (duosOffset, varName)
                    duosOffset += 1
                    nativePropsInts.append(CGGeneric(bitfields))

                    if propertyArray.usedForXrays():
                        ids = "&%s_propertyInfos[%d]" % (name, idsOffset)
                        idsOffset += propertyArray.length(chrome)
                    else:
                        ids = "nullptr"
                    duo = "{ %s, %s }" % (varName, ids)
                    nativePropsDuos.append(CGGeneric(duo))
                else:
                    bitfields = "false, 0"
                    nativePropsInts.append(CGGeneric(bitfields))

            iteratorAliasIndex = -1
            for index, item in enumerate(properties.methods.regular):
                if item.get("hasIteratorAlias"):
                    iteratorAliasIndex = index
                    break
            nativePropsInts.append(CGGeneric(str(iteratorAliasIndex)))

            nativePropsDuos = \
                [CGWrapper(CGIndenter(CGList(nativePropsDuos, ",\n")),
                           pre='{\n', post='\n}')]

            pre = ("static const NativePropertiesN<%d> %s = {\n" %
                   (duosOffset, name))
            post = "\n};\n"
            if descriptor.wantsXrays:
                pre = fill(
                    """
                    static uint16_t ${name}_sortedPropertyIndices[${size}];
                    static PropertyInfo ${name}_propertyInfos[${size}];

                    $*{pre}
                    """,
                    name=name,
                    size=idsOffset,
                    pre=pre)
                if iteratorAliasIndex > 0:
                    # The iteratorAliasMethodIndex is a signed integer, so the
                    # max value it can store is 2^(nbits-1)-1.
                    post = fill(
                        """
                        $*{post}
                        static_assert(${iteratorAliasIndex} < 1ull << (CHAR_BIT * sizeof(${name}.iteratorAliasMethodIndex) - 1),
                            "We have an iterator alias index that is oversized");
                        """,
                        post=post,
                        iteratorAliasIndex=iteratorAliasIndex,
                        name=name)
                post = fill(
                    """
                    $*{post}
                    static_assert(${propertyInfoCount} < 1ull << CHAR_BIT * sizeof(${name}.propertyInfoCount),
                        "We have a property info count that is oversized");
                    """,
                    post=post,
                    propertyInfoCount=idsOffset,
                    name=name)
                nativePropsInts.append(CGGeneric("%d" % idsOffset))
                nativePropsPtrs.append(CGGeneric("%s_sortedPropertyIndices" % name))
            else:
                nativePropsInts.append(CGGeneric("0"))
                nativePropsPtrs.append(CGGeneric("nullptr"))
            nativeProps = nativePropsInts + nativePropsPtrs + nativePropsDuos
            return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")),
                             pre=pre, post=post)

        nativeProperties = []
        if properties.hasNonChromeOnly():
            nativeProperties.append(
                generateNativeProperties("sNativeProperties", False))
        if properties.hasChromeOnly():
            nativeProperties.append(
                generateNativeProperties("sChromeOnlyNativeProperties", True))

        CGList.__init__(self, nativeProperties, "\n")

    def declare(self):
        return ""

    def define(self):
        return CGList.define(self)


class CGCollectJSONAttributesMethod(CGAbstractMethod):
    """
    Generate the CollectJSONAttributes method for an interface descriptor
    """
    def __init__(self, descriptor, toJSONMethod):
        args = [Argument('JSContext*', 'cx'),
                Argument('JS::Handle<JSObject*>', 'obj'),
                Argument('%s*' % descriptor.nativeType, 'self'),
                Argument('JS::Rooted<JSObject*>&', 'result')]
        CGAbstractMethod.__init__(self, descriptor, 'CollectJSONAttributes',
                                  'bool', args, canRunScript=True)
        self.toJSONMethod = toJSONMethod

    def definition_body(self):
        ret = ''
        interface = self.descriptor.interface
        toJSONCondition = PropertyDefiner.getControllingCondition(self.toJSONMethod,
                                                                  self.descriptor)
        needUnwrappedObj = False
        for m in interface.members:
            if m.isAttr() and not m.isStatic() and m.type.isJSONType():
                getAndDefine = fill(
                    """
                    JS::Rooted<JS::Value> temp(cx);
                    if (!get_${name}(cx, obj, self, JSJitGetterCallArgs(&temp))) {
                      return false;
                    }
                    if (!JS_DefineProperty(cx, result, "${name}", temp, JSPROP_ENUMERATE)) {
                      return false;
                    }
                    """,
                    name=IDLToCIdentifier(m.identifier.name))
                # Make sure we don't include things which are supposed to be
                # disabled.  Things that either don't have disablers or whose
                # disablers match the disablers for our toJSON method can't
                # possibly be disabled, but other things might be.
                condition = PropertyDefiner.getControllingCondition(m, self.descriptor)
                if condition.hasDisablers() and condition != toJSONCondition:
                    needUnwrappedObj = True;
                    ret += fill(
                        """
                        // This is unfortunately a linear scan through sAttributes, but we
                        // only do it for things which _might_ be disabled, which should
                        // help keep the performance problems down.
                        if (IsGetterEnabled(cx, unwrappedObj, (JSJitGetterOp)get_${name}, sAttributes)) {
                          $*{getAndDefine}
                        }
                        """,
                        name=IDLToCIdentifier(m.identifier.name),
                        getAndDefine=getAndDefine)
                else:
                    ret += fill(
                        """
                        { // scope for "temp"
                          $*{getAndDefine}
                        }
                        """,
                        getAndDefine=getAndDefine)
        ret += 'return true;\n'

        if needUnwrappedObj:
            # If we started allowing cross-origin objects here, we'd need to
            # use CheckedUnwrapDynamic and figure out whether it makes sense.
            # But in practice no one is trying to add toJSON methods to those,
            # so let's just guard against it.
            assert not self.descriptor.isMaybeCrossOriginObject()
            ret= fill(
                """
                JS::Rooted<JSObject*> unwrappedObj(cx, js::CheckedUnwrapStatic(obj));
                if (!unwrappedObj) {
                  // How did that happen?  We managed to get called with that
                  // object as "this"!  Just give up on sanity.
                  return false;
                }

                $*{ret}
                """,
                ret=ret);

        return ret


class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
    """
    Generate the CreateInterfaceObjects method for an interface descriptor.

    properties should be a PropertyArrays instance.
    """
    def __init__(self, descriptor, properties, haveUnscopables, haveLegacyWindowAliases):
        args = [Argument('JSContext*', 'aCx'),
                Argument('JS::Handle<JSObject*>', 'aGlobal'),
                Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'),
                Argument('bool', 'aDefineOnGlobal')]
        CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
        self.properties = properties
        self.haveUnscopables = haveUnscopables
        self.haveLegacyWindowAliases = haveLegacyWindowAliases

    def definition_body(self):
        (protoGetter, protoHandleGetter) = InterfacePrototypeObjectProtoGetter(self.descriptor)
        if protoHandleGetter is None:
            parentProtoType = "Rooted"
            getParentProto = "aCx, " + protoGetter
        else:
            parentProtoType = "Handle"
            getParentProto = protoHandleGetter
        getParentProto = getParentProto + "(aCx)"

        (protoGetter, protoHandleGetter) = InterfaceObjectProtoGetter(self.descriptor)
        if protoHandleGetter is None:
            getConstructorProto = "aCx, " + protoGetter
            constructorProtoType = "Rooted"
        else:
            getConstructorProto = protoHandleGetter
            constructorProtoType = "Handle"
        getConstructorProto += "(aCx)"

        needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
        needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject()

        # if we don't need to create anything, why are we generating this?
        assert needInterfaceObject or needInterfacePrototypeObject

        getParentProto = fill(
            """
            JS::${type}<JSObject*> parentProto(${getParentProto});
            if (!parentProto) {
              return;
            }
            """,
            type=parentProtoType,
            getParentProto=getParentProto)

        getConstructorProto = fill(
            """
            JS::${type}<JSObject*> constructorProto(${getConstructorProto});
            if (!constructorProto) {
              return;
            }
            """,
            type=constructorProtoType,
            getConstructorProto=getConstructorProto)

        idsToInit = []
        # There is no need to init any IDs in bindings that don't want Xrays.
        if self.descriptor.wantsXrays:
            if self.properties.hasNonChromeOnly():
                idsToInit.append("sNativeProperties")
            if self.properties.hasChromeOnly():
                idsToInit.append("sChromeOnlyNativeProperties")
        if len(idsToInit) > 0:
            initIdCalls = ["!InitIds(aCx, %s.Upcast())" % (properties)
                           for properties in idsToInit]
            idsInitedFlag = CGGeneric("static bool sIdsInited = false;\n")
            setFlag = CGGeneric("sIdsInited = true;\n")
            initIdConditionals = [CGIfWrapper(CGGeneric("return;\n"), call)
                                  for call in initIdCalls]
            initIds = CGList([idsInitedFlag,
                              CGIfWrapper(CGList(initIdConditionals + [setFlag]),
                                          "!sIdsInited && NS_IsMainThread()")])
        else:
            initIds = None

        if self.descriptor.interface.ctor():
            constructArgs = methodLength(self.descriptor.interface.ctor())
        else:
            constructArgs = 0
        if len(self.descriptor.interface.namedConstructors) > 0:
            namedConstructors = "namedConstructors"
        else:
            namedConstructors = "nullptr"

        if needInterfacePrototypeObject:
            protoClass = "&sPrototypeClass.mBase"
            protoCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)" % self.descriptor.name
            parentProto = "parentProto"
            getParentProto = CGGeneric(getParentProto)
        else:
            protoClass = "nullptr"
            protoCache = "nullptr"
            parentProto = "nullptr"
            getParentProto = None

        if needInterfaceObject:
            interfaceClass = "&sInterfaceObjectClass.mBase"
            interfaceCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)" % self.descriptor.name
            getConstructorProto = CGGeneric(getConstructorProto)
            constructorProto = "constructorProto"
        else:
            # We don't have slots to store the named constructors.
            assert len(self.descriptor.interface.namedConstructors) == 0
            interfaceClass = "nullptr"
            interfaceCache = "nullptr"
            getConstructorProto = None
            constructorProto = "nullptr"

        isGlobal = self.descriptor.isGlobal() is not None
        if self.properties.hasNonChromeOnly():
            properties = "sNativeProperties.Upcast()"
        else:
            properties = "nullptr"
        if self.properties.hasChromeOnly():
            chromeProperties = "sChromeOnlyNativeProperties.Upcast()"
        else:
            chromeProperties = "nullptr"

        toStringTag = self.descriptor.interface.toStringTag
        if toStringTag:
            toStringTag = '"%s"' % toStringTag
        else:
            toStringTag = "nullptr"

        call = fill(
            """
            JS::Heap<JSObject*>* protoCache = ${protoCache};
            JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
            dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
                                        ${protoClass}, protoCache,
                                        ${toStringTag},
                                        ${constructorProto}, ${interfaceClass}, ${constructArgs}, ${namedConstructors},
                                        interfaceCache,
                                        ${properties},
                                        ${chromeProperties},
                                        ${name}, aDefineOnGlobal,
                                        ${unscopableNames},
                                        ${isGlobal},
                                        ${legacyWindowAliases});
            """,
            protoClass=protoClass,
            parentProto=parentProto,
            protoCache=protoCache,
            toStringTag=toStringTag,
            constructorProto=constructorProto,
            interfaceClass=interfaceClass,
            constructArgs=constructArgs,
            namedConstructors=namedConstructors,
            interfaceCache=interfaceCache,
            properties=properties,
            chromeProperties=chromeProperties,
            name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr",
            unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr",
            isGlobal=toStringBool(isGlobal),
            legacyWindowAliases="legacyWindowAliases" if self.haveLegacyWindowAliases else "nullptr")

        # If we fail after here, we must clear interface and prototype caches
        # using this code: intermediate failure must not expose the interface in
        # partially-constructed state.  Note that every case after here needs an
        # interface prototype object.
        failureCode = dedent(
            """
            *protoCache = nullptr;
            if (interfaceCache) {
              *interfaceCache = nullptr;
            }
            return;
            """)

        aliasedMembers = [m for m in self.descriptor.interface.members if m.isMethod() and m.aliases]
        if aliasedMembers:
            assert needInterfacePrototypeObject

            def defineAlias(alias):
                if alias == "@@iterator":
                    symbolJSID = "SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::iterator))"
                    getSymbolJSID = CGGeneric(fill("JS::Rooted<jsid> iteratorId(aCx, ${symbolJSID});",
                                                   symbolJSID=symbolJSID))
                    defineFn = "JS_DefinePropertyById"
                    prop = "iteratorId"
                    enumFlags = "0" # Not enumerable, per spec.
                elif alias.startswith("@@"):
                    raise TypeError("Can't handle any well-known Symbol other than @@iterator")
                else:
                    getSymbolJSID = None
                    defineFn = "JS_DefineProperty"
                    prop = '"%s"' % alias
                    # XXX If we ever create non-enumerable properties that can
                    #     be aliased, we should consider making the aliases
                    #     match the enumerability of the property being aliased.
                    enumFlags = "JSPROP_ENUMERATE"
                return CGList([
                    getSymbolJSID,
                    CGGeneric(fill(
                        """
                        if (!${defineFn}(aCx, proto, ${prop}, aliasedVal, ${enumFlags})) {
                          $*{failureCode}
                        }
                        """,
                        defineFn=defineFn,
                        prop=prop,
                        enumFlags=enumFlags,
                        failureCode=failureCode))
                ], "\n")

            def defineAliasesFor(m):
                return CGList([
                    CGGeneric(fill(
                        """
                        if (!JS_GetProperty(aCx, proto, \"${prop}\", &aliasedVal)) {
                          $*{failureCode}
                        }
                        """,
                        failureCode=failureCode,
                        prop=m.identifier.name))
                ] + [defineAlias(alias) for alias in sorted(m.aliases)])

            defineAliases = CGList([
                CGGeneric(fill("""
                    // Set up aliases on the interface prototype object we just created.
                    JS::Handle<JSObject*> proto = GetProtoObjectHandle(aCx);
                    if (!proto) {
                      $*{failureCode}
                    }

                    """,
                    failureCode=failureCode)),
                CGGeneric("JS::Rooted<JS::Value> aliasedVal(aCx);\n\n")
            ] + [defineAliasesFor(m) for m in sorted(aliasedMembers)])
        else:
            defineAliases = None

        # Globals handle unforgeables directly in Wrap() instead of
        # via a holder.
        if self.descriptor.hasUnforgeableMembers and not self.descriptor.isGlobal():
            assert needInterfacePrototypeObject

            # We want to use the same JSClass and prototype as the object we'll
            # end up defining the unforgeable properties on in the end, so that
            # we can use JS_InitializePropertiesFromCompatibleNativeObject to do
            # a fast copy.  In the case of proxies that's null, because the
            # expando object is a vanilla object, but in the case of other DOM
            # objects it's whatever our class is.
            if self.descriptor.proxy:
                holderClass = "nullptr"
                holderProto = "nullptr"
            else:
                holderClass = "sClass.ToJSClass()"
                holderProto = "*protoCache"
            createUnforgeableHolder = CGGeneric(fill(
                """
                JS::Rooted<JSObject*> unforgeableHolder(aCx);
                {
                  JS::Rooted<JSObject*> holderProto(aCx, ${holderProto});
                  unforgeableHolder = JS_NewObjectWithoutMetadata(aCx, ${holderClass}, holderProto);
                  if (!unforgeableHolder) {
                    $*{failureCode}
                  }
                }
                """,
                holderProto=holderProto,
                holderClass=holderClass,
                failureCode=failureCode))
            defineUnforgeables = InitUnforgeablePropertiesOnHolder(self.descriptor,
                                                                   self.properties,
                                                                   failureCode)
            createUnforgeableHolder = CGList(
                [createUnforgeableHolder, defineUnforgeables])

            installUnforgeableHolder = CGGeneric(dedent(
                """
                if (*protoCache) {
                  js::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE,
                                      JS::ObjectValue(*unforgeableHolder));
                }
                """))

            unforgeableHolderSetup = CGList(
                [createUnforgeableHolder, installUnforgeableHolder], "\n")
        else:
            unforgeableHolderSetup = None

        if (self.descriptor.interface.isOnGlobalProtoChain() and
            needInterfacePrototypeObject):
            makeProtoPrototypeImmutable = CGGeneric(fill(
                """
                if (*${protoCache}) {
                  bool succeeded;
                  JS::Handle<JSObject*> prot = GetProtoObjectHandle(aCx);
                  if (!JS_SetImmutablePrototype(aCx, prot, &succeeded)) {
                    $*{failureCode}
                  }

                  MOZ_ASSERT(succeeded,
                             "making a fresh prototype object's [[Prototype]] "
                             "immutable can internally fail, but it should "
                             "never be unsuccessful");
                }
                """,
                protoCache=protoCache,
                failureCode=failureCode))
        else:
            makeProtoPrototypeImmutable = None

        return CGList(
            [getParentProto, getConstructorProto, initIds,
             CGGeneric(call), defineAliases, unforgeableHolderSetup,
             makeProtoPrototypeImmutable],
            "\n").define()


class CGGetProtoObjectHandleMethod(CGAbstractMethod):
    """
    A method for getting the interface prototype object.
    """
    def __init__(self, descriptor):
        CGAbstractMethod.__init__(
            self, descriptor, "GetProtoObjectHandle",
            'JS::Handle<JSObject*>',
            [Argument('JSContext*', 'aCx')],
            inline=True)

    def definition_body(self):
        return fill(
            """
            /* Get the interface prototype object for this class.  This will create the
               object as needed. */
            return GetPerInterfaceObjectHandle(aCx, prototypes::id::${name},
                                               &CreateInterfaceObjects,
                                               /* aDefineOnGlobal = */ true);

            """,
            name=self.descriptor.name)


class CGGetProtoObjectMethod(CGAbstractMethod):
    """
    A method for getting the interface prototype object.
    """
    def __init__(self, descriptor):
        CGAbstractMethod.__init__(
            self, descriptor, "GetProtoObject", "JSObject*",
            [Argument('JSContext*', 'aCx')])

    def definition_body(self):
        return "return GetProtoObjectHandle(aCx);\n"


class CGGetConstructorObjectHandleMethod(CGAbstractMethod):
    """
    A method for getting the interface constructor object.
    """
    def __init__(self, descriptor):
        CGAbstractMethod.__init__(
            self, descriptor, "GetConstructorObjectHandle",
            'JS::Handle<JSObject*>',
            [Argument('JSContext*', 'aCx'),
             Argument('bool', 'aDefineOnGlobal', 'true')],
            inline=True)

    def definition_body(self):
        return fill(
            """
            /* Get the interface object for this class.  This will create the object as
               needed. */

            return GetPerInterfaceObjectHandle(aCx, constructors::id::${name},
                                               &CreateInterfaceObjects,
                                               aDefineOnGlobal);
            """,
            name=self.descriptor.name)


class CGGetConstructorObjectMethod(CGAbstractMethod):
    """
    A method for getting the interface constructor object.
    """
    def __init__(self, descriptor):
        CGAbstractMethod.__init__(
            self, descriptor, "GetConstructorObject", "JSObject*",
            [Argument('JSContext*', 'aCx')])

    def definition_body(self):
        return "return GetConstructorObjectHandle(aCx);\n"


class CGGetNamedPropertiesObjectMethod(CGAbstractStaticMethod):
    def __init__(self, descriptor):
        args = [Argument('JSContext*', 'aCx')]
        CGAbstractStaticMethod.__init__(self, descriptor,
                                        'GetNamedPropertiesObject',
                                        'JSObject*', args)

    def definition_body(self):
        parentProtoName = self.descriptor.parentPrototypeName
        if parentProtoName is None:
            getParentProto = ""
            parentProto = "nullptr"
        else:
            getParentProto = fill(
                """
                JS::Rooted<JSObject*> parentProto(aCx, ${parent}::GetProtoObjectHandle(aCx));
                if (!parentProto) {
                  return nullptr;
                }
                """,
                parent=toBindingNamespace(parentProtoName))
            parentProto = "parentProto"
        return fill(
            """
            /* Make sure our global is sane.  Hopefully we can remove this sometime */
            JSObject* global = JS::CurrentGlobalOrNull(aCx);
            if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
              return nullptr;
            }

            /* Check to see whether the named properties object has already been created */
            ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);

            JS::Heap<JSObject*>& namedPropertiesObject = protoAndIfaceCache.EntrySlotOrCreate(namedpropertiesobjects::id::${ifaceName});
            if (!namedPropertiesObject) {
              $*{getParentProto}
              namedPropertiesObject = ${nativeType}::CreateNamedPropertiesObject(aCx, ${parentProto});
              DebugOnly<const DOMIfaceAndProtoJSClass*> clasp =
                DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(namedPropertiesObject));
              MOZ_ASSERT(clasp->mType == eNamedPropertiesObject,
                         "Expected ${nativeType}::CreateNamedPropertiesObject to return a named properties object");
              MOZ_ASSERT(clasp->mNativeHooks,
                         "The named properties object for ${nativeType} should have NativePropertyHooks.");
              MOZ_ASSERT(!clasp->mNativeHooks->mResolveOwnProperty,
                         "Shouldn't resolve the properties of the named properties object for ${nativeType} for Xrays.");
              MOZ_ASSERT(!clasp->mNativeHooks->mEnumerateOwnProperties,
                         "Shouldn't enumerate the properties of the named properties object for ${nativeType} for Xrays.");
            }
            return namedPropertiesObject.get();
            """,
            getParentProto=getParentProto,
            ifaceName=self.descriptor.name,
            parentProto=parentProto,
            nativeType=self.descriptor.nativeType)


def getConditionList(idlobj, cxName, objName):
    """
    Get the list of conditions for idlobj (to be used in "is this enabled"
    checks).  This will be returned as a CGList with " &&\n" as the separator,
    for readability.

    objName is the name of the object that we're working with, because some of
    our test functions want that.

    The return value is a possibly-empty CGList of conditions.
    """
    conditions = []
    pref = idlobj.getExtendedAttribute("Pref")
    if pref:
        assert isinstance(pref, list) and len(pref) == 1
        conditions.append("StaticPrefs::%s()" % prefIdentifier(pref[0]))
    if idlobj.getExtendedAttribute("ChromeOnly"):
        conditions.append("nsContentUtils::ThreadsafeIsSystemCaller(%s)" % cxName)
    func = idlobj.getExtendedAttribute("Func")
    if func:
        assert isinstance(func, list) and len(func) == 1
        conditions.append("%s(%s, %s)" % (func[0], cxName, objName))
    if idlobj.getExtendedAttribute("SecureContext"):
        conditions.append("mozilla::dom::IsSecureContextOrObjectIsFromSecureContext(%s, %s)" % (cxName, objName))

    return CGList((CGGeneric(cond) for cond in conditions), " &&\n")


class CGConstructorEnabled(CGAbstractMethod):
    """
    A method for testing whether we should be exposing this interface
    object or navigator property.  This can perform various tests
    depending on what conditions are specified on the interface.
    """
    def __init__(self, descriptor):
        CGAbstractMethod.__init__(self, descriptor,
                                  'ConstructorEnabled', 'bool',
                                  [Argument("JSContext*", "aCx"),
                                   Argument("JS::Handle<JSObject*>", "aObj")])

    def definition_body(self):
        body = CGList([], "\n")

        iface = self.descriptor.interface

        if not iface.isExposedOnMainThread():
            exposedInWindowCheck = dedent(
                """
                MOZ_ASSERT(!NS_IsMainThread(), "Why did we even get called?");
                """)
            body.append(CGGeneric(exposedInWindowCheck))

        if iface.isExposedInSomeButNotAllWorkers():
            workerGlobals = sorted(iface.getWorkerExposureSet())
            workerCondition = CGList((CGGeneric('strcmp(name, "%s")' % workerGlobal)
                                      for workerGlobal in workerGlobals), " && ")
            exposedInWorkerCheck = fill(
                """
                const char* name = js::GetObjectClass(aObj)->name;
                if (${workerCondition}) {
                  return false;
                }
                """, workerCondition=workerCondition.define())
            exposedInWorkerCheck = CGGeneric(exposedInWorkerCheck)
            if iface.isExposedOnMainThread():
                exposedInWorkerCheck = CGIfWrapper(exposedInWorkerCheck,
                                                   "!NS_IsMainThread()")
            body.append(exposedInWorkerCheck)

        conditions = getConditionList(iface, "aCx", "aObj")

        # We should really have some conditions
        assert len(body) or len(conditions)

        conditionsWrapper = ""
        if len(conditions):
            conditionsWrapper = CGWrapper(conditions,
                                          pre="return ",
                                          post=";\n",
                                          reindent=True)
        else:
            conditionsWrapper = CGGeneric("return true;\n")

        body.append(conditionsWrapper)
        return body.define()


def StructuredCloneTag(name):
    return "SCTAG_DOM_%s" % name.upper()


class CGSerializer(CGAbstractStaticMethod):
    """
    Implementation of serialization for things marked [Serializable].
    This gets stored in our DOMJSClass, so it can be static.

    The caller is expected to pass in the object whose DOMJSClass it
    used to get the serializer.
    """
    def __init__(self, descriptor):
        args = [Argument("JSContext*", "aCx"),
                Argument("JSStructuredCloneWriter*", "aWriter"),
                Argument("JS::Handle<JSObject*>", "aObj")]
        CGAbstractStaticMethod.__init__(self, descriptor, "Serialize",
                                        "bool", args)

    def definition_body(self):
        return fill(
            """
            MOZ_ASSERT(IsDOMObject(aObj), "Non-DOM object passed");
            MOZ_ASSERT(GetDOMClass(aObj)->mSerializer == &Serialize,
                       "Wrong object passed");
            return JS_WriteUint32Pair(aWriter, ${tag}, 0) &&
                   UnwrapDOMObject<${type}>(aObj)->WriteStructuredClone(aCx, aWriter);
            """,
            tag=StructuredCloneTag(self.descriptor.name),
            type=self.descriptor.nativeType)


class CGDeserializer(CGAbstractMethod):
    """
    Implementation of deserialization for things marked [Serializable].
    This will need to be accessed from WebIDLSerializable, so can't be static.
    """
    def __init__(self, descriptor):
        args = [Argument("JSContext*", "aCx"),
                Argument("nsIGlobalObject*", "aGlobal"),
                Argument("JSStructuredCloneReader*", "aReader")]
        CGAbstractMethod.__init__(self, descriptor, "Deserialize",
                                  "JSObject*", args)

    def definition_body(self):
        # WrapObject has different signatures depending on whether
        # the object is wrappercached.
        if self.descriptor.wrapperCache:
            wrapCall = dedent(
                """
                result = obj->WrapObject(aCx, nullptr);
                if (!result) {
                  return nullptr;
                }
                """)
        else:
            wrapCall = dedent(
                """
                if (!obj->WrapObject(aCx, nullptr, &result)) {
                  return nullptr;
                }
                """)

        return fill(
            """
            // Protect the result from a moving GC in ~RefPtr
            JS::Rooted<JSObject*> result(aCx);
            {  // Scope for the RefPtr
              RefPtr<${type}> obj = ${type}::ReadStructuredClone(aCx, aGlobal, aReader);
              if (!obj) {
                return nullptr;
              }
              $*{wrapCall}
            }
            return result;
            """,
            type=self.descriptor.nativeType,
            wrapCall=wrapCall)

def CreateBindingJSObject(descriptor, properties):
    objDecl = "BindingJSObjectCreator<%s> creator(aCx);\n" % descriptor.nativeType

    # We don't always need to root obj, but there are a variety
    # of cases where we do, so for simplicity, just always root it.
    if descriptor.proxy:
        if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
            assert not descriptor.isMaybeCrossOriginObject()
            create = dedent(
                """
                aObject->mExpandoAndGeneration.expando.setUndefined();
                JS::Rooted<JS::Value> expandoValue(aCx, JS::PrivateValue(&aObject->mExpandoAndGeneration));
                creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
                                          proto, /* aLazyProto = */ false, aObject,
                                          expandoValue, aReflector);
                """)
        else:
            if descriptor.isMaybeCrossOriginObject():
                proto = "nullptr"
                lazyProto = "true"
            else:
                proto = "proto"
                lazyProto = "false"
            create = fill(
                """
                creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
                                          ${proto}, /* aLazyProto = */ ${lazyProto},
                                          aObject, JS::UndefinedHandleValue, aReflector);
                """,
                proto=proto,
                lazyProto=lazyProto)
    else:
        create = dedent(
            """
            creator.CreateObject(aCx, sClass.ToJSClass(), proto, aObject, aReflector);
            """)
    return objDecl + create + dedent(
        """
        if (!aReflector) {
          return false;
        }
        """)


def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode,
                                      holderName="unforgeableHolder"):
    """
    Define the unforgeable properties on the unforgeable holder for
    the interface represented by descriptor.

    properties is a PropertyArrays instance.

    """
    assert (properties.unforgeableAttrs.hasNonChromeOnly() or
            properties.unforgeableAttrs.hasChromeOnly() or
            properties.unforgeableMethods.hasNonChromeOnly() or
            properties.unforgeableMethods.hasChromeOnly())

    unforgeables = []

    defineUnforgeableAttrs = fill(
        """
        if (!DefineUnforgeableAttributes(aCx, ${holderName}, %s)) {
          $*{failureCode}
        }
        """,
        failureCode=failureCode,
        holderName=holderName)
    defineUnforgeableMethods = fill(
        """
        if (!DefineUnforgeableMethods(aCx, ${holderName}, %s)) {
          $*{failureCode}
        }
        """,
        failureCode=failureCode,
        holderName=holderName)

    unforgeableMembers = [
        (defineUnforgeableAttrs, properties.unforgeableAttrs),
        (defineUnforgeableMethods, properties.unforgeableMethods)
    ]
    for (template, array) in unforgeableMembers:
        if array.hasNonChromeOnly():
            unforgeables.append(CGGeneric(template % array.variableName(False)))
        if array.hasChromeOnly():
            unforgeables.append(
                CGIfWrapper(CGGeneric(template % array.variableName(True)),
                            "nsContentUtils::ThreadsafeIsSystemCaller(aCx)"))

    if descriptor.interface.getExtendedAttribute("Unforgeable"):
        # We do our undefined toPrimitive here, not as a regular property
        # because we don't have a concept of value props anywhere in IDL.
        unforgeables.append(CGGeneric(fill(
            """
            JS::RootedId toPrimitive(aCx,
              SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::toPrimitive)));
            if (!JS_DefinePropertyById(aCx, ${holderName}, toPrimitive,
                                       JS::UndefinedHandleValue,
                                       JSPROP_READONLY | JSPROP_PERMANENT)) {
              $*{failureCode}
            }
            """,
            failureCode=failureCode,
            holderName=holderName)))

    return CGWrapper(CGList(unforgeables), pre="\n")


def CopyUnforgeablePropertiesToInstance(descriptor, failureCode):
    """
    Copy the unforgeable properties from the unforgeable holder for
    this interface to the instance object we have.
    """
    assert not descriptor.isGlobal();

    if not descriptor.hasUnforgeableMembers:
        return ""

    copyCode = [
        CGGeneric(dedent(
            """
            // Important: do unforgeable property setup after we have handed
            // over ownership of the C++ object to obj as needed, so that if
            // we fail and it ends up GCed it won't have problems in the
            // finalizer trying to drop its ownership of the C++ object.
            """))
    ]

    # For proxies, we want to define on the expando object, not directly on the
    # reflector, so we can make sure we don't get confused by named getters.
    if descriptor.proxy:
        copyCode.append(CGGeneric(fill(
            """
            JS::Rooted<JSObject*> expando(aCx,
              DOMProxyHandler::EnsureExpandoObject(aCx, aReflector));
            if (!expando) {
              $*{failureCode}
            }
            """,
            failureCode=failureCode)))
        obj = "expando"
    else:
        obj = "aReflector"

    copyCode.append(CGGeneric(fill(
        """
        JS::Rooted<JSObject*> unforgeableHolder(aCx,
          &js::GetReservedSlot(canonicalProto, DOM_INTERFACE_PROTO_SLOTS_BASE).toObject());
        if (!JS_InitializePropertiesFromCompatibleNativeObject(aCx, ${obj}, unforgeableHolder)) {
          $*{failureCode}
        }
        """,
        obj=obj,
        failureCode=failureCode)))

    return CGWrapper(CGList(copyCode), pre="\n").define()


def AssertInheritanceChain(descriptor):
    asserts = ""
    iface = descriptor.interface
    while iface:
        desc = descriptor.getDescriptor(iface.identifier.name)
        asserts += (
            "MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
            "           reinterpret_cast<%s*>(aObject),\n"
            "           \"Multiple inheritance for %s is broken.\");\n" %
            (desc.nativeType, desc.nativeType, desc.nativeType))
        iface = iface.parent
    asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
    return asserts


def InitMemberSlots(descriptor, failureCode):
    """
    Initialize member slots on our JS object if we're supposed to have some.

    Note that this is called after the SetWrapper() call in the
    wrapperCache case, since that can affect how our getters behave
    and we plan to invoke them here.  So if we fail, we need to
    ClearWrapper.
    """
    if not descriptor.interface.hasMembersInSlots():
        return ""
    return fill(
        """
        if (!UpdateMemberSlots(aCx, aReflector, aObject)) {
          $*{failureCode}
        }
        """,
        failureCode=failureCode)


def DeclareProto(descriptor):
    """
    Declare the canonicalProto and proto we have for our wrapping operation.
    """
    preamble = dedent(
        """
        JS::Handle<JSObject*> canonicalProto = GetProtoObjectHandle(aCx);
        if (!canonicalProto) {
          return false;
        }
        JS::Rooted<JSObject*> proto(aCx);
        """)
    if descriptor.isMaybeCrossOriginObject():
        return preamble + dedent(
            """
            MOZ_ASSERT(!aGivenProto,
                       "Shouldn't have constructors on cross-origin objects");
            // Set proto to canonicalProto to avoid preserving our wrapper if
            // we don't have to.
            proto = canonicalProto;
            """)

    return preamble + dedent(
        """
        if (aGivenProto) {
          proto = aGivenProto;
          // Unfortunately, while aGivenProto was in the compartment of aCx
          // coming in, we changed compartments to that of "parent" so may need
          // to wrap the proto here.
          if (js::GetContextCompartment(aCx) != js::GetObjectCompartment(proto)) {
            if (!JS_WrapObject(aCx, &proto)) {
              return false;
            }
          }
        } else {
          proto = canonicalProto;
        }
        """)


class CGWrapWithCacheMethod(CGAbstractMethod):
    """
    Create a wrapper JSObject for a given native that implements nsWrapperCache.

    properties should be a PropertyArrays instance.
    """
    def __init__(self, descriptor, properties):
        assert descriptor.interface.hasInterfacePrototypeObject()
        args = [Argument('JSContext*', 'aCx'),
                Argument(descriptor.nativeType + '*', 'aObject'),
                Argument('nsWrapperCache*', 'aCache'),
                Argument('JS::Handle<JSObject*>', 'aGivenProto'),
                Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
        CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
        self.properties = properties

    def definition_body(self):
        failureCode = dedent(
            """
            aCache->ReleaseWrapper(aObject);
            aCache->ClearWrapper();
            return false;
            """)

        if self.descriptor.proxy:
            finalize = "DOMProxyHandler::getInstance()->finalize"
        else:
            finalize = FINALIZE_HOOK_NAME

        return fill(
            """
            static_assert(!IsBaseOf<NonRefcountedDOMObject, ${nativeType}>::value,
                          "Shouldn't have wrappercached things that are not refcounted.");
            $*{assertInheritance}
            MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));
            MOZ_ASSERT(!aCache->GetWrapper(),
                       "You should probably not be using Wrap() directly; use "
                       "GetOrCreateDOMReflector instead");

            MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
                       "nsISupports must be on our primary inheritance chain");

            // If the wrapper cache contains a dead reflector then finalize that
            // now, ensuring that the finalizer for the old reflector always
            // runs before the new reflector is created and attached. This
            // avoids the awkward situation where there are multiple reflector
            // objects that contain pointers to the same native.

            if (JSObject* oldReflector = aCache->GetWrapperMaybeDead()) {
              ${finalize}(nullptr /* unused */, oldReflector);
              MOZ_ASSERT(!aCache->GetWrapperMaybeDead());
            }

            JS::Rooted<JSObject*> global(aCx, FindAssociatedGlobal(aCx, aObject->GetParentObject()));
            if (!global) {
              return false;
            }
            MOZ_ASSERT(JS_IsGlobalObject(global));
            JS::AssertObjectIsNotGray(global);

            // That might have ended up wrapping us already, due to the wonders
            // of XBL.  Check for that, and bail out as needed.
            aReflector.set(aCache->GetWrapper());
            if (aReflector) {
            #ifdef DEBUG
              AssertReflectorHasGivenProto(aCx, aReflector, aGivenProto);
            #endif // DEBUG
              return true;
            }

            JSAutoRealm ar(aCx, global);
            $*{declareProto}

            $*{createObject}

            aCache->SetWrapper(aReflector);
            $*{unforgeable}
            $*{slots}
            creator.InitializationSucceeded();

            MOZ_ASSERT(aCache->GetWrapperPreserveColor() &&
                       aCache->GetWrapperPreserveColor() == aReflector);
            // If proto != canonicalProto, we have to preserve our wrapper;
            // otherwise we won't be able to properly recreate it later, since
            // we won't know what proto to use.  Note that we don't check
            // aGivenProto here, since it's entirely possible (and even
            // somewhat common) to have a non-null aGivenProto which is the
            // same as canonicalProto.
            if (proto != canonicalProto) {
              PreserveWrapper(aObject);
            }

            return true;
            """,
            nativeType=self.descriptor.nativeType,
            assertInheritance=AssertInheritanceChain(self.descriptor),
            declareProto=DeclareProto(self.descriptor),
            createObject=CreateBindingJSObject(self.descriptor, self.properties),
            unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor,
                                                            failureCode),
            slots=InitMemberSlots(self.descriptor, failureCode),
            finalize=finalize)


class CGWrapMethod(CGAbstractMethod):
    def __init__(self, descriptor):
        # XXX can we wrap if we don't have an interface prototype object?
        assert descriptor.interface.hasInterfacePrototypeObject()
        args = [Argument('JSContext*', 'aCx'),
                Argument('T*', 'aObject'),
                Argument('JS::Handle<JSObject*>', 'aGivenProto')]
        CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args,
                                  inline=True, templateArgs=["class T"])

    def definition_body(self):
        return dedent("""
            JS::Rooted<JSObject*> reflector(aCx);
            return Wrap(aCx, aObject, aObject, aGivenProto, &reflector) ? reflector.get() : nullptr;
            """)


class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
    """
    Create a wrapper JSObject for a given native that does not implement
    nsWrapperCache.

    properties should be a PropertyArrays instance.
    """
    def __init__(self, descriptor, properties):
        # XXX can we wrap if we don't have an interface prototype object?
        assert descriptor.interface.hasInterfacePrototypeObject()
        args = [Argument('JSContext*', 'aCx'),
                Argument(descriptor.nativeType + '*', 'aObject'),
                Argument('JS::Handle<JSObject*>', 'aGivenProto'),
                Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
        CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
        self.properties = properties

    def definition_body(self):
        failureCode = "return false;\n"

        return fill(
            """
            $*{assertions}
            MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));

            JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
            $*{declareProto}

            $*{createObject}

            $*{unforgeable}

            $*{slots}

            creator.InitializationSucceeded();
            return true;
            """,
            assertions=AssertInheritanceChain(self.descriptor),
            declareProto=DeclareProto(self.descriptor),
            createObject=CreateBindingJSObject(self.descriptor, self.properties),
            unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor,
                                                            failureCode),
            slots=InitMemberSlots(self.descriptor, failureCode))


class CGWrapGlobalMethod(CGAbstractMethod):
    """
    Create a wrapper JSObject for a global.  The global must implement
    nsWrapperCache.

    properties should be a PropertyArrays instance.
    """
    def __init__(self, descriptor, properties):
        assert descriptor.interface.hasInterfacePrototypeObject()
        args = [Argument('JSContext*', 'aCx'),
                Argument(descriptor.nativeType + '*', 'aObject'),
                Argument('nsWrapperCache*', 'aCache'),
                Argument('JS::RealmOptions&', 'aOptions'),
                Argument('JSPrincipals*', 'aPrincipal'),
                Argument('bool', 'aInitStandardClasses'),
                Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
        CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
        self.descriptor = descriptor
        self.properties = properties

    def definition_body(self):
        if self.properties.hasNonChromeOnly():
            properties = "sNativeProperties.Upcast()"
        else:
            properties = "nullptr"
        if self.properties.hasChromeOnly():
            chromeProperties = "nsContentUtils::ThreadsafeIsSystemCaller(aCx) ? sChromeOnlyNativeProperties.Upcast() : nullptr"
        else:
            chromeProperties = "nullptr"

        failureCode = dedent(
            """
            aCache->ReleaseWrapper(aObject);
            aCache->ClearWrapper();
            return false;
            """);

        if self.descriptor.hasUnforgeableMembers:
            unforgeable = InitUnforgeablePropertiesOnHolder(
                self.descriptor, self.properties, failureCode,
                "aReflector").define();
        else:
            unforgeable = ""

        return fill(
            """
            $*{assertions}
            MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
                       "nsISupports must be on our primary inheritance chain");

            if (!CreateGlobal<${nativeType}, GetProtoObjectHandle>(aCx,
                                             aObject,
                                             aCache,
                                             sClass.ToJSClass(),
                                             aOptions,
                                             aPrincipal,
                                             aInitStandardClasses,
                                             aReflector)) {
              $*{failureCode}
            }

            // aReflector is a new global, so has a new realm.  Enter it
            // before doing anything with it.
            JSAutoRealm ar(aCx, aReflector);

            if (!DefineProperties(aCx, aReflector, ${properties}, ${chromeProperties})) {
              $*{failureCode}
            }
            $*{unforgeable}

            $*{slots}

            return true;
            """,
            assertions=AssertInheritanceChain(self.descriptor),
            nativeType=self.descriptor.nativeType,
            properties=properties,
            chromeProperties=chromeProperties,
            failureCode=failureCode,
            unforgeable=unforgeable,
            slots=InitMemberSlots(self.descriptor, failureCode))


class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod):
    def __init__(self, descriptor):
        args = [Argument('JSContext*', 'aCx'),
                Argument('JS::Handle<JSObject*>', 'aWrapper'),
                Argument(descriptor.nativeType + '*', 'aObject')]
        CGAbstractStaticMethod.__init__(self, descriptor, 'UpdateMemberSlots', 'bool', args)

    def definition_body(self):
        body = ("JS::Rooted<JS::Value> temp(aCx);\n"
                "JSJitGetterCallArgs args(&temp);\n")
        for m in self.descriptor.interface.members:
            if m.isAttr() and m.getExtendedAttribute("StoreInSlot"):
                # Skip doing this for the "window" and "self" attributes on the
                # Window interface, because those can't be gotten safely until
                # we have hooked it up correctly to the outer window.  The
                # window code handles doing the get itself.
                if (self.descriptor.interface.identifier.name == "Window" and
                    (m.identifier.name == "window" or m.identifier.name == "self")):
                    continue
                body += fill(
                    """

                    static_assert(${slot} < js::shadow::Object::MAX_FIXED_SLOTS,
                                  "Not enough fixed slots to fit '${interface}.${member}.  Ion's visitGetDOMMemberV/visitGetDOMMemberT assume StoreInSlot things are all in fixed slots.");
                    if (!get_${member}(aCx, aWrapper, aObject, args)) {
                      return false;
                    }
                    // Getter handled setting our reserved slots
                    """,
                    slot=memberReservedSlot(m, self.descriptor),
                    interface=self.descriptor.interface.identifier.name,
                    member=m.identifier.name)

        body += "\nreturn true;\n"
        return body


class CGClearCachedValueMethod(CGAbstractMethod):
    def __init__(self, descriptor, member):
        self.member = member
        # If we're StoreInSlot, we'll need to call the getter
        if member.getExtendedAttribute("StoreInSlot"):
            args = [Argument('JSContext*', 'aCx')]
            returnType = 'bool'
        else:
            args = []
            returnType = 'void'
        args.append(Argument(descriptor.nativeType + '*', 'aObject'))
        name = MakeClearCachedValueNativeName(member)
        CGAbstractMethod.__init__(self, descriptor, name, returnType, args)

    def definition_body(self):
        slotIndex = memberReservedSlot(self.member, self.descriptor)
        if self.member.getExtendedAttribute("StoreInSlot"):
            # We have to root things and save the old value in case
            # regetting fails, so we can restore it.
            declObj = "JS::Rooted<JSObject*> obj(aCx);\n"
            noopRetval = " true"
            saveMember = (
                "JS::Rooted<JS::Value> oldValue(aCx, js::GetReservedSlot(obj, %s));\n" %
                slotIndex)
            regetMember = fill(
                """
                JS::Rooted<JS::Value> temp(aCx);
                JSJitGetterCallArgs args(&temp);
                JSAutoRealm ar(aCx, obj);
                if (!get_${name}(aCx, obj, aObject, args)) {
                  js::SetReservedSlot(obj, ${slotIndex}, oldValue);
                  return false;
                }
                return true;
                """,
                name=self.member.identifier.name,
                slotIndex=slotIndex)
        else:
            declObj = "JSObject* obj;\n"
            noopRetval = ""
            saveMember = ""
            regetMember = ""

        if self.descriptor.wantsXrays:
            clearXrayExpandoSlots = fill(
                """
                xpc::ClearXrayExpandoSlots(obj, ${xraySlotIndex});
                """,
                xraySlotIndex=memberXrayExpandoReservedSlot(self.member,
                                                            self.descriptor))
        else :
            clearXrayExpandoSlots = ""

        return fill(
            """
            $*{declObj}
            obj = aObject->GetWrapper();
            if (!obj) {
              return${noopRetval};
            }
            $*{saveMember}
            js::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue());
            $*{clearXrayExpandoSlots}
            $*{regetMember}
            """,
            declObj=declObj,
            noopRetval=noopRetval,
            saveMember=saveMember,
            slotIndex=slotIndex,
            clearXrayExpandoSlots=clearXrayExpandoSlots,
            regetMember=regetMember)


class CGCrossOriginProperties(CGThing):
    def __init__(self, descriptor):
        attrs = []
        methods = []
        for m in descriptor.interface.members:
            if m.isAttr() and (m.getExtendedAttribute("CrossOriginReadable") or
                               m.getExtendedAttribute("CrossOriginWritable")):
                if m.isStatic():
                    raise TypeError("Don't know how to deal with static method %s" %
                                    m.identifier.name)
                if PropertyDefiner.getControllingCondition(m, descriptor).hasDisablers():
                    raise TypeError("Don't know how to deal with disabler for %s" %
                                    m.identifier.name)
                if len(m.bindingAliases) > 0:
                    raise TypeError("Don't know how to deal with aliases for %s" %
                                    m.identifier.name)
                attrs.extend(AttrDefiner.attrData(m, overrideFlags="0"))
            elif m.isMethod() and m.getExtendedAttribute("CrossOriginCallable"):
                if m.isStatic():
                    raise TypeError("Don't know how to deal with static method %s" %
                                    m.identifier.name)
                if PropertyDefiner.getControllingCondition(m, descriptor).hasDisablers():
                    raise TypeError("Don't know how to deal with disabler for %s" %
                                    m.identifier.name)
                if len(m.aliases) > 0:
                    raise TypeError("Don't know how to deal with aliases for %s" %
                                    m.identifier.name)
                methods.append(MethodDefiner.methodData(m, descriptor, overrideFlags="JSPROP_READONLY"))

        if len(attrs) > 0:
            self.attributeSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
                attrs, descriptor, AttrDefiner.formatSpec, '  JS_PS_END\n',
                AttrDefiner.condition, functools.partial(AttrDefiner.specData, crossOriginOnly=True))
        else:
            self.attributeSpecs = [' JS_PS_END\n']
        if len(methods) > 0:
            self.methodSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
                methods, descriptor, MethodDefiner.formatSpec, '  JS_FS_END\n',
                MethodDefiner.condition, MethodDefiner.specData)
        else:
            self.methodSpecs = ['  JS_FS_END\n']

    def declare(self):
        return fill("""
            extern JSPropertySpec sCrossOriginAttributes[${attributesLength}];
            extern JSFunctionSpec sCrossOriginMethods[${methodsLength}];
            """,
            attributesLength=len(self.attributeSpecs),
            methodsLength=len(self.methodSpecs))

    def define(self):
        return fill(
            """
            // We deliberately use brace-elision to make Visual Studio produce better initalization code.
            #if defined(__clang__)
            #pragma clang diagnostic push
            #pragma clang diagnostic ignored "-Wmissing-braces"
            #endif
            JSPropertySpec sCrossOriginAttributes[] = {
              $*{attributeSpecs}
            };
            JSFunctionSpec sCrossOriginMethods[] = {
              $*{methodSpecs}
            };
            #if defined(__clang__)
            #pragma clang diagnostic pop
            #endif
            """,
            attributeSpecs=",\n".join(self.attributeSpecs),
            methodSpecs=",\n".join(self.methodSpecs))


class CGCycleCollectionTraverseForOwningUnionMethod(CGAbstractMethod):
    """
    ImplCycleCollectionUnlink for owning union type.
    """
    def __init__(self, type):
        self.type = type
        args = [Argument("nsCycleCollectionTraversalCallback&", "aCallback"),
                Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion"),
                Argument("const char*", "aName"),
                Argument("uint32_t", "aFlags", "0")]
        CGAbstractMethod.__init__(self, None, "ImplCycleCollectionTraverse", "void", args)

    def deps(self):
        return self.type.getDeps()

    def definition_body(self):
        memberNames = [getUnionMemberName(t)
                       for t in self.type.flatMemberTypes
                       if idlTypeNeedsCycleCollection(t)]
        assert memberNames

        conditionTemplate = 'aUnion.Is%s()'
        functionCallTemplate = 'ImplCycleCollectionTraverse(aCallback, aUnion.GetAs%s(), "m%s", aFlags);\n'

        ifStaments = (CGIfWrapper(CGGeneric(functionCallTemplate % (m, m)),
                                  conditionTemplate % m)
                      for m in memberNames)

        return CGElseChain(ifStaments).define()


class CGCycleCollectionUnlinkForOwningUnionMethod(CGAbstractMethod):
    """
    ImplCycleCollectionUnlink for owning union type.
    """
    def __init__(self, type):
        self.type = type
        args = [Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion")]
        CGAbstractMethod.__init__(self, None, "ImplCycleCollectionUnlink", "void", args)

    def deps(self):
        return self.type.getDeps()

    def definition_body(self):
        return "aUnion.Uninit();\n"


builtinNames = {
    IDLType.Tags.bool: 'bool',
    IDLType.Tags.int8: 'int8_t',
    IDLType.Tags.int16: 'int16_t',
    IDLType.Tags.int32: 'int32_t',
    IDLType.Tags.int64: 'int64_t',
    IDLType.Tags.uint8: 'uint8_t',
    IDLType.Tags.uint16: 'uint16_t',
    IDLType.Tags.uint32: 'uint32_t',
    IDLType.Tags.uint64: 'uint64_t',
    IDLType.Tags.unrestricted_float: 'float',
    IDLType.Tags.float: 'float',
    IDLType.Tags.unrestricted_double: 'double',
    IDLType.Tags.double: 'double'
}

numericSuffixes = {
    IDLType.Tags.int8: '',
    IDLType.Tags.uint8: '',
    IDLType.Tags.int16: '',
    IDLType.Tags.uint16: '',
    IDLType.Tags.int32: '',
    IDLType.Tags.uint32: 'U',
    IDLType.Tags.int64: 'LL',
    IDLType.Tags.uint64: 'ULL',
    IDLType.Tags.unrestricted_float: 'F',
    IDLType.Tags.float: 'F',
    IDLType.Tags.unrestricted_double: '',
    IDLType.Tags.double: ''
}


def numericValue(t, v):
    if (t == IDLType.Tags.unrestricted_double or
        t == IDLType.Tags.unrestricted_float):
        typeName = builtinNames[t]
        if v == float("inf"):
            return "mozilla::PositiveInfinity<%s>()" % typeName
        if v == float("-inf"):
            return "mozilla::NegativeInfinity<%s>()" % typeName
        if math.isnan(v):
            return "mozilla::UnspecifiedNaN<%s>()" % typeName
    return "%s%s" % (v, numericSuffixes[t])


class CastableObjectUnwrapper():
    """
    A class for unwrapping an object stored in a JS Value (or
    MutableHandle<Value> or Handle<Value>) named by the "source" and
    "mutableSource" arguments based on the passed-in descriptor and storing it
    in a variable called by the name in the "target" argument.  The "source"
    argument should be able to produce a Value or Handle<Value>; the
    "mutableSource" argument should be able to produce a MutableHandle<Value>

    codeOnFailure is the code to run if unwrapping fails.

    If isCallbackReturnValue is "JSImpl" and our descriptor is also
    JS-implemented, fall back to just creating the right object if what we
    have isn't one already.
    """
    def __init__(self, descriptor, source, mutableSource, target, codeOnFailure,
                 exceptionCode=None, isCallbackReturnValue=False):
        self.substitution = {
            "type": descriptor.nativeType,
            "protoID": "prototypes::id::" + descriptor.name,
            "target": target,
            "codeOnFailure": codeOnFailure,
            "source": source,
            "mutableSource": mutableSource,
        }

        if (isCallbackReturnValue == "JSImpl" and
            descriptor.interface.isJSImplemented()):
            exceptionCode = exceptionCode or codeOnFailure
            self.substitution["codeOnFailure"] = fill(
                """
                // Be careful to not wrap random DOM objects here, even if
                // they're wrapped in opaque security wrappers for some reason.
                // XXXbz Wish we could check for a JS-implemented object
                // that already has a content reflection...
                if (!IsDOMObject(js::UncheckedUnwrap(&${source}.toObject()))) {
                  nsCOMPtr<nsIGlobalObject> contentGlobal;
                  JS::Handle<JSObject*> callback = CallbackOrNull();
                  if (!callback ||
                      !GetContentGlobalForJSImplementedObject(cx, callback, getter_AddRefs(contentGlobal))) {
                    $*{exceptionCode}
                  }
                  JS::Rooted<JSObject*> jsImplSourceObj(cx, &${source}.toObject());
                  MOZ_RELEASE_ASSERT(!js::IsWrapper(jsImplSourceObj),
                                     "Don't return JS implementations from other compartments");
                  JS::Rooted<JSObject*> jsImplSourceGlobal(cx, JS::GetNonCCWObjectGlobal(jsImplSourceObj));
                  ${target} = new ${type}(jsImplSourceObj, jsImplSourceGlobal, contentGlobal);
                } else {
                  $*{codeOnFailure}
                }
                """,
                exceptionCode=exceptionCode,
                **self.substitution)
        else:
            self.substitution["codeOnFailure"] = codeOnFailure

    def __str__(self):
        substitution = self.substitution.copy()
        substitution["codeOnFailure"] %= {
            'securityError': 'rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO'
        }
        return fill(
            """
            {
              // Our JSContext should be in the right global to do unwrapping in.
              nsresult rv = UnwrapObject<${protoID}, ${type}>(${mutableSource}, ${target}, cx);
              if (NS_FAILED(rv)) {
                $*{codeOnFailure}
              }
            }
            """,
            **substitution)


class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
    """
    As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
    """
    def __init__(self, descriptor, source, mutableSource, target, exceptionCode,
                 isCallbackReturnValue, sourceDescription):
        CastableObjectUnwrapper.__init__(
            self, descriptor, source, mutableSource, target,
            'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n'
            '%s' % (sourceDescription, descriptor.interface.identifier.name,
                    exceptionCode),
            exceptionCode,
            isCallbackReturnValue)


def getCallbackConversionInfo(type, idlObject, isMember, isCallbackReturnValue,
                              isOptional):
    """
    Returns a tuple containing the declType, declArgs, and basic
    conversion for the given callback type, with the given callback
    idl object in the given context (isMember/isCallbackReturnValue/isOptional).
    """
    name = idlObject.identifier.name

    # We can't use fast callbacks if isOptional because then we get an
    # Optional<RootedCallback> thing, which is not transparent to consumers.
    useFastCallback = (not isMember and not isCallbackReturnValue and
                       not isOptional)
    if useFastCallback:
        name = "binding_detail::Fast%s" % name
        rootArgs = ""
        args = "&${val}.toObject(), JS::CurrentGlobalOrNull(cx)"
    else:
        rootArgs = dedent(
            """
            JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject());
            JS::Rooted<JSObject*> tempGlobalRoot(cx, JS::CurrentGlobalOrNull(cx));
            """)
        args = "cx, tempRoot, tempGlobalRoot, GetIncumbentGlobal()"

    if type.nullable() or isCallbackReturnValue:
        declType = CGGeneric("RefPtr<%s>" % name)
    else:
        declType = CGGeneric("OwningNonNull<%s>" % name)

    if useFastCallback:
        declType = CGTemplatedType("RootedCallback", declType)
        declArgs = "cx"
    else:
        declArgs = None

    conversion = fill(
        """
        { // scope for tempRoot and tempGlobalRoot if needed
          $*{rootArgs}
          $${declName} = new ${name}(${args});
        }
        """,
        rootArgs=rootArgs,
        name=name,
        args=args)
    return (declType, declArgs, conversion)


class JSToNativeConversionInfo():
    """
    An object representing information about a JS-to-native conversion.
    """
    def __init__(self, template, declType=None, holderType=None,
                 dealWithOptional=False, declArgs=None,
                 holderArgs=None):
        """
        template: A string representing the conversion code.  This will have
                  template substitution performed on it as follows:

          ${val} is a handle to the JS::Value in question
          ${maybeMutableVal} May be a mutable handle to the JS::Value in
                             question. This is only OK to use if ${val} is
                             known to not be undefined.
          ${holderName} replaced by the holder's name, if any
          ${declName} replaced by the declaration's name
          ${haveValue} replaced by an expression that evaluates to a boolean
                       for whether we have a JS::Value.  Only used when
                       defaultValue is not None or when True is passed for
                       checkForValue to instantiateJSToNativeConversion.
                       This expression may not be already-parenthesized, so if
                       you use it with && or || make sure to put parens
                       around it.
          ${passedToJSImpl} replaced by an expression that evaluates to a boolean
                            for whether this value is being passed to a JS-
                            implemented interface.

        declType: A CGThing representing the native C++ type we're converting
                  to.  This is allowed to be None if the conversion code is
                  supposed to be used as-is.

        holderType: A CGThing representing the type of a "holder" which will
                    hold a possible reference to the C++ thing whose type we
                    returned in declType, or  None if no such holder is needed.

        dealWithOptional: A boolean indicating whether the caller has to do
                          optional-argument handling.  This should only be set
                          to true if the JS-to-native conversion is being done
                          for an optional argument or dictionary member with no
                          default value and if the returned template expects
                          both declType and holderType to be wrapped in
                          Optional<>, with ${declName} and ${holderName}
                          adjusted to point to the Value() of the Optional, and
                          Construct() calls to be made on the Optional<>s as
                          needed.

        declArgs: If not None, the arguments to pass to the ${declName}
                  constructor.  These will have template substitution performed
                  on them so you can use things like ${val}.  This is a
                  single string, not a list of strings.

        holderArgs: If not None, the arguments to pass to the ${holderName}
                    constructor.  These will have template substitution
                    performed on them so you can use things like ${val}.
                    This is a single string, not a list of strings.

        ${declName} must be in scope before the code from 'template' is entered.

        If holderType is not None then ${holderName} must be in scope before
        the code from 'template' is entered.
        """
        assert isinstance(template, str)
        assert declType is None or isinstance(declType, CGThing)
        assert holderType is None or isinstance(holderType, CGThing)
        self.template = template
        self.declType = declType
        self.holderType = holderType
        self.dealWithOptional = dealWithOptional
        self.declArgs = declArgs
        self.holderArgs = holderArgs


def getHandleDefault(defaultValue):
    tag = defaultValue.type.tag()
    if tag in numericSuffixes:
        # Some numeric literals require a suffix to compile without warnings
        return numericValue(tag, defaultValue.value)
    assert tag == IDLType.Tags.bool
    return toStringBool(defaultValue.value)


def handleDefaultStringValue(defaultValue, method):
    """
    Returns a string which ends up calling 'method' with a (char_t*, length)
    pair that sets this string default value.  This string is suitable for
    passing as the second argument of handleDefault; in particular it does not
    end with a ';'
    """
    assert (defaultValue.type.isDOMString() or
            defaultValue.type.isUSVString() or
            defaultValue.type.isByteString())
    return ("static const %(char_t)s data[] = { %(data)s };\n"
            "%(method)s(data, ArrayLength(data) - 1)") % {
                'char_t': "char" if defaultValue.type.isByteString() else "char16_t",
                'method': method,
                'data': ", ".join(["'" + char + "'" for char in
                                   defaultValue.value] + ["0"])
            }


def recordKeyType(recordType):
    assert recordType.keyType.isString()
    if recordType.keyType.isByteString():
        return "nsCString"
    return "nsString"


def recordKeyDeclType(recordType):
    return CGGeneric(recordKeyType(recordType))


def initializerForType(type):
    """
    Get the right initializer for the given type for a data location where we
    plan to then initialize it from a JS::Value.  Some types need to always be
    initialized even before we start the JS::Value-to-IDL-value conversion.

    Returns a string or None if no initialization is needed.
    """
    if type.isObject():
        return "nullptr"
    # We could probably return CGDictionary.getNonInitializingCtorArg() for the
    # dictionary case, but code outside DictionaryBase subclasses can't use
    # that, so we can't do it across the board.
    return None


# If this function is modified, modify CGNativeMember.getArg and
# CGNativeMember.getRetvalInfo accordingly.  The latter cares about the decltype
# and holdertype we end up using, because it needs to be able to return the code
# that will convert those to the actual return value of the callback function.
def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
                                isDefinitelyObject=False,
                                isMember=False,
                                isOptional=False,
                                invalidEnumValueFatal=True,
                                defaultValue=None,
                                isNullOrUndefined=False,
                                exceptionCode=None,
                                lenientFloatCode=None,
                                allowTreatNonCallableAsNull=False,
                                isCallbackReturnValue=False,
                                sourceDescription="value",
                                nestingLevel=""):
    """
    Get a template for converting a JS value to a native object based on the
    given type and descriptor.  If failureCode is given, then we're actually
    testing whether we can convert the argument to the desired type.  That
    means that failures to convert due to the JS value being the wrong type of
    value need to use failureCode instead of throwing exceptions.  Failures to
    convert that are due to JS exceptions (from toString or valueOf methods) or
    out of memory conditions need to throw exceptions no matter what
    failureCode is.  However what actually happens when throwing an exception
    can be controlled by exceptionCode.  The only requirement on that is that
    exceptionCode must end up doing a return, and every return from this
    function must happen via exceptionCode if exceptionCode is not None.

    If isDefinitelyObject is True, that means we know the value
    isObject() and we have no need to recheck that.

    if isMember is not False, we're being converted from a property of some JS
    object, not from an actual method argument, so we can't rely on our jsval
    being rooted or outliving us in any way.  Callers can pass "Dictionary",
    "Variadic", "Sequence", or "OwningUnion" to indicate that the conversion is
    for something that is a dictionary member, a variadic argument, a sequence,
    or an owning union respectively.

    If isOptional is true, then we are doing conversion of an optional
    argument with no default value.

    invalidEnumValueFatal controls whether an invalid enum value conversion
    attempt will throw (if true) or simply return without doing anything (if
    false).

    If defaultValue is not None, it's the IDL default value for this conversion

    If isEnforceRange is true, we're converting an integer and throwing if the
    value is out of range.

    If isClamp is true, we're converting an integer and clamping if the
    value is out of range.

    If lenientFloatCode is not None, it should be used in cases when
    we're a non-finite float that's not unrestricted.

    If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and
    [TreatNonObjectAsNull] extended attributes on nullable callback functions
    will be honored.

    If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be
    adjusted to make it easier to return from a callback.  Since that type is
    never directly observable by any consumers of the callback code, this is OK.
    Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior
    of the FailureFatalCastableObjectUnwrapper conversion; this is used for
    implementing auto-wrapping of JS-implemented return values from a
    JS-implemented interface.

    sourceDescription is a description of what this JS value represents, to be
    used in error reporting.  Callers should assume that it might get placed in
    the middle of a sentence.  If it ends up at the beginning of a sentence, its
    first character will be automatically uppercased.

    The return value from this function is a JSToNativeConversionInfo.
    """
    # If we have a defaultValue then we're not actually optional for
    # purposes of what we need to be declared as.
    assert defaultValue is None or not isOptional

    # Also, we should not have a defaultValue if we know we're an object
    assert not isDefinitelyObject or defaultValue is None

    # And we can't both be an object and be null or undefined
    assert not isDefinitelyObject or not isNullOrUndefined

    isClamp = type.clamp
    isEnforceRange = type.enforceRange

    # If exceptionCode is not set, we'll just rethrow the exception we got.
    # Note that we can't just set failureCode to exceptionCode, because setting
    # failureCode will prevent pending exceptions from being set in cases when
    # they really should be!
    if exceptionCode is None:
        exceptionCode = "return false;\n"

    # Unfortunately, .capitalize() on a string will lowercase things inside the
    # string, which we do not want.
    def firstCap(string):
        return string[0].upper() + string[1:]

    # Helper functions for dealing with failures due to the JS value being the
    # wrong type of value
    def onFailureNotAnObject(failureCode):
        return CGGeneric(
            failureCode or
            ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
             '%s' % (firstCap(sourceDescription), exceptionCode)))

    def onFailureBadType(failureCode, typeName):
        return CGGeneric(
            failureCode or
            ('ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n'
             '%s' % (firstCap(sourceDescription), typeName, exceptionCode)))

    def onFailureNotCallable(failureCode):
        return CGGeneric(
            failureCode or
            ('ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "%s");\n'
             '%s' % (firstCap(sourceDescription), exceptionCode)))

    # A helper function for handling default values.  Takes a template
    # body and the C++ code to set the default value and wraps the
    # given template body in handling for the default value.
    def handleDefault(template, setDefault):
        if defaultValue is None:
            return template
        return (
            "if (${haveValue}) {\n" +
            indent(template) +
            "} else {\n" +
            indent(setDefault) +
            "}\n")

    # A helper function for wrapping up the template body for
    # possibly-nullable objecty stuff
    def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None):
        if isNullOrUndefined and type.nullable():
            # Just ignore templateBody and set ourselves to null.
            # Note that we don't have to worry about default values
            # here either, since we already examined this value.
            return codeToSetNull

        if not isDefinitelyObject:
            # Handle the non-object cases by wrapping up the whole
            # thing in an if cascade.
            if type.nullable():
                elifLine = "} else if (${val}.isNullOrUndefined()) {\n"
                elifBody = codeToSetNull
            else:
                elifLine = ""
                elifBody = ""

            # Note that $${val} below expands to ${val}. This string is
            # used as a template later, and val will be filled in then.
            templateBody = fill(
                """
                if ($${val}.isObject()) {
                  $*{templateBody}
                $*{elifLine}
                  $*{elifBody}
                } else {
                  $*{failureBody}
                }
                """,
                templateBody=templateBody,
                elifLine=elifLine,
                elifBody=elifBody,
                failureBody=onFailureNotAnObject(failureCode).define())

            if isinstance(defaultValue, IDLNullValue):
                assert type.nullable()  # Parser should enforce this
                templateBody = handleDefault(templateBody, codeToSetNull)
            elif isinstance(defaultValue, IDLEmptySequenceValue):
                # Our caller will handle it
                pass
            else:
                assert defaultValue is None

        return templateBody

    # A helper function for converting things that look like a JSObject*.
    def handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription):
        if not isMember:
            if isOptional:
                # We have a specialization of Optional that will use a
                # Rooted for the storage here.
                declType = CGGeneric("JS::Handle<JSObject*>")
            else:
                declType = CGGeneric("JS::Rooted<JSObject*>")
            declArgs = "cx"
        else:
            assert (isMember in
                    ("Sequence", "Variadic", "Dictionary", "OwningUnion", "Record"))
            # We'll get traced by the sequence or dictionary or union tracer
            declType = CGGeneric("JSObject*")
            declArgs = None
        templateBody = "${declName} = &${val}.toObject();\n"

        # For JS-implemented APIs, we refuse to allow passing objects that the
        # API consumer does not subsume. The extra parens around
        # ($${passedToJSImpl}) suppress unreachable code warnings when
        # $${passedToJSImpl} is the literal `false`.  But Apple is shipping a
        # buggy clang (clang 3.9) in Xcode 8.3, so there even the parens are not
        # enough.  So we manually disable some warnings in clang.
        if not isinstance(descriptorProvider, Descriptor) or descriptorProvider.interface.isJSImplemented():
            templateBody = fill(
                """
                #ifdef __clang__
                #pragma clang diagnostic push
                #pragma clang diagnostic ignored "-Wunreachable-code"
                #pragma clang diagnostic ignored "-Wunreachable-code-return"
                #endif // __clang__
                if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
                  ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "${sourceDescription}");
                  $*{exceptionCode}
                }
                #ifdef __clang__
                #pragma clang diagnostic pop
                #endif // __clang__
                """,
                sourceDescription=sourceDescription,
                exceptionCode=exceptionCode) + templateBody

        setToNullCode = "${declName} = nullptr;\n"
        template = wrapObjectTemplate(templateBody, type, setToNullCode,
                                      failureCode)
        return JSToNativeConversionInfo(template, declType=declType,
                                        dealWithOptional=isOptional,
                                        declArgs=declArgs)

    def incrementNestingLevel():
        if nestingLevel is "":
            return 1
        return nestingLevel + 1

    assert not (isEnforceRange and isClamp)  # These are mutually exclusive

    if type.isSequence():
        assert not isEnforceRange and not isClamp

        if failureCode is None:
            notSequence = ('ThrowErrorMessage(cx, MSG_NOT_SEQUENCE, "%s");\n'
                           "%s" % (firstCap(sourceDescription), exceptionCode))
        else:
            notSequence = failureCode

        nullable = type.nullable()
        # Be very careful not to change "type": we need it later
        if nullable:
            elementType = type.inner.inner
        else:
            elementType = type.inner

        # We want to use auto arrays if we can, but we have to be careful with
        # reallocation behavior for arrays.  In particular, if we use auto
        # arrays for sequences and have a sequence of elements which are
        # themselves sequences or have sequences as members, we have a problem.
        # In that case, resizing the outermost AutoTArray to the right size
        # will memmove its elements, but AutoTArrays are not memmovable and
        # hence will end up with pointers to bogus memory, which is bad.  To
        # deal with this, we typically map WebIDL sequences to our Sequence
        # type, which is in fact memmovable.  The one exception is when we're
        # passing in a sequence directly as an argument without any sort of
        # optional or nullable complexity going on.  In that situation, we can
        # use an AutoSequence instead.  We have to keep using Sequence in the
        # nullable and optional cases because we don't want to leak the
        # AutoSequence type to consumers, which would be unavoidable with
        # Nullable<AutoSequence> or Optional<AutoSequence>.
        if isMember or isOptional or nullable or isCallbackReturnValue:
            sequenceClass = "Sequence"
        else:
            sequenceClass = "binding_detail::AutoSequence"

        # XXXbz we can't include the index in the sourceDescription, because
        # we don't really have a way to pass one in dynamically at runtime...
        elementInfo = getJSToNativeConversionInfo(
            elementType, descriptorProvider, isMember="Sequence",
            exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
            isCallbackReturnValue=isCallbackReturnValue,
            sourceDescription="element of %s" % sourceDescription,
            nestingLevel=incrementNestingLevel())
        if elementInfo.dealWithOptional:
            raise TypeError("Shouldn't have optional things in sequences")
        if elementInfo.holderType is not None:
            raise TypeError("Shouldn't need holders for sequences")

        typeName = CGTemplatedType(sequenceClass, elementInfo.declType)
        sequenceType = typeName.define()
        if nullable:
            typeName = CGTemplatedType("Nullable", typeName)
            arrayRef = "${declName}.SetValue()"
        else:
            arrayRef = "${declName}"

        elementConversion = string.Template(elementInfo.template).substitute({
            "val": "temp" + str(nestingLevel),
            "maybeMutableVal": "&temp" + str(nestingLevel),
            "declName": "slot" + str(nestingLevel),
            # We only need holderName here to handle isExternal()
            # interfaces, which use an internal holder for the
            # conversion even when forceOwningType ends up true.
            "holderName": "tempHolder" + str(nestingLevel),
            "passedToJSImpl": "${passedToJSImpl}"
        })

        elementInitializer = initializerForType(elementType)
        if elementInitializer is None:
            elementInitializer = ""
        else:
            elementInitializer = elementInitializer + ", "

        # NOTE: Keep this in sync with variadic conversions as needed
        templateBody = fill(
            """
            JS::ForOfIterator iter${nestingLevel}(cx);
            if (!iter${nestingLevel}.init($${val}, JS::ForOfIterator::AllowNonIterable)) {
              $*{exceptionCode}
            }
            if (!iter${nestingLevel}.valueIsIterable()) {
              $*{notSequence}
            }
            ${sequenceType} &arr${nestingLevel} = ${arrayRef};
            JS::Rooted<JS::Value> temp${nestingLevel}(cx);
            while (true) {
              bool done${nestingLevel};
              if (!iter${nestingLevel}.next(&temp${nestingLevel}, &done${nestingLevel})) {
                $*{exceptionCode}
              }
              if (done${nestingLevel}) {
                break;
              }
              ${elementType}* slotPtr${nestingLevel} = arr${nestingLevel}.AppendElement(${elementInitializer}mozilla::fallible);
              if (!slotPtr${nestingLevel}) {
                JS_ReportOutOfMemory(cx);
                $*{exceptionCode}
              }
              ${elementType}& slot${nestingLevel} = *slotPtr${nestingLevel};
              $*{elementConversion}
            }
            """,
            exceptionCode=exceptionCode,
            notSequence=notSequence,
            sequenceType=sequenceType,
            arrayRef=arrayRef,
            elementType=elementInfo.declType.define(),
            elementConversion=elementConversion,
            elementInitializer=elementInitializer,
            nestingLevel=str(nestingLevel))

        templateBody = wrapObjectTemplate(templateBody, type,
                                          "${declName}.SetNull();\n", notSequence)
        if isinstance(defaultValue, IDLEmptySequenceValue):
            if type.nullable():
                codeToSetEmpty = "${declName}.SetValue();\n"
            else:
                codeToSetEmpty = "/* Array is already empty; nothing to do */\n"
            templateBody = handleDefault(templateBody, codeToSetEmpty)

        # Sequence arguments that might contain traceable things need
        # to get traced
        if not isMember and typeNeedsRooting(elementType):
            holderType = CGTemplatedType("SequenceRooter", elementInfo.declType)
            # If our sequence is nullable, this will set the Nullable to be
            # not-null, but that's ok because we make an explicit SetNull() call
            # on it as needed if our JS value is actually null.
            holderArgs = "cx, &%s" % arrayRef
        else:
            holderType = None
            holderArgs = None

        return JSToNativeConversionInfo(templateBody, declType=typeName,
                                        holderType=holderType,
                                        dealWithOptional=isOptional,
                                        holderArgs=holderArgs)

    if type.isRecord():
        assert not isEnforceRange and not isClamp
        if failureCode is None:
            notRecord = ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
                         "%s" % (firstCap(sourceDescription), exceptionCode))
        else:
            notRecord = failureCode

        nullable = type.nullable()
        # Be very careful not to change "type": we need it later
        if nullable:
            recordType = type.inner
        else:
            recordType = type
        valueType = recordType.inner

        valueInfo = getJSToNativeConversionInfo(
            valueType, descriptorProvider, isMember="Record",
            exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
            isCallbackReturnValue=isCallbackReturnValue,
            sourceDescription="value in %s" % sourceDescription,
            nestingLevel=incrementNestingLevel())
        if valueInfo.dealWithOptional:
            raise TypeError("Shouldn't have optional things in record")
        if valueInfo.holderType is not None:
            raise TypeError("Shouldn't need holders for record")

        declType = CGTemplatedType("Record", [recordKeyDeclType(recordType),
                                              valueInfo.declType])
        typeName = declType.define()
        if nullable:
            declType = CGTemplatedType("Nullable", declType)
            recordRef = "${declName}.SetValue()"
        else:
            recordRef = "${declName}"

        valueConversion = string.Template(valueInfo.template).substitute({
            "val": "temp",
            "maybeMutableVal": "&temp",
            "declName": "slot",
            # We only need holderName here to handle isExternal()
            # interfaces, which use an internal holder for the
            # conversion even when forceOwningType ends up true.
            "holderName": "tempHolder",
            "passedToJSImpl": "${passedToJSImpl}"
        })

        keyType = recordKeyType(recordType)
        if recordType.keyType.isByteString():
            keyConversionFunction = "ConvertJSValueToByteString"
            hashKeyType = "nsCStringHashKey"
        else:
            hashKeyType = "nsStringHashKey"
            if recordType.keyType.isDOMString():
                keyConversionFunction = "ConvertJSValueToString"
            else:
                assert recordType.keyType.isUSVString()
                keyConversionFunction = "ConvertJSValueToUSVString"

        templateBody = fill(
            """
            auto& recordEntries = ${recordRef}.Entries();

            JS::Rooted<JSObject*> recordObj(cx, &$${val}.toObject());
            JS::RootedVector<jsid> ids(cx);
            if (!js::GetPropertyKeys(cx, recordObj,
                                     JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &ids)) {
              $*{exceptionCode}
            }
            if (!recordEntries.SetCapacity(ids.length(), mozilla::fallible)) {
              JS_ReportOutOfMemory(cx);
              $*{exceptionCode}
            }
            JS::Rooted<JS::Value> propNameValue(cx);
            JS::Rooted<JS::Value> temp(cx);
            JS::Rooted<jsid> curId(cx);
            JS::Rooted<JS::Value> idVal(cx);
            // Use a hashset to keep track of ids seen, to avoid
            // introducing nasty O(N^2) behavior scanning for them all the
            // time.  Ideally we'd use a data structure with O(1) lookup
            // _and_ ordering for the MozMap, but we don't have one lying
            // around.
            nsTHashtable<${hashKeyType}> idsSeen;
            for (size_t i = 0; i < ids.length(); ++i) {
              curId = ids[i];

              JS::Rooted<JS::PropertyDescriptor> desc(cx);
              if (!JS_GetOwnPropertyDescriptorById(cx, recordObj, curId,
                                                   &desc)) {
                $*{exceptionCode}
              }

              if (!desc.object() /* == undefined in spec terms */ ||
                  !desc.enumerable()) {
                continue;
              }

              idVal = js::IdToValue(curId);
              ${keyType} propName;
              // This will just throw if idVal is a Symbol, like the spec says
              // to do.
              if (!${keyConversionFunction}(cx, idVal, propName)) {
                $*{exceptionCode}
              }

              if (!JS_GetPropertyById(cx, recordObj, curId, &temp)) {
                $*{exceptionCode}
              }

              ${typeName}::EntryType* entry;
              if (!idsSeen.EnsureInserted(propName)) {
                // Find the existing entry.
                auto idx = recordEntries.IndexOf(propName);
                MOZ_ASSERT(idx != recordEntries.NoIndex,
                           "Why is it not found?");
                // Now blow it away to make it look like it was just added
                // to the array, because it's not obvious that it's
                // safe to write to its already-initialized mValue via our
                // normal codegen conversions.  For example, the value
                // could be a union and this would change its type, but
                // codegen assumes we won't do that.
                entry = recordEntries.ReconstructElementAt(idx);
              } else {
                // Safe to do an infallible append here, because we did a
                // SetCapacity above to the right capacity.
                entry = recordEntries.AppendElement();
              }
              entry->mKey = propName;
              ${valueType}& slot = entry->mValue;
              $*{valueConversion}
            }
            """,
            exceptionCode=exceptionCode,
            recordRef=recordRef,
            hashKeyType=hashKeyType,
            keyType=keyType,
            keyConversionFunction=keyConversionFunction,
            typeName=typeName,
            valueType=valueInfo.declType.define(),
            valueConversion=valueConversion)

        templateBody = wrapObjectTemplate(templateBody, type,
                                          "${declName}.SetNull();\n",
                                          notRecord)

        declArgs = None
        holderType = None
        holderArgs = None
        # record arguments that might contain traceable things need
        # to get traced
        if not isMember and isCallbackReturnValue:
            # Go ahead and just convert directly into our actual return value
            declType = CGWrapper(declType, post="&")
            declArgs = "aRetVal"
        elif not isMember and typeNeedsRooting(valueType):
            holderType = CGTemplatedType("RecordRooter",
                                         [recordKeyDeclType(recordType),
                                          valueInfo.declType])
            # If our record is nullable, this will set the Nullable to be
            # not-null, but that's ok because we make an explicit SetNull() call
            # on it as needed if our JS value is actually null.
            holderArgs = "cx, &%s" % recordRef

        return JSToNativeConversionInfo(templateBody, declType=declType,
                                        declArgs=declArgs,
                                        holderType=holderType,
                                        dealWithOptional=isOptional,
                                        holderArgs=holderArgs)

    if type.isUnion():
        nullable = type.nullable()
        if nullable:
            type = type.inner

        isOwningUnion = isMember or isCallbackReturnValue
        unionArgumentObj = "${declName}" if isOwningUnion else "${holderName}"
        if nullable:
            # If we're owning, we're a Nullable, which hasn't been told it has
            # a value.  Otherwise we're an already-constructed Maybe.
            unionArgumentObj += ".SetValue()" if isOwningUnion else ".ref()"

        memberTypes = type.flatMemberTypes
        names = []

        interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
        if len(interfaceMemberTypes) > 0:
            interfaceObject = []
            for memberType in interfaceMemberTypes:
                name = getUnionMemberName(memberType)
                interfaceObject.append(
                    CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext" %
                              (unionArgumentObj, name)))
                names.append(name)
            interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"),
                                        pre="done = ", post=";\n\n", reindent=True)
        else:
            interfaceObject = None

        sequenceObjectMemberTypes = filter(lambda t: t.isSequence(), memberTypes)
        if len(sequenceObjectMemberTypes) > 0:
            assert len(sequenceObjectMemberTypes) == 1
            name = getUnionMemberName(sequenceObjectMemberTypes[0])
            sequenceObject = CGGeneric(
                "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
                (unionArgumentObj, name))
            names.append(name)
        else:
            sequenceObject = None

        dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
        if len(dateObjectMemberTypes) > 0:
            assert len(dateObjectMemberTypes) == 1
            memberType = dateObjectMemberTypes[0]
            name = getUnionMemberName(memberType)
            dateObject = CGGeneric("%s.SetTo%s(cx, ${val});\n"
                                   "done = true;\n" % (unionArgumentObj, name))
            dateObject = CGIfWrapper(dateObject, "JS::ObjectIsDate(cx, argObj)")
            names.append(name)
        else:
            dateObject = None

        callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
        if len(callbackMemberTypes) > 0:
            assert len(callbackMemberTypes) == 1
            memberType = callbackMemberTypes[0]
            name = getUnionMemberName(memberType)
            callbackObject = CGGeneric(
                "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
                (unionArgumentObj, name))
            names.append(name)
        else:
            callbackObject = None

        dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
        if len(dictionaryMemberTypes) > 0:
            assert len(dictionaryMemberTypes) == 1
            name = getUnionMemberName(dictionaryMemberTypes[0])
            setDictionary = CGGeneric(
                "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
                (unionArgumentObj, name))
            names.append(name)
        else:
            setDictionary = None

        recordMemberTypes = filter(lambda t: t.isRecord(), memberTypes)
        if len(recordMemberTypes) > 0:
            assert len(recordMemberTypes) == 1
            name = getUnionMemberName(recordMemberTypes[0])
            recordObject = CGGeneric(
                "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
                (unionArgumentObj, name))
            names.append(name)
        else:
            recordObject = None

        objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
        if len(objectMemberTypes) > 0:
            assert len(objectMemberTypes) == 1
            # Very important to NOT construct a temporary Rooted here, since the
            # SetToObject call can call a Rooted constructor and we need to keep
            # stack discipline for Rooted.
            object = CGGeneric("if (!%s.SetToObject(cx, &${val}.toObject(), ${passedToJSImpl})) {\n"
                               "%s"
                               "}\n"
                               "done = true;\n" % (unionArgumentObj, indent(exceptionCode)))
            names.append(objectMemberTypes[0].name)
        else:
            object = None

        hasObjectTypes = interfaceObject or sequenceObject or dateObject or callbackObject or object or recordObject
        if hasObjectTypes:
            # "object" is not distinguishable from other types
            assert not object or not (interfaceObject or sequenceObject or dateObject or callbackObject or recordObject)
            if sequenceObject or dateObject or callbackObject:
                # An object can be both an sequence object and a callback or
                # dictionary, but we shouldn't have both in the union's members
                # because they are not distinguishable.
                assert not (sequenceObject and callbackObject)
                templateBody = CGElseChain([sequenceObject, dateObject, callbackObject])
            else:
                templateBody = None
            if interfaceObject:
                assert not object
                if templateBody:
                    templateBody = CGIfWrapper(templateBody, "!done")
                templateBody = CGList([interfaceObject, templateBody])
            else:
                templateBody = CGList([templateBody, object])

            if dateObject:
                templateBody.prepend(CGGeneric("JS::Rooted<JSObject*> argObj(cx, &${val}.toObject());\n"))

            if recordObject:
                templateBody = CGList([templateBody,
                                       CGIfWrapper(recordObject, "!done")])

            templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
        else:
            templateBody = CGGeneric()

        if setDictionary:
            assert not object
            templateBody = CGList([templateBody,
                                   CGIfWrapper(setDictionary, "!done")])

        stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
        numericTypes = [t for t in memberTypes if t.isNumeric()]
        booleanTypes = [t for t in memberTypes if t.isBoolean()]
        if stringTypes or numericTypes or booleanTypes:
            assert len(stringTypes) <= 1
            assert len(numericTypes) <= 1
            assert len(booleanTypes) <= 1

            # We will wrap all this stuff in a do { } while (0); so we
            # can use "break" for flow control.
            def getStringOrPrimitiveConversion(memberType):
                name = getUnionMemberName(memberType)
                return CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n"
                                 "break;\n" % (unionArgumentObj, name))
            other = CGList([])
            stringConversion = map(getStringOrPrimitiveConversion, stringTypes)
            numericConversion = map(getStringOrPrimitiveConversion, numericTypes)
            booleanConversion = map(getStringOrPrimitiveConversion, booleanTypes)
            if stringConversion:
                if booleanConversion:
                    other.append(CGIfWrapper(booleanConversion[0],
                                             "${val}.isBoolean()"))
                if numericConversion:
                    other.append(CGIfWrapper(numericConversion[0],
                                             "${val}.isNumber()"))
                other.append(stringConversion[0])
            elif numericConversion:
                if booleanConversion:
                    other.append(CGIfWrapper(booleanConversion[0],
                                             "${val}.isBoolean()"))
                other.append(numericConversion[0])
            else:
                assert booleanConversion
                other.append(booleanConversion[0])

            other = CGWrapper(CGIndenter(other), pre="do {\n", post="} while (false);\n")
            if hasObjectTypes or setDictionary:
                other = CGWrapper(CGIndenter(other), "{\n", post="}\n")
                if object:
                    templateBody = CGElseChain([templateBody, other])
                else:
                    other = CGWrapper(other, pre="if (!done) ")
                    templateBody = CGList([templateBody, other])
            else:
                assert templateBody.define() == ""
                templateBody = other
        else:
            other = None

        templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
        throw = CGGeneric(fill(
            """
            if (failed) {
              $*{exceptionCode}
            }
            if (!done) {
              ThrowErrorMessage(cx, MSG_NOT_IN_UNION, "${desc}", "${names}");
              $*{exceptionCode}
            }
            """,
            exceptionCode=exceptionCode,
            desc=firstCap(sourceDescription),
            names=", ".join(names)))

        templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw])), pre="{\n", post="}\n")

        typeName = CGUnionStruct.unionTypeDecl(type, isOwningUnion)
        argumentTypeName = typeName + "Argument"
        if nullable:
            typeName = "Nullable<" + typeName + " >"

        def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
            nullTest = "%s${val}.isNullOrUndefined()" % extraConditionForNull
            return CGIfElseWrapper(nullTest,
                                   CGGeneric("%s.SetNull();\n" % setToNullVar),
                                   templateBody)

        if type.hasNullableType:
            assert not nullable
            # Make sure to handle a null default value here
            if defaultValue and isinstance(defaultValue, IDLNullValue):
                assert defaultValue.type == type
                extraConditionForNull = "!(${haveValue}) || "
            else:
                extraConditionForNull = ""
            templateBody = handleNull(templateBody, unionArgumentObj,
                                      extraConditionForNull=extraConditionForNull)

        declType = CGGeneric(typeName)
        if isOwningUnion:
            holderType = None
        else:
            holderType = CGGeneric(argumentTypeName)
            if nullable:
                holderType = CGTemplatedType("Maybe", holderType)

        # If we're isOptional and not nullable the normal optional handling will
        # handle lazy construction of our holder.  If we're nullable and not
        # owning we do it all by hand because we do not want our holder
        # constructed if we're null.  But if we're owning we don't have a
        # holder anyway, so we can do the normal Optional codepath.
        declLoc = "${declName}"
        constructDecl = None
        if nullable:
            if isOptional and not isOwningUnion:
                holderArgs = "${declName}.Value().SetValue()"
                declType = CGTemplatedType("Optional", declType)
                constructDecl = CGGeneric("${declName}.Construct();\n")
                declLoc = "${declName}.Value()"
            else:
                holderArgs = "${declName}.SetValue()"
            if holderType is not None:
                constructHolder = CGGeneric("${holderName}.emplace(%s);\n" % holderArgs)
            else:
                constructHolder = None
            # Don't need to pass those args when the holder is being constructed
            holderArgs = None
        else:
            holderArgs = "${declName}"
            constructHolder = None

        if not isMember and isCallbackReturnValue:
            declType = CGWrapper(declType, post="&")
            declArgs = "aRetVal"
        else:
            declArgs = None

        if (defaultValue and
            not isinstance(defaultValue, IDLNullValue) and
            not isinstance(defaultValue, IDLDefaultDictionaryValue)):
            tag = defaultValue.type.tag()

            if tag in numericSuffixes or tag is IDLType.Tags.bool:
                defaultStr = getHandleDefault(defaultValue)
                # Make sure we actually construct the thing inside the nullable.
                value = declLoc + (".SetValue()" if nullable else "")
                name = getUnionMemberName(defaultValue.type)
                default = CGGeneric("%s.RawSetAs%s() = %s;\n" %
                                    (value, name, defaultStr))
            elif isinstance(defaultValue, IDLEmptySequenceValue):
                name = getUnionMemberName(defaultValue.type)
                # Make sure we actually construct the thing inside the nullable.
                value = declLoc + (".SetValue()" if nullable else "")
                # It's enough to set us to the right type; that will
                # create an empty array, which is all we need here.
                default = CGGeneric("%s.RawSetAs%s();\n" %
                                    (value, name))
            elif defaultValue.type.isEnum():
                name = getUnionMemberName(defaultValue.type)
                # Make sure we actually construct the thing inside the nullable.
                value = declLoc + (".SetValue()" if nullable else "")
                default = CGGeneric(
                    "%s.RawSetAs%s() = %s::%s;\n" %
                    (value, name,
                     defaultValue.type.inner.identifier.name,
                     getEnumValueName(defaultValue.value)))
            else:
                default = CGGeneric(
                    handleDefaultStringValue(
                        defaultValue, "%s.SetStringData" % unionArgumentObj) +
                    ";\n")

            templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody)

        templateBody = CGList([constructHolder, templateBody])

        if nullable:
            if defaultValue:
                if isinstance(defaultValue, IDLNullValue):
                    extraConditionForNull = "!(${haveValue}) || "
                else:
                    extraConditionForNull = "(${haveValue}) && "
            else:
                extraConditionForNull = ""
            templateBody = handleNull(templateBody, declLoc,
                                      extraConditionForNull=extraConditionForNull)
        elif (not type.hasNullableType and defaultValue and
              isinstance(defaultValue, IDLDefaultDictionaryValue)):
            assert type.hasDictionaryType()
            assert defaultValue.type.isDictionary()
            if not isOwningUnion and typeNeedsRooting(defaultValue.type):
                ctorArgs = "cx"
            else:
                ctorArgs = ""
            initDictionaryWithNull = CGIfWrapper(
                CGGeneric("return false;\n"),
                ('!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")'
                 % (declLoc, getUnionMemberName(defaultValue.type),
                    ctorArgs, type)))
            templateBody = CGIfElseWrapper("!(${haveValue})",
                                           initDictionaryWithNull,
                                           templateBody)

        templateBody = CGList([constructDecl, templateBody])

        return JSToNativeConversionInfo(templateBody.define(),
                                        declType=declType,
                                        declArgs=declArgs,
                                        holderType=holderType,
                                        holderArgs=holderArgs,
                                        dealWithOptional=isOptional and (not nullable or isOwningUnion))

    if type.isPromise():
        assert not type.nullable()
        assert defaultValue is None

        # We always have to hold a strong ref to Promise here, because
        # Promise::resolve returns an addrefed thing.
        argIsPointer = isCallbackReturnValue
        if argIsPointer:
            declType = CGGeneric("RefPtr<Promise>")
        else:
            declType = CGGeneric("OwningNonNull<Promise>")

        # Per spec, what we're supposed to do is take the original
        # Promise.resolve and call it with the original Promise as this
        # value to make a Promise out of whatever value we actually have
        # here.  The question is which global we should use.  There are
        # several cases to consider:
        #
        # 1) Normal call to API with a Promise argument.  This is a case the
        #    spec covers, and we should be using the current Realm's
        #    Promise.  That means the current compartment.
        # 2) Call to API with a Promise argument over Xrays.  In practice,
        #    this sort of thing seems to be used for giving an API
        #    implementation a way to wait for conclusion of an asyc
        #    operation, _not_ to expose the Promise to content code.  So we
        #    probably want to allow callers to use such an API in a
        #    "natural" way, by passing chrome-side promises; indeed, that
        #    may be all that the caller has to represent their async
        #    operation.  That means we really need to do the
        #    Promise.resolve() in the caller (chrome) compartment: if we do
        #    it in the content compartment, we will try to call .then() on
        #    the chrome promise while in the content compartment, which will
        #    throw and we'll just get a rejected Promise.  Note that this is
        #    also the reason why a caller who has a chrome Promise
        #    representing an async operation can't itself convert it to a
        #    content-side Promise (at least not without some serious
        #    gyrations).
        # 3) Promise return value from a callback or callback interface.
        #    Per spec, this should use the Realm of the callback object.  In
        #    our case, that's the compartment of the underlying callback,
        #    not the current compartment (which may be the compartment of
        #    some cross-compartment wrapper around said callback).
        # 4) Return value from a JS-implemented interface.  In this case we
        #    have a problem.  Our current compartment is the compartment of
        #    the JS implementation.  But if the JS implementation returned
        #    a page-side Promise (which is a totally sane thing to do, and
        #    in fact the right thing to do given that this return value is
        #    going right to content script) then we don't want to
        #    Promise.resolve with our current compartment Promise, because
        #    that will wrap it up in a chrome-side Promise, which is
        #    decidedly _not_ what's desired here.  So in that case we
        #    should really unwrap the return value and use the global of
        #    the result.  CheckedUnwrapStatic should be good enough for that;
        #    if it fails, then we're failing unwrap while in a
        #    system-privileged compartment, so presumably we have a dead
        #    object wrapper.  Just error out.  Do NOT fall back to using
        #    the current compartment instead: that will return a
        #    system-privileged rejected (because getting .then inside
        #    resolve() failed) Promise to the caller, which they won't be
        #    able to touch.  That's not helpful.  If we error out, on the
        #    other hand, they will get a content-side rejected promise.
        #    Same thing if the value returned is not even an object.
        if isCallbackReturnValue == "JSImpl":
            # Case 4 above.  Note that globalObj defaults to the current
            # compartment global.  Note that we don't use $*{exceptionCode}
            # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED)
            # which we don't really want here.
            assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n"
            getPromiseGlobal = fill(
                """
                if (!$${val}.isObject()) {
                  aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
                  return nullptr;
                }
                JSObject* unwrappedVal = js::CheckedUnwrapStatic(&$${val}.toObject());
                if (!unwrappedVal) {
                  // A slight lie, but not much of one, for a dead object wrapper.
                  aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
                  return nullptr;
                }
                globalObj = JS::GetNonCCWObjectGlobal(unwrappedVal);
                """,
                sourceDescription=sourceDescription)
        elif isCallbackReturnValue == "Callback":
            getPromiseGlobal = dedent(
                """
                // We basically want our entry global here.  Play it safe
                // and use GetEntryGlobal() to get it, with whatever
                // principal-clamping it ends up doing.
                globalObj = GetEntryGlobal()->GetGlobalJSObject();
                """)
        else:
            getPromiseGlobal = dedent(
                """
                globalObj = JS::CurrentGlobalOrNull(cx);
                """)

        templateBody = fill(
            """
            { // Scope for our GlobalObject, FastErrorResult, JSAutoRealm,
              // etc.

              JS::Rooted<JSObject*> globalObj(cx);
              $*{getPromiseGlobal}
              JSAutoRealm ar(cx, globalObj);
              GlobalObject promiseGlobal(cx, globalObj);
              if (promiseGlobal.Failed()) {
                $*{exceptionCode}
              }

              JS::Rooted<JS::Value> valueToResolve(cx, $${val});
              if (!JS_WrapValue(cx, &valueToResolve)) {
                $*{exceptionCode}
              }
              binding_detail::FastErrorResult promiseRv;
              nsCOMPtr<nsIGlobalObject> global =
                do_QueryInterface(promiseGlobal.GetAsSupports());
              if (!global) {
                promiseRv.ThrowWithCustomCleanup(NS_ERROR_UNEXPECTED);
                MOZ_ALWAYS_TRUE(promiseRv.MaybeSetPendingException(cx));
                $*{exceptionCode}
              }
              $${declName} = Promise::Resolve(global, cx, valueToResolve,
                                              promiseRv);
              if (promiseRv.MaybeSetPendingException(cx)) {
                $*{exceptionCode}
              }
            }
            """,
            getPromiseGlobal=getPromiseGlobal,
            exceptionCode=exceptionCode)

        return JSToNativeConversionInfo(templateBody,
                                        declType=declType,
                                        dealWithOptional=isOptional)

    if type.isGeckoInterface():
        assert not isEnforceRange and not isClamp

        descriptor = descriptorProvider.getDescriptor(
            type.unroll().inner.identifier.name)

        assert descriptor.nativeType != 'JSObject'

        if descriptor.interface.isCallback():
            (declType, declArgs,
             conversion) = getCallbackConversionInfo(type, descriptor.interface,
                                                     isMember,
                                                     isCallbackReturnValue,
                                                     isOptional)
            template = wrapObjectTemplate(conversion, type,
                                          "${declName} = nullptr;\n",
                                          failureCode)
            return JSToNativeConversionInfo(template, declType=declType,
                                            declArgs=declArgs,
                                            dealWithOptional=isOptional)

        if descriptor.interface.identifier.name == "WindowProxy":
            declType = CGGeneric("mozilla::dom::WindowProxyHolder")
            if type.nullable():
                declType = CGTemplatedType("Nullable", declType)
                windowProxyHolderRef = "${declName}.SetValue()"
            else:
                windowProxyHolderRef = "${declName}"

            failureCode = onFailureBadType(failureCode, descriptor.interface.identifier.name).define()
            templateBody = fill("""
                JS::Rooted<JSObject*> source(cx, &$${val}.toObject());
                if (NS_FAILED(UnwrapWindowProxyArg(cx, source, ${windowProxyHolderRef}))) {
                    $*{onFailure}
                }
                """,
                windowProxyHolderRef=windowProxyHolderRef,
                onFailure=failureCode)
            templateBody = wrapObjectTemplate(templateBody, type,
                                              "${declName}.SetNull();\n", failureCode)
            return JSToNativeConversionInfo(templateBody, declType=declType,
                                            dealWithOptional=isOptional)

        # This is an interface that we implement as a concrete class
        # or an XPCOM interface.

        # Allow null pointers for nullable types and old-binding classes, and
        # use an RefPtr or raw pointer for callback return values to make
        # them easier to return.
        argIsPointer = (type.nullable() or type.unroll().inner.isExternal() or
                        isCallbackReturnValue)

        # Sequence and dictionary members, as well as owning unions (which can
        # appear here as return values in JS-implemented interfaces) have to
        # hold a strong ref to the thing being passed down.  Those all set
        # isMember.
        #
        # Also, callback return values always end up addrefing anyway, so there
        # is no point trying to avoid it here and it makes other things simpler
        # since we can assume the return value is a strong ref.
        assert not descriptor.interface.isCallback()
        forceOwningType = isMember or isCallbackReturnValue

        typeName = descriptor.nativeType
        typePtr = typeName + "*"

        # Compute a few things:
        #  - declType is the type we want to return as the first element of our
        #    tuple.
        #  - holderType is the type we want to return as the third element
        #    of our tuple.

        # Set up some sensible defaults for these things insofar as we can.
        holderType = None
        if argIsPointer:
            if forceOwningType:
                declType = "RefPtr<" + typeName + ">"
            else:
                declType = typePtr
        else:
            if forceOwningType:
                declType = "OwningNonNull<" + typeName + ">"
            else:
                declType = "NonNull<" + typeName + ">"

        templateBody = ""
        if forceOwningType:
            templateBody += fill(
                """
                static_assert(IsRefcounted<${typeName}>::value, "We can only store refcounted classes.");
                """,
                typeName=typeName)

        if (not descriptor.interface.isConsequential() and
            not descriptor.interface.isExternal()):
            if failureCode is not None:
                templateBody += str(CastableObjectUnwrapper(
                    descriptor,
                    "${val}",
                    "${maybeMutableVal}",
                    "${declName}",
                    failureCode))
            else:
                templateBody += str(FailureFatalCastableObjectUnwrapper(
                    descriptor,
                    "${val}",
                    "${maybeMutableVal}",
                    "${declName}",
                    exceptionCode,
                    isCallbackReturnValue,
                    firstCap(sourceDescription)))
        else:
            # Either external, or new-binding non-castable.  We always have a
            # holder for these, because we don't actually know whether we have
            # to addref when unwrapping or not.  So we just pass an
            # getter_AddRefs(RefPtr) to XPConnect and if we'll need a release
            # it'll put a non-null pointer in there.
            if forceOwningType:
                # Don't return a holderType in this case; our declName
                # will just own stuff.
                templateBody += "RefPtr<" + typeName + "> ${holderName};\n"
            else:
                holderType = "RefPtr<" + typeName + ">"
            templateBody += (
                "JS::Rooted<JSObject*> source(cx, &${val}.toObject());\n" +
                "if (NS_FAILED(UnwrapArg<" + typeName + ">(cx, source, getter_AddRefs(${holderName})))) {\n")
            templateBody += CGIndenter(onFailureBadType(failureCode,
                                                        descriptor.interface.identifier.name)).define()
            templateBody += ("}\n"
                             "MOZ_ASSERT(${holderName});\n")

            # And store our value in ${declName}
            templateBody += "${declName} = ${holderName};\n"

        # Just pass failureCode, not onFailureBadType, here, so we'll report
        # the thing as not an object as opposed to not implementing whatever
        # our interface is.
        templateBody = wrapObjectTemplate(templateBody, type,
                                          "${declName} = nullptr;\n",
                                          failureCode)

        declType = CGGeneric(declType)
        if holderType is not None:
            holderType = CGGeneric(holderType)
        return JSToNativeConversionInfo(templateBody,
                                        declType=declType,
                                        holderType=holderType,
                                        dealWithOptional=isOptional)

    if type.isSpiderMonkeyInterface():
        assert not isEnforceRange and not isClamp
        name = type.unroll().name  # unroll() because it may be nullable
        interfaceType = CGGeneric(name)
        declType = interfaceType
        if type.nullable():
            declType = CGTemplatedType("Nullable", declType)
            objRef = "${declName}.SetValue()"
        else:
            objRef = "${declName}"

        # Again, this is a bit strange since we are actually building a
        # template string here. ${objRef} and $*{badType} below are filled in
        # right now; $${val} expands to ${val}, to be filled in later.
        template = fill(
            """
            if (!${objRef}.Init(&$${val}.toObject())) {
              $*{badType}
            }
            """,
            objRef=objRef,
            badType=onFailureBadType(failureCode, type.name).define())
        template = wrapObjectTemplate(template, type, "${declName}.SetNull();\n",
                                      failureCode)
        if not isMember:
            # This is a bit annoying.  In a union we don't want to have a
            # holder, since unions don't support that.  But if we're optional we
            # want to have a holder, so that the callee doesn't see
            # Optional<RootedSpiderMonkeyInterface<InterfaceType>>.  So do a
            # holder if we're optional and use a RootedSpiderMonkeyInterface
            # otherwise.
            if isOptional:
                holderType = CGTemplatedType("SpiderMonkeyInterfaceRooter", interfaceType)
                # If our SpiderMonkey interface is nullable, this will set the
                # Nullable to be not-null, but that's ok because we make an
                # explicit SetNull() call on it as needed if our JS value is
                # actually null.  XXXbz Because "Maybe" takes const refs for
                # constructor arguments, we can't pass a reference here; have
                # to pass a pointer.
                holderArgs = "cx, &%s" % objRef
                declArgs = None
            else:
                holderType = None
                holderArgs = None
                declType = CGTemplatedType("RootedSpiderMonkeyInterface", declType)
                declArgs = "cx"
        else:
            holderType = None
            holderArgs = None
            declArgs = None
        return JSToNativeConversionInfo(template,
                                        declType=declType,
                                        holderType=holderType,
                                        dealWithOptional=isOptional,
                                        declArgs=declArgs,
                                        holderArgs=holderArgs)

    if type.isDOMString() or type.isUSVString():
        assert not isEnforceRange and not isClamp

        treatAs = {
            "Default": "eStringify",
            "EmptyString": "eEmpty",
            "Null": "eNull",
        }
        if type.nullable():
            # For nullable strings null becomes a null string.
            treatNullAs = "Null"
            # For nullable strings undefined also becomes a null string.
            undefinedBehavior = "eNull"
        else:
            undefinedBehavior = "eStringify"
            if type.treatNullAsEmpty:
                treatNullAs = "EmptyString"
            else:
                treatNullAs = "Default"
        nullBehavior = treatAs[treatNullAs]

        def getConversionCode(varName):
            normalizeCode = ""
            if type.isUSVString():
                normalizeCode = "NormalizeUSVString(%s);\n" % varName

            conversionCode = fill("""
                if (!ConvertJSValueToString(cx, $${val}, ${nullBehavior}, ${undefinedBehavior}, ${varName})) {
                  $*{exceptionCode}
                }
                $*{normalizeCode}
                """