Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/bindings/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 892 kB image not shown  

Quellcode-Bibliothek Codegen.py   Sprache: Python

 
# 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 functools
import math
from operator import attrgetter
import os
import re
import string
import textwrap

from Configuration import (
    Descriptor,
    MemberIsLegacyUnforgeable,
    NoSuchDescriptorError,
    getAllTypes,
    getTypesFromCallback,
    getTypesFromDescriptor,
    getTypesFromDictionary,
)
from perfecthash import PerfectHash
from WebIDL import (
    BuiltinTypes,
    IDLAttribute,
    IDLBuiltinType,
    IDLDefaultDictionaryValue,
    IDLDictionary,
    IDLEmptySequenceValue,
    IDLInterfaceMember,
    IDLNullValue,
    IDLSequenceType,
    IDLType,
    IDLUndefinedValue,
)

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"
GETWRAPPERCACHE_HOOK_NAME = "_getWrapperCache"
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"
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 reservedSlot(slotIndex, forXray):
    base = "DOM_EXPANDO_RESERVED_SLOTS" if forXray else "DOM_INSTANCE_RESERVED_SLOTS"
    return "(%s + %d)" % (base, slotIndex)


def getSlotIndex(member, descriptor):
    slotIndex = member.slotIndices[descriptor.interface.identifier.name]
    return slotIndex[0] if isinstance(slotIndex, tuple) else slotIndex


def memberReservedSlot(member, descriptor):
    return reservedSlot(getSlotIndex(member, descriptor), False)


def memberXrayExpandoReservedSlot(member, descriptor):
    return reservedSlot(getSlotIndex(member, descriptor), True)


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 reflectedHTMLAttributesArrayIndex(descriptor, attr):
    slots = attr.slotIndices[descriptor.interface.identifier.name]
    return slots[1]


def getReflectedHTMLAttributesIface(descriptor):
    iface = descriptor.interface
    while iface:
        if iface.reflectedHTMLAttributesReturningFrozenArray:
            return iface
        iface = iface.parent
    return None


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.isUndefined()
        or 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.isUndefined()
        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
        )


