From 6d7bbe9aedb6f731113877e19aa8e814ace1fd3e Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 9 Nov 2012 07:43:58 -0800 Subject: [PATCH] Bug 779048 part 10. Start using the new callback codegen in argument and return value conversion. r=peterv,smaug --- content/base/src/WebSocket.h | 6 +- content/base/src/nsXMLHttpRequest.h | 7 +- dom/base/nsScreen.h | 7 +- dom/bindings/Codegen.py | 107 ++++++++++++++++-------- dom/bindings/test/TestBindingHeader.h | 22 ++--- dom/bindings/test/TestCodeGen.webidl | 4 +- dom/bindings/test/TestExampleGen.webidl | 4 +- 7 files changed, 105 insertions(+), 52 deletions(-) diff --git a/content/base/src/WebSocket.h b/content/base/src/WebSocket.h index 2cb78220785..1ba348142c8 100644 --- a/content/base/src/WebSocket.h +++ b/content/base/src/WebSocket.h @@ -16,6 +16,7 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/TypedArray.h" #include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/EventHandlerBinding.h" // Need this for BinaryType. #include "mozilla/dom/WebSocketBinding.h" @@ -50,10 +51,11 @@ namespace dom { nsresult rv = GetOn##_lowercase(aCx, &val); \ return NS_SUCCEEDED(rv) ? JSVAL_TO_OBJECT(val) : nullptr; \ } \ - void SetOn##_lowercase(JSContext* aCx, JSObject* aCallback, \ + void SetOn##_lowercase(JSContext* aCx, EventHandlerNonNull* aCallback,\ ErrorResult& aRv) \ { \ - aRv = SetOn##_lowercase(aCx, OBJECT_TO_JSVAL(aCallback)); \ + JSObject* callback = aCallback ? aCallback->Callable() : nullptr; \ + aRv = SetOn##_lowercase(aCx, JS::ObjectOrNullValue(callback)); \ } \ NS_IMETHOD GetOn##_lowercase(JSContext* cx, JS::Value* aVal); \ NS_IMETHOD SetOn##_lowercase(JSContext* cx, const JS::Value& aVal); diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h index 89ef403e05a..c7a920a8730 100644 --- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -41,6 +41,7 @@ #include "mozilla/dom/TypedArray.h" #include "mozilla/dom/XMLHttpRequestBinding.h" #include "mozilla/dom/XMLHttpRequestUploadBinding.h" +#include "mozilla/dom/EventHandlerBinding.h" #ifdef Status /* Xlib headers insist on this for some reason... Nuke it because @@ -60,10 +61,12 @@ class nsIDOMFormData; nsresult rv = GetOn##_lowercase(aCx, &val); \ return NS_SUCCEEDED(rv) ? JSVAL_TO_OBJECT(val) : nullptr; \ } \ - void SetOn##_lowercase(JSContext* aCx, JSObject* aCallback, \ + void SetOn##_lowercase(JSContext* aCx, \ + mozilla::dom::EventHandlerNonNull* aCallback, \ ErrorResult& aRv) \ { \ - aRv = SetOn##_lowercase(aCx, OBJECT_TO_JSVAL(aCallback)); \ + JSObject* callback = aCallback ? aCallback->Callable() : nullptr; \ + aRv = SetOn##_lowercase(aCx, JS::ObjectOrNullValue(callback)); \ } class nsXHREventTarget : public nsDOMEventTargetHelper, diff --git a/dom/base/nsScreen.h b/dom/base/nsScreen.h index 473e26b1a6c..7c0cc8b00a6 100644 --- a/dom/base/nsScreen.h +++ b/dom/base/nsScreen.h @@ -7,6 +7,7 @@ #include "mozilla/Attributes.h" #include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/EventHandlerBinding.h" #include "mozilla/dom/ScreenOrientation.h" #include "mozilla/ErrorResult.h" #include "mozilla/Hal.h" @@ -110,10 +111,12 @@ public: nsresult rv = GetOnmozorientationchange(aCx, &val); return NS_SUCCEEDED(rv) ? val.toObjectOrNull() : nullptr; } - void SetOnmozorientationchange(JSContext* aCx, JSObject* aCallback, + void SetOnmozorientationchange(JSContext* aCx, + mozilla::dom::EventHandlerNonNull* aCallback, ErrorResult& aRv) { - aRv = SetOnmozorientationchange(aCx, JS::ObjectOrNullValue(aCallback)); + JSObject* callback = aCallback ? aCallback->Callable() : nullptr; + aRv = SetOnmozorientationchange(aCx, JS::ObjectOrNullValue(callback)); } bool MozLockOrientation(const nsAString& aOrientation, ErrorResult& aRv); diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index c140c35cdea..924c7147978 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -2723,10 +2723,24 @@ for (uint32_t i = 0; i < length; ++i) { raise TypeError("Can't handle member callbacks; need to sort out " "rooting issues") - if type.nullable(): - declType = CGGeneric("JSObject*") + if descriptorProvider.workers: + if type.nullable(): + declType = CGGeneric("JSObject*") + else: + declType = CGGeneric("NonNull") + conversion = " ${declName} = &${val}.toObject();\n" else: - declType = CGGeneric("NonNull") + name = type.unroll().identifier.name + if type.nullable(): + declType = CGGeneric("nsRefPtr<%s>" % name); + else: + declType = CGGeneric("OwningNonNull<%s>" % name) + conversion = ( + " bool inited;\n" + " ${declName} = new %s(cx, ${obj}, &${val}.toObject(), &inited);\n" + " if (!inited) {\n" + "%s\n" + " }\n" % (name, CGIndenter(exceptionCodeIndented).define())) if type.treatNonCallableAsNull(): haveCallable = "JS_ObjectIsCallable(cx, &${val}.toObject())" @@ -2736,15 +2750,15 @@ for (uint32_t i = 0; i < length; ++i) { assert(isinstance(defaultValue, IDLNullValue)) haveCallable = "${haveValue} && " + haveCallable template = ( - "if (%s) {\n" - " ${declName} = &${val}.toObject();\n" + ("if (%s) {\n" % haveCallable) + + conversion + "} else {\n" " ${declName} = nullptr;\n" - "}" % haveCallable) + "}") else: template = wrapObjectTemplate( - "if (JS_ObjectIsCallable(cx, &${val}.toObject())) {\n" - " ${declName} = &${val}.toObject();\n" + "if (JS_ObjectIsCallable(cx, &${val}.toObject())) {\n" + + conversion + "} else {\n" "%s" "}" % CGIndenter(onFailureNotCallable(failureCode)).define(), @@ -3210,7 +3224,19 @@ if (!%(resultStr)s) { # See comments in WrapNewBindingObject explaining why we need # to wrap here. # NB: setValue(..., True) calls JS_WrapValue(), so is fallible - return (setValue("JS::ObjectOrNullValue(%s)" % result, True), False) + # XXXbz Event handlers are special for now, until we fix their + # storage to store the EventHandlerNonNull*. + if (descriptorProvider.workers or + type.unroll().identifier.name == "EventHandlerNonNull"): + return (setValue("JS::ObjectOrNullValue(%s)" % result, True), False) + + wrapCode = (("if (%(result)s) {\n" + + CGIndenter(CGGeneric(setValue( + "JS::ObjectValue(*%(result)s->Callable())", True))).define() + + "} else {\n" + + setValue("JS::NullValue()") + + "}") % { "result": result }) + return wrapCode, False if type.tag() == IDLType.Tags.any: # See comments in WrapNewBindingObject explaining why we need @@ -3315,7 +3341,7 @@ def infallibleForMember(member, type, descriptorProvider): return getWrapTemplateForType(type, descriptorProvider, 'result', None,\ memberIsCreator(member), "return false;")[1] -def typeNeedsCx(type, retVal=False): +def typeNeedsCx(type, descriptorProvider, retVal=False): if type is None: return False if type.nullable(): @@ -3323,10 +3349,16 @@ def typeNeedsCx(type, retVal=False): if type.isSequence() or type.isArray(): type = type.inner if type.isUnion(): - return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes) + return any(typeNeedsCx(t, descriptorProvider) for t in + type.unroll().flatMemberTypes) if retVal and type.isSpiderMonkeyInterface(): return True - return type.isCallback() or type.isAny() or type.isObject() + if type.isCallback(): + # XXXbz Event handlers are special for now, until we fix their + # storage to store the EventHandlerNonNull*. + return (descriptorProvider.workers or + type.unroll().identifier.name == "EventHandlerNonNull") + return type.isAny() or type.isObject() # Returns a tuple consisting of a CGThing containing the type of the return # value, or None if there is no need for a return value, and a boolean signaling @@ -3359,9 +3391,12 @@ def getRetvalDeclarationForType(returnType, descriptorProvider, result = CGWrapper(result, post="*") return result, False if returnType.isCallback(): - # XXXbz we're going to assume that callback types are always - # nullable for now. - return CGGeneric("JSObject*"), False + name = returnType.unroll().identifier.name + # XXXbz Event handlers are special for now, until we fix their + # storage to store the EventHandlerNonNull*. + if descriptorProvider.workers or name == "EventHandlerNonNull": + return CGGeneric("JSObject*"), False + return CGGeneric("nsRefPtr<%s>" % name), False if returnType.isAny(): return CGGeneric("JS::Value"), False if returnType.isObject() or returnType.isSpiderMonkeyInterface(): @@ -3393,9 +3428,9 @@ def isResultAlreadyAddRefed(descriptor, extendedAttributes): # Default to already_AddRefed on the main thread, raw pointer in workers return not descriptor.workers and not 'resultNotAddRefed' in extendedAttributes -def needCx(returnType, arguments, extendedAttributes): - return (typeNeedsCx(returnType, True) or - any(typeNeedsCx(a.type) for (a, _) in arguments) or +def needCx(returnType, arguments, extendedAttributes, descriptorProvider): + return (typeNeedsCx(returnType, descriptorProvider, True) or + any(typeNeedsCx(a.type, descriptorProvider) for (a, _) in arguments) or 'implicitJSContext' in extendedAttributes) class CGCallGenerator(CGThing): @@ -3434,7 +3469,8 @@ class CGCallGenerator(CGThing): if isFallible: args.append(CGGeneric("rv")) - needsCx = needCx(returnType, arguments, extendedAttributes) + needsCx = needCx(returnType, arguments, extendedAttributes, + descriptorProvider) if not "cx" in argsPre and needsCx: args.prepend(CGGeneric("cx")) @@ -4459,9 +4495,15 @@ def getUnionAccessorSignatureType(type, descriptorProvider): return CGGeneric(type.inner.identifier.name) if type.isCallback(): + if descriptorProvider.workers: + if type.nullable(): + return CGGeneric("JSObject*") + return CGGeneric("JSObject&") if type.nullable(): - return CGGeneric("JSObject*") - return CGGeneric("JSObject&") + typeName = "%s*" + else: + typeName = "%s&" + return CGGeneric(typeName % type.unroll().identifier.name) if type.isAny(): return CGGeneric("JS::Value") @@ -6741,13 +6783,8 @@ class CGNativeMember(ClassMethod): "return ${declName}.Ptr();") return result.define(), "nullptr", returnCode if type.isCallback(): - if type.nullable(): - returnCode = "return ${declName};" - else: - returnCode = "return ${declName}.Ptr();" - # XXXbz we're going to assume that callback types are always - # nullable for now in our return value. - return "JSObject*", "nullptr", returnCode + return ("already_AddRefed<%s>" % type.unroll().identifier.name, + "nullptr", "return ${declName}.forget();") if type.isAny(): return "JS::Value", "JS::UndefinedValue()", "return ${declName};" if type.isObject(): @@ -6811,7 +6848,8 @@ class CGNativeMember(ClassMethod): # And jscontext bits. needCx expects a list of tuples, in each of which # the first element is the actual argument if (self.passCxAsNeeded and - needCx(returnType, ((a, "") for a in argList), self.extendedAttrs)): + needCx(returnType, ((a, "") for a in argList), self.extendedAttrs, + self.descriptor)): args.insert(0, Argument("JSContext*", "cx")) return args @@ -6885,13 +6923,16 @@ class CGNativeMember(ClassMethod): if type.isCallback(): if type.nullable(): - declType = "JSObject*" + if optional: + declType = "nsRefPtr<%s>" + else: + declType = "%s*" else: if optional: - declType = "NonNull" + declType = "OwningNonNull<%s>" else: - declType = "JSObject&" - return declType, False, False + declType = "%s&" + return declType % type.unroll().identifier.name, False, False if type.isAny(): return "JS::Value", False, False diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h index d07cc9af33a..c25184e4a20 100644 --- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -353,16 +353,16 @@ public: void SetEnumAttribute(TestEnum); // Callback types - void PassCallback(JSContext*, JSObject&); - void PassNullableCallback(JSContext*, JSObject*); - void PassOptionalCallback(JSContext*, const Optional >&); - void PassOptionalNullableCallback(JSContext*, const Optional&); - void PassOptionalNullableCallbackWithDefaultValue(JSContext*, JSObject*); - JSObject* ReceiveCallback(JSContext*); - JSObject* ReceiveNullableCallback(JSContext*); - void PassNullableTreatAsNullCallback(JSContext*, JSObject*); - void PassOptionalNullableTreatAsNullCallback(JSContext*, const Optional&); - void PassOptionalNullableTreatAsNullCallbackWithDefaultValue(JSContext*, JSObject*); + void PassCallback(TestCallback&); + void PassNullableCallback(TestCallback*); + void PassOptionalCallback(const Optional >&); + void PassOptionalNullableCallback(const Optional >&); + void PassOptionalNullableCallbackWithDefaultValue(TestCallback*); + already_AddRefed ReceiveCallback(); + already_AddRefed ReceiveNullableCallback(); + void PassNullableTreatAsNullCallback(TestTreatAsNullCallback*); + void PassOptionalNullableTreatAsNullCallback(const Optional >&); + void PassOptionalNullableTreatAsNullCallbackWithDefaultValue(TestTreatAsNullCallback*); // Any types void PassAny(JSContext*, JS::Value); @@ -403,7 +403,7 @@ public: void PassUnionWithArrayBuffer(const ArrayBufferOrLong&); void PassUnionWithString(JSContext*, const StringOrObject&); //void PassUnionWithEnum(JSContext*, const TestEnumOrObject&); - void PassUnionWithCallback(JSContext*, const TestCallbackOrLong&); + //void PassUnionWithCallback(JSContext*, const TestCallbackOrLong&); void PassUnionWithObject(JSContext*, const ObjectOrLong&); // binaryNames tests diff --git a/dom/bindings/test/TestCodeGen.webidl b/dom/bindings/test/TestCodeGen.webidl index 36e5c5a72b5..82f72374cb3 100644 --- a/dom/bindings/test/TestCodeGen.webidl +++ b/dom/bindings/test/TestCodeGen.webidl @@ -350,7 +350,9 @@ interface TestInterface { void passUnionWithArrayBuffer((ArrayBuffer or long) arg); void passUnionWithString((DOMString or object) arg); //void passUnionWithEnum((TestEnum or object) arg); - void passUnionWithCallback((TestCallback or long) arg); + // Trying to use a callback in a union won't include the test + // headers, unfortunately, so won't compile. + //void passUnionWithCallback((TestCallback or long) arg); void passUnionWithObject((object or long) arg); //void passUnionWithDict((Dict or long) arg); diff --git a/dom/bindings/test/TestExampleGen.webidl b/dom/bindings/test/TestExampleGen.webidl index 2bad779c8f1..2497995d296 100644 --- a/dom/bindings/test/TestExampleGen.webidl +++ b/dom/bindings/test/TestExampleGen.webidl @@ -271,7 +271,9 @@ interface TestExampleInterface { void passUnionWithArrayBuffer((ArrayBuffer or long) arg); void passUnionWithString((DOMString or object) arg); //void passUnionWithEnum((TestEnum or object) arg); - void passUnionWithCallback((TestCallback or long) arg); + // Trying to use a callback in a union won't include the test + // headers, unfortunately, so won't compile. + // void passUnionWithCallback((TestCallback or long) arg); void passUnionWithObject((object or long) arg); //void passUnionWithDict((Dict or long) arg);