diff --git a/content/xbl/src/nsXBLMaybeCompiled.h b/content/xbl/src/nsXBLMaybeCompiled.h new file mode 100644 index 00000000000..9f1c2e118bd --- /dev/null +++ b/content/xbl/src/nsXBLMaybeCompiled.h @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXBLMaybeCompiled_h__ +#define nsXBLMaybeCompiled_h__ + +#include "js/RootingAPI.h" + +/* + * A union containing either a pointer representing uncompiled source or a + * JSObject* representing the compiled result. The class is templated on the + * source object type. + * + * The purpose of abstracting this as a separate class is to allow it to be + * wrapped in a JS::Heap to correctly handle post-barriering of the JSObject + * pointer, when present. + */ +template +class nsXBLMaybeCompiled +{ +public: + nsXBLMaybeCompiled() : mUncompiled(BIT_UNCOMPILED) {} + + nsXBLMaybeCompiled(UncompiledT* uncompiled) + : mUncompiled(reinterpret_cast(uncompiled) | BIT_UNCOMPILED) {} + + nsXBLMaybeCompiled(JSObject* compiled) : mCompiled(compiled) {} + + bool IsCompiled() const + { + return !(mUncompiled & BIT_UNCOMPILED); + } + + UncompiledT* GetUncompiled() const + { + MOZ_ASSERT(!IsCompiled(), "Attempt to get compiled function as uncompiled"); + uintptr_t unmasked = mUncompiled & ~BIT_UNCOMPILED; + return reinterpret_cast(unmasked); + } + + JSObject* GetJSFunction() const + { + MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled"); + return mCompiled; + } + +private: + JSObject*& UnsafeGetJSFunction() + { + MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled"); + return mCompiled; + } + + enum { BIT_UNCOMPILED = 1 << 0 }; + + union + { + // An pointer that represents the function before being compiled, with + // BIT_UNCOMPILED set. + uintptr_t mUncompiled; + + // The JS object for the compiled result. + JSObject* mCompiled; + }; + + friend class js::RootMethods >; +}; + +/* Add support for JS::Heap. */ +namespace js { + +template +struct RootMethods > : public RootMethods +{ + typedef struct RootMethods Base; + + static nsXBLMaybeCompiled initial() { return nsXBLMaybeCompiled(); } + + static bool poisoned(nsXBLMaybeCompiled function) + { + return function.IsCompiled() && Base::poisoned(function.GetJSFunction()); + } + + static bool needsPostBarrier(nsXBLMaybeCompiled function) + { + return function.IsCompiled() && Base::needsPostBarrier(function.GetJSFunction()); + } + +#ifdef JSGC_GENERATIONAL + static void postBarrier(nsXBLMaybeCompiled* functionp) + { + Base::postBarrier(&functionp->UnsafeGetJSFunction()); + } + + static void relocate(nsXBLMaybeCompiled* functionp) + { + Base::relocate(&functionp->UnsafeGetJSFunction()); + } +#endif +}; + +template +class HeapBase > +{ + const JS::Heap >& wrapper() const { + return *static_cast >*>(this); + } + + JS::Heap >& wrapper() { + return *static_cast >*>(this); + } + + const nsXBLMaybeCompiled* extract() const { + return wrapper().address(); + } + + nsXBLMaybeCompiled* extract() { + return wrapper().unsafeGet(); + } + +public: + bool IsCompiled() const { return extract()->IsCompiled(); } + UncompiledT* GetUncompiled() const { return extract()->GetUncompiled(); } + JSObject* GetJSFunction() const { return extract()->GetJSFunction(); } + + void SetUncompiled(UncompiledT* source) { + wrapper().set(nsXBLMaybeCompiled(source)); + } + + void SetJSFunction(JSObject* function) { + wrapper().set(nsXBLMaybeCompiled(function)); + } + + JS::Heap& AsHeapObject() + { + MOZ_ASSERT(extract()->IsCompiled()); + return *reinterpret_cast*>(this); + } +}; + +} /* namespace js */ + +#endif // nsXBLMaybeCompiled_h__ diff --git a/content/xbl/src/nsXBLProtoImplMethod.cpp b/content/xbl/src/nsXBLProtoImplMethod.cpp index bac7d5df57d..e9008ee3d66 100644 --- a/content/xbl/src/nsXBLProtoImplMethod.cpp +++ b/content/xbl/src/nsXBLProtoImplMethod.cpp @@ -24,8 +24,8 @@ using namespace mozilla; nsXBLProtoImplMethod::nsXBLProtoImplMethod(const PRUnichar* aName) : - nsXBLProtoImplMember(aName), - mUncompiledMethod(BIT_UNCOMPILED) + nsXBLProtoImplMember(aName), + mMethod() { MOZ_COUNT_CTOR(nsXBLProtoImplMethod); } @@ -108,12 +108,13 @@ nsXBLProtoImplMethod::InstallMember(JSContext* aCx, NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); // now we want to reevaluate our property using aContext and the script object for this window... - if (mJSMethodObject) { + JS::Rooted jsMethodObject(aCx, GetCompiledMethod()); + if (jsMethodObject) { nsDependentString name(mName); // First, make the function in the compartment of the scope object. JSAutoCompartment ac(aCx, scopeObject); - JS::Rooted method(aCx, ::JS_CloneFunctionObject(aCx, mJSMethodObject, scopeObject)); + JS::Rooted method(aCx, ::JS_CloneFunctionObject(aCx, jsMethodObject, scopeObject)); if (!method) { return NS_ERROR_OUT_OF_MEMORY; } @@ -149,7 +150,7 @@ nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& // No parameters or body was supplied, so don't install method. if (!uncompiledMethod) { // Early return after which we consider ourselves compiled. - mJSMethodObject = nullptr; + SetCompiledMethod(nullptr); return NS_OK; } @@ -159,7 +160,7 @@ nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& delete uncompiledMethod; // Early return after which we consider ourselves compiled. - mJSMethodObject = nullptr; + SetCompiledMethod(nullptr); return NS_OK; } @@ -219,7 +220,7 @@ nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& return rv; } - mJSMethodObject = methodObject; + SetCompiledMethod(methodObject); return NS_OK; } @@ -227,8 +228,8 @@ nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& void nsXBLProtoImplMethod::Trace(const TraceCallbacks& aCallbacks, void *aClosure) { - if (IsCompiled() && mJSMethodObject) { - aCallbacks.Trace(&mJSMethodObject, "mJSMethodObject", aClosure); + if (IsCompiled() && GetCompiledMethod()) { + aCallbacks.Trace(&mMethod.AsHeapObject(), "mMethod", aClosure); } } @@ -243,11 +244,7 @@ nsXBLProtoImplMethod::Read(nsIScriptContext* aContext, return rv; } - mJSMethodObject = methodObject; - -#ifdef DEBUG - mIsCompiled = true; -#endif + SetCompiledMethod(methodObject); return NS_OK; } @@ -256,15 +253,15 @@ nsresult nsXBLProtoImplMethod::Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream) { - if (mJSMethodObject) { + MOZ_ASSERT(IsCompiled()); + if (GetCompiledMethod()) { nsresult rv = aStream->Write8(XBLBinding_Serialize_Method); NS_ENSURE_SUCCESS(rv, rv); rv = aStream->WriteWStringZ(mName); NS_ENSURE_SUCCESS(rv, rv); - return XBL_SerializeFunction(aContext, aStream, - JS::Handle::fromMarkedLocation(&mJSMethodObject)); + return XBL_SerializeFunction(aContext, aStream, mMethod.AsHeapObject()); } return NS_OK; @@ -275,7 +272,7 @@ nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement) { NS_PRECONDITION(IsCompiled(), "Can't execute uncompiled method"); - if (!mJSMethodObject) { + if (!GetCompiledMethod()) { // Nothing to do here return NS_OK; } @@ -327,7 +324,7 @@ nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement) // Clone the function object, using thisObject as the parent so "this" is in // the scope chain of the resulting function (for backwards compat to the // days when this was an event handler). - JS::Rooted method(cx, ::JS_CloneFunctionObject(cx, mJSMethodObject, thisObject)); + JS::Rooted method(cx, ::JS_CloneFunctionObject(cx, GetCompiledMethod(), thisObject)); if (!method) return NS_ERROR_OUT_OF_MEMORY; @@ -364,12 +361,12 @@ nsXBLProtoImplAnonymousMethod::Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream, XBLBindingSerializeDetails aType) { - if (mJSMethodObject) { + MOZ_ASSERT(IsCompiled()); + if (GetCompiledMethod()) { nsresult rv = aStream->Write8(aType); NS_ENSURE_SUCCESS(rv, rv); - rv = XBL_SerializeFunction(aContext, aStream, - JS::Handle::fromMarkedLocation(&mJSMethodObject)); + rv = XBL_SerializeFunction(aContext, aStream, mMethod.AsHeapObject()); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/content/xbl/src/nsXBLProtoImplMethod.h b/content/xbl/src/nsXBLProtoImplMethod.h index d2cde87bb64..5a339c5cc83 100644 --- a/content/xbl/src/nsXBLProtoImplMethod.h +++ b/content/xbl/src/nsXBLProtoImplMethod.h @@ -11,6 +11,7 @@ #include "nsString.h" #include "jsapi.h" #include "nsString.h" +#include "nsXBLMaybeCompiled.h" #include "nsXBLProtoImplMember.h" #include "nsXBLSerialize.h" @@ -102,29 +103,31 @@ public: bool IsCompiled() const { - return !(mUncompiledMethod & BIT_UNCOMPILED); + return mMethod.IsCompiled(); } + void SetUncompiledMethod(nsXBLUncompiledMethod* aUncompiledMethod) { - mUncompiledMethod = uintptr_t(aUncompiledMethod) | BIT_UNCOMPILED; + mMethod.SetUncompiled(aUncompiledMethod); } + nsXBLUncompiledMethod* GetUncompiledMethod() const { - uintptr_t unmasked = mUncompiledMethod & ~BIT_UNCOMPILED; - return reinterpret_cast(unmasked); + return mMethod.GetUncompiled(); } protected: - enum { BIT_UNCOMPILED = 1 << 0 }; + void SetCompiledMethod(JSObject* aCompiledMethod) + { + mMethod.SetJSFunction(aCompiledMethod); + } - union { - uintptr_t mUncompiledMethod; // An object that represents the method before being compiled. - JSObject* mJSMethodObject; // The JS object for the method (after compilation) - }; + JSObject* GetCompiledMethod() const + { + return mMethod.GetJSFunction(); + } -#ifdef DEBUG - bool mIsCompiled; -#endif + JS::Heap > mMethod; }; class nsXBLProtoImplAnonymousMethod : public nsXBLProtoImplMethod { diff --git a/content/xbl/src/nsXBLProtoImplProperty.cpp b/content/xbl/src/nsXBLProtoImplProperty.cpp index 422ad9d4c20..b1eafbb0f5a 100644 --- a/content/xbl/src/nsXBLProtoImplProperty.cpp +++ b/content/xbl/src/nsXBLProtoImplProperty.cpp @@ -27,8 +27,6 @@ nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName, const PRUnichar* aReadOnly, uint32_t aLineNumber) : nsXBLProtoImplMember(aName), - mGetterText(nullptr), - mSetterText(nullptr), mJSAttributes(JSPROP_ENUMERATE) #ifdef DEBUG , mIsCompiled(false) @@ -55,8 +53,6 @@ nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName, nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName, const bool aIsReadOnly) : nsXBLProtoImplMember(aName), - mGetterText(nullptr), - mSetterText(nullptr), mJSAttributes(JSPROP_ENUMERATE) #ifdef DEBUG , mIsCompiled(false) @@ -72,12 +68,20 @@ nsXBLProtoImplProperty::~nsXBLProtoImplProperty() { MOZ_COUNT_DTOR(nsXBLProtoImplProperty); - if (!(mJSAttributes & JSPROP_GETTER)) { - delete mGetterText; + if (!mGetter.IsCompiled()) { + delete mGetter.GetUncompiled(); } - if (!(mJSAttributes & JSPROP_SETTER)) { - delete mSetterText; + if (!mSetter.IsCompiled()) { + delete mSetter.GetUncompiled(); + } +} + +void nsXBLProtoImplProperty::EnsureUncompiledText(PropertyOp& aPropertyOp) +{ + if (!aPropertyOp.GetUncompiled()) { + nsXBLTextWithLineNumber* text = new nsXBLTextWithLineNumber(); + aPropertyOp.SetUncompiled(text); } } @@ -86,13 +90,8 @@ nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText) { NS_PRECONDITION(!mIsCompiled, "Must not be compiled when accessing getter text"); - if (!mGetterText) { - mGetterText = new nsXBLTextWithLineNumber(); - if (!mGetterText) - return; - } - - mGetterText->AppendText(aText); + EnsureUncompiledText(mGetter); + mGetter.GetUncompiled()->AppendText(aText); } void @@ -100,13 +99,8 @@ nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText) { NS_PRECONDITION(!mIsCompiled, "Must not be compiled when accessing setter text"); - if (!mSetterText) { - mSetterText = new nsXBLTextWithLineNumber(); - if (!mSetterText) - return; - } - - mSetterText->AppendText(aText); + EnsureUncompiledText(mSetter); + mSetter.GetUncompiled()->AppendText(aText); } void @@ -114,13 +108,8 @@ nsXBLProtoImplProperty::SetGetterLineNumber(uint32_t aLineNumber) { NS_PRECONDITION(!mIsCompiled, "Must not be compiled when accessing getter text"); - if (!mGetterText) { - mGetterText = new nsXBLTextWithLineNumber(); - if (!mGetterText) - return; - } - - mGetterText->SetLineNumber(aLineNumber); + EnsureUncompiledText(mGetter); + mGetter.GetUncompiled()->SetLineNumber(aLineNumber); } void @@ -128,13 +117,8 @@ nsXBLProtoImplProperty::SetSetterLineNumber(uint32_t aLineNumber) { NS_PRECONDITION(!mIsCompiled, "Must not be compiled when accessing setter text"); - if (!mSetterText) { - mSetterText = new nsXBLTextWithLineNumber(); - if (!mSetterText) - return; - } - - mSetterText->SetLineNumber(aLineNumber); + EnsureUncompiledText(mSetter); + mSetter.GetUncompiled()->SetLineNumber(aLineNumber); } const char* gPropertyArgs[] = { "val" }; @@ -145,25 +129,26 @@ nsXBLProtoImplProperty::InstallMember(JSContext *aCx, { NS_PRECONDITION(mIsCompiled, "Should not be installing an uncompiled property"); + MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled()); MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx)); JS::Rooted globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject)); JS::Rooted scopeObject(aCx, xpc::GetXBLScope(aCx, globalObject)); NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); // now we want to reevaluate our property using aContext and the script object for this window... - if (mJSGetterObject || mJSSetterObject) { + if (mGetter.GetJSFunction() || mSetter.GetJSFunction()) { // First, enter the compartment of the scope object and clone the functions. JSAutoCompartment ac(aCx, scopeObject); JS::Rooted getter(aCx, nullptr); - if (mJSGetterObject) { - if (!(getter = ::JS_CloneFunctionObject(aCx, mJSGetterObject, scopeObject))) + if (mGetter.GetJSFunction()) { + if (!(getter = ::JS_CloneFunctionObject(aCx, mGetter.GetJSFunction(), scopeObject))) return NS_ERROR_OUT_OF_MEMORY; } JS::Rooted setter(aCx, nullptr); - if (mJSSetterObject) { - if (!(setter = ::JS_CloneFunctionObject(aCx, mJSSetterObject, scopeObject))) + if (mSetter.GetJSFunction()) { + if (!(setter = ::JS_CloneFunctionObject(aCx, mSetter.GetJSFunction(), scopeObject))) return NS_ERROR_OUT_OF_MEMORY; } @@ -192,6 +177,7 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin "Trying to compile an already-compiled property"); NS_PRECONDITION(aClassObject, "Must have class object to compile"); + MOZ_ASSERT(!mGetter.IsCompiled() && !mSetter.IsCompiled()); if (!mName) return NS_ERROR_FAILURE; // Without a valid name, we can't install the member. @@ -200,7 +186,7 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin nsresult rv = NS_OK; nsAutoCString functionUri; - if (mGetterText || mSetterText) { + if (mGetter.GetUncompiled() || mSetter.GetUncompiled()) { functionUri = aClassStr; int32_t hash = functionUri.RFindChar('#'); if (hash != kNotFound) { @@ -209,13 +195,14 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin } bool deletedGetter = false; - if (mGetterText && mGetterText->GetText()) { - nsDependentString getter(mGetterText->GetText()); + nsXBLTextWithLineNumber *getterText = mGetter.GetUncompiled(); + if (getterText && getterText->GetText()) { + nsDependentString getter(getterText->GetText()); if (!getter.IsEmpty()) { AutoPushJSContext cx(aContext->GetNativeContext()); JSAutoCompartment ac(cx, aClassObject); JS::CompileOptions options(cx); - options.setFileAndLine(functionUri.get(), mGetterText->GetLineNumber()) + options.setFileAndLine(functionUri.get(), getterText->GetLineNumber()) .setVersion(JSVERSION_LATEST); nsCString name = NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName); JS::RootedObject rootedNull(cx, nullptr); // See bug 781070. @@ -223,17 +210,16 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin rv = nsJSUtils::CompileFunction(cx, rootedNull, options, name, 0, nullptr, getter, getterObject.address()); - // Make sure we free mGetterText here before setting mJSGetterObject, since - // that'll overwrite mGetterText - delete mGetterText; + delete getterText; deletedGetter = true; - mJSGetterObject = getterObject; + + mGetter.SetJSFunction(getterObject); - if (mJSGetterObject && NS_SUCCEEDED(rv)) { + if (mGetter.GetJSFunction() && NS_SUCCEEDED(rv)) { mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED; } if (NS_FAILED(rv)) { - mJSGetterObject = nullptr; + mGetter.SetJSFunction(nullptr); mJSAttributes &= ~JSPROP_GETTER; /*chaining to return failure*/ } @@ -241,8 +227,8 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin } // if getter is not empty if (!deletedGetter) { // Empty getter - delete mGetterText; - mJSGetterObject = nullptr; + delete getterText; + mGetter.SetJSFunction(nullptr); } if (NS_FAILED(rv)) { @@ -256,13 +242,14 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin } bool deletedSetter = false; - if (mSetterText && mSetterText->GetText()) { - nsDependentString setter(mSetterText->GetText()); + nsXBLTextWithLineNumber *setterText = mSetter.GetUncompiled(); + if (setterText && setterText->GetText()) { + nsDependentString setter(setterText->GetText()); if (!setter.IsEmpty()) { AutoPushJSContext cx(aContext->GetNativeContext()); JSAutoCompartment ac(cx, aClassObject); JS::CompileOptions options(cx); - options.setFileAndLine(functionUri.get(), mSetterText->GetLineNumber()) + options.setFileAndLine(functionUri.get(), setterText->GetLineNumber()) .setVersion(JSVERSION_LATEST); nsCString name = NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName); JS::RootedObject rootedNull(cx, nullptr); // See bug 781070. @@ -270,17 +257,15 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin rv = nsJSUtils::CompileFunction(cx, rootedNull, options, name, 1, gPropertyArgs, setter, setterObject.address()); - // Make sure we free mSetterText here before setting mJSGetterObject, since - // that'll overwrite mSetterText - delete mSetterText; + delete setterText; deletedSetter = true; - mJSSetterObject = setterObject; + mSetter.SetJSFunction(setterObject); - if (mJSSetterObject && NS_SUCCEEDED(rv)) { + if (mSetter.GetJSFunction() && NS_SUCCEEDED(rv)) { mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED; } if (NS_FAILED(rv)) { - mJSSetterObject = nullptr; + mSetter.SetJSFunction(nullptr); mJSAttributes &= ~JSPROP_SETTER; /*chaining to return failure*/ } @@ -288,8 +273,8 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin } // if setter wasn't empty.... if (!deletedSetter) { // Empty setter - delete mSetterText; - mJSSetterObject = nullptr; + delete setterText; + mSetter.SetJSFunction(nullptr); } #ifdef DEBUG @@ -303,11 +288,11 @@ void nsXBLProtoImplProperty::Trace(const TraceCallbacks& aCallbacks, void *aClosure) { if (mJSAttributes & JSPROP_GETTER) { - aCallbacks.Trace(&mJSGetterObject, "mJSGetterObject", aClosure); + aCallbacks.Trace(&mGetter.AsHeapObject(), "mGetter", aClosure); } if (mJSAttributes & JSPROP_SETTER) { - aCallbacks.Trace(&mJSSetterObject, "mJSSetterObject", aClosure); + aCallbacks.Trace(&mSetter.AsHeapObject(), "mSetter", aClosure); } } @@ -316,27 +301,30 @@ nsXBLProtoImplProperty::Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream, XBLBindingSerializeDetails aType) { + MOZ_ASSERT(!mIsCompiled); + MOZ_ASSERT(!mGetter.GetUncompiled() && !mSetter.GetUncompiled()); + JSContext *cx = aContext->GetNativeContext(); + JS::Rooted getterObject(cx); if (aType == XBLBinding_Serialize_GetterProperty || aType == XBLBinding_Serialize_GetterSetterProperty) { - JS::Rooted getterObject(cx); nsresult rv = XBL_DeserializeFunction(aContext, aStream, &getterObject); NS_ENSURE_SUCCESS(rv, rv); - mJSGetterObject = getterObject; mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED; } - + mGetter.SetJSFunction(getterObject); + + JS::Rooted setterObject(cx); if (aType == XBLBinding_Serialize_SetterProperty || aType == XBLBinding_Serialize_GetterSetterProperty) { - JS::Rooted setterObject(cx); nsresult rv = XBL_DeserializeFunction(aContext, aStream, &setterObject); NS_ENSURE_SUCCESS(rv, rv); - mJSSetterObject = setterObject; mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED; } + mSetter.SetJSFunction(setterObject); #ifdef DEBUG mIsCompiled = true; @@ -370,14 +358,12 @@ nsXBLProtoImplProperty::Write(nsIScriptContext* aContext, NS_ENSURE_SUCCESS(rv, rv); if (mJSAttributes & JSPROP_GETTER) { - rv = XBL_SerializeFunction(aContext, aStream, - JS::Handle::fromMarkedLocation(&mJSGetterObject)); + rv = XBL_SerializeFunction(aContext, aStream, mGetter.AsHeapObject()); NS_ENSURE_SUCCESS(rv, rv); } if (mJSAttributes & JSPROP_SETTER) { - rv = XBL_SerializeFunction(aContext, aStream, - JS::Handle::fromMarkedLocation(&mJSSetterObject)); + rv = XBL_SerializeFunction(aContext, aStream, mSetter.AsHeapObject()); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/content/xbl/src/nsXBLProtoImplProperty.h b/content/xbl/src/nsXBLProtoImplProperty.h index 7d6ec03d961..144eb47367e 100644 --- a/content/xbl/src/nsXBLProtoImplProperty.h +++ b/content/xbl/src/nsXBLProtoImplProperty.h @@ -12,6 +12,7 @@ #include "jsapi.h" #include "nsString.h" #include "nsXBLSerialize.h" +#include "nsXBLMaybeCompiled.h" #include "nsXBLProtoImplMember.h" class nsXBLProtoImplProperty: public nsXBLProtoImplMember @@ -48,21 +49,17 @@ public: nsIObjectOutputStream* aStream) MOZ_OVERRIDE; protected: - union { - // The raw text for the getter (prior to compilation). - nsXBLTextWithLineNumber* mGetterText; - // The JS object for the getter (after compilation) - JSObject * mJSGetterObject; - }; + typedef JS::Heap > PropertyOp; - union { - // The raw text for the setter (prior to compilation). - nsXBLTextWithLineNumber* mSetterText; - // The JS object for the setter (after compilation) - JSObject * mJSSetterObject; - }; + void EnsureUncompiledText(PropertyOp& aPropertyOp); + + // The raw text for the getter, or the JS object (after compilation). + PropertyOp mGetter; + + // The raw text for the setter, or the JS object (after compilation). + PropertyOp mSetter; - unsigned mJSAttributes; // A flag for all our JS properties (getter/setter/readonly/shared/enum) + unsigned mJSAttributes; // A flag for all our JS properties (getter/setter/readonly/shared/enum) #ifdef DEBUG bool mIsCompiled;