From 7abaa88b2125e987b088ef8e7dd50b66c1730247 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 7 Feb 2014 15:56:14 -0500 Subject: [PATCH] Bug 956806 part 2. Share generic getters/setters/methods across all bindings. r=peterv --- dom/bindings/BindingUtils.cpp | 106 ++++++++++++++++++++++++++++++++++ dom/bindings/BindingUtils.h | 30 ++++++++-- dom/bindings/Codegen.py | 25 +++++--- dom/bindings/Configuration.py | 10 ++++ 4 files changed, 158 insertions(+), 13 deletions(-) diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index a5cab407732e..9e240aa718b8 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -89,6 +89,23 @@ ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, return false; } +bool +ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, + const ErrNum aErrorNumber, + prototypes::ID aProtoId) +{ + return ThrowInvalidThis(aCx, aArgs, aErrorNumber, + NamesOfInterfacesWithProtos[aProtoId]); +} + +bool +ThrowNoSetterArg(JSContext* aCx, prototypes::ID aProtoId) +{ + nsPrintfCString errorMessage("%s attribute setter", + NamesOfInterfacesWithProtos[aProtoId]); + return ThrowErrorMessage(aCx, MSG_MISSING_ARGUMENTS, errorMessage.get()); +} + } // namespace dom struct ErrorResult::Message { @@ -2202,5 +2219,94 @@ EnumerateGlobal(JSContext* aCx, JS::Handle aObj) return JS_EnumerateStandardClasses(aCx, aObj); } +bool +GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp) +{ + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); + prototypes::ID protoID = static_cast(info->protoID); + if (!args.thisv().isObject()) { + return ThrowInvalidThis(cx, args, + MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE, + protoID); + } + JS::Rooted obj(cx, &args.thisv().toObject()); + + void* self; + { + nsresult rv = UnwrapObject(obj, self, protoID, info->depth); + if (NS_FAILED(rv)) { + return ThrowInvalidThis(cx, args, + GetInvalidThisErrorForGetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO), + protoID); + } + } + + MOZ_ASSERT(info->type() == JSJitInfo::Getter); + JSJitGetterOp getter = info->getter; + return getter(cx, obj, self, JSJitGetterCallArgs(args)); +} + +bool +GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp) +{ + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); + prototypes::ID protoID = static_cast(info->protoID); + if (!args.thisv().isObject()) { + return ThrowInvalidThis(cx, args, + MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE, + protoID); + } + JS::Rooted obj(cx, &args.thisv().toObject()); + + void* self; + { + nsresult rv = UnwrapObject(obj, self, protoID, info->depth); + if (NS_FAILED(rv)) { + return ThrowInvalidThis(cx, args, + GetInvalidThisErrorForSetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO), + protoID); + } + } + if (args.length() == 0) { + return ThrowNoSetterArg(cx, protoID); + } + MOZ_ASSERT(info->type() == JSJitInfo::Setter); + JSJitSetterOp setter = info->setter; + if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) { + return false; + } + args.rval().set(JSVAL_VOID); + return true; +} + +bool +GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp) +{ + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); + prototypes::ID protoID = static_cast(info->protoID); + if (!args.thisv().isObject()) { + return ThrowInvalidThis(cx, args, + MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE, + protoID); + } + JS::Rooted obj(cx, &args.thisv().toObject()); + + void* self; + { + nsresult rv = UnwrapObject(obj, self, protoID, info->depth); + if (NS_FAILED(rv)) { + return ThrowInvalidThis(cx, args, + GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO), + protoID); + } + } + MOZ_ASSERT(info->type() == JSJitInfo::Method); + JSJitMethodOp method = info->method; + return method(cx, obj, self, JSJitMethodCallArgs(args)); +} + } // namespace dom } // namespace mozilla diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 8009c2bcea06..6b5a8aace8f2 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -93,6 +93,11 @@ ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, const ErrNum aErrorNumber, const char* aInterfaceName); +bool +ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, + const ErrNum aErrorNumber, + prototypes::ID aProtoId); + inline bool ThrowMethodFailedWithDetails(JSContext* cx, ErrorResult& rv, const char* ifaceName, @@ -206,9 +211,10 @@ IsDOMObject(JSObject* obj) // (for example, overload resolution uses unwrapping to tell what sort // of thing it's looking at). // U must be something that a T* can be assigned to (e.g. T* or an nsRefPtr). -template +template MOZ_ALWAYS_INLINE nsresult -UnwrapObject(JSObject* obj, U& value) +UnwrapObject(JSObject* obj, U& value, prototypes::ID protoID, + uint32_t protoDepth) { /* First check to see whether we have a DOM object */ const DOMClass* domClass = GetDOMClass(obj); @@ -234,8 +240,7 @@ UnwrapObject(JSObject* obj, U& value) /* This object is a DOM object. Double-check that it is safely castable to T by checking whether it claims to inherit from the class identified by protoID. */ - if (domClass->mInterfaceChain[PrototypeTraits::Depth] == - PrototypeID) { + if (domClass->mInterfaceChain[protoDepth] == protoID) { value = UnwrapDOMObject(obj); return NS_OK; } @@ -244,6 +249,14 @@ UnwrapObject(JSObject* obj, U& value) return NS_ERROR_XPC_BAD_CONVERT_JS; } +template +MOZ_ALWAYS_INLINE nsresult +UnwrapObject(JSObject* obj, U& value) +{ + return UnwrapObject(obj, value, PrototypeID, + PrototypeTraits::Depth); +} + inline bool IsNotDateOrRegExp(JSContext* cx, JS::Handle obj) { @@ -2455,6 +2468,15 @@ class InternedStringId } }; +bool +GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp); + +bool +GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp); + +bool +GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp); + } // namespace dom } // namespace mozilla diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index bc4a9b7d8ca5..a712b2e002e5 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1633,8 +1633,10 @@ class MethodDefiner(PropertyDefiner): jitinfo = ("reinterpret_cast(&%s_methodinfo)" % accessor) if m.get("allowCrossOriginThis", False): accessor = "genericCrossOriginMethod" - else: + elif self.descriptor.needsSpecialGenericOps(): accessor = "genericMethod" + else: + accessor = "GenericBindingMethod" else: jitinfo = "nullptr" @@ -1691,8 +1693,10 @@ class AttrDefiner(PropertyDefiner): accessor = "genericLenientGetter" elif attr.getExtendedAttribute("CrossOriginReadable"): accessor = "genericCrossOriginGetter" - else: + elif self.descriptor.needsSpecialGenericOps(): accessor = "genericGetter" + else: + accessor = "GenericBindingGetter" jitinfo = "&%s_getterinfo" % attr.identifier.name return "{ { JS_CAST_NATIVE_TO(%s, JSPropertyOp), %s } }" % \ (accessor, jitinfo) @@ -1710,8 +1714,10 @@ class AttrDefiner(PropertyDefiner): accessor = "genericLenientSetter" elif attr.getExtendedAttribute("CrossOriginWritable"): accessor = "genericCrossOriginSetter" - else: + elif self.descriptor.needsSpecialGenericOps(): accessor = "genericSetter" + else: + accessor = "GenericBindingSetter" jitinfo = "&%s_setterinfo" % attr.identifier.name return "{ { JS_CAST_NATIVE_TO(%s, JSStrictPropertyOp), %s } }" % \ (accessor, jitinfo) @@ -8739,7 +8745,7 @@ class CGDescriptor(CGThing): continue if (m.isMethod() and m == descriptor.operations['Jsonifier']): hasJsonifier = True - hasMethod = True + hasMethod = descriptor.needsSpecialGenericOps() jsonifierMethod = m elif (m.isMethod() and (not m.isIdentifierLess() or m == descriptor.operations['Stringifier'])): @@ -8751,7 +8757,7 @@ class CGDescriptor(CGThing): cgThings.append(CGMemberJITInfo(descriptor, m)) if m.getExtendedAttribute("CrossOriginCallable"): crossOriginMethods.add(m.identifier.name) - else: + elif descriptor.needsSpecialGenericOps(): hasMethod = True elif m.isAttr(): if m.stringifier: @@ -8767,7 +8773,7 @@ class CGDescriptor(CGThing): hasLenientGetter = True elif m.getExtendedAttribute("CrossOriginReadable"): crossOriginGetters.add(m.identifier.name) - else: + elif descriptor.needsSpecialGenericOps(): hasGetter = True if not m.readonly: for extAttr in ["PutForwards", "Replaceable"]: @@ -8785,17 +8791,18 @@ class CGDescriptor(CGThing): hasLenientSetter = True elif m.getExtendedAttribute("CrossOriginWritable"): crossOriginSetters.add(m.identifier.name) - else: + elif descriptor.needsSpecialGenericOps(): hasSetter = True elif m.getExtendedAttribute("PutForwards"): cgThings.append(CGSpecializedForwardingSetter(descriptor, m)) if m.getExtendedAttribute("CrossOriginWritable"): crossOriginSetters.add(m.identifier.name) - else: + elif descriptor.needsSpecialGenericOps(): hasSetter = True elif m.getExtendedAttribute("Replaceable"): cgThings.append(CGSpecializedReplaceableSetter(descriptor, m)) - hasSetter = True + if descriptor.needsSpecialGenericOps(): + hasSetter = True if (not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject()): cgThings.append(CGMemberJITInfo(descriptor, m)) diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index 66db8c4e2dd5..bb70e3c43993 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -507,6 +507,16 @@ class Descriptor(DescriptorProvider): "HTMLEmbedElement", "HTMLAppletElement"]) + def needsSpecialGenericOps(self): + """ + Returns true if this descriptor requires generic ops other than + GenericBindingMethod/GenericBindingGetter/GenericBindingSetter. + + In practice we need to do this if our this value might be an XPConnect + object or if we need to coerce null/undefined to the global. + """ + return self.hasXPConnectImpls or self.interface.isOnGlobalProtoChain() + # Some utility methods def getTypesFromDescriptor(descriptor): """