michael@0: #!/usr/bin/env/python michael@0: # qsgen.py - Generate XPConnect quick stubs. michael@0: # michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: # =About quick stubs= michael@0: # qsgen.py generates "quick stubs", custom SpiderMonkey getters, setters, and michael@0: # methods for specified XPCOM interface members. These quick stubs serve at michael@0: # runtime as replacements for the XPConnect functions XPC_WN_GetterSetter and michael@0: # XPC_WN_CallMethod, which are the extremely generic (and slow) SpiderMonkey michael@0: # getter/setter/methods otherwise used for all XPCOM member accesses from JS. michael@0: # michael@0: # There are two ways quick stubs win: michael@0: # 1. Pure, transparent optimization by partial evaluation. michael@0: # 2. Cutting corners. michael@0: # michael@0: # == Partial evaluation == michael@0: # Partial evaluation is when you execute part of a program early (before or at michael@0: # compile time) so that you don't have to execute it at run time. In this michael@0: # case, everything that involves interpreting xptcall data (for example, the michael@0: # big methodInfo loops in XPCWrappedNative::CallMethod and the switch statement michael@0: # in XPCConert::JSData2Native) might as well happen at build time, since all michael@0: # the type information for any given member is already known. That's what this michael@0: # script does. It gets the information from IDL instead of XPT files. Apart michael@0: # from that, the code in this script is very similar to what you'll find in michael@0: # XPConnect itself. The advantage is that it runs once, at build time, not in michael@0: # tight loops at run time. michael@0: # michael@0: # == Cutting corners == michael@0: # The XPConnect versions have to be slow because they do tons of work that's michael@0: # only necessary in a few cases. The quick stubs skip a lot of that work. So michael@0: # quick stubs necessarily differ from XPConnect in potentially observable ways. michael@0: # For many specific interface members, the differences are not observable from michael@0: # scripts or don't matter enough to worry about; but you do have to be careful michael@0: # which members you decide to generate quick stubs for. michael@0: # michael@0: # The complete list of known differences, as of this writing, after an michael@0: # assiduous search: michael@0: # michael@0: # - Quick stubs affect the handling of naming conflicts--that is, which C++ michael@0: # method gets called when a script uses an XPCOM feature that is declared in michael@0: # more than one of the interfaces the object implements. Without quick michael@0: # stubs, XPConnect just walks the interfaces in the order they're listed by michael@0: # nsClassInfo. You get the first interface that implements a feature with michael@0: # that name. With quick stubs, it's the same except that non-quick-stubbed michael@0: # features are shadowed. michael@0: # michael@0: # - Quick stub methods are JSFastNative, which means that when a quick stub michael@0: # method is called, no JS stack frame is created. This doesn't affect michael@0: # Mozilla security checks because they look for scripted JSStackFrames, not michael@0: # native ones. michael@0: # michael@0: # It does affect the 'stack' property of JavaScript exceptions, though: the michael@0: # stubbed member will not appear. (Note that if the stubbed member itself michael@0: # fails, the member name will appear in the 'message' property.) michael@0: # michael@0: # - Many quick stubs don't create an XPCCallContext. In those cases, no entry michael@0: # is added to the XPCCallContext stack. So native implementations of michael@0: # quick-stubbed methods must avoid nsXPConnect::GetCurrentNativeCallContext. michael@0: # michael@0: # (Even when a quick stub does have an XPCCallContext, it never pushes it all michael@0: # the way to READY_TO_CALL state, so a lot of its members are garbage. But michael@0: # this doesn't endanger native implementations of non-quick-stubbed methods michael@0: # that use GetCurrentNativeCallContext and are called indirectly from michael@0: # quick-stubbed methods, because only the current top XPCCallContext is michael@0: # exposed--nsAXPCNativeCallContext does not expose michael@0: # XPCCallContext::GetPrevCallContext.) michael@0: # michael@0: # - Quick stubs never suspend the JS request. So they are only suitable for michael@0: # main-thread-only interfaces. michael@0: # michael@0: # - Quick stubs don't call XPCContext::SetLastResult. This is visible on the michael@0: # Components object. michael@0: # michael@0: # - Quick stubs skip a security check that XPConnect does in michael@0: # XPCWrappedNative::CallMethod. This means the security manager doesn't have michael@0: # an opportunity to veto accesses to members for which quick stubs exist. michael@0: # michael@0: # - There are many features of IDL that XPConnect supports but qsgen does not, michael@0: # including dependent types, arrays, and out parameters. michael@0: michael@0: michael@0: import xpidl michael@0: import header michael@0: import makeutils michael@0: import os, re michael@0: import sys michael@0: michael@0: # === Preliminaries michael@0: michael@0: def warn(msg): michael@0: sys.stderr.write(msg + '\n') michael@0: michael@0: def unaliasType(t): michael@0: while t.kind == 'typedef': michael@0: t = t.realtype michael@0: assert t is not None michael@0: return t michael@0: michael@0: def isVoidType(type): michael@0: """ Return True if the given xpidl type is void. """ michael@0: return type.kind == 'builtin' and type.name == 'void' michael@0: michael@0: def isInterfaceType(t): michael@0: t = unaliasType(t) michael@0: assert t.kind in ('builtin', 'native', 'interface', 'forward') michael@0: return t.kind in ('interface', 'forward') michael@0: michael@0: def isSpecificInterfaceType(t, name): michael@0: """ True if `t` is an interface type with the given name, or a forward michael@0: declaration or typedef aliasing it. michael@0: michael@0: `name` must not be the name of a typedef but the actual name of the michael@0: interface. michael@0: """ michael@0: t = unaliasType(t) michael@0: return t.kind in ('interface', 'forward') and t.name == name michael@0: michael@0: def getBuiltinOrNativeTypeName(t): michael@0: t = unaliasType(t) michael@0: if t.kind == 'builtin': michael@0: return t.name michael@0: elif t.kind == 'native': michael@0: assert t.specialtype is not None michael@0: return '[%s]' % t.specialtype michael@0: else: michael@0: return None michael@0: michael@0: michael@0: # === Reading the file michael@0: michael@0: class UserError(Exception): michael@0: pass michael@0: michael@0: def findIDL(includePath, irregularFilenames, interfaceName): michael@0: filename = irregularFilenames.get(interfaceName, interfaceName) + '.idl' michael@0: for d in includePath: michael@0: # Not os.path.join: we need a forward slash even on Windows because michael@0: # this filename ends up in makedepend output. michael@0: path = d + '/' + filename michael@0: if os.path.exists(path): michael@0: return path michael@0: raise UserError("No IDL file found for interface %s " michael@0: "in include path %r" michael@0: % (interfaceName, includePath)) michael@0: michael@0: def loadIDL(parser, includePath, filename): michael@0: makeutils.dependencies.append(filename) michael@0: text = open(filename, 'r').read() michael@0: idl = parser.parse(text, filename=filename) michael@0: idl.resolve(includePath, parser) michael@0: return idl michael@0: michael@0: def removeStubMember(memberId, member): michael@0: if member not in member.iface.stubMembers: michael@0: raise UserError("Trying to remove member %s from interface %s, but it was never added" michael@0: % (member.name, member.iface.name)) michael@0: member.iface.stubMembers.remove(member) michael@0: michael@0: def addStubMember(memberId, member): michael@0: if member.kind == 'method' and not member.implicit_jscontext and not isVariantType(member.realtype): michael@0: for param in member.params: michael@0: for attrname, value in vars(param).items(): michael@0: if value is True: michael@0: if attrname == 'optional': michael@0: continue michael@0: michael@0: raise UserError("Method %s, parameter %s: " michael@0: "unrecognized property %r" michael@0: % (memberId, param.name, attrname)) michael@0: michael@0: # Add this member to the list. michael@0: member.iface.stubMembers.append(member) michael@0: michael@0: def checkStubMember(member): michael@0: memberId = member.iface.name + "." + member.name michael@0: if member.kind not in ('method', 'attribute'): michael@0: raise UserError("Member %s is %r, not a method or attribute." michael@0: % (memberId, member.kind)) michael@0: if member.noscript: michael@0: raise UserError("%s %s is noscript." michael@0: % (member.kind.capitalize(), memberId)) michael@0: if member.kind == 'method' and member.notxpcom: michael@0: raise UserError( michael@0: "%s %s: notxpcom methods are not supported." michael@0: % (member.kind.capitalize(), memberId)) michael@0: michael@0: if (member.kind == 'attribute' michael@0: and not member.readonly michael@0: and isSpecificInterfaceType(member.realtype, 'nsIVariant')): michael@0: raise UserError( michael@0: "Attribute %s: Non-readonly attributes of type nsIVariant " michael@0: "are not supported." michael@0: % memberId) michael@0: michael@0: # Check for unknown properties. michael@0: for attrname, value in vars(member).items(): michael@0: if value is True and attrname not in ('readonly','optional_argc', michael@0: 'implicit_jscontext', michael@0: 'getter', 'stringifier'): michael@0: raise UserError("%s %s: unrecognized property %r" michael@0: % (member.kind.capitalize(), memberId, michael@0: attrname)) michael@0: michael@0: def parseMemberId(memberId): michael@0: """ Split the geven member id into its parts. """ michael@0: pieces = memberId.split('.') michael@0: if len(pieces) < 2: michael@0: raise UserError("Member %r: Missing dot." % memberId) michael@0: if len(pieces) > 2: michael@0: raise UserError("Member %r: Dots out of control." % memberId) michael@0: return tuple(pieces) michael@0: michael@0: class Configuration: michael@0: def __init__(self, filename, includePath): michael@0: self.includePath = includePath michael@0: config = {} michael@0: execfile(filename, config) michael@0: # required settings michael@0: for name in ('name', 'members'): michael@0: if name not in config: michael@0: raise UserError(filename + ": `%s` was not defined." % name) michael@0: setattr(self, name, config[name]) michael@0: # optional settings michael@0: self.irregularFilenames = config.get('irregularFilenames', {}) michael@0: self.customIncludes = config.get('customIncludes', []) michael@0: self.customMethodCalls = config.get('customMethodCalls', {}) michael@0: self.newBindingProperties = config.get('newBindingProperties', {}) michael@0: michael@0: def readConfigFile(filename, includePath, cachedir): michael@0: # Read the config file. michael@0: conf = Configuration(filename, includePath) michael@0: michael@0: # Now read IDL files to connect the information in the config file to michael@0: # actual XPCOM interfaces, methods, and attributes. michael@0: interfaces = [] michael@0: interfacesByName = {} michael@0: parser = xpidl.IDLParser(cachedir) michael@0: michael@0: def getInterface(interfaceName, errorLoc): michael@0: iface = interfacesByName.get(interfaceName) michael@0: if iface is None: michael@0: idlFile = findIDL(conf.includePath, conf.irregularFilenames, michael@0: interfaceName) michael@0: idl = loadIDL(parser, conf.includePath, idlFile) michael@0: if not idl.hasName(interfaceName): michael@0: raise UserError("The interface %s was not found " michael@0: "in the idl file %r." michael@0: % (interfaceName, idlFile)) michael@0: iface = idl.getName(interfaceName, errorLoc) michael@0: iface.stubMembers = [] michael@0: iface.newBindingProperties = 'nullptr' michael@0: interfaces.append(iface) michael@0: interfacesByName[interfaceName] = iface michael@0: return iface michael@0: michael@0: stubbedInterfaces = [] michael@0: michael@0: for memberId in conf.members: michael@0: add = True michael@0: interfaceName, memberName = parseMemberId(memberId) michael@0: michael@0: # If the interfaceName starts with -, then remove this entry from the list michael@0: if interfaceName[0] == '-': michael@0: add = False michael@0: interfaceName = interfaceName[1:] michael@0: michael@0: iface = getInterface(interfaceName, errorLoc='looking for %r' % memberId) michael@0: michael@0: if not iface.attributes.scriptable: michael@0: raise UserError("Interface %s is not scriptable." % interfaceName) michael@0: michael@0: if memberName == '*': michael@0: if not add: michael@0: raise UserError("Can't use negation in stub list with wildcard, in %s.*" % interfaceName) michael@0: michael@0: # Stub all scriptable members of this interface. michael@0: for member in iface.members: michael@0: if member.kind in ('method', 'attribute') and not member.noscript: michael@0: addStubMember(iface.name + '.' + member.name, member) michael@0: michael@0: if member.iface not in stubbedInterfaces: michael@0: stubbedInterfaces.append(member.iface) michael@0: else: michael@0: # Look up a member by name. michael@0: if memberName not in iface.namemap: michael@0: idlFile = iface.idl.parser.lexer.filename michael@0: raise UserError("Interface %s has no member %r. " michael@0: "(See IDL file %r.)" michael@0: % (interfaceName, memberName, idlFile)) michael@0: member = iface.namemap.get(memberName, None) michael@0: if add: michael@0: if member in iface.stubMembers: michael@0: raise UserError("Member %s is specified more than once." michael@0: % memberId) michael@0: michael@0: addStubMember(memberId, member) michael@0: if member.iface not in stubbedInterfaces: michael@0: stubbedInterfaces.append(member.iface) michael@0: else: michael@0: removeStubMember(memberId, member) michael@0: michael@0: for (interfaceName, v) in conf.newBindingProperties.iteritems(): michael@0: iface = getInterface(interfaceName, errorLoc='looking for %r' % interfaceName) michael@0: iface.newBindingProperties = v michael@0: if iface not in stubbedInterfaces: michael@0: stubbedInterfaces.append(iface) michael@0: michael@0: # Now go through and check all the interfaces' members michael@0: for iface in stubbedInterfaces: michael@0: for member in iface.stubMembers: michael@0: checkStubMember(member) michael@0: michael@0: return conf, interfaces michael@0: michael@0: michael@0: # === Generating the header file michael@0: michael@0: def writeHeaderFile(filename, name): michael@0: print "Creating header file", filename michael@0: michael@0: headerMacro = '__gen_%s__' % filename.replace('.', '_') michael@0: f = open(filename, 'w') michael@0: try: michael@0: f.write("/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n" michael@0: "#ifndef " + headerMacro + "\n" michael@0: "#define " + headerMacro + "\n\n" michael@0: "bool " + name + "_DefineQuickStubs(" michael@0: "JSContext *cx, JSObject *proto, unsigned flags, " michael@0: "uint32_t count, const nsID **iids);\n\n" michael@0: "void " + name + "_MarkInterfaces();\n\n" michael@0: "void " + name + "_ClearInterfaces();\n\n" michael@0: "inline void " + name + "_InitInterfaces()\n" michael@0: "{\n" michael@0: " " + name + "_ClearInterfaces();\n" michael@0: "}\n\n" michael@0: "#endif\n") michael@0: finally: michael@0: f.close() michael@0: michael@0: # === Generating the source file michael@0: michael@0: class StringTable: michael@0: def __init__(self): michael@0: self.current_index = 0; michael@0: self.table = {} michael@0: self.reverse_table = {} michael@0: michael@0: def c_strlen(self, string): michael@0: return len(string) + 1 michael@0: michael@0: def stringIndex(self, string): michael@0: if string in self.table: michael@0: return self.table[string] michael@0: else: michael@0: result = self.current_index michael@0: self.table[string] = result michael@0: self.current_index += self.c_strlen(string) michael@0: return result michael@0: michael@0: def writeDefinition(self, f, name): michael@0: entries = self.table.items() michael@0: entries.sort(key=lambda x:x[1]) michael@0: # Avoid null-in-string warnings with GCC and potentially michael@0: # overlong string constants; write everything out the long way. michael@0: def explodeToCharArray(string): michael@0: return ", ".join(map(lambda x:"'%s'" % x, string)) michael@0: f.write("static const char %s[] = {\n" % name) michael@0: for (string, offset) in entries[:-1]: michael@0: f.write(" /* %5d */ %s, '\\0',\n" michael@0: % (offset, explodeToCharArray(string))) michael@0: f.write(" /* %5d */ %s, '\\0' };\n\n" michael@0: % (entries[-1][1], explodeToCharArray(entries[-1][0]))) michael@0: f.write("const char* xpc_qsStringTable = %s;\n\n" % name); michael@0: michael@0: def substitute(template, vals): michael@0: """ Simple replacement for string.Template, which isn't in Python 2.3. """ michael@0: def replacement(match): michael@0: return vals[match.group(1)] michael@0: return re.sub(r'\${(\w+)}', replacement, template) michael@0: michael@0: # From JSData2Native. michael@0: argumentUnboxingTemplates = { michael@0: 'octet': michael@0: " uint32_t ${name}_u32;\n" michael@0: " if (!JS::ToUint32(cx, ${argVal}, &${name}_u32))\n" michael@0: " return false;\n" michael@0: " uint8_t ${name} = (uint8_t) ${name}_u32;\n", michael@0: michael@0: 'short': michael@0: " int32_t ${name}_i32;\n" michael@0: " if (!JS::ToInt32(cx, ${argVal}, &${name}_i32))\n" michael@0: " return false;\n" michael@0: " int16_t ${name} = (int16_t) ${name}_i32;\n", michael@0: michael@0: 'unsigned short': michael@0: " uint32_t ${name}_u32;\n" michael@0: " if (!JS::ToUint32(cx, ${argVal}, &${name}_u32))\n" michael@0: " return false;\n" michael@0: " uint16_t ${name} = (uint16_t) ${name}_u32;\n", michael@0: michael@0: 'long': michael@0: " int32_t ${name};\n" michael@0: " if (!JS::ToInt32(cx, ${argVal}, &${name}))\n" michael@0: " return false;\n", michael@0: michael@0: 'unsigned long': michael@0: " uint32_t ${name};\n" michael@0: " if (!JS::ToUint32(cx, ${argVal}, &${name}))\n" michael@0: " return false;\n", michael@0: michael@0: 'long long': michael@0: " int64_t ${name};\n" michael@0: " if (!JS::ToInt64(cx, ${argVal}, &${name}))\n" michael@0: " return false;\n", michael@0: michael@0: 'unsigned long long': michael@0: " uint64_t ${name};\n" michael@0: " if (!JS::ToUint64(cx, ${argVal}, &${name}))\n" michael@0: " return false;\n", michael@0: michael@0: 'float': michael@0: " double ${name}_dbl;\n" michael@0: " if (!JS::ToNumber(cx, ${argVal}, &${name}_dbl))\n" michael@0: " return false;\n" michael@0: " float ${name} = (float) ${name}_dbl;\n", michael@0: michael@0: 'double': michael@0: " double ${name};\n" michael@0: " if (!JS::ToNumber(cx, ${argVal}, &${name}))\n" michael@0: " return false;\n", michael@0: michael@0: 'boolean': michael@0: " bool ${name} = JS::ToBoolean(${argVal});\n", michael@0: michael@0: '[astring]': michael@0: " xpc_qsAString ${name}(cx, ${argVal}, ${argPtr}, ${notPassed});\n" michael@0: " if (!${name}.IsValid())\n" michael@0: " return false;\n", michael@0: michael@0: '[domstring]': michael@0: " xpc_qsDOMString ${name}(cx, ${argVal},\n" michael@0: " ${argPtr}, ${notPassed},\n" michael@0: " xpc_qsDOMString::e${nullBehavior},\n" michael@0: " xpc_qsDOMString::e${undefinedBehavior});\n" michael@0: " if (!${name}.IsValid())\n" michael@0: " return false;\n", michael@0: michael@0: 'string': michael@0: " JSAutoByteString ${name}_bytes;\n" michael@0: " if (!xpc_qsJsvalToCharStr(cx, ${argVal}, &${name}_bytes))\n" michael@0: " return false;\n" michael@0: " char *${name} = ${name}_bytes.ptr();\n", michael@0: michael@0: '[cstring]': michael@0: " xpc_qsACString ${name}(cx, ${argVal}, ${argPtr}, ${notPassed});\n" michael@0: " if (!${name}.IsValid())\n" michael@0: " return false;\n", michael@0: michael@0: '[utf8string]': michael@0: " xpc_qsAUTF8String ${name}(cx, ${argVal}, ${argPtr}, ${notPassed});\n" michael@0: " if (!${name}.IsValid())\n" michael@0: " return false;\n", michael@0: michael@0: '[jsval]': michael@0: " JS::RootedValue ${name}(cx, ${argVal});\n" michael@0: } michael@0: michael@0: # From JSData2Native. michael@0: # michael@0: # Omitted optional arguments are treated as though the caller had passed JS michael@0: # `null`; this behavior is from XPCWrappedNative::CallMethod. The 'jsval' type, michael@0: # however, defaults to 'undefined'. michael@0: # michael@0: def writeArgumentUnboxing(f, i, name, type, optional, rvdeclared, michael@0: nullBehavior, undefinedBehavior, michael@0: propIndex=None): michael@0: # f - file to write to michael@0: # i - int or None - Indicates the source jsval. If i is an int, the source michael@0: # jsval is args[i]; otherwise it is args[0]. But if Python i >= C++ argc, michael@0: # which can only happen if optional is True, the argument is missing; michael@0: # use JSVAL_NULL as the source jsval instead. michael@0: # name - str - name of the native C++ variable to create. michael@0: # type - xpidl.{Interface,Native,Builtin} - IDL type of argument michael@0: # optional - bool - True if the parameter is optional. michael@0: # rvdeclared - bool - False if no |nsresult rv| has been declared earlier. michael@0: michael@0: typeName = getBuiltinOrNativeTypeName(type) michael@0: michael@0: isSetter = (i is None) michael@0: michael@0: notPassed = "false" michael@0: if isSetter: michael@0: argPtr = "args[0]" michael@0: argVal = "args[0]" michael@0: elif optional: michael@0: if typeName == "[jsval]": michael@0: val = "JS::UndefinedHandleValue" michael@0: else: michael@0: val = "JS::NullHandleValue" michael@0: argVal = "(%d < argc ? args[%d] : %s)" % (i, i, val) michael@0: if typeName == "[jsval]": michael@0: # This should use the rooted argument, michael@0: # however we probably won't ever need to support that. michael@0: argPtr = None michael@0: notPassed = None michael@0: else: michael@0: # Need a MutableHandleValue to pass into eg the xpc_qsAString michael@0: # constructor, but the corresponding argument may not have been michael@0: # passed in at all. In that case, point the MutableHandleValue at a michael@0: # dummy variable, and pass in a boolean saying that the argument michael@0: # wasn't passed (previously, this used a NULL ptr sentinel value.) michael@0: f.write(" JS::RootedValue {name}_dummy(cx);\n".format(name=name)) michael@0: f.write(" JS::MutableHandleValue {name}_mhv({i} < argc ? args[{i}] : &{name}_dummy);\n".format(name=name, i=i)) michael@0: f.write(" (void) {name}_mhv;\n".format(name=name, i=i)) michael@0: michael@0: argPtr = "{name}_mhv".format(name=name) michael@0: notPassed = "argc < {i}".format(i=i) michael@0: else: michael@0: argVal = "args[%d]" % i michael@0: argPtr = "args[%d]" % i michael@0: michael@0: params = { michael@0: 'name': name, michael@0: 'argVal': argVal, michael@0: 'argPtr': argPtr, michael@0: 'notPassed': notPassed, michael@0: 'nullBehavior': nullBehavior or 'DefaultNullBehavior', michael@0: 'undefinedBehavior': undefinedBehavior or 'DefaultUndefinedBehavior' michael@0: } michael@0: michael@0: if typeName is not None: michael@0: template = argumentUnboxingTemplates.get(typeName) michael@0: if template is not None: michael@0: f.write(substitute(template, params)) michael@0: return rvdeclared michael@0: # else fall through; the type isn't supported yet. michael@0: elif isInterfaceType(type): michael@0: if type.name == 'nsIVariant': michael@0: # Totally custom. michael@0: template = ( michael@0: " nsCOMPtr ${name}(already_AddRefed(" michael@0: "XPCVariant::newVariant(cx, ${argVal})));\n" michael@0: " if (!${name}) {\n" michael@0: " xpc_qsThrowBadArg(cx, NS_ERROR_INVALID_ARG, vp, %d);\n" michael@0: " return false;\n" michael@0: " }") % i michael@0: f.write(substitute(template, params)) michael@0: return rvdeclared michael@0: elif type.name == 'nsIAtom': michael@0: # Should have special atomizing behavior. Fall through. michael@0: pass michael@0: else: michael@0: if not rvdeclared: michael@0: f.write(" nsresult rv;\n"); michael@0: f.write(" %s *%s;\n" % (type.name, name)) michael@0: f.write(" xpc_qsSelfRef %sref;\n" % name) michael@0: f.write(" rv = xpc_qsUnwrapArg<%s>(" michael@0: "cx, %s, &%s, &%sref.ptr, %s);\n" michael@0: % (type.name, argVal, name, name, argPtr)) michael@0: f.write(" if (NS_FAILED(rv)) {\n") michael@0: if isSetter: michael@0: assert(propIndex is not None) michael@0: f.write(" xpc_qsThrowBadSetterValue(cx, rv, JSVAL_TO_OBJECT(vp[1]), (uint16_t)%s);\n" % michael@0: propIndex) michael@0: else: michael@0: f.write(" xpc_qsThrowBadArg(cx, rv, vp, %d);\n" % i) michael@0: f.write(" return false;\n" michael@0: " }\n") michael@0: return True michael@0: michael@0: warn("Unable to unbox argument of type %s (native type %s)" % (type.name, typeName)) michael@0: if i is None: michael@0: src = 'args[0]' michael@0: else: michael@0: src = 'args[%d]' % i michael@0: f.write(" !; // TODO - Unbox argument %s = %s\n" % (name, src)) michael@0: return rvdeclared michael@0: michael@0: def writeResultDecl(f, type, varname): michael@0: if isVoidType(type): michael@0: return # nothing to declare michael@0: michael@0: t = unaliasType(type) michael@0: if t.kind == 'builtin': michael@0: if not t.nativename.endswith('*'): michael@0: if type.kind == 'typedef': michael@0: typeName = type.name # use it michael@0: else: michael@0: typeName = t.nativename michael@0: f.write(" %s %s;\n" % (typeName, varname)) michael@0: return michael@0: elif t.kind == 'native': michael@0: name = getBuiltinOrNativeTypeName(t) michael@0: if name in ('[domstring]', '[astring]'): michael@0: f.write(" nsString %s;\n" % varname) michael@0: return michael@0: elif name == '[jsval]': michael@0: f.write(" JS::RootedValue %s(cx);\n" % varname) michael@0: return michael@0: elif t.kind in ('interface', 'forward'): michael@0: f.write(" nsCOMPtr<%s> %s;\n" % (type.name, varname)) michael@0: return michael@0: michael@0: warn("Unable to declare result of type %s" % type.name) michael@0: f.write(" !; // TODO - Declare out parameter `%s`.\n" % varname) michael@0: michael@0: def outParamForm(name, type): michael@0: type = unaliasType(type) michael@0: if type.kind == 'builtin': michael@0: return '&' + name michael@0: elif type.kind == 'native': michael@0: if getBuiltinOrNativeTypeName(type) == '[jsval]': michael@0: return '&' + name michael@0: if type.modifier == 'ref': michael@0: return name michael@0: else: michael@0: return '&' + name michael@0: else: michael@0: return 'getter_AddRefs(%s)' % name michael@0: michael@0: # From NativeData2JS. michael@0: resultConvTemplates = { michael@0: 'void': michael@0: " ${jsvalRef} = JSVAL_VOID;\n" michael@0: " return true;\n", michael@0: michael@0: 'octet': michael@0: " ${jsvalRef} = INT_TO_JSVAL((int32_t) result);\n" michael@0: " return true;\n", michael@0: michael@0: 'short': michael@0: " ${jsvalRef} = INT_TO_JSVAL((int32_t) result);\n" michael@0: " return true;\n", michael@0: michael@0: 'long': michael@0: " ${jsvalRef} = INT_TO_JSVAL(result);\n" michael@0: " return true;\n", michael@0: michael@0: 'long long': michael@0: " return xpc_qsInt64ToJsval(cx, result, ${jsvalPtr});\n", michael@0: michael@0: 'unsigned short': michael@0: " ${jsvalRef} = INT_TO_JSVAL((int32_t) result);\n" michael@0: " return true;\n", michael@0: michael@0: 'unsigned long': michael@0: " ${jsvalRef} = UINT_TO_JSVAL(result);\n" michael@0: " return true;\n", michael@0: michael@0: 'unsigned long long': michael@0: " return xpc_qsUint64ToJsval(cx, result, ${jsvalPtr});\n", michael@0: michael@0: 'float': michael@0: " ${jsvalRef} = JS_NumberValue(result);\n" michael@0: " return true;\n", michael@0: michael@0: 'double': michael@0: " ${jsvalRef} = JS_NumberValue(result);\n" michael@0: " return true;\n", michael@0: michael@0: 'boolean': michael@0: " ${jsvalRef} = (result ? JSVAL_TRUE : JSVAL_FALSE);\n" michael@0: " return true;\n", michael@0: michael@0: '[astring]': michael@0: " return xpc::StringToJsval(cx, result, ${jsvalPtr});\n", michael@0: michael@0: '[domstring]': michael@0: " return xpc::StringToJsval(cx, result, ${jsvalPtr});\n", michael@0: michael@0: '[jsval]': michael@0: " ${jsvalPtr}.set(result);\n" michael@0: " return JS_WrapValue(cx, ${jsvalPtr});\n" michael@0: } michael@0: michael@0: def isVariantType(t): michael@0: return isSpecificInterfaceType(t, 'nsIVariant') michael@0: michael@0: def writeResultConv(f, type, jsvalPtr, jsvalRef): michael@0: """ Emit code to convert the C++ variable `result` to a jsval. michael@0: michael@0: The emitted code contains a return statement; it returns true on michael@0: success, false on error. michael@0: """ michael@0: # From NativeData2JS. michael@0: typeName = getBuiltinOrNativeTypeName(type) michael@0: if typeName is not None: michael@0: template = resultConvTemplates.get(typeName) michael@0: if template is not None: michael@0: values = {'jsvalRef': jsvalRef, michael@0: 'jsvalPtr': jsvalPtr} michael@0: f.write(substitute(template, values)) michael@0: return michael@0: # else fall through; this type isn't supported yet michael@0: elif isInterfaceType(type): michael@0: if isVariantType(type): michael@0: f.write(" return xpc_qsVariantToJsval(cx, result, %s);\n" michael@0: % jsvalPtr) michael@0: return michael@0: else: michael@0: f.write(" if (!result) {\n" michael@0: " %s.setNull();\n" michael@0: " return true;\n" michael@0: " }\n" michael@0: " nsWrapperCache* cache = xpc_qsGetWrapperCache(result);\n" michael@0: " if (xpc_FastGetCachedWrapper(cx, cache, %s)) {\n" michael@0: " return true;\n" michael@0: " }\n" michael@0: " // After this point do not use 'result'!\n" michael@0: " qsObjectHelper helper(result, cache);\n" michael@0: " return xpc_qsXPCOMObjectToJsval(cx, " michael@0: "helper, &NS_GET_IID(%s), &interfaces[k_%s], %s);\n" michael@0: % (jsvalPtr, jsvalPtr, type.name, type.name, jsvalPtr)) michael@0: return michael@0: michael@0: warn("Unable to convert result of type %s" % type.name) michael@0: f.write(" !; // TODO - Convert `result` to jsval, store in `%s`.\n" michael@0: % jsvalRef) michael@0: f.write(" return xpc_qsThrow(cx, NS_ERROR_UNEXPECTED); // FIXME\n") michael@0: michael@0: def memberNeedsCallee(member): michael@0: return isInterfaceType(member.realtype) michael@0: michael@0: def validateParam(member, param): michael@0: def pfail(msg): michael@0: raise UserError( michael@0: member.iface.name + '.' + member.name + ": " michael@0: "parameter " + param.name + ": " + msg) michael@0: michael@0: if param.iid_is is not None: michael@0: pfail("iid_is parameters are not supported.") michael@0: if param.size_is is not None: michael@0: pfail("size_is parameters are not supported.") michael@0: if param.retval: michael@0: pfail("Unexpected retval parameter!") michael@0: if param.paramtype in ('out', 'inout'): michael@0: pfail("Out parameters are not supported.") michael@0: if param.const or param.array or param.shared: michael@0: pfail("I am a simple caveman.") michael@0: michael@0: def setOptimizationForMSVC(f, b): michael@0: """ Write a pragma that turns optimizations off (if b is False) or michael@0: on (if b is True) for MSVC. michael@0: """ michael@0: if b: michael@0: pragmaParam = "on" michael@0: else: michael@0: pragmaParam = "off" michael@0: f.write("#ifdef _MSC_VER\n") michael@0: f.write('# pragma optimize("", %s)\n'%pragmaParam) michael@0: f.write("#endif\n") michael@0: michael@0: def writeQuickStub(f, customMethodCalls, stringtable, member, stubName, michael@0: isSetter=False): michael@0: """ Write a single quick stub (a custom SpiderMonkey getter/setter/method) michael@0: for the specified XPCOM interface-member. michael@0: """ michael@0: # Workaround for suspected compiler bug. michael@0: # See https://bugzilla.mozilla.org/show_bug.cgi?id=750019 michael@0: disableOptimizationForMSVC = (stubName == 'nsIDOMHTMLDocument_Write') michael@0: michael@0: isAttr = (member.kind == 'attribute') michael@0: isMethod = (member.kind == 'method') michael@0: assert isAttr or isMethod michael@0: isGetter = isAttr and not isSetter michael@0: michael@0: signature = ("static bool\n" + michael@0: "%s(JSContext *cx, unsigned argc,%s jsval *vp)\n") michael@0: michael@0: customMethodCall = customMethodCalls.get(stubName, None) michael@0: michael@0: if customMethodCall is None: michael@0: customMethodCall = customMethodCalls.get(member.iface.name + '_', None) michael@0: if customMethodCall is not None: michael@0: if isMethod: michael@0: code = customMethodCall.get('code', None) michael@0: elif isGetter: michael@0: code = customMethodCall.get('getter_code', None) michael@0: else: michael@0: code = customMethodCall.get('setter_code', None) michael@0: else: michael@0: code = None michael@0: michael@0: if code is not None: michael@0: templateName = member.iface.name michael@0: if isGetter: michael@0: templateName += '_Get' michael@0: elif isSetter: michael@0: templateName += '_Set' michael@0: michael@0: # Generate the code for the stub, calling the template function michael@0: # that's shared between the stubs. The stubs can't have additional michael@0: # arguments, only the template function can. michael@0: callTemplate = signature % (stubName, '') michael@0: callTemplate += "{\n" michael@0: michael@0: nativeName = (member.binaryname is not None and member.binaryname michael@0: or header.firstCap(member.name)) michael@0: argumentValues = (customMethodCall['additionalArgumentValues'] michael@0: % nativeName) michael@0: callTemplate += (" return %s(cx, argc, %s, vp);\n" michael@0: % (templateName, argumentValues)) michael@0: callTemplate += "}\n\n" michael@0: michael@0: # Fall through and create the template function stub called from the michael@0: # real stubs, but only generate the stub once. Otherwise, just write michael@0: # out the call to the template function and return. michael@0: templateGenerated = templateName + '_generated' michael@0: if templateGenerated in customMethodCall: michael@0: f.write(callTemplate) michael@0: return michael@0: customMethodCall[templateGenerated] = True michael@0: michael@0: stubName = templateName michael@0: else: michael@0: callTemplate = "" michael@0: else: michael@0: callTemplate = "" michael@0: code = customMethodCall.get('code', None) michael@0: michael@0: unwrapThisFailureFatal = (customMethodCall is None or michael@0: customMethodCall.get('unwrapThisFailureFatal', True)); michael@0: if (not unwrapThisFailureFatal and not isAttr): michael@0: raise UserError(member.iface.name + '.' + member.name + ": " michael@0: "Unwrapping this failure must be fatal for methods") michael@0: michael@0: # Function prolog. michael@0: michael@0: # Only template functions can have additional arguments. michael@0: if customMethodCall is None or not 'additionalArguments' in customMethodCall: michael@0: additionalArguments = '' michael@0: else: michael@0: additionalArguments = " %s," % customMethodCall['additionalArguments'] michael@0: if disableOptimizationForMSVC: michael@0: setOptimizationForMSVC(f, False) michael@0: f.write(signature % (stubName, additionalArguments)) michael@0: f.write("{\n") michael@0: f.write(" XPC_QS_ASSERT_CONTEXT_OK(cx);\n") michael@0: michael@0: # Compute "args". michael@0: f.write(" JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n") michael@0: f.write(" (void) args;\n") michael@0: michael@0: # Compute "this". michael@0: f.write(" JS::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));\n" michael@0: " if (!obj)\n" michael@0: " return false;\n") michael@0: michael@0: # Get the 'self' pointer. michael@0: if customMethodCall is None or not 'thisType' in customMethodCall: michael@0: f.write(" %s *self;\n" % member.iface.name) michael@0: else: michael@0: f.write(" %s *self;\n" % customMethodCall['thisType']) michael@0: f.write(" xpc_qsSelfRef selfref;\n") michael@0: pthisval = 'JS::MutableHandleValue::fromMarkedLocation(&vp[1])' # as above, ok to overwrite vp[1] michael@0: michael@0: if unwrapThisFailureFatal: michael@0: unwrapFatalArg = "true" michael@0: else: michael@0: unwrapFatalArg = "false" michael@0: michael@0: f.write(" if (!xpc_qsUnwrapThis(cx, obj, &self, " michael@0: "&selfref.ptr, %s, %s))\n" % (pthisval, unwrapFatalArg)) michael@0: f.write(" return false;\n") michael@0: michael@0: if not unwrapThisFailureFatal: michael@0: f.write(" if (!self) {\n") michael@0: if (isGetter): michael@0: f.write(" args.rval().setNull();\n") michael@0: f.write(" return true;\n") michael@0: f.write(" }\n"); michael@0: michael@0: if isMethod: michael@0: # If there are any required arguments, check argc. michael@0: requiredArgs = len(member.params) michael@0: while requiredArgs and member.params[requiredArgs-1].optional: michael@0: requiredArgs -= 1 michael@0: elif isSetter: michael@0: requiredArgs = 1 michael@0: else: michael@0: requiredArgs = 0 michael@0: if requiredArgs: michael@0: f.write(" if (argc < %d)\n" % requiredArgs) michael@0: f.write(" return xpc_qsThrow(cx, " michael@0: "NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n") michael@0: michael@0: # Convert in-parameters. michael@0: rvdeclared = False michael@0: if isMethod: michael@0: for i, param in enumerate(member.params): michael@0: argName = 'arg%d' % i michael@0: argTypeKey = argName + 'Type' michael@0: if customMethodCall is None or not argTypeKey in customMethodCall: michael@0: validateParam(member, param) michael@0: realtype = param.realtype michael@0: else: michael@0: realtype = xpidl.Forward(name=customMethodCall[argTypeKey], michael@0: location='', doccomments='') michael@0: # Emit code to convert this argument from jsval. michael@0: rvdeclared = writeArgumentUnboxing( michael@0: f, i, argName, realtype, michael@0: optional=param.optional, michael@0: rvdeclared=rvdeclared, michael@0: nullBehavior=param.null, michael@0: undefinedBehavior=param.undefined) michael@0: elif isSetter: michael@0: rvdeclared = writeArgumentUnboxing(f, None, 'arg0', member.realtype, michael@0: optional=False, michael@0: rvdeclared=rvdeclared, michael@0: nullBehavior=member.null, michael@0: undefinedBehavior=member.undefined, michael@0: propIndex=stringtable.stringIndex(member.name)) michael@0: michael@0: canFail = customMethodCall is None or customMethodCall.get('canFail', True) michael@0: if canFail and not rvdeclared: michael@0: f.write(" nsresult rv;\n") michael@0: rvdeclared = True michael@0: michael@0: if code is not None: michael@0: f.write("%s\n" % code) michael@0: michael@0: if code is None or (isGetter and callTemplate is ""): michael@0: debugGetter = code is not None michael@0: if debugGetter: michael@0: f.write("#ifdef DEBUG\n") michael@0: f.write(" nsresult debug_rv;\n") michael@0: f.write(" nsCOMPtr<%s> debug_self;\n" michael@0: " CallQueryInterface(self, getter_AddRefs(debug_self));\n" michael@0: % member.iface.name); michael@0: prefix = 'debug_' michael@0: else: michael@0: prefix = '' michael@0: michael@0: resultname = prefix + 'result' michael@0: selfname = prefix + 'self' michael@0: nsresultname = prefix + 'rv' michael@0: michael@0: # Prepare out-parameter. michael@0: if isMethod or isGetter: michael@0: writeResultDecl(f, member.realtype, resultname) michael@0: michael@0: # Call the method. michael@0: if isMethod: michael@0: comName = header.methodNativeName(member) michael@0: argv = ['arg' + str(i) for i, p in enumerate(member.params)] michael@0: if member.implicit_jscontext: michael@0: argv.append('cx') michael@0: if member.optional_argc: michael@0: argv.append('std::min(argc, %d) - %d' % michael@0: (len(member.params), requiredArgs)) michael@0: if not isVoidType(member.realtype): michael@0: argv.append(outParamForm(resultname, member.realtype)) michael@0: args = ', '.join(argv) michael@0: else: michael@0: comName = header.attributeNativeName(member, isGetter) michael@0: if isGetter: michael@0: args = outParamForm(resultname, member.realtype) michael@0: else: michael@0: args = "arg0" michael@0: if member.implicit_jscontext: michael@0: args = "cx, " + args michael@0: michael@0: f.write(" ") michael@0: if canFail or debugGetter: michael@0: f.write("%s = " % nsresultname) michael@0: f.write("%s->%s(%s);\n" % (selfname, comName, args)) michael@0: michael@0: if debugGetter: michael@0: checkSuccess = "NS_SUCCEEDED(debug_rv)" michael@0: if canFail: michael@0: checkSuccess += " == NS_SUCCEEDED(rv)" michael@0: f.write(" MOZ_ASSERT(%s && " michael@0: "xpc_qsSameResult(debug_result, result),\n" michael@0: " \"Got the wrong answer from the custom " michael@0: "method call!\");\n" % checkSuccess) michael@0: f.write("#endif\n") michael@0: michael@0: if canFail: michael@0: # Check for errors. michael@0: f.write(" if (NS_FAILED(rv))\n") michael@0: if isMethod: michael@0: f.write(" return xpc_qsThrowMethodFailed(" michael@0: "cx, rv, vp);\n") michael@0: else: michael@0: f.write(" return xpc_qsThrowGetterSetterFailed(cx, rv, " + michael@0: "JSVAL_TO_OBJECT(vp[1]), (uint16_t)%d);\n" % michael@0: stringtable.stringIndex(member.name)) michael@0: michael@0: # Convert the return value. michael@0: if isMethod or isGetter: michael@0: writeResultConv(f, member.realtype, 'args.rval()', '*vp') michael@0: else: michael@0: f.write(" return true;\n") michael@0: michael@0: # Epilog. michael@0: f.write("}\n") michael@0: if disableOptimizationForMSVC: michael@0: setOptimizationForMSVC(f, True) michael@0: f.write("\n") michael@0: michael@0: # Now write out the call to the template function. michael@0: if customMethodCall is not None: michael@0: f.write(callTemplate) michael@0: michael@0: def writeAttrStubs(f, customMethodCalls, stringtable, attr): michael@0: getterName = (attr.iface.name + '_' michael@0: + header.attributeNativeName(attr, True)) michael@0: writeQuickStub(f, customMethodCalls, stringtable, attr, getterName) michael@0: if attr.readonly: michael@0: setterName = 'xpc_qsGetterOnlyNativeStub' michael@0: else: michael@0: setterName = (attr.iface.name + '_' michael@0: + header.attributeNativeName(attr, False)) michael@0: writeQuickStub(f, customMethodCalls, stringtable, attr, setterName, michael@0: isSetter=True) michael@0: michael@0: ps = ('{%d, %s, %s}' michael@0: % (stringtable.stringIndex(attr.name), getterName, setterName)) michael@0: return ps michael@0: michael@0: def writeMethodStub(f, customMethodCalls, stringtable, method): michael@0: """ Write a method stub to `f`. Return an xpc_qsFunctionSpec initializer. """ michael@0: michael@0: stubName = method.iface.name + '_' + header.methodNativeName(method) michael@0: writeQuickStub(f, customMethodCalls, stringtable, method, stubName) michael@0: fs = '{%d, %d, %s}' % (stringtable.stringIndex(method.name), michael@0: len(method.params), stubName) michael@0: return fs michael@0: michael@0: def writeStubsForInterface(f, customMethodCalls, stringtable, iface): michael@0: f.write("// === interface %s\n\n" % iface.name) michael@0: propspecs = [] michael@0: funcspecs = [] michael@0: for member in iface.stubMembers: michael@0: if member.kind == 'attribute': michael@0: ps = writeAttrStubs(f, customMethodCalls, stringtable, member) michael@0: propspecs.append(ps) michael@0: elif member.kind == 'method': michael@0: fs = writeMethodStub(f, customMethodCalls, stringtable, member) michael@0: funcspecs.append(fs) michael@0: else: michael@0: raise TypeError('expected attribute or method, not %r' michael@0: % member.__class__.__name__) michael@0: michael@0: iface.propspecs = propspecs michael@0: iface.funcspecs = funcspecs michael@0: michael@0: def hashIID(iid): michael@0: # See nsIDKey::HashCode in nsHashtable.h. michael@0: return int(iid[:8], 16) michael@0: michael@0: uuid_re = re.compile(r'^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})$') michael@0: michael@0: def writeResultXPCInterfacesArray(f, conf, resulttypes): michael@0: f.write("// === XPCNativeInterface cache \n\n") michael@0: count = len(resulttypes) michael@0: if count > 0: michael@0: f.write("static XPCNativeInterface* interfaces[%d];\n\n" % count) michael@0: f.write("void %s_MarkInterfaces()\n" michael@0: "{\n" % conf.name) michael@0: if count > 0: michael@0: f.write(" for (uint32_t i = 0; i < %d; ++i)\n" michael@0: " if (interfaces[i])\n" michael@0: " interfaces[i]->Mark();\n" % count) michael@0: f.write("}\n") michael@0: f.write("void %s_ClearInterfaces()\n" michael@0: "{\n" % conf.name) michael@0: if count > 0: michael@0: f.write(" memset(interfaces, 0, %d * " michael@0: "sizeof(XPCNativeInterface*));\n" % count) michael@0: f.write("}\n\n") michael@0: i = 0 michael@0: for type in resulttypes: michael@0: f.write("static const uint32_t k_%s = %d;\n" % (type, i)) michael@0: i += 1 michael@0: if count > 0: michael@0: f.write("\n\n") michael@0: michael@0: def writeSpecs(f, elementType, varname, spec_type, spec_indices, interfaces): michael@0: index = 0 michael@0: f.write("static const %s %s[] = {\n" % (elementType, varname)) michael@0: for iface in interfaces: michael@0: specs = getattr(iface, spec_type) michael@0: if specs: michael@0: spec_indices[iface.name] = index michael@0: f.write(" // %s (index %d)\n" % (iface.name,index)) michael@0: for s in specs: michael@0: f.write(" %s,\n" % s) michael@0: index += len(specs) michael@0: f.write("};\n\n") michael@0: michael@0: def writeDefiner(f, conf, stringtable, interfaces): michael@0: f.write("// === Definer\n\n") michael@0: michael@0: # Write out the properties and functions michael@0: propspecs_indices = {} michael@0: funcspecs_indices = {} michael@0: prop_array_name = "all_properties" michael@0: func_array_name = "all_functions" michael@0: writeSpecs(f, "xpc_qsPropertySpec", prop_array_name, michael@0: "propspecs", propspecs_indices, interfaces) michael@0: writeSpecs(f, "xpc_qsFunctionSpec", func_array_name, michael@0: "funcspecs", funcspecs_indices, interfaces) michael@0: michael@0: # generate the static hash table michael@0: loadFactor = 0.6 michael@0: size = int(len(interfaces) / loadFactor) michael@0: buckets = [[] for i in range(size)] michael@0: for iface in interfaces: michael@0: # This if-statement discards interfaces specified with michael@0: # "nsInterfaceName.*" that don't have any stub-able members. michael@0: if iface.stubMembers or iface.newBindingProperties: michael@0: h = hashIID(iface.attributes.uuid) michael@0: buckets[h % size].append(iface) michael@0: michael@0: # Calculate where each interface's entry will show up in tableData. Where michael@0: # there are hash collisions, the extra entries are added at the end of the michael@0: # table. michael@0: entryIndexes = {} michael@0: arraySize = size michael@0: for i, bucket in enumerate(buckets): michael@0: if bucket: michael@0: entryIndexes[bucket[0].attributes.uuid] = i michael@0: for iface in bucket[1:]: michael@0: entryIndexes[iface.attributes.uuid] = arraySize michael@0: arraySize += 1 michael@0: michael@0: entries = [" {{0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}, " michael@0: "0, 0, 0, 0, nullptr, XPC_QS_NULL_INDEX, XPC_QS_NULL_INDEX}" michael@0: for i in range(arraySize)] michael@0: for i, bucket in enumerate(buckets): michael@0: for j, iface in enumerate(bucket): michael@0: # iid field michael@0: uuid = iface.attributes.uuid.lower() michael@0: m = uuid_re.match(uuid) michael@0: assert m is not None michael@0: m0, m1, m2, m3, m4 = m.groups() michael@0: m3arr = ('{0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s}' michael@0: % (m3[0:2], m3[2:4], m4[0:2], m4[2:4], michael@0: m4[4:6], m4[6:8], m4[8:10], m4[10:12])) michael@0: iid = ('{0x%s, 0x%s, 0x%s, %s}' % (m0, m1, m2, m3arr)) michael@0: michael@0: # properties fields michael@0: prop_index = 0 michael@0: prop_n_entries = 0 michael@0: if iface.propspecs: michael@0: prop_index = propspecs_indices[iface.name] michael@0: prop_n_entries = len(iface.propspecs) michael@0: michael@0: # member fields michael@0: func_index = 0 michael@0: func_n_entries = 0 michael@0: if iface.funcspecs: michael@0: func_index = funcspecs_indices[iface.name] michael@0: func_n_entries = len(iface.funcspecs) michael@0: michael@0: # parentInterface field michael@0: baseName = iface.base michael@0: while baseName is not None: michael@0: piface = iface.idl.getName(baseName, None) michael@0: k = entryIndexes.get(piface.attributes.uuid) michael@0: if k is not None: michael@0: parentInterface = str(k) michael@0: break michael@0: baseName = piface.base michael@0: else: michael@0: parentInterface = "XPC_QS_NULL_INDEX" michael@0: michael@0: # chain field michael@0: if j == len(bucket) - 1: michael@0: chain = "XPC_QS_NULL_INDEX" michael@0: else: michael@0: k = entryIndexes[bucket[j+1].attributes.uuid] michael@0: chain = str(k) michael@0: michael@0: # add entry michael@0: entry = " /* %s */ {%s, %d, %d, %d, %d, %s, %s, %s}" % ( michael@0: iface.name, iid, prop_index, prop_n_entries, michael@0: func_index, func_n_entries, iface.newBindingProperties, michael@0: parentInterface, chain) michael@0: entries[entryIndexes[iface.attributes.uuid]] = entry michael@0: michael@0: f.write("static const xpc_qsHashEntry tableData[] = {\n") michael@0: f.write(",\n".join(entries)) michael@0: f.write("\n };\n\n") michael@0: f.write("// Make sure our table indices aren't overflowed\n" michael@0: "PR_STATIC_ASSERT((sizeof(tableData) / sizeof(tableData[0])) < (1 << (8 * sizeof(tableData[0].parentInterface))));\n" michael@0: "PR_STATIC_ASSERT((sizeof(tableData) / sizeof(tableData[0])) < (1 << (8 * sizeof(tableData[0].chain))));\n\n") michael@0: michael@0: # The string table for property and method names. michael@0: table_name = "stringtab" michael@0: stringtable.writeDefinition(f, table_name) michael@0: structNames = [prop_array_name, func_array_name] michael@0: for name in structNames: michael@0: f.write("PR_STATIC_ASSERT(sizeof(%s) < (1 << (8 * sizeof(%s[0].name_index))));\n" michael@0: % (table_name, name)) michael@0: f.write("\n") michael@0: michael@0: # the definer function (entry point to this quick stubs file) michael@0: f.write("namespace xpc {\n") michael@0: f.write("bool %s_DefineQuickStubs(" % conf.name) michael@0: f.write("JSContext *cx, JSObject *proto, unsigned flags, uint32_t count, " michael@0: "const nsID **iids)\n" michael@0: "{\n") michael@0: f.write(" return !!xpc_qsDefineQuickStubs(" michael@0: "cx, proto, flags, count, iids, %d, tableData, %s, %s, %s);\n" % ( michael@0: size, prop_array_name, func_array_name, table_name)) michael@0: f.write("}\n") michael@0: f.write("} // namespace xpc\n\n\n") michael@0: michael@0: michael@0: stubTopTemplate = '''\ michael@0: /* THIS FILE IS AUTOGENERATED - DO NOT EDIT */ michael@0: #include "jsapi.h" michael@0: #include "qsWinUndefs.h" michael@0: #include "qsObjectHelper.h" michael@0: #include "nsID.h" michael@0: #include "%s" michael@0: #include "nsCOMPtr.h" michael@0: #include "xpcprivate.h" // for XPCCallContext michael@0: #include "XPCQuickStubs.h" michael@0: #include michael@0: ''' michael@0: michael@0: def writeStubFile(filename, headerFilename, conf, interfaces): michael@0: print "Creating stub file", filename michael@0: makeutils.targets.append(filename) michael@0: michael@0: f = open(filename, 'w') michael@0: filesIncluded = set() michael@0: michael@0: def includeType(type): michael@0: type = unaliasType(type) michael@0: if type.kind in ('builtin', 'native'): michael@0: return None michael@0: file = conf.irregularFilenames.get(type.name, type.name) + '.h' michael@0: if file not in filesIncluded: michael@0: f.write('#include "%s"\n' % file) michael@0: filesIncluded.add(file) michael@0: return type michael@0: michael@0: def writeIncludesForMember(member): michael@0: assert member.kind in ('attribute', 'method') michael@0: resulttype = includeType(member.realtype) michael@0: if member.kind == 'method': michael@0: for p in member.params: michael@0: includeType(p.realtype) michael@0: return resulttype michael@0: michael@0: def writeIncludesForInterface(iface): michael@0: assert iface.kind == 'interface' michael@0: resulttypes = [] michael@0: for member in iface.stubMembers: michael@0: resulttype = writeIncludesForMember(member) michael@0: if resulttype is not None and not isVariantType(resulttype): michael@0: resulttypes.append(resulttype.name) michael@0: michael@0: includeType(iface) michael@0: michael@0: return resulttypes michael@0: michael@0: try: michael@0: f.write(stubTopTemplate % os.path.basename(headerFilename)) michael@0: resulttypes = [] michael@0: for iface in interfaces: michael@0: resulttypes.extend(writeIncludesForInterface(iface)) michael@0: for customInclude in conf.customIncludes: michael@0: f.write('#include "%s"\n' % customInclude) michael@0: f.write("\n\n") michael@0: writeResultXPCInterfacesArray(f, conf, frozenset(resulttypes)) michael@0: stringtable = StringTable() michael@0: for iface in interfaces: michael@0: writeStubsForInterface(f, conf.customMethodCalls, stringtable, iface) michael@0: writeDefiner(f, conf, stringtable, interfaces) michael@0: finally: michael@0: f.close() michael@0: michael@0: def main(): michael@0: from optparse import OptionParser michael@0: o = OptionParser(usage="usage: %prog [options] configfile") michael@0: o.add_option('-o', "--stub-output", michael@0: type='string', dest='stub_output', default=None, michael@0: help="Quick stub C++ source output file", metavar="FILE") michael@0: o.add_option('--header-output', type='string', default=None, michael@0: help="Quick stub header output file", metavar="FILE") michael@0: o.add_option('--makedepend-output', type='string', default=None, michael@0: help="gnumake dependencies output file", metavar="FILE") michael@0: o.add_option('--idlpath', type='string', default='.', michael@0: help="colon-separated directories to search for idl files", michael@0: metavar="PATH") michael@0: o.add_option('--cachedir', dest='cachedir', default='', michael@0: help="Directory in which to cache lex/parse tables.") michael@0: o.add_option("--verbose-errors", action='store_true', default=False, michael@0: help="When an error happens, display the Python traceback.") michael@0: (options, filenames) = o.parse_args() michael@0: michael@0: if len(filenames) != 1: michael@0: o.error("Exactly one config filename is needed.") michael@0: filename = filenames[0] michael@0: michael@0: if options.stub_output is None: michael@0: if filename.endswith('.qsconf') or filename.endswith('.py'): michael@0: options.stub_output = filename.rsplit('.', 1)[0] + '.cpp' michael@0: else: michael@0: options.stub_output = filename + '.cpp' michael@0: if options.header_output is None: michael@0: options.header_output = re.sub(r'(\.c|\.cpp)?$', '.h', michael@0: options.stub_output) michael@0: michael@0: if options.cachedir != '': michael@0: sys.path.append(options.cachedir) michael@0: if not os.path.isdir(options.cachedir): michael@0: os.makedirs(options.cachedir) michael@0: michael@0: try: michael@0: includePath = options.idlpath.split(':') michael@0: conf, interfaces = readConfigFile(filename, michael@0: includePath=includePath, michael@0: cachedir=options.cachedir) michael@0: writeStubFile(options.stub_output, options.header_output, michael@0: conf, interfaces) michael@0: writeHeaderFile(options.header_output, conf.name) michael@0: if options.makedepend_output is not None: michael@0: makeutils.writeMakeDependOutput(options.makedepend_output) michael@0: except Exception, exc: michael@0: if options.verbose_errors: michael@0: raise michael@0: elif isinstance(exc, (UserError, xpidl.IDLError)): michael@0: warn(str(exc)) michael@0: elif isinstance(exc, OSError): michael@0: warn("%s: %s" % (exc.__class__.__name__, exc)) michael@0: else: michael@0: raise michael@0: sys.exit(1) michael@0: michael@0: if __name__ == '__main__': michael@0: main()