1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/xpconnect/src/qsgen.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1340 @@ 1.4 +#!/usr/bin/env/python 1.5 +# qsgen.py - Generate XPConnect quick stubs. 1.6 +# 1.7 +# This Source Code Form is subject to the terms of the Mozilla Public 1.8 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.10 + 1.11 +# =About quick stubs= 1.12 +# qsgen.py generates "quick stubs", custom SpiderMonkey getters, setters, and 1.13 +# methods for specified XPCOM interface members. These quick stubs serve at 1.14 +# runtime as replacements for the XPConnect functions XPC_WN_GetterSetter and 1.15 +# XPC_WN_CallMethod, which are the extremely generic (and slow) SpiderMonkey 1.16 +# getter/setter/methods otherwise used for all XPCOM member accesses from JS. 1.17 +# 1.18 +# There are two ways quick stubs win: 1.19 +# 1. Pure, transparent optimization by partial evaluation. 1.20 +# 2. Cutting corners. 1.21 +# 1.22 +# == Partial evaluation == 1.23 +# Partial evaluation is when you execute part of a program early (before or at 1.24 +# compile time) so that you don't have to execute it at run time. In this 1.25 +# case, everything that involves interpreting xptcall data (for example, the 1.26 +# big methodInfo loops in XPCWrappedNative::CallMethod and the switch statement 1.27 +# in XPCConert::JSData2Native) might as well happen at build time, since all 1.28 +# the type information for any given member is already known. That's what this 1.29 +# script does. It gets the information from IDL instead of XPT files. Apart 1.30 +# from that, the code in this script is very similar to what you'll find in 1.31 +# XPConnect itself. The advantage is that it runs once, at build time, not in 1.32 +# tight loops at run time. 1.33 +# 1.34 +# == Cutting corners == 1.35 +# The XPConnect versions have to be slow because they do tons of work that's 1.36 +# only necessary in a few cases. The quick stubs skip a lot of that work. So 1.37 +# quick stubs necessarily differ from XPConnect in potentially observable ways. 1.38 +# For many specific interface members, the differences are not observable from 1.39 +# scripts or don't matter enough to worry about; but you do have to be careful 1.40 +# which members you decide to generate quick stubs for. 1.41 +# 1.42 +# The complete list of known differences, as of this writing, after an 1.43 +# assiduous search: 1.44 +# 1.45 +# - Quick stubs affect the handling of naming conflicts--that is, which C++ 1.46 +# method gets called when a script uses an XPCOM feature that is declared in 1.47 +# more than one of the interfaces the object implements. Without quick 1.48 +# stubs, XPConnect just walks the interfaces in the order they're listed by 1.49 +# nsClassInfo. You get the first interface that implements a feature with 1.50 +# that name. With quick stubs, it's the same except that non-quick-stubbed 1.51 +# features are shadowed. 1.52 +# 1.53 +# - Quick stub methods are JSFastNative, which means that when a quick stub 1.54 +# method is called, no JS stack frame is created. This doesn't affect 1.55 +# Mozilla security checks because they look for scripted JSStackFrames, not 1.56 +# native ones. 1.57 +# 1.58 +# It does affect the 'stack' property of JavaScript exceptions, though: the 1.59 +# stubbed member will not appear. (Note that if the stubbed member itself 1.60 +# fails, the member name will appear in the 'message' property.) 1.61 +# 1.62 +# - Many quick stubs don't create an XPCCallContext. In those cases, no entry 1.63 +# is added to the XPCCallContext stack. So native implementations of 1.64 +# quick-stubbed methods must avoid nsXPConnect::GetCurrentNativeCallContext. 1.65 +# 1.66 +# (Even when a quick stub does have an XPCCallContext, it never pushes it all 1.67 +# the way to READY_TO_CALL state, so a lot of its members are garbage. But 1.68 +# this doesn't endanger native implementations of non-quick-stubbed methods 1.69 +# that use GetCurrentNativeCallContext and are called indirectly from 1.70 +# quick-stubbed methods, because only the current top XPCCallContext is 1.71 +# exposed--nsAXPCNativeCallContext does not expose 1.72 +# XPCCallContext::GetPrevCallContext.) 1.73 +# 1.74 +# - Quick stubs never suspend the JS request. So they are only suitable for 1.75 +# main-thread-only interfaces. 1.76 +# 1.77 +# - Quick stubs don't call XPCContext::SetLastResult. This is visible on the 1.78 +# Components object. 1.79 +# 1.80 +# - Quick stubs skip a security check that XPConnect does in 1.81 +# XPCWrappedNative::CallMethod. This means the security manager doesn't have 1.82 +# an opportunity to veto accesses to members for which quick stubs exist. 1.83 +# 1.84 +# - There are many features of IDL that XPConnect supports but qsgen does not, 1.85 +# including dependent types, arrays, and out parameters. 1.86 + 1.87 + 1.88 +import xpidl 1.89 +import header 1.90 +import makeutils 1.91 +import os, re 1.92 +import sys 1.93 + 1.94 +# === Preliminaries 1.95 + 1.96 +def warn(msg): 1.97 + sys.stderr.write(msg + '\n') 1.98 + 1.99 +def unaliasType(t): 1.100 + while t.kind == 'typedef': 1.101 + t = t.realtype 1.102 + assert t is not None 1.103 + return t 1.104 + 1.105 +def isVoidType(type): 1.106 + """ Return True if the given xpidl type is void. """ 1.107 + return type.kind == 'builtin' and type.name == 'void' 1.108 + 1.109 +def isInterfaceType(t): 1.110 + t = unaliasType(t) 1.111 + assert t.kind in ('builtin', 'native', 'interface', 'forward') 1.112 + return t.kind in ('interface', 'forward') 1.113 + 1.114 +def isSpecificInterfaceType(t, name): 1.115 + """ True if `t` is an interface type with the given name, or a forward 1.116 + declaration or typedef aliasing it. 1.117 + 1.118 + `name` must not be the name of a typedef but the actual name of the 1.119 + interface. 1.120 + """ 1.121 + t = unaliasType(t) 1.122 + return t.kind in ('interface', 'forward') and t.name == name 1.123 + 1.124 +def getBuiltinOrNativeTypeName(t): 1.125 + t = unaliasType(t) 1.126 + if t.kind == 'builtin': 1.127 + return t.name 1.128 + elif t.kind == 'native': 1.129 + assert t.specialtype is not None 1.130 + return '[%s]' % t.specialtype 1.131 + else: 1.132 + return None 1.133 + 1.134 + 1.135 +# === Reading the file 1.136 + 1.137 +class UserError(Exception): 1.138 + pass 1.139 + 1.140 +def findIDL(includePath, irregularFilenames, interfaceName): 1.141 + filename = irregularFilenames.get(interfaceName, interfaceName) + '.idl' 1.142 + for d in includePath: 1.143 + # Not os.path.join: we need a forward slash even on Windows because 1.144 + # this filename ends up in makedepend output. 1.145 + path = d + '/' + filename 1.146 + if os.path.exists(path): 1.147 + return path 1.148 + raise UserError("No IDL file found for interface %s " 1.149 + "in include path %r" 1.150 + % (interfaceName, includePath)) 1.151 + 1.152 +def loadIDL(parser, includePath, filename): 1.153 + makeutils.dependencies.append(filename) 1.154 + text = open(filename, 'r').read() 1.155 + idl = parser.parse(text, filename=filename) 1.156 + idl.resolve(includePath, parser) 1.157 + return idl 1.158 + 1.159 +def removeStubMember(memberId, member): 1.160 + if member not in member.iface.stubMembers: 1.161 + raise UserError("Trying to remove member %s from interface %s, but it was never added" 1.162 + % (member.name, member.iface.name)) 1.163 + member.iface.stubMembers.remove(member) 1.164 + 1.165 +def addStubMember(memberId, member): 1.166 + if member.kind == 'method' and not member.implicit_jscontext and not isVariantType(member.realtype): 1.167 + for param in member.params: 1.168 + for attrname, value in vars(param).items(): 1.169 + if value is True: 1.170 + if attrname == 'optional': 1.171 + continue 1.172 + 1.173 + raise UserError("Method %s, parameter %s: " 1.174 + "unrecognized property %r" 1.175 + % (memberId, param.name, attrname)) 1.176 + 1.177 + # Add this member to the list. 1.178 + member.iface.stubMembers.append(member) 1.179 + 1.180 +def checkStubMember(member): 1.181 + memberId = member.iface.name + "." + member.name 1.182 + if member.kind not in ('method', 'attribute'): 1.183 + raise UserError("Member %s is %r, not a method or attribute." 1.184 + % (memberId, member.kind)) 1.185 + if member.noscript: 1.186 + raise UserError("%s %s is noscript." 1.187 + % (member.kind.capitalize(), memberId)) 1.188 + if member.kind == 'method' and member.notxpcom: 1.189 + raise UserError( 1.190 + "%s %s: notxpcom methods are not supported." 1.191 + % (member.kind.capitalize(), memberId)) 1.192 + 1.193 + if (member.kind == 'attribute' 1.194 + and not member.readonly 1.195 + and isSpecificInterfaceType(member.realtype, 'nsIVariant')): 1.196 + raise UserError( 1.197 + "Attribute %s: Non-readonly attributes of type nsIVariant " 1.198 + "are not supported." 1.199 + % memberId) 1.200 + 1.201 + # Check for unknown properties. 1.202 + for attrname, value in vars(member).items(): 1.203 + if value is True and attrname not in ('readonly','optional_argc', 1.204 + 'implicit_jscontext', 1.205 + 'getter', 'stringifier'): 1.206 + raise UserError("%s %s: unrecognized property %r" 1.207 + % (member.kind.capitalize(), memberId, 1.208 + attrname)) 1.209 + 1.210 +def parseMemberId(memberId): 1.211 + """ Split the geven member id into its parts. """ 1.212 + pieces = memberId.split('.') 1.213 + if len(pieces) < 2: 1.214 + raise UserError("Member %r: Missing dot." % memberId) 1.215 + if len(pieces) > 2: 1.216 + raise UserError("Member %r: Dots out of control." % memberId) 1.217 + return tuple(pieces) 1.218 + 1.219 +class Configuration: 1.220 + def __init__(self, filename, includePath): 1.221 + self.includePath = includePath 1.222 + config = {} 1.223 + execfile(filename, config) 1.224 + # required settings 1.225 + for name in ('name', 'members'): 1.226 + if name not in config: 1.227 + raise UserError(filename + ": `%s` was not defined." % name) 1.228 + setattr(self, name, config[name]) 1.229 + # optional settings 1.230 + self.irregularFilenames = config.get('irregularFilenames', {}) 1.231 + self.customIncludes = config.get('customIncludes', []) 1.232 + self.customMethodCalls = config.get('customMethodCalls', {}) 1.233 + self.newBindingProperties = config.get('newBindingProperties', {}) 1.234 + 1.235 +def readConfigFile(filename, includePath, cachedir): 1.236 + # Read the config file. 1.237 + conf = Configuration(filename, includePath) 1.238 + 1.239 + # Now read IDL files to connect the information in the config file to 1.240 + # actual XPCOM interfaces, methods, and attributes. 1.241 + interfaces = [] 1.242 + interfacesByName = {} 1.243 + parser = xpidl.IDLParser(cachedir) 1.244 + 1.245 + def getInterface(interfaceName, errorLoc): 1.246 + iface = interfacesByName.get(interfaceName) 1.247 + if iface is None: 1.248 + idlFile = findIDL(conf.includePath, conf.irregularFilenames, 1.249 + interfaceName) 1.250 + idl = loadIDL(parser, conf.includePath, idlFile) 1.251 + if not idl.hasName(interfaceName): 1.252 + raise UserError("The interface %s was not found " 1.253 + "in the idl file %r." 1.254 + % (interfaceName, idlFile)) 1.255 + iface = idl.getName(interfaceName, errorLoc) 1.256 + iface.stubMembers = [] 1.257 + iface.newBindingProperties = 'nullptr' 1.258 + interfaces.append(iface) 1.259 + interfacesByName[interfaceName] = iface 1.260 + return iface 1.261 + 1.262 + stubbedInterfaces = [] 1.263 + 1.264 + for memberId in conf.members: 1.265 + add = True 1.266 + interfaceName, memberName = parseMemberId(memberId) 1.267 + 1.268 + # If the interfaceName starts with -, then remove this entry from the list 1.269 + if interfaceName[0] == '-': 1.270 + add = False 1.271 + interfaceName = interfaceName[1:] 1.272 + 1.273 + iface = getInterface(interfaceName, errorLoc='looking for %r' % memberId) 1.274 + 1.275 + if not iface.attributes.scriptable: 1.276 + raise UserError("Interface %s is not scriptable." % interfaceName) 1.277 + 1.278 + if memberName == '*': 1.279 + if not add: 1.280 + raise UserError("Can't use negation in stub list with wildcard, in %s.*" % interfaceName) 1.281 + 1.282 + # Stub all scriptable members of this interface. 1.283 + for member in iface.members: 1.284 + if member.kind in ('method', 'attribute') and not member.noscript: 1.285 + addStubMember(iface.name + '.' + member.name, member) 1.286 + 1.287 + if member.iface not in stubbedInterfaces: 1.288 + stubbedInterfaces.append(member.iface) 1.289 + else: 1.290 + # Look up a member by name. 1.291 + if memberName not in iface.namemap: 1.292 + idlFile = iface.idl.parser.lexer.filename 1.293 + raise UserError("Interface %s has no member %r. " 1.294 + "(See IDL file %r.)" 1.295 + % (interfaceName, memberName, idlFile)) 1.296 + member = iface.namemap.get(memberName, None) 1.297 + if add: 1.298 + if member in iface.stubMembers: 1.299 + raise UserError("Member %s is specified more than once." 1.300 + % memberId) 1.301 + 1.302 + addStubMember(memberId, member) 1.303 + if member.iface not in stubbedInterfaces: 1.304 + stubbedInterfaces.append(member.iface) 1.305 + else: 1.306 + removeStubMember(memberId, member) 1.307 + 1.308 + for (interfaceName, v) in conf.newBindingProperties.iteritems(): 1.309 + iface = getInterface(interfaceName, errorLoc='looking for %r' % interfaceName) 1.310 + iface.newBindingProperties = v 1.311 + if iface not in stubbedInterfaces: 1.312 + stubbedInterfaces.append(iface) 1.313 + 1.314 + # Now go through and check all the interfaces' members 1.315 + for iface in stubbedInterfaces: 1.316 + for member in iface.stubMembers: 1.317 + checkStubMember(member) 1.318 + 1.319 + return conf, interfaces 1.320 + 1.321 + 1.322 +# === Generating the header file 1.323 + 1.324 +def writeHeaderFile(filename, name): 1.325 + print "Creating header file", filename 1.326 + 1.327 + headerMacro = '__gen_%s__' % filename.replace('.', '_') 1.328 + f = open(filename, 'w') 1.329 + try: 1.330 + f.write("/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n" 1.331 + "#ifndef " + headerMacro + "\n" 1.332 + "#define " + headerMacro + "\n\n" 1.333 + "bool " + name + "_DefineQuickStubs(" 1.334 + "JSContext *cx, JSObject *proto, unsigned flags, " 1.335 + "uint32_t count, const nsID **iids);\n\n" 1.336 + "void " + name + "_MarkInterfaces();\n\n" 1.337 + "void " + name + "_ClearInterfaces();\n\n" 1.338 + "inline void " + name + "_InitInterfaces()\n" 1.339 + "{\n" 1.340 + " " + name + "_ClearInterfaces();\n" 1.341 + "}\n\n" 1.342 + "#endif\n") 1.343 + finally: 1.344 + f.close() 1.345 + 1.346 +# === Generating the source file 1.347 + 1.348 +class StringTable: 1.349 + def __init__(self): 1.350 + self.current_index = 0; 1.351 + self.table = {} 1.352 + self.reverse_table = {} 1.353 + 1.354 + def c_strlen(self, string): 1.355 + return len(string) + 1 1.356 + 1.357 + def stringIndex(self, string): 1.358 + if string in self.table: 1.359 + return self.table[string] 1.360 + else: 1.361 + result = self.current_index 1.362 + self.table[string] = result 1.363 + self.current_index += self.c_strlen(string) 1.364 + return result 1.365 + 1.366 + def writeDefinition(self, f, name): 1.367 + entries = self.table.items() 1.368 + entries.sort(key=lambda x:x[1]) 1.369 + # Avoid null-in-string warnings with GCC and potentially 1.370 + # overlong string constants; write everything out the long way. 1.371 + def explodeToCharArray(string): 1.372 + return ", ".join(map(lambda x:"'%s'" % x, string)) 1.373 + f.write("static const char %s[] = {\n" % name) 1.374 + for (string, offset) in entries[:-1]: 1.375 + f.write(" /* %5d */ %s, '\\0',\n" 1.376 + % (offset, explodeToCharArray(string))) 1.377 + f.write(" /* %5d */ %s, '\\0' };\n\n" 1.378 + % (entries[-1][1], explodeToCharArray(entries[-1][0]))) 1.379 + f.write("const char* xpc_qsStringTable = %s;\n\n" % name); 1.380 + 1.381 +def substitute(template, vals): 1.382 + """ Simple replacement for string.Template, which isn't in Python 2.3. """ 1.383 + def replacement(match): 1.384 + return vals[match.group(1)] 1.385 + return re.sub(r'\${(\w+)}', replacement, template) 1.386 + 1.387 +# From JSData2Native. 1.388 +argumentUnboxingTemplates = { 1.389 + 'octet': 1.390 + " uint32_t ${name}_u32;\n" 1.391 + " if (!JS::ToUint32(cx, ${argVal}, &${name}_u32))\n" 1.392 + " return false;\n" 1.393 + " uint8_t ${name} = (uint8_t) ${name}_u32;\n", 1.394 + 1.395 + 'short': 1.396 + " int32_t ${name}_i32;\n" 1.397 + " if (!JS::ToInt32(cx, ${argVal}, &${name}_i32))\n" 1.398 + " return false;\n" 1.399 + " int16_t ${name} = (int16_t) ${name}_i32;\n", 1.400 + 1.401 + 'unsigned short': 1.402 + " uint32_t ${name}_u32;\n" 1.403 + " if (!JS::ToUint32(cx, ${argVal}, &${name}_u32))\n" 1.404 + " return false;\n" 1.405 + " uint16_t ${name} = (uint16_t) ${name}_u32;\n", 1.406 + 1.407 + 'long': 1.408 + " int32_t ${name};\n" 1.409 + " if (!JS::ToInt32(cx, ${argVal}, &${name}))\n" 1.410 + " return false;\n", 1.411 + 1.412 + 'unsigned long': 1.413 + " uint32_t ${name};\n" 1.414 + " if (!JS::ToUint32(cx, ${argVal}, &${name}))\n" 1.415 + " return false;\n", 1.416 + 1.417 + 'long long': 1.418 + " int64_t ${name};\n" 1.419 + " if (!JS::ToInt64(cx, ${argVal}, &${name}))\n" 1.420 + " return false;\n", 1.421 + 1.422 + 'unsigned long long': 1.423 + " uint64_t ${name};\n" 1.424 + " if (!JS::ToUint64(cx, ${argVal}, &${name}))\n" 1.425 + " return false;\n", 1.426 + 1.427 + 'float': 1.428 + " double ${name}_dbl;\n" 1.429 + " if (!JS::ToNumber(cx, ${argVal}, &${name}_dbl))\n" 1.430 + " return false;\n" 1.431 + " float ${name} = (float) ${name}_dbl;\n", 1.432 + 1.433 + 'double': 1.434 + " double ${name};\n" 1.435 + " if (!JS::ToNumber(cx, ${argVal}, &${name}))\n" 1.436 + " return false;\n", 1.437 + 1.438 + 'boolean': 1.439 + " bool ${name} = JS::ToBoolean(${argVal});\n", 1.440 + 1.441 + '[astring]': 1.442 + " xpc_qsAString ${name}(cx, ${argVal}, ${argPtr}, ${notPassed});\n" 1.443 + " if (!${name}.IsValid())\n" 1.444 + " return false;\n", 1.445 + 1.446 + '[domstring]': 1.447 + " xpc_qsDOMString ${name}(cx, ${argVal},\n" 1.448 + " ${argPtr}, ${notPassed},\n" 1.449 + " xpc_qsDOMString::e${nullBehavior},\n" 1.450 + " xpc_qsDOMString::e${undefinedBehavior});\n" 1.451 + " if (!${name}.IsValid())\n" 1.452 + " return false;\n", 1.453 + 1.454 + 'string': 1.455 + " JSAutoByteString ${name}_bytes;\n" 1.456 + " if (!xpc_qsJsvalToCharStr(cx, ${argVal}, &${name}_bytes))\n" 1.457 + " return false;\n" 1.458 + " char *${name} = ${name}_bytes.ptr();\n", 1.459 + 1.460 + '[cstring]': 1.461 + " xpc_qsACString ${name}(cx, ${argVal}, ${argPtr}, ${notPassed});\n" 1.462 + " if (!${name}.IsValid())\n" 1.463 + " return false;\n", 1.464 + 1.465 + '[utf8string]': 1.466 + " xpc_qsAUTF8String ${name}(cx, ${argVal}, ${argPtr}, ${notPassed});\n" 1.467 + " if (!${name}.IsValid())\n" 1.468 + " return false;\n", 1.469 + 1.470 + '[jsval]': 1.471 + " JS::RootedValue ${name}(cx, ${argVal});\n" 1.472 + } 1.473 + 1.474 +# From JSData2Native. 1.475 +# 1.476 +# Omitted optional arguments are treated as though the caller had passed JS 1.477 +# `null`; this behavior is from XPCWrappedNative::CallMethod. The 'jsval' type, 1.478 +# however, defaults to 'undefined'. 1.479 +# 1.480 +def writeArgumentUnboxing(f, i, name, type, optional, rvdeclared, 1.481 + nullBehavior, undefinedBehavior, 1.482 + propIndex=None): 1.483 + # f - file to write to 1.484 + # i - int or None - Indicates the source jsval. If i is an int, the source 1.485 + # jsval is args[i]; otherwise it is args[0]. But if Python i >= C++ argc, 1.486 + # which can only happen if optional is True, the argument is missing; 1.487 + # use JSVAL_NULL as the source jsval instead. 1.488 + # name - str - name of the native C++ variable to create. 1.489 + # type - xpidl.{Interface,Native,Builtin} - IDL type of argument 1.490 + # optional - bool - True if the parameter is optional. 1.491 + # rvdeclared - bool - False if no |nsresult rv| has been declared earlier. 1.492 + 1.493 + typeName = getBuiltinOrNativeTypeName(type) 1.494 + 1.495 + isSetter = (i is None) 1.496 + 1.497 + notPassed = "false" 1.498 + if isSetter: 1.499 + argPtr = "args[0]" 1.500 + argVal = "args[0]" 1.501 + elif optional: 1.502 + if typeName == "[jsval]": 1.503 + val = "JS::UndefinedHandleValue" 1.504 + else: 1.505 + val = "JS::NullHandleValue" 1.506 + argVal = "(%d < argc ? args[%d] : %s)" % (i, i, val) 1.507 + if typeName == "[jsval]": 1.508 + # This should use the rooted argument, 1.509 + # however we probably won't ever need to support that. 1.510 + argPtr = None 1.511 + notPassed = None 1.512 + else: 1.513 + # Need a MutableHandleValue to pass into eg the xpc_qsAString 1.514 + # constructor, but the corresponding argument may not have been 1.515 + # passed in at all. In that case, point the MutableHandleValue at a 1.516 + # dummy variable, and pass in a boolean saying that the argument 1.517 + # wasn't passed (previously, this used a NULL ptr sentinel value.) 1.518 + f.write(" JS::RootedValue {name}_dummy(cx);\n".format(name=name)) 1.519 + f.write(" JS::MutableHandleValue {name}_mhv({i} < argc ? args[{i}] : &{name}_dummy);\n".format(name=name, i=i)) 1.520 + f.write(" (void) {name}_mhv;\n".format(name=name, i=i)) 1.521 + 1.522 + argPtr = "{name}_mhv".format(name=name) 1.523 + notPassed = "argc < {i}".format(i=i) 1.524 + else: 1.525 + argVal = "args[%d]" % i 1.526 + argPtr = "args[%d]" % i 1.527 + 1.528 + params = { 1.529 + 'name': name, 1.530 + 'argVal': argVal, 1.531 + 'argPtr': argPtr, 1.532 + 'notPassed': notPassed, 1.533 + 'nullBehavior': nullBehavior or 'DefaultNullBehavior', 1.534 + 'undefinedBehavior': undefinedBehavior or 'DefaultUndefinedBehavior' 1.535 + } 1.536 + 1.537 + if typeName is not None: 1.538 + template = argumentUnboxingTemplates.get(typeName) 1.539 + if template is not None: 1.540 + f.write(substitute(template, params)) 1.541 + return rvdeclared 1.542 + # else fall through; the type isn't supported yet. 1.543 + elif isInterfaceType(type): 1.544 + if type.name == 'nsIVariant': 1.545 + # Totally custom. 1.546 + template = ( 1.547 + " nsCOMPtr<nsIVariant> ${name}(already_AddRefed<nsIVariant>(" 1.548 + "XPCVariant::newVariant(cx, ${argVal})));\n" 1.549 + " if (!${name}) {\n" 1.550 + " xpc_qsThrowBadArg(cx, NS_ERROR_INVALID_ARG, vp, %d);\n" 1.551 + " return false;\n" 1.552 + " }") % i 1.553 + f.write(substitute(template, params)) 1.554 + return rvdeclared 1.555 + elif type.name == 'nsIAtom': 1.556 + # Should have special atomizing behavior. Fall through. 1.557 + pass 1.558 + else: 1.559 + if not rvdeclared: 1.560 + f.write(" nsresult rv;\n"); 1.561 + f.write(" %s *%s;\n" % (type.name, name)) 1.562 + f.write(" xpc_qsSelfRef %sref;\n" % name) 1.563 + f.write(" rv = xpc_qsUnwrapArg<%s>(" 1.564 + "cx, %s, &%s, &%sref.ptr, %s);\n" 1.565 + % (type.name, argVal, name, name, argPtr)) 1.566 + f.write(" if (NS_FAILED(rv)) {\n") 1.567 + if isSetter: 1.568 + assert(propIndex is not None) 1.569 + f.write(" xpc_qsThrowBadSetterValue(cx, rv, JSVAL_TO_OBJECT(vp[1]), (uint16_t)%s);\n" % 1.570 + propIndex) 1.571 + else: 1.572 + f.write(" xpc_qsThrowBadArg(cx, rv, vp, %d);\n" % i) 1.573 + f.write(" return false;\n" 1.574 + " }\n") 1.575 + return True 1.576 + 1.577 + warn("Unable to unbox argument of type %s (native type %s)" % (type.name, typeName)) 1.578 + if i is None: 1.579 + src = 'args[0]' 1.580 + else: 1.581 + src = 'args[%d]' % i 1.582 + f.write(" !; // TODO - Unbox argument %s = %s\n" % (name, src)) 1.583 + return rvdeclared 1.584 + 1.585 +def writeResultDecl(f, type, varname): 1.586 + if isVoidType(type): 1.587 + return # nothing to declare 1.588 + 1.589 + t = unaliasType(type) 1.590 + if t.kind == 'builtin': 1.591 + if not t.nativename.endswith('*'): 1.592 + if type.kind == 'typedef': 1.593 + typeName = type.name # use it 1.594 + else: 1.595 + typeName = t.nativename 1.596 + f.write(" %s %s;\n" % (typeName, varname)) 1.597 + return 1.598 + elif t.kind == 'native': 1.599 + name = getBuiltinOrNativeTypeName(t) 1.600 + if name in ('[domstring]', '[astring]'): 1.601 + f.write(" nsString %s;\n" % varname) 1.602 + return 1.603 + elif name == '[jsval]': 1.604 + f.write(" JS::RootedValue %s(cx);\n" % varname) 1.605 + return 1.606 + elif t.kind in ('interface', 'forward'): 1.607 + f.write(" nsCOMPtr<%s> %s;\n" % (type.name, varname)) 1.608 + return 1.609 + 1.610 + warn("Unable to declare result of type %s" % type.name) 1.611 + f.write(" !; // TODO - Declare out parameter `%s`.\n" % varname) 1.612 + 1.613 +def outParamForm(name, type): 1.614 + type = unaliasType(type) 1.615 + if type.kind == 'builtin': 1.616 + return '&' + name 1.617 + elif type.kind == 'native': 1.618 + if getBuiltinOrNativeTypeName(type) == '[jsval]': 1.619 + return '&' + name 1.620 + if type.modifier == 'ref': 1.621 + return name 1.622 + else: 1.623 + return '&' + name 1.624 + else: 1.625 + return 'getter_AddRefs(%s)' % name 1.626 + 1.627 +# From NativeData2JS. 1.628 +resultConvTemplates = { 1.629 + 'void': 1.630 + " ${jsvalRef} = JSVAL_VOID;\n" 1.631 + " return true;\n", 1.632 + 1.633 + 'octet': 1.634 + " ${jsvalRef} = INT_TO_JSVAL((int32_t) result);\n" 1.635 + " return true;\n", 1.636 + 1.637 + 'short': 1.638 + " ${jsvalRef} = INT_TO_JSVAL((int32_t) result);\n" 1.639 + " return true;\n", 1.640 + 1.641 + 'long': 1.642 + " ${jsvalRef} = INT_TO_JSVAL(result);\n" 1.643 + " return true;\n", 1.644 + 1.645 + 'long long': 1.646 + " return xpc_qsInt64ToJsval(cx, result, ${jsvalPtr});\n", 1.647 + 1.648 + 'unsigned short': 1.649 + " ${jsvalRef} = INT_TO_JSVAL((int32_t) result);\n" 1.650 + " return true;\n", 1.651 + 1.652 + 'unsigned long': 1.653 + " ${jsvalRef} = UINT_TO_JSVAL(result);\n" 1.654 + " return true;\n", 1.655 + 1.656 + 'unsigned long long': 1.657 + " return xpc_qsUint64ToJsval(cx, result, ${jsvalPtr});\n", 1.658 + 1.659 + 'float': 1.660 + " ${jsvalRef} = JS_NumberValue(result);\n" 1.661 + " return true;\n", 1.662 + 1.663 + 'double': 1.664 + " ${jsvalRef} = JS_NumberValue(result);\n" 1.665 + " return true;\n", 1.666 + 1.667 + 'boolean': 1.668 + " ${jsvalRef} = (result ? JSVAL_TRUE : JSVAL_FALSE);\n" 1.669 + " return true;\n", 1.670 + 1.671 + '[astring]': 1.672 + " return xpc::StringToJsval(cx, result, ${jsvalPtr});\n", 1.673 + 1.674 + '[domstring]': 1.675 + " return xpc::StringToJsval(cx, result, ${jsvalPtr});\n", 1.676 + 1.677 + '[jsval]': 1.678 + " ${jsvalPtr}.set(result);\n" 1.679 + " return JS_WrapValue(cx, ${jsvalPtr});\n" 1.680 + } 1.681 + 1.682 +def isVariantType(t): 1.683 + return isSpecificInterfaceType(t, 'nsIVariant') 1.684 + 1.685 +def writeResultConv(f, type, jsvalPtr, jsvalRef): 1.686 + """ Emit code to convert the C++ variable `result` to a jsval. 1.687 + 1.688 + The emitted code contains a return statement; it returns true on 1.689 + success, false on error. 1.690 + """ 1.691 + # From NativeData2JS. 1.692 + typeName = getBuiltinOrNativeTypeName(type) 1.693 + if typeName is not None: 1.694 + template = resultConvTemplates.get(typeName) 1.695 + if template is not None: 1.696 + values = {'jsvalRef': jsvalRef, 1.697 + 'jsvalPtr': jsvalPtr} 1.698 + f.write(substitute(template, values)) 1.699 + return 1.700 + # else fall through; this type isn't supported yet 1.701 + elif isInterfaceType(type): 1.702 + if isVariantType(type): 1.703 + f.write(" return xpc_qsVariantToJsval(cx, result, %s);\n" 1.704 + % jsvalPtr) 1.705 + return 1.706 + else: 1.707 + f.write(" if (!result) {\n" 1.708 + " %s.setNull();\n" 1.709 + " return true;\n" 1.710 + " }\n" 1.711 + " nsWrapperCache* cache = xpc_qsGetWrapperCache(result);\n" 1.712 + " if (xpc_FastGetCachedWrapper(cx, cache, %s)) {\n" 1.713 + " return true;\n" 1.714 + " }\n" 1.715 + " // After this point do not use 'result'!\n" 1.716 + " qsObjectHelper helper(result, cache);\n" 1.717 + " return xpc_qsXPCOMObjectToJsval(cx, " 1.718 + "helper, &NS_GET_IID(%s), &interfaces[k_%s], %s);\n" 1.719 + % (jsvalPtr, jsvalPtr, type.name, type.name, jsvalPtr)) 1.720 + return 1.721 + 1.722 + warn("Unable to convert result of type %s" % type.name) 1.723 + f.write(" !; // TODO - Convert `result` to jsval, store in `%s`.\n" 1.724 + % jsvalRef) 1.725 + f.write(" return xpc_qsThrow(cx, NS_ERROR_UNEXPECTED); // FIXME\n") 1.726 + 1.727 +def memberNeedsCallee(member): 1.728 + return isInterfaceType(member.realtype) 1.729 + 1.730 +def validateParam(member, param): 1.731 + def pfail(msg): 1.732 + raise UserError( 1.733 + member.iface.name + '.' + member.name + ": " 1.734 + "parameter " + param.name + ": " + msg) 1.735 + 1.736 + if param.iid_is is not None: 1.737 + pfail("iid_is parameters are not supported.") 1.738 + if param.size_is is not None: 1.739 + pfail("size_is parameters are not supported.") 1.740 + if param.retval: 1.741 + pfail("Unexpected retval parameter!") 1.742 + if param.paramtype in ('out', 'inout'): 1.743 + pfail("Out parameters are not supported.") 1.744 + if param.const or param.array or param.shared: 1.745 + pfail("I am a simple caveman.") 1.746 + 1.747 +def setOptimizationForMSVC(f, b): 1.748 + """ Write a pragma that turns optimizations off (if b is False) or 1.749 + on (if b is True) for MSVC. 1.750 + """ 1.751 + if b: 1.752 + pragmaParam = "on" 1.753 + else: 1.754 + pragmaParam = "off" 1.755 + f.write("#ifdef _MSC_VER\n") 1.756 + f.write('# pragma optimize("", %s)\n'%pragmaParam) 1.757 + f.write("#endif\n") 1.758 + 1.759 +def writeQuickStub(f, customMethodCalls, stringtable, member, stubName, 1.760 + isSetter=False): 1.761 + """ Write a single quick stub (a custom SpiderMonkey getter/setter/method) 1.762 + for the specified XPCOM interface-member. 1.763 + """ 1.764 + # Workaround for suspected compiler bug. 1.765 + # See https://bugzilla.mozilla.org/show_bug.cgi?id=750019 1.766 + disableOptimizationForMSVC = (stubName == 'nsIDOMHTMLDocument_Write') 1.767 + 1.768 + isAttr = (member.kind == 'attribute') 1.769 + isMethod = (member.kind == 'method') 1.770 + assert isAttr or isMethod 1.771 + isGetter = isAttr and not isSetter 1.772 + 1.773 + signature = ("static bool\n" + 1.774 + "%s(JSContext *cx, unsigned argc,%s jsval *vp)\n") 1.775 + 1.776 + customMethodCall = customMethodCalls.get(stubName, None) 1.777 + 1.778 + if customMethodCall is None: 1.779 + customMethodCall = customMethodCalls.get(member.iface.name + '_', None) 1.780 + if customMethodCall is not None: 1.781 + if isMethod: 1.782 + code = customMethodCall.get('code', None) 1.783 + elif isGetter: 1.784 + code = customMethodCall.get('getter_code', None) 1.785 + else: 1.786 + code = customMethodCall.get('setter_code', None) 1.787 + else: 1.788 + code = None 1.789 + 1.790 + if code is not None: 1.791 + templateName = member.iface.name 1.792 + if isGetter: 1.793 + templateName += '_Get' 1.794 + elif isSetter: 1.795 + templateName += '_Set' 1.796 + 1.797 + # Generate the code for the stub, calling the template function 1.798 + # that's shared between the stubs. The stubs can't have additional 1.799 + # arguments, only the template function can. 1.800 + callTemplate = signature % (stubName, '') 1.801 + callTemplate += "{\n" 1.802 + 1.803 + nativeName = (member.binaryname is not None and member.binaryname 1.804 + or header.firstCap(member.name)) 1.805 + argumentValues = (customMethodCall['additionalArgumentValues'] 1.806 + % nativeName) 1.807 + callTemplate += (" return %s(cx, argc, %s, vp);\n" 1.808 + % (templateName, argumentValues)) 1.809 + callTemplate += "}\n\n" 1.810 + 1.811 + # Fall through and create the template function stub called from the 1.812 + # real stubs, but only generate the stub once. Otherwise, just write 1.813 + # out the call to the template function and return. 1.814 + templateGenerated = templateName + '_generated' 1.815 + if templateGenerated in customMethodCall: 1.816 + f.write(callTemplate) 1.817 + return 1.818 + customMethodCall[templateGenerated] = True 1.819 + 1.820 + stubName = templateName 1.821 + else: 1.822 + callTemplate = "" 1.823 + else: 1.824 + callTemplate = "" 1.825 + code = customMethodCall.get('code', None) 1.826 + 1.827 + unwrapThisFailureFatal = (customMethodCall is None or 1.828 + customMethodCall.get('unwrapThisFailureFatal', True)); 1.829 + if (not unwrapThisFailureFatal and not isAttr): 1.830 + raise UserError(member.iface.name + '.' + member.name + ": " 1.831 + "Unwrapping this failure must be fatal for methods") 1.832 + 1.833 + # Function prolog. 1.834 + 1.835 + # Only template functions can have additional arguments. 1.836 + if customMethodCall is None or not 'additionalArguments' in customMethodCall: 1.837 + additionalArguments = '' 1.838 + else: 1.839 + additionalArguments = " %s," % customMethodCall['additionalArguments'] 1.840 + if disableOptimizationForMSVC: 1.841 + setOptimizationForMSVC(f, False) 1.842 + f.write(signature % (stubName, additionalArguments)) 1.843 + f.write("{\n") 1.844 + f.write(" XPC_QS_ASSERT_CONTEXT_OK(cx);\n") 1.845 + 1.846 + # Compute "args". 1.847 + f.write(" JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n") 1.848 + f.write(" (void) args;\n") 1.849 + 1.850 + # Compute "this". 1.851 + f.write(" JS::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));\n" 1.852 + " if (!obj)\n" 1.853 + " return false;\n") 1.854 + 1.855 + # Get the 'self' pointer. 1.856 + if customMethodCall is None or not 'thisType' in customMethodCall: 1.857 + f.write(" %s *self;\n" % member.iface.name) 1.858 + else: 1.859 + f.write(" %s *self;\n" % customMethodCall['thisType']) 1.860 + f.write(" xpc_qsSelfRef selfref;\n") 1.861 + pthisval = 'JS::MutableHandleValue::fromMarkedLocation(&vp[1])' # as above, ok to overwrite vp[1] 1.862 + 1.863 + if unwrapThisFailureFatal: 1.864 + unwrapFatalArg = "true" 1.865 + else: 1.866 + unwrapFatalArg = "false" 1.867 + 1.868 + f.write(" if (!xpc_qsUnwrapThis(cx, obj, &self, " 1.869 + "&selfref.ptr, %s, %s))\n" % (pthisval, unwrapFatalArg)) 1.870 + f.write(" return false;\n") 1.871 + 1.872 + if not unwrapThisFailureFatal: 1.873 + f.write(" if (!self) {\n") 1.874 + if (isGetter): 1.875 + f.write(" args.rval().setNull();\n") 1.876 + f.write(" return true;\n") 1.877 + f.write(" }\n"); 1.878 + 1.879 + if isMethod: 1.880 + # If there are any required arguments, check argc. 1.881 + requiredArgs = len(member.params) 1.882 + while requiredArgs and member.params[requiredArgs-1].optional: 1.883 + requiredArgs -= 1 1.884 + elif isSetter: 1.885 + requiredArgs = 1 1.886 + else: 1.887 + requiredArgs = 0 1.888 + if requiredArgs: 1.889 + f.write(" if (argc < %d)\n" % requiredArgs) 1.890 + f.write(" return xpc_qsThrow(cx, " 1.891 + "NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n") 1.892 + 1.893 + # Convert in-parameters. 1.894 + rvdeclared = False 1.895 + if isMethod: 1.896 + for i, param in enumerate(member.params): 1.897 + argName = 'arg%d' % i 1.898 + argTypeKey = argName + 'Type' 1.899 + if customMethodCall is None or not argTypeKey in customMethodCall: 1.900 + validateParam(member, param) 1.901 + realtype = param.realtype 1.902 + else: 1.903 + realtype = xpidl.Forward(name=customMethodCall[argTypeKey], 1.904 + location='', doccomments='') 1.905 + # Emit code to convert this argument from jsval. 1.906 + rvdeclared = writeArgumentUnboxing( 1.907 + f, i, argName, realtype, 1.908 + optional=param.optional, 1.909 + rvdeclared=rvdeclared, 1.910 + nullBehavior=param.null, 1.911 + undefinedBehavior=param.undefined) 1.912 + elif isSetter: 1.913 + rvdeclared = writeArgumentUnboxing(f, None, 'arg0', member.realtype, 1.914 + optional=False, 1.915 + rvdeclared=rvdeclared, 1.916 + nullBehavior=member.null, 1.917 + undefinedBehavior=member.undefined, 1.918 + propIndex=stringtable.stringIndex(member.name)) 1.919 + 1.920 + canFail = customMethodCall is None or customMethodCall.get('canFail', True) 1.921 + if canFail and not rvdeclared: 1.922 + f.write(" nsresult rv;\n") 1.923 + rvdeclared = True 1.924 + 1.925 + if code is not None: 1.926 + f.write("%s\n" % code) 1.927 + 1.928 + if code is None or (isGetter and callTemplate is ""): 1.929 + debugGetter = code is not None 1.930 + if debugGetter: 1.931 + f.write("#ifdef DEBUG\n") 1.932 + f.write(" nsresult debug_rv;\n") 1.933 + f.write(" nsCOMPtr<%s> debug_self;\n" 1.934 + " CallQueryInterface(self, getter_AddRefs(debug_self));\n" 1.935 + % member.iface.name); 1.936 + prefix = 'debug_' 1.937 + else: 1.938 + prefix = '' 1.939 + 1.940 + resultname = prefix + 'result' 1.941 + selfname = prefix + 'self' 1.942 + nsresultname = prefix + 'rv' 1.943 + 1.944 + # Prepare out-parameter. 1.945 + if isMethod or isGetter: 1.946 + writeResultDecl(f, member.realtype, resultname) 1.947 + 1.948 + # Call the method. 1.949 + if isMethod: 1.950 + comName = header.methodNativeName(member) 1.951 + argv = ['arg' + str(i) for i, p in enumerate(member.params)] 1.952 + if member.implicit_jscontext: 1.953 + argv.append('cx') 1.954 + if member.optional_argc: 1.955 + argv.append('std::min<uint32_t>(argc, %d) - %d' % 1.956 + (len(member.params), requiredArgs)) 1.957 + if not isVoidType(member.realtype): 1.958 + argv.append(outParamForm(resultname, member.realtype)) 1.959 + args = ', '.join(argv) 1.960 + else: 1.961 + comName = header.attributeNativeName(member, isGetter) 1.962 + if isGetter: 1.963 + args = outParamForm(resultname, member.realtype) 1.964 + else: 1.965 + args = "arg0" 1.966 + if member.implicit_jscontext: 1.967 + args = "cx, " + args 1.968 + 1.969 + f.write(" ") 1.970 + if canFail or debugGetter: 1.971 + f.write("%s = " % nsresultname) 1.972 + f.write("%s->%s(%s);\n" % (selfname, comName, args)) 1.973 + 1.974 + if debugGetter: 1.975 + checkSuccess = "NS_SUCCEEDED(debug_rv)" 1.976 + if canFail: 1.977 + checkSuccess += " == NS_SUCCEEDED(rv)" 1.978 + f.write(" MOZ_ASSERT(%s && " 1.979 + "xpc_qsSameResult(debug_result, result),\n" 1.980 + " \"Got the wrong answer from the custom " 1.981 + "method call!\");\n" % checkSuccess) 1.982 + f.write("#endif\n") 1.983 + 1.984 + if canFail: 1.985 + # Check for errors. 1.986 + f.write(" if (NS_FAILED(rv))\n") 1.987 + if isMethod: 1.988 + f.write(" return xpc_qsThrowMethodFailed(" 1.989 + "cx, rv, vp);\n") 1.990 + else: 1.991 + f.write(" return xpc_qsThrowGetterSetterFailed(cx, rv, " + 1.992 + "JSVAL_TO_OBJECT(vp[1]), (uint16_t)%d);\n" % 1.993 + stringtable.stringIndex(member.name)) 1.994 + 1.995 + # Convert the return value. 1.996 + if isMethod or isGetter: 1.997 + writeResultConv(f, member.realtype, 'args.rval()', '*vp') 1.998 + else: 1.999 + f.write(" return true;\n") 1.1000 + 1.1001 + # Epilog. 1.1002 + f.write("}\n") 1.1003 + if disableOptimizationForMSVC: 1.1004 + setOptimizationForMSVC(f, True) 1.1005 + f.write("\n") 1.1006 + 1.1007 + # Now write out the call to the template function. 1.1008 + if customMethodCall is not None: 1.1009 + f.write(callTemplate) 1.1010 + 1.1011 +def writeAttrStubs(f, customMethodCalls, stringtable, attr): 1.1012 + getterName = (attr.iface.name + '_' 1.1013 + + header.attributeNativeName(attr, True)) 1.1014 + writeQuickStub(f, customMethodCalls, stringtable, attr, getterName) 1.1015 + if attr.readonly: 1.1016 + setterName = 'xpc_qsGetterOnlyNativeStub' 1.1017 + else: 1.1018 + setterName = (attr.iface.name + '_' 1.1019 + + header.attributeNativeName(attr, False)) 1.1020 + writeQuickStub(f, customMethodCalls, stringtable, attr, setterName, 1.1021 + isSetter=True) 1.1022 + 1.1023 + ps = ('{%d, %s, %s}' 1.1024 + % (stringtable.stringIndex(attr.name), getterName, setterName)) 1.1025 + return ps 1.1026 + 1.1027 +def writeMethodStub(f, customMethodCalls, stringtable, method): 1.1028 + """ Write a method stub to `f`. Return an xpc_qsFunctionSpec initializer. """ 1.1029 + 1.1030 + stubName = method.iface.name + '_' + header.methodNativeName(method) 1.1031 + writeQuickStub(f, customMethodCalls, stringtable, method, stubName) 1.1032 + fs = '{%d, %d, %s}' % (stringtable.stringIndex(method.name), 1.1033 + len(method.params), stubName) 1.1034 + return fs 1.1035 + 1.1036 +def writeStubsForInterface(f, customMethodCalls, stringtable, iface): 1.1037 + f.write("// === interface %s\n\n" % iface.name) 1.1038 + propspecs = [] 1.1039 + funcspecs = [] 1.1040 + for member in iface.stubMembers: 1.1041 + if member.kind == 'attribute': 1.1042 + ps = writeAttrStubs(f, customMethodCalls, stringtable, member) 1.1043 + propspecs.append(ps) 1.1044 + elif member.kind == 'method': 1.1045 + fs = writeMethodStub(f, customMethodCalls, stringtable, member) 1.1046 + funcspecs.append(fs) 1.1047 + else: 1.1048 + raise TypeError('expected attribute or method, not %r' 1.1049 + % member.__class__.__name__) 1.1050 + 1.1051 + iface.propspecs = propspecs 1.1052 + iface.funcspecs = funcspecs 1.1053 + 1.1054 +def hashIID(iid): 1.1055 + # See nsIDKey::HashCode in nsHashtable.h. 1.1056 + return int(iid[:8], 16) 1.1057 + 1.1058 +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})$') 1.1059 + 1.1060 +def writeResultXPCInterfacesArray(f, conf, resulttypes): 1.1061 + f.write("// === XPCNativeInterface cache \n\n") 1.1062 + count = len(resulttypes) 1.1063 + if count > 0: 1.1064 + f.write("static XPCNativeInterface* interfaces[%d];\n\n" % count) 1.1065 + f.write("void %s_MarkInterfaces()\n" 1.1066 + "{\n" % conf.name) 1.1067 + if count > 0: 1.1068 + f.write(" for (uint32_t i = 0; i < %d; ++i)\n" 1.1069 + " if (interfaces[i])\n" 1.1070 + " interfaces[i]->Mark();\n" % count) 1.1071 + f.write("}\n") 1.1072 + f.write("void %s_ClearInterfaces()\n" 1.1073 + "{\n" % conf.name) 1.1074 + if count > 0: 1.1075 + f.write(" memset(interfaces, 0, %d * " 1.1076 + "sizeof(XPCNativeInterface*));\n" % count) 1.1077 + f.write("}\n\n") 1.1078 + i = 0 1.1079 + for type in resulttypes: 1.1080 + f.write("static const uint32_t k_%s = %d;\n" % (type, i)) 1.1081 + i += 1 1.1082 + if count > 0: 1.1083 + f.write("\n\n") 1.1084 + 1.1085 +def writeSpecs(f, elementType, varname, spec_type, spec_indices, interfaces): 1.1086 + index = 0 1.1087 + f.write("static const %s %s[] = {\n" % (elementType, varname)) 1.1088 + for iface in interfaces: 1.1089 + specs = getattr(iface, spec_type) 1.1090 + if specs: 1.1091 + spec_indices[iface.name] = index 1.1092 + f.write(" // %s (index %d)\n" % (iface.name,index)) 1.1093 + for s in specs: 1.1094 + f.write(" %s,\n" % s) 1.1095 + index += len(specs) 1.1096 + f.write("};\n\n") 1.1097 + 1.1098 +def writeDefiner(f, conf, stringtable, interfaces): 1.1099 + f.write("// === Definer\n\n") 1.1100 + 1.1101 + # Write out the properties and functions 1.1102 + propspecs_indices = {} 1.1103 + funcspecs_indices = {} 1.1104 + prop_array_name = "all_properties" 1.1105 + func_array_name = "all_functions" 1.1106 + writeSpecs(f, "xpc_qsPropertySpec", prop_array_name, 1.1107 + "propspecs", propspecs_indices, interfaces) 1.1108 + writeSpecs(f, "xpc_qsFunctionSpec", func_array_name, 1.1109 + "funcspecs", funcspecs_indices, interfaces) 1.1110 + 1.1111 + # generate the static hash table 1.1112 + loadFactor = 0.6 1.1113 + size = int(len(interfaces) / loadFactor) 1.1114 + buckets = [[] for i in range(size)] 1.1115 + for iface in interfaces: 1.1116 + # This if-statement discards interfaces specified with 1.1117 + # "nsInterfaceName.*" that don't have any stub-able members. 1.1118 + if iface.stubMembers or iface.newBindingProperties: 1.1119 + h = hashIID(iface.attributes.uuid) 1.1120 + buckets[h % size].append(iface) 1.1121 + 1.1122 + # Calculate where each interface's entry will show up in tableData. Where 1.1123 + # there are hash collisions, the extra entries are added at the end of the 1.1124 + # table. 1.1125 + entryIndexes = {} 1.1126 + arraySize = size 1.1127 + for i, bucket in enumerate(buckets): 1.1128 + if bucket: 1.1129 + entryIndexes[bucket[0].attributes.uuid] = i 1.1130 + for iface in bucket[1:]: 1.1131 + entryIndexes[iface.attributes.uuid] = arraySize 1.1132 + arraySize += 1 1.1133 + 1.1134 + entries = [" {{0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}, " 1.1135 + "0, 0, 0, 0, nullptr, XPC_QS_NULL_INDEX, XPC_QS_NULL_INDEX}" 1.1136 + for i in range(arraySize)] 1.1137 + for i, bucket in enumerate(buckets): 1.1138 + for j, iface in enumerate(bucket): 1.1139 + # iid field 1.1140 + uuid = iface.attributes.uuid.lower() 1.1141 + m = uuid_re.match(uuid) 1.1142 + assert m is not None 1.1143 + m0, m1, m2, m3, m4 = m.groups() 1.1144 + m3arr = ('{0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s}' 1.1145 + % (m3[0:2], m3[2:4], m4[0:2], m4[2:4], 1.1146 + m4[4:6], m4[6:8], m4[8:10], m4[10:12])) 1.1147 + iid = ('{0x%s, 0x%s, 0x%s, %s}' % (m0, m1, m2, m3arr)) 1.1148 + 1.1149 + # properties fields 1.1150 + prop_index = 0 1.1151 + prop_n_entries = 0 1.1152 + if iface.propspecs: 1.1153 + prop_index = propspecs_indices[iface.name] 1.1154 + prop_n_entries = len(iface.propspecs) 1.1155 + 1.1156 + # member fields 1.1157 + func_index = 0 1.1158 + func_n_entries = 0 1.1159 + if iface.funcspecs: 1.1160 + func_index = funcspecs_indices[iface.name] 1.1161 + func_n_entries = len(iface.funcspecs) 1.1162 + 1.1163 + # parentInterface field 1.1164 + baseName = iface.base 1.1165 + while baseName is not None: 1.1166 + piface = iface.idl.getName(baseName, None) 1.1167 + k = entryIndexes.get(piface.attributes.uuid) 1.1168 + if k is not None: 1.1169 + parentInterface = str(k) 1.1170 + break 1.1171 + baseName = piface.base 1.1172 + else: 1.1173 + parentInterface = "XPC_QS_NULL_INDEX" 1.1174 + 1.1175 + # chain field 1.1176 + if j == len(bucket) - 1: 1.1177 + chain = "XPC_QS_NULL_INDEX" 1.1178 + else: 1.1179 + k = entryIndexes[bucket[j+1].attributes.uuid] 1.1180 + chain = str(k) 1.1181 + 1.1182 + # add entry 1.1183 + entry = " /* %s */ {%s, %d, %d, %d, %d, %s, %s, %s}" % ( 1.1184 + iface.name, iid, prop_index, prop_n_entries, 1.1185 + func_index, func_n_entries, iface.newBindingProperties, 1.1186 + parentInterface, chain) 1.1187 + entries[entryIndexes[iface.attributes.uuid]] = entry 1.1188 + 1.1189 + f.write("static const xpc_qsHashEntry tableData[] = {\n") 1.1190 + f.write(",\n".join(entries)) 1.1191 + f.write("\n };\n\n") 1.1192 + f.write("// Make sure our table indices aren't overflowed\n" 1.1193 + "PR_STATIC_ASSERT((sizeof(tableData) / sizeof(tableData[0])) < (1 << (8 * sizeof(tableData[0].parentInterface))));\n" 1.1194 + "PR_STATIC_ASSERT((sizeof(tableData) / sizeof(tableData[0])) < (1 << (8 * sizeof(tableData[0].chain))));\n\n") 1.1195 + 1.1196 + # The string table for property and method names. 1.1197 + table_name = "stringtab" 1.1198 + stringtable.writeDefinition(f, table_name) 1.1199 + structNames = [prop_array_name, func_array_name] 1.1200 + for name in structNames: 1.1201 + f.write("PR_STATIC_ASSERT(sizeof(%s) < (1 << (8 * sizeof(%s[0].name_index))));\n" 1.1202 + % (table_name, name)) 1.1203 + f.write("\n") 1.1204 + 1.1205 + # the definer function (entry point to this quick stubs file) 1.1206 + f.write("namespace xpc {\n") 1.1207 + f.write("bool %s_DefineQuickStubs(" % conf.name) 1.1208 + f.write("JSContext *cx, JSObject *proto, unsigned flags, uint32_t count, " 1.1209 + "const nsID **iids)\n" 1.1210 + "{\n") 1.1211 + f.write(" return !!xpc_qsDefineQuickStubs(" 1.1212 + "cx, proto, flags, count, iids, %d, tableData, %s, %s, %s);\n" % ( 1.1213 + size, prop_array_name, func_array_name, table_name)) 1.1214 + f.write("}\n") 1.1215 + f.write("} // namespace xpc\n\n\n") 1.1216 + 1.1217 + 1.1218 +stubTopTemplate = '''\ 1.1219 +/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */ 1.1220 +#include "jsapi.h" 1.1221 +#include "qsWinUndefs.h" 1.1222 +#include "qsObjectHelper.h" 1.1223 +#include "nsID.h" 1.1224 +#include "%s" 1.1225 +#include "nsCOMPtr.h" 1.1226 +#include "xpcprivate.h" // for XPCCallContext 1.1227 +#include "XPCQuickStubs.h" 1.1228 +#include <algorithm> 1.1229 +''' 1.1230 + 1.1231 +def writeStubFile(filename, headerFilename, conf, interfaces): 1.1232 + print "Creating stub file", filename 1.1233 + makeutils.targets.append(filename) 1.1234 + 1.1235 + f = open(filename, 'w') 1.1236 + filesIncluded = set() 1.1237 + 1.1238 + def includeType(type): 1.1239 + type = unaliasType(type) 1.1240 + if type.kind in ('builtin', 'native'): 1.1241 + return None 1.1242 + file = conf.irregularFilenames.get(type.name, type.name) + '.h' 1.1243 + if file not in filesIncluded: 1.1244 + f.write('#include "%s"\n' % file) 1.1245 + filesIncluded.add(file) 1.1246 + return type 1.1247 + 1.1248 + def writeIncludesForMember(member): 1.1249 + assert member.kind in ('attribute', 'method') 1.1250 + resulttype = includeType(member.realtype) 1.1251 + if member.kind == 'method': 1.1252 + for p in member.params: 1.1253 + includeType(p.realtype) 1.1254 + return resulttype 1.1255 + 1.1256 + def writeIncludesForInterface(iface): 1.1257 + assert iface.kind == 'interface' 1.1258 + resulttypes = [] 1.1259 + for member in iface.stubMembers: 1.1260 + resulttype = writeIncludesForMember(member) 1.1261 + if resulttype is not None and not isVariantType(resulttype): 1.1262 + resulttypes.append(resulttype.name) 1.1263 + 1.1264 + includeType(iface) 1.1265 + 1.1266 + return resulttypes 1.1267 + 1.1268 + try: 1.1269 + f.write(stubTopTemplate % os.path.basename(headerFilename)) 1.1270 + resulttypes = [] 1.1271 + for iface in interfaces: 1.1272 + resulttypes.extend(writeIncludesForInterface(iface)) 1.1273 + for customInclude in conf.customIncludes: 1.1274 + f.write('#include "%s"\n' % customInclude) 1.1275 + f.write("\n\n") 1.1276 + writeResultXPCInterfacesArray(f, conf, frozenset(resulttypes)) 1.1277 + stringtable = StringTable() 1.1278 + for iface in interfaces: 1.1279 + writeStubsForInterface(f, conf.customMethodCalls, stringtable, iface) 1.1280 + writeDefiner(f, conf, stringtable, interfaces) 1.1281 + finally: 1.1282 + f.close() 1.1283 + 1.1284 +def main(): 1.1285 + from optparse import OptionParser 1.1286 + o = OptionParser(usage="usage: %prog [options] configfile") 1.1287 + o.add_option('-o', "--stub-output", 1.1288 + type='string', dest='stub_output', default=None, 1.1289 + help="Quick stub C++ source output file", metavar="FILE") 1.1290 + o.add_option('--header-output', type='string', default=None, 1.1291 + help="Quick stub header output file", metavar="FILE") 1.1292 + o.add_option('--makedepend-output', type='string', default=None, 1.1293 + help="gnumake dependencies output file", metavar="FILE") 1.1294 + o.add_option('--idlpath', type='string', default='.', 1.1295 + help="colon-separated directories to search for idl files", 1.1296 + metavar="PATH") 1.1297 + o.add_option('--cachedir', dest='cachedir', default='', 1.1298 + help="Directory in which to cache lex/parse tables.") 1.1299 + o.add_option("--verbose-errors", action='store_true', default=False, 1.1300 + help="When an error happens, display the Python traceback.") 1.1301 + (options, filenames) = o.parse_args() 1.1302 + 1.1303 + if len(filenames) != 1: 1.1304 + o.error("Exactly one config filename is needed.") 1.1305 + filename = filenames[0] 1.1306 + 1.1307 + if options.stub_output is None: 1.1308 + if filename.endswith('.qsconf') or filename.endswith('.py'): 1.1309 + options.stub_output = filename.rsplit('.', 1)[0] + '.cpp' 1.1310 + else: 1.1311 + options.stub_output = filename + '.cpp' 1.1312 + if options.header_output is None: 1.1313 + options.header_output = re.sub(r'(\.c|\.cpp)?$', '.h', 1.1314 + options.stub_output) 1.1315 + 1.1316 + if options.cachedir != '': 1.1317 + sys.path.append(options.cachedir) 1.1318 + if not os.path.isdir(options.cachedir): 1.1319 + os.makedirs(options.cachedir) 1.1320 + 1.1321 + try: 1.1322 + includePath = options.idlpath.split(':') 1.1323 + conf, interfaces = readConfigFile(filename, 1.1324 + includePath=includePath, 1.1325 + cachedir=options.cachedir) 1.1326 + writeStubFile(options.stub_output, options.header_output, 1.1327 + conf, interfaces) 1.1328 + writeHeaderFile(options.header_output, conf.name) 1.1329 + if options.makedepend_output is not None: 1.1330 + makeutils.writeMakeDependOutput(options.makedepend_output) 1.1331 + except Exception, exc: 1.1332 + if options.verbose_errors: 1.1333 + raise 1.1334 + elif isinstance(exc, (UserError, xpidl.IDLError)): 1.1335 + warn(str(exc)) 1.1336 + elif isinstance(exc, OSError): 1.1337 + warn("%s: %s" % (exc.__class__.__name__, exc)) 1.1338 + else: 1.1339 + raise 1.1340 + sys.exit(1) 1.1341 + 1.1342 +if __name__ == '__main__': 1.1343 + main()