mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 07:15:46 +00:00
Bug 822470 part 5. Refactor callback codegen so it can be reused for callback interfaces too. r=peterv
This commit is contained in:
parent
cf1f63d55a
commit
a6ee53ef28
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user