aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/bindings/codegen/Codegen.py
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/bindings/codegen/Codegen.py')
-rw-r--r--components/script/dom/bindings/codegen/Codegen.py5788
1 files changed, 5788 insertions, 0 deletions
diff --git a/components/script/dom/bindings/codegen/Codegen.py b/components/script/dom/bindings/codegen/Codegen.py
new file mode 100644
index 00000000000..6d2cc0bde36
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Codegen.py
@@ -0,0 +1,5788 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# Common codegen classes.
+
+import os
+import string
+import operator
+
+from WebIDL import *
+from Configuration import NoSuchDescriptorError
+
+AUTOGENERATED_WARNING_COMMENT = \
+ "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
+ADDPROPERTY_HOOK_NAME = '_addProperty'
+FINALIZE_HOOK_NAME = '_finalize'
+TRACE_HOOK_NAME = '_trace'
+CONSTRUCT_HOOK_NAME = '_constructor'
+HASINSTANCE_HOOK_NAME = '_hasInstance'
+
+def replaceFileIfChanged(filename, newContents):
+ """
+ Read a copy of the old file, so that we don't touch it if it hasn't changed.
+ Returns True if the file was updated, false otherwise.
+ """
+ oldFileContents = ""
+ try:
+ oldFile = open(filename, 'rb')
+ oldFileContents = ''.join(oldFile.readlines())
+ oldFile.close()
+ except:
+ pass
+
+ if newContents == oldFileContents:
+ return False
+
+ f = open(filename, 'wb')
+ f.write(newContents)
+ f.close()
+
+def toStringBool(arg):
+ return str(not not arg).lower()
+
+def toBindingNamespace(arg):
+ return re.sub("((_workers)?$)", "Binding\\1", arg);
+
+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!
+
+class CGNativePropertyHooks(CGThing):
+ """
+ Generate a NativePropertyHooks for a given descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ if self.descriptor.workers:
+ return ""
+ return "extern const NativePropertyHooks NativeHooks;\n"
+ def define(self):
+ if self.descriptor.workers:
+ return ""
+ if self.descriptor.concrete and self.descriptor.proxy:
+ resolveOwnProperty = "ResolveOwnProperty"
+ enumerateOwnProperties = "EnumerateOwnProperties"
+ else:
+ enumerateOwnProperties = resolveOwnProperty = "NULL"
+ parent = self.descriptor.interface.parent
+ parentHooks = ("&" + toBindingNamespace(parent.identifier.name) + "::NativeHooks"
+ if parent else 'NULL')
+ return """
+const NativePropertyHooks NativeHooks = { %s, ResolveProperty, %s, EnumerateProperties, %s };
+""" % (resolveOwnProperty, enumerateOwnProperties, parentHooks)
+
+def DOMClass(descriptor):
+ protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeChain]
+ # 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)))
+ prototypeChainString = ', '.join(protoList)
+ nativeHooks = "NULL" if descriptor.workers else "&NativeHooks"
+ return """{
+ { %s },
+ %s, %s
+}""" % (prototypeChainString, toStringBool(descriptor.nativeIsISupports),
+ nativeHooks)
+
+class CGDOMJSClass(CGThing):
+ """
+ Generate a DOMJSClass for a given descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ return "extern DOMJSClass Class;\n"
+ def define(self):
+ traceHook = TRACE_HOOK_NAME if self.descriptor.customTrace else 'NULL'
+ return """
+DOMJSClass Class = {
+ { "%s",
+ JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1),
+ %s, /* addProperty */
+ JS_PropertyStub, /* delProperty */
+ JS_PropertyStub, /* getProperty */
+ JS_StrictPropertyStub, /* setProperty */
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ %s, /* finalize */
+ NULL, /* checkAccess */
+ NULL, /* call */
+ NULL, /* hasInstance */
+ NULL, /* construct */
+ %s, /* trace */
+ JSCLASS_NO_INTERNAL_MEMBERS
+ },
+ %s
+};
+""" % (self.descriptor.interface.identifier.name,
+ ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.workers and self.descriptor.wrapperCache else 'JS_PropertyStub',
+ FINALIZE_HOOK_NAME, traceHook,
+ CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
+
+class CGPrototypeJSClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ # We're purely for internal consumption
+ return ""
+ def define(self):
+ return """static JSClass PrototypeClass = {
+ "%sPrototype",
+ JSCLASS_HAS_RESERVED_SLOTS(1),
+ JS_PropertyStub, /* addProperty */
+ JS_PropertyStub, /* delProperty */
+ JS_PropertyStub, /* getProperty */
+ JS_StrictPropertyStub, /* setProperty */
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ NULL, /* finalize */
+ NULL, /* checkAccess */
+ NULL, /* call */
+ NULL, /* hasInstance */
+ NULL, /* construct */
+ NULL, /* trace */
+ JSCLASS_NO_INTERNAL_MEMBERS
+};
+""" % (self.descriptor.interface.identifier.name)
+
+class CGInterfaceObjectJSClass(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 not self.descriptor.hasInstanceInterface:
+ return ""
+ ctorname = "NULL" if not self.descriptor.interface.ctor() else CONSTRUCT_HOOK_NAME
+ hasinstance = HASINSTANCE_HOOK_NAME
+ return """
+static JSClass InterfaceObjectClass = {
+ "Function", 0,
+ JS_PropertyStub, /* addProperty */
+ JS_PropertyStub, /* delProperty */
+ JS_PropertyStub, /* getProperty */
+ JS_StrictPropertyStub, /* setProperty */
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ NULL, /* finalize */
+ NULL, /* checkAccess */
+ %s, /* call */
+ %s, /* hasInstance */
+ %s, /* construct */
+ NULL, /* trace */
+ JSCLASS_NO_INTERNAL_MEMBERS
+};
+""" % (ctorname, hasinstance, ctorname)
+
+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)
+ self.children = children
+ self.joiner = joiner
+ def append(self, child):
+ self.children.append(child)
+ def prepend(self, child):
+ self.children.insert(0, child)
+ def join(self, generator):
+ return self.joiner.join(filter(lambda s: len(s) > 0, (child for child in generator)))
+ 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)
+
+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
+
+# We'll want to insert the indent at the beginnings of lines, but we
+# don't want to indent empty lines. So only indent lines that have a
+# non-newline character on them.
+lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)
+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):
+ CGThing.__init__(self)
+ self.child = child
+ self.indent = " " * indentLevel
+ self.declareOnly = declareOnly
+ def declare(self):
+ decl = self.child.declare()
+ if decl is not "":
+ return re.sub(lineStartDetector, self.indent, decl)
+ else:
+ return ""
+ def define(self):
+ defn = self.child.define()
+ if defn is not "" and not self.declareOnly:
+ return re.sub(lineStartDetector, self.indent, defn)
+ else:
+ return defn
+
+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:
+ # We don't use lineStartDetector because we don't want to
+ # insert whitespace at the beginning of our _first_ line.
+ decl = stripTrailingWhitespace(
+ decl.replace("\n", "\n" + (" " * len(self.declarePre))))
+ return self.declarePre + decl + self.declarePost
+ def define(self):
+ if self.declareOnly:
+ return ''
+ defn = self.child.define()
+ if self.reindent:
+ # We don't use lineStartDetector because we don't want to
+ # insert whitespace at the beginning of our _first_ line.
+ defn = stripTrailingWhitespace(
+ defn.replace("\n", "\n" + (" " * len(self.definePre))))
+ return self.definePre + defn + self.definePost
+
+class CGIfWrapper(CGWrapper):
+ def __init__(self, child, condition):
+ pre = CGWrapper(CGGeneric(condition), pre="if (", post=") {\n",
+ reindent=True)
+ CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(),
+ post="\n}")
+
+class CGNamespace(CGWrapper):
+ def __init__(self, namespace, child, declareOnly=False):
+ pre = "namespace %s {\n" % namespace
+ post = "} // namespace %s\n" % namespace
+ CGWrapper.__init__(self, child, pre=pre, post=post,
+ declareOnly=declareOnly)
+ @staticmethod
+ def build(namespaces, child, declareOnly=False):
+ """
+ Static helper method to build multiple wrapped namespaces.
+ """
+ if not namespaces:
+ return CGWrapper(child, declareOnly=declareOnly)
+ inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly)
+ return CGNamespace(namespaces[0], inner, declareOnly=declareOnly)
+
+class CGIncludeGuard(CGWrapper):
+ """
+ Generates include guards for a header.
+ """
+ def __init__(self, prefix, child):
+ """|prefix| is the filename without the extension."""
+ define = 'mozilla_dom_%s_h__' % prefix
+ CGWrapper.__init__(self, child,
+ declarePre='#ifndef %s\n#define %s\n\n' % (define, define),
+ declarePost='\n#endif // %s\n' % define)
+
+def getTypes(descriptor):
+ """
+ Get all argument and return types for all members of the descriptor
+ """
+ members = [m for m in descriptor.interface.members]
+ if descriptor.interface.ctor():
+ members.append(descriptor.interface.ctor())
+ signatures = [s for m in members if m.isMethod() for s in m.signatures()]
+ types = []
+ for s in signatures:
+ assert len(s) == 2
+ (returnType, arguments) = s
+ types.append(returnType)
+ types.extend([a.type for a in arguments])
+
+ types.extend(a.type for a in members if a.isAttr())
+ return types
+
+class CGHeaders(CGWrapper):
+ """
+ Generates the appropriate include statements.
+ """
+ def __init__(self, descriptors, dictionaries, declareIncludes,
+ defineIncludes, child):
+ """
+ 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.
+ """
+
+ # Determine the filenames for which we need headers.
+ interfaceDeps = [d.interface for d in descriptors]
+ ancestors = []
+ for iface in interfaceDeps:
+ while iface.parent:
+ ancestors.append(iface.parent)
+ iface = iface.parent
+ interfaceDeps.extend(ancestors)
+ 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)
+
+ # Now find all the things we'll need as arguments because we
+ # need to wrap or unwrap them.
+ bindingHeaders = set()
+ for d in descriptors:
+ types = getTypes(d)
+ for dictionary in dictionaries:
+ curDict = dictionary
+ while curDict:
+ types.extend([m.type for m in curDict.members])
+ curDict = curDict.parent
+
+ for t in types:
+ if t.unroll().isUnion():
+ # UnionConversions.h includes UnionTypes.h
+ bindingHeaders.add("mozilla/dom/UnionConversions.h")
+ elif t.unroll().isInterface():
+ if t.unroll().isSpiderMonkeyInterface():
+ bindingHeaders.add("jsfriendapi.h")
+ bindingHeaders.add("mozilla/dom/TypedArray.h")
+ else:
+ typeDesc = d.getDescriptor(t.unroll().inner.identifier.name)
+ if typeDesc is not None:
+ implementationIncludes.add(typeDesc.headerFile)
+ bindingHeaders.add(self.getDeclarationFilename(typeDesc.interface))
+ elif t.unroll().isDictionary():
+ bindingHeaders.add(self.getDeclarationFilename(t.unroll().inner))
+
+ declareIncludes = set(declareIncludes)
+ for d in dictionaries:
+ if d.parent:
+ declareIncludes.add(self.getDeclarationFilename(d.parent))
+ bindingHeaders.add(self.getDeclarationFilename(d))
+
+ # Let the machinery do its thing.
+ def _includeString(includes):
+ return ''.join(['#include "%s"\n' % i for i in includes]) + '\n'
+ CGWrapper.__init__(self, child,
+ declarePre=_includeString(sorted(declareIncludes)),
+ definePre=_includeString(sorted(set(defineIncludes) |
+ bindingIncludes |
+ bindingHeaders |
+ implementationIncludes)))
+ @staticmethod
+ def getDeclarationFilename(decl):
+ # Use our local version of the header, not the exported one, so that
+ # test bindings, which don't export, will work correctly.
+ basename = os.path.basename(decl.filename())
+ return basename.replace('.webidl', 'Binding.h')
+
+def SortedTuples(l):
+ """
+ Sort a list of tuples based on the first item in the tuple
+ """
+ return sorted(l, key=operator.itemgetter(0))
+
+def SortedDictValues(d):
+ """
+ Returns a list of values from the dict sorted by key.
+ """
+ # Create a list of tuples containing key and value, sorted on key.
+ d = SortedTuples(d.items())
+ # We're only interested in the values.
+ return (i[1] for i in d)
+
+def UnionTypes(descriptors):
+ """
+ Returns a tuple containing a set of header filenames to include, a set of
+ tuples containing a type declaration and a boolean if the type is a struct
+ for member types of the unions and a CGList containing CGUnionStructs for
+ every union.
+ """
+
+ # Now find all the things we'll need as arguments and return values because
+ # we need to wrap or unwrap them.
+ headers = set()
+ declarations = set()
+ unionStructs = dict()
+ for d in descriptors:
+ if d.interface.isExternal():
+ continue
+
+ for t in getTypes(d):
+ t = t.unroll()
+ if t.isUnion():
+ name = str(t)
+ if not name in unionStructs:
+ unionStructs[name] = CGUnionStruct(t, d)
+ for f in t.flatMemberTypes:
+ f = f.unroll()
+ if f.isInterface():
+ if f.isSpiderMonkeyInterface():
+ headers.add("jsfriendapi.h")
+ headers.add("mozilla/dom/TypedArray.h")
+ else:
+ typeDesc = d.getDescriptor(f.inner.identifier.name)
+ if typeDesc is not None:
+ declarations.add((typeDesc.nativeType, False))
+ elif f.isDictionary():
+ declarations.add((f.inner.identifier.name, True))
+
+ return (headers, declarations, CGList(SortedDictValues(unionStructs), "\n"))
+
+def UnionConversions(descriptors):
+ """
+ Returns a CGThing to declare all union argument conversion helper structs.
+ """
+ # Now find all the things we'll need as arguments because we
+ # need to unwrap them.
+ unionConversions = dict()
+ for d in descriptors:
+ if d.interface.isExternal():
+ continue
+
+ def addUnionTypes(type):
+ if type.isUnion():
+ type = type.unroll()
+ name = str(type)
+ if not name in unionConversions:
+ unionConversions[name] = CGUnionConversionStruct(type, d)
+
+ members = [m for m in d.interface.members]
+ if d.interface.ctor():
+ members.append(d.interface.ctor())
+ signatures = [s for m in members if m.isMethod() for s in m.signatures()]
+ for s in signatures:
+ assert len(s) == 2
+ (_, arguments) = s
+ for a in arguments:
+ addUnionTypes(a.type)
+
+ for m in members:
+ if m.isAttr() and not m.readonly:
+ addUnionTypes(m.type)
+
+ return CGWrapper(CGList(SortedDictValues(unionConversions), "\n"),
+ post="\n\n")
+
+class Argument():
+ """
+ A class for outputting the type and name of an argument
+ """
+ def __init__(self, argType, name):
+ self.argType = argType
+ self.name = name
+ def __str__(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.
+ """
+ def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, static=False, templateArgs=None):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ self.name = name
+ self.returnType = returnType
+ self.args = args
+ self.inline = inline
+ self.alwaysInline = alwaysInline
+ self.static = static
+ self.templateArgs = templateArgs
+ def _argstring(self):
+ return ', '.join([str(a) for a in self.args])
+ def _template(self):
+ if self.templateArgs is None:
+ return ''
+ return 'template <%s>\n' % ', '.join(self.templateArgs)
+ def _decorators(self):
+ decorators = []
+ if self.alwaysInline:
+ decorators.append('MOZ_ALWAYS_INLINE')
+ elif self.inline:
+ decorators.append('inline')
+ if self.static:
+ decorators.append('static')
+ decorators.append(self.returnType)
+ maybeNewline = " " if self.inline else "\n"
+ return ' '.join(decorators) + maybeNewline
+ def declare(self):
+ if self.inline:
+ return self._define()
+ return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring())
+ def _define(self):
+ return self.definition_prologue() + "\n" + self.definition_body() + self.definition_epilogue()
+ def define(self):
+ return "" if self.inline else self._define()
+ def definition_prologue(self):
+ return "%s%s%s(%s)\n{" % (self._template(), self._decorators(),
+ self.name, self._argstring())
+ def definition_epilogue(self):
+ return "\n}\n"
+ def definition_body(self):
+ assert(False) # Override me!
+
+class CGAbstractStaticMethod(CGAbstractMethod):
+ """
+ Abstract base class for codegen of implementation-only (no
+ declaration) static methods.
+ """
+ def __init__(self, descriptor, name, returnType, args):
+ CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
+ inline=False, static=True)
+ def declare(self):
+ # We only have implementation
+ return ""
+
+class CGAbstractClassHook(CGAbstractStaticMethod):
+ """
+ Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
+ 'this' unwrapping as it assumes that the unwrapped type is always known.
+ """
+ def __init__(self, descriptor, name, returnType, args):
+ CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
+ args)
+
+ def definition_body_prologue(self):
+ return """
+ %s* self = UnwrapDOMObject<%s>(obj, eRegularDOMObject);
+""" % (self.descriptor.nativeType, self.descriptor.nativeType)
+
+ def definition_body(self):
+ return self.definition_body_prologue() + self.generate_code()
+
+ def generate_code(self):
+ # Override me
+ assert(False)
+
+class CGAddPropertyHook(CGAbstractClassHook):
+ """
+ A hook for addProperty, used to preserve our wrapper from GC.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
+ Argument('JSHandleId', 'id'), Argument('JSMutableHandleValue', 'vp')]
+ CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME,
+ 'JSBool', args)
+
+ def generate_code(self):
+ # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=774279
+ # Using a real trace hook might enable us to deal with non-nsISupports
+ # wrappercached things here.
+ assert self.descriptor.nativeIsISupports
+ return """ nsContentUtils::PreserveWrapper(reinterpret_cast<nsISupports*>(self), self);
+ return true;"""
+
+def finalizeHook(descriptor, hookName, context):
+ if descriptor.customFinalize:
+ return """if (self) {
+ self->%s(%s);
+}""" % (hookName, context)
+ clearWrapper = "ClearWrapper(self, self);\n" if descriptor.wrapperCache else ""
+ if descriptor.workers:
+ release = "self->Release();"
+ else:
+ assert descriptor.nativeIsISupports
+ release = """XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
+if (rt) {
+ rt->DeferredRelease(reinterpret_cast<nsISupports*>(self));
+} else {
+ NS_RELEASE(self);
+}"""
+ return clearWrapper + release
+
+class CGClassFinalizeHook(CGAbstractClassHook):
+ """
+ A hook for finalize, used to release our native object.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
+ 'void', args)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(finalizeHook(self.descriptor, self.name, self.args[0].name))).define()
+
+class CGClassTraceHook(CGAbstractClassHook):
+ """
+ A hook to trace through our native object; used for GC and CC
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSTracer*', 'trc'), Argument('JSObject*', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void',
+ args)
+
+ def generate_code(self):
+ return """ if (self) {
+ self->%s(%s);
+ }""" % (self.name, self.args[0].name)
+
+class CGClassConstructHook(CGAbstractStaticMethod):
+ """
+ JS-visible constructor for our objects
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
+ CGAbstractStaticMethod.__init__(self, descriptor, CONSTRUCT_HOOK_NAME,
+ 'JSBool', args)
+ self._ctor = self.descriptor.interface.ctor()
+
+ def define(self):
+ if not self._ctor:
+ return ""
+ return CGAbstractStaticMethod.define(self)
+
+ def definition_body(self):
+ return self.generate_code()
+
+ def generate_code(self):
+ preamble = """
+ JSObject* obj = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
+"""
+ if self.descriptor.workers:
+ preArgs = ["cx", "obj"]
+ else:
+ preamble += """
+ nsISupports* global;
+ xpc_qsSelfRef globalRef;
+ {
+ nsresult rv;
+ JS::Value val = OBJECT_TO_JSVAL(obj);
+ rv = xpc_qsUnwrapArg<nsISupports>(cx, val, &global, &globalRef.ptr, &val);
+ if (NS_FAILED(rv)) {
+ return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+ }
+"""
+ preArgs = ["global"]
+
+ name = self._ctor.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ callGenerator = CGMethodCall(preArgs, nativeName, True,
+ self.descriptor, self._ctor)
+ return preamble + callGenerator.define();
+
+class CGClassHasInstanceHook(CGAbstractStaticMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
+ Argument('JSMutableHandleValue', 'vp'), Argument('JSBool*', 'bp')]
+ CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
+ 'JSBool', args)
+
+ def define(self):
+ if not self.descriptor.hasInstanceInterface:
+ return ""
+ return CGAbstractStaticMethod.define(self)
+
+ def definition_body(self):
+ return self.generate_code()
+
+ def generate_code(self):
+ return """ if (!vp.isObject()) {
+ *bp = false;
+ return true;
+ }
+
+ jsval protov;
+ if (!JS_GetProperty(cx, obj, "prototype", &protov))
+ return false;
+ if (!protov.isObject()) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE,
+ "%s");
+ return false;
+ }
+ JSObject *objProto = &protov.toObject();
+
+ JSObject* instance = &vp.toObject();
+ JSObject* proto;
+ if (!JS_GetPrototype(cx, instance, &proto))
+ return false;
+ while (proto) {
+ if (proto == objProto) {
+ *bp = true;
+ return true;
+ }
+ if (!JS_GetPrototype(cx, proto, &proto))
+ return false;
+ }
+
+ nsISupports* native =
+ nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, instance);
+ nsCOMPtr<%s> qiResult = do_QueryInterface(native);
+ *bp = !!qiResult;
+ return true;
+""" % (self.descriptor.name, self.descriptor.hasInstanceInterface)
+
+def isChromeOnly(m):
+ return m.getExtendedAttribute("ChromeOnly")
+
+class PropertyDefiner:
+ """
+ A common superclass for defining things on prototype objects.
+
+ Subclasses should implement generateArray to generate the actual arrays of
+ things we're defining. They should also set self.chrome to the list of
+ things exposed to chrome and self.regular to the list of things exposed to
+ web pages. self.chrome must be a superset of self.regular but also include
+ all the ChromeOnly stuff.
+ """
+ def __init__(self, descriptor, name):
+ self.descriptor = descriptor
+ self.name = name
+ # self.prefCacheData will store an array of (prefname, bool*)
+ # pairs for our bool var caches. generateArray will fill it
+ # in as needed.
+ self.prefCacheData = []
+ def hasChromeOnly(self):
+ return len(self.chrome) > len(self.regular)
+ def hasNonChromeOnly(self):
+ return len(self.regular) > 0
+ def variableName(self, chrome):
+ if chrome and self.hasChromeOnly():
+ return "sChrome" + self.name
+ if self.hasNonChromeOnly():
+ return "s" + self.name
+ return "NULL"
+ def usedForXrays(self, chrome):
+ # We only need Xrays for methods, attributes and constants. And we only
+ # need them for the non-chrome ones if we have no chromeonly things.
+ # Otherwise (we have chromeonly attributes) we need Xrays for the chrome
+ # methods/attributes/constants. Finally, in workers there are no Xrays.
+ return ((self.name is "Methods" or self.name is "Attributes" or
+ self.name is "Constants") and
+ chrome == self.hasChromeOnly() and
+ not self.descriptor.workers)
+
+ def __str__(self):
+ # We only need to generate id arrays for things that will end
+ # up used via ResolveProperty or EnumerateProperties.
+ str = self.generateArray(self.regular, self.variableName(False),
+ self.usedForXrays(False))
+ if self.hasChromeOnly():
+ str += self.generateArray(self.chrome, self.variableName(True),
+ self.usedForXrays(True))
+ return str
+
+ @staticmethod
+ def getControllingPref(interfaceMember):
+ prefName = interfaceMember.getExtendedAttribute("Pref")
+ if prefName is None:
+ return None
+ # It's a list of strings
+ assert(len(prefName) is 1)
+ assert(prefName[0] is not None)
+ return prefName[0]
+
+ def generatePrefableArray(self, array, name, specTemplate, specTerminator,
+ specType, getPref, getDataTuple, doIdArrays):
+ """
+ This method generates our various arrays.
+
+ array is an array of interface members as passed to generateArray
+
+ name is the name as passed to generateArray
+
+ specTemplate is a template for each entry of the spec array
+
+ specTerminator is a terminator for the spec array (inserted every time
+ our controlling pref changes and at the end of the array)
+
+ specType is the actual typename of our spec
+
+ getPref is a callback function that takes an array entry and returns
+ the corresponding pref value.
+
+ getDataTuple is a callback function that takes an array entry and
+ returns a tuple suitable for substitution into specTemplate.
+ """
+
+ # We want to generate a single list of specs, but with specTerminator
+ # inserted at every point where the pref name controlling the member
+ # changes. That will make sure the order of the properties as exposed
+ # on the interface and interface prototype objects does not change when
+ # pref control is added to members while still allowing us to define all
+ # the members in the smallest number of JSAPI calls.
+ assert(len(array) is not 0)
+ lastPref = getPref(array[0]) # So we won't put a specTerminator
+ # at the very front of the list.
+ specs = []
+ prefableSpecs = []
+ if doIdArrays:
+ prefableIds = []
+
+ prefableTemplate = ' { true, &%s[%d] }'
+ prefCacheTemplate = '&%s[%d].enabled'
+ def switchToPref(props, pref):
+ # Remember the info about where our pref-controlled
+ # booleans live.
+ if pref is not None:
+ props.prefCacheData.append(
+ (pref, prefCacheTemplate % (name, len(prefableSpecs)))
+ )
+ # Set up pointers to the new sets of specs and ids
+ # inside prefableSpecs and prefableIds
+ prefableSpecs.append(prefableTemplate %
+ (name + "_specs", len(specs)))
+
+ switchToPref(self, lastPref)
+
+ for member in array:
+ curPref = getPref(member)
+ if lastPref != curPref:
+ # Terminate previous list
+ specs.append(specTerminator)
+ # And switch to our new pref
+ switchToPref(self, curPref)
+ lastPref = curPref
+ # And the actual spec
+ specs.append(specTemplate % getDataTuple(member))
+ specs.append(specTerminator)
+ prefableSpecs.append(" { false, NULL }");
+
+ arrays = (("static %s %s_specs[] = {\n" +
+ ',\n'.join(specs) + "\n" +
+ "};\n\n" +
+ "static Prefable<%s> %s[] = {\n" +
+ ',\n'.join(prefableSpecs) + "\n" +
+ "};\n\n") % (specType, name, specType, name))
+ if doIdArrays:
+ arrays += ("static jsid %s_ids[%i] = { JSID_VOID };\n\n" %
+ (name, len(specs)))
+ return arrays
+
+
+# The length of a method is the maximum of the lengths of the
+# argument lists of all its overloads.
+def methodLength(method):
+ signatures = method.signatures()
+ return max([len(arguments) for (retType, arguments) in signatures])
+
+class MethodDefiner(PropertyDefiner):
+ """
+ A class for defining methods on a prototype object.
+ """
+ def __init__(self, descriptor, name, static):
+ PropertyDefiner.__init__(self, descriptor, name)
+
+ # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
+ # We should be able to check for special operations without an
+ # identifier. For now we check if the name starts with __
+ methods = [m for m in descriptor.interface.members if
+ m.isMethod() and m.isStatic() == static and
+ not m.isIdentifierLess()]
+ self.chrome = [{"name": m.identifier.name,
+ "length": methodLength(m),
+ "flags": "JSPROP_ENUMERATE",
+ "pref": PropertyDefiner.getControllingPref(m) }
+ for m in methods]
+ self.regular = [{"name": m.identifier.name,
+ "length": methodLength(m),
+ "flags": "JSPROP_ENUMERATE",
+ "pref": PropertyDefiner.getControllingPref(m) }
+ for m in methods if not isChromeOnly(m)]
+
+ # FIXME Check for an existing iterator on the interface first.
+ if any(m.isGetter() and m.isIndexed() for m in methods):
+ self.chrome.append({"name": 'iterator',
+ "methodInfo": False,
+ "nativeName": "JS_ArrayIterator",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "pref": None })
+ self.regular.append({"name": 'iterator',
+ "methodInfo": False,
+ "nativeName": "JS_ArrayIterator",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "pref": None })
+
+ if not descriptor.interface.parent and not static and not descriptor.workers:
+ self.chrome.append({"name": 'QueryInterface',
+ "methodInfo": False,
+ "length": 1,
+ "flags": "0",
+ "pref": None })
+
+ if static:
+ if not descriptor.interface.hasInterfaceObject():
+ # static methods go on the interface object
+ assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
+ else:
+ if not descriptor.interface.hasInterfacePrototypeObject():
+ # non-static methods go on the interface prototype object
+ assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
+
+ def generateArray(self, array, name, doIdArrays):
+ if len(array) == 0:
+ return ""
+
+ def pref(m):
+ return m["pref"]
+
+ def specData(m):
+ if m.get("methodInfo", True):
+ jitinfo = ("&%s_methodinfo" % m["name"])
+ accessor = "genericMethod"
+ else:
+ jitinfo = "nullptr"
+ accessor = m.get("nativeName", m["name"])
+ return (m["name"], accessor, jitinfo, m["length"], m["flags"])
+
+ return self.generatePrefableArray(
+ array, name,
+ ' JS_FNINFO("%s", %s, %s, %s, %s)',
+ ' JS_FS_END',
+ 'JSFunctionSpec',
+ pref, specData, doIdArrays)
+
+class AttrDefiner(PropertyDefiner):
+ def __init__(self, descriptor, name):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.chrome = [m for m in descriptor.interface.members if m.isAttr()]
+ self.regular = [m for m in self.chrome if not isChromeOnly(m)]
+
+ def generateArray(self, array, name, doIdArrays):
+ if len(array) == 0:
+ return ""
+
+ def flags(attr):
+ return "JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS"
+
+ def getter(attr):
+ native = ("genericLenientGetter" if attr.hasLenientThis()
+ else "genericGetter")
+ return ("{(JSPropertyOp)%(native)s, &%(name)s_getterinfo}"
+ % {"name" : attr.identifier.name,
+ "native" : native})
+
+ def setter(attr):
+ if attr.readonly:
+ return "JSOP_NULLWRAPPER"
+ native = ("genericLenientSetter" if attr.hasLenientThis()
+ else "genericSetter")
+ return ("{(JSStrictPropertyOp)%(native)s, &%(name)s_setterinfo}"
+ % {"name" : attr.identifier.name,
+ "native" : native})
+
+ def specData(attr):
+ return (attr.identifier.name, flags(attr), getter(attr),
+ setter(attr))
+
+ return self.generatePrefableArray(
+ array, name,
+ ' { "%s", 0, %s, %s, %s}',
+ ' { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }',
+ 'JSPropertySpec',
+ PropertyDefiner.getControllingPref, specData, doIdArrays)
+
+class ConstDefiner(PropertyDefiner):
+ """
+ A class for definining constants on the interface object
+ """
+ def __init__(self, descriptor, name):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.chrome = [m for m in descriptor.interface.members if m.isConst()]
+ self.regular = [m for m in self.chrome if not isChromeOnly(m)]
+
+ def generateArray(self, array, name, doIdArrays):
+ if len(array) == 0:
+ return ""
+
+ def specData(const):
+ return (const.identifier.name,
+ convertConstIDLValueToJSVal(const.value))
+
+ return self.generatePrefableArray(
+ array, name,
+ ' { "%s", %s }',
+ ' { 0, JSVAL_VOID }',
+ 'ConstantSpec',
+ PropertyDefiner.getControllingPref, specData, doIdArrays)
+
+class PropertyArrays():
+ def __init__(self, descriptor):
+ self.staticMethods = MethodDefiner(descriptor, "StaticMethods", True)
+ self.methods = MethodDefiner(descriptor, "Methods", False)
+ self.attrs = AttrDefiner(descriptor, "Attributes")
+ self.consts = ConstDefiner(descriptor, "Constants")
+
+ @staticmethod
+ def arrayNames():
+ return [ "staticMethods", "methods", "attrs", "consts" ]
+
+ @staticmethod
+ def xrayRelevantArrayNames():
+ return [ "methods", "attrs", "consts" ]
+
+ def hasChromeOnly(self):
+ return reduce(lambda b, a: b or getattr(self, a).hasChromeOnly(),
+ self.arrayNames(), False)
+ def variableNames(self, chrome):
+ names = {}
+ for array in self.arrayNames():
+ names[array] = getattr(self, array).variableName(chrome)
+ return names
+ def __str__(self):
+ define = ""
+ for array in self.arrayNames():
+ define += str(getattr(self, array))
+ return define
+
+class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
+ """
+ Generate the CreateInterfaceObjects method for an interface descriptor.
+
+ properties should be a PropertyArrays instance.
+ """
+ def __init__(self, descriptor, properties):
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal'),
+ Argument('JSObject*', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'JSObject*', args)
+ self.properties = properties
+ def definition_body(self):
+ protoChain = self.descriptor.prototypeChain
+ if len(protoChain) == 1:
+ getParentProto = "JS_GetObjectPrototype(aCx, aGlobal)"
+ else:
+ parentProtoName = self.descriptor.prototypeChain[-2]
+ getParentProto = ("%s::GetProtoObject(aCx, aGlobal, aReceiver)" %
+ toBindingNamespace(parentProtoName))
+
+ needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
+ needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject()
+
+ # if we don't need to create anything, why are we generating this?
+ assert needInterfaceObject or needInterfacePrototypeObject
+
+ idsToInit = []
+ # There is no need to init any IDs in workers, because worker bindings
+ # don't have Xrays.
+ if not self.descriptor.workers:
+ for var in self.properties.xrayRelevantArrayNames():
+ props = getattr(self.properties, var)
+ # We only have non-chrome ids to init if we have no chrome ids.
+ if props.hasChromeOnly():
+ idsToInit.append(props.variableName(True))
+ elif props.hasNonChromeOnly():
+ idsToInit.append(props.variableName(False))
+ if len(idsToInit) > 0:
+ initIds = CGList(
+ [CGGeneric("!InitIds(aCx, %s, %s_ids)" % (varname, varname)) for
+ varname in idsToInit], ' ||\n')
+ if len(idsToInit) > 1:
+ initIds = CGWrapper(initIds, pre="(", post=")", reindent=True)
+ initIds = CGList(
+ [CGGeneric("%s_ids[0] == JSID_VOID &&" % idsToInit[0]), initIds],
+ "\n")
+ initIds = CGWrapper(initIds, pre="if (", post=") {", reindent=True)
+ initIds = CGList(
+ [initIds,
+ CGGeneric((" %s_ids[0] = JSID_VOID;\n"
+ " return NULL;") % idsToInit[0]),
+ CGGeneric("}")],
+ "\n")
+ else:
+ initIds = None
+
+ prefCacheData = []
+ for var in self.properties.arrayNames():
+ props = getattr(self.properties, var)
+ prefCacheData.extend(props.prefCacheData)
+ if len(prefCacheData) is not 0:
+ prefCacheData = [
+ CGGeneric('Preferences::AddBoolVarCache(%s, "%s");' % (ptr, pref)) for
+ (pref, ptr) in prefCacheData]
+ prefCache = CGWrapper(CGIndenter(CGList(prefCacheData, "\n")),
+ pre=("static bool sPrefCachesInited = false;\n"
+ "if (!sPrefCachesInited) {\n"
+ " sPrefCachesInited = true;\n"),
+ post="\n}")
+ else:
+ prefCache = None
+
+ getParentProto = ("JSObject* parentProto = %s;\n" +
+ "if (!parentProto) {\n" +
+ " return NULL;\n" +
+ "}\n") % getParentProto
+
+ needInterfaceObjectClass = (needInterfaceObject and
+ self.descriptor.hasInstanceInterface)
+ needConstructor = (needInterfaceObject and
+ not self.descriptor.hasInstanceInterface)
+ if self.descriptor.interface.ctor():
+ constructHook = CONSTRUCT_HOOK_NAME
+ constructArgs = methodLength(self.descriptor.interface.ctor())
+ else:
+ constructHook = "ThrowingConstructor"
+ constructArgs = 0
+
+ if self.descriptor.concrete:
+ if self.descriptor.proxy:
+ domClass = "&Class"
+ else:
+ domClass = "&Class.mClass"
+ else:
+ domClass = "nullptr"
+
+ call = """return dom::CreateInterfaceObjects(aCx, aGlobal, aReceiver, parentProto,
+ %s, %s, %s, %d,
+ %s,
+ %%(methods)s, %%(attrs)s,
+ %%(consts)s, %%(staticMethods)s,
+ %s);""" % (
+ "&PrototypeClass" if needInterfacePrototypeObject else "NULL",
+ "&InterfaceObjectClass" if needInterfaceObjectClass else "NULL",
+ constructHook if needConstructor else "NULL",
+ constructArgs,
+ domClass,
+ '"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL")
+ if self.properties.hasChromeOnly():
+ if self.descriptor.workers:
+ accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()"
+ else:
+ accessCheck = "xpc::AccessCheck::isChrome(js::GetObjectCompartment(aGlobal))"
+ chrome = CGIfWrapper(CGGeneric(call % self.properties.variableNames(True)),
+ accessCheck)
+ chrome = CGWrapper(chrome, pre="\n\n")
+ else:
+ chrome = None
+
+ functionBody = CGList(
+ [CGGeneric(getParentProto), initIds, prefCache, chrome,
+ CGGeneric(call % self.properties.variableNames(False))],
+ "\n\n")
+ return CGIndenter(functionBody).define()
+
+class CGGetPerInterfaceObject(CGAbstractMethod):
+ """
+ A method for getting a per-interface object (a prototype object or interface
+ constructor object).
+ """
+ def __init__(self, descriptor, name, idPrefix=""):
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal'),
+ Argument('JSObject*', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, name,
+ 'JSObject*', args, inline=True)
+ self.id = idPrefix + "id::" + self.descriptor.name
+ def definition_body(self):
+ return """
+
+ /* aGlobal and aReceiver are usually the same, but they can be different
+ too. For example a sandbox often has an xray wrapper for a window as the
+ prototype of the sandbox's global. In that case aReceiver is the xray
+ wrapper and aGlobal is the sandbox's global.
+ */
+
+ /* Make sure our global is sane. Hopefully we can remove this sometime */
+ if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
+ return NULL;
+ }
+ /* Check to see whether the interface objects are already installed */
+ JSObject** protoOrIfaceArray = GetProtoOrIfaceArray(aGlobal);
+ JSObject* cachedObject = protoOrIfaceArray[%s];
+ if (!cachedObject) {
+ protoOrIfaceArray[%s] = cachedObject = CreateInterfaceObjects(aCx, aGlobal, aReceiver);
+ }
+
+ /* cachedObject might _still_ be null, but that's OK */
+ return cachedObject;""" % (self.id, self.id)
+
+class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface prototype object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
+ "prototypes::")
+ def definition_body(self):
+ return """
+ /* Get the interface prototype object for this class. This will create the
+ object as needed. */""" + CGGetPerInterfaceObject.definition_body(self)
+
+class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface constructor object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject",
+ "constructors::")
+ def definition_body(self):
+ return """
+ /* Get the interface object for this class. This will create the object as
+ needed. */""" + CGGetPerInterfaceObject.definition_body(self)
+
+def CheckPref(descriptor, globalName, varName, retval, wrapperCache = None):
+ """
+ Check whether bindings should be enabled for this descriptor. If not, set
+ varName to false and return retval.
+ """
+ if not descriptor.prefable:
+ return ""
+
+ if wrapperCache:
+ wrapperCache = " %s->ClearIsDOMBinding();\n" % (wrapperCache)
+ else:
+ wrapperCache = ""
+
+ failureCode = (" %s = false;\n" +
+ " return %s;") % (varName, retval)
+ return """
+ {
+ XPCWrappedNativeScope* scope =
+ XPCWrappedNativeScope::FindInJSObjectScope(aCx, %s);
+ if (!scope) {
+%s
+ }
+
+ if (!scope->ExperimentalBindingsEnabled()) {
+%s%s
+ }
+ }
+""" % (globalName, failureCode, wrapperCache, failureCode)
+
+class CGDefineDOMInterfaceMethod(CGAbstractMethod):
+ """
+ A method for resolve hooks to try to lazily define the interface object for
+ a given interface.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aReceiver'),
+ Argument('bool*', 'aEnabled')]
+ CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'bool', args)
+
+ def declare(self):
+ if self.descriptor.workers:
+ return ''
+ return CGAbstractMethod.declare(self)
+
+ def define(self):
+ if self.descriptor.workers:
+ return ''
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ if self.descriptor.interface.hasInterfacePrototypeObject():
+ # We depend on GetProtoObject defining an interface constructor
+ # object as needed.
+ getter = "GetProtoObject"
+ else:
+ getter = "GetConstructorObject"
+
+ return (" JSObject* global = JS_GetGlobalForObject(aCx, aReceiver);\n" +
+ CheckPref(self.descriptor, "global", "*aEnabled", "false") +
+ """
+ *aEnabled = true;
+ return !!%s(aCx, global, aReceiver);""" % (getter))
+
+class CGPrefEnabled(CGAbstractMethod):
+ """
+ A method for testing whether the preference controlling this
+ interface is enabled. When it's not, the interface should not be
+ visible on the global.
+ """
+ def __init__(self, descriptor):
+ CGAbstractMethod.__init__(self, descriptor, 'PrefEnabled', 'bool', [])
+
+ def declare(self):
+ return CGAbstractMethod.declare(self)
+
+ def define(self):
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ return " return %s::PrefEnabled();" % self.descriptor.nativeType
+
+class CGIsMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSObject*', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, 'Is', 'bool', args)
+
+ def definition_body(self):
+ # Non-proxy implementation would check
+ # js::GetObjectJSClass(obj) == &Class.mBase
+ return """ return IsProxy(obj);"""
+
+def CreateBindingJSObject(descriptor, parent):
+ if descriptor.proxy:
+ create = """ JSObject *obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(),
+ JS::PrivateValue(aObject), proto, %s);
+ if (!obj) {
+ return NULL;
+ }
+
+"""
+ else:
+ create = """ JSObject* obj = JS_NewObject(aCx, &Class.mBase, proto, %s);
+ if (!obj) {
+ return NULL;
+ }
+
+ js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
+"""
+ return create % parent
+
+class CGWrapWithCacheMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
+ Argument(descriptor.nativeType + '*', 'aObject'),
+ Argument('nsWrapperCache*', 'aCache'),
+ Argument('bool*', 'aTriedToWrap')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
+
+ def definition_body(self):
+ if self.descriptor.workers:
+ return """ *aTriedToWrap = true;
+ return aObject->GetJSObject();"""
+
+ return """ *aTriedToWrap = true;
+
+ JSObject* parent = WrapNativeParent(aCx, aScope, aObject->GetParentObject());
+ if (!parent) {
+ return NULL;
+ }
+
+ JSAutoCompartment ac(aCx, parent);
+ JSObject* global = JS_GetGlobalForObject(aCx, parent);
+%s
+ JSObject* proto = GetProtoObject(aCx, global, global);
+ if (!proto) {
+ return NULL;
+ }
+
+%s
+ NS_ADDREF(aObject);
+
+ aCache->SetWrapper(obj);
+
+ return obj;""" % (CheckPref(self.descriptor, "global", "*aTriedToWrap", "NULL", "aCache"),
+ CreateBindingJSObject(self.descriptor, "parent"))
+
+class CGWrapMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ # XXX can we wrap if we don't have an interface prototype object?
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
+ Argument('T*', 'aObject'), Argument('bool*', 'aTriedToWrap')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args, inline=True, templateArgs=["class T"])
+
+ def definition_body(self):
+ return " return Wrap(aCx, aScope, aObject, aObject, aTriedToWrap);"
+
+class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ # XXX can we wrap if we don't have an interface prototype object?
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
+ Argument(descriptor.nativeType + '*', 'aObject')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
+
+ def definition_body(self):
+ return """
+ JSObject* global = JS_GetGlobalForObject(aCx, aScope);
+ JSObject* proto = GetProtoObject(aCx, global, global);
+ if (!proto) {
+ return NULL;
+ }
+
+%s
+ NS_ADDREF(aObject);
+
+ return obj;""" % CreateBindingJSObject(self.descriptor, "global")
+
+builtinNames = {
+ IDLType.Tags.bool: 'bool',
+ IDLType.Tags.int8: 'int8_t',
+ IDLType.Tags.int16: 'int16_t',
+ IDLType.Tags.int32: 'int32_t',
+ IDLType.Tags.int64: 'int64_t',
+ IDLType.Tags.uint8: 'uint8_t',
+ IDLType.Tags.uint16: 'uint16_t',
+ IDLType.Tags.uint32: 'uint32_t',
+ IDLType.Tags.uint64: 'uint64_t',
+ IDLType.Tags.float: 'float',
+ IDLType.Tags.double: 'double'
+}
+
+numericTags = [
+ IDLType.Tags.int8, IDLType.Tags.uint8,
+ IDLType.Tags.int16, IDLType.Tags.uint16,
+ IDLType.Tags.int32, IDLType.Tags.uint32,
+ IDLType.Tags.int64, IDLType.Tags.uint64,
+ IDLType.Tags.float, IDLType.Tags.double
+ ]
+
+class CastableObjectUnwrapper():
+ """
+ A class for unwrapping an object named by the "source" argument
+ based on the passed-in descriptor and storing it in a variable
+ called by the name in the "target" argument.
+
+ codeOnFailure is the code to run if unwrapping fails.
+ """
+ def __init__(self, descriptor, source, target, codeOnFailure):
+ assert descriptor.castable
+
+ self.substitution = { "type" : descriptor.nativeType,
+ "protoID" : "prototypes::id::" + descriptor.name,
+ "source" : source,
+ "target" : target,
+ "codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure), 4).define() }
+ if descriptor.hasXPConnectImpls:
+ # We don't use xpc_qsUnwrapThis because it will always throw on
+ # unwrap failure, whereas we want to control whether we throw or
+ # not.
+ self.substitution["codeOnFailure"] = CGIndenter(CGGeneric(string.Template(
+ "${type} *objPtr;\n"
+ "xpc_qsSelfRef objRef;\n"
+ "JS::Value val = JS::ObjectValue(*${source});\n"
+ "nsresult rv = xpc_qsUnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val);\n"
+ "if (NS_FAILED(rv)) {\n"
+ "${codeOnFailure}\n"
+ "}\n"
+ "// We should be castable!\n"
+ "MOZ_ASSERT(!objRef.ptr);\n"
+ "// We should have an object, too!\n"
+ "MOZ_ASSERT(objPtr);\n"
+ "${target} = objPtr;").substitute(self.substitution)), 4).define()
+
+ def __str__(self):
+ return string.Template(
+"""{
+ nsresult rv = UnwrapObject<${protoID}, ${type}>(cx, ${source}, ${target});
+ if (NS_FAILED(rv)) {
+${codeOnFailure}
+ }
+}""").substitute(self.substitution)
+
+class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
+ """
+ As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
+ """
+ def __init__(self, descriptor, source, target):
+ CastableObjectUnwrapper.__init__(self, descriptor, source, target,
+ "return Throw<%s>(cx, rv);" %
+ toStringBool(not descriptor.workers))
+
+class CallbackObjectUnwrapper:
+ """
+ A class for unwrapping objects implemented in JS.
+
+ |source| is the JSObject we want to use in native code.
+ |target| is an nsCOMPtr of the appropriate type in which we store the result.
+ """
+ def __init__(self, descriptor, source, target, codeOnFailure=None):
+ if codeOnFailure is None:
+ codeOnFailure = ("return Throw<%s>(cx, rv);" %
+ toStringBool(not descriptor.workers))
+ self.descriptor = descriptor
+ self.substitution = { "nativeType" : descriptor.nativeType,
+ "source" : source,
+ "target" : target,
+ "codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure)).define() }
+
+ def __str__(self):
+ if self.descriptor.workers:
+ return string.Template(
+ "${target} = ${source};"
+ ).substitute(self.substitution)
+
+ return string.Template(
+ """nsresult rv;
+XPCCallContext ccx(JS_CALLER, cx);
+if (!ccx.IsValid()) {
+ rv = NS_ERROR_XPC_BAD_CONVERT_JS;
+${codeOnFailure}
+}
+
+const nsIID& iid = NS_GET_IID(${nativeType});
+nsRefPtr<nsXPCWrappedJS> wrappedJS;
+rv = nsXPCWrappedJS::GetNewOrUsed(ccx, ${source}, iid,
+ NULL, getter_AddRefs(wrappedJS));
+if (NS_FAILED(rv) || !wrappedJS) {
+${codeOnFailure}
+}
+
+// Use a temp nsCOMPtr for the null-check, because ${target} might be
+// OwningNonNull, not an nsCOMPtr.
+nsCOMPtr<${nativeType}> tmp = do_QueryObject(wrappedJS.get());
+if (!tmp) {
+${codeOnFailure}
+}
+${target} = tmp.forget();""").substitute(self.substitution)
+
+def dictionaryHasSequenceMember(dictionary):
+ return (any(typeIsSequenceOrHasSequenceMember(m.type) for m in
+ dictionary.members) or
+ (dictionary.parent and
+ dictionaryHasSequenceMember(dictionary.parent)))
+
+def typeIsSequenceOrHasSequenceMember(type):
+ if type.nullable():
+ type = type.inner
+ if type.isSequence():
+ return True
+ if type.isArray():
+ elementType = type.inner
+ return typeIsSequenceOrHasSequenceMember(elementType)
+ if type.isDictionary():
+ return dictionaryHasSequenceMember(type.inner)
+ if type.isUnion():
+ return any(typeIsSequenceOrHasSequenceMember(m.type) for m in
+ type.flatMemberTypes)
+ return False
+
+def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
+ isDefinitelyObject=False,
+ isMember=False,
+ isOptional=False,
+ invalidEnumValueFatal=True,
+ defaultValue=None,
+ treatNullAs="Default",
+ treatUndefinedAs="Default",
+ isEnforceRange=False,
+ isClamp=False):
+ """
+ Get a template for converting a JS value to a native object based on the
+ given type and descriptor. If failureCode is given, then we're actually
+ testing whether we can convert the argument to the desired type. That
+ means that failures to convert due to the JS value being the wrong type of
+ value need to use failureCode instead of throwing exceptions. Failures to
+ convert that are due to JS exceptions (from toString or valueOf methods) or
+ out of memory conditions need to throw exceptions no matter what
+ failureCode is.
+
+ If isDefinitelyObject is True, that means we know the value
+ isObject() and we have no need to recheck that.
+
+ if isMember is True, we're being converted from a property of some
+ JS object, not from an actual method argument, so we can't rely on
+ our jsval being rooted or outliving us in any way. Any caller
+ passing true needs to ensure that it is handled correctly in
+ typeIsSequenceOrHasSequenceMember.
+
+ If isOptional is true, then we are doing conversion of an optional
+ argument with no default value.
+
+ invalidEnumValueFatal controls whether an invalid enum value conversion
+ attempt will throw (if true) or simply return without doing anything (if
+ false).
+
+ If defaultValue is not None, it's the IDL default value for this conversion
+
+ If isEnforceRange is true, we're converting an integer and throwing if the
+ value is out of range.
+
+ If isClamp is true, we're converting an integer and clamping if the
+ value is out of range.
+
+ The return value from this function is a tuple consisting of four things:
+
+ 1) A string representing the conversion code. This will have template
+ substitution performed on it as follows:
+
+ ${val} replaced by an expression for the JS::Value in question
+ ${valPtr} is a pointer to the JS::Value in question
+ ${holderName} replaced by the holder's name, if any
+ ${declName} replaced by the declaration's name
+ ${haveValue} replaced by an expression that evaluates to a boolean
+ for whether we have a JS::Value. Only used when
+ defaultValue is not None.
+
+ 2) A CGThing representing the native C++ type we're converting to
+ (declType). This is allowed to be None if the conversion code is
+ supposed to be used as-is.
+ 3) A CGThing representing the type of a "holder" (holderType) which will
+ hold a possible reference to the C++ thing whose type we returned in #1,
+ or None if no such holder is needed.
+ 4) A boolean indicating whether the caller has to do optional-argument handling.
+ This will only be true if isOptional is true and if the returned template
+ expects both declType and holderType to be wrapped in Optional<>, with
+ ${declName} and ${holderName} adjusted to point to the Value() of the
+ Optional, and Construct() calls to be made on the Optional<>s as needed.
+
+ ${declName} must be in scope before the generated code is entered.
+
+ If holderType is not None then ${holderName} must be in scope
+ before the generated code is entered.
+ """
+ # If we have a defaultValue then we're not actually optional for
+ # purposes of what we need to be declared as.
+ assert(defaultValue is None or not isOptional)
+
+ # Also, we should not have a defaultValue if we know we're an object
+ assert(not isDefinitelyObject or defaultValue is None)
+
+ # Helper functions for dealing with failures due to the JS value being the
+ # wrong type of value
+ def onFailureNotAnObject(failureCode):
+ return CGWrapper(CGGeneric(
+ failureCode or
+ 'return ThrowErrorMessage(cx, MSG_NOT_OBJECT);'), post="\n")
+ def onFailureBadType(failureCode, typeName):
+ return CGWrapper(CGGeneric(
+ failureCode or
+ 'return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");' % typeName), post="\n")
+
+ # A helper function for handling default values. Takes a template
+ # body and the C++ code to set the default value and wraps the
+ # given template body in handling for the default value.
+ def handleDefault(template, setDefault):
+ if defaultValue is None:
+ return template
+ return CGWrapper(
+ CGIndenter(CGGeneric(template)),
+ pre="if (${haveValue}) {\n",
+ post=("\n"
+ "} else {\n"
+ "%s;\n"
+ "}" %
+ CGIndenter(CGGeneric(setDefault)).define())).define()
+
+ # A helper function for handling null default values. Much like
+ # handleDefault, but checks that the default value, if it exists, is null.
+ def handleDefaultNull(template, codeToSetNull):
+ if (defaultValue is not None and
+ not isinstance(defaultValue, IDLNullValue)):
+ raise TypeError("Can't handle non-null default value here")
+ return handleDefault(template, codeToSetNull)
+
+ # A helper function for wrapping up the template body for
+ # possibly-nullable objecty stuff
+ def wrapObjectTemplate(templateBody, isDefinitelyObject, type,
+ codeToSetNull, failureCode=None):
+ if not isDefinitelyObject:
+ # Handle the non-object cases by wrapping up the whole
+ # thing in an if cascade.
+ templateBody = (
+ "if (${val}.isObject()) {\n" +
+ CGIndenter(CGGeneric(templateBody)).define() + "\n")
+ if type.nullable():
+ templateBody += (
+ "} else if (${val}.isNullOrUndefined()) {\n"
+ " %s;\n" % codeToSetNull)
+ templateBody += (
+ "} else {\n" +
+ CGIndenter(onFailureNotAnObject(failureCode)).define() +
+ "}")
+ if type.nullable():
+ templateBody = handleDefaultNull(templateBody, codeToSetNull)
+ else:
+ assert(defaultValue is None)
+
+ return templateBody
+
+ assert not (isEnforceRange and isClamp) # These are mutually exclusive
+
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ assert not isEnforceRange and not isClamp
+
+ if failureCode is not None:
+ raise TypeError("Can't handle sequences when failureCode is not None")
+ nullable = type.nullable();
+ # Be very careful not to change "type": we need it later
+ if nullable:
+ elementType = type.inner.inner
+ else:
+ elementType = type.inner
+
+ # We have to be careful with reallocation behavior for arrays. In
+ # particular, if we have a sequence of elements which are themselves
+ # sequences (so nsAutoTArrays) or have sequences as members, we have a
+ # problem. In that case, resizing the outermost nsAutoTarray to the
+ # right size will memmove its elements, but nsAutoTArrays are not
+ # memmovable and hence will end up with pointers to bogus memory, which
+ # is bad. To deal with this, we disallow sequences, arrays,
+ # dictionaries, and unions which contain sequences as sequence item
+ # types. If WebIDL ever adds another container type, we'd have to
+ # disallow it as well.
+ if typeIsSequenceOrHasSequenceMember(elementType):
+ raise TypeError("Can't handle a sequence containing another "
+ "sequence as an element or member of an element. "
+ "See the big comment explaining why.\n%s" %
+ str(type.location))
+
+ (elementTemplate, elementDeclType,
+ elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
+ elementType, descriptorProvider, isMember=True)
+ if dealWithOptional:
+ raise TypeError("Shouldn't have optional things in sequences")
+ if elementHolderType is not None:
+ raise TypeError("Shouldn't need holders for sequences")
+
+ typeName = CGWrapper(elementDeclType, pre="Sequence< ", post=" >")
+ if nullable:
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >")
+ arrayRef = "${declName}.Value()"
+ else:
+ arrayRef = "${declName}"
+ # If we're optional, the const will come from the Optional
+ mutableTypeName = typeName
+ if not isOptional:
+ typeName = CGWrapper(typeName, pre="const ")
+
+ templateBody = ("""JSObject* seq = &${val}.toObject();\n
+if (!IsArrayLike(cx, seq)) {
+ return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+}
+uint32_t length;
+// JS_GetArrayLength actually works on all objects
+if (!JS_GetArrayLength(cx, seq, &length)) {
+ return false;
+}
+Sequence< %s > &arr = const_cast< Sequence< %s >& >(%s);
+if (!arr.SetCapacity(length)) {
+ return Throw<%s>(cx, NS_ERROR_OUT_OF_MEMORY);
+}
+for (uint32_t i = 0; i < length; ++i) {
+ jsval temp;
+ if (!JS_GetElement(cx, seq, i, &temp)) {
+ return false;
+ }
+""" % (toStringBool(descriptorProvider.workers),
+ elementDeclType.define(),
+ elementDeclType.define(),
+ arrayRef,
+ toStringBool(descriptorProvider.workers)))
+
+ templateBody += CGIndenter(CGGeneric(
+ string.Template(elementTemplate).substitute(
+ {
+ "val" : "temp",
+ "valPtr": "&temp",
+ "declName" : "(*arr.AppendElement())"
+ }
+ ))).define()
+
+ templateBody += "\n}"
+ templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+ type,
+ "const_cast< %s & >(${declName}).SetNull()" % mutableTypeName.define())
+ return (templateBody, typeName, None, isOptional)
+
+ if type.isUnion():
+ if isMember:
+ raise TypeError("Can't handle unions as members, we have a "
+ "holderType")
+ nullable = type.nullable();
+ if nullable:
+ type = type.inner
+
+ assert(defaultValue is None or
+ (isinstance(defaultValue, IDLNullValue) and nullable))
+
+ unionArgumentObj = "${holderName}"
+ if isOptional or nullable:
+ unionArgumentObj += ".ref()"
+
+ memberTypes = type.flatMemberTypes
+ names = []
+
+ interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
+ if len(interfaceMemberTypes) > 0:
+ interfaceObject = []
+ for memberType in interfaceMemberTypes:
+ if type.isGeckoInterface():
+ name = memberType.inner.identifier.name
+ else:
+ name = memberType.name
+ interfaceObject.append(CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext" % (unionArgumentObj, name)))
+ names.append(name)
+ interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), pre="done = ", post=";\n", reindent=True)
+ else:
+ interfaceObject = None
+
+ arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
+ if len(arrayObjectMemberTypes) > 0:
+ assert len(arrayObjectMemberTypes) == 1
+ memberType = arrayObjectMemberTypes[0]
+ name = memberType.name
+ arrayObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ # XXX Now we're supposed to check for an array or a platform object
+ # that supports indexed properties... skip that last for now. It's a
+ # bit of a pain.
+ arrayObject = CGWrapper(CGIndenter(arrayObject),
+ pre="if (IsArrayLike(cx, &argObj)) {\n",
+ post="}")
+ names.append(name)
+ else:
+ arrayObject = None
+
+ dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
+ if len(dateObjectMemberTypes) > 0:
+ assert len(dateObjectMemberTypes) == 1
+ memberType = dateObjectMemberTypes[0]
+ name = memberType.name
+ dateObject = CGGeneric("%s.SetTo%s(cx, ${val}, ${valPtr});\n"
+ "done = true;" % (unionArgumentObj, name))
+ dateObject = CGWrapper(CGIndenter(dateObject),
+ pre="if (JS_ObjectIsDate(cx, &argObj)) {\n",
+ post="\n}")
+ names.append(name)
+ else:
+ dateObject = None
+
+ callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
+ if len(callbackMemberTypes) > 0:
+ assert len(callbackMemberTypes) == 1
+ memberType = callbackMemberTypes[0]
+ name = memberType.name
+ callbackObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ names.append(name)
+ else:
+ callbackObject = None
+
+ dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
+ if len(dictionaryMemberTypes) > 0:
+ raise TypeError("No support for unwrapping dictionaries as member "
+ "of a union")
+ else:
+ dictionaryObject = None
+
+ if callbackObject or dictionaryObject:
+ nonPlatformObject = CGList([callbackObject, dictionaryObject], "\n")
+ nonPlatformObject = CGWrapper(CGIndenter(nonPlatformObject),
+ pre="if (!IsPlatformObject(cx, &argObj)) {\n",
+ post="\n}")
+ else:
+ nonPlatformObject = None
+
+ objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
+ if len(objectMemberTypes) > 0:
+ object = CGGeneric("%s.SetToObject(&argObj);\n"
+ "done = true;" % unionArgumentObj)
+ else:
+ object = None
+
+ hasObjectTypes = interfaceObject or arrayObject or dateObject or nonPlatformObject or object
+ if hasObjectTypes:
+ # If we try more specific object types first then we need to check
+ # whether that succeeded before converting to object.
+ if object and (interfaceObject or arrayObject or dateObject or nonPlatformObject):
+ object = CGWrapper(CGIndenter(object), pre="if (!done) {\n",
+ post=("\n}"))
+
+ if arrayObject or dateObject or nonPlatformObject:
+ # An object can be both an array object and not a platform
+ # object, but we shouldn't have both in the union's members
+ # because they are not distinguishable.
+ assert not (arrayObject and nonPlatformObject)
+ templateBody = CGList([arrayObject, dateObject, nonPlatformObject], " else ")
+ else:
+ templateBody = None
+ if interfaceObject:
+ if templateBody:
+ templateBody = CGList([templateBody, object], "\n")
+ templateBody = CGWrapper(CGIndenter(templateBody),
+ pre="if (!done) {\n", post=("\n}"))
+ templateBody = CGList([interfaceObject, templateBody], "\n")
+ else:
+ templateBody = CGList([templateBody, object], "\n")
+
+ if any([arrayObject, dateObject, nonPlatformObject, object]):
+ templateBody.prepend(CGGeneric("JSObject& argObj = ${val}.toObject();"))
+ templateBody = CGWrapper(CGIndenter(templateBody),
+ pre="if (${val}.isObject()) {\n",
+ post="\n}")
+ else:
+ templateBody = CGGeneric()
+
+ otherMemberTypes = filter(lambda t: t.isString() or t.isEnum(),
+ memberTypes)
+ otherMemberTypes.extend(t for t in memberTypes if t.isPrimitive())
+ if len(otherMemberTypes) > 0:
+ assert len(otherMemberTypes) == 1
+ memberType = otherMemberTypes[0]
+ if memberType.isEnum():
+ name = memberType.inner.identifier.name
+ else:
+ name = memberType.name
+ other = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ names.append(name)
+ if hasObjectTypes:
+ other = CGWrapper(CGIndenter(other), "{\n", post="\n}")
+ if object:
+ join = " else "
+ else:
+ other = CGWrapper(other, pre="if (!done) ")
+ join = "\n"
+ templateBody = CGList([templateBody, other], join)
+ else:
+ other = None
+
+ templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
+ throw = CGGeneric("if (failed) {\n"
+ " return false;\n"
+ "}\n"
+ "if (!done) {\n"
+ " return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n"
+ "}" % ", ".join(names))
+ templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
+
+ typeName = type.name
+ argumentTypeName = typeName + "Argument"
+ if nullable:
+ typeName = "Nullable<" + typeName + " >"
+ if isOptional:
+ nonConstDecl = "const_cast<Optional<" + typeName + " >& >(${declName})"
+ else:
+ nonConstDecl = "const_cast<" + typeName + "& >(${declName})"
+ typeName = "const " + typeName
+
+ def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
+ null = CGGeneric("if (%s${val}.isNullOrUndefined()) {\n"
+ " %s.SetNull();\n"
+ "}" % (extraConditionForNull, setToNullVar))
+ templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}")
+ return CGList([null, templateBody], " else ")
+
+ if type.hasNullableType:
+ templateBody = handleNull(templateBody, unionArgumentObj)
+
+ declType = CGGeneric(typeName)
+ holderType = CGGeneric(argumentTypeName)
+ if isOptional:
+ mutableDecl = nonConstDecl + ".Value()"
+ declType = CGWrapper(declType, pre="const Optional<", post=" >")
+ holderType = CGWrapper(holderType, pre="Maybe<", post=" >")
+ constructDecl = CGGeneric(nonConstDecl + ".Construct();")
+ if nullable:
+ constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
+ else:
+ constructHolder = CGGeneric("${holderName}.construct(${declName}.Value());")
+ else:
+ mutableDecl = nonConstDecl
+ constructDecl = None
+ if nullable:
+ holderType = CGWrapper(holderType, pre="Maybe<", post=" >")
+ constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
+ else:
+ constructHolder = CGWrapper(holderType, post=" ${holderName}(${declName});")
+ holderType = None
+
+ templateBody = CGList([constructHolder, templateBody], "\n")
+ if nullable:
+ if defaultValue:
+ assert(isinstance(defaultValue, IDLNullValue))
+ valueMissing = "!(${haveValue}) || "
+ else:
+ valueMissing = ""
+ templateBody = handleNull(templateBody, mutableDecl,
+ extraConditionForNull=valueMissing)
+ templateBody = CGList([constructDecl, templateBody], "\n")
+
+ return templateBody.define(), declType, holderType, False
+
+ if type.isGeckoInterface():
+ assert not isEnforceRange and not isClamp
+
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+ # This is an interface that we implement as a concrete class
+ # or an XPCOM interface.
+
+ # Allow null pointers for nullable types and old-binding classes
+ argIsPointer = type.nullable() or type.unroll().inner.isExternal()
+
+ # Sequences and non-worker callbacks have to hold a strong ref to the
+ # thing being passed down.
+ forceOwningType = (descriptor.interface.isCallback() and
+ not descriptor.workers) or isMember
+
+ typeName = descriptor.nativeType
+ typePtr = typeName + "*"
+
+ # Compute a few things:
+ # - declType is the type we want to return as the first element of our
+ # tuple.
+ # - holderType is the type we want to return as the third element
+ # of our tuple.
+
+ # Set up some sensible defaults for these things insofar as we can.
+ holderType = None
+ if argIsPointer:
+ if forceOwningType:
+ declType = "nsRefPtr<" + typeName + ">"
+ else:
+ declType = typePtr
+ else:
+ if forceOwningType:
+ declType = "OwningNonNull<" + typeName + ">"
+ else:
+ declType = "NonNull<" + typeName + ">"
+
+ templateBody = ""
+ if descriptor.castable:
+ if descriptor.prefable:
+ raise TypeError("We don't support prefable castable object "
+ "arguments (like %s), because we don't know "
+ "how to handle them being preffed off" %
+ descriptor.interface.identifier.name)
+ if descriptor.interface.isConsequential():
+ raise TypeError("Consequential interface %s being used as an "
+ "argument but flagged as castable" %
+ descriptor.interface.identifier.name)
+ if failureCode is not None:
+ templateBody += str(CastableObjectUnwrapper(
+ descriptor,
+ "&${val}.toObject()",
+ "${declName}",
+ failureCode))
+ else:
+ templateBody += str(FailureFatalCastableObjectUnwrapper(
+ descriptor,
+ "&${val}.toObject()",
+ "${declName}"))
+ elif descriptor.interface.isCallback():
+ templateBody += str(CallbackObjectUnwrapper(
+ descriptor,
+ "&${val}.toObject()",
+ "${declName}",
+ codeOnFailure=failureCode))
+ elif descriptor.workers:
+ templateBody += "${declName} = &${val}.toObject();"
+ else:
+ # Either external, or new-binding non-castable. We always have a
+ # holder for these, because we don't actually know whether we have
+ # to addref when unwrapping or not. So we just pass an
+ # getter_AddRefs(nsRefPtr) to XPConnect and if we'll need a release
+ # it'll put a non-null pointer in there.
+ if forceOwningType:
+ # Don't return a holderType in this case; our declName
+ # will just own stuff.
+ templateBody += "nsRefPtr<" + typeName + "> ${holderName};\n"
+ else:
+ holderType = "nsRefPtr<" + typeName + ">"
+ templateBody += (
+ "jsval tmpVal = ${val};\n" +
+ typePtr + " tmp;\n"
+ "if (NS_FAILED(xpc_qsUnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), &tmpVal))) {\n")
+ templateBody += CGIndenter(onFailureBadType(failureCode,
+ descriptor.interface.identifier.name)).define()
+ templateBody += ("}\n"
+ "MOZ_ASSERT(tmp);\n")
+
+ if not isDefinitelyObject:
+ # Our tmpVal will go out of scope, so we can't rely on it
+ # for rooting
+ templateBody += (
+ "if (tmpVal != ${val} && !${holderName}) {\n"
+ " // We have to have a strong ref, because we got this off\n"
+ " // some random object that might get GCed\n"
+ " ${holderName} = tmp;\n"
+ "}\n")
+
+ # And store our tmp, before it goes out of scope.
+ templateBody += "${declName} = tmp;"
+
+ templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+ type, "${declName} = NULL",
+ failureCode)
+
+ declType = CGGeneric(declType)
+ if holderType is not None:
+ holderType = CGGeneric(holderType)
+ return (templateBody, declType, holderType, isOptional)
+
+ if type.isSpiderMonkeyInterface():
+ assert not isEnforceRange and not isClamp
+ if isMember:
+ raise TypeError("Can't handle member arraybuffers or "
+ "arraybuffer views because making sure all the "
+ "objects are properly rooted is hard")
+ name = type.name
+ # By default, we use a Maybe<> to hold our typed array. And in the optional
+ # non-nullable case we want to pass Optional<TypedArray> to consumers, not
+ # Optional<NonNull<TypedArray> >, so jump though some hoops to do that.
+ holderType = "Maybe<%s>" % name
+ constructLoc = "${holderName}"
+ constructMethod = "construct"
+ constructInternal = "ref"
+ if type.nullable():
+ if isOptional:
+ declType = "const Optional<" + name + "*>"
+ else:
+ declType = name + "*"
+ else:
+ if isOptional:
+ declType = "const Optional<" + name + ">"
+ # We don't need a holder in this case
+ holderType = None
+ constructLoc = "(const_cast<Optional<" + name + ">& >(${declName}))"
+ constructMethod = "Construct"
+ constructInternal = "Value"
+ else:
+ declType = "NonNull<" + name + ">"
+ template = (
+ "%s.%s(cx, &${val}.toObject());\n"
+ "if (!%s.%s().inited()) {\n"
+ "%s" # No newline here because onFailureBadType() handles that
+ "}\n" %
+ (constructLoc, constructMethod, constructLoc, constructInternal,
+ CGIndenter(onFailureBadType(failureCode, type.name)).define()))
+ nullableTarget = ""
+ if type.nullable():
+ if isOptional:
+ mutableDecl = "(const_cast<Optional<" + name + "*>& >(${declName}))"
+ template += "%s.Construct();\n" % mutableDecl
+ nullableTarget = "%s.Value()" % mutableDecl
+ else:
+ nullableTarget = "${declName}"
+ template += "%s = ${holderName}.addr();" % nullableTarget
+ elif not isOptional:
+ template += "${declName} = ${holderName}.addr();"
+ template = wrapObjectTemplate(template, isDefinitelyObject, type,
+ "%s = NULL" % nullableTarget,
+ failureCode)
+
+ if holderType is not None:
+ holderType = CGGeneric(holderType)
+ # We handle all the optional stuff ourselves; no need for caller to do it.
+ return (template, CGGeneric(declType), holderType, False)
+
+ if type.isString():
+ assert not isEnforceRange and not isClamp
+
+ treatAs = {
+ "Default": "eStringify",
+ "EmptyString": "eEmpty",
+ "Null": "eNull"
+ }
+ if type.nullable():
+ # For nullable strings null becomes a null string.
+ treatNullAs = "Null"
+ # For nullable strings undefined becomes a null string unless
+ # specified otherwise.
+ if treatUndefinedAs == "Default":
+ treatUndefinedAs = "Null"
+ nullBehavior = treatAs[treatNullAs]
+ if treatUndefinedAs == "Missing":
+ raise TypeError("We don't support [TreatUndefinedAs=Missing]")
+ undefinedBehavior = treatAs[treatUndefinedAs]
+
+ def getConversionCode(varName):
+ conversionCode = (
+ "if (!ConvertJSValueToString(cx, ${val}, ${valPtr}, %s, %s, %s)) {\n"
+ " return false;\n"
+ "}" % (nullBehavior, undefinedBehavior, varName))
+ if defaultValue is None:
+ return conversionCode
+
+ if isinstance(defaultValue, IDLNullValue):
+ assert(type.nullable())
+ return handleDefault(conversionCode,
+ "%s.SetNull()" % varName)
+ return handleDefault(
+ conversionCode,
+ ("static const PRUnichar data[] = { %s };\n"
+ "%s.SetData(data, ArrayLength(data) - 1)" %
+ (", ".join(["'" + char + "'" for char in defaultValue.value] + ["0"]),
+ varName)))
+
+ if isMember:
+ # We have to make a copy, because our jsval may well not
+ # live as long as our string needs to.
+ declType = CGGeneric("nsString")
+ return (
+ "{\n"
+ " FakeDependentString str;\n"
+ "%s\n"
+ " ${declName} = str;\n"
+ "}\n" % CGIndenter(CGGeneric(getConversionCode("str"))).define(),
+ declType, None, isOptional)
+
+ if isOptional:
+ declType = "Optional<nsAString>"
+ else:
+ declType = "NonNull<nsAString>"
+
+ return (
+ "%s\n"
+ "const_cast<%s&>(${declName}) = &${holderName};" %
+ (getConversionCode("${holderName}"), declType),
+ CGGeneric("const " + declType), CGGeneric("FakeDependentString"),
+ # No need to deal with Optional here; we have handled it already
+ False)
+
+ if type.isEnum():
+ assert not isEnforceRange and not isClamp
+
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated arguments "
+ "yet")
+ enum = type.inner.identifier.name
+ if invalidEnumValueFatal:
+ handleInvalidEnumValueCode = " MOZ_ASSERT(index >= 0);\n"
+ else:
+ handleInvalidEnumValueCode = (
+ " if (index < 0) {\n"
+ " return true;\n"
+ " }\n")
+
+ template = (
+ "{\n"
+ " bool ok;\n"
+ " int index = FindEnumStringIndex<%(invalidEnumValueFatal)s>(cx, ${val}, %(values)s, \"%(enumtype)s\", &ok);\n"
+ " if (!ok) {\n"
+ " return false;\n"
+ " }\n"
+ "%(handleInvalidEnumValueCode)s"
+ " ${declName} = static_cast<%(enumtype)s>(index);\n"
+ "}" % { "enumtype" : enum,
+ "values" : enum + "Values::strings",
+ "invalidEnumValueFatal" : toStringBool(invalidEnumValueFatal),
+ "handleInvalidEnumValueCode" : handleInvalidEnumValueCode })
+
+ if defaultValue is not None:
+ assert(defaultValue.type.tag() == IDLType.Tags.domstring)
+ template = handleDefault(template,
+ ("${declName} = %sValues::%s" %
+ (enum,
+ getEnumValueName(defaultValue.value))))
+ return (template, CGGeneric(enum), None, isOptional)
+
+ if type.isCallback():
+ assert not isEnforceRange and not isClamp
+
+ if isMember:
+ raise TypeError("Can't handle member callbacks; need to sort out "
+ "rooting issues")
+ # XXXbz we're going to assume that callback types are always
+ # nullable and always have [TreatNonCallableAsNull] for now.
+ haveCallable = "${val}.isObject() && JS_ObjectIsCallable(cx, &${val}.toObject())"
+ if defaultValue is not None:
+ assert(isinstance(defaultValue, IDLNullValue))
+ haveCallable = "${haveValue} && " + haveCallable
+ return (
+ "if (%s) {\n"
+ " ${declName} = &${val}.toObject();\n"
+ "} else {\n"
+ " ${declName} = NULL;\n"
+ "}" % haveCallable,
+ CGGeneric("JSObject*"), None, isOptional)
+
+ if type.isAny():
+ assert not isEnforceRange and not isClamp
+
+ if isMember:
+ raise TypeError("Can't handle member 'any'; need to sort out "
+ "rooting issues")
+ templateBody = "${declName} = ${val};"
+ templateBody = handleDefaultNull(templateBody,
+ "${declName} = JS::NullValue()")
+ return (templateBody, CGGeneric("JS::Value"), None, isOptional)
+
+ if type.isObject():
+ assert not isEnforceRange and not isClamp
+
+ if isMember:
+ raise TypeError("Can't handle member 'object'; need to sort out "
+ "rooting issues")
+ template = wrapObjectTemplate("${declName} = &${val}.toObject();",
+ isDefinitelyObject, type,
+ "${declName} = NULL",
+ failureCode)
+ if type.nullable():
+ declType = CGGeneric("JSObject*")
+ else:
+ declType = CGGeneric("NonNull<JSObject>")
+ return (template, declType, None, isOptional)
+
+ if type.isDictionary():
+ if failureCode is not None:
+ raise TypeError("Can't handle dictionaries when failureCode is not None")
+ # There are no nullable dictionaries
+ assert not type.nullable()
+ # All optional dictionaries always have default values, so we
+ # should be able to assume not isOptional here.
+ assert not isOptional
+
+ typeName = CGDictionary.makeDictionaryName(type.inner,
+ descriptorProvider.workers)
+ actualTypeName = typeName
+ selfRef = "${declName}"
+
+ declType = CGGeneric(actualTypeName)
+
+ # If we're a member of something else, the const
+ # will come from the Optional or our container.
+ if not isMember:
+ declType = CGWrapper(declType, pre="const ")
+ selfRef = "const_cast<%s&>(%s)" % (typeName, selfRef)
+
+ # We do manual default value handling here, because we
+ # actually do want a jsval, and we only handle null anyway
+ if defaultValue is not None:
+ assert(isinstance(defaultValue, IDLNullValue))
+ val = "(${haveValue}) ? ${val} : JSVAL_NULL"
+ else:
+ val = "${val}"
+
+ template = ("if (!%s.Init(cx, %s)) {\n"
+ " return false;\n"
+ "}" % (selfRef, val))
+
+ return (template, declType, None, False)
+
+ if not type.isPrimitive():
+ raise TypeError("Need conversion for argument type '%s'" % str(type))
+
+ typeName = builtinNames[type.tag()]
+
+ conversionBehavior = "eDefault"
+ if isEnforceRange:
+ conversionBehavior = "eEnforceRange"
+ elif isClamp:
+ conversionBehavior = "eClamp"
+
+ if type.nullable():
+ dataLoc = "${declName}.SetValue()"
+ nullCondition = "${val}.isNullOrUndefined()"
+ if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
+ nullCondition = "!(${haveValue}) || " + nullCondition
+ template = (
+ "if (%s) {\n"
+ " ${declName}.SetNull();\n"
+ "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
+ " return false;\n"
+ "}" % (nullCondition, typeName, conversionBehavior, dataLoc))
+ declType = CGGeneric("Nullable<" + typeName + ">")
+ else:
+ assert(defaultValue is None or
+ not isinstance(defaultValue, IDLNullValue))
+ dataLoc = "${declName}"
+ template = (
+ "if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
+ " return false;\n"
+ "}" % (typeName, conversionBehavior, dataLoc))
+ declType = CGGeneric(typeName)
+ if (defaultValue is not None and
+ # We already handled IDLNullValue, so just deal with the other ones
+ not isinstance(defaultValue, IDLNullValue)):
+ tag = defaultValue.type.tag()
+ if tag in numericTags:
+ defaultStr = defaultValue.value
+ else:
+ assert(tag == IDLType.Tags.bool)
+ defaultStr = toStringBool(defaultValue.value)
+ template = CGWrapper(CGIndenter(CGGeneric(template)),
+ pre="if (${haveValue}) {\n",
+ post=("\n"
+ "} else {\n"
+ " %s = %s;\n"
+ "}" % (dataLoc, defaultStr))).define()
+
+ return (template, declType, None, isOptional)
+
+def instantiateJSToNativeConversionTemplate(templateTuple, replacements,
+ argcAndIndex=None):
+ """
+ Take a tuple as returned by getJSToNativeConversionTemplate and a set of
+ replacements as required by the strings in such a tuple, and generate code
+ to convert into stack C++ types.
+
+ If argcAndIndex is not None it must be a dict that can be used to
+ replace ${argc} and ${index}, where ${index} is the index of this
+ argument (0-based) and ${argc} is the total number of arguments.
+ """
+ (templateBody, declType, holderType, dealWithOptional) = templateTuple
+
+ if dealWithOptional and argcAndIndex is None:
+ raise TypeError("Have to deal with optional things, but don't know how")
+ if argcAndIndex is not None and declType is None:
+ raise TypeError("Need to predeclare optional things, so they will be "
+ "outside the check for big enough arg count!");
+
+ result = CGList([], "\n")
+ # Make a copy of "replacements" since we may be about to start modifying it
+ replacements = dict(replacements)
+ originalHolderName = replacements["holderName"]
+ if holderType is not None:
+ if dealWithOptional:
+ replacements["holderName"] = (
+ "const_cast< %s & >(%s.Value())" %
+ (holderType.define(), originalHolderName))
+ mutableHolderType = CGWrapper(holderType, pre="Optional< ", post=" >")
+ holderType = CGWrapper(mutableHolderType, pre="const ")
+ result.append(
+ CGList([holderType, CGGeneric(" "),
+ CGGeneric(originalHolderName),
+ CGGeneric(";")]))
+
+ originalDeclName = replacements["declName"]
+ if declType is not None:
+ if dealWithOptional:
+ replacements["declName"] = (
+ "const_cast< %s & >(%s.Value())" %
+ (declType.define(), originalDeclName))
+ mutableDeclType = CGWrapper(declType, pre="Optional< ", post=" >")
+ declType = CGWrapper(mutableDeclType, pre="const ")
+ result.append(
+ CGList([declType, CGGeneric(" "),
+ CGGeneric(originalDeclName),
+ CGGeneric(";")]))
+
+ conversion = CGGeneric(
+ string.Template(templateBody).substitute(replacements)
+ )
+
+ if argcAndIndex is not None:
+ if dealWithOptional:
+ declConstruct = CGIndenter(
+ CGGeneric("const_cast< %s &>(%s).Construct();" %
+ (mutableDeclType.define(), originalDeclName)))
+ if holderType is not None:
+ holderConstruct = CGIndenter(
+ CGGeneric("const_cast< %s &>(%s).Construct();" %
+ (mutableHolderType.define(), originalHolderName)))
+ else:
+ holderConstruct = None
+ else:
+ declConstruct = None
+ holderConstruct = None
+
+ conversion = CGList(
+ [CGGeneric(
+ string.Template("if (${index} < ${argc}) {").substitute(
+ argcAndIndex
+ )),
+ declConstruct,
+ holderConstruct,
+ CGIndenter(conversion),
+ CGGeneric("}")],
+ "\n")
+
+ result.append(conversion)
+ # Add an empty CGGeneric to get an extra newline after the argument
+ # conversion.
+ result.append(CGGeneric(""))
+ return result;
+
+def convertConstIDLValueToJSVal(value):
+ if isinstance(value, IDLNullValue):
+ return "JSVAL_NULL"
+ tag = value.type.tag()
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
+ IDLType.Tags.uint16, IDLType.Tags.int32]:
+ return "INT_TO_JSVAL(%s)" % (value.value)
+ if tag == IDLType.Tags.uint32:
+ return "UINT_TO_JSVAL(%s)" % (value.value)
+ if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
+ return "DOUBLE_TO_JSVAL(%s)" % (value.value)
+ if tag == IDLType.Tags.bool:
+ return "JSVAL_TRUE" if value.value else "JSVAL_FALSE"
+ if tag in [IDLType.Tags.float, IDLType.Tags.double]:
+ return "DOUBLE_TO_JSVAL(%s)" % (value.value)
+ raise TypeError("Const value of unhandled type: " + value.type)
+
+class CGArgumentConverter(CGThing):
+ """
+ A class that takes an IDL argument object, its index in the
+ argument list, and the argv and argc strings and generates code to
+ unwrap the argument to the right native type.
+ """
+ def __init__(self, argument, index, argv, argc, descriptorProvider,
+ invalidEnumValueFatal=True):
+ CGThing.__init__(self)
+ self.argument = argument
+ if argument.variadic:
+ raise TypeError("We don't support variadic arguments yet " +
+ str(argument.location))
+ assert(not argument.defaultValue or argument.optional)
+
+ replacer = {
+ "index" : index,
+ "argc" : argc,
+ "argv" : argv
+ }
+ self.replacementVariables = {
+ "declName" : "arg%d" % index,
+ "holderName" : ("arg%d" % index) + "_holder"
+ }
+ self.replacementVariables["val"] = string.Template(
+ "${argv}[${index}]"
+ ).substitute(replacer)
+ self.replacementVariables["valPtr"] = (
+ "&" + self.replacementVariables["val"])
+ if argument.defaultValue:
+ self.replacementVariables["haveValue"] = string.Template(
+ "${index} < ${argc}").substitute(replacer)
+ self.descriptorProvider = descriptorProvider
+ if self.argument.optional and not self.argument.defaultValue:
+ self.argcAndIndex = replacer
+ else:
+ self.argcAndIndex = None
+ self.invalidEnumValueFatal = invalidEnumValueFatal
+
+ def define(self):
+ return instantiateJSToNativeConversionTemplate(
+ getJSToNativeConversionTemplate(self.argument.type,
+ self.descriptorProvider,
+ isOptional=(self.argcAndIndex is not None),
+ invalidEnumValueFatal=self.invalidEnumValueFatal,
+ defaultValue=self.argument.defaultValue,
+ treatNullAs=self.argument.treatNullAs,
+ treatUndefinedAs=self.argument.treatUndefinedAs,
+ isEnforceRange=self.argument.enforceRange,
+ isClamp=self.argument.clamp),
+ self.replacementVariables,
+ self.argcAndIndex).define()
+
+def getWrapTemplateForType(type, descriptorProvider, result, successCode,
+ isCreator):
+ """
+ Reflect a C++ value stored in "result", of IDL type "type" into JS. The
+ "successCode" is the code to run once we have successfully done the
+ conversion. The resulting string should be used with string.Template, it
+ needs the following keys when substituting: jsvalPtr/jsvalRef/obj.
+
+ Returns (templateString, infallibility of conversion template)
+ """
+ haveSuccessCode = successCode is not None
+ if not haveSuccessCode:
+ successCode = "return true;"
+
+ def setValue(value, callWrapValue=False):
+ """
+ Returns the code to set the jsval to value. If "callWrapValue" is true
+ JS_WrapValue will be called on the jsval.
+ """
+ if not callWrapValue:
+ tail = successCode
+ elif haveSuccessCode:
+ tail = ("if (!JS_WrapValue(cx, ${jsvalPtr})) {\n" +
+ " return false;\n" +
+ "}\n" +
+ successCode)
+ else:
+ tail = "return JS_WrapValue(cx, ${jsvalPtr});"
+ return ("${jsvalRef} = %s;\n" +
+ tail) % (value)
+
+ def wrapAndSetPtr(wrapCall, failureCode=None):
+ """
+ Returns the code to set the jsval by calling "wrapCall". "failureCode"
+ is the code to run if calling "wrapCall" fails
+ """
+ if failureCode is None:
+ if not haveSuccessCode:
+ return "return " + wrapCall + ";"
+ failureCode = "return false;"
+ str = ("if (!%s) {\n" +
+ CGIndenter(CGGeneric(failureCode)).define() + "\n" +
+ "}\n" +
+ successCode) % (wrapCall)
+ return str
+
+ if type is None or type.isVoid():
+ return (setValue("JSVAL_VOID"), True)
+
+ if type.isArray():
+ raise TypeError("Can't handle array return values yet")
+
+ if type.isSequence():
+ if type.nullable():
+ # Nullable sequences are Nullable< nsTArray<T> >
+ (recTemplate, recInfall) = getWrapTemplateForType(type.inner, descriptorProvider,
+ "%s.Value()" % result, successCode,
+ isCreator)
+ return ("""
+if (%s.IsNull()) {
+%s
+}
+%s""" % (result, CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define(), recTemplate), recInfall)
+
+ # Now do non-nullable sequences. We use setting the element
+ # in the array as our succcess code because when we succeed in
+ # wrapping that's what we should do.
+ innerTemplate = wrapForType(
+ type.inner, descriptorProvider,
+ {
+ 'result' : "%s[i]" % result,
+ 'successCode': ("if (!JS_DefineElement(cx, returnArray, i, tmp,\n"
+ " NULL, NULL, JSPROP_ENUMERATE)) {\n"
+ " return false;\n"
+ "}"),
+ 'jsvalRef': "tmp",
+ 'jsvalPtr': "&tmp",
+ 'isCreator': isCreator
+ }
+ )
+ innerTemplate = CGIndenter(CGGeneric(innerTemplate)).define()
+ return (("""
+uint32_t length = %s.Length();
+JSObject *returnArray = JS_NewArrayObject(cx, length, NULL);
+if (!returnArray) {
+ return false;
+}
+jsval tmp;
+for (uint32_t i = 0; i < length; ++i) {
+%s
+}\n""" % (result, innerTemplate)) + setValue("JS::ObjectValue(*returnArray)"), False)
+
+ if type.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
+ if type.nullable():
+ wrappingCode = ("if (!%s) {\n" % (result) +
+ CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
+ "}\n")
+ else:
+ wrappingCode = ""
+ if (not descriptor.interface.isExternal() and
+ not descriptor.interface.isCallback()):
+ if descriptor.wrapperCache:
+ wrapMethod = "WrapNewBindingObject"
+ else:
+ if not isCreator:
+ raise MethodNotCreatorError(descriptor.interface.identifier.name)
+ wrapMethod = "WrapNewBindingNonWrapperCachedObject"
+ wrap = "%s(cx, ${obj}, %s, ${jsvalPtr})" % (wrapMethod, result)
+ # We don't support prefable stuff in workers.
+ assert(not descriptor.prefable or not descriptor.workers)
+ if not descriptor.prefable:
+ # Non-prefable bindings can only fail to wrap as a new-binding object
+ # if they already threw an exception. Same thing for
+ # non-prefable bindings.
+ failed = ("MOZ_ASSERT(JS_IsExceptionPending(cx));\n" +
+ "return false;")
+ else:
+ if descriptor.notflattened:
+ raise TypeError("%s is prefable but not flattened; "
+ "fallback won't work correctly" %
+ descriptor.interface.identifier.name)
+ # Try old-style wrapping for bindings which might be preffed off.
+ failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalPtr})" % result)
+ wrappingCode += wrapAndSetPtr(wrap, failed)
+ else:
+ if descriptor.notflattened:
+ getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
+ else:
+ getIID = ""
+ wrap = "WrapObject(cx, ${obj}, %s, %s${jsvalPtr})" % (result, getIID)
+ wrappingCode += wrapAndSetPtr(wrap)
+ return (wrappingCode, False)
+
+ if type.isString():
+ if type.nullable():
+ return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalPtr})" % result), False)
+ else:
+ return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalPtr})" % result), False)
+
+ if type.isEnum():
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated return types "
+ "yet")
+ return ("""MOZ_ASSERT(uint32_t(%(result)s) < ArrayLength(%(strings)s));
+JSString* %(resultStr)s = JS_NewStringCopyN(cx, %(strings)s[uint32_t(%(result)s)].value, %(strings)s[uint32_t(%(result)s)].length);
+if (!%(resultStr)s) {
+ return false;
+}
+""" % { "result" : result,
+ "resultStr" : result + "_str",
+ "strings" : type.inner.identifier.name + "Values::strings" } +
+ setValue("JS::StringValue(%s_str)" % result), False)
+
+ if type.isCallback():
+ assert not type.isInterface()
+ # XXXbz we're going to assume that callback types are always
+ # nullable and always have [TreatNonCallableAsNull] for now.
+ # See comments in WrapNewBindingObject explaining why we need
+ # to wrap here.
+ # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+ return (setValue("JS::ObjectOrNullValue(%s)" % result, True), False)
+
+ if type.tag() == IDLType.Tags.any:
+ # See comments in WrapNewBindingObject explaining why we need
+ # to wrap here.
+ # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+ return (setValue(result, True), False)
+
+ if type.isObject() or type.isSpiderMonkeyInterface():
+ # See comments in WrapNewBindingObject explaining why we need
+ # to wrap here.
+ if type.nullable():
+ toValue = "JS::ObjectOrNullValue(%s)"
+ else:
+ toValue = "JS::ObjectValue(*%s)"
+ # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+ return (setValue(toValue % result, True), False)
+
+ if not type.isPrimitive():
+ raise TypeError("Need to learn to wrap %s" % type)
+
+ if type.nullable():
+ (recTemplate, recInfal) = getWrapTemplateForType(type.inner, descriptorProvider,
+ "%s.Value()" % result, successCode,
+ isCreator)
+ return ("if (%s.IsNull()) {\n" % result +
+ CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
+ "}\n" + recTemplate, recInfal)
+
+ tag = type.tag()
+
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
+ IDLType.Tags.uint16, IDLType.Tags.int32]:
+ return (setValue("INT_TO_JSVAL(int32_t(%s))" % result), True)
+
+ elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64, IDLType.Tags.float,
+ IDLType.Tags.double]:
+ # XXXbz will cast to double do the "even significand" thing that webidl
+ # calls for for 64-bit ints? Do we care?
+ return (setValue("JS_NumberValue(double(%s))" % result), True)
+
+ elif tag == IDLType.Tags.uint32:
+ return (setValue("UINT_TO_JSVAL(%s)" % result), True)
+
+ elif tag == IDLType.Tags.bool:
+ return (setValue("BOOLEAN_TO_JSVAL(%s)" % result), True)
+
+ else:
+ raise TypeError("Need to learn to wrap primitive: %s" % type)
+
+def wrapForType(type, descriptorProvider, templateValues):
+ """
+ Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict
+ that should contain:
+
+ * 'jsvalRef': a C++ reference to the jsval in which to store the result of
+ the conversion
+ * 'jsvalPtr': a C++ pointer to the jsval in which to store the result of
+ the conversion
+ * 'obj' (optional): the name of the variable that contains the JSObject to
+ use as a scope when wrapping, if not supplied 'obj'
+ will be used as the name
+ * 'result' (optional): the name of the variable in which the C++ value is
+ stored, if not supplied 'result' will be used as
+ the name
+ * 'successCode' (optional): the code to run once we have successfully done
+ the conversion, if not supplied 'return true;'
+ will be used as the code
+ * 'isCreator' (optional): If true, we're wrapping for the return value of
+ a [Creator] method. Assumed false if not set.
+ """
+ wrap = getWrapTemplateForType(type, descriptorProvider,
+ templateValues.get('result', 'result'),
+ templateValues.get('successCode', None),
+ templateValues.get('isCreator', False))[0]
+
+ defaultValues = {'obj': 'obj'}
+ return string.Template(wrap).substitute(defaultValues, **templateValues)
+
+def infallibleForMember(member, type, descriptorProvider):
+ """
+ Determine the fallibility of changing a C++ value of IDL type "type" into
+ JS for the given attribute. Apart from isCreator, all the defaults are used,
+ since the fallbility does not change based on the boolean values,
+ and the template will be discarded.
+
+ CURRENT ASSUMPTIONS:
+ We assume that successCode for wrapping up return values cannot contain
+ failure conditions.
+ """
+ return getWrapTemplateForType(type, descriptorProvider, 'result', None,\
+ memberIsCreator(member))[1]
+
+def typeNeedsCx(type, retVal=False):
+ if type is None:
+ return False
+ if type.nullable():
+ type = type.inner
+ if type.isSequence() or type.isArray():
+ type = type.inner
+ if type.isUnion():
+ return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
+ if retVal and type.isSpiderMonkeyInterface():
+ return True
+ return type.isCallback() or type.isAny() or type.isObject()
+
+# Returns a tuple consisting of a CGThing containing the type of the return
+# value, or None if there is no need for a return value, and a boolean signaling
+# whether the return value is passed in an out parameter.
+def getRetvalDeclarationForType(returnType, descriptorProvider,
+ resultAlreadyAddRefed):
+ if returnType is None or returnType.isVoid():
+ # Nothing to declare
+ return None, False
+ if returnType.isPrimitive() and returnType.tag() in builtinNames:
+ result = CGGeneric(builtinNames[returnType.tag()])
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Nullable<", post=">")
+ return result, False
+ if returnType.isString():
+ return CGGeneric("nsString"), True
+ if returnType.isEnum():
+ if returnType.nullable():
+ raise TypeError("We don't support nullable enum return values")
+ return CGGeneric(returnType.inner.identifier.name), False
+ if returnType.isGeckoInterface():
+ result = CGGeneric(descriptorProvider.getDescriptor(
+ returnType.unroll().inner.identifier.name).nativeType)
+ if resultAlreadyAddRefed:
+ result = CGWrapper(result, pre="nsRefPtr<", post=">")
+ else:
+ result = CGWrapper(result, post="*")
+ return result, False
+ if returnType.isCallback():
+ # XXXbz we're going to assume that callback types are always
+ # nullable for now.
+ return CGGeneric("JSObject*"), False
+ if returnType.isAny():
+ return CGGeneric("JS::Value"), False
+ if returnType.isObject() or returnType.isSpiderMonkeyInterface():
+ return CGGeneric("JSObject*"), False
+ if returnType.isSequence():
+ nullable = returnType.nullable()
+ if nullable:
+ returnType = returnType.inner
+ # If our result is already addrefed, use the right type in the
+ # sequence argument here.
+ (result, _) = getRetvalDeclarationForType(returnType.inner,
+ descriptorProvider,
+ resultAlreadyAddRefed)
+ result = CGWrapper(result, pre="nsTArray< ", post=" >")
+ if nullable:
+ result = CGWrapper(result, pre="Nullable< ", post=" >")
+ return result, True
+ raise TypeError("Don't know how to declare return value for %s" %
+ returnType)
+
+def isResultAlreadyAddRefed(descriptor, extendedAttributes):
+ # Default to already_AddRefed on the main thread, raw pointer in workers
+ return not descriptor.workers and not 'resultNotAddRefed' in extendedAttributes
+
+class CGCallGenerator(CGThing):
+ """
+ A class to generate an actual call to a C++ object. Assumes that the C++
+ object is stored in a variable whose name is given by the |object| argument.
+
+ errorReport should be a CGThing for an error report or None if no
+ error reporting is needed.
+ """
+ def __init__(self, errorReport, arguments, argsPre, returnType,
+ extendedAttributes, descriptorProvider, nativeMethodName,
+ static, object="self", declareResult=True):
+ CGThing.__init__(self)
+
+ assert errorReport is None or isinstance(errorReport, CGThing)
+
+ isFallible = errorReport is not None
+
+ resultAlreadyAddRefed = isResultAlreadyAddRefed(descriptorProvider,
+ extendedAttributes)
+ (result, resultOutParam) = getRetvalDeclarationForType(returnType,
+ descriptorProvider,
+ resultAlreadyAddRefed)
+
+ args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
+ for (a, name) in arguments:
+ # This is a workaround for a bug in Apple's clang.
+ if a.type.isObject() and not a.type.nullable() and not a.optional:
+ name = "(JSObject&)" + name
+ args.append(CGGeneric(name))
+
+ # Return values that go in outparams go here
+ if resultOutParam:
+ args.append(CGGeneric("result"))
+ if isFallible:
+ args.append(CGGeneric("rv"))
+
+ needsCx = (typeNeedsCx(returnType, True) or
+ any(typeNeedsCx(a.type) for (a, _) in arguments) or
+ 'implicitJSContext' in extendedAttributes)
+
+ if not "cx" in argsPre and needsCx:
+ args.prepend(CGGeneric("cx"))
+
+ # Build up our actual call
+ self.cgRoot = CGList([], "\n")
+
+ call = CGGeneric(nativeMethodName)
+ if static:
+ call = CGWrapper(call, pre="%s::" % descriptorProvider.nativeType)
+ else:
+ call = CGWrapper(call, pre="%s->" % object)
+ call = CGList([call, CGWrapper(args, pre="(", post=");")])
+ if result is not None:
+ if declareResult:
+ result = CGWrapper(result, post=" result;")
+ self.cgRoot.prepend(result)
+ if not resultOutParam:
+ call = CGWrapper(call, pre="result = ")
+
+ call = CGWrapper(call)
+ self.cgRoot.append(call)
+
+ if isFallible:
+ self.cgRoot.prepend(CGGeneric("ErrorResult rv;"))
+ self.cgRoot.append(CGGeneric("if (rv.Failed()) {"))
+ self.cgRoot.append(CGIndenter(errorReport))
+ self.cgRoot.append(CGGeneric("}"))
+
+ def define(self):
+ return self.cgRoot.define()
+
+class MethodNotCreatorError(Exception):
+ def __init__(self, typename):
+ self.typename = typename
+
+class CGPerSignatureCall(CGThing):
+ """
+ This class handles the guts of generating code for a particular
+ call signature. A call signature consists of four things:
+
+ 1) A return type, which can be None to indicate that there is no
+ actual return value (e.g. this is an attribute setter) or an
+ IDLType if there's an IDL type involved (including |void|).
+ 2) An argument list, which is allowed to be empty.
+ 3) A name of a native method to call.
+ 4) Whether or not this method is static.
+
+ We also need to know whether this is a method or a getter/setter
+ to do error reporting correctly.
+
+ The idlNode parameter can be either a method or an attr. We can query
+ |idlNode.identifier| in both cases, so we can be agnostic between the two.
+ """
+ # XXXbz For now each entry in the argument list is either an
+ # IDLArgument or a FakeArgument, but longer-term we may want to
+ # have ways of flagging things like JSContext* or optional_argc in
+ # there.
+
+ def __init__(self, returnType, argsPre, arguments, nativeMethodName, static,
+ descriptor, idlNode, argConversionStartsAt=0,
+ getter=False, setter=False):
+ CGThing.__init__(self)
+ self.returnType = returnType
+ self.descriptor = descriptor
+ self.idlNode = idlNode
+ self.extendedAttributes = descriptor.getExtendedAttributes(idlNode,
+ getter=getter,
+ setter=setter)
+ self.argsPre = argsPre
+ self.arguments = arguments
+ self.argCount = len(arguments)
+ if self.argCount > argConversionStartsAt:
+ # Insert our argv in there
+ cgThings = [CGGeneric(self.getArgvDecl())]
+ else:
+ cgThings = []
+ cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgv(),
+ self.getArgc(), self.descriptor,
+ invalidEnumValueFatal=not setter) for
+ i in range(argConversionStartsAt, self.argCount)])
+
+ cgThings.append(CGCallGenerator(
+ self.getErrorReport() if self.isFallible() else None,
+ self.getArguments(), self.argsPre, returnType,
+ self.extendedAttributes, descriptor, nativeMethodName,
+ static))
+ self.cgRoot = CGList(cgThings, "\n")
+
+ def getArgv(self):
+ return "argv" if self.argCount > 0 else ""
+ def getArgvDecl(self):
+ return "\nJS::Value* argv = JS_ARGV(cx, vp);\n"
+ def getArgc(self):
+ return "argc"
+ def getArguments(self):
+ return [(a, "arg" + str(i)) for (i, a) in enumerate(self.arguments)]
+
+ def isFallible(self):
+ return not 'infallible' in self.extendedAttributes
+
+ def wrap_return_value(self):
+ isCreator = memberIsCreator(self.idlNode)
+ if isCreator:
+ # We better be returning addrefed things!
+ assert(isResultAlreadyAddRefed(self.descriptor,
+ self.extendedAttributes) or
+ # Workers use raw pointers for new-object return
+ # values or something
+ self.descriptor.workers)
+
+ resultTemplateValues = { 'jsvalRef': '*vp', 'jsvalPtr': 'vp',
+ 'isCreator': isCreator}
+ try:
+ return wrapForType(self.returnType, self.descriptor,
+ resultTemplateValues)
+ except MethodNotCreatorError, err:
+ assert not isCreator
+ raise TypeError("%s being returned from non-creator method or property %s.%s" %
+ (err.typename,
+ self.descriptor.interface.identifier.name,
+ self.idlNode.identifier.name))
+
+ def getErrorReport(self):
+ return CGGeneric('return ThrowMethodFailedWithDetails<%s>(cx, rv, "%s", "%s");'
+ % (toStringBool(not self.descriptor.workers),
+ self.descriptor.interface.identifier.name,
+ self.idlNode.identifier.name))
+
+ def define(self):
+ return (self.cgRoot.define() + "\n" + self.wrap_return_value())
+
+class CGSwitch(CGList):
+ """
+ A class to generate code for a switch statement.
+
+ Takes three constructor arguments: an expression, a list of cases,
+ and an optional default.
+
+ Each case is a CGCase. The default is a CGThing for the body of
+ the default case, if any.
+ """
+ def __init__(self, expression, cases, default=None):
+ CGList.__init__(self, [CGIndenter(c) for c in cases], "\n")
+ self.prepend(CGWrapper(CGGeneric(expression),
+ pre="switch (", post=") {"));
+ if default is not None:
+ self.append(
+ CGIndenter(
+ CGWrapper(
+ CGIndenter(default),
+ pre="default: {\n",
+ post="\n break;\n}"
+ )
+ )
+ )
+
+ self.append(CGGeneric("}"))
+
+class CGCase(CGList):
+ """
+ A class to generate code for a case statement.
+
+ Takes three constructor arguments: an expression, a CGThing for
+ the body (allowed to be None if there is no body), and an optional
+ argument (defaulting to False) for whether to fall through.
+ """
+ def __init__(self, expression, body, fallThrough=False):
+ CGList.__init__(self, [], "\n")
+ self.append(CGWrapper(CGGeneric(expression), pre="case ", post=": {"))
+ bodyList = CGList([body], "\n")
+ if fallThrough:
+ bodyList.append(CGGeneric("/* Fall through */"))
+ else:
+ bodyList.append(CGGeneric("break;"))
+ self.append(CGIndenter(bodyList));
+ self.append(CGGeneric("}"))
+
+class CGMethodCall(CGThing):
+ """
+ A class to generate selection of a method signature from a set of
+ signatures and generation of a call to that signature.
+ """
+ def __init__(self, argsPre, nativeMethodName, static, descriptor, method):
+ CGThing.__init__(self)
+
+ methodName = '"%s.%s"' % (descriptor.interface.identifier.name, method.identifier.name)
+
+ def requiredArgCount(signature):
+ arguments = signature[1]
+ if len(arguments) == 0:
+ return 0
+ requiredArgs = len(arguments)
+ while requiredArgs and arguments[requiredArgs-1].optional:
+ requiredArgs -= 1
+ return requiredArgs
+
+ def getPerSignatureCall(signature, argConversionStartsAt=0):
+ return CGPerSignatureCall(signature[0], argsPre, signature[1],
+ nativeMethodName, static, descriptor,
+ method, argConversionStartsAt)
+
+
+ signatures = method.signatures()
+ if len(signatures) == 1:
+ # Special case: we can just do a per-signature method call
+ # here for our one signature and not worry about switching
+ # on anything.
+ signature = signatures[0]
+ self.cgRoot = CGList([ CGIndenter(getPerSignatureCall(signature)) ])
+ requiredArgs = requiredArgCount(signature)
+
+
+ if requiredArgs > 0:
+ code = (
+ "if (argc < %d) {\n"
+ " return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, %s);\n"
+ "}" % (requiredArgs, methodName))
+ self.cgRoot.prepend(
+ CGWrapper(CGIndenter(CGGeneric(code)), pre="\n", post="\n"))
+ return
+
+ # Need to find the right overload
+ maxArgCount = method.maxArgCount
+ allowedArgCounts = method.allowedArgCounts
+
+ argCountCases = []
+ for argCount in allowedArgCounts:
+ possibleSignatures = method.signaturesForArgCount(argCount)
+ if len(possibleSignatures) == 1:
+ # easy case!
+ signature = possibleSignatures[0]
+
+ # (possibly) important optimization: if signature[1] has >
+ # argCount arguments and signature[1][argCount] is optional and
+ # there is only one signature for argCount+1, then the
+ # signature for argCount+1 is just ourselves and we can fall
+ # through.
+ if (len(signature[1]) > argCount and
+ signature[1][argCount].optional and
+ (argCount+1) in allowedArgCounts and
+ len(method.signaturesForArgCount(argCount+1)) == 1):
+ argCountCases.append(
+ CGCase(str(argCount), None, True))
+ else:
+ argCountCases.append(
+ CGCase(str(argCount), getPerSignatureCall(signature)))
+ continue
+
+ distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
+
+ # We can't handle unions at the distinguishing index.
+ for (returnType, args) in possibleSignatures:
+ if args[distinguishingIndex].type.isUnion():
+ raise TypeError("No support for unions as distinguishing "
+ "arguments yet: %s",
+ args[distinguishingIndex].location)
+
+ # Convert all our arguments up to the distinguishing index.
+ # Doesn't matter which of the possible signatures we use, since
+ # they all have the same types up to that point; just use
+ # possibleSignatures[0]
+ caseBody = [CGGeneric("JS::Value* argv_start = JS_ARGV(cx, vp);")]
+ caseBody.extend([ CGArgumentConverter(possibleSignatures[0][1][i],
+ i, "argv_start", "argc",
+ descriptor) for i in
+ range(0, distinguishingIndex) ])
+
+ # Select the right overload from our set.
+ distinguishingArg = "argv_start[%d]" % distinguishingIndex
+
+ def pickFirstSignature(condition, filterLambda):
+ sigs = filter(filterLambda, possibleSignatures)
+ assert len(sigs) < 2
+ if len(sigs) > 0:
+ if condition is None:
+ caseBody.append(
+ getPerSignatureCall(sigs[0], distinguishingIndex))
+ else:
+ caseBody.append(CGGeneric("if (" + condition + ") {"))
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sigs[0], distinguishingIndex)))
+ caseBody.append(CGGeneric("}"))
+ return True
+ return False
+
+ # First check for null or undefined
+ pickFirstSignature("%s.isNullOrUndefined()" % distinguishingArg,
+ lambda s: (s[1][distinguishingIndex].type.nullable() or
+ s[1][distinguishingIndex].type.isDictionary()))
+
+ # Now check for distinguishingArg being an object that implements a
+ # non-callback interface. That includes typed arrays and
+ # arraybuffers.
+ interfacesSigs = [
+ s for s in possibleSignatures
+ if (s[1][distinguishingIndex].type.isObject() or
+ s[1][distinguishingIndex].type.isNonCallbackInterface()) ]
+ # There might be more than one of these; we need to check
+ # which ones we unwrap to.
+
+ if len(interfacesSigs) > 0:
+ # The spec says that we should check for "platform objects
+ # implementing an interface", but it's enough to guard on these
+ # being an object. The code for unwrapping non-callback
+ # interfaces and typed arrays will just bail out and move on to
+ # the next overload if the object fails to unwrap correctly. We
+ # could even not do the isObject() check up front here, but in
+ # cases where we have multiple object overloads it makes sense
+ # to do it only once instead of for each overload. That will
+ # also allow the unwrapping test to skip having to do codegen
+ # for the null-or-undefined case, which we already handled
+ # above.
+ caseBody.append(CGGeneric("if (%s.isObject()) {" %
+ (distinguishingArg)))
+ for sig in interfacesSigs:
+ caseBody.append(CGIndenter(CGGeneric("do {")));
+ type = sig[1][distinguishingIndex].type
+
+ # The argument at index distinguishingIndex can't possibly
+ # be unset here, because we've already checked that argc is
+ # large enough that we can examine this argument.
+ testCode = instantiateJSToNativeConversionTemplate(
+ getJSToNativeConversionTemplate(type, descriptor,
+ failureCode="break;",
+ isDefinitelyObject=True),
+ {
+ "declName" : "arg%d" % distinguishingIndex,
+ "holderName" : ("arg%d" % distinguishingIndex) + "_holder",
+ "val" : distinguishingArg
+ })
+
+ # Indent by 4, since we need to indent further than our "do" statement
+ caseBody.append(CGIndenter(testCode, 4));
+ # If we got this far, we know we unwrapped to the right
+ # interface, so just do the call. Start conversion with
+ # distinguishingIndex + 1, since we already converted
+ # distinguishingIndex.
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sig, distinguishingIndex + 1), 4))
+ caseBody.append(CGIndenter(CGGeneric("} while (0);")))
+
+ caseBody.append(CGGeneric("}"))
+
+ # XXXbz Now we're supposed to check for distinguishingArg being
+ # an array or a platform object that supports indexed
+ # properties... skip that last for now. It's a bit of a pain.
+ pickFirstSignature("%s.isObject() && IsArrayLike(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s:
+ (s[1][distinguishingIndex].type.isArray() or
+ s[1][distinguishingIndex].type.isSequence() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # Check for Date objects
+ # XXXbz Do we need to worry about security wrappers around the Date?
+ pickFirstSignature("%s.isObject() && JS_ObjectIsDate(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s: (s[1][distinguishingIndex].type.isDate() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # Check for vanilla JS objects
+ # XXXbz Do we need to worry about security wrappers?
+ pickFirstSignature("%s.isObject() && !IsPlatformObject(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s: (s[1][distinguishingIndex].type.isCallback() or
+ s[1][distinguishingIndex].type.isCallbackInterface() or
+ s[1][distinguishingIndex].type.isDictionary() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # The remaining cases are mutually exclusive. The
+ # pickFirstSignature calls are what change caseBody
+ # Check for strings or enums
+ if pickFirstSignature(None,
+ lambda s: (s[1][distinguishingIndex].type.isString() or
+ s[1][distinguishingIndex].type.isEnum())):
+ pass
+ # Check for primitives
+ elif pickFirstSignature(None,
+ lambda s: s[1][distinguishingIndex].type.isPrimitive()):
+ pass
+ # Check for "any"
+ elif pickFirstSignature(None,
+ lambda s: s[1][distinguishingIndex].type.isAny()):
+ pass
+ else:
+ # Just throw; we have no idea what we're supposed to
+ # do with this.
+ caseBody.append(CGGeneric("return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);" %
+ toStringBool(not descriptor.workers)))
+
+ argCountCases.append(CGCase(str(argCount),
+ CGList(caseBody, "\n")))
+
+ overloadCGThings = []
+ overloadCGThings.append(
+ CGGeneric("unsigned argcount = NS_MIN(argc, %du);" %
+ maxArgCount))
+ overloadCGThings.append(
+ CGSwitch("argcount",
+ argCountCases,
+ CGGeneric("return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, %s);\n" % methodName)))
+ overloadCGThings.append(
+ CGGeneric('MOZ_NOT_REACHED("We have an always-returning default case");\n'
+ 'return false;'))
+ self.cgRoot = CGWrapper(CGIndenter(CGList(overloadCGThings, "\n")),
+ pre="\n")
+
+ def define(self):
+ return self.cgRoot.define()
+
+class CGGetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object getter call for a particular IDL
+ getter.
+ """
+ def __init__(self, returnType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, returnType, [], [],
+ nativeMethodName, False, descriptor,
+ attr, getter=True)
+
+class FakeArgument():
+ """
+ A class that quacks like an IDLArgument. This is used to make
+ setters look like method calls or for special operations.
+ """
+ def __init__(self, type, interfaceMember):
+ self.type = type
+ self.optional = False
+ self.variadic = False
+ self.defaultValue = None
+ self.treatNullAs = interfaceMember.treatNullAs
+ self.treatUndefinedAs = interfaceMember.treatUndefinedAs
+ self.enforceRange = False
+ self.clamp = False
+
+class CGSetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object setter call for a particular IDL
+ setter.
+ """
+ def __init__(self, argType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, None, [],
+ [FakeArgument(argType, attr)],
+ nativeMethodName, False, descriptor, attr,
+ setter=True)
+ def wrap_return_value(self):
+ # We have no return value
+ return "\nreturn true;"
+ def getArgc(self):
+ return "1"
+ def getArgvDecl(self):
+ # We just get our stuff from our last arg no matter what
+ return ""
+
+class FakeCastableDescriptor():
+ def __init__(self, descriptor):
+ self.castable = True
+ self.workers = descriptor.workers
+ self.nativeType = descriptor.nativeType
+ self.name = descriptor.name
+ self.hasXPConnectImpls = descriptor.hasXPConnectImpls
+
+class CGAbstractBindingMethod(CGAbstractStaticMethod):
+ """
+ Common class to generate the JSNatives for all our methods, getters, and
+ setters. This will generate the function declaration and unwrap the
+ |this| object. Subclasses are expected to override the generate_code
+ function to do the rest of the work. This function should return a
+ CGThing which is already properly indented.
+ """
+ def __init__(self, descriptor, name, args, unwrapFailureCode=None):
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ if unwrapFailureCode is None:
+ self.unwrapFailureCode = ("return Throw<%s>(cx, rv);" %
+ toStringBool(not descriptor.workers))
+ else:
+ self.unwrapFailureCode = unwrapFailureCode
+
+ def definition_body(self):
+ # Our descriptor might claim that we're not castable, simply because
+ # we're someone's consequential interface. But for this-unwrapping, we
+ # know that we're the real deal. So fake a descriptor here for
+ # consumption by FailureFatalCastableObjectUnwrapper.
+ unwrapThis = CGIndenter(CGGeneric(
+ str(CastableObjectUnwrapper(
+ FakeCastableDescriptor(self.descriptor),
+ "obj", "self", self.unwrapFailureCode))))
+ return CGList([ self.getThis(), unwrapThis,
+ self.generate_code() ], "\n").define()
+
+ def getThis(self):
+ return CGIndenter(
+ CGGeneric("js::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));\n"
+ "if (!obj) {\n"
+ " return false;\n"
+ "}\n"
+ "\n"
+ "%s* self;" % self.descriptor.nativeType))
+
+ def generate_code(self):
+ assert(False) # Override me
+
+def MakeNativeName(name):
+ return name[0].upper() + name[1:]
+
+class CGGenericMethod(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL method..
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
+ Argument('JS::Value*', 'vp')]
+ CGAbstractBindingMethod.__init__(self, descriptor, 'genericMethod', args)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(
+ "const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "JSJitMethodOp method = (JSJitMethodOp)info->op;\n"
+ "return method(cx, obj, self, argc, vp);"))
+
+class CGSpecializedMethod(CGAbstractStaticMethod):
+ """
+ A class for generating the C++ code for a specialized method that the JIT
+ can call with lower overhead.
+ """
+ def __init__(self, descriptor, method):
+ self.method = method
+ name = method.identifier.name
+ args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
+ CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
+
+ def definition_body(self):
+ name = self.method.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ return CGMethodCall([], nativeName, self.method.isStatic(),
+ self.descriptor, self.method).define()
+
+class CGGenericGetter(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL attribute getter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
+ Argument('JS::Value*', 'vp')]
+ if lenientThis:
+ name = "genericLenientGetter"
+ unwrapFailureCode = (
+ "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
+ "JS_SET_RVAL(cx, vp, JS::UndefinedValue());\n"
+ "return true;")
+ else:
+ name = "genericGetter"
+ unwrapFailureCode = None
+ CGAbstractBindingMethod.__init__(self, descriptor, name, args,
+ unwrapFailureCode)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(
+ "const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "JSJitPropertyOp getter = info->op;\n"
+ "return getter(cx, obj, self, vp);"))
+
+class CGSpecializedGetter(CGAbstractStaticMethod):
+ """
+ A class for generating the code for a specialized attribute getter
+ that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'get_' + attr.identifier.name
+ args = [ Argument('JSContext*', 'cx'),
+ Argument('JSHandleObject', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('JS::Value*', 'vp') ]
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
+
+ def definition_body(self):
+ name = self.attr.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ # resultOutParam does not depend on whether resultAlreadyAddRefed is set
+ (_, resultOutParam) = getRetvalDeclarationForType(self.attr.type,
+ self.descriptor,
+ False)
+ infallible = ('infallible' in
+ self.descriptor.getExtendedAttributes(self.attr,
+ getter=True))
+ if resultOutParam or self.attr.type.nullable() or not infallible:
+ nativeName = "Get" + nativeName
+ return CGIndenter(CGGetterCall(self.attr.type, nativeName,
+ self.descriptor, self.attr)).define()
+
+class CGGenericSetter(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL attribute setter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
+ Argument('JS::Value*', 'vp')]
+ if lenientThis:
+ name = "genericLenientSetter"
+ unwrapFailureCode = (
+ "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
+ "return true;")
+ else:
+ name = "genericSetter"
+ unwrapFailureCode = None
+ CGAbstractBindingMethod.__init__(self, descriptor, name, args,
+ unwrapFailureCode)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(
+ "JS::Value* argv = JS_ARGV(cx, vp);\n"
+ "JS::Value undef = JS::UndefinedValue();\n"
+ "if (argc == 0) {\n"
+ " argv = &undef;\n"
+ "}\n"
+ "const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "JSJitPropertyOp setter = info->op;\n"
+ "if (!setter(cx, obj, self, argv)) {\n"
+ " return false;\n"
+ "}\n"
+ "*vp = JSVAL_VOID;\n"
+ "return true;"))
+
+class CGSpecializedSetter(CGAbstractStaticMethod):
+ """
+ A class for generating the code for a specialized attribute setter
+ that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'set_' + attr.identifier.name
+ args = [ Argument('JSContext*', 'cx'),
+ Argument('JSHandleObject', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('JS::Value*', 'argv')]
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
+
+ def definition_body(self):
+ name = self.attr.identifier.name
+ nativeName = "Set" + MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ return CGIndenter(CGSetterCall(self.attr.type, nativeName,
+ self.descriptor, self.attr)).define()
+
+def memberIsCreator(member):
+ return member.getExtendedAttribute("Creator") is not None
+
+class CGMemberJITInfo(CGThing):
+ """
+ A class for generating the JITInfo for a property that points to
+ our specialized getter and setter.
+ """
+ def __init__(self, descriptor, member):
+ self.member = member
+ self.descriptor = descriptor
+
+ def declare(self):
+ return ""
+
+ def defineJitInfo(self, infoName, opName, infallible):
+ protoID = "prototypes::id::%s" % self.descriptor.name
+ depth = "PrototypeTraits<%s>::Depth" % protoID
+ failstr = "true" if infallible else "false"
+ return ("\n"
+ "const JSJitInfo %s = {\n"
+ " %s,\n"
+ " %s,\n"
+ " %s,\n"
+ " %s, /* isInfallible. False in setters. */\n"
+ " false /* isConstant. Only relevant for getters. */\n"
+ "};\n" % (infoName, opName, protoID, depth, failstr))
+
+ def define(self):
+ if self.member.isAttr():
+ getterinfo = ("%s_getterinfo" % self.member.identifier.name)
+ getter = ("(JSJitPropertyOp)get_%s" % self.member.identifier.name)
+ getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
+ getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
+ result = self.defineJitInfo(getterinfo, getter, getterinfal)
+ if not self.member.readonly:
+ setterinfo = ("%s_setterinfo" % self.member.identifier.name)
+ setter = ("(JSJitPropertyOp)set_%s" % self.member.identifier.name)
+ # Setters are always fallible, since they have to do a typed unwrap.
+ result += self.defineJitInfo(setterinfo, setter, False)
+ return result
+ if self.member.isMethod():
+ methodinfo = ("%s_methodinfo" % self.member.identifier.name)
+ # Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition.
+ method = ("(JSJitPropertyOp)%s" % self.member.identifier.name)
+
+ # Methods are infallible if they are infallible, have no arguments
+ # to unwrap, and have a return type that's infallible to wrap up for
+ # return.
+ methodInfal = False
+ sigs = self.member.signatures()
+ if len(sigs) == 1:
+ # Don't handle overloading. If there's more than one signature,
+ # one of them must take arguments.
+ sig = sigs[0]
+ if len(sig[1]) == 0 and infallibleForMember(self.member, sig[0], self.descriptor):
+ # No arguments and infallible return boxing
+ methodInfal = True
+
+ result = self.defineJitInfo(methodinfo, method, methodInfal)
+ return result
+ raise TypeError("Illegal member type to CGPropertyJITInfo")
+
+def getEnumValueName(value):
+ # Some enum values can be empty strings. Others might have weird
+ # characters in them. Deal with the former by returning "_empty",
+ # deal with possible name collisions from that by throwing if the
+ # enum value is actually "_empty", and throw on any value
+ # containing chars other than [a-z] or '-' for now. Replace '-' with '_'.
+ value = value.replace('-', '_')
+ if value == "_empty":
+ raise SyntaxError('"_empty" is not an IDL enum value we support yet')
+ if value == "":
+ return "_empty"
+ if not re.match("^[a-z_]+$", value):
+ raise SyntaxError('Enum value "' + value + '" contains characters '
+ 'outside [a-z_]')
+ return MakeNativeName(value)
+
+class CGEnum(CGThing):
+ def __init__(self, enum):
+ CGThing.__init__(self)
+ self.enum = enum
+
+ def declare(self):
+ return """
+ enum valuelist {
+ %s
+ };
+
+ extern const EnumEntry strings[%d];
+""" % (",\n ".join(map(getEnumValueName, self.enum.values())),
+ len(self.enum.values()) + 1)
+
+ def define(self):
+ return """
+ const EnumEntry strings[%d] = {
+ %s,
+ { NULL, 0 }
+ };
+""" % (len(self.enum.values()) + 1,
+ ",\n ".join(['{"' + val + '", ' + str(len(val)) + '}' for val in self.enum.values()]))
+
+def getUnionAccessorSignatureType(type, descriptorProvider):
+ """
+ Returns the types that are used in the getter and setter signatures for
+ union types
+ """
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ nullable = type.nullable();
+ if nullable:
+ type = type.inner.inner
+ else:
+ type = type.inner
+ (elementTemplate, elementDeclType,
+ elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
+ type, descriptorProvider, isSequenceMember=True)
+ typeName = CGWrapper(elementDeclType, pre="Sequence< ", post=" >&")
+ if nullable:
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
+
+ return typeName
+
+ if type.isUnion():
+ typeName = CGGeneric(type.name)
+ if type.nullable():
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
+
+ return typeName
+
+ if type.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+ typeName = CGGeneric(descriptor.nativeType)
+ # Allow null pointers for nullable types and old-binding classes
+ if type.nullable() or type.unroll().inner.isExternal():
+ typeName = CGWrapper(typeName, post="*")
+ else:
+ typeName = CGWrapper(typeName, post="&")
+ return typeName
+
+ if type.isSpiderMonkeyInterface():
+ typeName = CGGeneric(type.name)
+ if type.nullable():
+ typeName = CGWrapper(typeName, post="*")
+ else:
+ typeName = CGWrapper(typeName, post="&")
+ return typeName
+
+ if type.isString():
+ return CGGeneric("const nsAString&")
+
+ if type.isEnum():
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated arguments or "
+ "union members yet")
+ return CGGeneric(type.inner.identifier.name)
+
+ if type.isCallback():
+ return CGGeneric("JSObject*")
+
+ if type.isAny():
+ return CGGeneric("JS::Value")
+
+ if type.isObject():
+ typeName = CGGeneric("JSObject")
+ if type.nullable():
+ typeName = CGWrapper(typeName, post="*")
+ else:
+ typeName = CGWrapper(typeName, post="&")
+ return typeName
+
+ if not type.isPrimitive():
+ raise TypeError("Need native type for argument type '%s'" % str(type))
+
+ typeName = CGGeneric(builtinNames[type.tag()])
+ if type.nullable():
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
+ return typeName
+
+def getUnionTypeTemplateVars(type, descriptorProvider):
+ # For dictionaries and sequences we need to pass None as the failureCode
+ # for getJSToNativeConversionTemplate.
+ # Also, for dictionaries we would need to handle conversion of
+ # null/undefined to the dictionary correctly.
+ if type.isDictionary() or type.isSequence():
+ raise TypeError("Can't handle dictionaries or sequences in unions")
+
+ if type.isGeckoInterface():
+ name = type.inner.identifier.name
+ elif type.isEnum():
+ name = type.inner.identifier.name
+ elif type.isArray() or type.isSequence():
+ name = str(type)
+ else:
+ name = type.name
+
+ tryNextCode = """tryNext = true;
+return true;"""
+ if type.isGeckoInterface():
+ tryNextCode = ("""if (mUnion.mType != mUnion.eUninitialized) {
+ mUnion.Destroy%s();
+}""" % name) + tryNextCode
+ (template, declType, holderType,
+ dealWithOptional) = getJSToNativeConversionTemplate(
+ type, descriptorProvider, failureCode=tryNextCode,
+ isDefinitelyObject=True)
+
+ # This is ugly, but UnionMember needs to call a constructor with no
+ # arguments so the type can't be const.
+ structType = declType.define()
+ if structType.startswith("const "):
+ structType = structType[6:]
+ externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
+
+ if type.isObject():
+ setter = CGGeneric("void SetToObject(JSObject* obj)\n"
+ "{\n"
+ " mUnion.mValue.mObject.SetValue() = obj;\n"
+ " mUnion.mType = mUnion.eObject;\n"
+ "}")
+ else:
+ jsConversion = string.Template(template).substitute(
+ {
+ "val": "value",
+ "valPtr": "pvalue",
+ "declName": "SetAs" + name + "()",
+ "holderName": "m" + name + "Holder"
+ }
+ )
+ jsConversion = CGWrapper(CGGeneric(jsConversion),
+ post="\n"
+ "return true;")
+ setter = CGWrapper(CGIndenter(jsConversion),
+ pre="bool TrySetTo" + name + "(JSContext* cx, const JS::Value& value, JS::Value* pvalue, bool& tryNext)\n"
+ "{\n"
+ " tryNext = false;\n",
+ post="\n"
+ "}")
+
+ return {
+ "name": name,
+ "structType": structType,
+ "externalType": externalType,
+ "setter": CGIndenter(setter).define(),
+ "holderType": holderType.define() if holderType else None
+ }
+
+def mapTemplate(template, templateVarArray):
+ return map(lambda v: string.Template(template).substitute(v),
+ templateVarArray)
+
+class CGUnionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ CGThing.__init__(self)
+ self.type = type.unroll()
+ self.descriptorProvider = descriptorProvider
+
+ def declare(self):
+ templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
+ self.type.flatMemberTypes)
+
+ callDestructors = []
+ enumValues = []
+ methods = []
+ if self.type.hasNullableType:
+ callDestructors.append(" case eNull:\n"
+ " break;")
+ enumValues.append("eNull")
+ methods.append(""" bool IsNull() const
+ {
+ return mType == eNull;
+ }""")
+
+ destructorTemplate = """ void Destroy${name}()
+ {
+ MOZ_ASSERT(Is${name}(), "Wrong type!");
+ mValue.m${name}.Destroy();
+ mType = eUninitialized;
+ }"""
+ destructors = mapTemplate(destructorTemplate, templateVars)
+ callDestructors.extend(mapTemplate(" case e${name}:\n"
+ " Destroy${name}();\n"
+ " break;", templateVars))
+ enumValues.extend(mapTemplate("e${name}", templateVars))
+ methodTemplate = """ bool Is${name}() const
+ {
+ return mType == e${name};
+ }
+ ${externalType} GetAs${name}() const
+ {
+ MOZ_ASSERT(Is${name}(), "Wrong type!");
+ // The cast to ${externalType} is needed to work around a bug in Apple's
+ // clang compiler, for some reason it doesn't call |S::operator T&| when
+ // casting S<T> to T& and T is forward declared.
+ return (${externalType})mValue.m${name}.Value();
+ }
+ ${structType}& SetAs${name}()
+ {
+ mType = e${name};
+ return mValue.m${name}.SetValue();
+ }"""
+ methods.extend(mapTemplate(methodTemplate, templateVars))
+ values = mapTemplate("UnionMember<${structType} > m${name};", templateVars)
+ return string.Template("""
+class ${structName} {
+public:
+ ${structName}() : mType(eUninitialized)
+ {
+ }
+ ~${structName}()
+ {
+ switch (mType) {
+${callDestructors}
+ case eUninitialized:
+ break;
+ }
+ }
+
+${methods}
+
+private:
+ friend class ${structName}Argument;
+
+${destructors}
+
+ enum Type {
+ eUninitialized,
+ ${enumValues}
+ };
+ union Value {
+ ${values}
+ };
+
+ Type mType;
+ Value mValue;
+};
+
+""").substitute(
+ {
+ "structName": self.type.__str__(),
+ "callDestructors": "\n".join(callDestructors),
+ "destructors": "\n".join(destructors),
+ "methods": "\n\n".join(methods),
+ "enumValues": ",\n ".join(enumValues),
+ "values": "\n ".join(values),
+ })
+
+ def define(self):
+ return """
+"""
+
+class CGUnionConversionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ CGThing.__init__(self)
+ self.type = type.unroll()
+ self.descriptorProvider = descriptorProvider
+
+ def declare(self):
+ setters = []
+
+ if self.type.hasNullableType:
+ setters.append(""" bool SetNull()
+ {
+ mUnion.mType = mUnion.eNull;
+ return true;
+ }""")
+
+ templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
+ self.type.flatMemberTypes)
+ structName = self.type.__str__()
+
+ setters.extend(mapTemplate("${setter}", templateVars))
+ private = "\n".join(mapTemplate(""" ${structType}& SetAs${name}()
+ {
+ mUnion.mType = mUnion.e${name};
+ return mUnion.mValue.m${name}.SetValue();
+ }""", templateVars))
+ private += "\n\n"
+ holders = filter(lambda v: v["holderType"] is not None, templateVars)
+ if len(holders) > 0:
+ private += "\n".join(mapTemplate(" ${holderType} m${name}Holder;", holders))
+ private += "\n\n"
+ private += " " + structName + "& mUnion;"
+ return string.Template("""
+class ${structName}Argument {
+public:
+ ${structName}Argument(const ${structName}& aUnion) : mUnion(const_cast<${structName}&>(aUnion))
+ {
+ }
+
+${setters}
+
+private:
+${private}
+};
+""").substitute({"structName": structName,
+ "setters": "\n\n".join(setters),
+ "private": private
+ })
+
+ def define(self):
+ return """
+"""
+
+class ClassItem:
+ """ Use with CGClass """
+ def __init__(self, name, visibility):
+ self.name = name
+ self.visibility = visibility
+ def declare(self, cgClass):
+ assert False
+ def define(self, cgClass):
+ assert False
+
+class ClassBase(ClassItem):
+ def __init__(self, name, visibility='public'):
+ ClassItem.__init__(self, name, visibility)
+ def declare(self, cgClass):
+ return '%s %s' % (self.visibility, self.name)
+ def define(self, cgClass):
+ # Only in the header
+ return ''
+
+class ClassMethod(ClassItem):
+ def __init__(self, name, returnType, args, inline=False, static=False,
+ virtual=False, const=False, bodyInHeader=False,
+ templateArgs=None, visibility='public', body=None):
+ self.returnType = returnType
+ self.args = args
+ self.inline = inline or bodyInHeader
+ self.static = static
+ self.virtual = virtual
+ self.const = const
+ self.bodyInHeader = bodyInHeader
+ self.templateArgs = templateArgs
+ self.body = body
+ ClassItem.__init__(self, name, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.inline:
+ decorators.append('inline')
+ if declaring:
+ if self.static:
+ decorators.append('static')
+ if self.virtual:
+ decorators.append('virtual')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getBody(self):
+ # Override me or pass a string to constructor
+ assert self.body is not None
+ return self.body
+
+ def declare(self, cgClass):
+ templateClause = 'template <%s>\n' % ', '.join(self.templateArgs) \
+ if self.bodyInHeader and self.templateArgs else ''
+ args = ', '.join([str(a) for a in self.args])
+ if self.bodyInHeader:
+ body = CGIndenter(CGGeneric(self.getBody())).define()
+ body = '\n{\n' + body + '\n}'
+ else:
+ body = ';'
+
+ return string.Template("""${templateClause}${decorators}${returnType}
+${name}(${args})${const}${body}
+""").substitute({ 'templateClause': templateClause,
+ 'decorators': self.getDecorators(True),
+ 'returnType': self.returnType,
+ 'name': self.name,
+ 'const': ' const' if self.const else '',
+ 'args': args,
+ 'body': body })
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ templateArgs = cgClass.templateArgs
+ if templateArgs:
+ if cgClass.templateSpecialization:
+ templateArgs = \
+ templateArgs[len(cgClass.templateSpecialization):]
+
+ if templateArgs:
+ templateClause = \
+ 'template <%s>\n' % ', '.join([str(a) for a in templateArgs])
+ else:
+ templateClause = ''
+
+ args = ', '.join([str(a) for a in self.args])
+
+ body = CGIndenter(CGGeneric(self.getBody())).define()
+
+ return string.Template("""${templateClause}${decorators}${returnType}
+${className}::${name}(${args})${const}
+{
+${body}
+}\n
+""").substitute({ 'templateClause': templateClause,
+ 'decorators': self.getDecorators(False),
+ 'returnType': self.returnType,
+ 'className': cgClass.getNameString(),
+ 'name': self.name,
+ 'args': args,
+ 'const': ' const' if self.const else '',
+ 'body': body })
+
+class ClassConstructor(ClassItem):
+ """
+ Used for adding a constructor to a CGClass.
+
+ args is a list of Argument objects that are the arguments taken by the
+ constructor.
+
+ inline should be True if the constructor should be marked inline.
+
+ bodyInHeader should be True if the body should be placed in the class
+ declaration in the header.
+
+ visibility determines the visibility of the constructor (public,
+ protected, private), defaults to private.
+
+ baseConstructors is a list of strings containing calls to base constructors,
+ defaults to None.
+
+ body contains a string with the code for the constructor, defaults to None.
+ """
+ def __init__(self, args, inline=False, bodyInHeader=False,
+ visibility="private", baseConstructors=None, body=None):
+ self.args = args
+ self.inline = inline or bodyInHeader
+ self.bodyInHeader = bodyInHeader
+ self.baseConstructors = baseConstructors
+ self.body = body
+ ClassItem.__init__(self, None, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.inline and declaring:
+ decorators.append('inline')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getInitializationList(self, cgClass):
+ items = [str(c) for c in self.baseConstructors]
+ for m in cgClass.members:
+ if not m.static:
+ initialize = m.getBody()
+ if initialize:
+ items.append(m.name + "(" + initialize + ")")
+
+ if len(items) > 0:
+ return '\n : ' + ',\n '.join(items)
+ return ''
+
+ def getBody(self):
+ assert self.body is not None
+ return self.body
+
+ def declare(self, cgClass):
+ args = ', '.join([str(a) for a in self.args])
+ if self.bodyInHeader:
+ body = ' ' + self.getBody();
+ body = stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+ body = self.getInitializationList(cgClass) + '\n{\n' + body + '}'
+ else:
+ body = ';'
+
+ return string.Template("""${decorators}${className}(${args})${body}
+""").substitute({ 'decorators': self.getDecorators(True),
+ 'className': cgClass.getNameString(),
+ 'args': args,
+ 'body': body })
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ args = ', '.join([str(a) for a in self.args])
+
+ body = ' ' + self.getBody()
+ body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+
+ return string.Template("""${decorators}
+${className}::${className}(${args})${initializationList}
+{${body}}\n
+""").substitute({ 'decorators': self.getDecorators(False),
+ 'className': cgClass.getNameString(),
+ 'args': args,
+ 'initializationList': self.getInitializationList(cgClass),
+ 'body': body })
+
+class ClassMember(ClassItem):
+ def __init__(self, name, type, visibility="private", static=False,
+ body=None):
+ self.type = type;
+ self.static = static
+ self.body = body
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return '%s%s %s;\n' % ('static ' if self.static else '', self.type,
+ self.name)
+
+ def define(self, cgClass):
+ if not self.static:
+ return ''
+ if self.body:
+ body = " = " + self.body
+ else:
+ body = ""
+ return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(),
+ self.name, body)
+
+class ClassTypedef(ClassItem):
+ def __init__(self, name, type, visibility="public"):
+ self.type = type
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return 'typedef %s %s;\n' % (self.type, self.name)
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class ClassEnum(ClassItem):
+ def __init__(self, name, entries, values=None, visibility="public"):
+ self.entries = entries
+ self.values = values
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ entries = []
+ for i in range(0, len(self.entries)):
+ if i >= len(self.values):
+ entry = '%s' % self.entries[i]
+ else:
+ entry = '%s = %s' % (self.entries[i], self.values[i])
+ entries.append(entry)
+ name = '' if not self.name else ' ' + self.name
+ return 'enum%s\n{\n %s\n};\n' % (name, ',\n '.join(entries))
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class CGClass(CGThing):
+ def __init__(self, name, bases=[], members=[], constructors=[], methods=[],
+ typedefs = [], enums=[], templateArgs=[],
+ templateSpecialization=[], isStruct=False, indent=''):
+ CGThing.__init__(self)
+ self.name = name
+ self.bases = bases
+ self.members = members
+ self.constructors = constructors
+ self.methods = methods
+ self.typedefs = typedefs
+ self.enums = enums
+ self.templateArgs = templateArgs
+ self.templateSpecialization = templateSpecialization
+ self.isStruct = isStruct
+ self.indent = indent
+ self.defaultVisibility ='public' if isStruct else 'private'
+
+ def getNameString(self):
+ className = self.name
+ if self.templateSpecialization:
+ className = className + \
+ '<%s>' % ', '.join([str(a) for a
+ in self.templateSpecialization])
+ return className
+
+ def declare(self):
+ result = ''
+ if self.templateArgs:
+ templateArgs = [str(a) for a in self.templateArgs]
+ templateArgs = templateArgs[len(self.templateSpecialization):]
+ result = result + self.indent + 'template <%s>\n' \
+ % ','.join([str(a) for a in templateArgs])
+
+ type = 'struct' if self.isStruct else 'class'
+
+ if self.templateSpecialization:
+ specialization = \
+ '<%s>' % ', '.join([str(a) for a in self.templateSpecialization])
+ else:
+ specialization = ''
+
+ result = result + '%s%s %s%s' \
+ % (self.indent, type, self.name, specialization)
+
+ if self.bases:
+ result = result + ' : %s' % ', '.join([d.declare(self) for d in self.bases])
+
+ result = result + '\n%s{\n' % self.indent
+
+ def declareMembers(cgClass, memberList, defaultVisibility, itemCount,
+ separator=''):
+ members = { 'private': [], 'protected': [], 'public': [] }
+
+ for member in memberList:
+ members[member.visibility].append(member)
+
+
+ if defaultVisibility == 'public':
+ order = [ 'public', 'protected', 'private' ]
+ else:
+ order = [ 'private', 'protected', 'public' ]
+
+ result = ''
+
+ lastVisibility = defaultVisibility
+ for visibility in order:
+ list = members[visibility]
+ if list:
+ if visibility != lastVisibility:
+ if itemCount:
+ result = result + '\n'
+ result = result + visibility + ':\n'
+ itemCount = 0
+ for member in list:
+ if itemCount != 0:
+ result = result + separator
+ declaration = member.declare(cgClass)
+ declaration = CGIndenter(CGGeneric(declaration)).define()
+ result = result + declaration
+ itemCount = itemCount + 1
+ lastVisibility = visibility
+ return (result, lastVisibility, itemCount)
+
+ order = [(self.enums, ''), (self.typedefs, ''), (self.members, ''),
+ (self.constructors, '\n'), (self.methods, '\n')]
+
+ lastVisibility = self.defaultVisibility
+ itemCount = 0
+ for (memberList, separator) in order:
+ (memberString, lastVisibility, itemCount) = \
+ declareMembers(self, memberList, lastVisibility, itemCount,
+ separator)
+ if self.indent:
+ memberString = CGIndenter(CGGeneric(memberString),
+ len(self.indent)).define()
+ result = result + memberString
+
+ result = result + self.indent + '};\n'
+ return result
+
+ def define(self):
+ def defineMembers(cgClass, memberList, itemCount, separator=''):
+ result = ''
+ for member in memberList:
+ if itemCount != 0:
+ result = result + separator
+ result = result + member.define(cgClass)
+ itemCount = itemCount + 1
+ return (result, itemCount)
+
+ order = [(self.members, '\n'), (self.constructors, '\n'),
+ (self.methods, '\n')]
+
+ result = ''
+ itemCount = 0
+ for (memberList, separator) in order:
+ (memberString, itemCount) = defineMembers(self, memberList,
+ itemCount, separator)
+ result = result + memberString
+ return result
+
+class CGResolveOwnProperty(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('JSPropertyDescriptor*', 'desc')]
+ CGAbstractMethod.__init__(self, descriptor, "ResolveOwnProperty", "bool", args)
+ def definition_body(self):
+ return """ JSObject* obj = wrapper;
+ if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+ }
+ // We rely on getOwnPropertyDescriptor not shadowing prototype properties by named
+ // properties. If that changes we'll need to filter here.
+ return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, set, desc);
+"""
+
+class CGEnumerateOwnProperties(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('JS::AutoIdVector&', 'props')]
+ CGAbstractMethod.__init__(self, descriptor, "EnumerateOwnProperties", "bool", args)
+ def definition_body(self):
+ return """ JSObject* obj = wrapper;
+ if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+ }
+ // We rely on getOwnPropertyNames not shadowing prototype properties by named
+ // properties. If that changes we'll need to filter here.
+ return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);
+"""
+
+class CGXrayHelper(CGAbstractMethod):
+ def __init__(self, descriptor, name, args, properties):
+ CGAbstractMethod.__init__(self, descriptor, name, "bool", args)
+ self.properties = properties
+
+ def definition_body(self):
+ varNames = self.properties.variableNames(True)
+
+ methods = self.properties.methods
+ if methods.hasNonChromeOnly() or methods.hasChromeOnly():
+ methodArgs = """// %(methods)s has an end-of-list marker at the end that we ignore
+%(methods)s, %(methods)s_ids, %(methods)s_specs, ArrayLength(%(methods)s) - 1""" % varNames
+ else:
+ methodArgs = "NULL, NULL, NULL, 0"
+ methodArgs = CGGeneric(methodArgs)
+
+ attrs = self.properties.attrs
+ if attrs.hasNonChromeOnly() or attrs.hasChromeOnly():
+ attrArgs = """// %(attrs)s has an end-of-list marker at the end that we ignore
+%(attrs)s, %(attrs)s_ids, %(attrs)s_specs, ArrayLength(%(attrs)s) - 1""" % varNames
+ else:
+ attrArgs = "NULL, NULL, NULL, 0"
+ attrArgs = CGGeneric(attrArgs)
+
+ consts = self.properties.consts
+ if consts.hasNonChromeOnly() or consts.hasChromeOnly():
+ constArgs = """// %(consts)s has an end-of-list marker at the end that we ignore
+%(consts)s, %(consts)s_ids, %(consts)s_specs, ArrayLength(%(consts)s) - 1""" % varNames
+ else:
+ constArgs = "NULL, NULL, NULL, 0"
+ constArgs = CGGeneric(constArgs)
+
+ prefixArgs = CGGeneric(self.getPrefixArgs())
+
+ return CGIndenter(
+ CGWrapper(CGList([prefixArgs, methodArgs, attrArgs, constArgs], ",\n"),
+ pre=("return Xray%s(" % self.name),
+ post=");",
+ reindent=True)).define()
+
+class CGResolveProperty(CGXrayHelper):
+ def __init__(self, descriptor, properties):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('JSPropertyDescriptor*', 'desc')]
+ CGXrayHelper.__init__(self, descriptor, "ResolveProperty", args,
+ properties)
+
+ def getPrefixArgs(self):
+ return "cx, wrapper, id, desc"
+
+
+class CGEnumerateProperties(CGXrayHelper):
+ def __init__(self, descriptor, properties):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('JS::AutoIdVector&', 'props')]
+ CGXrayHelper.__init__(self, descriptor, "EnumerateProperties", args,
+ properties)
+
+ def getPrefixArgs(self):
+ return "props"
+
+class CGPrototypeTraitsClass(CGClass):
+ def __init__(self, descriptor, indent=''):
+ templateArgs = [Argument('prototypes::ID', 'PrototypeID')]
+ templateSpecialization = ['prototypes::id::' + descriptor.name]
+ enums = [ClassEnum('', ['Depth'],
+ [descriptor.interface.inheritanceDepth()])]
+ typedefs = [ClassTypedef('NativeType', descriptor.nativeType)]
+ CGClass.__init__(self, 'PrototypeTraits', indent=indent,
+ templateArgs=templateArgs,
+ templateSpecialization=templateSpecialization,
+ enums=enums, typedefs=typedefs, isStruct=True)
+
+class CGPrototypeIDMapClass(CGClass):
+ def __init__(self, descriptor, indent=''):
+ templateArgs = [Argument('class', 'ConcreteClass')]
+ templateSpecialization = [descriptor.nativeType]
+ enums = [ClassEnum('', ['PrototypeID'],
+ ['prototypes::id::' + descriptor.name])]
+ CGClass.__init__(self, 'PrototypeIDMap', indent=indent,
+ templateArgs=templateArgs,
+ templateSpecialization=templateSpecialization,
+ enums=enums, isStruct=True)
+
+class CGClassForwardDeclare(CGThing):
+ def __init__(self, name, isStruct=False):
+ CGThing.__init__(self)
+ self.name = name
+ self.isStruct = isStruct
+ def declare(self):
+ type = 'struct' if self.isStruct else 'class'
+ return '%s %s;\n' % (type, self.name)
+ def define(self):
+ # Header only
+ return ''
+
+class CGProxySpecialOperation(CGPerSignatureCall):
+ """
+ Base class for classes for calling an indexed or named special operation
+ (don't use this directly, use the derived classes below).
+ """
+ def __init__(self, descriptor, operation):
+ nativeName = MakeNativeName(descriptor.binaryNames.get(operation, operation))
+ operation = descriptor.operations[operation]
+ assert len(operation.signatures()) == 1
+ signature = operation.signatures()[0]
+ extendedAttributes = descriptor.getExtendedAttributes(operation)
+
+ (returnType, arguments) = signature
+
+ # We pass len(arguments) as the final argument so that the
+ # CGPerSignatureCall won't do any argument conversion of its own.
+ CGPerSignatureCall.__init__(self, returnType, "", arguments, nativeName,
+ False, descriptor, operation,
+ len(arguments))
+
+ if operation.isSetter() or operation.isCreator():
+ # arguments[0] is the index or name of the item that we're setting.
+ argument = arguments[1]
+ template = getJSToNativeConversionTemplate(argument.type, descriptor,
+ treatNullAs=argument.treatNullAs,
+ treatUndefinedAs=argument.treatUndefinedAs)
+ templateValues = {
+ "declName": argument.identifier.name,
+ "holderName": argument.identifier.name + "_holder",
+ "val": "desc->value",
+ "valPtr": "&desc->value"
+ }
+ self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(template, templateValues))
+ elif operation.isGetter():
+ self.cgRoot.prepend(CGGeneric("bool found;"))
+
+ def getArguments(self):
+ args = [(a, a.identifier.name) for a in self.arguments]
+ if self.idlNode.isGetter():
+ args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
+ self.idlNode),
+ "found"))
+ return args
+
+ def wrap_return_value(self):
+ if not self.idlNode.isGetter() or self.templateValues is None:
+ return ""
+
+ wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues))
+ wrap = CGIfWrapper(wrap, "found")
+ return "\n" + wrap.define()
+
+class CGProxyIndexedGetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ """
+ def __init__(self, descriptor, templateValues=None):
+ self.templateValues = templateValues
+ CGProxySpecialOperation.__init__(self, descriptor, 'IndexedGetter')
+
+class CGProxyIndexedSetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed setter.
+ """
+ def __init__(self, descriptor):
+ CGProxySpecialOperation.__init__(self, descriptor, 'IndexedSetter')
+
+class CGProxyNamedGetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an named getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ """
+ def __init__(self, descriptor, templateValues=None):
+ self.templateValues = templateValues
+ CGProxySpecialOperation.__init__(self, descriptor, 'NamedGetter')
+
+class CGProxyNamedSetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to a named setter.
+ """
+ def __init__(self, descriptor):
+ CGProxySpecialOperation.__init__(self, descriptor, 'NamedSetter')
+
+class CGProxyIsProxy(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSObject*', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True)
+ def declare(self):
+ return ""
+ def definition_body(self):
+ return " return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();"
+
+class CGProxyUnwrap(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSObject*', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", descriptor.nativeType + '*', args, alwaysInline=True)
+ def declare(self):
+ return ""
+ def definition_body(self):
+ return """ if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+ }
+ MOZ_ASSERT(IsProxy(obj));
+ return static_cast<%s*>(js::GetProxyPrivate(obj).toPrivate());""" % (self.descriptor.nativeType)
+
+class CGDOMJSProxyHandlerDOMClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ return "extern const DOMClass Class;\n"
+ def define(self):
+ return """
+const DOMClass Class = """ + DOMClass(self.descriptor) + """;
+
+"""
+
+class CGDOMJSProxyHandler_CGDOMJSProxyHandler(ClassConstructor):
+ def __init__(self):
+ ClassConstructor.__init__(self, [], inline=True, visibility="private",
+ baseConstructors=["mozilla::dom::DOMProxyHandler(Class)"],
+ body="")
+
+class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('JSPropertyDescriptor*', 'desc')]
+ ClassMethod.__init__(self, "getOwnPropertyDescriptor", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+
+ setOrIndexedGet = ""
+ if indexedGetter or indexedSetter:
+ setOrIndexedGet += "int32_t index = GetArrayIndexFromId(cx, id);\n"
+
+ if indexedGetter:
+ readonly = toStringBool(self.descriptor.operations['IndexedSetter'] is None)
+ fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
+ templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
+ 'obj': 'proxy', 'successCode': fillDescriptor}
+ get = ("if (index >= 0) {\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
+ "}\n") % (self.descriptor.nativeType)
+
+ if indexedSetter or self.descriptor.operations['NamedSetter']:
+ setOrIndexedGet += "if (set) {\n"
+ if indexedSetter:
+ setOrIndexedGet += (" if (index >= 0) {\n")
+ if not 'IndexedCreator' in self.descriptor.operations:
+ # FIXME need to check that this is a 'supported property index'
+ assert False
+ setOrIndexedGet += (" FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" +
+ " return true;\n" +
+ " }\n")
+ if self.descriptor.operations['NamedSetter']:
+ setOrIndexedGet += " if (JSID_IS_STRING(id)) {\n"
+ if not 'NamedCreator' in self.descriptor.operations:
+ # FIXME need to check that this is a 'supported property name'
+ assert False
+ setOrIndexedGet += (" FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" +
+ " return true;\n" +
+ " }\n")
+ setOrIndexedGet += "}"
+ if indexedGetter:
+ setOrIndexedGet += (" else {\n" +
+ CGIndenter(CGGeneric(get)).define() +
+ "}")
+ setOrIndexedGet += "\n\n"
+ elif indexedGetter:
+ setOrIndexedGet += ("if (!set) {\n" +
+ CGIndenter(CGGeneric(get)).define() +
+ "}\n\n")
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None)
+ fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
+ templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
+ 'obj': 'proxy', 'successCode': fillDescriptor}
+ # Once we start supporting OverrideBuiltins we need to make
+ # ResolveOwnProperty or EnumerateOwnProperties filter out named
+ # properties that shadow prototype properties.
+ namedGet = ("\n" +
+ "if (!set && JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" +
+ "}\n") % (self.descriptor.nativeType)
+ else:
+ namedGet = ""
+
+ return setOrIndexedGet + """JSObject* expando;
+if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
+ unsigned flags = (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED;
+ if (!JS_GetPropertyDescriptorById(cx, expando, id, flags, desc)) {
+ return false;
+ }
+ if (desc->obj) {
+ // Pretend the property lives on the wrapper.
+ desc->obj = proxy;
+ return true;
+ }
+}
+""" + namedGet + """
+desc->obj = NULL;
+return true;"""
+
+class CGDOMJSProxyHandler_defineProperty(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('jsid', 'id'),
+ Argument('JSPropertyDescriptor*', 'desc')]
+ ClassMethod.__init__(self, "defineProperty", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ set = ""
+
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+ if indexedSetter:
+ if not (self.descriptor.operations['IndexedCreator'] is indexedSetter):
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
+ "if (index >= 0) {\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
+ " return true;\n" +
+ "}\n") % (self.descriptor.nativeType)
+ elif self.descriptor.operations['IndexedGetter']:
+ set += ("if (GetArrayIndexFromId(cx, id) >= 0) {\n" +
+ " return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
+ "}\n") % self.descriptor.name
+
+ namedSetter = self.descriptor.operations['NamedSetter']
+ if namedSetter:
+ if not self.descriptor.operations['NamedCreator'] is namedSetter:
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += ("if (JSID_IS_STRING(id)) {\n" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + "\n" +
+ "}\n") % (self.descriptor.nativeType)
+ elif self.descriptor.operations['NamedGetter']:
+ set += ("if (JSID_IS_STRING(id)) {\n" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define() +
+ " if (found) {\n"
+ " return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
+ " }\n" +
+ " return true;\n"
+ "}\n") % (self.descriptor.nativeType, self.descriptor.name)
+ return set + """return mozilla::dom::DOMProxyHandler::defineProperty(%s);""" % ", ".join(a.name for a in self.args)
+
+class CGDOMJSProxyHandler_getOwnPropertyNames(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('JS::AutoIdVector&', 'props')]
+ ClassMethod.__init__(self, "getOwnPropertyNames", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ addIndices = """uint32_t length = UnwrapProxy(proxy)->Length();
+MOZ_ASSERT(int32_t(length) >= 0);
+for (int32_t i = 0; i < int32_t(length); ++i) {
+ if (!props.append(INT_TO_JSID(i))) {
+ return false;
+ }
+}
+
+"""
+ else:
+ addIndices = ""
+
+ return addIndices + """JSObject* expando;
+if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = DOMProxyHandler::GetExpandoObject(proxy)) &&
+ !js::GetPropertyNames(cx, expando, JSITER_OWNONLY | JSITER_HIDDEN, &props)) {
+ return false;
+}
+
+// FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=772869 Add named items
+return true;"""
+
+class CGDOMJSProxyHandler_hasOwn(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('jsid', 'id'), Argument('bool*', 'bp')]
+ ClassMethod.__init__(self, "hasOwn", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ indexed = ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
+ "if (index >= 0) {\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" +
+ " *bp = found;\n" +
+ " return true;\n" +
+ "}\n\n") % (self.descriptor.nativeType)
+ else:
+ indexed = ""
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ named = ("if (JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
+ " jsval nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + "\n" +
+ " *bp = found;\n"
+ " return true;\n"
+ "}\n" +
+ "\n") % (self.descriptor.nativeType)
+ else:
+ named = ""
+
+ return indexed + """JSObject* expando = GetExpandoObject(proxy);
+if (expando) {
+ JSBool b = true;
+ JSBool ok = JS_HasPropertyById(cx, expando, id, &b);
+ *bp = !!b;
+ if (!ok || *bp) {
+ return ok;
+ }
+}
+
+""" + named + """*bp = false;
+return true;"""
+
+class CGDOMJSProxyHandler_get(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('JSObject*', 'receiver'), Argument('jsid', 'id'),
+ Argument('JS::Value*', 'vp')]
+ ClassMethod.__init__(self, "get", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ getFromExpando = """JSObject* expando = DOMProxyHandler::GetExpandoObject(proxy);
+if (expando) {
+ JSBool hasProp;
+ if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
+ return false;
+ }
+
+ if (hasProp) {
+ return JS_GetPropertyById(cx, expando, id, vp);
+ }
+}"""
+
+ templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp', 'obj': 'proxy'}
+
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ getIndexedOrExpando = ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
+ "if (index >= 0) {\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define()) % (self.descriptor.nativeType)
+ getIndexedOrExpando += """
+ // Even if we don't have this index, we don't forward the
+ // get on to our expando object.
+} else {
+ %s
+}
+""" % (stripTrailingWhitespace(getFromExpando.replace('\n', '\n ')))
+ else:
+ getIndexedOrExpando = getFromExpando + "\n"
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ getNamed = ("if (JSID_IS_STRING(id)) {\n" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() +
+ "}\n") % (self.descriptor.nativeType)
+ else:
+ getNamed = ""
+
+ return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ "Should not have a XrayWrapper here");
+
+%s
+bool found;
+if (!GetPropertyOnPrototype(cx, proxy, id, &found, vp)) {
+ return false;
+}
+
+if (found) {
+ return true;
+}
+%s
+vp->setUndefined();
+return true;""" % (getIndexedOrExpando, getNamed)
+
+class CGDOMJSProxyHandler_obj_toString(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy')]
+ ClassMethod.__init__(self, "obj_toString", "JSString*", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ stringifier = self.descriptor.operations['Stringifier']
+ if stringifier:
+ name = stringifier.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ signature = stringifier.signatures()[0]
+ returnType = signature[0]
+ extendedAttributes = self.descriptor.getExtendedAttributes(stringifier)
+ infallible = 'infallible' in extendedAttributes
+ if not infallible:
+ error = CGGeneric(
+ ('ThrowMethodFailedWithDetails(cx, rv, "%s", "toString");\n' +
+ "return NULL;") % self.descriptor.interface.identifier.name)
+ else:
+ error = None
+ call = CGCallGenerator(error, [], "", returnType, extendedAttributes, self.descriptor, nativeName, False, object="UnwrapProxy(proxy)")
+ return call.define() + """
+
+JSString* jsresult;
+return xpc_qsStringToJsstring(cx, result, &jsresult) ? jsresult : NULL;"""
+
+ return "return mozilla::dom::DOMProxyHandler::obj_toString(cx, \"%s\");" % self.descriptor.name
+
+class CGDOMJSProxyHandler_finalize(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')]
+ ClassMethod.__init__(self, "finalize", "void", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ return ("%s self = UnwrapProxy(proxy);\n\n" % (self.descriptor.nativeType + "*") +
+ finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name))
+
+class CGDOMJSProxyHandler_getElementIfPresent(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('JSObject*', 'receiver'),
+ Argument('uint32_t', 'index'),
+ Argument('JS::Value*', 'vp'), Argument('bool*', 'present')]
+ ClassMethod.__init__(self, "getElementIfPresent", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ successCode = """*present = found;
+return true;"""
+ templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp',
+ 'obj': 'proxy', 'successCode': successCode}
+ get = ("%s* self = UnwrapProxy(proxy);\n" +
+ CGProxyIndexedGetter(self.descriptor, templateValues).define() + "\n"
+ "// We skip the expando object if there is an indexed getter.\n" +
+ "\n") % (self.descriptor.nativeType)
+ else:
+ get = """
+
+JSObject* expando = GetExpandoObject(proxy);
+if (expando) {
+ JSBool isPresent;
+ if (!JS_GetElementIfPresent(cx, expando, index, expando, vp, &isPresent)) {
+ return false;
+ }
+ if (isPresent) {
+ *present = true;
+ return true;
+ }
+}
+"""
+
+ return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ "Should not have a XrayWrapper here");
+
+""" + get + """
+// No need to worry about name getters here, so just check the proto.
+
+JSObject *proto;
+if (!js::GetObjectProto(cx, proxy, &proto)) {
+ return false;
+}
+if (proto) {
+ JSBool isPresent;
+ if (!JS_GetElementIfPresent(cx, proto, index, proxy, vp, &isPresent)) {
+ return false;
+ }
+ *present = isPresent;
+ return true;
+}
+
+*present = false;
+// Can't Debug_SetValueRangeToCrashOnTouch because it's not public
+return true;"""
+
+class CGDOMJSProxyHandler_getInstance(ClassMethod):
+ def __init__(self):
+ ClassMethod.__init__(self, "getInstance", "DOMProxyHandler*", [], static=True)
+ def getBody(self):
+ return """static DOMProxyHandler instance;
+return &instance;"""
+
+class CGDOMJSProxyHandler(CGClass):
+ def __init__(self, descriptor):
+ constructors = [CGDOMJSProxyHandler_CGDOMJSProxyHandler()]
+ methods = [CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor)]
+ if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']:
+ methods.append(CGDOMJSProxyHandler_defineProperty(descriptor))
+ methods.extend([CGDOMJSProxyHandler_getOwnPropertyNames(descriptor),
+ CGDOMJSProxyHandler_hasOwn(descriptor),
+ CGDOMJSProxyHandler_get(descriptor),
+ CGDOMJSProxyHandler_obj_toString(descriptor),
+ CGDOMJSProxyHandler_finalize(descriptor),
+ CGDOMJSProxyHandler_getElementIfPresent(descriptor),
+ CGDOMJSProxyHandler_getInstance()])
+ CGClass.__init__(self, 'DOMProxyHandler',
+ bases=[ClassBase('mozilla::dom::DOMProxyHandler')],
+ constructors=constructors,
+ methods=methods)
+
+def stripTrailingWhitespace(text):
+ tail = '\n' if text.endswith('\n') else ''
+ lines = text.splitlines()
+ for i in range(len(lines)):
+ lines[i] = lines[i].rstrip()
+ return '\n'.join(lines) + tail
+
+class CGDescriptor(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+
+ assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
+
+ cgThings = []
+ if descriptor.interface.hasInterfacePrototypeObject():
+ (hasMethod, hasGetter, hasLenientGetter,
+ hasSetter, hasLenientSetter) = False, False, False, False, False
+ for m in descriptor.interface.members:
+ if m.isMethod() and not m.isStatic() and not m.isIdentifierLess():
+ cgThings.append(CGSpecializedMethod(descriptor, m))
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ hasMethod = True
+ elif m.isAttr():
+ cgThings.append(CGSpecializedGetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientGetter = True
+ else:
+ hasGetter = True
+ if not m.readonly:
+ cgThings.append(CGSpecializedSetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientSetter = True
+ else:
+ hasSetter = True
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ if hasMethod: cgThings.append(CGGenericMethod(descriptor))
+ if hasGetter: cgThings.append(CGGenericGetter(descriptor))
+ if hasLenientGetter: cgThings.append(CGGenericGetter(descriptor,
+ lenientThis=True))
+ if hasSetter: cgThings.append(CGGenericSetter(descriptor))
+ if hasLenientSetter: cgThings.append(CGGenericSetter(descriptor,
+ lenientThis=True))
+
+ if descriptor.concrete and not descriptor.proxy:
+ if not descriptor.workers and descriptor.wrapperCache:
+ cgThings.append(CGAddPropertyHook(descriptor))
+
+ # Always have a finalize hook, regardless of whether the class wants a
+ # custom hook.
+ cgThings.append(CGClassFinalizeHook(descriptor))
+
+ # Only generate a trace hook if the class wants a custom hook.
+ if (descriptor.customTrace):
+ cgThings.append(CGClassTraceHook(descriptor))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGClassConstructHook(descriptor))
+ cgThings.append(CGClassHasInstanceHook(descriptor))
+ cgThings.append(CGInterfaceObjectJSClass(descriptor))
+
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGPrototypeJSClass(descriptor))
+
+ properties = PropertyArrays(descriptor)
+ cgThings.append(CGGeneric(define=str(properties)))
+ cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGGetProtoObjectMethod(descriptor))
+ else:
+ cgThings.append(CGGetConstructorObjectMethod(descriptor))
+
+ # Set up our Xray callbacks as needed. Note that we don't need to do
+ # it in workers.
+ if (descriptor.interface.hasInterfacePrototypeObject() and
+ not descriptor.workers):
+ if descriptor.concrete and descriptor.proxy:
+ cgThings.append(CGResolveOwnProperty(descriptor))
+ cgThings.append(CGEnumerateOwnProperties(descriptor))
+ cgThings.append(CGResolveProperty(descriptor, properties))
+ cgThings.append(CGEnumerateProperties(descriptor, properties))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
+ if (not descriptor.interface.isExternal() and
+ # Workers stuff is never pref-controlled
+ not descriptor.workers and
+ descriptor.interface.getExtendedAttribute("PrefControlled") is not None):
+ cgThings.append(CGPrefEnabled(descriptor))
+
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGNativePropertyHooks(descriptor))
+
+ if descriptor.concrete:
+ if descriptor.proxy:
+ cgThings.append(CGProxyIsProxy(descriptor))
+ cgThings.append(CGProxyUnwrap(descriptor))
+ cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor))
+ cgThings.append(CGDOMJSProxyHandler(descriptor))
+ cgThings.append(CGIsMethod(descriptor))
+ else:
+ cgThings.append(CGDOMJSClass(descriptor))
+
+ if descriptor.wrapperCache:
+ cgThings.append(CGWrapWithCacheMethod(descriptor))
+ cgThings.append(CGWrapMethod(descriptor))
+ else:
+ cgThings.append(CGWrapNonWrapperCacheMethod(descriptor))
+
+ cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
+ cgThings = CGWrapper(cgThings, pre='\n', post='\n')
+ self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
+ cgThings),
+ post='\n')
+
+ def declare(self):
+ return self.cgRoot.declare()
+ def define(self):
+ return self.cgRoot.define()
+
+class CGNamespacedEnum(CGThing):
+ def __init__(self, namespace, enumName, names, values, comment=""):
+
+ if not values:
+ values = []
+
+ # Account for explicit enum values.
+ entries = []
+ for i in range(0, len(names)):
+ if len(values) > i and values[i] is not None:
+ entry = "%s = %s" % (names[i], values[i])
+ else:
+ entry = names[i]
+ entries.append(entry)
+
+ # Append a Count.
+ entries.append('_' + enumName + '_Count')
+
+ # Indent.
+ entries = [' ' + e for e in entries]
+
+ # Build the enum body.
+ enumstr = comment + 'enum %s\n{\n%s\n};\n' % (enumName, ',\n'.join(entries))
+ curr = CGGeneric(declare=enumstr)
+
+ # Add some whitespace padding.
+ curr = CGWrapper(curr, pre='\n',post='\n')
+
+ # Add the namespace.
+ curr = CGNamespace(namespace, curr)
+
+ # Add the typedef
+ typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName)
+ curr = CGList([curr, CGGeneric(declare=typedef)])
+
+ # Save the result.
+ self.node = curr
+
+ def declare(self):
+ return self.node.declare()
+ def define(self):
+ assert False # Only for headers.
+
+class CGDictionary(CGThing):
+ def __init__(self, dictionary, descriptorProvider):
+ self.dictionary = dictionary;
+ self.workers = descriptorProvider.workers
+ if all(CGDictionary(d, descriptorProvider).generatable for
+ d in CGDictionary.getDictionaryDependencies(dictionary)):
+ self.generatable = True
+ else:
+ self.generatable = False
+ # Nothing else to do here
+ return
+ # Getting a conversion template for interface types can fail
+ # if we don't have a relevant descriptor when self.workers is True.
+ # If that happens, just mark ourselves as not being
+ # generatable and move on.
+ try:
+ self.memberInfo = [
+ (member,
+ getJSToNativeConversionTemplate(member.type,
+ descriptorProvider,
+ isMember=True,
+ isOptional=(not member.defaultValue),
+ defaultValue=member.defaultValue))
+ for member in dictionary.members ]
+ except NoSuchDescriptorError, err:
+ if not self.workers:
+ raise err
+ self.generatable = False
+
+ def declare(self):
+ if not self.generatable:
+ return ""
+ d = self.dictionary
+ if d.parent:
+ inheritance = ": public %s " % self.makeClassName(d.parent)
+ else:
+ inheritance = ""
+ memberDecls = [" %s %s;" %
+ (self.getMemberType(m), m[0].identifier.name)
+ for m in self.memberInfo]
+
+ return (string.Template(
+ "struct ${selfName} ${inheritance}{\n"
+ " ${selfName}() {}\n"
+ " bool Init(JSContext* cx, const JS::Value& val);\n"
+ "\n" +
+ "\n".join(memberDecls) + "\n"
+ "private:\n"
+ " // Disallow copy-construction\n"
+ " ${selfName}(const ${selfName}&) MOZ_DELETE;\n" +
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ (" static bool InitIds(JSContext* cx);\n"
+ " static bool initedIds;\n" if not self.workers else "") +
+ "\n".join(" static jsid " +
+ self.makeIdName(m.identifier.name) + ";" for
+ m in d.members) + "\n"
+ "};").substitute( { "selfName": self.makeClassName(d),
+ "inheritance": inheritance }))
+
+ def define(self):
+ if not self.generatable:
+ return ""
+ d = self.dictionary
+ if d.parent:
+ initParent = ("// Per spec, we init the parent's members first\n"
+ "if (!%s::Init(cx, val)) {\n"
+ " return false;\n"
+ "}\n" % self.makeClassName(d.parent))
+ else:
+ initParent = ""
+
+ memberInits = [CGIndenter(self.getMemberConversion(m)).define()
+ for m in self.memberInfo]
+ idinit = [CGGeneric('!InternJSString(cx, %s, "%s")' %
+ (m.identifier.name + "_id", m.identifier.name))
+ for m in d.members]
+ idinit = CGList(idinit, " ||\n")
+ idinit = CGWrapper(idinit, pre="if (",
+ post=(") {\n"
+ " return false;\n"
+ "}"),
+ reindent=True)
+
+ return string.Template(
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ ("bool ${selfName}::initedIds = false;\n" +
+ "\n".join("jsid ${selfName}::%s = JSID_VOID;" %
+ self.makeIdName(m.identifier.name)
+ for m in d.members) + "\n"
+ "\n"
+ "bool\n"
+ "${selfName}::InitIds(JSContext* cx)\n"
+ "{\n"
+ " MOZ_ASSERT(!initedIds);\n"
+ "${idInit}\n"
+ " initedIds = true;\n"
+ " return true;\n"
+ "}\n"
+ "\n" if not self.workers else "") +
+ "bool\n"
+ "${selfName}::Init(JSContext* cx, const JS::Value& val)\n"
+ "{\n" +
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ (" if (!initedIds && !InitIds(cx)) {\n"
+ " return false;\n"
+ " }\n" if not self.workers else "") +
+ "${initParent}"
+ " JSBool found;\n"
+ " JS::Value temp;\n"
+ " bool isNull = val.isNullOrUndefined();\n"
+ " if (!isNull && !val.isObject()) {\n"
+ " return Throw<${isMainThread}>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
+ " }\n"
+ "\n"
+ "${initMembers}\n"
+ " return true;\n"
+ "}").substitute({
+ "selfName": self.makeClassName(d),
+ "initParent": CGIndenter(CGGeneric(initParent)).define(),
+ "initMembers": "\n\n".join(memberInits),
+ "idInit": CGIndenter(idinit).define(),
+ "isMainThread": toStringBool(not self.workers)
+ })
+
+ @staticmethod
+ def makeDictionaryName(dictionary, workers):
+ suffix = "Workers" if workers else ""
+ return dictionary.identifier.name + suffix
+
+ def makeClassName(self, dictionary):
+ return self.makeDictionaryName(dictionary, self.workers)
+
+ def getMemberType(self, memberInfo):
+ (member, (templateBody, declType,
+ holderType, dealWithOptional)) = memberInfo
+ # We can't handle having a holderType here
+ assert holderType is None
+ if dealWithOptional:
+ declType = CGWrapper(declType, pre="Optional< ", post=" >")
+ return declType.define()
+
+ def getMemberConversion(self, memberInfo):
+ (member, (templateBody, declType,
+ holderType, dealWithOptional)) = memberInfo
+ replacements = { "val": "temp",
+ "valPtr": "&temp",
+ # Use this->%s to refer to members, because we don't
+ # control the member names and want to make sure we're
+ # talking about the member, not some local that
+ # shadows the member. Another option would be to move
+ # the guts of init to a static method which is passed
+ # an explicit reference to our dictionary object, so
+ # we couldn't screw this up even if we wanted to....
+ "declName": ("(this->%s)" % member.identifier.name),
+ # We need a holder name for external interfaces, but
+ # it's scoped down to the conversion so we can just use
+ # anything we want.
+ "holderName": "holder"}
+ # We can't handle having a holderType here
+ assert holderType is None
+ if dealWithOptional:
+ replacements["declName"] = "(" + replacements["declName"] + ".Value())"
+ if member.defaultValue:
+ replacements["haveValue"] = "found"
+
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ if self.workers:
+ propName = member.identifier.name
+ propCheck = ('JS_HasProperty(cx, &val.toObject(), "%s", &found)' %
+ propName)
+ propGet = ('JS_GetProperty(cx, &val.toObject(), "%s", &temp)' %
+ propName)
+ else:
+ propId = self.makeIdName(member.identifier.name);
+ propCheck = ("JS_HasPropertyById(cx, &val.toObject(), %s, &found)" %
+ propId)
+ propGet = ("JS_GetPropertyById(cx, &val.toObject(), %s, &temp)" %
+ propId)
+
+ conversionReplacements = {
+ "prop": "(this->%s)" % member.identifier.name,
+ "convert": string.Template(templateBody).substitute(replacements),
+ "propCheck": propCheck,
+ "propGet": propGet
+ }
+ conversion = ("if (isNull) {\n"
+ " found = false;\n"
+ "} else if (!${propCheck}) {\n"
+ " return false;\n"
+ "}\n")
+ if member.defaultValue:
+ conversion += (
+ "if (found) {\n"
+ " if (!${propGet}) {\n"
+ " return false;\n"
+ " }\n"
+ "}\n"
+ "${convert}")
+ else:
+ conversion += (
+ "if (found) {\n"
+ " ${prop}.Construct();\n"
+ " if (!${propGet}) {\n"
+ " return false;\n"
+ " }\n"
+ "${convert}\n"
+ "}")
+ conversionReplacements["convert"] = CGIndenter(
+ CGGeneric(conversionReplacements["convert"])).define()
+
+ return CGGeneric(
+ string.Template(conversion).substitute(conversionReplacements)
+ )
+
+ @staticmethod
+ def makeIdName(name):
+ return name + "_id"
+
+ @staticmethod
+ def getDictionaryDependencies(dictionary):
+ deps = set();
+ if dictionary.parent:
+ deps.add(dictionary.parent)
+ for member in dictionary.members:
+ if member.type.isDictionary():
+ deps.add(member.type.unroll().inner)
+ return deps
+
+
+class CGRegisterProtos(CGAbstractMethod):
+ def __init__(self, config):
+ CGAbstractMethod.__init__(self, None, 'Register', 'void',
+ [Argument('nsScriptNameSpaceManager*', 'aNameSpaceManager')])
+ self.config = config
+
+ def _defineMacro(self):
+ return """
+#define REGISTER_PROTO(_dom_class, _pref_check) \\
+ aNameSpaceManager->RegisterDefineDOMInterface(NS_LITERAL_STRING(#_dom_class), _dom_class##Binding::DefineDOMInterface, _pref_check);\n\n"""
+ def _undefineMacro(self):
+ return "\n#undef REGISTER_PROTO"
+ def _registerProtos(self):
+ def getPrefCheck(desc):
+ if desc.interface.getExtendedAttribute("PrefControlled") is None:
+ return "nullptr"
+ return "%sBinding::PrefEnabled" % desc.name
+ lines = ["REGISTER_PROTO(%s, %s);" % (desc.name, getPrefCheck(desc))
+ for desc in self.config.getDescriptors(hasInterfaceObject=True,
+ isExternal=False,
+ workers=False,
+ register=True)]
+ return '\n'.join(lines) + '\n'
+ def definition_body(self):
+ return self._defineMacro() + self._registerProtos() + self._undefineMacro()
+
+class CGBindingRoot(CGThing):
+ """
+ Root codegen class for binding generation. Instantiate the class, and call
+ declare or define to generate header or cpp code (respectively).
+ """
+ def __init__(self, config, prefix, webIDLFile):
+ descriptors = config.getDescriptors(webIDLFile=webIDLFile,
+ hasInterfaceOrInterfacePrototypeObject=True)
+ dictionaries = config.getDictionaries(webIDLFile)
+
+ forwardDeclares = [CGClassForwardDeclare('XPCWrappedNativeScope')]
+
+ descriptorsForForwardDeclaration = list(descriptors)
+ for dictionary in dictionaries:
+ curDict = dictionary
+ ifacemembers = []
+ while curDict:
+ ifacemembers.extend([m.type.unroll().inner for m
+ in curDict.members
+ if m.type.unroll().isInterface()])
+ curDict = curDict.parent
+ # Put in all the non-worker descriptors
+ descriptorsForForwardDeclaration.extend(
+ [config.getDescriptor(iface.identifier.name, False) for
+ iface in ifacemembers])
+ # And now the worker ones. But these may not exist, so we
+ # have to be more careful.
+ for iface in ifacemembers:
+ try:
+ descriptorsForForwardDeclaration.append(
+ config.getDescriptor(iface.identifier.name, True))
+ except NoSuchDescriptorError:
+ # just move along
+ pass
+
+ for x in descriptorsForForwardDeclaration:
+ nativeType = x.nativeType
+ components = x.nativeType.split('::')
+ className = components[-1]
+ # JSObject is a struct, not a class
+ declare = CGClassForwardDeclare(className, className is "JSObject")
+ if len(components) > 1:
+ declare = CGNamespace.build(components[:-1],
+ CGWrapper(declare, declarePre='\n',
+ declarePost='\n'),
+ declareOnly=True)
+ forwardDeclares.append(CGWrapper(declare, declarePost='\n'))
+
+ forwardDeclares = CGList(forwardDeclares)
+
+ descriptorsWithPrototype = filter(lambda d: d.interface.hasInterfacePrototypeObject(),
+ descriptors)
+ traitsClasses = [CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype]
+
+ # We must have a 1:1 mapping here, skip for prototypes that have more
+ # than one concrete class implementation.
+ traitsClasses.extend([CGPrototypeIDMapClass(d) for d in descriptorsWithPrototype
+ if d.uniqueImplementation])
+
+ # Wrap all of that in our namespaces.
+ if len(traitsClasses) > 0:
+ traitsClasses = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(CGList(traitsClasses),
+ declarePre='\n'),
+ declareOnly=True)
+ traitsClasses = CGWrapper(traitsClasses, declarePost='\n')
+ else:
+ traitsClasses = None
+
+ # Do codegen for all the enums
+ def makeEnum(e):
+ return CGNamespace.build([e.identifier.name + "Values"],
+ CGEnum(e))
+ def makeEnumTypedef(e):
+ return CGGeneric(declare=("typedef %sValues::valuelist %s;\n" %
+ (e.identifier.name, e.identifier.name)))
+ cgthings = [ fun(e) for e in config.getEnums(webIDLFile)
+ for fun in [makeEnum, makeEnumTypedef] ]
+
+ # Do codegen for all the dictionaries. We have to be a bit careful
+ # here, because we have to generate these in order from least derived
+ # to most derived so that class inheritance works out. We also have to
+ # generate members before the dictionary that contains them.
+ #
+ # XXXbz this will fail if we have two webidl files A and B such that A
+ # declares a dictionary which inherits from a dictionary in B and B
+ # declares a dictionary (possibly a different one!) that inherits from a
+ # dictionary in A. The good news is that I expect this to never happen.
+ reSortedDictionaries = []
+ dictionaries = set(dictionaries)
+ while len(dictionaries) != 0:
+ # Find the dictionaries that don't depend on anything else anymore
+ # and move them over.
+ toMove = [d for d in dictionaries if
+ len(CGDictionary.getDictionaryDependencies(d) &
+ dictionaries) == 0]
+ if len(toMove) == 0:
+ raise TypeError("Loop in dictionary dependency graph")
+ dictionaries = dictionaries - set(toMove)
+ reSortedDictionaries.extend(toMove)
+
+ dictionaries = reSortedDictionaries
+ cgthings.extend([CGDictionary(d, config.getDescriptorProvider(True))
+ for d in dictionaries])
+ cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False))
+ for d in dictionaries])
+
+ # Do codegen for all the descriptors
+ cgthings.extend([CGDescriptor(x) for x in descriptors])
+
+ # And make sure we have the right number of newlines at the end
+ curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(curr, pre="\n"))
+
+ curr = CGList([forwardDeclares,
+ CGWrapper(CGGeneric("using namespace mozilla::dom;"),
+ defineOnly=True),
+ traitsClasses, curr],
+ "\n")
+
+ # Add header includes.
+ curr = CGHeaders(descriptors,
+ dictionaries,
+ ['mozilla/dom/BindingUtils.h',
+ 'mozilla/dom/DOMJSClass.h',
+ 'mozilla/dom/DOMJSProxyHandler.h'],
+ ['mozilla/dom/Nullable.h',
+ 'PrimitiveConversions.h',
+ 'XPCQuickStubs.h',
+ 'nsDOMQS.h',
+ 'AccessCheck.h',
+ 'WorkerPrivate.h',
+ 'nsContentUtils.h',
+ 'mozilla/Preferences.h',
+ # Have to include nsDOMQS.h to get fast arg unwrapping
+ # for old-binding things with castability.
+ 'nsDOMQS.h'
+ ],
+ curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard(prefix, curr)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Store the final result.
+ self.root = curr
+
+ def declare(self):
+ return stripTrailingWhitespace(self.root.declare())
+ def define(self):
+ return stripTrailingWhitespace(self.root.define())
+
+
+class GlobalGenRoots():
+ """
+ Roots for global codegen.
+
+ To generate code, call the method associated with the target, and then
+ call the appropriate define/declare method.
+ """
+
+ @staticmethod
+ def PrototypeList(config):
+
+ # Prototype ID enum.
+ protos = [d.name for d in config.getDescriptors(hasInterfacePrototypeObject=True)]
+ idEnum = CGNamespacedEnum('id', 'ID', protos, [0])
+ idEnum = CGList([idEnum])
+ idEnum.append(CGGeneric(declare="const unsigned MaxProtoChainLength = " +
+ str(config.maxProtoChainLength) + ";\n\n"))
+
+ # Wrap all of that in our namespaces.
+ idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'],
+ CGWrapper(idEnum, pre='\n'))
+ idEnum = CGWrapper(idEnum, post='\n')
+
+ curr = CGList([idEnum])
+
+ # Constructor ID enum.
+ constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True,
+ hasInterfacePrototypeObject=False)]
+ idEnum = CGNamespacedEnum('id', 'ID', constructors, [0])
+
+ # Wrap all of that in our namespaces.
+ idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'],
+ CGWrapper(idEnum, pre='\n'))
+ idEnum = CGWrapper(idEnum, post='\n')
+
+ curr.append(idEnum)
+
+ traitsDecl = CGGeneric(declare="""
+template <prototypes::ID PrototypeID>
+struct PrototypeTraits;
+
+template <class ConcreteClass>
+struct PrototypeIDMap;
+""")
+
+ traitsDecl = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(traitsDecl, post='\n'))
+
+ curr.append(traitsDecl)
+
+ # Add include guards.
+ curr = CGIncludeGuard('PrototypeList', curr)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def RegisterBindings(config):
+
+ # TODO - Generate the methods we want
+ curr = CGRegisterProtos(config)
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(curr, post='\n'))
+ curr = CGWrapper(curr, post='\n')
+
+ # Add the includes
+ defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
+ for desc in config.getDescriptors(hasInterfaceObject=True,
+ workers=False,
+ register=True)]
+ defineIncludes.append('nsScriptNameSpaceManager.h')
+ curr = CGHeaders([], [], [], defineIncludes, curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('RegisterBindings', curr)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def UnionTypes(config):
+
+ (includes, declarations, unions) = UnionTypes(config.getDescriptors())
+ includes.add("mozilla/dom/BindingUtils.h")
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'], unions)
+
+ curr = CGWrapper(curr, post='\n')
+
+ namespaces = []
+ stack = [CGList([])]
+ for (clazz, isStruct) in SortedTuples(declarations):
+ elements = clazz.split("::")
+ clazz = CGClassForwardDeclare(elements.pop(), isStruct=isStruct)
+ i = 0
+ if len(elements) > 0:
+ common = min(len(namespaces), len(elements))
+ while i < common and namespaces[i] == elements[i]:
+ i += 1
+
+ # pop all the namespaces that should be closed
+ namespaces = namespaces[:i]
+
+ # add all the namespaces that should be opened
+ for j, namespace in enumerate(elements[i:]):
+ namespaces.append(namespace)
+ # every CGNamespace that we add holds a CGList
+ list = CGList([])
+ # add the new namespace to the list on top of the stack
+ stack[i + j].append(CGNamespace(namespace, list))
+ # set the top of the namespace stack to the list of the new
+ # namespace
+ stack[i + j + 1:] = [list]
+
+ stack[len(elements)].append(clazz)
+
+ curr = CGList([stack[0], curr], "\n")
+
+ curr = CGHeaders([], [], includes, [], curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('UnionTypes', curr)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def UnionConversions(config):
+
+ unions = UnionConversions(config.getDescriptors())
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'], unions)
+
+ curr = CGWrapper(curr, post='\n')
+
+ curr = CGHeaders([], [], ["nsDebug.h", "mozilla/dom/UnionTypes.h", "nsDOMQS.h"], [], curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('UnionConversions', curr)
+
+ # Done.
+ return curr