js/xpconnect/src/qsgen.py

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

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

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

     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/.
     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.
    85 import xpidl
    86 import header
    87 import makeutils
    88 import os, re
    89 import sys
    91 # === Preliminaries
    93 def warn(msg):
    94     sys.stderr.write(msg + '\n')
    96 def unaliasType(t):
    97     while t.kind == 'typedef':
    98         t = t.realtype
    99     assert t is not None
   100     return t
   102 def isVoidType(type):
   103     """ Return True if the given xpidl type is void. """
   104     return type.kind == 'builtin' and type.name == 'void'
   106 def isInterfaceType(t):
   107     t = unaliasType(t)
   108     assert t.kind in ('builtin', 'native', 'interface', 'forward')
   109     return t.kind in ('interface', 'forward')
   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.
   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
   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
   132 # === Reading the file
   134 class UserError(Exception):
   135     pass
   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))
   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
   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)
   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
   170                     raise UserError("Method %s, parameter %s: "
   171                                     "unrecognized property %r"
   172                                     % (memberId, param.name, attrname))
   174     # Add this member to the list.
   175     member.iface.stubMembers.append(member)
   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))
   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)
   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))
   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)
   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', {})
   232 def readConfigFile(filename, includePath, cachedir):
   233     # Read the config file.
   234     conf = Configuration(filename, includePath)
   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)
   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
   259     stubbedInterfaces = []
   261     for memberId in conf.members:
   262         add = True
   263         interfaceName, memberName = parseMemberId(memberId)
   265         # If the interfaceName starts with -, then remove this entry from the list
   266         if interfaceName[0] == '-':
   267             add = False
   268             interfaceName = interfaceName[1:]
   270         iface = getInterface(interfaceName, errorLoc='looking for %r' % memberId)
   272         if not iface.attributes.scriptable:
   273             raise UserError("Interface %s is not scriptable." % interfaceName)
   275         if memberName == '*':
   276             if not add:
   277                 raise UserError("Can't use negation in stub list with wildcard, in %s.*" % interfaceName)
   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)
   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)
   299                 addStubMember(memberId, member)
   300                 if member.iface not in stubbedInterfaces:
   301                     stubbedInterfaces.append(member.iface)
   302             else:
   303                 removeStubMember(memberId, member)
   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)
   311     # Now go through and check all the interfaces' members
   312     for iface in stubbedInterfaces:
   313         for member in iface.stubMembers:
   314             checkStubMember(member)
   316     return conf, interfaces
   319 # === Generating the header file
   321 def writeHeaderFile(filename, name):
   322     print "Creating header file", filename
   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()
   343 # === Generating the source file
   345 class StringTable:
   346     def __init__(self):
   347         self.current_index = 0;
   348         self.table = {}
   349         self.reverse_table = {}
   351     def c_strlen(self, string):
   352         return len(string) + 1
   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
   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);
   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)
   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",
   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",
   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",
   404     'long':
   405         "    int32_t ${name};\n"
   406         "    if (!JS::ToInt32(cx, ${argVal}, &${name}))\n"
   407         "        return false;\n",
   409     'unsigned long':
   410         "    uint32_t ${name};\n"
   411         "    if (!JS::ToUint32(cx, ${argVal}, &${name}))\n"
   412         "        return false;\n",
   414     'long long':
   415         "    int64_t ${name};\n"
   416         "    if (!JS::ToInt64(cx, ${argVal}, &${name}))\n"
   417         "        return false;\n",
   419     'unsigned long long':
   420         "    uint64_t ${name};\n"
   421         "    if (!JS::ToUint64(cx, ${argVal}, &${name}))\n"
   422         "        return false;\n",
   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",
   430     'double':
   431         "    double ${name};\n"
   432         "    if (!JS::ToNumber(cx, ${argVal}, &${name}))\n"
   433         "        return false;\n",
   435     'boolean':
   436         "    bool ${name} = JS::ToBoolean(${argVal});\n",
   438     '[astring]':
   439         "    xpc_qsAString ${name}(cx, ${argVal}, ${argPtr}, ${notPassed});\n"
   440         "    if (!${name}.IsValid())\n"
   441         "        return false;\n",
   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",
   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",
   457     '[cstring]':
   458         "    xpc_qsACString ${name}(cx, ${argVal}, ${argPtr}, ${notPassed});\n"
   459         "    if (!${name}.IsValid())\n"
   460         "        return false;\n",
   462     '[utf8string]':
   463         "    xpc_qsAUTF8String ${name}(cx, ${argVal}, ${argPtr}, ${notPassed});\n"
   464         "    if (!${name}.IsValid())\n"
   465         "        return false;\n",
   467     '[jsval]':
   468         "    JS::RootedValue ${name}(cx, ${argVal});\n"
   469     }
   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.
   490     typeName = getBuiltinOrNativeTypeName(type)
   492     isSetter = (i is None)
   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))
   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
   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         }
   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
   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
   582 def writeResultDecl(f, type, varname):
   583     if isVoidType(type):
   584         return  # nothing to declare
   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
   607     warn("Unable to declare result of type %s" % type.name)
   608     f.write("    !; // TODO - Declare out parameter `%s`.\n" % varname)
   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
   624 # From NativeData2JS.
   625 resultConvTemplates = {
   626     'void':
   627             "    ${jsvalRef} = JSVAL_VOID;\n"
   628             "    return true;\n",
   630     'octet':
   631         "    ${jsvalRef} = INT_TO_JSVAL((int32_t) result);\n"
   632         "    return true;\n",
   634     'short':
   635         "    ${jsvalRef} = INT_TO_JSVAL((int32_t) result);\n"
   636         "    return true;\n",
   638     'long':
   639         "    ${jsvalRef} = INT_TO_JSVAL(result);\n"
   640         "    return true;\n",
   642     'long long':
   643         "    return xpc_qsInt64ToJsval(cx, result, ${jsvalPtr});\n",
   645     'unsigned short':
   646         "    ${jsvalRef} = INT_TO_JSVAL((int32_t) result);\n"
   647         "    return true;\n",
   649     'unsigned long':
   650         "    ${jsvalRef} = UINT_TO_JSVAL(result);\n"
   651         "    return true;\n",
   653     'unsigned long long':
   654         "    return xpc_qsUint64ToJsval(cx, result, ${jsvalPtr});\n",
   656     'float':
   657         "    ${jsvalRef} = JS_NumberValue(result);\n"
   658         "    return true;\n",
   660     'double':
   661         "    ${jsvalRef} =  JS_NumberValue(result);\n"
   662         "    return true;\n",
   664     'boolean':
   665         "    ${jsvalRef} = (result ? JSVAL_TRUE : JSVAL_FALSE);\n"
   666         "    return true;\n",
   668     '[astring]':
   669         "    return xpc::StringToJsval(cx, result, ${jsvalPtr});\n",
   671     '[domstring]':
   672         "    return xpc::StringToJsval(cx, result, ${jsvalPtr});\n",
   674     '[jsval]':
   675         "    ${jsvalPtr}.set(result);\n"
   676         "    return JS_WrapValue(cx, ${jsvalPtr});\n"
   677     }
   679 def isVariantType(t):
   680     return isSpecificInterfaceType(t, 'nsIVariant')
   682 def writeResultConv(f, type, jsvalPtr, jsvalRef):
   683     """ Emit code to convert the C++ variable `result` to a jsval.
   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
   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")
   724 def memberNeedsCallee(member):
   725     return isInterfaceType(member.realtype)
   727 def validateParam(member, param):
   728     def pfail(msg):
   729         raise UserError(
   730             member.iface.name + '.' + member.name + ": "
   731             "parameter " + param.name + ": " + msg)
   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.")
   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")
   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')
   765     isAttr = (member.kind == 'attribute')
   766     isMethod = (member.kind == 'method')
   767     assert isAttr or isMethod
   768     isGetter = isAttr and not isSetter
   770     signature = ("static bool\n" +
   771                  "%s(JSContext *cx, unsigned argc,%s jsval *vp)\n")
   773     customMethodCall = customMethodCalls.get(stubName, None)
   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
   787         if code is not None:
   788             templateName = member.iface.name
   789             if isGetter:
   790                 templateName += '_Get'
   791             elif isSetter:
   792                 templateName += '_Set'
   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"
   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"
   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
   817             stubName = templateName
   818         else:
   819             callTemplate = ""
   820     else:
   821         callTemplate = ""
   822         code = customMethodCall.get('code', None)
   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")
   830     # Function prolog.
   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")
   843     # Compute "args".
   844     f.write("    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n")
   845     f.write("    (void) args;\n")
   847     # Compute "this".
   848     f.write("    JS::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));\n"
   849             "    if (!obj)\n"
   850             "        return false;\n")
   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]
   860     if unwrapThisFailureFatal:
   861         unwrapFatalArg = "true"
   862     else:
   863         unwrapFatalArg = "false"
   865     f.write("    if (!xpc_qsUnwrapThis(cx, obj, &self, "
   866             "&selfref.ptr, %s, %s))\n" % (pthisval, unwrapFatalArg))
   867     f.write("        return false;\n")
   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");
   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")
   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))
   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
   922     if code is not None:
   923         f.write("%s\n" % code)
   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 = ''
   937         resultname = prefix + 'result'
   938         selfname = prefix + 'self'
   939         nsresultname = prefix + 'rv'
   941         # Prepare out-parameter.
   942         if isMethod or isGetter:
   943             writeResultDecl(f, member.realtype, resultname)
   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
   966         f.write("    ")
   967         if canFail or debugGetter:
   968             f.write("%s = " % nsresultname)
   969         f.write("%s->%s(%s);\n" % (selfname, comName, args))
   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")
   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))
   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")
   998     # Epilog.
   999     f.write("}\n")
  1000     if disableOptimizationForMSVC:
  1001         setOptimizationForMSVC(f, True)
  1002     f.write("\n")
  1004     # Now write out the call to the template function.
  1005     if customMethodCall is not None:
  1006         f.write(callTemplate)
  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)
  1020     ps = ('{%d, %s, %s}'
  1021           % (stringtable.stringIndex(attr.name), getterName, setterName))
  1022     return ps
  1024 def writeMethodStub(f, customMethodCalls, stringtable, method):
  1025     """ Write a method stub to `f`. Return an xpc_qsFunctionSpec initializer. """
  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
  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__)
  1048     iface.propspecs = propspecs
  1049     iface.funcspecs = funcspecs
  1051 def hashIID(iid):
  1052     # See nsIDKey::HashCode in nsHashtable.h.
  1053     return int(iid[:8], 16)
  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})$')
  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")
  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")
  1095 def writeDefiner(f, conf, stringtable, interfaces):
  1096     f.write("// === Definer\n\n")
  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)
  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)
  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
  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))
  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)
  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)
  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"
  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)
  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
  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")
  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")
  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")
  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 '''
  1228 def writeStubFile(filename, headerFilename, conf, interfaces):
  1229     print "Creating stub file", filename
  1230     makeutils.targets.append(filename)
  1232     f = open(filename, 'w')
  1233     filesIncluded = set()
  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
  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
  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)
  1261         includeType(iface)
  1263         return resulttypes
  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()
  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()
  1300     if len(filenames) != 1:
  1301         o.error("Exactly one config filename is needed.")
  1302     filename = filenames[0]
  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)
  1313     if options.cachedir != '':
  1314         sys.path.append(options.cachedir)
  1315         if not os.path.isdir(options.cachedir):
  1316             os.makedirs(options.cachedir)
  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)
  1339 if __name__ == '__main__':
  1340     main()

mercurial