js/xpconnect/src/qsgen.py

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

mercurial