/dom/bindings/Codegen.py
Python | 3057 lines | 2926 code | 50 blank | 81 comment | 60 complexity | 9eac3658c943bf5d8497682d9eb5c60a MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, BSD-2-Clause, LGPL-3.0, AGPL-1.0, MPL-2.0-no-copyleft-exception, GPL-2.0, JSON, Apache-2.0, 0BSD, MIT
- # 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
- from WebIDL import *
- 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 = '_Construct'
- 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):
- return " extern const NativePropertyHooks NativeHooks;\n"
- def define(self):
- parent = self.descriptor.interface.parent
- parentHooks = ("&" + toBindingNamespace(parent.identifier.name) + "::NativeHooks"
- if parent else 'NULL')
- return """
- const NativePropertyHooks NativeHooks = { ResolveProperty, EnumerateProperties, %s };
- """ % parentHooks
- 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'
- protoList = ['prototypes::id::' + proto for proto in self.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.
- while len(protoList) < self.descriptor.config.maxProtoChainLength:
- protoList.append('prototypes::id::_ID_Count')
- prototypeChainString = ', '.join(protoList)
- 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, /* construct */
- NULL, /* hasInstance */
- %s, /* trace */
- JSCLASS_NO_INTERNAL_MEMBERS
- },
- { %s },
- -1, %s, DOM_OBJECT_SLOT,
- &NativeHooks
- };
- """ % (self.descriptor.interface.identifier.name,
- ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.workers else 'JS_PropertyStub',
- FINALIZE_HOOK_NAME, traceHook, prototypeChainString,
- str(self.descriptor.nativeIsISupports).lower())
- 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 = {
- "%s Prototype", 0,
- 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, /* construct */
- NULL, /* hasInstance */
- 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, /* construct */
- %s, /* hasInstance */
- NULL, /* trace */
- JSCLASS_NO_INTERNAL_MEMBERS
- };
- """ % (ctorname, ctorname, hasinstance)
- 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 declare(self):
- return self.joiner.join([child.declare() for child in self.children
- if child is not None])
- def define(self):
- return self.joiner.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):
- CGThing.__init__(self)
- self.child = child
- self.indent = " " * indentLevel
- 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 "":
- return re.sub(lineStartDetector, self.indent, defn)
- else:
- return ""
- 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 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 child
- return CGNamespace(namespaces[0], CGNamespace.build(namespaces[1:],
- child),
- declareOnly=declareOnly)
- class CGIncludeGuard(CGWrapper):
- """
- Generates include guards for a header.
- """
- def __init__(self, prefix, child):
- """|prefix| is the filename without the extension."""
- define = 'mozilla_dom_%s_h__' % prefix
- CGWrapper.__init__(self, child,
- declarePre='#ifndef %s\n#define %s\n\n' % (define, define),
- declarePost='\n#endif // %s\n' % define)
- class CGHeaders(CGWrapper):
- """
- Generates the appropriate include statements.
- """
- def __init__(self, descriptors, 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.getInterfaceFilename(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:
- members = [m for m in d.interface.members]
- 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])
- attrs = [a for a in members if a.isAttr()]
- types.extend([a.type for a in attrs])
- for t in types:
- if t.unroll().isInterface():
- if t.unroll().isArrayBuffer():
- bindingHeaders.add("jsfriendapi.h")
- else:
- typeDesc = d.getDescriptor(t.unroll().inner.identifier.name)
- if typeDesc is not None:
- implementationIncludes.add(typeDesc.headerFile)
- bindingHeaders.add(self.getInterfaceFilename(typeDesc.interface))
- # 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(declareIncludes),
- definePre=_includeString(sorted(set(defineIncludes) |
- bindingIncludes |
- bindingHeaders |
- implementationIncludes)))
- @staticmethod
- def getInterfaceFilename(interface):
- basename = os.path.basename(interface.filename())
- return 'mozilla/dom/' + \
- basename.replace('.webidl', 'Binding.h')
- 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.
- static should be True to generate a static method, which only has
- a definition.
- """
- def __init__(self, descriptor, name, returnType, args, inline=False, static=False):
- CGThing.__init__(self)
- self.descriptor = descriptor
- self.name = name
- self.returnType = returnType
- self.args = args
- self.inline = inline
- self.static = static
- def _argstring(self):
- return ', '.join([str(a) for a in self.args])
- def _decorators(self):
- decorators = []
- if self.inline:
- decorators.append('inline')
- if self.static:
- decorators.append('static')
- decorators.append(self.returnType)
- return ' '.join(decorators)
- def declare(self):
- if self.inline:
- return self._define()
- return "\n %s %s(%s);\n" % (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):
- maybeNewline = " " if self.inline else "\n"
- return "\n%s%s%s(%s)\n{" % (self._decorators(), maybeNewline,
- 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 """
- MOZ_ASSERT(js::GetObjectJSClass(obj) == Class.ToJSClass());
- %s* self = UnwrapDOMObject<%s>(obj, Class.ToJSClass());
- """ % (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('JSObject*', 'obj'),
- Argument('jsid', 'id'), Argument('jsval*', 'vp')]
- CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME,
- 'JSBool', args)
- def generate_code(self):
- return """
- JSCompartment* compartment = js::GetObjectCompartment(obj);
- xpc::CompartmentPrivate* priv =
- static_cast<xpc::CompartmentPrivate*>(JS_GetCompartmentPrivate(compartment));
- if (!priv->RegisterDOMExpandoObject(obj)) {
- return false;
- }
- self->SetPreservingWrapper(true);
- return true;"""
- 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):
- if self.descriptor.customFinalize:
- return """ if (self) {
- self->%s(%s);
- }""" % (self.name, self.args[0].name)
- if self.descriptor.workers:
- release = "self->Release();"
- else:
- assert self.descriptor.nativeIsISupports
- release = """
- XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
- if (rt) {
- rt->DeferredRelease(NativeToSupports(self));
- } else {
- NS_RELEASE(self);
- }"""
- return """
- self->ClearWrapper();
- %s""" % (release)
- 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)));
- """
- preArgs = ""
- 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._ctor.identifier.name)
- nativeName = self.descriptor.binaryNames.get(name, nativeName)
- 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('JSObject*', 'obj'),
- Argument('const jsval*', 'v'), 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 (!v->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;
- }
- obj = &protov.toObject();
- JSObject* instance = &v->toObject();
- JSObject* proto = JS_GetPrototype(instance);
- while (proto) {
- if (proto == obj) {
- *bp = true;
- return true;
- }
- proto = JS_GetPrototype(proto);
- }
- 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
- 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 __str__(self):
- str = self.generateArray(self.regular, self.variableName(False))
- if self.hasChromeOnly():
- str += self.generateArray(self.chrome, self.variableName(True))
- return str
- # 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)
- methods = [m for m in descriptor.interface.members if
- m.isMethod() and m.isStatic() == static]
- self.chrome = [{"name": m.identifier.name,
- "length": methodLength(m),
- "flags": "JSPROP_ENUMERATE"} for m in methods]
- self.regular = [{"name": m.identifier.name,
- "length": methodLength(m),
- "flags": "JSPROP_ENUMERATE"}
- for m in methods if not isChromeOnly(m)]
- if not descriptor.interface.parent and not static and not descriptor.workers:
- self.chrome.append({"name": 'QueryInterface',
- "length": 1,
- "flags": "0"})
- self.regular.append({"name": 'QueryInterface',
- "length": 1,
- "flags": "0"})
- if static:
- if not descriptor.interface.hasInterfaceObject():
- # static methods go on the interface object
- assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
- else:
- if not descriptor.interface.hasInterfacePrototypeObject():
- # non-static methods go on the interface prototype object
- assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
- @staticmethod
- def generateArray(array, name):
- if len(array) == 0:
- return ""
- funcdecls = [' JS_FN("%s", %s, %s, %s)' %
- (m["name"], m["name"], m["length"], m["flags"])
- for m in array]
- # And add our JS_FS_END
- funcdecls.append(' JS_FS_END')
- return ("static JSFunctionSpec %s[] = {\n" +
- ',\n'.join(funcdecls) + "\n" +
- "};\n\n" +
- "static jsid %s_ids[%i] = { JSID_VOID };\n\n") % (name, name, len(array))
- 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)]
- @staticmethod
- def generateArray(array, name):
- if len(array) == 0:
- return ""
- def flags(attr):
- flags = "JSPROP_SHARED | JSPROP_ENUMERATE"
- if generateNativeAccessors:
- flags = "JSPROP_NATIVE_ACCESSORS | " + flags
- elif attr.readonly:
- return "JSPROP_READONLY | " + flags
- return flags
- def getter(attr):
- return "get_" + attr.identifier.name
- def setter(attr):
- if attr.readonly:
- return "NULL"
- return "set_" + attr.identifier.name
- attrdecls = [' { "%s", 0, %s, (JSPropertyOp)%s, (JSStrictPropertyOp)%s }' %
- (attr.identifier.name, flags(attr), getter(attr),
- setter(attr)) for attr in array]
- attrdecls.append(' { 0, 0, 0, 0, 0 }')
- return ("static JSPropertySpec %s[] = {\n" +
- ',\n'.join(attrdecls) + "\n" +
- "};\n\n" +
- "static jsid %s_ids[%i] = { JSID_VOID };\n\n") % (name, name, len(array))
- 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)]
- @staticmethod
- def generateArray(array, name):
- if len(array) == 0:
- return ""
- constdecls = [' { "%s", %s }' %
- (const.identifier.name,
- convertConstIDLValueToJSVal(const.value))
- for const in array]
- constdecls.append(' { 0, JSVAL_VOID }')
- return ("static ConstantSpec %s[] = {\n" +
- ',\n'.join(constdecls) + "\n" +
- "};\n\n" +
- "static jsid %s_ids[%i] = { JSID_VOID };\n\n") % (name, name, len(array))
- 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" ]
- 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 = []
- for var in self.properties.arrayNames():
- props = getattr(self.properties, var)
- if props.hasNonChromeOnly():
- idsToInit.append(props.variableName(False))
- if props.hasChromeOnly() and not self.descriptor.workers:
- idsToInit.append(props.variableName(True))
- 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
-
- getParentProto = ("JSObject* parentProto = %s;\n"
- "if (!parentProto) {\n"
- " return NULL;\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 = "ThrowingConstructorWorkers" if self.descriptor.workers else "ThrowingConstructor"
- constructArgs = 0
- call = CGGeneric(("return dom::CreateInterfaceObjects(aCx, aGlobal, aReceiver, parentProto,\n"
- " %s, %s, %s, %d,\n"
- " %%(methods)s, %%(attrs)s, %%(consts)s, %%(staticMethods)s,\n"
- " %s);") % (
- "&PrototypeClass" if needInterfacePrototypeObject else "NULL",
- "&InterfaceObjectClass" if needInterfaceObjectClass else "NULL",
- constructHook if needConstructor else "NULL",
- constructArgs,
- '"' + 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))"
- accessCheck = "if (" + accessCheck + ") {\n"
- chrome = CGWrapper(CGGeneric((CGIndenter(call).define() % self.properties.variableNames(True))),
- pre=accessCheck, post="\n}")
- else:
- chrome = None
- functionBody = CGList(
- [CGGeneric(getParentProto), initIds, chrome,
- CGGeneric(call.define() % 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 = ""
- return """
- {
- XPCWrappedNativeScope* scope =
- XPCWrappedNativeScope::FindInJSObjectScope(aCx, %s);
- if (!scope) {
- return %s;
- }
- if (!scope->ExperimentalBindingsEnabled()) {
- %s %s = false;
- return %s;
- }
- }
- """ % (globalName, retval, wrapperCache, varName, retval)
- 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 CGNativeToSupportsMethod(CGAbstractStaticMethod):
- """
- A method to cast our native to an nsISupports. We do it by casting up the
- interface chain in hopes of getting to something that singly-inherits from
- nsISupports.
- """
- def __init__(self, descriptor):
- args = [Argument(descriptor.nativeType + '*', 'aNative')]
- CGAbstractStaticMethod.__init__(self, descriptor, 'NativeToSupports', 'nsISupports*', args)
- def definition_body(self):
- cur = CGGeneric("aNative")
- for proto in reversed(self.descriptor.prototypeChain[:-1]):
- d = self.descriptor.getDescriptor(proto)
- cast = "static_cast<%s*>(\n" % d.nativeType;
- cur = CGWrapper(CGIndenter(cur), pre=cast, post=")")
- return CGIndenter(CGWrapper(cur, pre="return ", post=";")).define();
- 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(descriptor.nativeType + '*', 'aObject'),
- 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;
- }
- JSAutoEnterCompartment ac;
- if (js::GetGlobalForObjectCrossCompartment(parent) != aScope) {
- if (!ac.enter(aCx, parent)) {
- return NULL;
- }
- }
- JSObject* global = JS_GetGlobalForObject(aCx, parent);
- %s
- JSObject* proto = GetProtoObject(aCx, global, global);
- if (!proto) {
- return NULL;
- }
- JSObject* obj = JS_NewObject(aCx, &Class.mBase, proto, parent);
- if (!obj) {
- return NULL;
- }
- js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
- NS_ADDREF(aObject);
- aObject->SetWrapper(obj);
- return obj;""" % (CheckPref(self.descriptor, "global", "*aTriedToWrap", "NULL", "aObject"))
- 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'
- }
- 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" : codeOnFailure }
- def __str__(self):
- return string.Template(
- """ {
- nsresult rv = UnwrapObject<${protoID}>(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" : codeOnFailure }
- 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}
- }
- ${target} = do_QueryObject(wrappedJS.get());
- if (!${target}) {
- ${codeOnFailure}
- }""").substitute(self.substitution)
- def getArgumentConversionTemplate(type, descriptor):
- if type.isSequence() or type.isArray():
- raise TypeError("Can't handle sequence or array arguments yet")
- if descriptor is not None:
- assert(type.isInterface())
- # This is an interface that we implement as a concrete class
- # or an XPCOM interface.
- argIsPointer = type.nullable() or type.unroll().inner.isExternal()
- if argIsPointer:
- nameSuffix = ""
- else:
- nameSuffix = "_ptr"
- # If we're going to QI, we want an nsCOMPtr. But note that XPConnect
- # unwrapping may or may not QI, and we don't know whether it will. So
- # we use a raw pointer for the isExternal() case, and if a ref is needed
- # it'll be handled by the xpc_qsSelfRef we put on the stack later.
- if descriptor.castable or type.unroll().inner.isExternal() or descriptor.workers:
- declType = " ${typeName}*"
- else:
- declType = " nsCOMPtr<${typeName}>"
- template = declType + " ${name}%s;\n" % nameSuffix
- # We have to be very careful here to put anything that might need to
- # hold references across the C++ call in |template| and not
- # |templateBody|, since things in |templateBody| will go out of scope
- # before the call happens.
- templateBody = " if (${argVal}.isObject()) {"
- if descriptor.castable:
- templateBody += str(FailureFatalCastableObjectUnwrapper(
- descriptor,
- "&${argVal}.toObject()",
- "${name}"+nameSuffix)).replace("\n", "\n ") + "\n"
- elif descriptor.interface.isCallback():
- templateBody += str(CallbackObjectUnwrapper(
- descriptor,
- "&${argVal}.toObject()",
- "${name}"+nameSuffix)) + "\n"
- elif descriptor.workers:
- templateBody += """
- ${name}%s = &${argVal}.toObject();
- MOZ_ASSERT(${name}%s);
- """ % (nameSuffix, nameSuffix)
- else:
- template += " xpc_qsSelfRef tmpRef_${name};\n"
- template += " jsval tmpVal_${name} = ${argVal};\n"
- templateBody += """
- ${typeName}* tmp;
- if (NS_FAILED(xpc_qsUnwrapArg<${typeName}>(cx, ${argVal}, &tmp, &tmpRef_${name}.ptr,
- &tmpVal_${name}))) {
- return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
- }
- MOZ_ASSERT(tmp);
- ${name}%s = tmp;
- """ % (toStringBool(not descriptor.workers), nameSuffix)
- if type.nullable():
- templateBody += (
- " } else if (${argVal}.isNullOrUndefined()) {\n"
- " ${name}%s = NULL;\n" % nameSuffix)
- templateBody += (
- " } else {\n"
- " return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
- " }\n" % toStringBool(not descriptor.workers))
- template += templateBody
- if not argIsPointer:
- template += " ${typeName} &${name} = *${name}_ptr;\n"
-
- return template
- if type.isArrayBuffer():
- template = (
- " JSObject* ${name};\n"
- " if (${argVal}.isObject() && JS_IsArrayBufferObject(&${argVal}.toObject(), cx)) {\n"
- " ${name} = &${argVal}.toObject();\n"
- " }")
- if type.nullable():
- template += (
- " else if (${argVal}.isNullOrUndefined()) {\n"
- " ${name} = NULL;\n"
- " }")
- template += (
- # XXXbz We don't know whether we're on workers, so play it safe
- " else {\n"
- " return Throw<false>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
- " }")
- return template
- if type.isInterface():
- raise TypeError("Interface type with no descriptor: " + type)
- if type.isString():
- # XXXbz Need to figure out string behavior based on extended args? Also, how to
- # detect them?
- # For nullable strings that are not otherwise annotated, null
- # and undefined become null strings.
- if type.nullable():
- nullBehavior = "eNull"
- undefinedBehavior = "eNull"
- else:
- nullBehavior = "eStringify"
- undefinedBehavior = "eStringify"
- return (
- " const xpc_qsDOMString ${name}(cx, ${argVal}, ${argPtr},\n"
- " xpc_qsDOMString::%s,\n"
- " xpc_qsDOMString::%s);\n"
- " if (!${name}.IsValid()) {\n"
- " return false;\n"
- " }\n" % (nullBehavior, undefinedBehavior))
- if type.isEnum():
- if type.nullable():
- raise TypeError("We don't support nullable enumerated arguments "
- "yet")
- enum = type.inner.identifier.name
- return (
- " %(enumtype)s ${name};\n"
- " {\n"
- " bool ok;\n"
- " ${name} = static_cast<%(enumtype)s>(FindEnumStringIndex(cx, ${argVal}, %(values)s, &ok));\n"
- " if (!ok) {\n"
- " return false;\n"
- " }\n"
- " }" % { "enumtype" : enum,
- "values" : enum + "Values::strings" })
- if type.isCallback():
- # XXXbz we're going to assume that callback types are always
- # nullable and always have [TreatNonCallableAsNull] for now.
- return (
- " JSObject* ${name};\n"
- " if (${argVal}.isObject() && JS_ObjectIsCallable(cx, &${argVal}.toObject())) {\n"
- " ${name} = &${argVal}.toObject();\n"
- " } else {\n"
- " ${name} = NULL;\n"
- " }\n")
- if type.isAny():
- return " JS::Value ${name} = ${argVal};\n"
- if not type.isPrimitive():
- raise TypeError("Need conversion for argument type '%s'" % type)
- tag = type.tag()
- replacements = dict()
- if type.nullable():
- replacements["declareArg"] = (
- " Nullable<${typeName}> ${name};\n"
- " if (${argVal}.isNullOrUndefined()) {\n"
- " ${name}.SetNull();\n"
- " } else"
- )
- replacements["finalValueSetter"] = "${name}.SetValue"
- else:
- replacements["declareArg"] = " ${typeName} ${name};\n"
- replacements["finalValueSetter"] = "${name} = "
- replacements["intermediateCast"] = ""
-
- if tag == IDLType.Tags.bool:
- replacements["jstype"] = "JSBool"
- replacements["converter"] = "JS_ValueToBoolean"
- elif tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
- IDLType.Tags.uint16, IDLType.Tags.int32, IDLType.Tags.uint32]:
- # XXXbz need to add support for [EnforceRange] and [Clamp]
- # The output of JS_ValueToECMAInt32 is determined as follows:
- # 1) The value is converted to a double
- # 2) Anything that's not a finite double returns 0
- # 3) The double is rounded towards zero to the nearest integer
- # 4) The resulting integer is reduced mod 2^32. The output of this
- # operation is an integer in the range [0, 2^32).
- # 5) If the resulting number is >= 2^31, 2^32 is subtracted from it.
- #
- # The result of all this is a number in the range [-2^31, 2^31)
- #
- # WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types
- # are defined in the same way, except that step 4 uses reduction mod
- # 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5
- # is only done for the signed types.
- #
- # C/C++ define integer conversion semantics to unsigned types as taking
- # your input integer mod (1 + largest value representable in the
- # unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32,
- # converting to the unsigned int of the relevant width will correctly
- # perform step 4; in particular, the 2^32 possibly subtracted in step 5
- # will become 0.
- #
- # Once we have step 4 done, we're just going to assume 2s-complement
- # representation and cast directly to the type we really want.
- #
- # So we can cast directly for all unsigned types and for int32_t; for
- # the smaller-width signed types we need to cast through the
- # corresponding unsigned type.
- replacements["jstype"] = "int32_t"
- replacements["converter"] = "JS::ToInt32"
- if tag is IDLType.Tags.int8:
- replacements["intermediateCast"] = "(uint8_t)"
- elif tag is IDLType.Tags.int16:
- replacements["intermediateCast"] = "(uint16_t)"
- else:
- replacements["intermediateCast"] = ""
- elif tag is IDLType.Tags.int64:
- # XXXbz this may not match what WebIDL says to do in terms of reducing
- # mod 2^64. Should we check?
- replacements["jstype"] = "int64_t"
- replacements["converter"] = "xpc::ValueToInt64"
- elif tag is IDLType.Tags.uint64:
- # XXXbz this may not match what WebIDL says to do in terms of reducing
- # mod 2^64. Should we check?
- replacements["jstype"] = "uint64_t"
- replacements["converter"] = "xpc::ValueToUint64"
- elif tag in [IDLType.Tags.float, IDLType.Tags.double]:
- replacements["jstype"] = "double"
- replacements["converter"] = "JS::ToNumber"
- else:
- raise TypeError("Unknown primitive type '%s'" % type);
- # We substitute the %(name)s things here. Our caller will
- # substitute the ${name} things.
- return (" %(jstype)s ${name}_jstype;\n"
- "%(declareArg)s" # No leading whitespace or newline here, on purpose
- " if (%(converter)s(cx, ${argVal}, &${name}_jstype)) {\n"
- " %(finalValueSetter)s((${typeName})%(intermediateCast)s${name}_jstype);\n"
- " } else {\n"
- " return false;\n"
- " }\n" % replacements)
- 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)
- def convertIDLDefaultValueToJSVal(value):
- if value.type:
- tag = value.type.tag()
- if tag == IDLType.Tags.domstring:
- assert False # Not implemented!
- return convertConstIDLValueToJSVal(value)
- unindenter = re.compile("^ ", re.MULTILINE)
- 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):
- CGThing.__init__(self)
- self.argument = argument
- # XXXbz should optional jsval args get JSVAL_VOID? What about
- # others?
- self.replacementVariables = {
- "index" : index,
- "argc" : argc,
- "argv" : argv,
- "defaultValue" : "JSVAL_VOID",
- "name" : "arg%d" % index
- }
- if argument.optional:
- if argument.defaultValue:
- self.replacementVariables["defaultValue"] = convertIDLDefaultValueToJSVal(argument.defaultValue)
- self.replacementVariables["argVal"] = string.Template(
- "(${index} < ${argc} ? ${argv}[${index}] : ${defaultValue})"
- ).substitute(self.replacementVariables)
- self.replacementVariables["argPtr"] = string.Template(
- "(${index} < ${argc} ? &${argv}[${index}] : NULL)"
- ).substitute(self.replacementVariables)
- else:
- self.replacementVariables["argVal"] = string.Template(
- "${argv}[${index}]"
- ).substitute(self.replacementVariables)
- self.replacementVariables["argPtr"] = (
- "&" + self.replacementVariables["argVal"])
- self.descriptor = None
- if argument.type.isPrimitive():
- self.replacementVariables["typeName"] = builtinNames[argument.type.tag()]
- elif argument.type.isInterface() and not argument.type.isArrayBuffer():
- descriptor = descriptorProvider.getDescriptor(
- argument.type.unroll().inner.identifier.name)
- self.descriptor = descriptor
- self.replacementVariables["typeName"] = descriptor.nativeType
- def define(self):
- return string.Template(
- re.sub(unindenter,
- "",
- getArgumentConversionTemplate(self.argument.type,
- self.descriptor))
- ).substitute(self.replacementVariables)
- def getWrapTemplateForTypeImpl(type, result, descriptorProvider,
- resultAlreadyAddRefed):
- if type is None or type.isVoid():
- return """
- ${jsvalRef} = JSVAL_VOID;
- return true;"""
- if type.isSequence() or type.isArray():
- raise TypeError("Can't handle sequence or array return values yet")
- if type.isInterface() and not type.isArrayBuffer():
- descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
- wrappingCode = ("""
- if (!%s) {
- ${jsvalRef} = JSVAL_NULL;
- return true;
- }""" % result) if type.nullable() else ""
- if descriptor.castable and not type.unroll().inner.isExternal():
- wrappingCode += """
- if (WrapNewBindingObject(cx, obj, %s, ${jsvalPtr})) {
- return true;
- }""" % 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.
- wrappingCode += """
- MOZ_ASSERT(JS_IsExceptionPending(cx));
- return false;"""
- else:
- # Try old-style wrapping for bindings which might be preffed off.
- wrappingCode += """
- return HandleNewBindingWrappingFailure(cx, obj, %s, ${jsvalPtr});""" % result
- else:
- if descriptor.notflattened:
- getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
- else:
- getIID = ""
- wrappingCode += """
- return WrapObject(cx, obj, %s, %s${jsvalPtr});""" % (result, getIID)
- return wrappingCode
- if type.isString():
- if type.nullable():
- return """
- return xpc::StringToJsval(cx, %s, ${jsvalPtr});""" % result
- else:
- return """
- return xpc::NonVoidStringToJsval(cx, %s, ${jsvalPtr});""" % result
- 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* result_str = JS_NewStringCopyN(cx, %(strings)s[uint32_t(%(result)s)].value, %(strings)s[uint32_t(%(result)s)].length);
- if (!result_str) {
- return false;
- }
- ${jsvalRef} = JS::StringValue(result_str);
- return true;""" % { "result" : result,
- "strings" : type.inner.identifier.name + "Values::strings" }
- if type.isCallback() and 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.
- return """
- ${jsvalRef} = JS::ObjectOrNullValue(%s);
- return JS_WrapValue(cx, ${jsvalPtr});""" % result
- if type.tag() == IDLType.Tags.any:
- # See comments in WrapNewBindingObject explaining why we need
- # to wrap here.
- return """
- ${jsvalRef} = %s;\n
- return JS_WrapValue(cx, ${jsvalPtr});""" % result
- if not type.isPrimitive():
- raise TypeError("Need to learn to wrap %s" % type)
- if type.nullable():
- return """
- if (%s.IsNull()) {
- ${jsvalRef} = JSVAL_NULL;
- return true;
- }
- %s""" % (result, getWrapTemplateForTypeImpl(type.inner, "%s.Value()" % result,
- descriptorProvider,
- resultAlreadyAddRefed))
-
- tag = type.tag()
-
- if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
- IDLType.Tags.uint16, IDLType.Tags.int32]:
- return """
- ${jsvalRef} = INT_TO_JSVAL(int32_t(%s));
- return true;""" % result
- 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 """
- return JS_NewNumberValue(cx, double(%s), ${jsvalPtr});""" % result
- elif tag == IDLType.Tags.uint32:
- return """
- ${jsvalRef} = UINT_TO_JSVAL(%s);
- return true;""" % result
- elif tag == IDLType.Tags.bool:
- return """
- ${jsvalRef} = BOOLEAN_TO_JSVAL(%s);
- return true;""" % result
- else:
- raise TypeError("Need to learn to wrap primitive: %s" % type)
- def getWrapTemplateForType(type, descriptorProvider, resultAlreadyAddRefed):
- return getWrapTemplateForTypeImpl(type, "result", descriptorProvider,
- resultAlreadyAddRefed)
- class CGCallGenerator(CGThing):
- """
- A class to generate an actual call to a C++ object. Assumes that the C++
- object is stored in a variable named "self".
- """
- def __init__(self, errorReport, argCount, argsPre, returnType,
- resultAlreadyAddRefed, descriptorProvider, nativeMethodName, static):
- CGThing.__init__(self)
- isFallible = errorReport is not None
- args = CGList([CGGeneric("arg" + str(i)) for i in range(argCount)], ", ")
- resultOutParam = returnType is not None and returnType.isString()
- # Return values that go in outparams go here
- if resultOutParam:
- args.append(CGGeneric("result"))
- if isFallible:
- args.append(CGGeneric("rv"))
- if returnType is None or returnType.isVoid():
- # Nothing to declare
- result = None
- elif returnType.isPrimitive() and returnType.tag() in builtinNames:
- result = CGGeneric(builtinNames[returnType.tag()])
- if returnType.nullable():
- result = CGWrapper(result, pre="Nullable<", post=">")
- elif returnType.isString():
- result = CGGeneric("nsString")
- elif returnType.isEnum():
- if returnType.nullable():
- raise TypeError("We don't support nullable enum return values")
- result = CGGeneric(returnType.inner.identifier.name)
- elif returnType.isInterface() and not returnType.isArrayBuffer():
- result = CGGeneric(descriptorProvider.getDescriptor(
- returnType.unroll().inner.identifier.name).nativeType)
- if resultAlreadyAddRefed:
- result = CGWrapper(result, pre="nsRefPtr<", post=">")
- else:
- result = CGWrapper(result, post="*")
- elif returnType.isCallback():
- # XXXbz we're going to assume that callback types are always
- # nullable for now.
- result = CGGeneric("JSObject*")
- elif returnType.tag() is IDLType.Tags.any:
- result = CGGeneric("JS::Value")
- else:
- raise TypeError("Don't know how to declare return value for %s" %
- returnType)
- # Build up our actual call
- self.cgRoot = CGList([], "\n")
- call = CGGeneric(nativeMethodName)
- if static:
- call = CGWrapper(call, pre="%s::" % (descriptorProvider.getDescriptor(
- returnType.unroll().inner.identifier.name).nativeType))
- else:
- call = CGWrapper(call, pre="self->")
- call = CGList([call, CGWrapper(args, pre="(" + argsPre, post=");")])
- if result is not None:
- 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("nsresult rv = NS_OK;"))
- self.cgRoot.append(CGGeneric("if (NS_FAILED(rv)) {"))
- self.cgRoot.append(CGIndenter(CGGeneric(errorReport)))
- self.cgRoot.append(CGGeneric("}"))
- def define(self):
- return self.cgRoot.define()
- 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, extendedAttributes, argConversionStartsAt=0):
- CGThing.__init__(self)
- self.returnType = returnType
- self.descriptor = descriptor
- self.idlNode = idlNode
- self.extendedAttributes = extendedAttributes
- # Default to already_AddRefed on the main thread, raw pointer in workers
- self.resultAlreadyAddRefed = not descriptor.workers and not 'resultNotAddRefed' in self.extendedAttributes
- self.argsPre = "cx, " if 'implicitJSContext' in self.extendedAttributes else ""
- self.argsPre += argsPre
- 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) for
- i in range(argConversionStartsAt, self.argCount)])
- cgThings.append(CGCallGenerator(
- self.getErrorReport() if self.isFallible() else None,
- self.argCount, self.argsPre, returnType,
- self.resultAlreadyAddRefed, 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 isFallible(self):
- return not 'infallible' in self.extendedAttributes
- def wrap_return_value(self):
- resultTemplateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp'}
- return string.Template(
- re.sub(unindenter,
- "",
- getWrapTemplateForType(self.returnType, self.descriptor,
- self.resultAlreadyAddRefed))
- ).substitute(resultTemplateValues)
- def getErrorReport(self):
- return '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() + 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,
- extendedAttributes):
- CGThing.__init__(self)
- 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 maxSigLength(signatures):
- return max([len(s[1]) for s in signatures])
- def signaturesForArgCount(i, signatures):
- return filter(
- lambda s: len(s[1]) == i or (len(s[1]) > i and
- s[1][i].optional),
- signatures)
- def findDistinguishingIndex(argCount, signatures):
- def isValidDistinguishingIndex(idx, signatures):
- for firstSigIndex in range(0, len(signatures)):
- for secondSigIndex in range(0, firstSigIndex):
- firstType = signatures[firstSigIndex][1][idx].type
- secondType = signatures[secondSigIndex][1][idx].type
- if not firstType.isDistinguishableFrom(secondType):
- return False
- return True
- for idx in range(0, argCount):
- if isValidDistinguishingIndex(idx, signatures):
- return idx
- return -1
- def getPerSignatureCall(signature, argConversionStartsAt=0):
- return CGPerSignatureCall(signature[0], argsPre, signature[1],
- nativeMethodName, static, descriptor,
- method, extendedAttributes,
- 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:
- self.cgRoot.prepend(
- CGWrapper(
- CGIndenter(
- CGGeneric(
- "if (argc < %d) {\n"
- " return Throw<%s>(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n"
- "}" % (requiredArgs,
- toStringBool(not descriptor.workers)))
- ),
- pre="\n", post="\n")
- )
- return
- # Need to find the right overload
- maxSigArgs = maxSigLength(signatures)
- allowedArgCounts = [ i for i in range(0, maxSigArgs+1)
- if len(signaturesForArgCount(i, signatures)) != 0 ]
- argCountCases = []
- for argCount in allowedArgCounts:
- possibleSignatures = signaturesForArgCount(argCount, signatures)
- 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(signaturesForArgCount(argCount+1, signatures)) == 1):
- argCountCases.append(
- CGCase(str(argCount), None, True))
- else:
- argCountCases.append(
- CGCase(str(argCount), getPerSignatureCall(signature)))
- continue
- distinguishingIndex = findDistinguishingIndex(argCount,
- possibleSignatures)
- if distinguishingIndex == -1:
- raise TypeError(("Signatures with %s arguments for " +
- descriptor.interface.identifier.name + "." +
- method.identifier.name +
- " are not distinguishable") % argCount)
- for idx in range(0, distinguishingIndex):
- firstSigType = possibleSignatures[0][1][idx].type
- for sigIdx in range(1, len(possibleSignatures)):
- if possibleSignatures[sigIdx][1][idx].type != firstSigType:
- raise TypeError(("Signatures with %d arguments for " +
- descriptor.interface.identifier.name +
- "." + method.identifier.name +
- " have different types at index %d" +
- " which is before distinguishing" +
- " index %d") % (argCount,
- idx,
- distinguishingIndex))
- # 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())
- # Now check for distinguishingArg being a platform object.
- # We can actually check separately for array buffers and
- # other things.
- # XXXbz Do we need to worry about security
- # wrappers around the array buffer?
- pickFirstSignature("%s.isObject() && JS_IsArrayBufferObject(&%s.toObject(), cx)" %
- (distinguishingArg, distinguishingArg),
- lambda s: (s[1][distinguishingIndex].type.isArrayBuffer() or
- s[1][distinguishingIndex].type.isObject()))
-
- interfacesSigs = [
- s for s in possibleSignatures
- if (s[1][distinguishingIndex].type.isObject() or
- (s[1][distinguishingIndex].type.isInterface() and
- not s[1][distinguishingIndex].type.isArrayBuffer() and
- not s[1][distinguishingIndex].type.isCallback())) ]
- # There might be more than one of these; we need to check
- # which ones we unwrap to.
-
- if len(interfacesSigs) > 0:
- caseBody.append(CGGeneric("if (%s.isObject() &&\n"
- " IsPlatformObject(cx, &%s.toObject())) {" %
- (distinguishingArg, distinguishingArg)))
- for sig in interfacesSigs:
- caseBody.append(CGIndenter(CGGeneric("do {")));
- type = sig[1][distinguishingIndex].type
-
- # XXXbz this duplicates some argument-unwrapping code!
- interfaceDesc = descriptor.getDescriptor(
- type.unroll().inner.identifier.name)
- argIsPointer = (type.nullable() or
- type.unroll().inner.isExternal())
- if argIsPointer:
- nameSuffix = ""
- else:
- nameSuffix = "_ptr"
- if (interfaceDesc.castable or
- type.unroll().inner.isExternal() or
- interfaceDesc.workers):
- declType = " ${typeName}*"
- else:
- declType = " nsCOMPtr<${typeName}>"
- template = declType + " ${name}%s;\n" % nameSuffix
- if interfaceDesc.castable:
- template += str(CastableObjectUnwrapper(
- interfaceDesc,
- "&${argVal}.toObject()",
- "${name}"+nameSuffix,
- "break;")) + "\n"
- elif interfaceDesc.workers:
- template += """
- ${name}%s = &${argVal}.toObject();
- MOZ_ASSERT(${name}%s);
- """ % (nameSuffix, nameSuffix)
- else:
- template += " xpc_qsSelfRef tmpRef_${name};\n"
- template += " jsval tmpVal_${name} = ${argVal};\n"
- template += """
- ${typeName}* tmp;
- if (NS_FAILED(xpc_qsUnwrapArg<${typeName}>(cx, ${argVal}, &tmp, &tmpRef_${name}.ptr,
- &tmpVal_${name}))) {
- break;
- }
- MOZ_ASSERT(tmp);
- ${name}%s = tmp;
- """ % nameSuffix
- if not argIsPointer:
- template += " ${typeName} &${name} = *${name}_ptr;\n"
- testCode = string.Template(template).substitute(
- {
- "typeName": interfaceDesc.nativeType,
- "name" : "arg%d" % distinguishingIndex,
- "argVal" : distinguishingArg
- }
- )
- caseBody.append(CGIndenter(CGGeneric(testCode)));
- # 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(CGIndenter(
- getPerSignatureCall(sig, distinguishingIndex + 1))))
- 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.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);" %
- maxSigArgs))
- overloadCGThings.append(
- CGSwitch("argcount",
- argCountCases,
- CGGeneric("return Throw<%s>(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);" %
- toStringBool(not descriptor.workers))))
- 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 CGGetterSetterCall(CGPerSignatureCall):
- """
- A class to generate a native object getter or setter call for a
- particular IDL getter or setter.
- """
- def __init__(self, returnType, arguments, nativeMethodName, descriptor,
- attr, extendedAttributes):
- CGPerSignatureCall.__init__(self, returnType, "", arguments,
- nativeMethodName, False, descriptor, attr,
- extendedAttributes)
- def getArgv(self):
- if generateNativeAccessors:
- return CGPerSignatureCall.getArgv(self)
- return "vp"
- class CGGetterCall(CGGetterSetterCall):
- """
- A class to generate a native object getter call for a particular IDL
- getter.
- """
- def __init__(self, returnType, nativeMethodName, descriptor, attr,
- extendedAttributes):
- CGGetterSetterCall.__init__(self, returnType, [], nativeMethodName,
- descriptor, attr, extendedAttributes)
- def getArgc(self):
- if generateNativeAccessors:
- return CGGetterSetterCall.getArgc()
- return "0"
- def getArgvDecl(self):
- if generateNativeAccessors:
- return CGPerSignatureCall.getArgvDecl(self)
- # We just get our stuff from vp
- return ""
- class FakeArgument():
- def __init__(self, type):
- self.type = type
- self.optional = False
- class CGSetterCall(CGGetterSetterCall):
- """
- A class to generate a native object setter call for a particular IDL
- setter.
- """
- def __init__(self, argType, nativeMethodName, descriptor, attr,
- extendedAttributes):
- CGGetterSetterCall.__init__(self, None, [FakeArgument(argType)],
- nativeMethodName, descriptor, attr,
- extendedAttributes)
- def wrap_return_value(self):
- if generateNativeAccessors:
- return CGGetterSetterCall.wrap_return_value(self)
- # We have no return value
- return "\nreturn true;"
- def getArgc(self):
- if generateNativeAccessors:
- return CGGetterSetterCall.getArgc(self)
- return "1"
- def getArgvDecl(self):
- if generateNativeAccessors:
- return (CGPerSignatureCall.getArgvDecl(self) +
- "jsval undef = JS::UndefinedValue();\n"
- "if (argc == 0) {\n"
- " argv = &undef;\n"
- " argc = 1;\n"
- "}")
- # We just get our stuff from vp
- return ""
- 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, extendedAttributes):
- self.extendedAttributes = extendedAttributes
- CGAbstractStaticMethod.__init__(self, descriptor, name, "JSBool", args)
- def definition_body(self):
- unwrapThis = CGGeneric(
- str(FailureFatalCastableObjectUnwrapper(self.descriptor, "obj", "self")))
- return CGList([ self.getThis(), unwrapThis,
- self.generate_code() ], "\n").define()
- def getThis(self):
- return CGIndenter(
- CGGeneric("JSObject* obj = 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 CGNativeMethod(CGAbstractBindingMethod):
- """
- A class for generating the C++ code for an IDL method..
- """
- def __init__(self, descriptor, method):
- self.method = method
- baseName = method.identifier.name
- args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
- Argument('JS::Value*', 'vp')]
- CGAbstractBindingMethod.__init__(self, descriptor, baseName, args,
- descriptor.getExtendedAttributes(method))
- def generate_code(self):
- name = self.method.identifier.name
- nativeName = self.descriptor.binaryNames.get(name, MakeNativeName(name))
- return CGMethodCall("", nativeName, self.method.isStatic(),
- self.descriptor, self.method,
- self.extendedAttributes)
- class CGNativeGetter(CGAbstractBindingMethod):
- """
- A class for generating the C++ code for an IDL attribute getter.
- """
- def __init__(self, descriptor, attr):
- self.attr = attr
- name = 'get_' + attr.identifier.name
- if generateNativeAccessors:
- args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
- Argument('JS::Value*', 'vp')]
- else:
- args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'),
- Argument('jsid', 'id'), Argument('JS::Value*', 'vp')]
- CGAbstractBindingMethod.__init__(self, descriptor, name, args,
- descriptor.getExtendedAttributes(self.attr, getter=True))
- def getThis(self):
- if generateNativeAccessors:
- return CGAbstractBindingMethod.getThis(self)
- return CGIndenter(
- CGGeneric("%s* self;" % self.descriptor.nativeType))
- def generate_code(self):
- nativeMethodName = "Get" + MakeNativeName(self.attr.identifier.name)
- return CGIndenter(CGGetterCall(self.attr.type, nativeMethodName, self.descriptor,
- self.attr, self.extendedAttributes))
- class CGNativeSetter(CGAbstractBindingMethod):
- """
- A class for generating the C++ code for an IDL attribute setter.
- """
- def __init__(self, descriptor, attr):
- self.attr = attr
- baseName = attr.identifier.name
- name = 'set_' + attr.identifier.name
- if generateNativeAccessors:
- args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
- Argument('JS::Value*', 'vp')]
- else:
- args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'),
- Argument('jsid', 'id'), Argument('JSBool', 'strict'),
- Argument('JS::Value*', 'vp')]
- CGAbstractBindingMethod.__init__(self, descriptor, name, args,
- descriptor.getExtendedAttributes(self.attr, setter=True))
- def getThis(self):
- if generateNativeAccessors:
- return CGAbstractBindingMethod.getThis(self)
- return CGIndenter(
- CGGeneric("%s* self;" % self.descriptor.nativeType))
- def generate_code(self):
- nativeMethodName = "Set" + MakeNativeName(self.attr.identifier.name)
- return CGIndenter(CGSetterCall(self.attr.type, nativeMethodName, self.descriptor,
- self.attr, self.extendedAttributes))
- 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 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()]))
- 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 = ' ' + self.getBody();
- body = body.replace('\n', '\n ').rstrip(' ')
- 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 = ' ' + self.getBody()
- body = body.replace('\n', '\n ').rstrip(' ')
- 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 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 getBody(self):
- assert self.body is not None
- return self.body
- 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 ''
- return '%s %s::%s = %s;\n' % (self.type, cgClass.getNameString(),
- self.name, self.getBody())
- 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=[], methods=[], typedefs = [],
- enums=[], templateArgs=[], templateSpecialization=[],
- isStruct=False, indent=''):
- CGThing.__init__(self)
- self.name = name
- self.bases = bases
- self.members = members
- 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 + ' '
- else:
- result = result + separator + ' '
- declaration = member.declare(cgClass)
- declaration = declaration.replace('\n', '\n ')
- declaration = declaration.rstrip(' ')
- result = result + declaration
- itemCount = itemCount + 1
- lastVisibility = visibility
- return (result, lastVisibility, itemCount)
- order = [(self.enums, ''), (self.typedefs, ''), (self.members, ''),
- (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 = self.indent + memberString
- memberString = memberString.replace('\n', '\n' + self.indent)
- memberString = memberString.rstrip(' ')
- result = result + memberString
- result = result + self.indent + '};\n\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.methods, '\n')]
- result = ''
- itemCount = 0
- for (memberList, separator) in order:
- (memberString, itemCount) = defineMembers(self, memberList,
- itemCount, separator)
- result = result + memberString
- return result
- class CGResolveProperty(CGAbstractMethod):
- def __init__(self, descriptor, properties):
- args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
- Argument('jsid', 'id'), Argument('bool', 'set'),
- Argument('JSPropertyDescriptor*', 'desc')]
- CGAbstractMethod.__init__(self, descriptor, "ResolveProperty", "bool", args)
- self.properties = properties
- def definition_body(self):
- str = ""
- varNames = self.properties.variableNames(True)
- methods = self.properties.methods
- if methods.hasNonChromeOnly() or methods.hasChromeOnly():
- str += """ for (size_t i = 0; i < ArrayLength(%(methods)s_ids); ++i) {
- if (id == %(methods)s_ids[i]) {
- JSFunction *fun = JS_NewFunctionById(cx, %(methods)s[i].call, %(methods)s[i].nargs, 0, wrapper, id);
- if (!fun)
- return false;
- JSObject *funobj = JS_GetFunctionObject(fun);
- desc->value.setObject(*funobj);
- desc->attrs = %(methods)s[i].flags;
- desc->obj = wrapper;
- desc->setter = nsnull;
- desc->getter = nsnull;
- return true;
- }
- }
- """ % varNames
- attrs = self.properties.attrs
- if attrs.hasNonChromeOnly() or attrs.hasChromeOnly():
- str += """ for (size_t i = 0; i < ArrayLength(%(attrs)s_ids); ++i) {
- if (id == %(attrs)s_ids[i]) {
- desc->attrs = %(attrs)s[i].flags;
- desc->obj = wrapper;
- desc->setter = %(attrs)s[i].setter;
- desc->getter = %(attrs)s[i].getter;
- return true;
- }
- }
- """ % varNames
- return str + " return true;"
- class CGEnumerateProperties(CGAbstractMethod):
- def __init__(self, descriptor, properties):
- args = [Argument('JS::AutoIdVector&', 'props')]
- CGAbstractMethod.__init__(self, descriptor, "EnumerateProperties", "bool", args)
- self.properties = properties
- def definition_body(self):
- str = ""
- varNames = self.properties.variableNames(True)
- methods = self.properties.methods
- if methods.hasNonChromeOnly() or methods.hasChromeOnly():
- str += """ for (size_t i = 0; i < sizeof(%(methods)s_ids); ++i) {
- if ((%(methods)s[i].flags & JSPROP_ENUMERATE) &&
- !props.append(%(methods)s_ids[i])) {
- return false;
- }
- }
- """ % varNames
- attrs = self.properties.attrs
- if attrs.hasNonChromeOnly() or attrs.hasChromeOnly():
- str += """ for (size_t i = 0; i < sizeof(%(attrs)s_ids); ++i) {
- if ((%(attrs)s[i].flags & JSPROP_ENUMERATE) &&
- !props.append(%(attrs)s_ids[i])) {
- return false;
- }
- }
- """ % varNames
- return str + " return true;"
- 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 ''
- def stripTrailingWhitespace(text):
- lines = text.splitlines()
- for i in range(len(lines)):
- lines[i] = lines[i].rstrip()
- return '\n'.join(lines)
- class CGDescriptor(CGThing):
- def __init__(self, descriptor):
- CGThing.__init__(self)
- assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
- cgThings = []
- if descriptor.interface.hasInterfacePrototypeObject():
- cgThings.extend([CGNativeMethod(descriptor, m) for m in
- descriptor.interface.members if
- m.isMethod() and not m.isStatic()])
- cgThings.extend([CGNativeGetter(descriptor, a) for a in
- descriptor.interface.members if a.isAttr()])
- cgThings.extend([CGNativeSetter(descriptor, a) for a in
- descriptor.interface.members if
- a.isAttr() and not a.readonly])
- if descriptor.concrete:
- if not descriptor.workers:
- cgThings.append(CGAddPropertyHook(descriptor))
- # Always have a finalize hook, regardless of whether the class wants a
- # custom hook.
- if descriptor.nativeIsISupports:
- cgThings.append(CGNativeToSupportsMethod(descriptor))
- 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.concrete or descriptor.interface.hasInterfacePrototypeObject():
- cgThings.append(CGNativePropertyHooks(descriptor))
- if descriptor.concrete:
- cgThings.append(CGDOMJSClass(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(CGIndenter(CGGetProtoObjectMethod(descriptor)))
- else:
- cgThings.append(CGIndenter(CGGetConstructorObjectMethod(descriptor)))
- if descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject():
- cgThings.append(CGResolveProperty(descriptor, properties))
- cgThings.append(CGEnumerateProperties(descriptor, properties))
- if descriptor.interface.hasInterfaceObject():
- cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
- if descriptor.concrete:
- cgThings.append(CGWrapMethod(descriptor))
- cgThings = CGList(cgThings)
- cgThings = CGWrapper(cgThings, 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 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) \\
- aNameSpaceManager->RegisterDefineDOMInterface(NS_LITERAL_STRING(#_dom_class), _dom_class##Binding::DefineDOMInterface);\n\n"""
- def _undefineMacro(self):
- return "\n#undef REGISTER_PROTO"
- def _registerProtos(self):
- lines = ["REGISTER_PROTO(%s);" % desc.name
- for desc in self.config.getDescriptors(hasInterfaceObject=True,
- isExternal=False,
- workers=False)]
- 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)
- forwardDeclares = [CGClassForwardDeclare('XPCWrappedNativeScope')]
- for x in descriptors:
- nativeType = x.nativeType
- components = x.nativeType.split('::')
- declare = CGClassForwardDeclare(components[-1])
- 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 descriptors and 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] ]
- cgthings.extend([CGDescriptor(x) for x in descriptors])
- curr = CGList(cgthings, "\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,
- ['mozilla/dom/BindingUtils.h',
- 'mozilla/dom/DOMJSClass.h'],
- ['mozilla/dom/Nullable.h',
- 'XPCQuickStubs.h',
- 'AccessCheck.h',
- 'WorkerPrivate.h',
- 'nsContentUtils.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.getInterfaceFilename(desc.interface)
- for desc in config.getDescriptors(hasInterfaceObject=True,
- workers=False)]
- defineIncludes.append('nsScriptNameSpaceManager.h')
- curr = CGHeaders([], [], defineIncludes, curr)
- # Add include guards.
- curr = CGIncludeGuard('RegisterBindings', curr)
- # Done.
- return curr