# 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
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 mayUseXrayExpandoSlots(descriptor, attr): assertnot 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 andnot attr.type.isGeckoInterface()
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()
): returnFalse elif type.isCallback() or type.isPromise() or type.isGeckoInterface(): returnTrue 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
) returnFalse 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 ifand 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.
""" whileTrue: if type.isSequence(): # Sequences can always throw "not an object" returnTrue 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. returnFalse
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. returnFalse # The float check needs to come before the isPrimitive() check, # because floats are primitives too. if type.isFloat(): # Floats can throw if restricted. returnnot 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. returnTrue 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. returnnot 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. returnFalse 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. returnTrue if type.isUnion(): # Can throw if a type not in the union is passed in. returnTrue 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 andnot desc.isGlobal()
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 = {}
@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 andreturn 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"notin 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 andnot prev.endswith("\n")) or nl isNone: 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) ifnot 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 `*` isfor"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: ifnot (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 classfor things that spit out code. """
def __init__(self): pass# Nothing for now
def declare(self): """Produce code for a header file.""" assertFalse# Override me!
def define(self): """Produce code for a cpp file.""" assertFalse# Override me!
def deps(self): """Produce the deps for a pp file""" assertFalse# 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:
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"
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 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 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 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*> orNoneif no
such function exists. """
parentProtoName = descriptor.parentPrototypeName if descriptor.hasNamedPropertiesObject:
protoGetter = "GetNamedPropertiesObject"
protoHandleGetter = None elif parentProtoName isNone:
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"
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 andnot 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. """ assertnot 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 isNone:
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""
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
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. """
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))
)
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 andnot 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: ifnot 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 whileTrue: 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() andnot 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 elseNone))
for t in getAllTypes(
descriptors + callbackDescriptors, dictionaries, callbacks
):
addHeadersForType(t)
def addHeaderForFunc(func, desc): if func isNone: return # Include the right class header, which we can only do # if this is a class member function. if desc isnotNoneandnot 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 isnotNone:
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"
@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 isNone 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. """
for t in unionTypes:
name = str(t) if name notin 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() andnot 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: assertnot f.nullable()
addHeadersForType(f)
# 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 classfor outputting the type and name of an argument """
class CGAbstractMethod(CGThing): """
An abstract classfor 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 isnotNone 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
--> --------------------
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet)
¤
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.