def idlTypeNeedsCallContext(type, descriptor=None, allowTreatNonCallableAsNull=False):
    """
    Returns whether the given type needs error reporting via a
    BindingCallContext for JS-to-C++ conversions.  This will happen when the
    conversion can throw an exception due to logic in the IDL spec or
    Gecko-specific security checks.  In particular, a type needs a
    BindingCallContext if and only if the JS-to-C++ conversion for that type can
    end up calling ThrowErrorMessage.

    For some types this depends on the descriptor (e.g. because we do certain
    checks only for some kinds of interfaces).

    The allowTreatNonCallableAsNull optimization is there so we can avoid
    generating an unnecessary BindingCallContext for all the event handler
    attribute setters.

    """
    while True:
        if type.isSequence():
            # Sequences can always throw "not an object"
            return True
        if type.nullable():
            # treatNonObjectAsNull() and treatNonCallableAsNull() are
            # only sane things to test on nullable types, so do that now.
            if (
                allowTreatNonCallableAsNull
                and type.isCallback()
                and (type.treatNonObjectAsNull() or type.treatNonCallableAsNull())
            ):
                # This can't throw. so never needs a method description.
                return False
            type = type.inner
        else:
            break

    if type.isUndefined():
        # Clearly doesn't need a method description; we can only get here from
        # CGHeaders trying to decide whether to include the method description
        # header.
        return False
    # The float check needs to come before the isPrimitive() check,
    # because floats are primitives too.
    if type.isFloat():
        # Floats can throw if restricted.
        return not type.isUnrestricted()
    if type.isPrimitive() and type.tag() in builtinNames:
        # Numbers can throw if enforcing range.
        return type.hasEnforceRange()
    if type.isEnum():
        # Can throw on invalid value.
        return True
    if type.isString():
        # Can throw if it's a ByteString
        return type.isByteString()
    if type.isAny():
        # JS-implemented interfaces do extra security checks so need a
        # method description here.  If we have no descriptor, this
        # might be JS-implemented thing, so it will do the security
        # check and we need the method description.
        return not descriptor or descriptor.interface.isJSImplemented()
    if type.isPromise():
        # JS-to-Promise conversion won't cause us to throw any
        # specific exceptions, so does not need a method description.
        return False
    if (
        type.isObject()
        or type.isInterface()
        or type.isCallback()
        or type.isDictionary()
        or type.isRecord()
        or type.isObservableArray()
    ):
        # These can all throw if a primitive is passed in, at the very least.
        # There are some rare cases when we know we have an object, but those
        # are not worth the complexity of optimizing for.
        #
        # Note that we checked the [LegacyTreatNonObjectAsNull] case already when
        # unwrapping nullables.
        return True
    if type.isUnion():
        # Can throw if a type not in the union is passed in.
        return True
    raise TypeError("Don't know whether type '%s' needs a method description" % 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()


def wantsGetWrapperCache(desc):
    return (
        desc.concrete and desc.wrapperCache and not desc.isGlobal() and not desc.proxy
    )


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).
    """

    # We'll want to insert the indent at the beginnings of lines, but we
    # don't want to indent empty lines.
    padding = indentLevel * " "
    return "\n".join(
        [
            (padding + line) if line and line[0] != "#" else line
            for line in s.split("\n")
        ]
    )


# 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 inand 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 function accessor for a WebIDL string table, using the existing
    concatenated names string and mapping indexes to offsets in that string:

    const char *accessorName(unsigned int index) {
      static const uint16_t offsets = { ... };
      return BindingName(offsets[index]);
    }

    This is more efficient than the more natural:

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

    The uint16_t offsets are smaller than the pointer equivalents, and the
    concatenated string 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 "const char *%s(unsigned int aIndex);\n" % self.accessorName

    def define(self):
        offsets = []
        for s in self.strings:
            offsets.append(BindingNamesOffsetEnum(s))
        return fill(
            """
            ${static}const char *${name}(unsigned int aIndex)
            {
              static const BindingNamesOffset offsets[] = {
                $*{offsets}
              };
              return BindingName(offsets[aIndex]);
            }
            """,
            static="static " if self.static else "",
            name=self.accessorName,
            offsets="".join("BindingNamesOffset::%s,\n" % o for o in offsets),
        )


class CGNativePropertyHooks(CGThing):
    """
    Generate a NativePropertyHooks for a given descriptor
    """

    def __init__(self, descriptor, properties):
        CGThing.__init__(self)
        assert descriptor.wantsXrays
        self.descriptor = descriptor
        self.properties = properties

    def declare(self):
        return ""

    def define(self):
        if (
            self.descriptor.concrete
            and self.descriptor.proxy
            and not self.descriptor.isMaybeCrossOriginObject()
        ):
            if self.descriptor.needsXrayNamedDeleterHook():
                deleteNamedProperty = "DeleteNamedProperty"
            else:
                deleteNamedProperty = "nullptr"
            namedOrIndexed = fill(
                """
                const NativeNamedOrIndexedPropertyHooks sNativeNamedOrIndexedPropertyHooks = {
                  binding_detail::ResolveOwnProperty,
                  binding_detail::EnumerateOwnProperties,
                  ${deleteNamedProperty}
                };
                """,
                deleteNamedProperty=deleteNamedProperty,
            )
            namedOrIndexedPointer = "&sNativeNamedOrIndexedPropertyHooks"
        elif self.descriptor.needsXrayResolveHooks():
            namedOrIndexed = dedent(
                """
                const NativeNamedOrIndexedPropertyHooks sNativeNamedOrIndexedPropertyHooks = {
                  ResolveOwnPropertyViaResolve,
                  EnumerateOwnPropertiesViaGetOwnPropertyNames,
                  nullptr
                };
                """
            )
            namedOrIndexedPointer = "&sNativeNamedOrIndexedPropertyHooks"
        else:
            namedOrIndexed = ""
            namedOrIndexedPointer = "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"

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

        return namedOrIndexed + fill(
            """
            bool sNativePropertiesInited = false;
            const NativePropertyHooks sNativePropertyHooks = {
              ${namedOrIndexedPointer},
              { ${regular}, ${chrome}, &sNativePropertiesInited },
              ${prototypeID},
              ${constructorID},
              ${expandoClass}
            };
            """,
            namedOrIndexedPointer=namedOrIndexedPointer,
            regular=regular,
            chrome=chrome,
            prototypeID=prototypeID,
            constructorID=constructorID,
            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"

    if wantsGetWrapperCache(descriptor):
        wrapperCacheGetter = GETWRAPPERCACHE_HOOK_NAME
    else:
        wrapperCacheGetter = "nullptr"

    if descriptor.hasOrdinaryObjectPrototype():
        getProto = "JS::GetRealmObjectPrototypeHandle"
    else:
        getProto = "GetProtoObjectHandle"

    return fill(
        """
          { ${protoChain} },
          std::is_base_of_v<nsISupports, ${nativeType}>,
          ${hooks},
          FindAssociatedGlobalForNative<${nativeType}>::Get,
          ${getProto},
          GetCCParticipant<${nativeType}>::Get(),
          ${serializer},
          ${wrapperCacheGetter}
        """,
        protoChain=", ".join(protoList),
        nativeType=descriptor.nativeType,
        hooks=NativePropertyHooks(descriptor),
        serializer=serializer,
        wrapperCacheGetter=wrapperCacheGetter,
        getProto=getProto,
    )


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
            iface = getReflectedHTMLAttributesIface(self.descriptor)
            if iface:
                traceHook = (
                    "%s::ReflectedHTMLAttributeSlots::Trace"
                    % toBindingNamespace(iface.identifier.name)
                )
            else:
                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 JSClassOps sClassOps = {
              ${addProperty}, /* addProperty */
              nullptr,               /* delProperty */
              nullptr,               /* enumerate */
              ${newEnumerate}, /* newEnumerate */
              ${resolve}, /* resolve */
              ${mayResolve}, /* mayResolve */
              ${finalize}, /* finalize */
              ${call}, /* call */
              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):
        iface = getReflectedHTMLAttributesIface(self.descriptor)
        if iface:
            ops = (
                "&%s::ReflectedHTMLAttributeSlots::sXrayExpandoObjectClassOps"
                % toBindingNamespace(iface.identifier.name)
            )
        else:
            ops = "&xpc::XrayExpandoObjectClassOps"
        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_WITH_OPS(static, sXrayExpandoObjectClass, ${memberSlots},
                                               ${ops});
            """,
            memberSlots=self.descriptor.interface.totalMembersInSlots,
            ops=ops,
        )


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:
        protoHandleGetter = None
        if descriptor.interface.getExtendedAttribute("ExceptionClass"):
            protoGetter = "JS::GetRealmErrorPrototype"
        elif descriptor.interface.isIteratorInterface():
            protoGetter = "JS::GetRealmIteratorPrototype"
        elif descriptor.interface.isAsyncIteratorInterface():
            protoGetter = "JS::GetRealmAsyncIteratorPrototype"
        else:
            protoGetter = "JS::GetRealmObjectPrototype"
            protoHandleGetter = "JS::GetRealmObjectPrototypeHandle"
    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.hasLegacyUnforgeableMembers
            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},
              ${prototypeID},
              ${depth},
              ${hooks},
              ${protoGetter}
            };
            """,
            name=self.descriptor.interface.getClassName(),
            slotCount=slotCount,
            type=type,
            hooks=NativePropertyHooks(self.descriptor),
            prototypeID=prototypeID,
            depth=depth,
            protoGetter=protoGetter,
        )


def InterfaceObjectProtoGetter(descriptor):
    """
    Returns the name of the function to call to get the prototype to use for the
    interface object's prototype as a JS::Handle.
    """
    assert not descriptor.interface.isNamespace()
    parentInterface = descriptor.interface.parent
    if parentInterface:
        parentIfaceName = parentInterface.identifier.name
        parentDesc = descriptor.getDescriptor(parentIfaceName)
        prefix = toBindingNamespace(parentDesc.name)
        protoHandleGetter = prefix + "::GetConstructorObjectHandle"
    else:
        protoHandleGetter = "JS::GetRealmFunctionPrototypeHandle"
    return protoHandleGetter


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

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

    def define(self):
        classString = self.descriptor.interface.getExtendedAttribute("ClassString")
        if classString is None:
            classString = self.descriptor.interface.identifier.name
        else:
            classString = classString[0]
        return fill(
            """
            static const DOMIfaceAndProtoJSClass sNamespaceObjectClass = {
              {
                "${classString}",
                JSCLASS_IS_DOMIFACEANDPROTOJSCLASS,
                JS_NULL_CLASS_OPS,
                JS_NULL_CLASS_SPEC,
                JS_NULL_CLASS_EXT,
                JS_NULL_OBJECT_OPS
              },
              eNamespace,
              prototypes::id::_ID_Count,
              0,
              ${hooks},
              // This isn't strictly following the spec (see
              // https://console.spec.whatwg.org/#ref-for-dfn-namespace-object),
              // but should be ok for Xrays.
              JS::GetRealmObjectPrototype
            };
            """,
            classString=classString,
            hooks=NativePropertyHooks(self.descriptor),
        )


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

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

    def define(self):
        if self.descriptor.interface.ctor():
            ctorname = CONSTRUCT_HOOK_NAME
            constructorArgs = methodLength(self.descriptor.interface.ctor())
        else:
            ctorname = "ThrowingConstructor"
            constructorArgs = 0
        constructorName = self.descriptor.interface.getClassName()
        wantsIsInstance = self.descriptor.interface.hasInterfacePrototypeObject()

        prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
        protoHandleGetter = InterfaceObjectProtoGetter(self.descriptor)

        return fill(
            """
            static const DOMInterfaceInfo sInterfaceObjectInfo = {
              { ${ctorname}, ${hooks} },
              ${protoHandleGetter},
              ${depth},
              ${prototypeID},
              ${wantsIsInstance},
              ${constructorArgs},
              "${constructorName}",
            };
            """,
            ctorname=ctorname,
            hooks=NativePropertyHooks(self.descriptor),
            protoHandleGetter=protoHandleGetter,
            depth=depth,
            prototypeID=prototypeID,
            wantsIsInstance=toStringBool(wantsIsInstance),
            constructorArgs=constructorArgs,
            constructorName=constructorName,
        )


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(CGThing):
    """
    Generates namespace block that wraps other CGThings.
    """

    def __init__(self, namespace, child):
        CGThing.__init__(self)
        self.child = child
        self.pre = "namespace %s {\n" % namespace
        self.post = "} // namespace %s\n" % namespace

    def declare(self):
        decl = self.child.declare()
        if len(decl.strip()) == 0:
            return ""
        return self.pre + decl + self.post

    def define(self):
        defn = self.child.define()
        if len(defn.strip()) == 0:
            return ""
        return self.pre + defn + self.post

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

    @staticmethod
    def build(namespaces, child):
        """
        Static helper method to build multiple wrapped namespaces.
        """
        if not namespaces:
            return CGWrapper(child)
        return CGNamespace("::".join(namespaces), child)


class CGIncludeGuard(CGWrapper):
    """
    Generates include guards for a header.
    """

    def __init__(self, prefix, child):
        """|prefix| is the filename without the extension."""
        define = "DOM_%s_H_" % prefix.upper()
        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()
        )

        # 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(typeAndPossibleOriginType):
            """
            Add the relevant headers for this type.  We use its origin type, if
            passed, to decide what to do with interface types.
            """
            t, originType = typeAndPossibleOriginType
            isFromDictionary = originType and originType.isDictionary()
            isFromCallback = originType and originType.isCallback()
            # Dictionaries have members that need to be actually
            # declared, not just forward-declared.
            # Callbacks have nullable union arguments that need to be actually
            # declared, not just forward-declared.
            if isFromDictionary:
                headerSet = declareIncludes
            elif isFromCallback and t.nullable() and t.isUnion():
                headerSet = declareIncludes
            else:
                headerSet = bindingHeaders
            # Strip off outer layers and add headers they might require.  (This
            # is conservative: only nullable non-pointer types need Nullable.h;
            # only sequences or observable arrays outside unions need
            # ForOfIterator.h; only functions that return, and attributes that
            # are, sequences or observable arrays in interfaces need Array.h, &c.)
            unrolled = t
            while True:
                if idlTypeNeedsCallContext(unrolled):
                    bindingHeaders.add("mozilla/dom/BindingCallContext.h")
                if unrolled.nullable():
                    headerSet.add("mozilla/dom/Nullable.h")
                elif unrolled.isSequence() or unrolled.isObservableArray():
                    bindingHeaders.add("js/Array.h")
                    bindingHeaders.add("js/ForOfIterator.h")
                    if unrolled.isObservableArray():
                        bindingHeaders.add("mozilla/dom/ObservableArrayProxyHandler.h")
                else:
                    break
                unrolled = unrolled.inner
            if unrolled.isUnion():
                headerSet.add(self.getUnionDeclarationFilename(config, unrolled))
                for t in unrolled.flatMemberTypes:
                    addHeadersForType((t, None))
            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
                    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))
                # And if it needs rooting, we need RootedDictionary too
                if typeNeedsRooting(unrolled):
                    headerSet.add("mozilla/dom/RootedDictionary.h")
            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 isFromDictionary 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, originType if isFromDictionary else None))

        for t in getAllTypes(
            descriptors + callbackDescriptors, dictionaries, callbacks
        ):
            addHeadersForType(t)

        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 or an async iterator interface generated
            # for a separate iterable interface, skip generating type includes,
            # as we have what we need in IterableIterator.h
            if (
                desc.interface.isIteratorInterface()
                or desc.interface.isAsyncIteratorInterface()
            ):
                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)

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

        # Let the machinery do its thing.
        def _includeString(includes):
            def headerName(include):
                # System headers are specified inside angle brackets.
                if include.startswith("<"):
                    return include
                # Non-system headers need to be placed in quotes.
                return '"%s"' % include

            return "".join(["#include %s\n" % headerName(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 "" 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")
                    # Sequences can always throw "not an object" exceptions.
                    implheaders.add("mozilla/dom/BindingCallContext.h")
                    if typeNeedsRooting(f):
                        headers.add("mozilla/dom/RootedSequence.h")
                f = f.unroll()
                if idlTypeNeedsCallContext(f):
                    implheaders.add("mozilla/dom/BindingCallContext.h")
                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("js/RootingAPI.h")
                        headers.add("js/Value.h")
                        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)
                    # And if it needs rooting, we need RootedRecord too
                    if typeNeedsRooting(f):
                        headers.add("mozilla/dom/RootedRecord.h")

            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),
    )


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.

    signatureOnly should be True to only declare the signature (either in
--> --------------------

--> maximum size reached

--> --------------------

83%


¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.77Bemerkung:  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.