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