diff --git a/content/xbl/src/nsXBLProtoImpl.cpp b/content/xbl/src/nsXBLProtoImpl.cpp index f3c7402c79de..3b34673cdb23 100644 --- a/content/xbl/src/nsXBLProtoImpl.cpp +++ b/content/xbl/src/nsXBLProtoImpl.cpp @@ -187,6 +187,12 @@ nsXBLProtoImpl::DestroyMembers(nsXBLProtoImplMember* aBrokenMember) } curr->Destroy(compiled); } + + // Now clear out mMembers so we don't try to call Destroy() on them again + delete mMembers; + mMembers = nsnull; + mConstructor = nsnull; + mDestructor = nsnull; } nsresult diff --git a/content/xbl/src/nsXBLProtoImpl.h b/content/xbl/src/nsXBLProtoImpl.h index bb324ba871ec..10998ba342a9 100644 --- a/content/xbl/src/nsXBLProtoImpl.h +++ b/content/xbl/src/nsXBLProtoImpl.h @@ -84,11 +84,14 @@ protected: public: nsCString mClassName; // The name of the class. + +protected: void* mClassObject; // The class object for the binding. We'll use this to pre-compile properties // and methods for the binding. nsXBLProtoImplMember* mMembers; // The members of an implementation are chained in this singly-linked list. +public: nsXBLProtoImplAnonymousMethod* mConstructor; // Our class constructor. nsXBLProtoImplAnonymousMethod* mDestructor; // Our class destructor. }; diff --git a/content/xbl/src/nsXBLProtoImplMethod.cpp b/content/xbl/src/nsXBLProtoImplMethod.cpp index cefb3b370822..28338cf612ea 100644 --- a/content/xbl/src/nsXBLProtoImplMethod.cpp +++ b/content/xbl/src/nsXBLProtoImplMethod.cpp @@ -55,6 +55,9 @@ MOZ_DECL_CTOR_COUNTER(nsXBLProtoImplMethod) nsXBLProtoImplMethod::nsXBLProtoImplMethod(const PRUnichar* aName) : nsXBLProtoImplMember(aName), mUncompiledMethod(nsnull) +#ifdef DEBUG + , mIsCompiled(PR_FALSE) +#endif { MOZ_COUNT_CTOR(nsXBLProtoImplMethod); } @@ -67,6 +70,8 @@ nsXBLProtoImplMethod::~nsXBLProtoImplMethod() void nsXBLProtoImplMethod::Destroy(PRBool aIsCompiled) { + NS_PRECONDITION(aIsCompiled == mIsCompiled, + "Incorrect aIsCompiled in nsXBLProtoImplMethod::Destroy"); if (aIsCompiled) { if (mJSMethodObject) RemoveJSGCRoot(&mJSMethodObject); @@ -81,6 +86,8 @@ nsXBLProtoImplMethod::Destroy(PRBool aIsCompiled) void nsXBLProtoImplMethod::AppendBodyText(const nsAString& aText) { + NS_PRECONDITION(!mIsCompiled, + "Must not be compiled when accessing uncompiled method"); if (!mUncompiledMethod) { mUncompiledMethod = new nsXBLUncompiledMethod(); if (!mUncompiledMethod) @@ -93,6 +100,8 @@ nsXBLProtoImplMethod::AppendBodyText(const nsAString& aText) void nsXBLProtoImplMethod::AddParameter(const nsAString& aText) { + NS_PRECONDITION(!mIsCompiled, + "Must not be compiled when accessing uncompiled method"); if (!mUncompiledMethod) { mUncompiledMethod = new nsXBLUncompiledMethod(); if (!mUncompiledMethod) @@ -105,6 +114,8 @@ nsXBLProtoImplMethod::AddParameter(const nsAString& aText) void nsXBLProtoImplMethod::SetLineNumber(PRUint32 aLineNumber) { + NS_PRECONDITION(!mIsCompiled, + "Must not be compiled when accessing uncompiled method"); if (!mUncompiledMethod) { mUncompiledMethod = new nsXBLUncompiledMethod(); if (!mUncompiledMethod) @@ -121,6 +132,8 @@ nsXBLProtoImplMethod::InstallMember(nsIScriptContext* aContext, void* aTargetClassObject, const nsCString& aClassStr) { + NS_PRECONDITION(mIsCompiled, + "Should not be installing an uncompiled method"); JSContext* cx = (JSContext*) aContext->GetNativeContext(); JSObject * scriptObject = (JSObject *) aScriptObject; NS_ASSERTION(scriptObject, "uh-oh, script Object should NOT be null or bad things will happen"); @@ -147,9 +160,16 @@ nsresult nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr, void* aClassObject) { - if (!aClassObject) - return NS_OK; // Nothing to do. + NS_PRECONDITION(!mIsCompiled, + "Trying to compile an already-compiled method"); + NS_PRECONDITION(aClassObject, + "Must have class object to compile"); +#ifdef DEBUG + // We have some "ok" early returns after which we consider ourselves compiled + mIsCompiled = PR_TRUE; +#endif + // No parameters or body was supplied, so don't install method. if (!mUncompiledMethod) return NS_OK; @@ -168,6 +188,12 @@ nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& return NS_OK; } +#ifdef DEBUG + // OK, now we have some error early returns that mean we're not + // really compiled... + mIsCompiled = PR_FALSE; +#endif + // We have a method. // Allocate an array for our arguments. PRInt32 paramCount = mUncompiledMethod->GetParameterCount(); @@ -221,16 +247,25 @@ nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& // Root the compiled prototype script object. JSContext* cx = NS_REINTERPRET_CAST(JSContext*, aContext->GetNativeContext()); - if (!cx) return NS_ERROR_UNEXPECTED; - AddJSGCRoot(&mJSMethodObject, "nsXBLProtoImplMethod::mJSMethodObject"); + rv = cx ? + AddJSGCRoot(&mJSMethodObject, "nsXBLProtoImplMethod::mJSMethodObject") : + NS_ERROR_UNEXPECTED; + if (NS_FAILED(rv)) { + mJSMethodObject = nsnull; + } } - return NS_OK; +#ifdef DEBUG + mIsCompiled = NS_SUCCEEDED(rv); +#endif + return rv; } nsresult nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement) { + NS_PRECONDITION(mIsCompiled, "Can't execute uncompiled method"); + if (!mJSMethodObject) { // Nothing to do here return NS_OK; diff --git a/content/xbl/src/nsXBLProtoImplMethod.h b/content/xbl/src/nsXBLProtoImplMethod.h index 2e5a03f3eeab..c689302977f2 100644 --- a/content/xbl/src/nsXBLProtoImplMethod.h +++ b/content/xbl/src/nsXBLProtoImplMethod.h @@ -138,6 +138,10 @@ protected: nsXBLUncompiledMethod* mUncompiledMethod; // An object that represents the method before being compiled. JSObject * mJSMethodObject; // The JS object for the method (after compilation) }; + +#ifdef DEBUG + PRBool mIsCompiled; +#endif }; class nsXBLProtoImplAnonymousMethod : public nsXBLProtoImplMethod { diff --git a/content/xbl/src/nsXBLProtoImplProperty.cpp b/content/xbl/src/nsXBLProtoImplProperty.cpp index 87c9916f32db..50adfe410fcc 100644 --- a/content/xbl/src/nsXBLProtoImplProperty.cpp +++ b/content/xbl/src/nsXBLProtoImplProperty.cpp @@ -56,6 +56,9 @@ nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName, mGetterText(nsnull), mSetterText(nsnull), mJSAttributes(JSPROP_ENUMERATE) +#ifdef DEBUG + , mIsCompiled(PR_FALSE) +#endif { MOZ_COUNT_CTOR(nsXBLProtoImplProperty); @@ -79,6 +82,8 @@ nsXBLProtoImplProperty::~nsXBLProtoImplProperty() void nsXBLProtoImplProperty::Destroy(PRBool aIsCompiled) { + NS_PRECONDITION(aIsCompiled == mIsCompiled, + "Incorrect aIsCompiled in nsXBLProtoImplProperty::Destroy"); if (aIsCompiled) { if (mJSGetterObject) RemoveJSGCRoot(&mJSGetterObject); @@ -96,6 +101,8 @@ nsXBLProtoImplProperty::Destroy(PRBool aIsCompiled) void nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText) { + NS_PRECONDITION(!mIsCompiled, + "Must not be compiled when accessing getter text"); if (!mGetterText) { mGetterText = new nsXBLTextWithLineNumber(); if (!mGetterText) @@ -108,6 +115,8 @@ nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText) void nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText) { + NS_PRECONDITION(!mIsCompiled, + "Must not be compiled when accessing setter text"); if (!mSetterText) { mSetterText = new nsXBLTextWithLineNumber(); if (!mSetterText) @@ -118,7 +127,10 @@ nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText) } void -nsXBLProtoImplProperty::SetGetterLineNumber(PRUint32 aLineNumber) { +nsXBLProtoImplProperty::SetGetterLineNumber(PRUint32 aLineNumber) +{ + NS_PRECONDITION(!mIsCompiled, + "Must not be compiled when accessing getter text"); if (!mGetterText) { mGetterText = new nsXBLTextWithLineNumber(); if (!mGetterText) @@ -129,7 +141,10 @@ nsXBLProtoImplProperty::SetGetterLineNumber(PRUint32 aLineNumber) { } void -nsXBLProtoImplProperty::SetSetterLineNumber(PRUint32 aLineNumber) { +nsXBLProtoImplProperty::SetSetterLineNumber(PRUint32 aLineNumber) +{ + NS_PRECONDITION(!mIsCompiled, + "Must not be compiled when accessing setter text"); if (!mSetterText) { mSetterText = new nsXBLTextWithLineNumber(); if (!mSetterText) @@ -148,6 +163,8 @@ nsXBLProtoImplProperty::InstallMember(nsIScriptContext* aContext, void* aTargetClassObject, const nsCString& aClassStr) { + NS_PRECONDITION(mIsCompiled, + "Should not be installing an uncompiled property"); JSContext* cx = (JSContext*) aContext->GetNativeContext(); JSObject * scriptObject = (JSObject *) aScriptObject; NS_ASSERTION(scriptObject, "uh-oh, script Object should NOT be null or bad things will happen"); @@ -183,8 +200,10 @@ nsresult nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr, void* aClassObject) { - if (!aClassObject) - return NS_OK; // Nothing to do. + NS_PRECONDITION(!mIsCompiled, + "Trying to compile an already-compiled property"); + NS_PRECONDITION(aClassObject, + "Must have class object to compile"); if (!mName) return NS_ERROR_FAILURE; // Without a valid name, we can't install the member. @@ -246,7 +265,15 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin mJSGetterObject = nsnull; } - nsresult rvG=rv; + if (NS_FAILED(rv)) { + // We failed to compile our getter. So either we've set it to null, or + // it's still set to the text object. In either case, it's safe to return + // the error here, since then we'll be cleaned up as uncompiled and that + // will be ok. Going on and compiling the setter and _then_ returning an + // error, on the other hand, will try to clean up a compiled setter as + // uncompiled and crash. + return rv; + } PRBool deletedSetter = PR_FALSE; if (mSetterText) { @@ -292,6 +319,10 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin delete mSetterText; mJSSetterObject = nsnull; } + +#ifdef DEBUG + mIsCompiled = NS_SUCCEEDED(rv); +#endif - return NS_SUCCEEDED(rv) ? rvG : rv; + return rv; } diff --git a/content/xbl/src/nsXBLProtoImplProperty.h b/content/xbl/src/nsXBLProtoImplProperty.h index b6e4fa6e59d8..0b991335e60a 100644 --- a/content/xbl/src/nsXBLProtoImplProperty.h +++ b/content/xbl/src/nsXBLProtoImplProperty.h @@ -88,6 +88,10 @@ protected: }; uintN mJSAttributes; // A flag for all our JS properties (getter/setter/readonly/shared/enum) + +#ifdef DEBUG + PRBool mIsCompiled; +#endif }; #endif // nsXBLProtoImplProperty_h__