def writeMethodStub(f, customMethodCalls, stringtable, method): """ Write a method stub to `f`. Return an xpc_qsFunctionSpec initializer. """ stubName = method.iface.name + "_" + header.methodNativeName(method) writeQuickStub(f, customMethodCalls, stringtable, method, stubName) fs = "{%d, %d, %s}" % (stringtable.stringIndex(method.name), len(method.params), stubName) return fs
def writeStub(f, customMethodCalls, member, stubName, writeThisUnwrapping, writeCheckForFailure, writeResultWrapping, isSetter=False): """ Write a single quick stub (a custom SpiderMonkey getter/setter/method) for the specified XPCOM interface-member. """ if member.kind == 'method' and member.forward: member = member.iface.namemap[member.forward] isAttr = (member.kind == 'attribute') isMethod = (member.kind == 'method') assert isAttr or isMethod isNotxpcom = isMethod and member.notxpcom isGetter = isAttr and not isSetter signature = "static JSBool\n" if isAttr: # JSPropertyOp signature. if isSetter: signature += "%s(JSContext *cx, JSHandleObject obj, JSHandleId id, JSBool strict,%s jsval *vp)\n" else: signature += "%s(JSContext *cx, JSHandleObject obj, JSHandleId id,%s jsval *vp)\n" else: # JSFastNative. signature += "%s(JSContext *cx, unsigned argc,%s jsval *vp)\n" customMethodCall = customMethodCalls.get(stubName, None) if customMethodCall is None: customMethodCall = customMethodCalls.get(member.iface.name + '_', None) if customMethodCall is not None: if isMethod: code = customMethodCall.get('code', None) elif isGetter: code = customMethodCall.get('getter_code', None) else: code = customMethodCall.get('setter_code', None) else: code = None if code is not None: templateName = member.iface.name if isGetter: templateName += '_Get' elif isSetter: templateName += '_Set' # Generate the code for the stub, calling the template function # that's shared between the stubs. The stubs can't have additional # arguments, only the template function can. callTemplate = signature % (stubName, '') callTemplate += "{\n" argumentValues = (customMethodCall['additionalArgumentValues'] % header.methodNativeName(member)) if isAttr: callTemplate += (" return %s(cx, obj, id%s, %s, vp);\n" % (templateName, ", strict" if isSetter else "", argumentValues)) else: callTemplate += (" return %s(cx, argc, %s, vp);\n" % (templateName, argumentValues)) callTemplate += "}\n\n" # Fall through and create the template function stub called from the # real stubs, but only generate the stub once. Otherwise, just write # out the call to the template function and return. templateGenerated = templateName + '_generated' if templateGenerated in customMethodCall: f.write(callTemplate) return customMethodCall[templateGenerated] = True stubName = templateName else: callTemplate = "" else: callTemplate = "" code = customMethodCall.get('code', None) # Function prolog. # Only template functions can have additional arguments. if customMethodCall is None or not 'additionalArguments' in customMethodCall: additionalArguments = '' else: additionalArguments = " %s," % customMethodCall['additionalArguments'] f.write(signature % (stubName, additionalArguments)) f.write("{\n") f.write(" XPC_QS_ASSERT_CONTEXT_OK(cx);\n") # For methods, compute "this". if isMethod: f.write(" JSObject *obj = JS_THIS_OBJECT(cx, vp);\n" " if (!obj)\n" " return JS_FALSE;\n") # Create ccx if needed. haveCcx = memberNeedsCcx(member) if haveCcx: f.write(" XPCCallContext ccx(JS_CALLER, cx, obj, " "JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));\n") if isInterfaceType(member.realtype): f.write(" XPCLazyCallContext lccx(ccx);\n") selfname = writeThisUnwrapping(f, member, isMethod, isGetter, customMethodCall, haveCcx) rvdeclared = False if isMethod: inArgs = argumentsLength(member) # If there are any required arguments, check argc. requiredArgs = inArgs while requiredArgs and member.params[requiredArgs-1].optional: requiredArgs -= 1 if requiredArgs: f.write(" if (argc < %d)\n" % requiredArgs) f.write(" return xpc_qsThrow(cx, " "NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n") # Convert in-parameters. if inArgs > 0: f.write(" jsval *argv = JS_ARGV(cx, vp);\n") for i in range(inArgs): param = member.params[i] argName = 'arg%d' % i argTypeKey = argName + 'Type' if customMethodCall is None or not argTypeKey in customMethodCall: validateParam(member, param) realtype = param.realtype else: realtype = xpidl.Forward(name=customMethodCall[argTypeKey], location='', doccomments='') # Emit code to convert this argument from jsval. rvdeclared = writeArgumentUnboxing( f, i, argName, realtype, haveCcx=haveCcx, optional=param.optional, rvdeclared=rvdeclared, nullBehavior=param.null, undefinedBehavior=param.undefined) if inArgs < len(member.params): f.write(" nsWrapperCache *cache;\n") elif isSetter: rvdeclared = writeArgumentUnboxing(f, None, 'arg0', member.realtype, haveCcx=False, optional=False, rvdeclared=rvdeclared, nullBehavior=member.null, undefinedBehavior=member.undefined) canFail = not isNotxpcom and (customMethodCall is None or customMethodCall.get('canFail', True)) if canFail and not rvdeclared: f.write(" nsresult rv;\n") rvdeclared = True if code is not None: f.write("%s\n" % code) if code is None or (isGetter and callTemplate is ""): debugGetter = code is not None if debugGetter: f.write("#ifdef DEBUG\n") f.write(" nsresult debug_rv;\n") f.write(" nsCOMPtr<%s> debug_self;\n" " CallQueryInterface(self, getter_AddRefs(debug_self));\n" % member.iface.name); prefix = 'debug_' else: prefix = '' resultname = prefix + 'result' selfname = prefix + selfname nsresultname = prefix + 'rv' # Prepare out-parameter. if isMethod or isGetter: writeResultDecl(f, member, resultname) # Call the method. if isMethod: comName = header.methodNativeName(member) argv = ['arg' + str(i) for i in range(inArgs)] if inArgs < len(member.params): argv.append(outParamForm('cache', member.params[inArgs].realtype)) if member.implicit_jscontext: argv.append('cx') if member.optional_argc: argv.append('argc - %d' % requiredArgs) if not isNotxpcom and not isVoidType(member.realtype): argv.append(outParamForm(resultname, member.realtype)) args = ', '.join(argv) else: comName = header.attributeNativeName(member, isGetter) if isGetter: args = outParamForm(resultname, member.realtype) else: args = "arg0" if member.implicit_jscontext: args = "cx, " + args f.write(" ") if canFail or debugGetter: f.write("%s = " % nsresultname) elif isNotxpcom: f.write("%s = " % resultname) f.write("%s->%s(%s);\n" % (selfname, comName, args)) if debugGetter: checkSuccess = "NS_SUCCEEDED(debug_rv)" if canFail: checkSuccess += " == NS_SUCCEEDED(rv)" f.write(" NS_ASSERTION(%s && " "xpc_qsSameResult(debug_result, result),\n" " \"Got the wrong answer from the custom " "method call!\");\n" % checkSuccess) f.write("#endif\n") if canFail: # Check for errors. writeCheckForFailure(f, isMethod, isGetter, haveCcx) # Convert the return value. if isMethod or isGetter: writeResultWrapping(f, member, 'vp', '*vp') else: f.write(" return JS_TRUE;\n") # Epilog. f.write("}\n\n") # Now write out the call to the template function. if customMethodCall is not None: f.write(callTemplate)
def writeQuickStub(f, customMethodCalls, stringtable, member, stubName, isSetter=False): """ Write a single quick stub (a custom SpiderMonkey getter/setter/method) for the specified XPCOM interface-member. """ # Workaround for suspected compiler bug. # See https://bugzilla.mozilla.org/show_bug.cgi?id=750019 disableOptimizationForMSVC = stubName == "nsIDOMHTMLDocument_Write" isAttr = member.kind == "attribute" isMethod = member.kind == "method" assert isAttr or isMethod isGetter = isAttr and not isSetter signature = "static bool\n" + "%s(JSContext *cx, unsigned argc,%s jsval *vp)\n" customMethodCall = customMethodCalls.get(stubName, None) if customMethodCall is None: customMethodCall = customMethodCalls.get(member.iface.name + "_", None) if customMethodCall is not None: if isMethod: code = customMethodCall.get("code", None) elif isGetter: code = customMethodCall.get("getter_code", None) else: code = customMethodCall.get("setter_code", None) else: code = None if code is not None: templateName = member.iface.name if isGetter: templateName += "_Get" elif isSetter: templateName += "_Set" # Generate the code for the stub, calling the template function # that's shared between the stubs. The stubs can't have additional # arguments, only the template function can. callTemplate = signature % (stubName, "") callTemplate += "{\n" nativeName = member.binaryname is not None and member.binaryname or header.firstCap(member.name) argumentValues = customMethodCall["additionalArgumentValues"] % nativeName callTemplate += " return %s(cx, argc, %s, vp);\n" % (templateName, argumentValues) callTemplate += "}\n\n" # Fall through and create the template function stub called from the # real stubs, but only generate the stub once. Otherwise, just write # out the call to the template function and return. templateGenerated = templateName + "_generated" if templateGenerated in customMethodCall: f.write(callTemplate) return customMethodCall[templateGenerated] = True stubName = templateName else: callTemplate = "" else: callTemplate = "" code = customMethodCall.get("code", None) unwrapThisFailureFatal = customMethodCall is None or customMethodCall.get("unwrapThisFailureFatal", True) if not unwrapThisFailureFatal and not isAttr: raise UserError( member.iface.name + "." + member.name + ": " "Unwrapping this failure must be fatal for methods" ) # Function prolog. # Only template functions can have additional arguments. if customMethodCall is None or not "additionalArguments" in customMethodCall: additionalArguments = "" else: additionalArguments = " %s," % customMethodCall["additionalArguments"] if disableOptimizationForMSVC: setOptimizationForMSVC(f, False) f.write(signature % (stubName, additionalArguments)) f.write("{\n") f.write(" XPC_QS_ASSERT_CONTEXT_OK(cx);\n") # Compute "args". f.write(" JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n") f.write(" (void) args;\n") # Compute "this". f.write(" JS::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));\n" " if (!obj)\n" " return false;\n") # Get the 'self' pointer. if customMethodCall is None or not "thisType" in customMethodCall: f.write(" %s *self;\n" % member.iface.name) else: f.write(" %s *self;\n" % customMethodCall["thisType"]) f.write(" xpc_qsSelfRef selfref;\n") pthisval = "JS::MutableHandleValue::fromMarkedLocation(&vp[1])" # as above, ok to overwrite vp[1] if unwrapThisFailureFatal: unwrapFatalArg = "true" else: unwrapFatalArg = "false" f.write(" if (!xpc_qsUnwrapThis(cx, obj, &self, " "&selfref.ptr, %s, %s))\n" % (pthisval, unwrapFatalArg)) f.write(" return false;\n") if not unwrapThisFailureFatal: f.write(" if (!self) {\n") if isGetter: f.write(" args.rval().setNull();\n") f.write(" return true;\n") f.write(" }\n") if isMethod: # If there are any required arguments, check argc. requiredArgs = len(member.params) while requiredArgs and member.params[requiredArgs - 1].optional: requiredArgs -= 1 elif isSetter: requiredArgs = 1 else: requiredArgs = 0 if requiredArgs: f.write(" if (argc < %d)\n" % requiredArgs) f.write(" return xpc_qsThrow(cx, " "NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n") # Convert in-parameters. rvdeclared = False if isMethod: for i, param in enumerate(member.params): argName = "arg%d" % i argTypeKey = argName + "Type" if customMethodCall is None or not argTypeKey in customMethodCall: validateParam(member, param) realtype = param.realtype else: realtype = xpidl.Forward(name=customMethodCall[argTypeKey], location="", doccomments="") # Emit code to convert this argument from jsval. rvdeclared = writeArgumentUnboxing( f, i, argName, realtype, optional=param.optional, rvdeclared=rvdeclared, nullBehavior=param.null, undefinedBehavior=param.undefined, ) elif isSetter: rvdeclared = writeArgumentUnboxing( f, None, "arg0", member.realtype, optional=False, rvdeclared=rvdeclared, nullBehavior=member.null, undefinedBehavior=member.undefined, propIndex=stringtable.stringIndex(member.name), ) canFail = customMethodCall is None or customMethodCall.get("canFail", True) if canFail and not rvdeclared: f.write(" nsresult rv;\n") rvdeclared = True if code is not None: f.write("%s\n" % code) if code is None or (isGetter and callTemplate is ""): debugGetter = code is not None if debugGetter: f.write("#ifdef DEBUG\n") f.write(" nsresult debug_rv;\n") f.write( " nsCOMPtr<%s> debug_self;\n" " CallQueryInterface(self, getter_AddRefs(debug_self));\n" % member.iface.name ) prefix = "debug_" else: prefix = "" resultname = prefix + "result" selfname = prefix + "self" nsresultname = prefix + "rv" # Prepare out-parameter. if isMethod or isGetter: writeResultDecl(f, member.realtype, resultname) # Call the method. if isMethod: comName = header.methodNativeName(member) argv = ["arg" + str(i) for i, p in enumerate(member.params)] if member.implicit_jscontext: argv.append("cx") if member.optional_argc: argv.append("std::min<uint32_t>(argc, %d) - %d" % (len(member.params), requiredArgs)) if not isVoidType(member.realtype): argv.append(outParamForm(resultname, member.realtype)) args = ", ".join(argv) else: comName = header.attributeNativeName(member, isGetter) if isGetter: args = outParamForm(resultname, member.realtype) else: args = "arg0" if member.implicit_jscontext: args = "cx, " + args f.write(" ") if canFail or debugGetter: f.write("%s = " % nsresultname) f.write("%s->%s(%s);\n" % (selfname, comName, args)) if debugGetter: checkSuccess = "NS_SUCCEEDED(debug_rv)" if canFail: checkSuccess += " == NS_SUCCEEDED(rv)" f.write( " MOZ_ASSERT(%s && " "xpc_qsSameResult(debug_result, result),\n" ' "Got the wrong answer from the custom ' 'method call!");\n' % checkSuccess ) f.write("#endif\n") if canFail: # Check for errors. f.write(" if (NS_FAILED(rv))\n") if isMethod: f.write(" return xpc_qsThrowMethodFailed(" "cx, rv, vp);\n") else: f.write( " return xpc_qsThrowGetterSetterFailed(cx, rv, " + "JSVAL_TO_OBJECT(vp[1]), (uint16_t)%d);\n" % stringtable.stringIndex(member.name) ) # Convert the return value. if isMethod or isGetter: writeResultConv(f, member.realtype, "args.rval()", "*vp") else: f.write(" return true;\n") # Epilog. f.write("}\n") if disableOptimizationForMSVC: setOptimizationForMSVC(f, True) f.write("\n") # Now write out the call to the template function. if customMethodCall is not None: f.write(callTemplate)
def writeStub(f, customMethodCalls, member, stubName, writeThisUnwrapping, writeCheckForFailure, writeResultWrapping, isSetter=False): """ Write a single quick stub (a custom SpiderMonkey getter/setter/method) for the specified XPCOM interface-member. """ if member.kind == 'method' and member.forward: member = member.iface.namemap[member.forward] isAttr = (member.kind == 'attribute') isMethod = (member.kind == 'method') assert isAttr or isMethod isNotxpcom = isMethod and member.notxpcom isGetter = isAttr and not isSetter signature = "static JSBool\n" if isAttr: # JSPropertyOp signature. if isSetter: signature += "%s(JSContext *cx, JSObject *obj, jsid id, JSBool strict,%s jsval *vp)\n" else: signature += "%s(JSContext *cx, JSObject *obj, jsid id,%s jsval *vp)\n" else: # JSFastNative. signature += "%s(JSContext *cx, uintN argc,%s jsval *vp)\n" customMethodCall = customMethodCalls.get(stubName, None) if customMethodCall is None: customMethodCall = customMethodCalls.get(member.iface.name + '_', None) if customMethodCall is not None: if isMethod: code = customMethodCall.get('code', None) elif isGetter: code = customMethodCall.get('getter_code', None) else: code = customMethodCall.get('setter_code', None) else: code = None if code is not None: templateName = member.iface.name if isGetter: templateName += '_Get' elif isSetter: templateName += '_Set' # Generate the code for the stub, calling the template function # that's shared between the stubs. The stubs can't have additional # arguments, only the template function can. callTemplate = signature % (stubName, '') callTemplate += "{\n" argumentValues = (customMethodCall['additionalArgumentValues'] % header.methodNativeName(member)) if isAttr: callTemplate += (" return %s(cx, obj, id%s, %s, vp);\n" % (templateName, ", strict" if isSetter else "", argumentValues)) else: callTemplate += (" return %s(cx, argc, %s, vp);\n" % (templateName, argumentValues)) callTemplate += "}\n\n" # Fall through and create the template function stub called from the # real stubs, but only generate the stub once. Otherwise, just write # out the call to the template function and return. templateGenerated = templateName + '_generated' if templateGenerated in customMethodCall: f.write(callTemplate) return customMethodCall[templateGenerated] = True stubName = templateName else: callTemplate = "" else: callTemplate = "" code = customMethodCall.get('code', None) # Function prolog. # Only template functions can have additional arguments. if customMethodCall is None or not 'additionalArguments' in customMethodCall: additionalArguments = '' else: additionalArguments = " %s," % customMethodCall['additionalArguments'] f.write(signature % (stubName, additionalArguments)) f.write("{\n") f.write(" XPC_QS_ASSERT_CONTEXT_OK(cx);\n") # For methods, compute "this". if isMethod: f.write(" JSObject *obj = JS_THIS_OBJECT(cx, vp);\n" " if (!obj)\n" " return JS_FALSE;\n") # Create ccx if needed. haveCcx = memberNeedsCcx(member) if haveCcx: f.write(" XPCCallContext ccx(JS_CALLER, cx, obj, " "JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));\n") if isInterfaceType(member.realtype): f.write(" XPCLazyCallContext lccx(ccx);\n") selfname = writeThisUnwrapping(f, member, isMethod, isGetter, customMethodCall, haveCcx) rvdeclared = False if isMethod: inArgs = argumentsLength(member) # If there are any required arguments, check argc. requiredArgs = inArgs while requiredArgs and member.params[requiredArgs - 1].optional: requiredArgs -= 1 if requiredArgs: f.write(" if (argc < %d)\n" % requiredArgs) f.write(" return xpc_qsThrow(cx, " "NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n") # Convert in-parameters. if inArgs > 0: f.write(" jsval *argv = JS_ARGV(cx, vp);\n") for i in range(inArgs): param = member.params[i] argName = 'arg%d' % i argTypeKey = argName + 'Type' if customMethodCall is None or not argTypeKey in customMethodCall: validateParam(member, param) realtype = param.realtype else: realtype = xpidl.Forward(name=customMethodCall[argTypeKey], location='', doccomments='') # Emit code to convert this argument from jsval. rvdeclared = writeArgumentUnboxing( f, i, argName, realtype, haveCcx=haveCcx, optional=param.optional, rvdeclared=rvdeclared, nullBehavior=param.null, undefinedBehavior=param.undefined) if inArgs < len(member.params): f.write(" nsWrapperCache *cache;\n") elif isSetter: rvdeclared = writeArgumentUnboxing(f, None, 'arg0', member.realtype, haveCcx=False, optional=False, rvdeclared=rvdeclared, nullBehavior=member.null, undefinedBehavior=member.undefined) canFail = not isNotxpcom and (customMethodCall is None or customMethodCall.get('canFail', True)) if canFail and not rvdeclared: f.write(" nsresult rv;\n") rvdeclared = True if code is not None: f.write("%s\n" % code) if code is None or (isGetter and callTemplate is ""): debugGetter = code is not None if debugGetter: f.write("#ifdef DEBUG\n") f.write(" nsresult debug_rv;\n") f.write( " nsCOMPtr<%s> debug_self;\n" " CallQueryInterface(self, getter_AddRefs(debug_self));\n" % member.iface.name) prefix = 'debug_' else: prefix = '' resultname = prefix + 'result' selfname = prefix + selfname nsresultname = prefix + 'rv' # Prepare out-parameter. if isMethod or isGetter: writeResultDecl(f, member, resultname) # Call the method. if isMethod: comName = header.methodNativeName(member) argv = ['arg' + str(i) for i in range(inArgs)] if inArgs < len(member.params): argv.append( outParamForm('cache', member.params[inArgs].realtype)) if member.implicit_jscontext: argv.append('cx') if member.optional_argc: argv.append('argc - %d' % requiredArgs) if not isNotxpcom and not isVoidType(member.realtype): argv.append(outParamForm(resultname, member.realtype)) args = ', '.join(argv) else: comName = header.attributeNativeName(member, isGetter) if isGetter: args = outParamForm(resultname, member.realtype) else: args = "arg0" if member.implicit_jscontext: args = "cx, " + args f.write(" ") if canFail or debugGetter: f.write("%s = " % nsresultname) elif isNotxpcom: f.write("%s = " % resultname) f.write("%s->%s(%s);\n" % (selfname, comName, args)) if debugGetter: checkSuccess = "NS_SUCCEEDED(debug_rv)" if canFail: checkSuccess += " == NS_SUCCEEDED(rv)" f.write(" NS_ASSERTION(%s && " "xpc_qsSameResult(debug_result, result),\n" " \"Got the wrong answer from the custom " "method call!\");\n" % checkSuccess) f.write("#endif\n") if canFail: # Check for errors. writeCheckForFailure(f, isMethod, isGetter, haveCcx) # Convert the return value. if isMethod or isGetter: writeResultWrapping(f, member, 'vp', '*vp') else: f.write(" return JS_TRUE;\n") # Epilog. f.write("}\n\n") # Now write out the call to the template function. if customMethodCall is not None: f.write(callTemplate)