js/xpconnect/src/qsgen.py

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

mercurial