Bug 822470 part 5. Refactor callback codegen so it can be reused for callback interfaces too. r=peterv

This commit is contained in:
Boris Zbarsky 2013-01-28 08:34:30 -05:00
parent cf1f63d55a
commit a6ee53ef28

View File

@ -7654,26 +7654,30 @@ class CGExampleRoot(CGThing):
def define(self): def define(self):
return self.root.define() return self.root.define()
class CGCallbackFunction(CGClass): class CGCallback(CGClass):
def __init__(self, callback, descriptorProvider): def __init__(self, idlObject, descriptorProvider, baseName, methods,
if callback.isWorkerOnly() and not descriptorProvider.workers: getters=[], setters=[]):
self.generatable = False self.baseName = baseName
return name = idlObject.identifier.name
name = callback.identifier.name
if descriptorProvider.workers: if descriptorProvider.workers:
name += "Workers" name += "Workers"
try: try:
# For our public Call() method we want most of the same args and the # For our public methods that needThisHandling we want most of the
# same return type as what CallCallback generates. So we want to # same args and the same return type as what CallbackMember
# take advantage of all its CGNativeMember infrastructure, but that # generates. So we want to take advantage of all its
# infrastructure can't deal with templates and most especially # CGNativeMember infrastructure, but that infrastructure can't deal
# template arguments. So just cheat and have CallCallback compute # with templates and most especially template arguments. So just
# all those things for us. # cheat and have CallbackMember compute all those things for us.
callCallback = CallCallback(callback, descriptorProvider) realMethods = []
for method in methods:
if not method.needThisHandling:
realMethods.append(method)
else:
realMethods.extend(self.getMethodImpls(method))
CGClass.__init__(self, name, CGClass.__init__(self, name,
bases=[ClassBase("CallbackFunction")], bases=[ClassBase(baseName)],
constructors=self.getConstructors(), constructors=self.getConstructors(),
methods=self.getCallImpls(callCallback)) methods=realMethods+getters+setters)
self.generatable = True self.generatable = True
except NoSuchDescriptorError, err: except NoSuchDescriptorError, err:
if not descriptorProvider.workers: if not descriptorProvider.workers:
@ -7699,21 +7703,13 @@ class CGCallbackFunction(CGClass):
bodyInHeader=True, bodyInHeader=True,
visibility="public", visibility="public",
baseConstructors=[ baseConstructors=[
"CallbackFunction(cx, aOwner, aCallback, aInited)" "%s(cx, aOwner, aCallback, aInited)" % self.baseName
],
body=""),
ClassConstructor(
[Argument("CallbackFunction*", "aOther")],
bodyInHeader=True,
visibility="public",
explicit=True,
baseConstructors=[
"CallbackFunction(aOther)"
], ],
body="")] body="")]
def getCallImpls(self, callCallback): def getMethodImpls(self, method):
args = list(callCallback.args) assert method.needThisHandling
args = list(method.args)
# Strip out the JSContext*/JSObject* args # Strip out the JSContext*/JSObject* args
# that got added. # that got added.
assert args[0].name == "cx" and args[0].argType == "JSContext*" assert args[0].name == "cx" and args[0].argType == "JSContext*"
@ -7741,24 +7737,47 @@ class CGCallbackFunction(CGClass):
" aRv.Throw(NS_ERROR_FAILURE);\n" " aRv.Throw(NS_ERROR_FAILURE);\n"
" return${errorReturn};\n" " return${errorReturn};\n"
"}\n" "}\n"
"return Call(${callArgs});").substitute({ "return ${methodName}(${callArgs});").substitute({
"errorReturn" : callCallback.getDefaultRetval(), "errorReturn" : method.getDefaultRetval(),
"callArgs" : ", ".join(argnamesWithThis) "callArgs" : ", ".join(argnamesWithThis),
"methodName": method.name,
}) })
bodyWithoutThis = string.Template( bodyWithoutThis = string.Template(
setupCall + setupCall +
"return Call(${callArgs});").substitute({ "return ${methodName}(${callArgs});").substitute({
"errorReturn" : callCallback.getDefaultRetval(), "errorReturn" : method.getDefaultRetval(),
"callArgs" : ", ".join(argnamesWithoutThis) "callArgs" : ", ".join(argnamesWithoutThis),
"methodName": method.name,
}) })
return [ClassMethod("Call", callCallback.returnType, args, return [ClassMethod(method.name, method.returnType, args,
bodyInHeader=True, bodyInHeader=True,
templateArgs=["typename T"], templateArgs=["typename T"],
body=bodyWithThis), body=bodyWithThis),
ClassMethod("Call", callCallback.returnType, argsWithoutThis, ClassMethod(method.name, method.returnType, argsWithoutThis,
bodyInHeader=True, bodyInHeader=True,
body=bodyWithoutThis), body=bodyWithoutThis),
callCallback] method]
class CGCallbackFunction(CGCallback):
def __init__(self, callback, descriptorProvider):
if callback.isWorkerOnly() and not descriptorProvider.workers:
self.generatable = False
return
CGCallback.__init__(self, callback, descriptorProvider,
"CallbackFunction",
methods=[CallCallback(callback, descriptorProvider)])
def getConstructors(self):
return CGCallback.getConstructors(self) + [
ClassConstructor(
[Argument("CallbackFunction*", "aOther")],
bodyInHeader=True,
visibility="public",
explicit=True,
baseConstructors=[
"CallbackFunction(aOther)"
],
body="")]
class FakeMember(): class FakeMember():
def __init__(self): def __init__(self):
@ -7775,11 +7794,14 @@ class FakeMember():
return True return True
return None return None
class CallCallback(CGNativeMember): class CallbackMember(CGNativeMember):
def __init__(self, callback, descriptorProvider): def __init__(self, sig, name, descriptorProvider, needThisHandling):
sig = callback.signatures()[0] """
needThisHandling is True if we need to be able to accept a specified
thisObj, False otherwise.
"""
self.retvalType = sig[0] self.retvalType = sig[0]
self.callback = callback self.originalSig = sig
args = sig[1] args = sig[1]
self.argCount = len(args) self.argCount = len(args)
if self.argCount > 0: if self.argCount > 0:
@ -7791,11 +7813,18 @@ class CallCallback(CGNativeMember):
lastArg.identifier.name)) lastArg.identifier.name))
else: else:
self.argCountStr = "%d" % self.argCount self.argCountStr = "%d" % self.argCount
self.needThisHandling = needThisHandling
# If needThisHandling, we generate ourselves as private and the caller
# will handle generating public versions that handle the "this" stuff.
visibility = "private" if needThisHandling else "public"
# We don't care, for callback codegen, whether our original member was
# a method or attribure or whatnot. Just always pass FakeMember()
# here.
CGNativeMember.__init__(self, descriptorProvider, FakeMember(), CGNativeMember.__init__(self, descriptorProvider, FakeMember(),
"Call", (self.retvalType, args), name, (self.retvalType, args),
extendedAttrs={}, extendedAttrs={},
passCxAsNeeded=False, passCxAsNeeded=False,
visibility="private", visibility=visibility,
jsObjectsArePtr=True) jsObjectsArePtr=True)
# We have to do all the generation of our body now, because # We have to do all the generation of our body now, because
# the caller relies on us throwing if we can't manage it. # the caller relies on us throwing if we can't manage it.
@ -7805,9 +7834,12 @@ class CallCallback(CGNativeMember):
def getImpl(self): def getImpl(self):
replacements = { replacements = {
"declRval": self.getRvalDecl(),
"errorReturn" : self.getDefaultRetval(), "errorReturn" : self.getDefaultRetval(),
"returnResult": self.getResultConversion(), "returnResult": self.getResultConversion(),
"convertArgs": self.getArgConversions(), "convertArgs": self.getArgConversions(),
"doCall": self.getCall(),
"setupCall": self.getCallSetup(),
} }
if self.argCount > 0: if self.argCount > 0:
replacements["argCount"] = self.argCountStr replacements["argCount"] = self.argCountStr
@ -7818,23 +7850,17 @@ class CallCallback(CGNativeMember):
" return${errorReturn};\n" " return${errorReturn};\n"
"}\n" "}\n"
).substitute(replacements) ).substitute(replacements)
replacements["argv"] = "argv.begin()"
replacements["argc"] = "argc"
else: else:
# Avoid weird 0-sized arrays # Avoid weird 0-sized arrays
replacements["argvDecl"] = "" replacements["argvDecl"] = ""
replacements["argv"] = "nullptr"
replacements["argc"] = "0"
return string.Template( return string.Template(
"JS::Value rval = JSVAL_VOID;\n" # Newlines and semicolons are in the values
"${argvDecl}" # Newlines and semicolons are in the value "${setupCall}"
"${declRval}"
"${argvDecl}"
"${convertArgs}" "${convertArgs}"
"if (!JS_CallFunctionValue(cx, aThisObj, JS::ObjectValue(*mCallback),\n" "${doCall}"
" ${argc}, ${argv}, &rval)) {\n"
" aRv.Throw(NS_ERROR_UNEXPECTED);\n"
" return${errorReturn};\n"
"}\n"
"${returnResult}").substitute(replacements) "${returnResult}").substitute(replacements)
def getResultConversion(self): def getResultConversion(self):
@ -7860,11 +7886,11 @@ class CallCallback(CGNativeMember):
return convertType.define() + "\n" + assignRetval return convertType.define() + "\n" + assignRetval
def getArgConversions(self): def getArgConversions(self):
# Just reget the arglist from self.callback, because our superclasses # Just reget the arglist from self.originalSig, because our superclasses
# just have way to many members they like to clobber, so I can't find a # just have way to many members they like to clobber, so I can't find a
# safe member name to store it in. # safe member name to store it in.
argConversions = [self.getArgConversion(i, arg) for (i, arg) argConversions = [self.getArgConversion(i, arg) for (i, arg)
in enumerate(self.callback.signatures()[0][1])] in enumerate(self.originalSig[1])]
# Do them back to front, so our argc modifications will work # Do them back to front, so our argc modifications will work
# correctly, because we examine trailing arguments first. # correctly, because we examine trailing arguments first.
argConversions.reverse(); argConversions.reverse();
@ -7875,8 +7901,7 @@ class CallCallback(CGNativeMember):
post="\n} while (0);") post="\n} while (0);")
for c in argConversions] for c in argConversions]
if self.argCount > 0: if self.argCount > 0:
argConversions.insert(0, argConversions.insert(0, self.getArgcDecl())
CGGeneric("unsigned argc = %s;" % self.argCountStr));
# And slap them together. # And slap them together.
return CGList(argConversions, "\n\n").define() + "\n\n" return CGList(argConversions, "\n\n").define() + "\n\n"
@ -7939,11 +7964,67 @@ class CallCallback(CGNativeMember):
def getArgs(self, returnType, argList): def getArgs(self, returnType, argList):
args = CGNativeMember.getArgs(self, returnType, argList) args = CGNativeMember.getArgs(self, returnType, argList)
if not self.needThisHandling:
return args
# We want to allow the caller to pass in a "this" object, as # We want to allow the caller to pass in a "this" object, as
# well as a JSContext. # well as a JSContext.
return [Argument("JSContext*", "cx"), return [Argument("JSContext*", "cx"),
Argument("JSObject*", "aThisObj")] + args Argument("JSObject*", "aThisObj")] + args
def getCallSetup(self):
if self.needThisHandling:
# It's been done for us already
return ""
return string.Template(
"CallSetup s(mCallback);\n"
"JSContext* cx = s.GetContext();\n"
"if (!cx) {\n"
" aRv.Throw(NS_ERROR_UNEXPECTED);\n"
" return${errorReturn};\n"
"}\n").substitute({
"errorReturn" : self.getDefaultRetval(),
})
def getArgcDecl(self):
return CGGeneric("unsigned argc = %s;" % self.argCountStr);
class CallbackMethod(CallbackMember):
def __init__(self, sig, name, descriptorProvider, needThisHandling):
CallbackMember.__init__(self, sig, name, descriptorProvider,
needThisHandling)
def getRvalDecl(self):
return "JS::Value rval = JSVAL_VOID;\n"
def getCall(self):
replacements = {
"errorReturn" : self.getDefaultRetval(),
"thisObj": self.getThisObj(),
"getCallable": self.getCallableDecl()
}
if self.argCount > 0:
replacements["argv"] = "argv.begin()"
replacements["argc"] = "argc"
else:
replacements["argv"] = "nullptr"
replacements["argc"] = "0"
return string.Template("${getCallable}"
"if (!JS_CallFunctionValue(cx, ${thisObj}, callable,\n"
" ${argc}, ${argv}, &rval)) {\n"
" aRv.Throw(NS_ERROR_UNEXPECTED);\n"
" return${errorReturn};\n"
"}\n").substitute(replacements)
class CallCallback(CallbackMethod):
def __init__(self, callback, descriptorProvider):
CallbackMethod.__init__(self, callback.signatures()[0], "Call",
descriptorProvider, needThisHandling=True)
def getThisObj(self):
return "aThisObj"
def getCallableDecl(self):
return "JS::Value callable = JS::ObjectValue(*mCallback);\n"
class GlobalGenRoots(): class GlobalGenRoots():
""" """
Roots for global codegen. Roots for global codegen.