Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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() |