/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
Large files files are truncated, but you can click here to view the full file
- # 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"…
Large files files are truncated, but you can click here to view the full file