js/xpconnect/src/qsgen.py

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 #!/usr/bin/env/python
michael@0 2 # qsgen.py - Generate XPConnect quick stubs.
michael@0 3 #
michael@0 4 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 5 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 7
michael@0 8 # =About quick stubs=
michael@0 9 # qsgen.py generates "quick stubs", custom SpiderMonkey getters, setters, and
michael@0 10 # methods for specified XPCOM interface members. These quick stubs serve at
michael@0 11 # runtime as replacements for the XPConnect functions XPC_WN_GetterSetter and
michael@0 12 # XPC_WN_CallMethod, which are the extremely generic (and slow) SpiderMonkey
michael@0 13 # getter/setter/methods otherwise used for all XPCOM member accesses from JS.
michael@0 14 #
michael@0 15 # There are two ways quick stubs win:
michael@0 16 # 1. Pure, transparent optimization by partial evaluation.
michael@0 17 # 2. Cutting corners.
michael@0 18 #
michael@0 19 # == Partial evaluation ==
michael@0 20 # Partial evaluation is when you execute part of a program early (before or at
michael@0 21 # compile time) so that you don't have to execute it at run time. In this
michael@0 22 # case, everything that involves interpreting xptcall data (for example, the
michael@0 23 # big methodInfo loops in XPCWrappedNative::CallMethod and the switch statement
michael@0 24 # in XPCConert::JSData2Native) might as well happen at build time, since all
michael@0 25 # the type information for any given member is already known. That's what this
michael@0 26 # script does. It gets the information from IDL instead of XPT files. Apart
michael@0 27 # from that, the code in this script is very similar to what you'll find in
michael@0 28 # XPConnect itself. The advantage is that it runs once, at build time, not in
michael@0 29 # tight loops at run time.
michael@0 30 #
michael@0 31 # == Cutting corners ==
michael@0 32 # The XPConnect versions have to be slow because they do tons of work that's
michael@0 33 # only necessary in a few cases. The quick stubs skip a lot of that work. So
michael@0 34 # quick stubs necessarily differ from XPConnect in potentially observable ways.
michael@0 35 # For many specific interface members, the differences are not observable from
michael@0 36 # scripts or don't matter enough to worry about; but you do have to be careful
michael@0 37 # which members you decide to generate quick stubs for.
michael@0 38 #
michael@0 39 # The complete list of known differences, as of this writing, after an
michael@0 40 # assiduous search:
michael@0 41 #
michael@0 42 # - Quick stubs affect the handling of naming conflicts--that is, which C++
michael@0 43 # method gets called when a script uses an XPCOM feature that is declared in
michael@0 44 # more than one of the interfaces the object implements. Without quick
michael@0 45 # stubs, XPConnect just walks the interfaces in the order they're listed by
michael@0 46 # nsClassInfo. You get the first interface that implements a feature with
michael@0 47 # that name. With quick stubs, it's the same except that non-quick-stubbed
michael@0 48 # features are shadowed.
michael@0 49 #
michael@0 50 # - Quick stub methods are JSFastNative, which means that when a quick stub
michael@0 51 # method is called, no JS stack frame is created. This doesn't affect
michael@0 52 # Mozilla security checks because they look for scripted JSStackFrames, not
michael@0 53 # native ones.
michael@0 54 #
michael@0 55 # It does affect the 'stack' property of JavaScript exceptions, though: the
michael@0 56 # stubbed member will not appear. (Note that if the stubbed member itself
michael@0 57 # fails, the member name will appear in the 'message' property.)
michael@0 58 #
michael@0 59 # - Many quick stubs don't create an XPCCallContext. In those cases, no entry
michael@0 60 # is added to the XPCCallContext stack. So native implementations of
michael@0 61 # quick-stubbed methods must avoid nsXPConnect::GetCurrentNativeCallContext.
michael@0 62 #
michael@0 63 # (Even when a quick stub does have an XPCCallContext, it never pushes it all
michael@0 64 # the way to READY_TO_CALL state, so a lot of its members are garbage. But
michael@0 65 # this doesn't endanger native implementations of non-quick-stubbed methods
michael@0 66 # that use GetCurrentNativeCallContext and are called indirectly from
michael@0 67 # quick-stubbed methods, because only the current top XPCCallContext is
michael@0 68 # exposed--nsAXPCNativeCallContext does not expose
michael@0 69 # XPCCallContext::GetPrevCallContext.)
michael@0 70 #
michael@0 71 # - Quick stubs never suspend the JS request. So they are only suitable for
michael@0 72 # main-thread-only interfaces.
michael@0 73 #
michael@0 74 # - Quick stubs don't call XPCContext::SetLastResult. This is visible on the
michael@0 75 # Components object.
michael@0 76 #
michael@0 77 # - Quick stubs skip a security check that XPConnect does in
michael@0 78 # XPCWrappedNative::CallMethod. This means the security manager doesn't have
michael@0 79 # an opportunity to veto accesses to members for which quick stubs exist.
michael@0 80 #
michael@0 81 # - There are many features of IDL that XPConnect supports but qsgen does not,
michael@0 82 # including dependent types, arrays, and out parameters.
michael@0 83
michael@0 84
michael@0 85 import xpidl
michael@0 86 import header
michael@0 87 import makeutils
michael@0 88 import os, re
michael@0 89 import sys
michael@0 90
michael@0 91 # === Preliminaries
michael@0 92
michael@0 93 def warn(msg):
michael@0 94 sys.stderr.write(msg + '\n')
michael@0 95
michael@0 96 def unaliasType(t):
michael@0 97 while t.kind == 'typedef':
michael@0 98 t = t.realtype
michael@0 99 assert t is not None
michael@0 100 return t
michael@0 101
michael@0 102 def isVoidType(type):
michael@0 103 """ Return True if the given xpidl type is void. """
michael@0 104 return type.kind == 'builtin' and type.name == 'void'
michael@0 105
michael@0 106 def isInterfaceType(t):
michael@0 107 t = unaliasType(t)
michael@0 108 assert t.kind in ('builtin', 'native', 'interface', 'forward')
michael@0 109 return t.kind in ('interface', 'forward')
michael@0 110
michael@0 111 def isSpecificInterfaceType(t, name):
michael@0 112 """ True if `t` is an interface type with the given name, or a forward
michael@0 113 declaration or typedef aliasing it.
michael@0 114
michael@0 115 `name` must not be the name of a typedef but the actual name of the
michael@0 116 interface.
michael@0 117 """
michael@0 118 t = unaliasType(t)
michael@0 119 return t.kind in ('interface', 'forward') and t.name == name
michael@0 120
michael@0 121 def getBuiltinOrNativeTypeName(t):
michael@0 122 t = unaliasType(t)
michael@0 123 if t.kind == 'builtin':
michael@0 124 return t.name
michael@0 125 elif t.kind == 'native':
michael@0 126 assert t.specialtype is not None
michael@0 127 return '[%s]' % t.specialtype
michael@0 128 else:
michael@0 129 return None
michael@0 130
michael@0 131
michael@0 132 # === Reading the file
michael@0 133
michael@0 134 class UserError(Exception):
michael@0 135 pass
michael@0 136
michael@0 137 def findIDL(includePath, irregularFilenames, interfaceName):
michael@0 138 filename = irregularFilenames.get(interfaceName, interfaceName) + '.idl'
michael@0 139 for d in includePath:
michael@0 140 # Not os.path.join: we need a forward slash even on Windows because
michael@0 141 # this filename ends up in makedepend output.
michael@0 142 path = d + '/' + filename
michael@0 143 if os.path.exists(path):
michael@0 144 return path
michael@0 145 raise UserError("No IDL file found for interface %s "
michael@0 146 "in include path %r"
michael@0 147 % (interfaceName, includePath))
michael@0 148
michael@0 149 def loadIDL(parser, includePath, filename):
michael@0 150 makeutils.dependencies.append(filename)
michael@0 151 text = open(filename, 'r').read()
michael@0 152 idl = parser.parse(text, filename=filename)
michael@0 153 idl.resolve(includePath, parser)
michael@0 154 return idl
michael@0 155
michael@0 156 def removeStubMember(memberId, member):
michael@0 157 if member not in member.iface.stubMembers:
michael@0 158 raise UserError("Trying to remove member %s from interface %s, but it was never added"
michael@0 159 % (member.name, member.iface.name))
michael@0 160 member.iface.stubMembers.remove(member)
michael@0 161
michael@0 162 def addStubMember(memberId, member):
michael@0 163 if member.kind == 'method' and not member.implicit_jscontext and not isVariantType(member.realtype):
michael@0 164 for param in member.params:
michael@0 165 for attrname, value in vars(param).items():
michael@0 166 if value is True:
michael@0 167 if attrname == 'optional':
michael@0 168 continue
michael@0 169
michael@0 170 raise UserError("Method %s, parameter %s: "
michael@0 171 "unrecognized property %r"
michael@0 172 % (memberId, param.name, attrname))
michael@0 173
michael@0 174 # Add this member to the list.
michael@0 175 member.iface.stubMembers.append(member)
michael@0 176
michael@0 177 def checkStubMember(member):
michael@0 178 memberId = member.iface.name + "." + member.name
michael@0 179 if member.kind not in ('method', 'attribute'):
michael@0 180 raise UserError("Member %s is %r, not a method or attribute."
michael@0 181 % (memberId, member.kind))
michael@0 182 if member.noscript:
michael@0 183 raise UserError("%s %s is noscript."
michael@0 184 % (member.kind.capitalize(), memberId))
michael@0 185 if member.kind == 'method' and member.notxpcom:
michael@0 186 raise UserError(
michael@0 187 "%s %s: notxpcom methods are not supported."
michael@0 188 % (member.kind.capitalize(), memberId))
michael@0 189
michael@0 190 if (member.kind == 'attribute'
michael@0 191 and not member.readonly
michael@0 192 and isSpecificInterfaceType(member.realtype, 'nsIVariant')):
michael@0 193 raise UserError(
michael@0 194 "Attribute %s: Non-readonly attributes of type nsIVariant "
michael@0 195 "are not supported."
michael@0 196 % memberId)
michael@0 197
michael@0 198 # Check for unknown properties.
michael@0 199 for attrname, value in vars(member).items():
michael@0 200 if value is True and attrname not in ('readonly','optional_argc',
michael@0 201 'implicit_jscontext',
michael@0 202 'getter', 'stringifier'):
michael@0 203 raise UserError("%s %s: unrecognized property %r"
michael@0 204 % (member.kind.capitalize(), memberId,
michael@0 205 attrname))
michael@0 206
michael@0 207 def parseMemberId(memberId):
michael@0 208 """ Split the geven member id into its parts. """
michael@0 209 pieces = memberId.split('.')
michael@0 210 if len(pieces) < 2:
michael@0 211 raise UserError("Member %r: Missing dot." % memberId)
michael@0 212 if len(pieces) > 2:
michael@0 213 raise UserError("Member %r: Dots out of control." % memberId)
michael@0 214 return tuple(pieces)
michael@0 215
michael@0 216 class Configuration:
michael@0 217 def __init__(self, filename, includePath):
michael@0 218 self.includePath = includePath
michael@0 219 config = {}
michael@0 220 execfile(filename, config)
michael@0 221 # required settings
michael@0 222 for name in ('name', 'members'):
michael@0 223 if name not in config:
michael@0 224 raise UserError(filename + ": `%s` was not defined." % name)
michael@0 225 setattr(self, name, config[name])
michael@0 226 # optional settings
michael@0 227 self.irregularFilenames = config.get('irregularFilenames', {})
michael@0 228 self.customIncludes = config.get('customIncludes', [])
michael@0 229 self.customMethodCalls = config.get('customMethodCalls', {})
michael@0 230 self.newBindingProperties = config.get('newBindingProperties', {})
michael@0 231
michael@0 232 def readConfigFile(filename, includePath, cachedir):
michael@0 233 # Read the config file.
michael@0 234 conf = Configuration(filename, includePath)
michael@0 235
michael@0 236 # Now read IDL files to connect the information in the config file to
michael@0 237 # actual XPCOM interfaces, methods, and attributes.
michael@0 238 interfaces = []
michael@0 239 interfacesByName = {}
michael@0 240 parser = xpidl.IDLParser(cachedir)
michael@0 241
michael@0 242 def getInterface(interfaceName, errorLoc):
michael@0 243 iface = interfacesByName.get(interfaceName)
michael@0 244 if iface is None:
michael@0 245 idlFile = findIDL(conf.includePath, conf.irregularFilenames,
michael@0 246 interfaceName)
michael@0 247 idl = loadIDL(parser, conf.includePath, idlFile)
michael@0 248 if not idl.hasName(interfaceName):
michael@0 249 raise UserError("The interface %s was not found "
michael@0 250 "in the idl file %r."
michael@0 251 % (interfaceName, idlFile))
michael@0 252 iface = idl.getName(interfaceName, errorLoc)
michael@0 253 iface.stubMembers = []
michael@0 254 iface.newBindingProperties = 'nullptr'
michael@0 255 interfaces.append(iface)
michael@0 256 interfacesByName[interfaceName] = iface
michael@0 257 return iface
michael@0 258
michael@0 259 stubbedInterfaces = []
michael@0 260
michael@0 261 for memberId in conf.members:
michael@0 262 add = True
michael@0 263 interfaceName, memberName = parseMemberId(memberId)
michael@0 264
michael@0 265 # If the interfaceName starts with -, then remove this entry from the list
michael@0 266 if interfaceName[0] == '-':
michael@0 267 add = False
michael@0 268 interfaceName = interfaceName[1:]
michael@0 269
michael@0 270 iface = getInterface(interfaceName, errorLoc='looking for %r' % memberId)
michael@0 271
michael@0 272 if not iface.attributes.scriptable:
michael@0 273 raise UserError("Interface %s is not scriptable." % interfaceName)
michael@0 274
michael@0 275 if memberName == '*':
michael@0 276 if not add:
michael@0 277 raise UserError("Can't use negation in stub list with wildcard, in %s.*" % interfaceName)
michael@0 278
michael@0 279 # Stub all scriptable members of this interface.
michael@0 280 for member in iface.members:
michael@0 281 if member.kind in ('method', 'attribute') and not member.noscript:
michael@0 282 addStubMember(iface.name + '.' + member.name, member)
michael@0 283
michael@0 284 if member.iface not in stubbedInterfaces:
michael@0 285 stubbedInterfaces.append(member.iface)
michael@0 286 else:
michael@0 287 # Look up a member by name.
michael@0 288 if memberName not in iface.namemap:
michael@0 289 idlFile = iface.idl.parser.lexer.filename
michael@0 290 raise UserError("Interface %s has no member %r. "
michael@0 291 "(See IDL file %r.)"
michael@0 292 % (interfaceName, memberName, idlFile))
michael@0 293 member = iface.namemap.get(memberName, None)
michael@0 294 if add:
michael@0 295 if member in iface.stubMembers:
michael@0 296 raise UserError("Member %s is specified more than once."
michael@0 297 % memberId)
michael@0 298
michael@0 299 addStubMember(memberId, member)
michael@0 300 if member.iface not in stubbedInterfaces:
michael@0 301 stubbedInterfaces.append(member.iface)
michael@0 302 else:
michael@0 303 removeStubMember(memberId, member)
michael@0 304
michael@0 305 for (interfaceName, v) in conf.newBindingProperties.iteritems():
michael@0 306 iface = getInterface(interfaceName, errorLoc='looking for %r' % interfaceName)
michael@0 307 iface.newBindingProperties = v
michael@0 308 if iface not in stubbedInterfaces:
michael@0 309 stubbedInterfaces.append(iface)
michael@0 310
michael@0 311 # Now go through and check all the interfaces' members
michael@0 312 for iface in stubbedInterfaces:
michael@0 313 for member in iface.stubMembers:
michael@0 314 checkStubMember(member)
michael@0 315
michael@0 316 return conf, interfaces
michael@0 317
michael@0 318
michael@0 319 # === Generating the header file
michael@0 320
michael@0 321 def writeHeaderFile(filename, name):
michael@0 322 print "Creating header file", filename
michael@0 323
michael@0 324 headerMacro = '__gen_%s__' % filename.replace('.', '_')
michael@0 325 f = open(filename, 'w')
michael@0 326 try:
michael@0 327 f.write("/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
michael@0 328 "#ifndef " + headerMacro + "\n"
michael@0 329 "#define " + headerMacro + "\n\n"
michael@0 330 "bool " + name + "_DefineQuickStubs("
michael@0 331 "JSContext *cx, JSObject *proto, unsigned flags, "
michael@0 332 "uint32_t count, const nsID **iids);\n\n"
michael@0 333 "void " + name + "_MarkInterfaces();\n\n"
michael@0 334 "void " + name + "_ClearInterfaces();\n\n"
michael@0 335 "inline void " + name + "_InitInterfaces()\n"
michael@0 336 "{\n"
michael@0 337 " " + name + "_ClearInterfaces();\n"
michael@0 338 "}\n\n"
michael@0 339 "#endif\n")
michael@0 340 finally:
michael@0 341 f.close()
michael@0 342
michael@0 343 # === Generating the source file
michael@0 344
michael@0 345 class StringTable:
michael@0 346 def __init__(self):
michael@0 347 self.current_index = 0;
michael@0 348 self.table = {}
michael@0 349 self.reverse_table = {}
michael@0 350
michael@0 351 def c_strlen(self, string):
michael@0 352 return len(string) + 1
michael@0 353
michael@0 354 def stringIndex(self, string):
michael@0 355 if string in self.table:
michael@0 356 return self.table[string]
michael@0 357 else:
michael@0 358 result = self.current_index
michael@0 359 self.table[string] = result
michael@0 360 self.current_index += self.c_strlen(string)
michael@0 361 return result
michael@0 362
michael@0 363 def writeDefinition(self, f, name):
michael@0 364 entries = self.table.items()
michael@0 365 entries.sort(key=lambda x:x[1])
michael@0 366 # Avoid null-in-string warnings with GCC and potentially
michael@0 367 # overlong string constants; write everything out the long way.
michael@0 368 def explodeToCharArray(string):
michael@0 369 return ", ".join(map(lambda x:"'%s'" % x, string))
michael@0 370 f.write("static const char %s[] = {\n" % name)
michael@0 371 for (string, offset) in entries[:-1]:
michael@0 372 f.write(" /* %5d */ %s, '\\0',\n"
michael@0 373 % (offset, explodeToCharArray(string)))
michael@0 374 f.write(" /* %5d */ %s, '\\0' };\n\n"
michael@0 375 % (entries[-1][1], explodeToCharArray(entries[-1][0])))
michael@0 376 f.write("const char* xpc_qsStringTable = %s;\n\n" % name);
michael@0 377
michael@0 378 def substitute(template, vals):
michael@0 379 """ Simple replacement for string.Template, which isn't in Python 2.3. """
michael@0 380 def replacement(match):
michael@0 381 return vals[match.group(1)]
michael@0 382 return re.sub(r'\${(\w+)}', replacement, template)
michael@0 383
michael@0 384 # From JSData2Native.
michael@0 385 argumentUnboxingTemplates = {
michael@0 386 'octet':
michael@0 387 " uint32_t ${name}_u32;\n"
michael@0 388 " if (!JS::ToUint32(cx, ${argVal}, &${name}_u32))\n"
michael@0 389 " return false;\n"
michael@0 390 " uint8_t ${name} = (uint8_t) ${name}_u32;\n",
michael@0 391
michael@0 392 'short':
michael@0 393 " int32_t ${name}_i32;\n"
michael@0 394 " if (!JS::ToInt32(cx, ${argVal}, &${name}_i32))\n"
michael@0 395 " return false;\n"
michael@0 396 " int16_t ${name} = (int16_t) ${name}_i32;\n",
michael@0 397
michael@0 398 'unsigned short':
michael@0 399 " uint32_t ${name}_u32;\n"
michael@0 400 " if (!JS::ToUint32(cx, ${argVal}, &${name}_u32))\n"
michael@0 401 " return false;\n"
michael@0 402 " uint16_t ${name} = (uint16_t) ${name}_u32;\n",
michael@0 403
michael@0 404 'long':
michael@0 405 " int32_t ${name};\n"
michael@0 406 " if (!JS::ToInt32(cx, ${argVal}, &${name}))\n"
michael@0 407 " return false;\n",
michael@0 408
michael@0 409 'unsigned long':
michael@0 410 " uint32_t ${name};\n"
michael@0 411 " if (!JS::ToUint32(cx, ${argVal}, &${name}))\n"
michael@0 412 " return false;\n",
michael@0 413
michael@0 414 'long long':
michael@0 415 " int64_t ${name};\n"
michael@0 416 " if (!JS::ToInt64(cx, ${argVal}, &${name}))\n"
michael@0 417 " return false;\n",
michael@0 418
michael@0 419 'unsigned long long':
michael@0 420 " uint64_t ${name};\n"
michael@0 421 " if (!JS::ToUint64(cx, ${argVal}, &${name}))\n"
michael@0 422 " return false;\n",
michael@0 423
michael@0 424 'float':
michael@0 425 " double ${name}_dbl;\n"
michael@0 426 " if (!JS::ToNumber(cx, ${argVal}, &${name}_dbl))\n"
michael@0 427 " return false;\n"
michael@0 428 " float ${name} = (float) ${name}_dbl;\n",
michael@0 429
michael@0 430 'double':
michael@0 431 " double ${name};\n"
michael@0 432 " if (!JS::ToNumber(cx, ${argVal}, &${name}))\n"
michael@0 433 " return false;\n",
michael@0 434
michael@0 435 'boolean':
michael@0 436 " bool ${name} = JS::ToBoolean(${argVal});\n",
michael@0 437
michael@0 438 '[astring]':
michael@0 439 " xpc_qsAString ${name}(cx, ${argVal}, ${argPtr}, ${notPassed});\n"
michael@0 440 " if (!${name}.IsValid())\n"
michael@0 441 " return false;\n",
michael@0 442
michael@0 443 '[domstring]':
michael@0 444 " xpc_qsDOMString ${name}(cx, ${argVal},\n"
michael@0 445 " ${argPtr}, ${notPassed},\n"
michael@0 446 " xpc_qsDOMString::e${nullBehavior},\n"
michael@0 447 " xpc_qsDOMString::e${undefinedBehavior});\n"
michael@0 448 " if (!${name}.IsValid())\n"
michael@0 449 " return false;\n",
michael@0 450
michael@0 451 'string':
michael@0 452 " JSAutoByteString ${name}_bytes;\n"
michael@0 453 " if (!xpc_qsJsvalToCharStr(cx, ${argVal}, &${name}_bytes))\n"
michael@0 454 " return false;\n"
michael@0 455 " char *${name} = ${name}_bytes.ptr();\n",
michael@0 456
michael@0 457 '[cstring]':
michael@0 458 " xpc_qsACString ${name}(cx, ${argVal}, ${argPtr}, ${notPassed});\n"
michael@0 459 " if (!${name}.IsValid())\n"
michael@0 460 " return false;\n",
michael@0 461
michael@0 462 '[utf8string]':
michael@0 463 " xpc_qsAUTF8String ${name}(cx, ${argVal}, ${argPtr}, ${notPassed});\n"
michael@0 464 " if (!${name}.IsValid())\n"
michael@0 465 " return false;\n",
michael@0 466
michael@0 467 '[jsval]':
michael@0 468 " JS::RootedValue ${name}(cx, ${argVal});\n"
michael@0 469 }
michael@0 470
michael@0 471 # From JSData2Native.
michael@0 472 #
michael@0 473 # Omitted optional arguments are treated as though the caller had passed JS
michael@0 474 # `null`; this behavior is from XPCWrappedNative::CallMethod. The 'jsval' type,
michael@0 475 # however, defaults to 'undefined'.
michael@0 476 #
michael@0 477 def writeArgumentUnboxing(f, i, name, type, optional, rvdeclared,
michael@0 478 nullBehavior, undefinedBehavior,
michael@0 479 propIndex=None):
michael@0 480 # f - file to write to
michael@0 481 # i - int or None - Indicates the source jsval. If i is an int, the source
michael@0 482 # jsval is args[i]; otherwise it is args[0]. But if Python i >= C++ argc,
michael@0 483 # which can only happen if optional is True, the argument is missing;
michael@0 484 # use JSVAL_NULL as the source jsval instead.
michael@0 485 # name - str - name of the native C++ variable to create.
michael@0 486 # type - xpidl.{Interface,Native,Builtin} - IDL type of argument
michael@0 487 # optional - bool - True if the parameter is optional.
michael@0 488 # rvdeclared - bool - False if no |nsresult rv| has been declared earlier.
michael@0 489
michael@0 490 typeName = getBuiltinOrNativeTypeName(type)
michael@0 491
michael@0 492 isSetter = (i is None)
michael@0 493
michael@0 494 notPassed = "false"
michael@0 495 if isSetter:
michael@0 496 argPtr = "args[0]"
michael@0 497 argVal = "args[0]"
michael@0 498 elif optional:
michael@0 499 if typeName == "[jsval]":
michael@0 500 val = "JS::UndefinedHandleValue"
michael@0 501 else:
michael@0 502 val = "JS::NullHandleValue"
michael@0 503 argVal = "(%d < argc ? args[%d] : %s)" % (i, i, val)
michael@0 504 if typeName == "[jsval]":
michael@0 505 # This should use the rooted argument,
michael@0 506 # however we probably won't ever need to support that.
michael@0 507 argPtr = None
michael@0 508 notPassed = None
michael@0 509 else:
michael@0 510 # Need a MutableHandleValue to pass into eg the xpc_qsAString
michael@0 511 # constructor, but the corresponding argument may not have been
michael@0 512 # passed in at all. In that case, point the MutableHandleValue at a
michael@0 513 # dummy variable, and pass in a boolean saying that the argument
michael@0 514 # wasn't passed (previously, this used a NULL ptr sentinel value.)
michael@0 515 f.write(" JS::RootedValue {name}_dummy(cx);\n".format(name=name))
michael@0 516 f.write(" JS::MutableHandleValue {name}_mhv({i} < argc ? args[{i}] : &{name}_dummy);\n".format(name=name, i=i))
michael@0 517 f.write(" (void) {name}_mhv;\n".format(name=name, i=i))
michael@0 518
michael@0 519 argPtr = "{name}_mhv".format(name=name)
michael@0 520 notPassed = "argc < {i}".format(i=i)
michael@0 521 else:
michael@0 522 argVal = "args[%d]" % i
michael@0 523 argPtr = "args[%d]" % i
michael@0 524
michael@0 525 params = {
michael@0 526 'name': name,
michael@0 527 'argVal': argVal,
michael@0 528 'argPtr': argPtr,
michael@0 529 'notPassed': notPassed,
michael@0 530 'nullBehavior': nullBehavior or 'DefaultNullBehavior',
michael@0 531 'undefinedBehavior': undefinedBehavior or 'DefaultUndefinedBehavior'
michael@0 532 }
michael@0 533
michael@0 534 if typeName is not None:
michael@0 535 template = argumentUnboxingTemplates.get(typeName)
michael@0 536 if template is not None:
michael@0 537 f.write(substitute(template, params))
michael@0 538 return rvdeclared
michael@0 539 # else fall through; the type isn't supported yet.
michael@0 540 elif isInterfaceType(type):
michael@0 541 if type.name == 'nsIVariant':
michael@0 542 # Totally custom.
michael@0 543 template = (
michael@0 544 " nsCOMPtr<nsIVariant> ${name}(already_AddRefed<nsIVariant>("
michael@0 545 "XPCVariant::newVariant(cx, ${argVal})));\n"
michael@0 546 " if (!${name}) {\n"
michael@0 547 " xpc_qsThrowBadArg(cx, NS_ERROR_INVALID_ARG, vp, %d);\n"
michael@0 548 " return false;\n"
michael@0 549 " }") % i
michael@0 550 f.write(substitute(template, params))
michael@0 551 return rvdeclared
michael@0 552 elif type.name == 'nsIAtom':
michael@0 553 # Should have special atomizing behavior. Fall through.
michael@0 554 pass
michael@0 555 else:
michael@0 556 if not rvdeclared:
michael@0 557 f.write(" nsresult rv;\n");
michael@0 558 f.write(" %s *%s;\n" % (type.name, name))
michael@0 559 f.write(" xpc_qsSelfRef %sref;\n" % name)
michael@0 560 f.write(" rv = xpc_qsUnwrapArg<%s>("
michael@0 561 "cx, %s, &%s, &%sref.ptr, %s);\n"
michael@0 562 % (type.name, argVal, name, name, argPtr))
michael@0 563 f.write(" if (NS_FAILED(rv)) {\n")
michael@0 564 if isSetter:
michael@0 565 assert(propIndex is not None)
michael@0 566 f.write(" xpc_qsThrowBadSetterValue(cx, rv, JSVAL_TO_OBJECT(vp[1]), (uint16_t)%s);\n" %
michael@0 567 propIndex)
michael@0 568 else:
michael@0 569 f.write(" xpc_qsThrowBadArg(cx, rv, vp, %d);\n" % i)
michael@0 570 f.write(" return false;\n"
michael@0 571 " }\n")
michael@0 572 return True
michael@0 573
michael@0 574 warn("Unable to unbox argument of type %s (native type %s)" % (type.name, typeName))
michael@0 575 if i is None:
michael@0 576 src = 'args[0]'
michael@0 577 else:
michael@0 578 src = 'args[%d]' % i
michael@0 579 f.write(" !; // TODO - Unbox argument %s = %s\n" % (name, src))
michael@0 580 return rvdeclared
michael@0 581
michael@0 582 def writeResultDecl(f, type, varname):
michael@0 583 if isVoidType(type):
michael@0 584 return # nothing to declare
michael@0 585
michael@0 586 t = unaliasType(type)
michael@0 587 if t.kind == 'builtin':
michael@0 588 if not t.nativename.endswith('*'):
michael@0 589 if type.kind == 'typedef':
michael@0 590 typeName = type.name # use it
michael@0 591 else:
michael@0 592 typeName = t.nativename
michael@0 593 f.write(" %s %s;\n" % (typeName, varname))
michael@0 594 return
michael@0 595 elif t.kind == 'native':
michael@0 596 name = getBuiltinOrNativeTypeName(t)
michael@0 597 if name in ('[domstring]', '[astring]'):
michael@0 598 f.write(" nsString %s;\n" % varname)
michael@0 599 return
michael@0 600 elif name == '[jsval]':
michael@0 601 f.write(" JS::RootedValue %s(cx);\n" % varname)
michael@0 602 return
michael@0 603 elif t.kind in ('interface', 'forward'):
michael@0 604 f.write(" nsCOMPtr<%s> %s;\n" % (type.name, varname))
michael@0 605 return
michael@0 606
michael@0 607 warn("Unable to declare result of type %s" % type.name)
michael@0 608 f.write(" !; // TODO - Declare out parameter `%s`.\n" % varname)
michael@0 609
michael@0 610 def outParamForm(name, type):
michael@0 611 type = unaliasType(type)
michael@0 612 if type.kind == 'builtin':
michael@0 613 return '&' + name
michael@0 614 elif type.kind == 'native':
michael@0 615 if getBuiltinOrNativeTypeName(type) == '[jsval]':
michael@0 616 return '&' + name
michael@0 617 if type.modifier == 'ref':
michael@0 618 return name
michael@0 619 else:
michael@0 620 return '&' + name
michael@0 621 else:
michael@0 622 return 'getter_AddRefs(%s)' % name
michael@0 623
michael@0 624 # From NativeData2JS.
michael@0 625 resultConvTemplates = {
michael@0 626 'void':
michael@0 627 " ${jsvalRef} = JSVAL_VOID;\n"
michael@0 628 " return true;\n",
michael@0 629
michael@0 630 'octet':
michael@0 631 " ${jsvalRef} = INT_TO_JSVAL((int32_t) result);\n"
michael@0 632 " return true;\n",
michael@0 633
michael@0 634 'short':
michael@0 635 " ${jsvalRef} = INT_TO_JSVAL((int32_t) result);\n"
michael@0 636 " return true;\n",
michael@0 637
michael@0 638 'long':
michael@0 639 " ${jsvalRef} = INT_TO_JSVAL(result);\n"
michael@0 640 " return true;\n",
michael@0 641
michael@0 642 'long long':
michael@0 643 " return xpc_qsInt64ToJsval(cx, result, ${jsvalPtr});\n",
michael@0 644
michael@0 645 'unsigned short':
michael@0 646 " ${jsvalRef} = INT_TO_JSVAL((int32_t) result);\n"
michael@0 647 " return true;\n",
michael@0 648
michael@0 649 'unsigned long':
michael@0 650 " ${jsvalRef} = UINT_TO_JSVAL(result);\n"
michael@0 651 " return true;\n",
michael@0 652
michael@0 653 'unsigned long long':
michael@0 654 " return xpc_qsUint64ToJsval(cx, result, ${jsvalPtr});\n",
michael@0 655
michael@0 656 'float':
michael@0 657 " ${jsvalRef} = JS_NumberValue(result);\n"
michael@0 658 " return true;\n",
michael@0 659
michael@0 660 'double':
michael@0 661 " ${jsvalRef} = JS_NumberValue(result);\n"
michael@0 662 " return true;\n",
michael@0 663
michael@0 664 'boolean':
michael@0 665 " ${jsvalRef} = (result ? JSVAL_TRUE : JSVAL_FALSE);\n"
michael@0 666 " return true;\n",
michael@0 667
michael@0 668 '[astring]':
michael@0 669 " return xpc::StringToJsval(cx, result, ${jsvalPtr});\n",
michael@0 670
michael@0 671 '[domstring]':
michael@0 672 " return xpc::StringToJsval(cx, result, ${jsvalPtr});\n",
michael@0 673
michael@0 674 '[jsval]':
michael@0 675 " ${jsvalPtr}.set(result);\n"
michael@0 676 " return JS_WrapValue(cx, ${jsvalPtr});\n"
michael@0 677 }
michael@0 678
michael@0 679 def isVariantType(t):
michael@0 680 return isSpecificInterfaceType(t, 'nsIVariant')
michael@0 681
michael@0 682 def writeResultConv(f, type, jsvalPtr, jsvalRef):
michael@0 683 """ Emit code to convert the C++ variable `result` to a jsval.
michael@0 684
michael@0 685 The emitted code contains a return statement; it returns true on
michael@0 686 success, false on error.
michael@0 687 """
michael@0 688 # From NativeData2JS.
michael@0 689 typeName = getBuiltinOrNativeTypeName(type)
michael@0 690 if typeName is not None:
michael@0 691 template = resultConvTemplates.get(typeName)
michael@0 692 if template is not None:
michael@0 693 values = {'jsvalRef': jsvalRef,
michael@0 694 'jsvalPtr': jsvalPtr}
michael@0 695 f.write(substitute(template, values))
michael@0 696 return
michael@0 697 # else fall through; this type isn't supported yet
michael@0 698 elif isInterfaceType(type):
michael@0 699 if isVariantType(type):
michael@0 700 f.write(" return xpc_qsVariantToJsval(cx, result, %s);\n"
michael@0 701 % jsvalPtr)
michael@0 702 return
michael@0 703 else:
michael@0 704 f.write(" if (!result) {\n"
michael@0 705 " %s.setNull();\n"
michael@0 706 " return true;\n"
michael@0 707 " }\n"
michael@0 708 " nsWrapperCache* cache = xpc_qsGetWrapperCache(result);\n"
michael@0 709 " if (xpc_FastGetCachedWrapper(cx, cache, %s)) {\n"
michael@0 710 " return true;\n"
michael@0 711 " }\n"
michael@0 712 " // After this point do not use 'result'!\n"
michael@0 713 " qsObjectHelper helper(result, cache);\n"
michael@0 714 " return xpc_qsXPCOMObjectToJsval(cx, "
michael@0 715 "helper, &NS_GET_IID(%s), &interfaces[k_%s], %s);\n"
michael@0 716 % (jsvalPtr, jsvalPtr, type.name, type.name, jsvalPtr))
michael@0 717 return
michael@0 718
michael@0 719 warn("Unable to convert result of type %s" % type.name)
michael@0 720 f.write(" !; // TODO - Convert `result` to jsval, store in `%s`.\n"
michael@0 721 % jsvalRef)
michael@0 722 f.write(" return xpc_qsThrow(cx, NS_ERROR_UNEXPECTED); // FIXME\n")
michael@0 723
michael@0 724 def memberNeedsCallee(member):
michael@0 725 return isInterfaceType(member.realtype)
michael@0 726
michael@0 727 def validateParam(member, param):
michael@0 728 def pfail(msg):
michael@0 729 raise UserError(
michael@0 730 member.iface.name + '.' + member.name + ": "
michael@0 731 "parameter " + param.name + ": " + msg)
michael@0 732
michael@0 733 if param.iid_is is not None:
michael@0 734 pfail("iid_is parameters are not supported.")
michael@0 735 if param.size_is is not None:
michael@0 736 pfail("size_is parameters are not supported.")
michael@0 737 if param.retval:
michael@0 738 pfail("Unexpected retval parameter!")
michael@0 739 if param.paramtype in ('out', 'inout'):
michael@0 740 pfail("Out parameters are not supported.")
michael@0 741 if param.const or param.array or param.shared:
michael@0 742 pfail("I am a simple caveman.")
michael@0 743
michael@0 744 def setOptimizationForMSVC(f, b):
michael@0 745 """ Write a pragma that turns optimizations off (if b is False) or
michael@0 746 on (if b is True) for MSVC.
michael@0 747 """
michael@0 748 if b:
michael@0 749 pragmaParam = "on"
michael@0 750 else:
michael@0 751 pragmaParam = "off"
michael@0 752 f.write("#ifdef _MSC_VER\n")
michael@0 753 f.write('# pragma optimize("", %s)\n'%pragmaParam)
michael@0 754 f.write("#endif\n")
michael@0 755
michael@0 756 def writeQuickStub(f, customMethodCalls, stringtable, member, stubName,
michael@0 757 isSetter=False):
michael@0 758 """ Write a single quick stub (a custom SpiderMonkey getter/setter/method)
michael@0 759 for the specified XPCOM interface-member.
michael@0 760 """
michael@0 761 # Workaround for suspected compiler bug.
michael@0 762 # See https://bugzilla.mozilla.org/show_bug.cgi?id=750019
michael@0 763 disableOptimizationForMSVC = (stubName == 'nsIDOMHTMLDocument_Write')
michael@0 764
michael@0 765 isAttr = (member.kind == 'attribute')
michael@0 766 isMethod = (member.kind == 'method')
michael@0 767 assert isAttr or isMethod
michael@0 768 isGetter = isAttr and not isSetter
michael@0 769
michael@0 770 signature = ("static bool\n" +
michael@0 771 "%s(JSContext *cx, unsigned argc,%s jsval *vp)\n")
michael@0 772
michael@0 773 customMethodCall = customMethodCalls.get(stubName, None)
michael@0 774
michael@0 775 if customMethodCall is None:
michael@0 776 customMethodCall = customMethodCalls.get(member.iface.name + '_', None)
michael@0 777 if customMethodCall is not None:
michael@0 778 if isMethod:
michael@0 779 code = customMethodCall.get('code', None)
michael@0 780 elif isGetter:
michael@0 781 code = customMethodCall.get('getter_code', None)
michael@0 782 else:
michael@0 783 code = customMethodCall.get('setter_code', None)
michael@0 784 else:
michael@0 785 code = None
michael@0 786
michael@0 787 if code is not None:
michael@0 788 templateName = member.iface.name
michael@0 789 if isGetter:
michael@0 790 templateName += '_Get'
michael@0 791 elif isSetter:
michael@0 792 templateName += '_Set'
michael@0 793
michael@0 794 # Generate the code for the stub, calling the template function
michael@0 795 # that's shared between the stubs. The stubs can't have additional
michael@0 796 # arguments, only the template function can.
michael@0 797 callTemplate = signature % (stubName, '')
michael@0 798 callTemplate += "{\n"
michael@0 799
michael@0 800 nativeName = (member.binaryname is not None and member.binaryname
michael@0 801 or header.firstCap(member.name))
michael@0 802 argumentValues = (customMethodCall['additionalArgumentValues']
michael@0 803 % nativeName)
michael@0 804 callTemplate += (" return %s(cx, argc, %s, vp);\n"
michael@0 805 % (templateName, argumentValues))
michael@0 806 callTemplate += "}\n\n"
michael@0 807
michael@0 808 # Fall through and create the template function stub called from the
michael@0 809 # real stubs, but only generate the stub once. Otherwise, just write
michael@0 810 # out the call to the template function and return.
michael@0 811 templateGenerated = templateName + '_generated'
michael@0 812 if templateGenerated in customMethodCall:
michael@0 813 f.write(callTemplate)
michael@0 814 return
michael@0 815 customMethodCall[templateGenerated] = True
michael@0 816
michael@0 817 stubName = templateName
michael@0 818 else:
michael@0 819 callTemplate = ""
michael@0 820 else:
michael@0 821 callTemplate = ""
michael@0 822 code = customMethodCall.get('code', None)
michael@0 823
michael@0 824 unwrapThisFailureFatal = (customMethodCall is None or
michael@0 825 customMethodCall.get('unwrapThisFailureFatal', True));
michael@0 826 if (not unwrapThisFailureFatal and not isAttr):
michael@0 827 raise UserError(member.iface.name + '.' + member.name + ": "
michael@0 828 "Unwrapping this failure must be fatal for methods")
michael@0 829
michael@0 830 # Function prolog.
michael@0 831
michael@0 832 # Only template functions can have additional arguments.
michael@0 833 if customMethodCall is None or not 'additionalArguments' in customMethodCall:
michael@0 834 additionalArguments = ''
michael@0 835 else:
michael@0 836 additionalArguments = " %s," % customMethodCall['additionalArguments']
michael@0 837 if disableOptimizationForMSVC:
michael@0 838 setOptimizationForMSVC(f, False)
michael@0 839 f.write(signature % (stubName, additionalArguments))
michael@0 840 f.write("{\n")
michael@0 841 f.write(" XPC_QS_ASSERT_CONTEXT_OK(cx);\n")
michael@0 842
michael@0 843 # Compute "args".
michael@0 844 f.write(" JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n")
michael@0 845 f.write(" (void) args;\n")
michael@0 846
michael@0 847 # Compute "this".
michael@0 848 f.write(" JS::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));\n"
michael@0 849 " if (!obj)\n"
michael@0 850 " return false;\n")
michael@0 851
michael@0 852 # Get the 'self' pointer.
michael@0 853 if customMethodCall is None or not 'thisType' in customMethodCall:
michael@0 854 f.write(" %s *self;\n" % member.iface.name)
michael@0 855 else:
michael@0 856 f.write(" %s *self;\n" % customMethodCall['thisType'])
michael@0 857 f.write(" xpc_qsSelfRef selfref;\n")
michael@0 858 pthisval = 'JS::MutableHandleValue::fromMarkedLocation(&vp[1])' # as above, ok to overwrite vp[1]
michael@0 859
michael@0 860 if unwrapThisFailureFatal:
michael@0 861 unwrapFatalArg = "true"
michael@0 862 else:
michael@0 863 unwrapFatalArg = "false"
michael@0 864
michael@0 865 f.write(" if (!xpc_qsUnwrapThis(cx, obj, &self, "
michael@0 866 "&selfref.ptr, %s, %s))\n" % (pthisval, unwrapFatalArg))
michael@0 867 f.write(" return false;\n")
michael@0 868
michael@0 869 if not unwrapThisFailureFatal:
michael@0 870 f.write(" if (!self) {\n")
michael@0 871 if (isGetter):
michael@0 872 f.write(" args.rval().setNull();\n")
michael@0 873 f.write(" return true;\n")
michael@0 874 f.write(" }\n");
michael@0 875
michael@0 876 if isMethod:
michael@0 877 # If there are any required arguments, check argc.
michael@0 878 requiredArgs = len(member.params)
michael@0 879 while requiredArgs and member.params[requiredArgs-1].optional:
michael@0 880 requiredArgs -= 1
michael@0 881 elif isSetter:
michael@0 882 requiredArgs = 1
michael@0 883 else:
michael@0 884 requiredArgs = 0
michael@0 885 if requiredArgs:
michael@0 886 f.write(" if (argc < %d)\n" % requiredArgs)
michael@0 887 f.write(" return xpc_qsThrow(cx, "
michael@0 888 "NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n")
michael@0 889
michael@0 890 # Convert in-parameters.
michael@0 891 rvdeclared = False
michael@0 892 if isMethod:
michael@0 893 for i, param in enumerate(member.params):
michael@0 894 argName = 'arg%d' % i
michael@0 895 argTypeKey = argName + 'Type'
michael@0 896 if customMethodCall is None or not argTypeKey in customMethodCall:
michael@0 897 validateParam(member, param)
michael@0 898 realtype = param.realtype
michael@0 899 else:
michael@0 900 realtype = xpidl.Forward(name=customMethodCall[argTypeKey],
michael@0 901 location='', doccomments='')
michael@0 902 # Emit code to convert this argument from jsval.
michael@0 903 rvdeclared = writeArgumentUnboxing(
michael@0 904 f, i, argName, realtype,
michael@0 905 optional=param.optional,
michael@0 906 rvdeclared=rvdeclared,
michael@0 907 nullBehavior=param.null,
michael@0 908 undefinedBehavior=param.undefined)
michael@0 909 elif isSetter:
michael@0 910 rvdeclared = writeArgumentUnboxing(f, None, 'arg0', member.realtype,
michael@0 911 optional=False,
michael@0 912 rvdeclared=rvdeclared,
michael@0 913 nullBehavior=member.null,
michael@0 914 undefinedBehavior=member.undefined,
michael@0 915 propIndex=stringtable.stringIndex(member.name))
michael@0 916
michael@0 917 canFail = customMethodCall is None or customMethodCall.get('canFail', True)
michael@0 918 if canFail and not rvdeclared:
michael@0 919 f.write(" nsresult rv;\n")
michael@0 920 rvdeclared = True
michael@0 921
michael@0 922 if code is not None:
michael@0 923 f.write("%s\n" % code)
michael@0 924
michael@0 925 if code is None or (isGetter and callTemplate is ""):
michael@0 926 debugGetter = code is not None
michael@0 927 if debugGetter:
michael@0 928 f.write("#ifdef DEBUG\n")
michael@0 929 f.write(" nsresult debug_rv;\n")
michael@0 930 f.write(" nsCOMPtr<%s> debug_self;\n"
michael@0 931 " CallQueryInterface(self, getter_AddRefs(debug_self));\n"
michael@0 932 % member.iface.name);
michael@0 933 prefix = 'debug_'
michael@0 934 else:
michael@0 935 prefix = ''
michael@0 936
michael@0 937 resultname = prefix + 'result'
michael@0 938 selfname = prefix + 'self'
michael@0 939 nsresultname = prefix + 'rv'
michael@0 940
michael@0 941 # Prepare out-parameter.
michael@0 942 if isMethod or isGetter:
michael@0 943 writeResultDecl(f, member.realtype, resultname)
michael@0 944
michael@0 945 # Call the method.
michael@0 946 if isMethod:
michael@0 947 comName = header.methodNativeName(member)
michael@0 948 argv = ['arg' + str(i) for i, p in enumerate(member.params)]
michael@0 949 if member.implicit_jscontext:
michael@0 950 argv.append('cx')
michael@0 951 if member.optional_argc:
michael@0 952 argv.append('std::min<uint32_t>(argc, %d) - %d' %
michael@0 953 (len(member.params), requiredArgs))
michael@0 954 if not isVoidType(member.realtype):
michael@0 955 argv.append(outParamForm(resultname, member.realtype))
michael@0 956 args = ', '.join(argv)
michael@0 957 else:
michael@0 958 comName = header.attributeNativeName(member, isGetter)
michael@0 959 if isGetter:
michael@0 960 args = outParamForm(resultname, member.realtype)
michael@0 961 else:
michael@0 962 args = "arg0"
michael@0 963 if member.implicit_jscontext:
michael@0 964 args = "cx, " + args
michael@0 965
michael@0 966 f.write(" ")
michael@0 967 if canFail or debugGetter:
michael@0 968 f.write("%s = " % nsresultname)
michael@0 969 f.write("%s->%s(%s);\n" % (selfname, comName, args))
michael@0 970
michael@0 971 if debugGetter:
michael@0 972 checkSuccess = "NS_SUCCEEDED(debug_rv)"
michael@0 973 if canFail:
michael@0 974 checkSuccess += " == NS_SUCCEEDED(rv)"
michael@0 975 f.write(" MOZ_ASSERT(%s && "
michael@0 976 "xpc_qsSameResult(debug_result, result),\n"
michael@0 977 " \"Got the wrong answer from the custom "
michael@0 978 "method call!\");\n" % checkSuccess)
michael@0 979 f.write("#endif\n")
michael@0 980
michael@0 981 if canFail:
michael@0 982 # Check for errors.
michael@0 983 f.write(" if (NS_FAILED(rv))\n")
michael@0 984 if isMethod:
michael@0 985 f.write(" return xpc_qsThrowMethodFailed("
michael@0 986 "cx, rv, vp);\n")
michael@0 987 else:
michael@0 988 f.write(" return xpc_qsThrowGetterSetterFailed(cx, rv, " +
michael@0 989 "JSVAL_TO_OBJECT(vp[1]), (uint16_t)%d);\n" %
michael@0 990 stringtable.stringIndex(member.name))
michael@0 991
michael@0 992 # Convert the return value.
michael@0 993 if isMethod or isGetter:
michael@0 994 writeResultConv(f, member.realtype, 'args.rval()', '*vp')
michael@0 995 else:
michael@0 996 f.write(" return true;\n")
michael@0 997
michael@0 998 # Epilog.
michael@0 999 f.write("}\n")
michael@0 1000 if disableOptimizationForMSVC:
michael@0 1001 setOptimizationForMSVC(f, True)
michael@0 1002 f.write("\n")
michael@0 1003
michael@0 1004 # Now write out the call to the template function.
michael@0 1005 if customMethodCall is not None:
michael@0 1006 f.write(callTemplate)
michael@0 1007
michael@0 1008 def writeAttrStubs(f, customMethodCalls, stringtable, attr):
michael@0 1009 getterName = (attr.iface.name + '_'
michael@0 1010 + header.attributeNativeName(attr, True))
michael@0 1011 writeQuickStub(f, customMethodCalls, stringtable, attr, getterName)
michael@0 1012 if attr.readonly:
michael@0 1013 setterName = 'xpc_qsGetterOnlyNativeStub'
michael@0 1014 else:
michael@0 1015 setterName = (attr.iface.name + '_'
michael@0 1016 + header.attributeNativeName(attr, False))
michael@0 1017 writeQuickStub(f, customMethodCalls, stringtable, attr, setterName,
michael@0 1018 isSetter=True)
michael@0 1019
michael@0 1020 ps = ('{%d, %s, %s}'
michael@0 1021 % (stringtable.stringIndex(attr.name), getterName, setterName))
michael@0 1022 return ps
michael@0 1023
michael@0 1024 def writeMethodStub(f, customMethodCalls, stringtable, method):
michael@0 1025 """ Write a method stub to `f`. Return an xpc_qsFunctionSpec initializer. """
michael@0 1026
michael@0 1027 stubName = method.iface.name + '_' + header.methodNativeName(method)
michael@0 1028 writeQuickStub(f, customMethodCalls, stringtable, method, stubName)
michael@0 1029 fs = '{%d, %d, %s}' % (stringtable.stringIndex(method.name),
michael@0 1030 len(method.params), stubName)
michael@0 1031 return fs
michael@0 1032
michael@0 1033 def writeStubsForInterface(f, customMethodCalls, stringtable, iface):
michael@0 1034 f.write("// === interface %s\n\n" % iface.name)
michael@0 1035 propspecs = []
michael@0 1036 funcspecs = []
michael@0 1037 for member in iface.stubMembers:
michael@0 1038 if member.kind == 'attribute':
michael@0 1039 ps = writeAttrStubs(f, customMethodCalls, stringtable, member)
michael@0 1040 propspecs.append(ps)
michael@0 1041 elif member.kind == 'method':
michael@0 1042 fs = writeMethodStub(f, customMethodCalls, stringtable, member)
michael@0 1043 funcspecs.append(fs)
michael@0 1044 else:
michael@0 1045 raise TypeError('expected attribute or method, not %r'
michael@0 1046 % member.__class__.__name__)
michael@0 1047
michael@0 1048 iface.propspecs = propspecs
michael@0 1049 iface.funcspecs = funcspecs
michael@0 1050
michael@0 1051 def hashIID(iid):
michael@0 1052 # See nsIDKey::HashCode in nsHashtable.h.
michael@0 1053 return int(iid[:8], 16)
michael@0 1054
michael@0 1055 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 1056
michael@0 1057 def writeResultXPCInterfacesArray(f, conf, resulttypes):
michael@0 1058 f.write("// === XPCNativeInterface cache \n\n")
michael@0 1059 count = len(resulttypes)
michael@0 1060 if count > 0:
michael@0 1061 f.write("static XPCNativeInterface* interfaces[%d];\n\n" % count)
michael@0 1062 f.write("void %s_MarkInterfaces()\n"
michael@0 1063 "{\n" % conf.name)
michael@0 1064 if count > 0:
michael@0 1065 f.write(" for (uint32_t i = 0; i < %d; ++i)\n"
michael@0 1066 " if (interfaces[i])\n"
michael@0 1067 " interfaces[i]->Mark();\n" % count)
michael@0 1068 f.write("}\n")
michael@0 1069 f.write("void %s_ClearInterfaces()\n"
michael@0 1070 "{\n" % conf.name)
michael@0 1071 if count > 0:
michael@0 1072 f.write(" memset(interfaces, 0, %d * "
michael@0 1073 "sizeof(XPCNativeInterface*));\n" % count)
michael@0 1074 f.write("}\n\n")
michael@0 1075 i = 0
michael@0 1076 for type in resulttypes:
michael@0 1077 f.write("static const uint32_t k_%s = %d;\n" % (type, i))
michael@0 1078 i += 1
michael@0 1079 if count > 0:
michael@0 1080 f.write("\n\n")
michael@0 1081
michael@0 1082 def writeSpecs(f, elementType, varname, spec_type, spec_indices, interfaces):
michael@0 1083 index = 0
michael@0 1084 f.write("static const %s %s[] = {\n" % (elementType, varname))
michael@0 1085 for iface in interfaces:
michael@0 1086 specs = getattr(iface, spec_type)
michael@0 1087 if specs:
michael@0 1088 spec_indices[iface.name] = index
michael@0 1089 f.write(" // %s (index %d)\n" % (iface.name,index))
michael@0 1090 for s in specs:
michael@0 1091 f.write(" %s,\n" % s)
michael@0 1092 index += len(specs)
michael@0 1093 f.write("};\n\n")
michael@0 1094
michael@0 1095 def writeDefiner(f, conf, stringtable, interfaces):
michael@0 1096 f.write("// === Definer\n\n")
michael@0 1097
michael@0 1098 # Write out the properties and functions
michael@0 1099 propspecs_indices = {}
michael@0 1100 funcspecs_indices = {}
michael@0 1101 prop_array_name = "all_properties"
michael@0 1102 func_array_name = "all_functions"
michael@0 1103 writeSpecs(f, "xpc_qsPropertySpec", prop_array_name,
michael@0 1104 "propspecs", propspecs_indices, interfaces)
michael@0 1105 writeSpecs(f, "xpc_qsFunctionSpec", func_array_name,
michael@0 1106 "funcspecs", funcspecs_indices, interfaces)
michael@0 1107
michael@0 1108 # generate the static hash table
michael@0 1109 loadFactor = 0.6
michael@0 1110 size = int(len(interfaces) / loadFactor)
michael@0 1111 buckets = [[] for i in range(size)]
michael@0 1112 for iface in interfaces:
michael@0 1113 # This if-statement discards interfaces specified with
michael@0 1114 # "nsInterfaceName.*" that don't have any stub-able members.
michael@0 1115 if iface.stubMembers or iface.newBindingProperties:
michael@0 1116 h = hashIID(iface.attributes.uuid)
michael@0 1117 buckets[h % size].append(iface)
michael@0 1118
michael@0 1119 # Calculate where each interface's entry will show up in tableData. Where
michael@0 1120 # there are hash collisions, the extra entries are added at the end of the
michael@0 1121 # table.
michael@0 1122 entryIndexes = {}
michael@0 1123 arraySize = size
michael@0 1124 for i, bucket in enumerate(buckets):
michael@0 1125 if bucket:
michael@0 1126 entryIndexes[bucket[0].attributes.uuid] = i
michael@0 1127 for iface in bucket[1:]:
michael@0 1128 entryIndexes[iface.attributes.uuid] = arraySize
michael@0 1129 arraySize += 1
michael@0 1130
michael@0 1131 entries = [" {{0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}, "
michael@0 1132 "0, 0, 0, 0, nullptr, XPC_QS_NULL_INDEX, XPC_QS_NULL_INDEX}"
michael@0 1133 for i in range(arraySize)]
michael@0 1134 for i, bucket in enumerate(buckets):
michael@0 1135 for j, iface in enumerate(bucket):
michael@0 1136 # iid field
michael@0 1137 uuid = iface.attributes.uuid.lower()
michael@0 1138 m = uuid_re.match(uuid)
michael@0 1139 assert m is not None
michael@0 1140 m0, m1, m2, m3, m4 = m.groups()
michael@0 1141 m3arr = ('{0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s}'
michael@0 1142 % (m3[0:2], m3[2:4], m4[0:2], m4[2:4],
michael@0 1143 m4[4:6], m4[6:8], m4[8:10], m4[10:12]))
michael@0 1144 iid = ('{0x%s, 0x%s, 0x%s, %s}' % (m0, m1, m2, m3arr))
michael@0 1145
michael@0 1146 # properties fields
michael@0 1147 prop_index = 0
michael@0 1148 prop_n_entries = 0
michael@0 1149 if iface.propspecs:
michael@0 1150 prop_index = propspecs_indices[iface.name]
michael@0 1151 prop_n_entries = len(iface.propspecs)
michael@0 1152
michael@0 1153 # member fields
michael@0 1154 func_index = 0
michael@0 1155 func_n_entries = 0
michael@0 1156 if iface.funcspecs:
michael@0 1157 func_index = funcspecs_indices[iface.name]
michael@0 1158 func_n_entries = len(iface.funcspecs)
michael@0 1159
michael@0 1160 # parentInterface field
michael@0 1161 baseName = iface.base
michael@0 1162 while baseName is not None:
michael@0 1163 piface = iface.idl.getName(baseName, None)
michael@0 1164 k = entryIndexes.get(piface.attributes.uuid)
michael@0 1165 if k is not None:
michael@0 1166 parentInterface = str(k)
michael@0 1167 break
michael@0 1168 baseName = piface.base
michael@0 1169 else:
michael@0 1170 parentInterface = "XPC_QS_NULL_INDEX"
michael@0 1171
michael@0 1172 # chain field
michael@0 1173 if j == len(bucket) - 1:
michael@0 1174 chain = "XPC_QS_NULL_INDEX"
michael@0 1175 else:
michael@0 1176 k = entryIndexes[bucket[j+1].attributes.uuid]
michael@0 1177 chain = str(k)
michael@0 1178
michael@0 1179 # add entry
michael@0 1180 entry = " /* %s */ {%s, %d, %d, %d, %d, %s, %s, %s}" % (
michael@0 1181 iface.name, iid, prop_index, prop_n_entries,
michael@0 1182 func_index, func_n_entries, iface.newBindingProperties,
michael@0 1183 parentInterface, chain)
michael@0 1184 entries[entryIndexes[iface.attributes.uuid]] = entry
michael@0 1185
michael@0 1186 f.write("static const xpc_qsHashEntry tableData[] = {\n")
michael@0 1187 f.write(",\n".join(entries))
michael@0 1188 f.write("\n };\n\n")
michael@0 1189 f.write("// Make sure our table indices aren't overflowed\n"
michael@0 1190 "PR_STATIC_ASSERT((sizeof(tableData) / sizeof(tableData[0])) < (1 << (8 * sizeof(tableData[0].parentInterface))));\n"
michael@0 1191 "PR_STATIC_ASSERT((sizeof(tableData) / sizeof(tableData[0])) < (1 << (8 * sizeof(tableData[0].chain))));\n\n")
michael@0 1192
michael@0 1193 # The string table for property and method names.
michael@0 1194 table_name = "stringtab"
michael@0 1195 stringtable.writeDefinition(f, table_name)
michael@0 1196 structNames = [prop_array_name, func_array_name]
michael@0 1197 for name in structNames:
michael@0 1198 f.write("PR_STATIC_ASSERT(sizeof(%s) < (1 << (8 * sizeof(%s[0].name_index))));\n"
michael@0 1199 % (table_name, name))
michael@0 1200 f.write("\n")
michael@0 1201
michael@0 1202 # the definer function (entry point to this quick stubs file)
michael@0 1203 f.write("namespace xpc {\n")
michael@0 1204 f.write("bool %s_DefineQuickStubs(" % conf.name)
michael@0 1205 f.write("JSContext *cx, JSObject *proto, unsigned flags, uint32_t count, "
michael@0 1206 "const nsID **iids)\n"
michael@0 1207 "{\n")
michael@0 1208 f.write(" return !!xpc_qsDefineQuickStubs("
michael@0 1209 "cx, proto, flags, count, iids, %d, tableData, %s, %s, %s);\n" % (
michael@0 1210 size, prop_array_name, func_array_name, table_name))
michael@0 1211 f.write("}\n")
michael@0 1212 f.write("} // namespace xpc\n\n\n")
michael@0 1213
michael@0 1214
michael@0 1215 stubTopTemplate = '''\
michael@0 1216 /* THIS FILE IS AUTOGENERATED - DO NOT EDIT */
michael@0 1217 #include "jsapi.h"
michael@0 1218 #include "qsWinUndefs.h"
michael@0 1219 #include "qsObjectHelper.h"
michael@0 1220 #include "nsID.h"
michael@0 1221 #include "%s"
michael@0 1222 #include "nsCOMPtr.h"
michael@0 1223 #include "xpcprivate.h" // for XPCCallContext
michael@0 1224 #include "XPCQuickStubs.h"
michael@0 1225 #include <algorithm>
michael@0 1226 '''
michael@0 1227
michael@0 1228 def writeStubFile(filename, headerFilename, conf, interfaces):
michael@0 1229 print "Creating stub file", filename
michael@0 1230 makeutils.targets.append(filename)
michael@0 1231
michael@0 1232 f = open(filename, 'w')
michael@0 1233 filesIncluded = set()
michael@0 1234
michael@0 1235 def includeType(type):
michael@0 1236 type = unaliasType(type)
michael@0 1237 if type.kind in ('builtin', 'native'):
michael@0 1238 return None
michael@0 1239 file = conf.irregularFilenames.get(type.name, type.name) + '.h'
michael@0 1240 if file not in filesIncluded:
michael@0 1241 f.write('#include "%s"\n' % file)
michael@0 1242 filesIncluded.add(file)
michael@0 1243 return type
michael@0 1244
michael@0 1245 def writeIncludesForMember(member):
michael@0 1246 assert member.kind in ('attribute', 'method')
michael@0 1247 resulttype = includeType(member.realtype)
michael@0 1248 if member.kind == 'method':
michael@0 1249 for p in member.params:
michael@0 1250 includeType(p.realtype)
michael@0 1251 return resulttype
michael@0 1252
michael@0 1253 def writeIncludesForInterface(iface):
michael@0 1254 assert iface.kind == 'interface'
michael@0 1255 resulttypes = []
michael@0 1256 for member in iface.stubMembers:
michael@0 1257 resulttype = writeIncludesForMember(member)
michael@0 1258 if resulttype is not None and not isVariantType(resulttype):
michael@0 1259 resulttypes.append(resulttype.name)
michael@0 1260
michael@0 1261 includeType(iface)
michael@0 1262
michael@0 1263 return resulttypes
michael@0 1264
michael@0 1265 try:
michael@0 1266 f.write(stubTopTemplate % os.path.basename(headerFilename))
michael@0 1267 resulttypes = []
michael@0 1268 for iface in interfaces:
michael@0 1269 resulttypes.extend(writeIncludesForInterface(iface))
michael@0 1270 for customInclude in conf.customIncludes:
michael@0 1271 f.write('#include "%s"\n' % customInclude)
michael@0 1272 f.write("\n\n")
michael@0 1273 writeResultXPCInterfacesArray(f, conf, frozenset(resulttypes))
michael@0 1274 stringtable = StringTable()
michael@0 1275 for iface in interfaces:
michael@0 1276 writeStubsForInterface(f, conf.customMethodCalls, stringtable, iface)
michael@0 1277 writeDefiner(f, conf, stringtable, interfaces)
michael@0 1278 finally:
michael@0 1279 f.close()
michael@0 1280
michael@0 1281 def main():
michael@0 1282 from optparse import OptionParser
michael@0 1283 o = OptionParser(usage="usage: %prog [options] configfile")
michael@0 1284 o.add_option('-o', "--stub-output",
michael@0 1285 type='string', dest='stub_output', default=None,
michael@0 1286 help="Quick stub C++ source output file", metavar="FILE")
michael@0 1287 o.add_option('--header-output', type='string', default=None,
michael@0 1288 help="Quick stub header output file", metavar="FILE")
michael@0 1289 o.add_option('--makedepend-output', type='string', default=None,
michael@0 1290 help="gnumake dependencies output file", metavar="FILE")
michael@0 1291 o.add_option('--idlpath', type='string', default='.',
michael@0 1292 help="colon-separated directories to search for idl files",
michael@0 1293 metavar="PATH")
michael@0 1294 o.add_option('--cachedir', dest='cachedir', default='',
michael@0 1295 help="Directory in which to cache lex/parse tables.")
michael@0 1296 o.add_option("--verbose-errors", action='store_true', default=False,
michael@0 1297 help="When an error happens, display the Python traceback.")
michael@0 1298 (options, filenames) = o.parse_args()
michael@0 1299
michael@0 1300 if len(filenames) != 1:
michael@0 1301 o.error("Exactly one config filename is needed.")
michael@0 1302 filename = filenames[0]
michael@0 1303
michael@0 1304 if options.stub_output is None:
michael@0 1305 if filename.endswith('.qsconf') or filename.endswith('.py'):
michael@0 1306 options.stub_output = filename.rsplit('.', 1)[0] + '.cpp'
michael@0 1307 else:
michael@0 1308 options.stub_output = filename + '.cpp'
michael@0 1309 if options.header_output is None:
michael@0 1310 options.header_output = re.sub(r'(\.c|\.cpp)?$', '.h',
michael@0 1311 options.stub_output)
michael@0 1312
michael@0 1313 if options.cachedir != '':
michael@0 1314 sys.path.append(options.cachedir)
michael@0 1315 if not os.path.isdir(options.cachedir):
michael@0 1316 os.makedirs(options.cachedir)
michael@0 1317
michael@0 1318 try:
michael@0 1319 includePath = options.idlpath.split(':')
michael@0 1320 conf, interfaces = readConfigFile(filename,
michael@0 1321 includePath=includePath,
michael@0 1322 cachedir=options.cachedir)
michael@0 1323 writeStubFile(options.stub_output, options.header_output,
michael@0 1324 conf, interfaces)
michael@0 1325 writeHeaderFile(options.header_output, conf.name)
michael@0 1326 if options.makedepend_output is not None:
michael@0 1327 makeutils.writeMakeDependOutput(options.makedepend_output)
michael@0 1328 except Exception, exc:
michael@0 1329 if options.verbose_errors:
michael@0 1330 raise
michael@0 1331 elif isinstance(exc, (UserError, xpidl.IDLError)):
michael@0 1332 warn(str(exc))
michael@0 1333 elif isinstance(exc, OSError):
michael@0 1334 warn("%s: %s" % (exc.__class__.__name__, exc))
michael@0 1335 else:
michael@0 1336 raise
michael@0 1337 sys.exit(1)
michael@0 1338
michael@0 1339 if __name__ == '__main__':
michael@0 1340 main()

mercurial