Stretch and fold again, rebasing up prior to the automatic patches. Remove the pesky NS_OKONSTACK crud, and feel the love.
authorBenjamin Smedberg <benjamin@smedbergs.us>
Thu, 17 Jul 2008 16:29:37 -0400
changeset 159 969d8d4050b8
parent 158 3bc262b4ce40
child 160 fd41ae75c798
push id35
push userbsmedberg@mozilla.com
push dateThu, 17 Jul 2008 20:29:51 +0000
Stretch and fold again, rebasing up prior to the automatic patches. Remove the pesky NS_OKONSTACK crud, and feel the love.
JSString-okonstack
LazyMessage-okonstack
SVGVals-allowonstack
SVGVals-okonstack
TestPageLoad-namespace
WalkState-okonstack
XPCNativeScriptableShared-okonstack
gc-hashtables.patch
mozStorageTransaction-allowonstack
mozStorageTransaction-okonstack
nsAssignment-okonstack
nsCanvasRenderingContext2D_ContextState-allowonstack
nsCanvasRenderingContext2D_ContextState-okonstack
nsGUIEvents-allowonstack
nsGUIEvents-okonstack
nsMutationEvent-allowonstack
nsMutationEvent-okonstack
nsRangeStore-allowonstack
nsRangeStore-okonstack
nsSortState-allowonstack
nsSortState-okonstack
nsTimeout-GCObject
nsTimeout-GCObject-okonstack
nsURIHashKey-okonstack
nsZipQueueItem-allowonstack
nsZipQueueItem-okonstack
proxy-create-namespace
series
static-check-gc-attributes
static-check-storage-classes
static-check-storage-classes2
deleted file mode 100644
--- a/JSString-okonstack
+++ /dev/null
@@ -1,22 +0,0 @@
-diff --git a/js/src/jsstr.h b/js/src/jsstr.h
---- a/js/src/jsstr.h
-+++ b/js/src/jsstr.h
-@@ -55,11 +55,17 @@
- 
- JS_BEGIN_EXTERN_C
- 
-+#ifdef NS_STATIC_CHECKING
-+#define JS_OKONSTACK __attribute__((user("NS_okOnStack")))
-+#else
-+#define JS_OKONSTACK
-+#endif
-+
- /*
-  * Base class for strings. Native strings (including growable strings and
-  * dependent strings) and external strings both derive from this class.
-  */
--struct JSString : public JSFinalizedGCThing {
-+struct JS_OKONSTACK JSString : public JSFinalizedGCThing {
-     /*
-      * Bag of bits used to store the length, among other things. The exact
-      * meaning of this field depends on whether the string is dependent.
deleted file mode 100644
--- a/LazyMessage-okonstack
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/toolkit/components/places/src/nsNavHistory.h b/toolkit/components/places/src/nsNavHistory.h
---- a/toolkit/components/places/src/nsNavHistory.h
-+++ b/toolkit/components/places/src/nsNavHistory.h
-@@ -529,7 +529,8 @@ protected:
- 
- #ifdef LAZY_ADD
-   // lazy add committing
--  struct LazyMessage {
-+  // XXXbsmedberg: more TArray types... ugh
-+  struct NS_OKONSTACK LazyMessage {
-     enum MessageType { Type_Invalid, Type_AddURI, Type_Title, Type_Favicon };
-     LazyMessage()
-     {
new file mode 100644
--- /dev/null
+++ b/SVGVals-allowonstack
@@ -0,0 +1,36 @@
+diff --git a/content/svg/content/src/nsSVGLength2.h b/content/svg/content/src/nsSVGLength2.h
+--- a/content/svg/content/src/nsSVGLength2.h
++++ b/content/svg/content/src/nsSVGLength2.h
+@@ -116,8 +116,8 @@ private:
+     DOMBaseVal(nsSVGLength2* aVal, nsSVGElement *aSVGElement)
+       : mVal(aVal), mSVGElement(aSVGElement) {}
+     
+-    nsSVGLength2* mVal; // kept alive because it belongs to mSVGElement
+-    nsRefPtr<nsSVGElement> mSVGElement;
++    nsSVGLength2 *const mVal; // kept alive because it belongs to mSVGElement
++    nsSVGElement *const mSVGElement;
+     
+     NS_IMETHOD GetUnitType(PRUint16* aResult)
+       { *aResult = mVal->mSpecifiedUnitType; return NS_OK; }
+@@ -155,8 +155,8 @@ private:
+     DOMAnimVal(nsSVGLength2* aVal, nsSVGElement *aSVGElement)
+       : mVal(aVal), mSVGElement(aSVGElement) {}
+     
+-    nsSVGLength2* mVal; // kept alive because it belongs to mSVGElement
+-    nsRefPtr<nsSVGElement> mSVGElement;
++    nsSVGLength2 *const mVal; // kept alive because it belongs to mSVGElement
++    nsSVGElement *const mSVGElement;
+     
+     NS_IMETHOD GetUnitType(PRUint16* aResult)
+       { *aResult = mVal->mSpecifiedUnitType; return NS_OK; }
+@@ -191,8 +191,8 @@ private:
+     DOMAnimatedLength(nsSVGLength2* aVal, nsSVGElement *aSVGElement)
+       : mVal(aVal), mSVGElement(aSVGElement) {}
+     
+-    nsSVGLength2* mVal; // kept alive because it belongs to content
+-    nsRefPtr<nsSVGElement> mSVGElement;
++    nsSVGLength2 *const mVal; // kept alive because it belongs to content
++    nsSVGElement *const mSVGElement;
+ 
+     NS_IMETHOD GetBaseVal(nsIDOMSVGLength **aBaseVal)
+       { return mVal->ToDOMBaseVal(aBaseVal, mSVGElement); }
deleted file mode 100644
--- a/SVGVals-okonstack
+++ /dev/null
@@ -1,66 +0,0 @@
-diff --git a/content/svg/content/src/nsSVGLength2.h b/content/svg/content/src/nsSVGLength2.h
---- a/content/svg/content/src/nsSVGLength2.h
-+++ b/content/svg/content/src/nsSVGLength2.h
-@@ -109,15 +109,17 @@ private:
-   nsresult ToDOMBaseVal(nsIDOMSVGLength **aResult, nsSVGElement* aSVGElement);
-   nsresult ToDOMAnimVal(nsIDOMSVGLength **aResult, nsSVGElement* aSVGElement);
- 
--  struct DOMBaseVal : public nsIDOMSVGLength
-+  struct
-+    NS_OKONSTACK
-+  DOMBaseVal : public XPCOMGCFinalizedObject, public nsIDOMSVGLength
-   {
-     NS_DECL_ISUPPORTS
- 
-     DOMBaseVal(nsSVGLength2* aVal, nsSVGElement *aSVGElement)
-       : mVal(aVal), mSVGElement(aSVGElement) {}
-     
--    nsSVGLength2* mVal; // kept alive because it belongs to mSVGElement
--    nsRefPtr<nsSVGElement> mSVGElement;
-+    nsSVGLength2 *const mVal; // kept alive because it belongs to mSVGElement
-+    nsSVGElement *const mSVGElement;
-     
-     NS_IMETHOD GetUnitType(PRUint16* aResult)
-       { *aResult = mVal->mSpecifiedUnitType; return NS_OK; }
-@@ -148,15 +150,17 @@ private:
-       { mVal->ConvertToSpecifiedUnits(unitType, mSVGElement); return NS_OK; }
-   };
- 
--  struct DOMAnimVal : public nsIDOMSVGLength
-+  struct
-+    NS_OKONSTACK
-+  DOMAnimVal : public XPCOMGCFinalizedObject, public nsIDOMSVGLength
-   {
-     NS_DECL_ISUPPORTS
- 
-     DOMAnimVal(nsSVGLength2* aVal, nsSVGElement *aSVGElement)
-       : mVal(aVal), mSVGElement(aSVGElement) {}
-     
--    nsSVGLength2* mVal; // kept alive because it belongs to mSVGElement
--    nsRefPtr<nsSVGElement> mSVGElement;
-+    nsSVGLength2 *const mVal; // kept alive because it belongs to mSVGElement
-+    nsSVGElement *const mSVGElement;
-     
-     NS_IMETHOD GetUnitType(PRUint16* aResult)
-       { *aResult = mVal->mSpecifiedUnitType; return NS_OK; }
-@@ -184,15 +188,17 @@ private:
-       { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; }
-   };
- 
--  struct DOMAnimatedLength : public nsIDOMSVGAnimatedLength
-+  struct
-+    NS_OKONSTACK
-+  DOMAnimatedLength : public XPCOMGCFinalizedObject, public nsIDOMSVGAnimatedLength
-   {
-     NS_DECL_ISUPPORTS
- 
-     DOMAnimatedLength(nsSVGLength2* aVal, nsSVGElement *aSVGElement)
-       : mVal(aVal), mSVGElement(aSVGElement) {}
-     
--    nsSVGLength2* mVal; // kept alive because it belongs to content
--    nsRefPtr<nsSVGElement> mSVGElement;
-+    nsSVGLength2 *const mVal; // kept alive because it belongs to content
-+    nsSVGElement *const mSVGElement;
- 
-     NS_IMETHOD GetBaseVal(nsIDOMSVGLength **aBaseVal)
-       { return mVal->ToDOMBaseVal(aBaseVal, mSVGElement); }
--- a/TestPageLoad-namespace
+++ b/TestPageLoad-namespace
@@ -22,22 +22,20 @@ diff --git a/netwerk/test/TestPageLoad.c
  NS_IMPL_ISUPPORTS2(MyListener,
                     nsIRequestObserver,
 @@ -221,6 +227,8 @@ MyListener::OnDataAvailable(nsIRequest *
  // NotificationCallbacks implementation
  //-----------------------------------------------------------------------------
  
 +namespace TestPageLoad {
 +
- class MyNotifications : public XPCOMGCFinalizedObject
-                       , public nsIInterfaceRequestor
+ class MyNotifications : public nsIInterfaceRequestor
                        , public nsIProgressEventSink
-@@ -233,6 +241,10 @@ public:
+ {
+@@ -232,6 +240,8 @@ public:
      MyNotifications() { }
      virtual ~MyNotifications() {}
  };
 +
 +}
-+
-+using namespace TestPageLoad;
  
  NS_IMPL_THREADSAFE_ISUPPORTS2(MyNotifications,
                                nsIInterfaceRequestor,
deleted file mode 100644
--- a/WalkState-okonstack
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/accessible/src/base/nsAccessibleTreeWalker.h b/accessible/src/base/nsAccessibleTreeWalker.h
---- a/accessible/src/base/nsAccessibleTreeWalker.h
-+++ b/accessible/src/base/nsAccessibleTreeWalker.h
-@@ -51,7 +51,8 @@
- 
- enum { eSiblingsUninitialized = -1, eSiblingsWalkFrames = -2 };
- 
--struct WalkState {
-+// XXXbsmedberg: This is totally wrong, but I'm just going to punt for now!
-+struct NS_OKONSTACK WalkState {
-   nsCOMPtr<nsIAccessible> accessible;
-   nsCOMPtr<nsIDOMNode> domNode;
-   nsCOMPtr<nsIDOMNodeList> siblingList;
deleted file mode 100644
--- a/XPCNativeScriptableShared-okonstack
+++ /dev/null
@@ -1,14 +0,0 @@
-diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h
---- a/js/src/xpconnect/src/xpcprivate.h
-+++ b/js/src/xpconnect/src/xpcprivate.h
-@@ -1635,7 +1635,9 @@ public:
- 
- // This class is GC-allocated, except when we're doing a hash lookup
- // in XPCNativeScriptableSharedMap, when it's a temporary stack object
--class XPCNativeScriptableShared : public MMgc::GCFinalizable
-+class
-+  NS_OKONSTACK
-+XPCNativeScriptableShared : public MMgc::GCFinalizable
- {
- public:
-     const XPCNativeScriptableFlags& GetFlags() const {return mFlags;}
--- a/gc-hashtables.patch
+++ b/gc-hashtables.patch
@@ -1,9 +1,11 @@
 Give hashtable keys knowledge about the outermost allocation they are in... kinda. In some of these cases I just added the param without actually making the key class safe.
+* * *
+* * *
 
 diff --git a/caps/include/nsScriptSecurityManager.h b/caps/include/nsScriptSecurityManager.h
 --- a/caps/include/nsScriptSecurityManager.h
 +++ b/caps/include/nsScriptSecurityManager.h
 @@ -96,7 +96,7 @@ public:
      typedef const nsIPrincipal* KeyType;
      typedef const nsIPrincipal* KeyTypePointer;
  
@@ -300,72 +302,105 @@ diff --git a/layout/base/nsBidiPresUtils
 +  nsFrameContinuationState(const void *aFrame, const void *entryStore)
 +    : nsVoidPtrHashKey(aFrame, entryStore) {}
  
    /**
     * The first visual frame in the continuation chain containing this frame, or
 diff --git a/layout/style/nsCSSLoader.h b/layout/style/nsCSSLoader.h
 --- a/layout/style/nsCSSLoader.h
 +++ b/layout/style/nsCSSLoader.h
-@@ -225,24 +225,22 @@ public:
+@@ -225,24 +225,26 @@ public:
    typedef nsURIAndPrincipalHashKey* KeyType;
    typedef const nsURIAndPrincipalHashKey* KeyTypePointer;
  
 -  nsURIAndPrincipalHashKey(const nsURIAndPrincipalHashKey* aKey)
 -    : nsURIHashKey(aKey->mKey), mPrincipal(aKey->mPrincipal)
 +  nsURIAndPrincipalHashKey(const nsURIAndPrincipalHashKey* aKey,
 +                           const void *entryStore)
 +    : nsURIHashKey(aKey->mKey, entryStore), mPrincipal(aKey->mPrincipal)
    {
 -    MOZ_COUNT_CTOR(nsURIAndPrincipalHashKey);
++    NS_GetGC()->WriteBarrierTrap(entryStore, aKey->mPrincipal);
    }
++
++  // this constructor may only be called if the hashkey is allocated
++  // on the stack!
    nsURIAndPrincipalHashKey(nsIURI* aURI, nsIPrincipal* aPrincipal)
 -    : nsURIHashKey(aURI), mPrincipal(aPrincipal)
-+    : nsURIHashKey(aURI, this), mPrincipal(aPrincipal)
++    : nsURIHashKey(aURI, nsnull), mPrincipal(aPrincipal)
    {
 -    MOZ_COUNT_CTOR(nsURIAndPrincipalHashKey);
    }
    nsURIAndPrincipalHashKey(const nsURIAndPrincipalHashKey& toCopy)
--    : nsURIHashKey(toCopy), mPrincipal(toCopy.mPrincipal)
-+    : nsURIHashKey(toCopy)
+     : nsURIHashKey(toCopy), mPrincipal(toCopy.mPrincipal)
    {
 -    MOZ_COUNT_CTOR(nsURIAndPrincipalHashKey);
 +    NS_NOTREACHED("This code doesn't work!");
    }
    ~nsURIAndPrincipalHashKey()
    {
 -    MOZ_COUNT_DTOR(nsURIAndPrincipalHashKey);
    }
   
    nsURIAndPrincipalHashKey* GetKey() const {
+@@ -274,7 +276,7 @@ public:
+   enum { ALLOW_MEMMOVE = PR_TRUE };
+  
+ protected:
+-  nsCOMPtr<nsIPrincipal> mPrincipal;
++  nsIPrincipal *const mPrincipal;
+ };
+ 
+ /***********************************************************************
 diff --git a/layout/svg/base/src/nsSVGFilterFrame.cpp b/layout/svg/base/src/nsSVGFilterFrame.cpp
 --- a/layout/svg/base/src/nsSVGFilterFrame.cpp
 +++ b/layout/svg/base/src/nsSVGFilterFrame.cpp
 @@ -134,7 +134,8 @@ private:
  
    class ImageAnalysisEntry : public nsStringHashKey {
    public:
 -    ImageAnalysisEntry(KeyTypePointer aStr) : nsStringHashKey(aStr) { }
 +    ImageAnalysisEntry(KeyTypePointer aStr, const void *entryStore)
 +      : nsStringHashKey(aStr, entryStore) { }
      ImageAnalysisEntry(const ImageAnalysisEntry& toCopy) : nsStringHashKey(toCopy),
        mInfo(toCopy.mInfo) { }
  
 diff --git a/netwerk/base/public/nsURIHashKey.h b/netwerk/base/public/nsURIHashKey.h
 --- a/netwerk/base/public/nsURIHashKey.h
 +++ b/netwerk/base/public/nsURIHashKey.h
-@@ -52,7 +52,7 @@ public:
+@@ -52,11 +52,16 @@ public:
      typedef nsIURI* KeyType;
      typedef const nsIURI* KeyTypePointer;
  
 -    nsURIHashKey(const nsIURI* aKey) :
+-        mKey(const_cast<nsIURI*>(aKey)) { MOZ_COUNT_CTOR(nsURIHashKey); }
++    // if entryStore is null, this class was allocated on the stack
++    // see the nsURIAndPrincipalHashKey subclass
 +    nsURIHashKey(const nsIURI* aKey, const void *entryStore) :
-         mKey(const_cast<nsIURI*>(aKey)) { MOZ_COUNT_CTOR(nsURIHashKey); }
++        mKey(const_cast<nsIURI*>(aKey)) {
++        if (entryStore)
++            NS_GetGC()->WriteBarrierTrap(entryStore, aKey);
++    }
      nsURIHashKey(const nsURIHashKey& toCopy) :
-         mKey(toCopy.mKey) { MOZ_COUNT_CTOR(nsURIHashKey); }
+-        mKey(toCopy.mKey) { MOZ_COUNT_CTOR(nsURIHashKey); }
+-    ~nsURIHashKey() { MOZ_COUNT_DTOR(nsURIHashKey); }
++        mKey(toCopy.mKey) { }
++    ~nsURIHashKey() { }
+ 
+     nsIURI* GetKey() const { return mKey; }
+ 
+@@ -78,7 +83,7 @@ public:
+     enum { ALLOW_MEMMOVE = PR_TRUE };
+ 
+ protected:
+-    nsCOMPtr<nsIURI> mKey;
++    nsIURI *const mKey;
+ };
+ 
+ #endif // nsURIHashKey_h__
 diff --git a/netwerk/cookie/src/nsCookieService.h b/netwerk/cookie/src/nsCookieService.h
 --- a/netwerk/cookie/src/nsCookieService.h
 +++ b/netwerk/cookie/src/nsCookieService.h
 @@ -73,7 +73,7 @@ class nsCookieEntry : public PLDHashEntr
      typedef const char* KeyTypePointer;
  
      // do nothing with aHost - we require mHead to be set before we're live!
 -    nsCookieEntry(KeyTypePointer aHost)
rename from mozStorageTransaction-okonstack
rename to mozStorageTransaction-allowonstack
--- a/mozStorageTransaction-okonstack
+++ b/mozStorageTransaction-allowonstack
@@ -1,27 +1,14 @@
-mozStorageTransaction should only appear on the stack. Ready for upstream to mozilla-central
-* * *
-* * *
+mozStorageTransaction may appear on the stack. Which is ok, because we can make mConnection a const pointer
 
 diff --git a/storage/public/mozStorageHelper.h b/storage/public/mozStorageHelper.h
 --- a/storage/public/mozStorageHelper.h
 +++ b/storage/public/mozStorageHelper.h
-@@ -57,7 +57,9 @@
-  * you may not get the transaction type you ask for, and you won't be able
-  * to rollback.
-  */
--class mozStorageTransaction
-+class
-+  NS_OKONSTACK
-+mozStorageTransaction
- {
- public:
-   mozStorageTransaction(mozIStorageConnection* aConnection,
-@@ -136,7 +138,7 @@ public:
+@@ -136,7 +136,7 @@ public:
    }
  
  protected:
 -  nsCOMPtr<mozIStorageConnection> mConnection;
 +  mozIStorageConnection* const mConnection;
    PRBool mHasTransaction;
    PRBool mCommitOnComplete;
    PRBool mCompleted;
deleted file mode 100644
--- a/nsAssignment-okonstack
+++ /dev/null
@@ -1,67 +0,0 @@
-diff --git a/content/xul/templates/src/nsRuleNetwork.cpp b/content/xul/templates/src/nsRuleNetwork.cpp
---- a/content/xul/templates/src/nsRuleNetwork.cpp
-+++ b/content/xul/templates/src/nsRuleNetwork.cpp
-@@ -139,11 +139,10 @@ nsAssignmentSet::Add(const nsAssignment&
-     if (HasAssignmentFor(aAssignment.mVariable))
-         return NS_ERROR_UNEXPECTED;
- 
--    List* list = new List;
-+    List* list = new List(aAssignment);
-     if (! list)
-         return NS_ERROR_OUT_OF_MEMORY;
- 
--    list->mAssignment = aAssignment;
-     list->mRefCnt     = 1;
-     list->mNext       = mAssignments;
- 
-diff --git a/content/xul/templates/src/nsRuleNetwork.h b/content/xul/templates/src/nsRuleNetwork.h
---- a/content/xul/templates/src/nsRuleNetwork.h
-+++ b/content/xul/templates/src/nsRuleNetwork.h
-@@ -223,11 +223,8 @@ public:
-  */
- class nsAssignment {
- public:
--    nsCOMPtr<nsIAtom> mVariable;
--    nsCOMPtr<nsIRDFNode> mValue;
--
--    nsAssignment() : mValue()
--        { MOZ_COUNT_CTOR(nsAssignment); }
-+    nsIAtom *const mVariable;
-+    nsIRDFNode *const mValue;
- 
-     nsAssignment(nsIAtom* aVariable, nsIRDFNode* aValue)
-         : mVariable(aVariable),
-@@ -241,11 +238,6 @@ public:
- 
-     ~nsAssignment() { MOZ_COUNT_DTOR(nsAssignment); }
- 
--    nsAssignment& operator=(const nsAssignment& aAssignment) {
--        mVariable = aAssignment.mVariable;
--        mValue    = aAssignment.mValue;
--        return *this; }
--
-     PRBool operator==(const nsAssignment& aAssignment) const {
-         return mVariable == aAssignment.mVariable && mValue == aAssignment.mValue; }
- 
-@@ -254,8 +246,8 @@ public:
- 
-     PLHashNumber Hash() const {
-         // XXX I have no idea if this hashing function is good or not // XXX change this
--        PLHashNumber temp = PLHashNumber(NS_PTR_TO_INT32(mValue.get())) >> 2; // strip alignment bits
--        return (temp & 0xffff) | NS_PTR_TO_INT32(mVariable.get()); }
-+        PLHashNumber temp = PLHashNumber(NS_PTR_TO_INT32(mValue)) >> 2; // strip alignment bits
-+        return (temp & 0xffff) | NS_PTR_TO_INT32(mVariable); }
- };
- 
- 
-@@ -273,7 +265,9 @@ protected:
- protected:
-     class List {
-     public:
--        List() { MOZ_COUNT_CTOR(nsAssignmentSet::List); }
-+        List(const nsAssignment& aAssignment)
-+            : mAssignment(aAssignment) {
-+            MOZ_COUNT_CTOR(nsAssignmentSet::List); }
- 
-         ~List() {
-             MOZ_COUNT_DTOR(nsAssignmentSet::List);
rename from nsCanvasRenderingContext2D_ContextState-okonstack
rename to nsCanvasRenderingContext2D_ContextState-allowonstack
--- a/nsCanvasRenderingContext2D_ContextState-okonstack
+++ b/nsCanvasRenderingContext2D_ContextState-allowonstack
@@ -1,25 +1,12 @@
 diff --git a/content/canvas/src/nsCanvasRenderingContext2D.cpp b/content/canvas/src/nsCanvasRenderingContext2D.cpp
 --- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
 +++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
-@@ -434,7 +434,11 @@ protected:
-     PRPackedBool mDirtyStyle[STYLE_MAX];
- 
-     // state stack handling
--    class ContextState {
-+    class
-+        NS_GC_TYPE
-+        NS_OKONSTACK
-+    ContextState
-+    {
-     public:
-         ContextState() : globalAlpha(1.0),
-                          textAlign(TEXT_ALIGN_START),
-@@ -478,11 +482,11 @@ protected:
+@@ -478,11 +478,11 @@ protected:
          TextBaseline textBaseline;
  
          nscolor colorStyles[STYLE_MAX];
 -        nsCOMPtr<nsCanvasGradient> gradientStyles[STYLE_MAX];
 -        nsCOMPtr<nsCanvasPattern> patternStyles[STYLE_MAX];
 +        nsCanvasGradient* gradientStyles[STYLE_MAX];
 +        nsCanvasPattern* patternStyles[STYLE_MAX];
      };
new file mode 100644
--- /dev/null
+++ b/nsGUIEvents-allowonstack
@@ -0,0 +1,43 @@
+diff --git a/content/events/src/nsEventListenerManager.cpp b/content/events/src/nsEventListenerManager.cpp
+--- a/content/events/src/nsEventListenerManager.cpp
++++ b/content/events/src/nsEventListenerManager.cpp
+@@ -1359,7 +1359,8 @@ nsEventListenerManager::FixContextMenuEv
+     // the DOM event. Since we never call InitMouseEvent() on the event, 
+     // the client X/Y will be 0,0. We can make use of that if the widget is null.
+     if (contextMenuKey) {
+-      aPresContext->GetViewManager()->GetWidget(getter_AddRefs(((nsGUIEvent*)aEvent)->widget));
++      // XXX need write barrier for aEvent->widget
++      aPresContext->GetViewManager()->GetWidget(&((nsGUIEvent*)aEvent)->widget);
+       aEvent->refPoint.x = 0;
+       aEvent->refPoint.y = 0;
+     }
+diff --git a/widget/public/nsGUIEvent.h b/widget/public/nsGUIEvent.h
+--- a/widget/public/nsGUIEvent.h
++++ b/widget/public/nsGUIEvent.h
+@@ -495,7 +495,7 @@ public:
+   }
+ 
+   /// Originator of the event
+-  nsCOMPtr<nsIWidget> widget;           
++  nsIWidget* widget;           
+   /// Internal platform specific message.
+   void* nativeMsg;        
+ };
+@@ -681,7 +681,7 @@ public:
+   : nsInputEvent(isTrusted, msg, w, type), button(0) {}
+ 
+   /// The possible related target
+-  nsCOMPtr<nsISupports> relatedTarget;
++  nsISupports*          relatedTarget;
+ 
+   PRInt16               button;
+ };
+@@ -1010,7 +1010,7 @@ public:
+     command = aCommand;
+   }
+ 
+-  nsCOMPtr<nsIAtom> command;
++  nsIAtom* command;
+ };
+ 
+ /**
deleted file mode 100644
--- a/nsGUIEvents-okonstack
+++ /dev/null
@@ -1,359 +0,0 @@
-* * *
-
-diff --git a/content/events/src/nsEventListenerManager.cpp b/content/events/src/nsEventListenerManager.cpp
---- a/content/events/src/nsEventListenerManager.cpp
-+++ b/content/events/src/nsEventListenerManager.cpp
-@@ -1359,7 +1359,8 @@ nsEventListenerManager::FixContextMenuEv
-     // the DOM event. Since we never call InitMouseEvent() on the event, 
-     // the client X/Y will be 0,0. We can make use of that if the widget is null.
-     if (contextMenuKey) {
--      aPresContext->GetViewManager()->GetWidget(getter_AddRefs(((nsGUIEvent*)aEvent)->widget));
-+      // XXX need write barrier for aEvent->widget
-+      aPresContext->GetViewManager()->GetWidget(&((nsGUIEvent*)aEvent)->widget);
-       aEvent->refPoint.x = 0;
-       aEvent->refPoint.y = 0;
-     }
-diff --git a/widget/public/nsGUIEvent.h b/widget/public/nsGUIEvent.h
---- a/widget/public/nsGUIEvent.h
-+++ b/widget/public/nsGUIEvent.h
-@@ -385,7 +385,10 @@ enum nsWindowZ {
-  * General event
-  */
- 
--class nsEvent
-+class
-+  NS_GC_TYPE
-+  NS_OKONSTACK
-+nsEvent
- {
- protected:
-   nsEvent(PRBool isTrusted, PRUint32 msg, PRUint8 structType)
-@@ -448,7 +451,9 @@ public:
-  * General graphic user interface event
-  */
- 
--class nsGUIEvent : public nsEvent
-+class
-+  NS_OKONSTACK
-+nsGUIEvent : public nsEvent
- {
- protected:
-   nsGUIEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w, PRUint8 structType)
-@@ -465,7 +470,7 @@ public:
-   }
- 
-   /// Originator of the event
--  nsCOMPtr<nsIWidget> widget;           
-+  nsIWidget* widget;           
-   /// Internal platform specific message.
-   void* nativeMsg;        
- };
-@@ -474,7 +479,9 @@ public:
-  * Script error event
-  */
- 
--class nsScriptErrorEvent : public nsEvent
-+class
-+  NS_OKONSTACK
-+nsScriptErrorEvent : public nsEvent
- {
- public:
-   nsScriptErrorEvent(PRBool isTrusted, PRUint32 msg)
-@@ -488,7 +495,9 @@ public:
-   const PRUnichar*  fileName;
- };
- 
--class nsBeforePageUnloadEvent : public nsEvent
-+class
-+  NS_OKONSTACK
-+nsBeforePageUnloadEvent : public nsEvent
- {
- public:
-   nsBeforePageUnloadEvent(PRBool isTrusted, PRUint32 msg)
-@@ -503,7 +512,9 @@ public:
-  * Window resize event
-  */
- 
--class nsSizeEvent : public nsGUIEvent
-+class
-+  NS_OKONSTACK
-+nsSizeEvent : public nsGUIEvent
- {
- public:
-   nsSizeEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -524,7 +535,9 @@ public:
-  * Window size mode event
-  */
- 
--class nsSizeModeEvent : public nsGUIEvent
-+class
-+  NS_OKONSTACK
-+nsSizeModeEvent : public nsGUIEvent
- {
- public:
-   nsSizeModeEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -540,7 +553,9 @@ public:
-  * Window z-level event
-  */
- 
--class nsZLevelEvent : public nsGUIEvent
-+class
-+  NS_OKONSTACK
-+nsZLevelEvent : public nsGUIEvent
- {
- public:
-   nsZLevelEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -561,7 +576,9 @@ public:
-  * Window repaint event
-  */
- 
--class nsPaintEvent : public nsGUIEvent
-+class
-+  NS_OKONSTACK
-+nsPaintEvent : public nsGUIEvent
- {
- public:
-   nsPaintEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -582,7 +599,9 @@ public:
-  * Scrollbar event
-  */
- 
--class nsScrollbarEvent : public nsGUIEvent
-+class
-+  NS_OKONSTACK
-+nsScrollbarEvent : public nsGUIEvent
- {
- public:
-   nsScrollbarEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -595,7 +614,9 @@ public:
-   PRUint32        position; 
- };
- 
--class nsScrollPortEvent : public nsGUIEvent
-+class
-+  NS_OKONSTACK
-+nsScrollPortEvent : public nsGUIEvent
- {
- public:
-   enum orientType {
-@@ -613,7 +634,9 @@ public:
-   orientType orient;
- };
- 
--class nsInputEvent : public nsGUIEvent
-+class
-+  NS_OKONSTACK
-+nsInputEvent : public nsGUIEvent
- {
- protected:
-   nsInputEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w,
-@@ -644,19 +667,23 @@ public:
-  * Mouse event
-  */
- 
--class nsMouseEvent_base : public nsInputEvent
-+class
-+  NS_OKONSTACK
-+nsMouseEvent_base : public nsInputEvent
- {
- public:
-   nsMouseEvent_base(PRBool isTrusted, PRUint32 msg, nsIWidget *w, PRUint8 type)
-   : nsInputEvent(isTrusted, msg, w, type), button(0) {}
- 
-   /// The possible related target
--  nsCOMPtr<nsISupports> relatedTarget;
-+  nsISupports*          relatedTarget;
- 
-   PRInt16               button;
- };
- 
--class nsMouseEvent : public nsMouseEvent_base
-+class
-+  NS_OKONSTACK
-+nsMouseEvent : public nsMouseEvent_base
- {
- public:
-   enum buttonType  { eLeftButton = 0, eMiddleButton = 1, eRightButton = 2 };
-@@ -700,7 +727,9 @@ public:
-  * Accessible event
-  */
- 
--class nsAccessibleEvent : public nsInputEvent
-+class
-+  NS_OKONSTACK
-+nsAccessibleEvent : public nsInputEvent
- {
- public:
-   nsAccessibleEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -726,7 +755,9 @@ struct nsAlternativeCharCode {
-   PRUint32 mShiftedCharCode;
- };
- 
--class nsKeyEvent : public nsInputEvent
-+class
-+  NS_OKONSTACK
-+nsKeyEvent : public nsInputEvent
- {
- public:
-   nsKeyEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -776,7 +807,9 @@ struct nsTextEventReply
- 
- typedef struct nsTextEventReply nsTextEventReply;
- 
--class nsTextEvent : public nsInputEvent
-+class
-+  NS_OKONSTACK
-+nsTextEvent : public nsInputEvent
- {
- public:
-   nsTextEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -792,7 +825,9 @@ public:
-   PRBool            isChar;
- };
- 
--class nsCompositionEvent : public nsInputEvent
-+class
-+  NS_OKONSTACK
-+nsCompositionEvent : public nsInputEvent
- {
- public:
-   nsCompositionEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -803,7 +838,9 @@ public:
-   nsTextEventReply theReply;
- };
- 
--class nsMouseScrollEvent : public nsMouseEvent_base
-+class
-+  NS_OKONSTACK
-+nsMouseScrollEvent : public nsMouseEvent_base
- {
- public:
-   enum nsMouseScrollFlags {
-@@ -832,7 +869,9 @@ struct nsReconversionEventReply {
-   PRUnichar *mReconversionString;
- };
- 
--class nsReconversionEvent : public nsInputEvent
-+class
-+  NS_OKONSTACK
-+nsReconversionEvent : public nsInputEvent
- {
- public:
-   nsReconversionEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -854,7 +893,9 @@ struct nsQueryCaretRectEventReply
-   nsRect mCaretRect;
- };
- 
--class nsQueryCaretRectEvent : public nsInputEvent
-+class
-+  NS_OKONSTACK
-+nsQueryCaretRectEvent : public nsInputEvent
- {
- public:
-   nsQueryCaretRectEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -865,7 +906,9 @@ public:
-   nsQueryCaretRectEventReply theReply;
- };
- 
--class nsQueryContentEvent : public nsGUIEvent
-+class
-+  NS_OKONSTACK
-+nsQueryContentEvent : public nsGUIEvent
- {
- public:
-   nsQueryContentEvent(PRBool aIsTrusted, PRUint32 aMsg, nsIWidget *aWidget) :
-@@ -916,7 +959,9 @@ public:
-  * for the event
-  */
- 
--class nsMenuEvent : public nsGUIEvent
-+class
-+  NS_OKONSTACK
-+nsMenuEvent : public nsGUIEvent
- {
- public:
-   nsMenuEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -936,7 +981,9 @@ public:
-  * originator is a weak pointer (does not hold a strong reference).
-  */
- 
--class nsFormEvent : public nsEvent
-+class
-+  NS_OKONSTACK
-+nsFormEvent : public nsEvent
- {
- public:
-   nsFormEvent(PRBool isTrusted, PRUint32 msg)
-@@ -951,7 +998,9 @@ public:
- /**
- * Focus event
- */
--class nsFocusEvent : public nsGUIEvent
-+class
-+  NS_OKONSTACK
-+nsFocusEvent : public nsGUIEvent
- {
- public:
-   nsFocusEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
-@@ -969,7 +1018,9 @@ public:
-  * Custom commands for example from the operating system.
-  */
- 
--class nsCommandEvent : public nsGUIEvent
-+class
-+  NS_OKONSTACK
-+nsCommandEvent : public nsGUIEvent
- {
- public:
-   nsCommandEvent(PRBool isTrusted, nsIAtom* aEventType,
-@@ -980,13 +1031,15 @@ public:
-     command = aCommand;
-   }
- 
--  nsCOMPtr<nsIAtom> command;
-+  nsIAtom* command;
- };
- 
- /**
-  * blocked popup window event
-  */
--class nsPopupBlockedEvent : public nsEvent
-+class
-+  NS_OKONSTACK
-+nsPopupBlockedEvent : public nsEvent
- {
- public:
-   nsPopupBlockedEvent(PRBool isTrusted, PRUint32 msg)
-@@ -1004,7 +1057,9 @@ public:
- /**
-  * DOM UIEvent
-  */
--class nsUIEvent : public nsEvent
-+class
-+  NS_OKONSTACK
-+nsUIEvent : public nsEvent
- {
- public:
-   nsUIEvent(PRBool isTrusted, PRUint32 msg, PRInt32 d)
-@@ -1019,7 +1074,9 @@ public:
- /**
-  * PageTransition event
-  */
--class nsPageTransitionEvent : public nsEvent
-+class
-+  NS_OKONSTACK
-+nsPageTransitionEvent : public nsEvent
- {
- public:
-   nsPageTransitionEvent(PRBool isTrusted, PRUint32 msg, PRBool p)
-@@ -1034,7 +1091,9 @@ public:
- /**
-  * XUL command event
-  */
--class nsXULCommandEvent : public nsInputEvent
-+class
-+  NS_OKONSTACK
-+nsXULCommandEvent : public nsInputEvent
- {
- public:
-   nsXULCommandEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
rename from nsMutationEvent-okonstack
rename to nsMutationEvent-allowonstack
--- a/nsMutationEvent-okonstack
+++ b/nsMutationEvent-allowonstack
@@ -1,19 +1,12 @@
 diff --git a/content/events/public/nsMutationEvent.h b/content/events/public/nsMutationEvent.h
 --- a/content/events/public/nsMutationEvent.h
 +++ b/content/events/public/nsMutationEvent.h
-@@ -44,20 +44,26 @@
- #include "nsIDOMEventTarget.h"
- #include "nsIContent.h"
- 
--class nsMutationEvent : public nsEvent
-+class
-+  NS_OKONSTACK
-+nsMutationEvent : public nsEvent
+@@ -48,16 +48,20 @@ class nsMutationEvent : public nsEvent
  {
  public:
    nsMutationEvent(PRBool isTrusted, PRUint32 msg)
 -    : nsEvent(isTrusted, msg, NS_MUTATION_EVENT),
 -      mAttrChange(0)
 +    : nsEvent(isTrusted, msg, NS_MUTATION_EVENT)
 +    , mRelatedNode(nsnull)
 +    , mAttrName(nsnull)
rename from nsRangeStore-okonstack
rename to nsRangeStore-allowonstack
--- a/nsRangeStore-okonstack
+++ b/nsRangeStore-allowonstack
@@ -1,9 +1,9 @@
-* * *
+Allow nsRangeStore to be used on the stack. This is ugly because of write barriers, which I'm ignoring for the moment. TODO: figure these out!
 
 diff --git a/editor/libeditor/base/nsSelectionState.cpp b/editor/libeditor/base/nsSelectionState.cpp
 --- a/editor/libeditor/base/nsSelectionState.cpp
 +++ b/editor/libeditor/base/nsSelectionState.cpp
 @@ -273,9 +273,9 @@ nsRangeUpdater::SelAdjCreateNode(nsIDOMN
      item = (nsRangeStore*)mArray.ElementAt(i);
      if (!item) return NS_ERROR_NULL_POINTER;
      
@@ -184,28 +184,17 @@ diff --git a/editor/libeditor/base/nsSel
 +  aRange->GetStartContainer(&startNode);
 +  aRange->GetEndContainer(&endNode);
    aRange->GetStartOffset(&startOffset);
    aRange->GetEndOffset(&endOffset);
    return NS_OK;
 diff --git a/editor/libeditor/base/nsSelectionState.h b/editor/libeditor/base/nsSelectionState.h
 --- a/editor/libeditor/base/nsSelectionState.h
 +++ b/editor/libeditor/base/nsSelectionState.h
-@@ -53,16 +53,20 @@ class nsISelection;
-  */
- 
- // first a helper struct for saving/setting ranges
--struct nsRangeStore 
-+struct
-+  NS_GC_TYPE
-+  NS_OKONSTACK
-+nsRangeStore 
- {
-   nsRangeStore();
-   ~nsRangeStore();
+@@ -60,9 +60,10 @@ struct nsRangeStore
    nsresult StoreRange(nsIDOMRange *aRange);
    nsresult GetRange(nsCOMPtr<nsIDOMRange> *outRange);
          
 -  nsCOMPtr<nsIDOMNode> startNode;
 +  // XXXwrite barriers
 +  nsIDOMNode* startNode;
    PRInt32              startOffset;
 -  nsCOMPtr<nsIDOMNode> endNode;
rename from nsSortState-okonstack
rename to nsSortState-allowonstack
--- a/nsSortState-okonstack
+++ b/nsSortState-allowonstack
@@ -17,46 +17,25 @@ diff --git a/content/xul/templates/src/n
 -  if (aContainer != aSortState->lastContainer.get()) {
 +  if (aContainer != aSortState->lastContainer) {
      aSortState->lastContainer = aContainer;
      aSortState->lastWasFirst = PR_FALSE;
      aSortState->lastWasLast = PR_FALSE;
 diff --git a/content/xul/templates/src/nsXULSortService.h b/content/xul/templates/src/nsXULSortService.h
 --- a/content/xul/templates/src/nsXULSortService.h
 +++ b/content/xul/templates/src/nsXULSortService.h
-@@ -72,7 +72,10 @@ enum nsSortState_direction {
- };
-   
- // the sort state holds info about the current sort
--struct nsSortState
-+struct 
-+  NS_GC_TYPE
-+  NS_OKONSTACK
-+nsSortState
- {
-   PRBool initialized;
-   PRBool invertSort;
-@@ -84,12 +87,14 @@ struct nsSortState
+@@ -84,12 +84,14 @@ struct nsSortState
    nsAutoString sort;
    nsCOMArray<nsIAtom> sortKeys;
  
 -  nsCOMPtr<nsIXULTemplateQueryProcessor> processor;
 -  nsCOMPtr<nsIContent> lastContainer;
 +  nsIXULTemplateQueryProcessor* processor;
 +  nsIContent* lastContainer;
    PRBool lastWasFirst, lastWasLast;
  
    nsSortState()
      : initialized(PR_FALSE)
 +    , processor(nsnull)
 +    , lastContainer(nsnull)
    {
    }
  };
-@@ -112,7 +117,7 @@ struct contentSortInfo {
- //
- //   This is the sort service.
- //
--class XULSortServiceImpl : public nsIXULSortService
-+class XULSortServiceImpl : public XPCOMGCFinalizedObject, public nsIXULSortService
- {
- protected:
-   XULSortServiceImpl(void) {}
rename from nsTimeout-GCObject-okonstack
rename to nsTimeout-GCObject
--- a/nsTimeout-GCObject-okonstack
+++ b/nsTimeout-GCObject
@@ -1,14 +1,45 @@
+diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp
+--- a/dom/src/base/nsGlobalWindow.cpp
++++ b/dom/src/base/nsGlobalWindow.cpp
+@@ -568,24 +568,8 @@ nsTimeout::nsTimeout()
+ #endif
+ 
+   memset(this, 0, sizeof(*this));
+-
+-  MOZ_COUNT_CTOR(nsTimeout);
+-}
+-
+-nsTimeout::~nsTimeout()
+-{
+-#ifdef DEBUG_jst
+-  {
+-    extern int gTimeoutCnt;
+-
+-    --gTimeoutCnt;
+-  }
+-#endif
+-
+-  MOZ_COUNT_DTOR(nsTimeout);
+-}
+-
+-  
++}
++
+ //*****************************************************************************
+ //***    nsGlobalWindow: Object Management
+ //*****************************************************************************
 diff --git a/dom/src/base/nsGlobalWindow.h b/dom/src/base/nsGlobalWindow.h
 --- a/dom/src/base/nsGlobalWindow.h
 +++ b/dom/src/base/nsGlobalWindow.h
-@@ -145,7 +145,9 @@ NS_CreateJSTimeoutHandler(nsGlobalWindow
+@@ -145,10 +145,9 @@ NS_CreateJSTimeoutHandler(nsGlobalWindow
   * timeout.  Holds a strong reference to an nsIScriptTimeoutHandler, which
   * abstracts the language specific cruft.
   */
 -struct nsTimeout : PRCList
-+struct
-+  NS_OKONSTACK
-+nsTimeout : XPCOMGCObject, PRCList
++struct nsTimeout : XPCOMGCObject, PRCList
  {
    nsTimeout();
-   ~nsTimeout();
+-  ~nsTimeout();
+ 
+   nsrefcnt Release();
+   nsrefcnt AddRef();
deleted file mode 100644
--- a/nsURIHashKey-okonstack
+++ /dev/null
@@ -1,66 +0,0 @@
-* * *
-
-diff --git a/layout/style/nsCSSLoader.h b/layout/style/nsCSSLoader.h
---- a/layout/style/nsCSSLoader.h
-+++ b/layout/style/nsCSSLoader.h
-@@ -229,13 +229,17 @@ public:
-                            const void *entryStore)
-     : nsURIHashKey(aKey->mKey, entryStore), mPrincipal(aKey->mPrincipal)
-   {
-+    NS_GetGC()->WriteBarrierTrap(entryStore, aKey->mPrincipal);
-   }
-+
-+  // this constructor may only be called if the hashkey is allocated
-+  // on the stack!
-   nsURIAndPrincipalHashKey(nsIURI* aURI, nsIPrincipal* aPrincipal)
--    : nsURIHashKey(aURI, this), mPrincipal(aPrincipal)
-+    : nsURIHashKey(aURI, nsnull), mPrincipal(aPrincipal)
-   {
-   }
-   nsURIAndPrincipalHashKey(const nsURIAndPrincipalHashKey& toCopy)
--    : nsURIHashKey(toCopy)
-+    : nsURIHashKey(toCopy), mPrincipal(toCopy.mPrincipal)
-   {
-     NS_NOTREACHED("This code doesn't work!");
-   }
-@@ -272,7 +276,7 @@ public:
-   enum { ALLOW_MEMMOVE = PR_TRUE };
-  
- protected:
--  nsCOMPtr<nsIPrincipal> mPrincipal;
-+  nsIPrincipal *const mPrincipal;
- };
- 
- /***********************************************************************
-diff --git a/netwerk/base/public/nsURIHashKey.h b/netwerk/base/public/nsURIHashKey.h
---- a/netwerk/base/public/nsURIHashKey.h
-+++ b/netwerk/base/public/nsURIHashKey.h
-@@ -52,11 +52,16 @@ public:
-     typedef nsIURI* KeyType;
-     typedef const nsIURI* KeyTypePointer;
- 
-+    // if entryStore is null, this class was allocated on the stack
-+    // see the nsURIAndPrincipalHashKey subclass
-     nsURIHashKey(const nsIURI* aKey, const void *entryStore) :
--        mKey(const_cast<nsIURI*>(aKey)) { MOZ_COUNT_CTOR(nsURIHashKey); }
-+        mKey(const_cast<nsIURI*>(aKey)) {
-+        if (entryStore)
-+            NS_GetGC()->WriteBarrierTrap(entryStore, aKey);
-+    }
-     nsURIHashKey(const nsURIHashKey& toCopy) :
--        mKey(toCopy.mKey) { MOZ_COUNT_CTOR(nsURIHashKey); }
--    ~nsURIHashKey() { MOZ_COUNT_DTOR(nsURIHashKey); }
-+        mKey(toCopy.mKey) { }
-+    ~nsURIHashKey() { }
- 
-     nsIURI* GetKey() const { return mKey; }
- 
-@@ -78,7 +83,7 @@ public:
-     enum { ALLOW_MEMMOVE = PR_TRUE };
- 
- protected:
--    nsCOMPtr<nsIURI> mKey;
-+    nsIURI *const mKey;
- };
- 
- #endif // nsURIHashKey_h__
new file mode 100644
--- /dev/null
+++ b/nsZipQueueItem-allowonstack
@@ -0,0 +1,53 @@
+diff --git a/modules/libjar/zipwriter/src/nsZipWriter.cpp b/modules/libjar/zipwriter/src/nsZipWriter.cpp
+--- a/modules/libjar/zipwriter/src/nsZipWriter.cpp
++++ b/modules/libjar/zipwriter/src/nsZipWriter.cpp
+@@ -383,7 +383,7 @@ NS_IMETHODIMP nsZipWriter::AddEntryFile(
+         item.mOperation = OPERATION_ADD;
+         item.mZipEntry = aZipEntry;
+         item.mCompression = aCompression;
+-        rv = aFile->Clone(getter_AddRefs(item.mFile));
++        rv = aFile->Clone(&item.mFile);
+         NS_ENSURE_SUCCESS(rv, rv);
+         if (!mQueue.AppendElement(item))
+             return NS_ERROR_OUT_OF_MEMORY;
+@@ -862,7 +862,7 @@ inline nsresult nsZipWriter::BeginProces
+ 
+         if (!isdir) {
+             // Set up for fall through to stream reader
+-            rv = NS_NewLocalFileInputStream(getter_AddRefs(aItem->mStream),
++            rv = NS_NewLocalFileInputStream(&aItem->mStream,
+                                             aItem->mFile);
+             NS_ENSURE_SUCCESS(rv, rv);
+         }
+diff --git a/modules/libjar/zipwriter/src/nsZipWriter.h b/modules/libjar/zipwriter/src/nsZipWriter.h
+--- a/modules/libjar/zipwriter/src/nsZipWriter.h
++++ b/modules/libjar/zipwriter/src/nsZipWriter.h
+@@ -60,11 +60,16 @@ struct nsZipQueueItem
+ struct nsZipQueueItem
+ {
+ public:
++    nsZipQueueItem() : mFile(nsnull), mChannel(nsnull), mStream(nsnull) { }
++
+     PRUint32 mOperation;
+     nsCString mZipEntry;
+-    nsCOMPtr<nsIFile> mFile;
+-    nsCOMPtr<nsIChannel> mChannel;
+-    nsCOMPtr<nsIInputStream> mStream;
++
++    // XXXbsmedberg: need write barriers on mFile/mChannel/mStream when this
++    // is actually a GC class (nsZipWriter.mQueue)
++    nsIFile* mFile;
++    nsIChannel* mChannel;
++    nsIInputStream* mStream;
+     PRTime mModTime;
+     PRInt32 mCompression;
+ };
+@@ -92,7 +97,7 @@ private:
+     nsCOMPtr<nsISupports> mProcessContext;
+     nsCOMPtr<nsIOutputStream> mStream;
+     nsCOMArray<nsZipHeader> mHeaders;
+-    nsTArray<nsZipQueueItem> mQueue;
++    nsTArray<nsZipQueueItem, GCAllocator> mQueue;
+     nsDataHashtable<nsCStringHashKey, PRInt32> mEntryHash;
+     nsCString mComment;
+ 
deleted file mode 100644
--- a/nsZipQueueItem-okonstack
+++ /dev/null
@@ -1,60 +0,0 @@
-diff --git a/modules/libjar/zipwriter/src/nsZipWriter.cpp b/modules/libjar/zipwriter/src/nsZipWriter.cpp
---- a/modules/libjar/zipwriter/src/nsZipWriter.cpp
-+++ b/modules/libjar/zipwriter/src/nsZipWriter.cpp
-@@ -383,7 +383,7 @@ NS_IMETHODIMP nsZipWriter::AddEntryFile(
-         item.mOperation = OPERATION_ADD;
-         item.mZipEntry = aZipEntry;
-         item.mCompression = aCompression;
--        rv = aFile->Clone(getter_AddRefs(item.mFile));
-+        rv = aFile->Clone(&item.mFile);
-         NS_ENSURE_SUCCESS(rv, rv);
-         if (!mQueue.AppendElement(item))
-             return NS_ERROR_OUT_OF_MEMORY;
-@@ -862,7 +862,7 @@ inline nsresult nsZipWriter::BeginProces
- 
-         if (!isdir) {
-             // Set up for fall through to stream reader
--            rv = NS_NewLocalFileInputStream(getter_AddRefs(aItem->mStream),
-+            rv = NS_NewLocalFileInputStream(&aItem->mStream,
-                                             aItem->mFile);
-             NS_ENSURE_SUCCESS(rv, rv);
-         }
-diff --git a/modules/libjar/zipwriter/src/nsZipWriter.h b/modules/libjar/zipwriter/src/nsZipWriter.h
---- a/modules/libjar/zipwriter/src/nsZipWriter.h
-+++ b/modules/libjar/zipwriter/src/nsZipWriter.h
-@@ -57,14 +57,22 @@
- 
- #define OPERATION_ADD 0
- #define OPERATION_REMOVE 1
--struct nsZipQueueItem
-+struct 
-+  NS_GC_TYPE
-+  NS_OKONSTACK
-+nsZipQueueItem
- {
- public:
-+    nsZipQueueItem() : mFile(nsnull), mChannel(nsnull), mStream(nsnull) { }
-+
-     PRUint32 mOperation;
-     nsCString mZipEntry;
--    nsCOMPtr<nsIFile> mFile;
--    nsCOMPtr<nsIChannel> mChannel;
--    nsCOMPtr<nsIInputStream> mStream;
-+
-+    // XXXbsmedberg: need write barriers on mFile/mChannel/mStream when this
-+    // is actually a GC class (nsZipWriter.mQueue)
-+    nsIFile* mFile;
-+    nsIChannel* mChannel;
-+    nsIInputStream* mStream;
-     PRTime mModTime;
-     PRInt32 mCompression;
- };
-@@ -93,7 +101,7 @@ private:
-     nsCOMPtr<nsISupports> mProcessContext;
-     nsCOMPtr<nsIOutputStream> mStream;
-     nsCOMArray<nsZipHeader> mHeaders;
--    nsTArray<nsZipQueueItem> mQueue;
-+    nsTArray<nsZipQueueItem, GCAllocator> mQueue;
-     nsDataHashtable<nsCStringHashKey, PRInt32> mEntryHash;
-     nsCString mComment;
- 
--- a/proxy-create-namespace
+++ b/proxy-create-namespace
@@ -7,31 +7,32 @@ diff --git a/xpcom/proxy/tests/proxy-cre
  /***************************************************************************/
  /* ProxyTest                                                               */
  /***************************************************************************/
 +
 +namespace proxy_create_threadsafety {
  
  class ProxyTest : public nsIRunnable,
                    public nsITestProxy,
-@@ -230,6 +232,8 @@ private:
+@@ -231,6 +233,8 @@ private:
      nsCOMPtr<nsIThread> mThreadTwo;
  };
  
 +} // namespace
 +
  int
  main(int argc, char **argv)
  {
-@@ -241,7 +245,7 @@ main(int argc, char **argv)
-         NS_GetComponentRegistrar(&registrar);
+@@ -242,7 +246,8 @@ main(int argc, char **argv)
+         NS_GetComponentRegistrar(getter_AddRefs(registrar));
          registrar->AutoRegister(nsnull);
  
--        nsITestProxy* tester = new ProxyTest();
-+        nsITestProxy* tester = new proxy_create_threadsafety::ProxyTest();
+-        nsCOMPtr<nsITestProxy> tester = new ProxyTest();
++        nsCOMPtr<nsITestProxy> tester =
++            new proxy_create_threadsafety::ProxyTest();
          tester->Test(0, 0, nsnull);
      }
  
-@@ -249,4 +253,3 @@ main(int argc, char **argv)
+@@ -250,4 +255,3 @@ main(int argc, char **argv)
  
      return 0;
  }
 -
--- a/series
+++ b/series
@@ -125,17 +125,17 @@ zipreader-cache
 root-xptimanager
 threadmanager-shutdown-norecreate
 nsScanner-gc
 nsObjectMapEntry
 root-rdfstuff
 nsXHTMLParanoidFragmentSink
 multiple-comptrs
 static-check-gc-attributes
-nsRangeStore-okonstack
+nsRangeStore-allowonstack
 accessible-stack-classes
 widget-stack-classes
 psm-stack-classes
 spellcheck-stack-classes
 necko-stack-classes
 securebrowser-cast
 profilemigrator-immortal
 hgignore-update
@@ -144,61 +144,50 @@ xpconnect-singleton-shutdown
 classinfo2nativeset-link
 propertycache-gc-assert
 proxyobjectcallinfo-finalizer
 registration-order-test-fixup
 nativewrapper-finalizers
 http-connection-reclamation
 root-stringbundles
 rdf-hashtable
-mozStorageTransaction-okonstack
+mozStorageTransaction-allowonstack
 mozStorageStatementScoper-stackclass
 no-immediate-CXXFLAGS
 root-nsNavHistory
 remove-manual-addrefs
 rdf-anonymous-resource-refcounting
-JSString-okonstack
 nsProxyObject-reorder-bases
 nsCookie-reorder-base
-XPCNativeScriptableShared-okonstack
 xpctest_echo-GET_CALL_CONTEXT-macrofixup
-nsZipQueueItem-okonstack
+nsZipQueueItem-allowonstack
 rdf-BlobImpl-reorder-bases
-nsGUIEvents-okonstack
+nsGUIEvents-allowonstack
 nsPSPrinterList-stackclass
 FORWARD_TO_INNER_CREATE-stackcomptr
-nsTimeout-GCObject-okonstack
-nsMutationEvent-okonstack
-nsCanvasRenderingContext2D_ContextState-okonstack
-nsSortState-okonstack
+nsTimeout-GCObject
+nsMutationEvent-allowonstack
+nsCanvasRenderingContext2D_ContextState-allowonstack
+nsSortState-allowonstack
 xslt-stack-objects
 nsISVGValueUtils-comptr-macros
-SVGVals-okonstack
+SVGVals-allowonstack
 NS_ENSURE_NATIVE_PATH_SEG-manual-comptr
 nsDocument_cpp-shell-manual-rewrite
-nsAssignment-okonstack
-nsURIHashKey-okonstack
 nsStyleBackground-nostack
 nsSelection_cpp-RangeData-fixup
 nsTreeImageCacheEntry-pseudoconst
 nsTransitionKey-sucky-hack
-WalkState-okonstack
 nsMorkReader-stackclass
-LazyMessage-okonstack
 PendingLookup-constmembers
 nsProxiedService-stackclass
 nsNSSCertificate-nostack
 workaround-TestProxies-garburator-crash
 BookmarkImportFrame-notonstack
-automatic-garburator
-automatic-gcobject
-automatic-remove-addrefs
 nsCSSValue-Array-comptr
 nsCSSShadowArray-refcounting
-static-check-storage-classes
 storage-class-annotations
 proxy-create-namespace
 nsHashtable-annotation
 nsSegmentedBuffer-allocator-unmanaged
-static-check-storage-classes2
 NS_FINALIZER_NOT_REQUIRED-xpcom
 nsChainedEventQueue-gcobject
 TestPageLoad-namespace
--- a/static-check-gc-attributes
+++ b/static-check-gc-attributes
@@ -1,32 +1,28 @@
-Add static checking of gc types and correct inheritance of GCFinalizable. This actually has multiple parts, some of which should be upstreamed immediately:
-
-stack checking switched to treehydra and tightened up significantly.
+Add static checking of gc types and correct inheritance of GCFinalizable. Storage-class analysis is getting more precise. See http://wiki.mozilla.org/XPCOMGC/Static_Checker for some readable descriptions.
 
-need NS_MANAGED annotation
-
-should remove the finalizer-related annotations for the subsequenet finalizer-checking patch
+This doesn't yet re-implement finalizer correctness, thought it will now that we have a reasonable pretense of storage classes. Come to think of it, I should be more precise right now about non-managed classes with destructors... these need to be marked NS_GCOK
 
 diff --git a/config/config.mk b/config/config.mk
 --- a/config/config.mk
 +++ b/config/config.mk
 @@ -514,6 +514,7 @@ TREEHYDRA_MODULES = \
  TREEHYDRA_MODULES = \
    $(topsrcdir)/xpcom/analysis/outparams.js \
    $(topsrcdir)/xpcom/analysis/stack.js \
 +  $(topsrcdir)/xpcom/analysis/gc.js \
    $(NULL)
  
  DEHYDRA_ARGS = \
 diff --git a/xpcom/analysis/gc.js b/xpcom/analysis/gc.js
 new file mode 100644
 --- /dev/null
 +++ b/xpcom/analysis/gc.js
-@@ -0,0 +1,272 @@
+@@ -0,0 +1,131 @@
 +include("unstable/lazy_types.js");
 +include("gcc_util.js");
 +
 +function process_type(c)
 +{
 +  if (!c.isIncomplete && (c.kind == 'class' || c.kind == 'struct')) {
 +    if (inheritsFromGCFinalizable(c) &&
 +	!leftmostFinalizable(c))
@@ -34,146 +30,16 @@ new file mode 100644
 +
 +    if (isXPCOMGCFinalizedObject(c) &&
 +	!leftmostFinalizable(c) &&
 +       c.name != "XPCOMGCFinalizedObject")
 +      error("MMgc::GCFinalizable is not the left-most base of class " + c.name + " which inherits from XPCOMGCFinalizedObject", c.loc);
 +  }
 +}
 +
-+function isGC(c)
-+{
-+  function calculate()
-+  {
-+    if (hasAttribute(c, 'NS_GC') ||
-+	c.name == 'MMgc::GCObject' ||
-+	c.name == 'MMgc::GCFinalizable' ||
-+	c.name == 'XPCOMGCObject' ||
-+	c.name == 'XPCOMGCFinalizedObject')
-+      return true;
-+
-+    // nsIFrame is a figment of your imagination!
-+    if (c.name == 'nsIFrame')
-+      return false;
-+
-+    for each (let base in c.bases)
-+    if (isGC(base, false))
-+      return true;
-+
-+    for each (let member in c.members) {
-+      if (member.isFunction)
-+	continue;
-+
-+      let type = member.type;
-+      while (true) {
-+	if (type.isArray) {
-+	  type = type.type;
-+	  continue;
-+	}
-+
-+	if (type.typedef) {
-+	  if (hasAttribute(type, 'NS_GC'))
-+	    return true;
-+
-+	  type = type.typedef;
-+	  continue;
-+	}
-+	break;
-+      }
-+
-+      if (type === undefined) {
-+	warning("incomplete type for member " + member + ".", member.loc);
-+	continue;
-+      }
-+
-+      if (type.isPointer || type.isReference)
-+	continue;
-+
-+      if (!type.kind || (type.kind != 'class' && type.kind != 'struct'))
-+	continue;
-+
-+      if (isGC(type, false))
-+	return true;
-+    }
-+    return false;
-+  }
-+
-+  if (c.isIncomplete)
-+    throw Error("Can't get GC status of incomplete class.");
-+
-+  if (!c.hasOwnProperty("isGC"))
-+    c.isGC = calculate();
-+
-+  return c.isGC;
-+}
-+
-+function okOnStack(c)
-+{
-+  function calculate()
-+  {
-+    if (!isGC(c))
-+      return true;
-+
-+    // GC classes which are annotated with "NS_okOnStack" are acceptable
-+    // on the stack
-+    if (hasAttribute(c, "NS_okOnStack"))
-+      return true;
-+
-+    for each (let member in c.members) {
-+      if (member.isFunction)
-+	continue;
-+
-+      let type = member.type;
-+      while (true) {
-+	if (type.isArray) {
-+	  type = type.type;
-+	  continue;
-+	}
-+
-+	if (type.typedef) {
-+	  type = type.typedef;
-+	  continue;
-+	}
-+	break;
-+      }
-+
-+      if (type === undefined) {
-+	warning("incomplete type for member " + member + ".", member.loc);
-+	continue;
-+      }
-+
-+      if (type.isPointer || type.isReference)
-+	continue;
-+
-+      if (!type.kind || (type.kind != 'class' && type.kind != 'struct'))
-+	continue;
-+
-+      if (!okOnStack(type))
-+	return false;
-+    }
-+
-+    for each (let b in c.bases)
-+      if (!okOnStack(b))
-+	return false;
-+
-+    if (hasAttribute(c, "NS_GC"))
-+      return false;
-+
-+    return true;
-+  }
-+
-+  if (c.isIncomplete)
-+    throw Error("Can't get ok-on-stack status of incomplete class.");
-+
-+  if (!c.hasOwnProperty('okOnStack'))
-+    c.okOnStack = calculate();
-+
-+  return c.okOnStack;
-+}
-+
 +/**
 + * is the left-most base of this class (exluding XPCOMGCFinalizedObject)
 + * MMgc::GCFinalizable?
 + */
 +function leftmostFinalizable(c)
 +{
 +  function calculate()
 +  {
@@ -247,62 +113,747 @@ new file mode 100644
 +  }
 +
 +  if (!c.hasOwnProperty('isXPCOMGCFinalizedObject'))
 +    c.isXPCOMGCFinalizedObject = calculate();
 +
 +  return c.isXPCOMGCFinalizedObject;
 +}
 +
-+function isBaseOf(child, parent)
-+{
-+  if (!child.bases)
-+    return false;
-+
-+  for each (let base in child.bases)
-+    if (base === parent)
-+      return true;
-+
-+  return false;
-+}
-+
 +function process_function(f, stmts)
 +{
 +  var stmt;
 +  function getLocation()
 +  {
 +    if (stmt.loc)
 +      return stmt.loc;
 +    return f.loc;
 +  }
 +
 +  function processVar(v)
 +  {
 +    // v.fieldOf.type is a struct/class if this is a stack constructor
 +    // it's a pointer if this is some other construction technique
 +    if (v.isConstructor) {
-+      if ((!v.fieldOf || !v.fieldOf.type.isPointer) &&
-+	  !okOnStack(v.memberOf)) {
++      if ((!v.fieldOf || !v.fieldOf.type.isPointer)) {
 +	if (v.fieldOf &&
 +	    v.fieldOf.fieldOf &&
 +	    v.fieldOf.fieldOf.name == "this") {
 +	  return;
 +	}
-+	error("Constructing GC class " + v.memberOf.name + " on the stack.",
-+	  getLocation());
++        let m = getStorageClass(v.memberOf).blockStack([]);
++        if (m)
++          error("Constructing GC class %s on the stack\n%s".format(v.memberOf.name, m),
++	    getLocation());
 +      }
 +    }
 +  }
 +  for each (stmt in stmts)
 +    iter(processVar, stmt.statements);
 +}
+diff --git a/xpcom/analysis/stack.js b/xpcom/analysis/stack.js
+--- a/xpcom/analysis/stack.js
++++ b/xpcom/analysis/stack.js
+@@ -1,73 +1,5 @@ include("gcc_util.js");
+ include("gcc_util.js");
+ include("unstable/lazy_types.js");
+-
+-function process_type(c)
+-{
+-  if ((c.kind == 'class' || c.kind == 'struct') &&
+-      !c.isIncomplete)
+-    isStack(c);
+-}
+-
+-function isStack(c)
+-{
+-  function calculate()
+-  {
+-    if (hasAttribute(c, 'NS_stack'))
+-      return true;
+-
+-    for each (let base in c.bases)
+-      if (isStack(base))
+-        return true;
+-
+-    for each (let member in c.members) {
+-      if (member.isFunction)
+-        continue;
+-
+-      let type = member.type;
+-      while (true) {
+-        if (type === undefined)
+-          break;
+-
+-        if (type.isArray) {
+-          type = type.type;
+-          continue;
+-        }
+-
+-        if (type.typedef) {
+-          if (hasAttribute(type, 'NS_stack'))
+-            return true;
+-
+-          type = type.typedef;
+-          continue;
+-        }
+-        break;
+-      }
+-
+-      if (type === undefined) {
+-        warning("incomplete type for member " + member + ".", member.loc);
+-        continue;
+-      }
+-
+-      if (type.isPointer || type.isReference)
+-        continue;
+-
+-      if (!type.kind || (type.kind != 'class' && type.kind != 'struct'))
+-        continue;
+-
+-      if (isStack(type))
+-        return true;
+-    }
+-    return false;
+-  }
+-
+-  if (c.isIncomplete)
+-    throw Error("Can't get stack property for incomplete type.");
+-
+-  if (!c.hasOwnProperty('isStack'))
+-    c.isStack = calculate();
+-
+-  return c.isStack;
+-}
+ 
+ function isVoidPtr(t)
+ {
+@@ -95,18 +27,18 @@ function operator_new_assign(stmt)
+       let name = IDENTIFIER_POINTER(nameid);
+       if (name == "operator new" || name == "operator new []") {
+         // if this is placement-new, ignore it (should we issue a warning?)
+-        let fncallobj = dehydra_convert(TREE_TYPE(fncall));
+-        if (fncallobj.parameters.length == 2 &&
+-            isVoidPtr(fncallobj.parameters[1]))
+-          return null;
++        let fncallobj = dehydra_convert(fncall);
++        if (fncallobj.type.parameters.length == 2 &&
++            isVoidPtr(fncallobj.type.parameters[1]))
++          return [null, null];
+ 
+-        return varDecl;
++        return [varDecl, fncallobj];
+       }
+     }
+   }
+   catch (e if e.TreeCheckError) { }
+ 
+-  return null;
++  return [null, null];
+ }
+ 
+ function process_tree(fndecl)
+@@ -129,7 +61,7 @@ function process_tree(fndecl)
+       return location_of(DECL_SAVED_TREE(fndecl));
+     }
+     
+-    function check_opnew_assignment(varDecl, stmt)
++    function check_opnew_assignment(varDecl, fncallobj, stmt)
+     {
+       if (TREE_CODE(stmt) != GIMPLE_MODIFY_STMT) {
+         warning("operator new not followed by a GIMPLE_MODIFY_STMT: " + TREE_CODE(stmt), getLocation(stmt));
+@@ -151,9 +83,70 @@ function process_tree(fndecl)
+         return;
+       }
+       destType = destType.type;
++      if (destType.kind === undefined ||
++          (destType.kind != 'struct' &&
++           destType.kind != 'class'))
++        return;
+ 
+-      if (isStack(destType))
+-        error("constructed object of type '" + destType.name + "' not on the stack.", getLocation(stmt));
++      // Figure out what operator new is being called and determine whether
++      // it's a mallocation, GCAllocation, or GCFinalizedAllocation
++      let allocType;
++      if (fncallobj.memberOf === undefined) {
++        allocType = 'malloc';
++      }
++      else {
++        let cx = fncallobj.memberOf;
++        if (cx.name == 'MMgc::GCObject' ||
++            cx.name == 'XPCOMGCObject') {
++          allocType = 'gc';
++        }
++        else if (cx.name == 'MMgc::GCFinalizedObject' ||
++                 cx.name == 'XPCOMGCFinalizedObject') {
++          allocType = 'gcfinalized';
++        }
++        else if (cx.name == 'MMgc::GCRoot') {
++          // TODO: think about this more
++          allocType = 'gcfinalized';
++        }
++        else if (hasAttribute(fncallobj, 'NS_mallocator')) {
++          allocType = 'malloc';
++        }
++        else if (hasAttribute(fncallobj, 'NS_gcallocator')) {
++          allocType = 'gc';
++        }
++        else if (hasAttribute(fncallobj, 'NS_gcfinalizedallocator')) {
++          allocType = 'gcfinalized';
++        }
++        else {
++          error("Call to un-annotated %s\n%s:   declared here".format(fncallobj.name, fncallobj.loc), getLocation(stmt));
++          return;
++        }
++      }
++
++      let m;
++      let sc = getStorageClass(destType);
++      switch (allocType) {
++      case 'malloc':
++        m = sc.blockMalloc([]);
++        if (m)
++          error("constructed object of type %s on the malloc heap\n%s".format(destType.name, m), getLocation(stmt));
++        break;
++      case 'gc':
++        m = sc.blockGC([]);
++        if (m)
++          error("constructed object of type %s on the GC heap\n%s".format(destType.name, m), getLocation(stmt));
++        m = sc.canSkipFinalization([]);
++        if (m)
++          error("constructed object of type %s without finalization\n%s".format(destType.name, m), getLocation(stmt));
++        break;
++      case 'gcfinalized':
++        m = sc.blockGC([]);
++        if (m)
++          error("constructed object of type %s on the GC heap\n%s".format(destType.name, m), getLocation(stmt));
++        break;
++      default:
++        throw Error("analysis error: allocType was unrecognized value '%s'".format(allocType));
++      }
+     }
+ 
+     if (TREE_CODE(t) != STATEMENT_LIST)
+@@ -162,11 +155,12 @@ function process_tree(fndecl)
+     // if we find a tuple of "operator new" / GIMPLE_MODIFY_STMT casting
+     // the result of operator new to a pointer type
+     let opnew = null;
++    let fncallobj;
+     for (let stmt in iter_statement_list(t)) {
+       if (opnew != null)
+-        check_opnew_assignment(opnew, stmt);
++        check_opnew_assignment(opnew, fncallobj, stmt);
+ 
+-      opnew = operator_new_assign(stmt);
++      [opnew, fncallobj] = operator_new_assign(stmt);
+     }
+ 
+     if (opnew != null)
+diff --git a/xpcom/analysis/static-checking.js b/xpcom/analysis/static-checking.js
+--- a/xpcom/analysis/static-checking.js
++++ b/xpcom/analysis/static-checking.js
+@@ -1,3 +1,5 @@
++require({ version: '1.8' });
++
+ /**
+  * A script for GCC-dehydra to analyze the Mozilla codebase and catch
+  * patterns that are incorrect, but which cannot be detected by a compiler. */
+@@ -10,6 +12,65 @@ function treehydra_enabled() {
+   return this.hasOwnProperty('TREE_CODE');
+ }
+ 
++String.prototype.format = function string_format() {
++  // there are two modes of operation... unnamed indices are read in order;
++  // named indices using %(name)s. The two styles cannot be mixed.
++  // Unnamed indices can be passed as either a single argument to this function,
++  // multiple arguments to this function, or as a single array argument
++  let curindex = 0;
++  let d;
++  
++  if (arguments.length > 1) {
++    d = arguments;
++  }
++  else
++    d = arguments[0];
++  
++  function r(s, key, type) {
++    let v;
++    if (key == "") {
++      if (curindex == -1)
++        throw Error("Cannot mix named and positional indices in string formatting.");
++
++      if (curindex == 0 && (!(d instanceof Object) || !(0 in d))) {
++        v = d;
++      }
++      else if (!(curindex in d))
++        throw Error("Insufficient number of items in format, requesting item %i".format(curindex));
++      else {
++        v = d[curindex];
++      }
++
++      ++curindex;
++    }
++    else {
++      key = key.slice(1, -1);
++      if (curindex > 0)
++        throw Error("Cannot mix named and positional indices in string formatting.");
++      curindex = -1;
++      
++      if (!(key in d))
++        throw Error("Key '%s' not present during string substitution.".format(key));
++      v = d[key];
++    }
++    switch (type) {
++    case "s":
++      return v.toString();
++    case "r":
++      return uneval(v);
++    case "i":
++      return parseInt(v);
++    case "f":
++      return Number(v);
++    case "%":
++      return "%";
++    default:
++      throw Error("Unexpected format character '%s'.".format(type));
++    }
++  }
++  return this.replace(/%(\([^)]+\))?(.)/g, r);
++};
++               
+ include('unstable/getopt.js');
+ [options, args] = getopt();
+ 
+@@ -43,19 +104,26 @@ function process_type(c)
+       module.process_type(c);
+ }
+ 
++function hasAnyAttribute(c, attrlist)
++{
++  if (c.attributes === undefined)
++    return false;
++  
++  for each (let attr in c.attributes) {
++    if (attr.name == 'user' && attrlist.some(function(v) {
++      return v == attr.value[0];
++    }))
++      return attr.value[0];
++  }
++  
++  return false;
++}
++
+ function hasAttribute(c, attrname)
+ {
+-  var attr;
++  return hasAnyAttribute(c, [attrname]) != false;
++}
+ 
+-  if (c.attributes === undefined)
+-    return false;
+-
+-  for each (attr in c.attributes)
+-    if (attr.name == 'user' && attr.value[0] == attrname)
+-      return true;
+-
+-  return false;
+-}
+ function process_function(f, stmts)
+ {
+   for each (let module in modules)
+@@ -83,3 +151,384 @@ function input_end()
+     if (module.hasOwnProperty('input_end'))
+       module.input_end();
+ }
++
++function StorageReason(loc, message, prev)
++{
++  this.loc = loc;
++  this.message = message;
++  this.prev = prev;
++}
++StorageReason.prototype.toString = function()
++{
++  let loc = this.loc;
++  if (loc === undefined)
++    loc = "<unknown location>";
++  
++  let str = "%s:   %s".format(loc, this.message);
++  if (this.prev)
++    str += "\n%s".format(this.prev);
++  return str;
++};
++
++/**
++ * The various Storage types have a common interface:
++ * blockStack(stack)
++ * blockGlobal(stack)
++ * blockMalloc(stack)
++ * blockGC(stack)
++ * safeFinalizer(stack)
++ * canSkipFinalization(stack)
++ * 
++ * Each of these returns null if everything's ok, or a StorageReason
++ * 
++ * Any sub-types are checked against the type stack to ensure we don't enter
++ * an iloop checking pointer classes.
++ */
++function checkstack(stack, i)
++{
++  return stack.some(function(si) si === i);
++}
++
++ValueStorageClass =  {
++  blockStack: function() { return null; },
++  blockGlobal: function() { return null; },
++  blockMalloc: function() { return null; },
++  blockGC: function() { return null; },
++  safeFinalizer: function() { return null; },
++  canSkipFinalization: function() { return null; },
++  toString: function() { return "{ValueStorageClass)"; }
++};
++
++function PointerStorageClass(type)
++{
++  this._type = type;
++}
++PointerStorageClass.prototype = {
++  blockNotManaged: function(stack) {
++    if (checkstack(stack, this._type.type))
++      return null;
++
++    stack = stack.concat(this._type);
++    let subtypeClass = getStorageClass(this._type.type, stack);
++    if (!subtypeClass.blockGC(stack)) {
++      let reason = subtypeClass.blockMalloc(stack);
++      if (reason)
++        return new StorageReason(this._type.type.loc, "pointer to", reason);
++    }
++    return null;
++  },
++  
++  blockStack: function() { return null; },
++  blockGlobal: function(stack) { return this.blockNotManaged(stack); },
++  blockMalloc: function(stack) { return this.blockNotManaged(stack); },
++  blockGC: function() { return null; },
++  safeFinalizer: function() { return null; },
++  canSkipFinalization: function() { return null; },
++  toString: function() { return "PointerStorageClass(%s)".format(this._type); }
++};
++
++function ArrayStorageClass(type)
++{
++  this._type = type;
++}
++ArrayStorageClass.prototype = {
++  /* Just forward everything to the inner... */
++  _forward: function(fname, stack) {
++    stack = stack.concat(this._type);
++    return getStorageClass(this._type.type)[fname](stack);
++  },
++  blockStack: function(stack) {
++    return this._forward('blockStack', stack);
++  },
++  blockGlobal: function(stack) {
++    return this._forward('blockGlobal', stack);
++  },
++  blockMalloc: function(stack) {
++    return this._forward('blockMalloc', stack);
++  },
++  blockGC: function(stack) {
++    return this._forward('blockGC', stack);
++  },
++  safeFinalizer: function(stack) {
++    return this._forward('safeFinalizer', stack);
++  },
++  canSkipFinalization: function(stack) {
++    return this._forward('canSkipFinalization', stack);
++  },
++  toString: function() { return "ArrayStorageClass(%s)".format(this._type); }
++};
++
++function TypedefStorageClass(type)
++{
++  this._type = type;
++}
++TypedefStorageClass.prototype = {
++  _forward: function(fname, stack) {
++    stack = stack.concat(this._type);
++    return getStorageClass(this._type.typedef)[fname](stack);
++  },
++  
++  blockStack: function(stack) {
++    let m = hasAnyAttribute(this._type, ["NS_GC"]);
++    if (m != false)
++      return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
++    return this._forward('blockStack', stack);
++  },
++  
++  blockGlobal: function(stack) {
++    let m = hasAnyAttribute(this._type, ["NS_stack", "NS_managed", "NS_GC"]);
++    if (m != false)
++      return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
++    return this._forward('blockGlobal', stack);
++  },
++  
++  blockMalloc: function(stack) {
++    let m = hasAnyAttribute(this._type, ["NS_stack", "NS_managed", "NS_GC"]);
++    if (m != false)
++      return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
++    return this._forward('blockMalloc', stack);
++  },
++  
++  blockGC: function(stack) {
++    let m = hasAnyAttribute(this._type, ["NS_stack", "NS_NoGC"]);
++    if (m != false)
++      return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
++    return this._forward('blockGC', stack);
++  },
++  
++  safeFinalizer: function(stack) {
++    return this._forward('safeFinalizer', stack);
++  },
++  canSkipFinalization: function(stack) {
++    return this._forward('canSkipFinalization', stack);
++  },
++  toString: function() { return "TypedefStorageClass(%s)".format(this._type); }
++};
++
++/**
++ * Does a class have a declared destructor?
++ */
++function hasOwnDestructor(c)
++{
++  for each (let member in c.members) {
++    // dehydra version
++    if (member.isFunction && /::~/(member.name))
++      return true;
++    
++    // treehydra version
++    if (member.isDestructor &&
++        !DECL_ARTIFICIAL(member._type))
++      return true;
++  }
++
++  return false;
++}
++
++function StructStorageClass(type)
++{
++  this._type = type;
++}
++StructStorageClass.prototype = {
++  /**
++   * @returns false or a StorageReason
++   * @note This function does not recurse, it's only for local use
++   */
++  isManaged: function() {
++    let m = hasAnyAttribute(this._type, ["NS_GC", "NS_managed"]);
++    if (m != false)
++      return new StorageReason(this._type.loc, "Class %s is annotated %s".format(this._type.name, m));
++    
++    if (this._type.name == 'MMgc::GCObject' ||
++        this._type.name == 'MMgc::GCFinalizable' ||
++        this._type.name == 'XPCOMGCObject' ||
++        this._type.name == 'XPCOMGCFinalizedObject')
++      return new StorageReason(this._type.loc, "Class %s is considered managed.".format(this._type.name));
++    
++    return null;
++  },
++  
++  iterItems: function(fname, stack) {
++    if (this._type.isIncomplete)
++      return null;
++
++    stack = stack.concat(this._type);
++
++    for each (let base in this._type.bases) {
++      let r = getStorageClass(base)[fname](stack);
++      if (r != null)
++        return new StorageReason(this._type.loc, "class %s is a base of %s".format([base.name, this._type.name]), r);
++    }
++    for each (let member in this._type.members) {
++      if (member.isFunction || member.isStatic)
++        continue;
++      
++      let r = getStorageClass(member)[fname](stack);
++      if (r != null)
++        return new StorageReason(member.loc, "due to member %s".format(member.name), r);
++    }
++    return null;
++  },
++
++  blockStack: function(stack) {
++    let m = hasAnyAttribute(this._type, ["NS_GC"]);
++    if (m != false)
++      return new StorageReason(this._type.loc, "%s %s marked as %s".format(this._type.kind, this._type.name, m));
++    return this.iterItems('blockStack', stack);
++  },
++  
++  blockGlobal: function(stack) {
++    let m = hasAnyAttribute(this._type, ["NS_stack"]);
++    if (m != false)
++      return new StorageReason(this._type.loc, "%s %s marked as %s".format(this._type.kind, this._type.name, m));
++
++    m = this.isManaged();
++    if (m != null)
++      return m;
++
++    return this.iterItems('blockGlobal', stack);
++  },
++  
++  blockMalloc: function(stack) {
++    let m = hasAnyAttribute(this._type, ["NS_stack"]);
++    if (m != false) {
++      return new StorageReason(this._type.loc, "%s %s marked as %s".format(this._type.kind, this._type.name, m));
++    }
++    
++    m = this.isManaged();
++    if (m != null)
++      return m;
++    
++    return this.iterItems('blockMalloc', stack);
++  },
++  
++  blockGC: function(stack) {
++    let m = hasAnyAttribute(this._type, ["NS_NoGC"]);
++    if (m != false)
++      return new StorageReason(this._type.loc, "%s %s marked as %s".format(this._type.kind, this._type.name, m));
++    
++    return this.iterItems('blockGC', stack);
++  },
++  
++  safeFinalizer: function(stack) {
++    let m = this.iterItems('safeFinalizer', stack);
++    if (m != null)
++      return m;
++
++    m = hasAnyAttribute(this._type, ["NS_GCOK"]);
++    if (m != false)
++      return null;
++
++    m = this.isManaged();
++    if (m != null)
++      return null; // Managed classes are finalizer-safe
++
++    if (hasOwnDestructor(this._type))
++      return new StorageReason(this._type.loc, "%s %s not annotated for as GC and has a destructor.".format(this._type.kind, this._type.name));
++    
++    // If this class doesn't have a destructor, it's finalizer-safe.
++    // Implicit destructors are dealt with above in iterItems
++    return null;
++  },
++  
++  canSkipFinalization: function(stack) {
++    let m = this.iterItems('canSkipFinalization', stack);
++    if (m != null)
++      return m;
++    
++    if (hasAttribute(this._type, 'NS_finalizer_not_required'))
++      return null;
++
++    if (this._type.name == 'MMgc::GCObject' ||
++        this._type.name == 'XPCOMGCObject')
++      return null;
++    
++    if (hasOwnDestructor(this._type))
++      return new StorageReason(this._type.loc, "%s %s has a destructor and is not annotated NS_FINALIZER_NOT_REQUIRED.".format(this._type.kind, this._type.name));
++
++    return null;
++  },
++  toString: function() { return "StructStorageClass(%s)".format(this._type); }
++};
++
++function DeclStorageClass(decl)
++{
++  this._decl = decl;
++}
++DeclStorageClass.prototype = {
++  blockStack: function(stack) {
++    return getStorageClass(this._decl.type).blockStack(stack);
++  },
++  blockGlobal: function(stack) {
++    if (hasAttribute(this._decl, "NS_unmanaged"))
++      return null;
++    
++    return getStorageClass(this._decl.type).blockGlobal(stack);
++  },
++  blockMalloc: function(stack) {
++    if (hasAttribute(this._decl, "NS_unmanaged"))
++      return null;
++    
++    return getStorageClass(this._decl.type).blockMalloc(stack);
++  },
++  blockGC: function(stack) {
++    return getStorageClass(this._decl.type).blockGC(stack);
++  },
++  safeFinalizer: function(stack) {
++    return getStorageClass(this._decl.type).safeFinalizer(stack);
++  },
++  canSkipFinalization: function(stack) {
++    return getStorageClass(this._decl.type).canSkipFinalization(stack);
++  },
++  toString: function() { return "DeclStorageClass(%s)".format(this._type); }
++};
++
++/**
++ * Get the storage class for a type.
++ */
++function getStorageClass(type)
++{
++  if (type.typedef)
++    return new TypedefStorageClass(type);
++  
++  if (type.isPointer || type.isReference)
++    return new PointerStorageClass(type);
++  
++  if (type.isArray)
++    return new ArrayStorageClass(type);
++  
++  if (type.isFunction)
++    throw Error("How did a function decl end up in getStorageClass?");
++
++  if (type.kind) {
++    switch (type.kind) {
++    case "struct":
++    case "class":
++      return new StructStorageClass(type);
++    case "union":
++      // TODO: what the hell should union rules be? For now, bail!
++      return ValueStorageClass;
++    case "enum":
++      return ValueStorageClass;
++    default:
++      throw Error("Unrecognized type.kind '%s'".format(type.kind));
++    }
++  }
++  
++  if (type.parameters) {
++    // this is a function *type*... we can end up here via function pointers
++    return ValueStorageClass;
++  }
++
++  if (type.precision || type.name == 'void') {
++    if (type.type)
++      throw Error("I thought this was a numeric type, but apparently it's not: %s".format(type));
++    
++    return ValueStorageClass;
++  }
++
++  // If we're here, whatever we have is probably a decl...
++  if (type.type === undefined)
++    throw Error("Analysis error: %s should be a decl but doesn't have a type".format(type));
++
++  return new DeclStorageClass(type);
++}
 diff --git a/xpcom/base/nscore.h b/xpcom/base/nscore.h
 --- a/xpcom/base/nscore.h
 +++ b/xpcom/base/nscore.h
-@@ -487,17 +487,37 @@ typedef PRUint32 nsrefcnt;
+@@ -487,17 +487,45 @@ typedef PRUint32 nsrefcnt;
   *
   * NS_STACK_CLASS: a class which must only be instantiated on the stack
   * NS_FINAL_CLASS: a class which may not be subclassed
 + * NS_GC_TYPE    : a class which must be GC-allocated
 + * NS_NOGC_TYPE  : a class which must not be GC-allocated
 + * NS_MAYBEGC    : A type which may be part of a GC object which is not
 + *                 finalized. Classes/structs without explicit or implied
 + *                 destructors (POD types) are automatically assumed to be
@@ -310,33 +861,41 @@ diff --git a/xpcom/base/nscore.h b/xpcom
 + *                 finalizer safe.
 + * NS_MAYBEGCFINALIZED: The destructor of such a type is GC-safe, and is not
 + *                 optional: GC type which has a maybegcfinalized member must
 + *                 be finalized.
   */
  #ifdef NS_STATIC_CHECKING
  #define NS_STACK_CLASS __attribute__((user("NS_stack")))
  #define NS_FINAL_CLASS __attribute__((user("NS_final")))
++#define NS_MANAGED     __attribute__((user("NS_managed")))
  #define NS_GC_TYPE     __attribute__((user("NS_GC")))
  #define NS_NOGC_TYPE   __attribute__((user("NS_NoGC")))
-+#define NS_MAYBEGC     __attribute__((user("NS_MaybeGC")))
-+#define NS_MAYBEGCFINALIZED __attribute__((user("NS_MaybeGCFinalized")))
++#define NS_GCOK        __attribute__((user("NS_GCOK")))
++#define NS_FINALIZER_NOT_REQUIRED __attribute__((user("NS_finalizer_not_required")))
++#define NS_UNMANAGED   __attribute__((user("NS_unmanaged")))
 +#define NS_FINALIZER   __attribute__((user("NS_Finalizer")))
 +#define NS_NO_FINALIZER __attribute__((user("NS_No_Finalizer")))
-+#define NS_OKONSTACK   __attribute__((user("NS_okOnStack")))
++#define NS_MALLOCATOR  __attribute__((user("NS_mallocator")))
++#define NS_GCALLOCATOR __attribute__((user("NS_gcallocator")))
++#define NS_GCFINALIZEDALLOCATOR __attribute__((user("NS_gcfinalizedallocator")))
  #else
  #define NS_STACK_CLASS
  #define NS_FINAL_CLASS
++#define NS_MANAGED
  #define NS_GC_TYPE
  #define NS_NOGC_TYPE
-+#define NS_MAYBEGC
-+#define NS_MAYBEGCFINALIZED
++#define NS_GCOK
++#define NS_FINALIZER_NOT_REQUIRED
++#define NS_UNMANAGED
 +#define NS_FINALIZER
 +#define NS_NO_FINALIZER
-+#define NS_OKONSTACK
++#define NS_MALLOCATOR
++#define NS_GCALLOCATOR
++#define NS_GCFINALIZEDALLOCATOR
  #endif
  
  /**
 diff --git a/xpcom/tests/static-checker/GCInheritance1.cpp b/xpcom/tests/static-checker/GCInheritance1.cpp
 new file mode 100644
 --- /dev/null
 +++ b/xpcom/tests/static-checker/GCInheritance1.cpp
 @@ -0,0 +1,11 @@
@@ -362,20 +921,21 @@ new file mode 100644
 +// left-most base
 +struct A : public XPCOMGCFinalizedObject
 +{
 +};
 diff --git a/xpcom/tests/static-checker/GCNew.cpp b/xpcom/tests/static-checker/GCNew.cpp
 new file mode 100644
 --- /dev/null
 +++ b/xpcom/tests/static-checker/GCNew.cpp
-@@ -0,0 +1,12 @@
+@@ -0,0 +1,13 @@
 +#include "nscore.h"
++#include "nsISupportsUtils.h"
 +
-+struct NS_GC_TYPE A
++struct NS_GC_TYPE A : public XPCOMGCObject
 +{
 +  A();
 +  int i;
 +};
 +
 +A* f()
 +{
 +  return new A();
@@ -488,20 +1048,39 @@ new file mode 100644
 +};
 +
 +void f()
 +{
 +  A a;
 +}
 +
 +} // namespace
+diff --git a/xpcom/tests/static-checker/GCPointersManaged.cpp b/xpcom/tests/static-checker/GCPointersManaged.cpp
+new file mode 100644
+--- /dev/null
++++ b/xpcom/tests/static-checker/GCPointersManaged.cpp
+@@ -0,0 +1,14 @@
++#include "nscore.h"
++
++struct NS_GC_TYPE A;
++
++struct B
++{
++  // pointers to GC types imply that class B is a managed type
++  A *a;
++};
++
++void f()
++{
++  B *b = new B();
++}
 diff --git a/xpcom/tests/static-checker/Makefile.in b/xpcom/tests/static-checker/Makefile.in
 --- a/xpcom/tests/static-checker/Makefile.in
 +++ b/xpcom/tests/static-checker/Makefile.in
-@@ -41,11 +41,27 @@ srcdir = @srcdir@
+@@ -41,11 +41,28 @@ srcdir = @srcdir@
  srcdir = @srcdir@
  VPATH = @srcdir@
  
 +MODULE = xpcom
 +
  # MAKE_DIRS: List of directories to build while looping over directories.
  MAKE_DIRS               += $(MDDEPDIR)
  GARBAGE_DIRS            += $(MDDEPDIR)
@@ -509,53 +1088,35 @@ diff --git a/xpcom/tests/static-checker/
  include $(DEPTH)/config/autoconf.mk
 +
 +GC_FAILURE_TESTCASES = \
 +  GCNotStack.cpp \
 +  GCNotStack-constructor2.cpp \
 +  GCNotStack-return.cpp \
 +  GCInheritance1.cpp \
 +  GCInheritance2.cpp \
++  GCPointersManaged.cpp \
 +  $(NULL)
 +
 +GC_PASS_TESTCASES = \
 +  GCNew.cpp \
 +  GCNotStack-constructor.cpp \
 +  GCNotStack-static.cpp \
 +  $(NULL)
  
  FINAL_FAILURE_TESTCASES = \
    TestFinal.cpp \
-@@ -57,6 +73,7 @@ STACK_FAILURE_TESTCASES = \
-   TestStackTemplate.cpp \
-   StackNoConstructor.cpp \
-   StackVector.cpp \
-+  TestStackCOMPtr.cpp \
-   $(NULL)
- 
- STACK_PASS_TESTCASES = \
 @@ -99,6 +116,7 @@ OUTPARAMS_PASS_TESTCASES = \
    $(NULL)
  
  STATIC_FAILURE_TESTCASES = \
 +  $(GC_FAILURE_TESTCASES) \
    $(FINAL_FAILURE_TESTCASES) \
    $(STACK_FAILURE_TESTCASES) \
    $(NULL)
 @@ -110,6 +128,7 @@ STATIC_PASS_TESTCASES = \
  STATIC_PASS_TESTCASES = \
    $(OUTPARAMS_PASS_TESTCASES) \
    $(STACK_PASS_TESTCASES) \
 +  $(GC_PASS_TESTCASES) \
    $(NULL)
  
  include $(topsrcdir)/config/rules.mk
-diff --git a/xpcom/tests/static-checker/TestStackCOMPtr.cpp b/xpcom/tests/static-checker/TestStackCOMPtr.cpp
-new file mode 100644
---- /dev/null
-+++ b/xpcom/tests/static-checker/TestStackCOMPtr.cpp
-@@ -0,0 +1,6 @@
-+#include "nsCOMPtr.h"
-+
-+void TestFunc()
-+{
-+  nsCOMPtr<nsISupports> f = nsnull;
-+}
deleted file mode 100644
--- a/static-check-storage-classes
+++ /dev/null
@@ -1,671 +0,0 @@
-Storage-class analysis is getting more precise. See http://wiki.mozilla.org/XPCOMGC/Static_Checker for some readable descriptions.
-
-diff --git a/xpcom/analysis/stack.js b/xpcom/analysis/stack.js
---- a/xpcom/analysis/stack.js
-+++ b/xpcom/analysis/stack.js
-@@ -1,73 +1,5 @@ include("gcc_util.js");
- include("gcc_util.js");
- include("unstable/lazy_types.js");
--
--function process_type(c)
--{
--  if ((c.kind == 'class' || c.kind == 'struct') &&
--      !c.isIncomplete)
--    isStack(c);
--}
--
--function isStack(c)
--{
--  function calculate()
--  {
--    if (hasAttribute(c, 'NS_stack'))
--      return true;
--
--    for each (let base in c.bases)
--      if (isStack(base))
--        return true;
--
--    for each (let member in c.members) {
--      if (member.isFunction)
--        continue;
--
--      let type = member.type;
--      while (true) {
--        if (type === undefined)
--          break;
--
--        if (type.isArray) {
--          type = type.type;
--          continue;
--        }
--
--        if (type.typedef) {
--          if (hasAttribute(type, 'NS_stack'))
--            return true;
--
--          type = type.typedef;
--          continue;
--        }
--        break;
--      }
--
--      if (type === undefined) {
--        warning("incomplete type for member " + member + ".", member.loc);
--        continue;
--      }
--
--      if (type.isPointer || type.isReference)
--        continue;
--
--      if (!type.kind || (type.kind != 'class' && type.kind != 'struct'))
--        continue;
--
--      if (isStack(type))
--        return true;
--    }
--    return false;
--  }
--
--  if (c.isIncomplete)
--    throw Error("Can't get stack property for incomplete type.");
--
--  if (!c.hasOwnProperty('isStack'))
--    c.isStack = calculate();
--
--  return c.isStack;
--}
- 
- function isVoidPtr(t)
- {
-@@ -95,18 +27,18 @@ function operator_new_assign(stmt)
-       let name = IDENTIFIER_POINTER(nameid);
-       if (name == "operator new" || name == "operator new []") {
-         // if this is placement-new, ignore it (should we issue a warning?)
--        let fncallobj = dehydra_convert(TREE_TYPE(fncall));
--        if (fncallobj.parameters.length == 2 &&
--            isVoidPtr(fncallobj.parameters[1]))
--          return null;
-+        let fncallobj = dehydra_convert(fncall);
-+        if (fncallobj.type.parameters.length == 2 &&
-+            isVoidPtr(fncallobj.type.parameters[1]))
-+          return [null, null];
- 
--        return varDecl;
-+        return [varDecl, fncallobj];
-       }
-     }
-   }
-   catch (e if e.TreeCheckError) { }
- 
--  return null;
-+  return [null, null];
- }
- 
- function process_tree(fndecl)
-@@ -129,7 +61,7 @@ function process_tree(fndecl)
-       return location_of(DECL_SAVED_TREE(fndecl));
-     }
-     
--    function check_opnew_assignment(varDecl, stmt)
-+    function check_opnew_assignment(varDecl, fncallobj, stmt)
-     {
-       if (TREE_CODE(stmt) != GIMPLE_MODIFY_STMT) {
-         warning("operator new not followed by a GIMPLE_MODIFY_STMT: " + TREE_CODE(stmt), getLocation(stmt));
-@@ -151,9 +83,69 @@ function process_tree(fndecl)
-         return;
-       }
-       destType = destType.type;
-+      if (destType.kind === undefined ||
-+          (destType.kind != 'struct' &&
-+           destType.kind != 'class'))
-+        return;
- 
--      if (isStack(destType))
--        error("constructed object of type '" + destType.name + "' not on the stack.", getLocation(stmt));
-+      // Figure out what operator new is being called and determine whether
-+      // it's a mallocation, GCAllocation, or GCFinalizedAllocation
-+      let allocType;
-+      if (fncallobj.memberOf === undefined) {
-+        allocType = 'malloc';
-+      }
-+      else {
-+        let cx = fncallobj.memberOf;
-+        if (cx.name == 'MMgc::GCObject' ||
-+            cx.name == 'XPCOMGCObject') {
-+          allocType = 'gc';
-+        }
-+        else if (cx.name == 'MMgc::GCFinalizedObject' ||
-+                 cx.name == 'XPCOMGCFinalizedObject') {
-+          allocType = 'gcfinalized';
-+        }
-+        else if (cx.name == 'MMgc::GCRoot') {
-+          allocType = 'malloc';
-+        }
-+        else if (hasAttribute(fncallobj, 'NS_mallocator')) {
-+          allocType = 'malloc';
-+        }
-+        else if (hasAttribute(fncallobj, 'NS_gcallocator')) {
-+          allocType = 'gc';
-+        }
-+        else if (hasAttribute(fncallobj, 'NS_gcfinalizedallocator')) {
-+          allocType = 'gcfinalized';
-+        }
-+        else {
-+          error("Call to un-annotated %s\n%s:   declared here".format(fncallobj.name, fncallobj.loc), getLocation(stmt));
-+          return;
-+        }
-+      }
-+
-+      let m;
-+      let sc = getStorageClass(destType);
-+      switch (allocType) {
-+      case 'malloc':
-+        m = sc.blockMalloc();
-+        if (m)
-+          error("constructed object of type %s on the malloc heap\n%s".format(destType.name, m), getLocation(stmt));
-+        break;
-+      case 'gc':
-+        m = sc.blockGC();
-+        if (m)
-+          error("constructed object of type %s on the GC heap\n%s".format(destType.name, m), getLocation(stmt));
-+        m = sc.canSkipFinalization();
-+        if (m)
-+          error("constructed object of type %s without finalization\n%s".format(destType.name, m), getLocation(stmt));
-+        break;
-+      case 'gcfinalized':
-+        m = sc.blockGC();
-+        if (m)
-+          error("constructed object of type %s on the GC heap\n%s".format(destType.name, m), getLocation(stmt));
-+        break;
-+      default:
-+        throw Error("analysis error: allocType was unrecognized value '%s'".format(allocType));
-+      }
-     }
- 
-     if (TREE_CODE(t) != STATEMENT_LIST)
-@@ -162,11 +154,12 @@ function process_tree(fndecl)
-     // if we find a tuple of "operator new" / GIMPLE_MODIFY_STMT casting
-     // the result of operator new to a pointer type
-     let opnew = null;
-+    let fncallobj;
-     for (let stmt in iter_statement_list(t)) {
-       if (opnew != null)
--        check_opnew_assignment(opnew, stmt);
-+        check_opnew_assignment(opnew, fncallobj, stmt);
- 
--      opnew = operator_new_assign(stmt);
-+      [opnew, fncallobj] = operator_new_assign(stmt);
-     }
- 
-     if (opnew != null)
-diff --git a/xpcom/analysis/static-checking.js b/xpcom/analysis/static-checking.js
---- a/xpcom/analysis/static-checking.js
-+++ b/xpcom/analysis/static-checking.js
-@@ -10,6 +10,65 @@ function treehydra_enabled() {
-   return this.hasOwnProperty('TREE_CODE');
- }
- 
-+String.prototype.format = function string_format() {
-+  // there are two modes of operation... unnamed indices are read in order;
-+  // named indices using %(name)s. The two styles cannot be mixed.
-+  // Unnamed indices can be passed as either a single argument to this function,
-+  // multiple arguments to this function, or as a single array argument
-+  let curindex = 0;
-+  let d;
-+  
-+  if (arguments.length > 1) {
-+    d = arguments;
-+  }
-+  else
-+    d = arguments[0];
-+  
-+  function r(s, key, type) {
-+    let v;
-+    if (key == "") {
-+      if (curindex == -1)
-+        throw Error("Cannot mix named and positional indices in string formatting.");
-+
-+      if (curindex == 0 && (!(d instanceof Object) || !(0 in d))) {
-+        v = d;
-+      }
-+      else if (!(curindex in d))
-+        throw Error("Insufficient number of items in format, requesting item %i".format(curindex));
-+      else {
-+        v = d[curindex];
-+      }
-+
-+      ++curindex;
-+    }
-+    else {
-+      key = key.slice(1, -1);
-+      if (curindex > 0)
-+        throw Error("Cannot mix named and positional indices in string formatting.");
-+      curindex = -1;
-+      
-+      if (!(key in d))
-+        throw Error("Key '%s' not present during string substitution.".format(key));
-+      v = d[key];
-+    }
-+    switch (type) {
-+    case "s":
-+      return v.toString();
-+    case "r":
-+      return uneval(v);
-+    case "i":
-+      return parseInt(v);
-+    case "f":
-+      return Number(v);
-+    case "%":
-+      return "%";
-+    default:
-+      throw Error("Unexpected format character '%s'.".format(type));
-+    }
-+  }
-+  return this.replace(/%(\([^)]+\))?(.)/g, r);
-+};
-+               
- include('unstable/getopt.js');
- [options, args] = getopt();
- 
-@@ -43,18 +102,24 @@ function process_type(c)
-       module.process_type(c);
- }
- 
-+function hasAnyAttribute(c, attrlist)
-+{
-+  if (c.attributes === undefined)
-+    return false;
-+  
-+  for each (let attr in c.attributes) {
-+    if (attr.name == 'user' && attrlist.some(function(v) {
-+      return v == attr.value[0];
-+    }))
-+      return attr.value[0];
-+  }
-+  
-+  return false;
-+}
-+
- function hasAttribute(c, attrname)
- {
--  var attr;
--
--  if (c.attributes === undefined)
--    return false;
--
--  for each (attr in c.attributes)
--    if (attr.name == 'user' && attr.value[0] == attrname)
--      return true;
--
--  return false;
-+  return hasAnyAttribute(c, [attrname]) != false;
- }
- function process_function(f, stmts)
- {
-@@ -83,3 +148,307 @@ function input_end()
-     if (module.hasOwnProperty('input_end'))
-       module.input_end();
- }
-+
-+function StorageReason(loc, message, prev)
-+{
-+  this.loc = loc;
-+  this.message = message;
-+  this.prev = prev;
-+}
-+StorageReason.prototype.toString = function()
-+{
-+  let str = "%s:   %s".format(loc_string(this.loc), this.message);
-+  if (this.prev)
-+    str += "\n%s".format(this.prev);
-+  return str;
-+};
-+
-+/**
-+ * The various Storage types have a common interface:
-+ * blockStack()
-+ * blockGlobal()
-+ * blockMalloc()
-+ * blockGC()
-+ * safeFinalizer()
-+ * canSkipFinalization()
-+ * 
-+ * each of these returns null if everything's ok, or a StorageReason
-+ */
-+ValueStorageClass =  {
-+  blockStack: function() { return null; },
-+  blockGlobal: function() { return null; },
-+  blockMalloc: function() { return null; },
-+  blockGC: function() { return null; },
-+  safeFinalizer: function() { return null; },
-+  canSkipFinalization: function() { return null; }
-+};
-+
-+function PointerStorageClass(type)
-+{
-+  this._type = type;
-+}
-+PointerStorageClass.prototype = {
-+  blockNotManaged: function() {
-+    let subtypeClass = getStorageClass(this._type.type);
-+    if (!subtypeClass.blockGC()) {
-+      let reason = subtypeClass.blockMalloc();
-+      if (reason)
-+        return new StorageReason(this._type.loc, "pointer to", reason);
-+    }
-+    return null;
-+  },
-+  
-+  blockStack: function() { return null; },
-+  blockGlobal: function() { return this.checkNotManaged(); },
-+  blockMalloc: function() { return this.checkNotManaged(); },
-+  blockGC: function() { return null; },
-+  safeFinalizer: function() { return null; },
-+  canSkipFinalization: function() { return null; }
-+};
-+
-+function ArrayStorageClass(type)
-+{
-+  this._type = type;
-+}
-+ArrayStorageClass.prototype = {
-+  /* Just forward everything to the inner... */
-+  blockStack: function() { return getStorageClass(this._type).blockStack(); },
-+  blockGlobal: function() { return getStorageClass(this._type).blockGlobal(); },
-+  blockMalloc: function() { return getStorageClass(this._type).blockMalloc(); },
-+  blockGC: function() { return getStorageClass(this._type).blockGC(); },
-+  safeFinalizer: function() { return getStorageClass(this._type).safeFinalizer(); },
-+  canSkipFinalization: function() { return getStorageClass(this._type).canSkipFinalization(); }
-+};
-+
-+function TypedefStorageClass(type)
-+{
-+  this._type = type;
-+}
-+TypedefStorageClass.prototype = {
-+  blockStack: function() {
-+    let m = hasAnyAttribute(this._type, ["NS_GC"]);
-+    if (m != null)
-+      return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
-+    return getStorageClass(this._type).blockStack();
-+  },
-+  
-+  blockGlobal: function() {
-+    let m = hasAnyAttribute(this._type, ["NS_stack", "NS_managed", "NS_GC"]);
-+    if (m != null)
-+      return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
-+    return getStorageClass(this._type).blockGlobal();
-+  },
-+  
-+  blockMalloc: function() {
-+    let m = hasAnyAttribute(this._type, ["NS_stack", "NS_managed", "NS_GC"]);
-+    if (m != null)
-+      return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
-+    return getStorageClass(this._type).blockMalloc();
-+  },
-+  
-+  blockGC: function() {
-+    let m = hasAnyAttribute(this._type, ["NS_stack", "NS_NoGC"]);
-+    if (m != null)
-+      return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
-+    return getStorageClass(this._type).blockGC();
-+  },
-+  
-+  safeFinalizer: function() { return getStorageClass(this._type).safeFinalizer(); },
-+  canSkipFinalization: function() { return getStorageClass(this._type).canSkipFinalization(); }
-+};
-+
-+/**
-+ * Does a class have a declared destructor?
-+ */
-+function hasOwnDestructor(c)
-+{
-+  for (let member in c.members)
-+    if (member.isFunction && /^~/(member.name))
-+      return true;
-+
-+  return false;
-+}
-+
-+function StructStorageClass(type)
-+{
-+  this._type = type;
-+}
-+StructStorageClass.prototype = {
-+  /**
-+   * @returns false or a StorageReason
-+   * @note This function does not recurse, it's only for local use
-+   */
-+  isManaged: function() {
-+    let m = hasAnyAttribute(this._type, ["NS_GC", "NS_managed"]);
-+    if (m != false)
-+      return new StorageReason(this._type.loc, "Class %s is annotated %s".format(this._type.name, m));
-+    
-+    if (this._type.name == 'MMgc::GCObject' ||
-+        this._type.name == 'MMgc::GCFinalizable' ||
-+        this._type.name == 'XPCOMGCObject' ||
-+        this._type.name == 'XPCOMGCFinalizedObject')
-+      return new StorageReason(this._type.loc, "Class %s is considered managed.".format(this._type.name));
-+    
-+    return null;
-+  },
-+  
-+  iterItems: function(fname) {
-+    for each (let base in this._type.bases) {
-+      let r = getStorageClass(base)[fname]();
-+      if (r != null)
-+        return new StorageReason(this._type.loc, "class %s is a base of %s".format([base.name, this._type.name]), r);
-+    }
-+    for each (let member in this._type.members) {
-+      if (member.isFunction || member.isStatic)
-+        continue;
-+      
-+      let r = getStorageClass(member)[fname]();
-+      if (r != null)
-+        return new StorageReason(member.loc, "due to member %s".format(member.name), r);
-+    }
-+    return null;
-+  },
-+
-+  blockStack: function() {
-+    let m = hasAnyAttribute(this._type, ["NS_GC"]);
-+    if (m != false)
-+      return new StorageReason(this._type.loc, "%s %s marked as %s".format(this._type.kind, this._type.name, m));
-+    return this.iterItems('blockStack');
-+  },
-+  
-+  blockGlobal: function() {
-+    let m = hasAnyAttribute(this._type, ["NS_stack"]);
-+    if (m != false)
-+      return new StorageReason(this._type.loc, "%s %s marked as %s".format(this._type.kind, this._type.name, m));
-+
-+    m = this.isManaged();
-+    if (m != null)
-+      return m;
-+
-+    return this.iterItems('blockGlobal');
-+  },
-+  
-+  blockMalloc: function() {
-+    let m = hasAnyAttribute(this._type, ["NS_stack"]);
-+    if (m != false) {
-+      return new StorageReason(this._type.loc, "%s %s marked as %s".format(this._type.kind, this._type.name, m));
-+    }
-+    
-+    m = this.isManaged();
-+    if (m != null)
-+      return m;
-+    
-+    return this.iterItems('blockMalloc');
-+  },
-+  
-+  blockGC: function() {
-+    let m = hasAnyAttribute(this._type, ["NS_NoGC"]);
-+    if (m != false)
-+      return new StorageReason(this._type.loc, "%s %s marked as %s".format(this._type.kind, this._type.name, m));
-+    
-+    return this.iterItems('blockGC');
-+  },
-+  
-+  safeFinalizer: function() {
-+    let m = this.iterItems('safeFinalizer');
-+    if (m != null)
-+      return m;
-+
-+    m = hasAnyAttribute(this._type, ["NS_GCOK"]);
-+    if (m != false)
-+      return null;
-+
-+    m = this.isManaged();
-+    if (m != null)
-+      return null; // Managed classes are finalizer-safe
-+
-+    if (hasOwnDestructor(this._type))
-+      return new StorageReason(this._type.loc, "%s %s not annotated for as GC and has a destructor.".format(this._type.kind, this._type.name));
-+    
-+    // If this class doesn't have a destructor, it's finalizer-safe.
-+    // Implicit destructors are dealt with above in iterItems
-+    return null;
-+  },
-+  
-+  canSkipFinalization: function() {
-+    let m = this.iterItems('canSkipFinalization');
-+    if (m != null)
-+      return m;
-+    
-+    if (hasAttribute(this._type, 'NS_finalizer_not_required'))
-+      return null;
-+
-+    if (hasOwnDestructor(this._type))
-+      return new StorageReason(this._type.loc, "%s %s has a destructor and is not annotated NS_FINALIZER_NOT_REQUIRED.".format(this._type.kind, this._type.name));
-+    
-+    return null;
-+  }
-+};
-+
-+function DeclStorageClass(decl)
-+{
-+  this._decl = decl;
-+  print("Created decl storage class for %s".format(decl));
-+}
-+DeclStorageClass.prototype = {
-+  blockStack: function() { return getStorageClass(this._decl.type).blockStack(); },
-+  blockGlobal: function() {
-+    if (hasAttribute(this._decl, "NS_unmanaged"))
-+      return null;
-+    
-+    return getStorageClass(this._decl.type).blockGlobal();
-+  },
-+  blockMalloc: function() {
-+    if (hasAttribute(this._decl, "NS_unmanaged"))
-+      return null;
-+    
-+    return getStorageClass(this._decl.type).blockMalloc();
-+  },
-+  blockGC: function() { return getStorageClass(this._decl.type).blockGC(); }
-+};
-+
-+/**
-+ * Get the storage class for a type.
-+ */
-+function getStorageClass(type)
-+{
-+  if (type.typedef)
-+    return new TypedefStorageClass(type);
-+  
-+  if (type.isPointer || type.isReference)
-+    return new PointerStorageClass(type);
-+  
-+  if (type.isArray)
-+    return new ArrayStorageClass(type);
-+  
-+  if (type.isFunction)
-+    throw Error("How did a function decl end up in getStorageClass?");
-+
-+  if (type.kind) {
-+    switch (type.kind) {
-+    case "struct":
-+    case "class":
-+      return new StructStorageClass(type);
-+    case "union":
-+      // TODO: what the hell should union rules be? For now, bail!
-+      return ValueStorageClass;
-+    default:
-+      throw Error("Unrecognized type.kind '%s'".format(type.kind));
-+    }
-+  }
-+  
-+  if (type.parameters) {
-+    // this is a function *type*... we can end up here via function pointers
-+    return ValueStorageClass;
-+  }
-+
-+  if (type.precision) {
-+    if (type.type)
-+      throw Error("I thought this was a numeric type, but apparently it's not: %s".format(type));
-+    
-+    return ValueStorageClass;
-+  }
-+  
-+  // If we're here, whatever we have is probably a decl...
-+  return new DeclStorageClass(type);
-+}
-diff --git a/xpcom/base/nscore.h b/xpcom/base/nscore.h
---- a/xpcom/base/nscore.h
-+++ b/xpcom/base/nscore.h
-@@ -501,13 +501,18 @@ typedef PRUint32 nsrefcnt;
- #ifdef NS_STATIC_CHECKING
- #define NS_STACK_CLASS __attribute__((user("NS_stack")))
- #define NS_FINAL_CLASS __attribute__((user("NS_final")))
-+#define NS_MANAGED     __attribute__((user("NS_managed")))
- #define NS_GC_TYPE     __attribute__((user("NS_GC")))
- #define NS_NOGC_TYPE   __attribute__((user("NS_NoGC")))
--#define NS_MAYBEGC     __attribute__((user("NS_MaybeGC")))
--#define NS_MAYBEGCFINALIZED __attribute__((user("NS_MaybeGCFinalized")))
-+#define NS_GCOK        __attribute__((user("NS_GCOK")))
-+#define NS_FINALIZER_NOT_REQUIRED __attribute__((user("NS_finalizer_not_required")))
-+#define NS_UNMANAGED   __attribute__((user("NS_unmanaged")))
- #define NS_FINALIZER   __attribute__((user("NS_Finalizer")))
- #define NS_NO_FINALIZER __attribute__((user("NS_No_Finalizer")))
--#define NS_OKONSTACK   __attribute__((user("NS_okOnStack")))
-+#define NS_MALLOCATOR  __attribute__((user("NS_mallocator")))
-+#define NS_GCALLOCATOR __attribute__((user("NS_gcallocator")))
-+#define NS_GCFINALIZEDALLOCATOR __attribute__((user("NS_gcfinalizedallocator")))
-+#define NS_OKONSTACK /* get rid of me, quick! */
- #else
- #define NS_STACK_CLASS
- #define NS_FINAL_CLASS
-diff --git a/xpcom/tests/static-checker/GCNew.cpp b/xpcom/tests/static-checker/GCNew.cpp
---- a/xpcom/tests/static-checker/GCNew.cpp
-+++ b/xpcom/tests/static-checker/GCNew.cpp
-@@ -1,6 +1,7 @@
- #include "nscore.h"
-+#include "nsISupportsUtils.h"
- 
--struct NS_GC_TYPE A
-+struct NS_GC_TYPE A : public XPCOMGCObject
- {
-   A();
-   int i;
-diff --git a/xpcom/tests/static-checker/Makefile.in b/xpcom/tests/static-checker/Makefile.in
---- a/xpcom/tests/static-checker/Makefile.in
-+++ b/xpcom/tests/static-checker/Makefile.in
-@@ -73,7 +73,6 @@ STACK_FAILURE_TESTCASES = \
-   TestStackTemplate.cpp \
-   StackNoConstructor.cpp \
-   StackVector.cpp \
--  TestStackCOMPtr.cpp \
-   $(NULL)
- 
- STACK_PASS_TESTCASES = \
-diff --git a/xpcom/tests/static-checker/TestStackCOMPtr.cpp b/xpcom/tests/static-checker/TestStackCOMPtr.cpp
-deleted file mode 100644
---- a/xpcom/tests/static-checker/TestStackCOMPtr.cpp
-+++ /dev/null
-@@ -1,6 +0,0 @@
--#include "nsCOMPtr.h"
--
--void TestFunc()
--{
--  nsCOMPtr<nsISupports> f = nsnull;
--}
deleted file mode 100644
--- a/static-check-storage-classes2
+++ /dev/null
@@ -1,651 +0,0 @@
-diff --git a/xpcom/analysis/gc.js b/xpcom/analysis/gc.js
---- a/xpcom/analysis/gc.js
-+++ b/xpcom/analysis/gc.js
-@@ -13,136 +13,6 @@ function process_type(c)
-        c.name != "XPCOMGCFinalizedObject")
-       error("MMgc::GCFinalizable is not the left-most base of class " + c.name + " which inherits from XPCOMGCFinalizedObject", c.loc);
-   }
--}
--
--function isGC(c)
--{
--  function calculate()
--  {
--    if (hasAttribute(c, 'NS_GC') ||
--	c.name == 'MMgc::GCObject' ||
--	c.name == 'MMgc::GCFinalizable' ||
--	c.name == 'XPCOMGCObject' ||
--	c.name == 'XPCOMGCFinalizedObject')
--      return true;
--
--    // nsIFrame is a figment of your imagination!
--    if (c.name == 'nsIFrame')
--      return false;
--
--    for each (let base in c.bases)
--    if (isGC(base, false))
--      return true;
--
--    for each (let member in c.members) {
--      if (member.isFunction)
--	continue;
--
--      let type = member.type;
--      while (true) {
--	if (type.isArray) {
--	  type = type.type;
--	  continue;
--	}
--
--	if (type.typedef) {
--	  if (hasAttribute(type, 'NS_GC'))
--	    return true;
--
--	  type = type.typedef;
--	  continue;
--	}
--	break;
--      }
--
--      if (type === undefined) {
--	warning("incomplete type for member " + member + ".", member.loc);
--	continue;
--      }
--
--      if (type.isPointer || type.isReference)
--	continue;
--
--      if (!type.kind || (type.kind != 'class' && type.kind != 'struct'))
--	continue;
--
--      if (isGC(type, false))
--	return true;
--    }
--    return false;
--  }
--
--  if (c.isIncomplete)
--    throw Error("Can't get GC status of incomplete class.");
--
--  if (!c.hasOwnProperty("isGC"))
--    c.isGC = calculate();
--
--  return c.isGC;
--}
--
--function okOnStack(c)
--{
--  function calculate()
--  {
--    if (!isGC(c))
--      return true;
--
--    // GC classes which are annotated with "NS_okOnStack" are acceptable
--    // on the stack
--    if (hasAttribute(c, "NS_okOnStack"))
--      return true;
--
--    for each (let member in c.members) {
--      if (member.isFunction)
--	continue;
--
--      let type = member.type;
--      while (true) {
--	if (type.isArray) {
--	  type = type.type;
--	  continue;
--	}
--
--	if (type.typedef) {
--	  type = type.typedef;
--	  continue;
--	}
--	break;
--      }
--
--      if (type === undefined) {
--	warning("incomplete type for member " + member + ".", member.loc);
--	continue;
--      }
--
--      if (type.isPointer || type.isReference)
--	continue;
--
--      if (!type.kind || (type.kind != 'class' && type.kind != 'struct'))
--	continue;
--
--      if (!okOnStack(type))
--	return false;
--    }
--
--    for each (let b in c.bases)
--      if (!okOnStack(b))
--	return false;
--
--    if (hasAttribute(c, "NS_GC"))
--      return false;
--
--    return true;
--  }
--
--  if (c.isIncomplete)
--    throw Error("Can't get ok-on-stack status of incomplete class.");
--
--  if (!c.hasOwnProperty('okOnStack'))
--    c.okOnStack = calculate();
--
--  return c.okOnStack;
- }
- 
- /**
-@@ -228,18 +98,6 @@ function isXPCOMGCFinalizedObject(c)
-   return c.isXPCOMGCFinalizedObject;
- }
- 
--function isBaseOf(child, parent)
--{
--  if (!child.bases)
--    return false;
--
--  for each (let base in child.bases)
--    if (base === parent)
--      return true;
--
--  return false;
--}
--
- function process_function(f, stmts)
- {
-   var stmt;
-@@ -255,15 +113,16 @@ function process_function(f, stmts)
-     // v.fieldOf.type is a struct/class if this is a stack constructor
-     // it's a pointer if this is some other construction technique
-     if (v.isConstructor) {
--      if ((!v.fieldOf || !v.fieldOf.type.isPointer) &&
--	  !okOnStack(v.memberOf)) {
-+      if ((!v.fieldOf || !v.fieldOf.type.isPointer)) {
- 	if (v.fieldOf &&
- 	    v.fieldOf.fieldOf &&
- 	    v.fieldOf.fieldOf.name == "this") {
- 	  return;
- 	}
--	error("Constructing GC class " + v.memberOf.name + " on the stack.",
--	  getLocation());
-+        let m = getStorageClass(v.memberOf).blockStack([]);
-+        if (m)
-+          error("Constructing GC class %s on the stack\n%s".format(v.memberOf.name, m),
-+	    getLocation());
-       }
-     }
-   }
-diff --git a/xpcom/analysis/stack.js b/xpcom/analysis/stack.js
---- a/xpcom/analysis/stack.js
-+++ b/xpcom/analysis/stack.js
-@@ -105,7 +105,8 @@ function process_tree(fndecl)
-           allocType = 'gcfinalized';
-         }
-         else if (cx.name == 'MMgc::GCRoot') {
--          allocType = 'malloc';
-+          // TODO: think about this more
-+          allocType = 'gcfinalized';
-         }
-         else if (hasAttribute(fncallobj, 'NS_mallocator')) {
-           allocType = 'malloc';
-@@ -126,20 +127,20 @@ function process_tree(fndecl)
-       let sc = getStorageClass(destType);
-       switch (allocType) {
-       case 'malloc':
--        m = sc.blockMalloc();
-+        m = sc.blockMalloc([]);
-         if (m)
-           error("constructed object of type %s on the malloc heap\n%s".format(destType.name, m), getLocation(stmt));
-         break;
-       case 'gc':
--        m = sc.blockGC();
-+        m = sc.blockGC([]);
-         if (m)
-           error("constructed object of type %s on the GC heap\n%s".format(destType.name, m), getLocation(stmt));
--        m = sc.canSkipFinalization();
-+        m = sc.canSkipFinalization([]);
-         if (m)
-           error("constructed object of type %s without finalization\n%s".format(destType.name, m), getLocation(stmt));
-         break;
-       case 'gcfinalized':
--        m = sc.blockGC();
-+        m = sc.blockGC([]);
-         if (m)
-           error("constructed object of type %s on the GC heap\n%s".format(destType.name, m), getLocation(stmt));
-         break;
-diff --git a/xpcom/analysis/static-checking.js b/xpcom/analysis/static-checking.js
---- a/xpcom/analysis/static-checking.js
-+++ b/xpcom/analysis/static-checking.js
-@@ -1,3 +1,5 @@
-+require({ version: '1.8' });
-+
- /**
-  * A script for GCC-dehydra to analyze the Mozilla codebase and catch
-  * patterns that are incorrect, but which cannot be detected by a compiler. */
-@@ -121,6 +123,7 @@ function hasAttribute(c, attrname)
- {
-   return hasAnyAttribute(c, [attrname]) != false;
- }
-+
- function process_function(f, stmts)
- {
-   for each (let module in modules)
-@@ -157,7 +160,11 @@ function StorageReason(loc, message, pre
- }
- StorageReason.prototype.toString = function()
- {
--  let str = "%s:   %s".format(loc_string(this.loc), this.message);
-+  let loc = this.loc;
-+  if (loc === undefined)
-+    loc = "<unknown location>";
-+  
-+  let str = "%s:   %s".format(loc, this.message);
-   if (this.prev)
-     str += "\n%s".format(this.prev);
-   return str;
-@@ -165,22 +172,31 @@ StorageReason.prototype.toString = funct
- 
- /**
-  * The various Storage types have a common interface:
-- * blockStack()
-- * blockGlobal()
-- * blockMalloc()
-- * blockGC()
-- * safeFinalizer()
-- * canSkipFinalization()
-+ * blockStack(stack)
-+ * blockGlobal(stack)
-+ * blockMalloc(stack)
-+ * blockGC(stack)
-+ * safeFinalizer(stack)
-+ * canSkipFinalization(stack)
-  * 
-- * each of these returns null if everything's ok, or a StorageReason
-+ * Each of these returns null if everything's ok, or a StorageReason
-+ * 
-+ * Any sub-types are checked against the type stack to ensure we don't enter
-+ * an iloop checking pointer classes.
-  */
-+function checkstack(stack, i)
-+{
-+  return stack.some(function(si) si === i);
-+}
-+
- ValueStorageClass =  {
-   blockStack: function() { return null; },
-   blockGlobal: function() { return null; },
-   blockMalloc: function() { return null; },
-   blockGC: function() { return null; },
-   safeFinalizer: function() { return null; },
--  canSkipFinalization: function() { return null; }
-+  canSkipFinalization: function() { return null; },
-+  toString: function() { return "{ValueStorageClass)"; }
- };
- 
- function PointerStorageClass(type)
-@@ -188,22 +204,27 @@ function PointerStorageClass(type)
-   this._type = type;
- }
- PointerStorageClass.prototype = {
--  blockNotManaged: function() {
--    let subtypeClass = getStorageClass(this._type.type);
--    if (!subtypeClass.blockGC()) {
--      let reason = subtypeClass.blockMalloc();
-+  blockNotManaged: function(stack) {
-+    if (checkstack(stack, this._type.type))
-+      return null;
-+
-+    stack = stack.concat(this._type);
-+    let subtypeClass = getStorageClass(this._type.type, stack);
-+    if (!subtypeClass.blockGC(stack)) {
-+      let reason = subtypeClass.blockMalloc(stack);
-       if (reason)
--        return new StorageReason(this._type.loc, "pointer to", reason);
-+        return new StorageReason(this._type.type.loc, "pointer to", reason);
-     }
-     return null;
-   },
-   
-   blockStack: function() { return null; },
--  blockGlobal: function() { return this.checkNotManaged(); },
--  blockMalloc: function() { return this.checkNotManaged(); },
-+  blockGlobal: function(stack) { return this.blockNotManaged(stack); },
-+  blockMalloc: function(stack) { return this.blockNotManaged(stack); },
-   blockGC: function() { return null; },
-   safeFinalizer: function() { return null; },
--  canSkipFinalization: function() { return null; }
-+  canSkipFinalization: function() { return null; },
-+  toString: function() { return "PointerStorageClass(%s)".format(this._type); }
- };
- 
- function ArrayStorageClass(type)
-@@ -212,12 +233,29 @@ function ArrayStorageClass(type)
- }
- ArrayStorageClass.prototype = {
-   /* Just forward everything to the inner... */
--  blockStack: function() { return getStorageClass(this._type).blockStack(); },
--  blockGlobal: function() { return getStorageClass(this._type).blockGlobal(); },
--  blockMalloc: function() { return getStorageClass(this._type).blockMalloc(); },
--  blockGC: function() { return getStorageClass(this._type).blockGC(); },
--  safeFinalizer: function() { return getStorageClass(this._type).safeFinalizer(); },
--  canSkipFinalization: function() { return getStorageClass(this._type).canSkipFinalization(); }
-+  _forward: function(fname, stack) {
-+    stack = stack.concat(this._type);
-+    return getStorageClass(this._type.type)[fname](stack);
-+  },
-+  blockStack: function(stack) {
-+    return this._forward('blockStack', stack);
-+  },
-+  blockGlobal: function(stack) {
-+    return this._forward('blockGlobal', stack);
-+  },
-+  blockMalloc: function(stack) {
-+    return this._forward('blockMalloc', stack);
-+  },
-+  blockGC: function(stack) {
-+    return this._forward('blockGC', stack);
-+  },
-+  safeFinalizer: function(stack) {
-+    return this._forward('safeFinalizer', stack);
-+  },
-+  canSkipFinalization: function(stack) {
-+    return this._forward('canSkipFinalization', stack);
-+  },
-+  toString: function() { return "ArrayStorageClass(%s)".format(this._type); }
- };
- 
- function TypedefStorageClass(type)
-@@ -225,36 +263,46 @@ function TypedefStorageClass(type)
-   this._type = type;
- }
- TypedefStorageClass.prototype = {
--  blockStack: function() {
--    let m = hasAnyAttribute(this._type, ["NS_GC"]);
--    if (m != null)
--      return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
--    return getStorageClass(this._type).blockStack();
-+  _forward: function(fname, stack) {
-+    stack = stack.concat(this._type);
-+    return getStorageClass(this._type.typedef)[fname](stack);
-   },
-   
--  blockGlobal: function() {
--    let m = hasAnyAttribute(this._type, ["NS_stack", "NS_managed", "NS_GC"]);
--    if (m != null)
-+  blockStack: function(stack) {
-+    let m = hasAnyAttribute(this._type, ["NS_GC"]);
-+    if (m != false)
-       return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
--    return getStorageClass(this._type).blockGlobal();
-+    return this._forward('blockStack', stack);
-   },
-   
--  blockMalloc: function() {
-+  blockGlobal: function(stack) {
-     let m = hasAnyAttribute(this._type, ["NS_stack", "NS_managed", "NS_GC"]);
--    if (m != null)
-+    if (m != false)
-       return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
--    return getStorageClass(this._type).blockMalloc();
-+    return this._forward('blockGlobal', stack);
-   },
-   
--  blockGC: function() {
--    let m = hasAnyAttribute(this._type, ["NS_stack", "NS_NoGC"]);
--    if (m != null)
-+  blockMalloc: function(stack) {
-+    let m = hasAnyAttribute(this._type, ["NS_stack", "NS_managed", "NS_GC"]);
-+    if (m != false)
-       return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
--    return getStorageClass(this._type).blockGC();
-+    return this._forward('blockMalloc', stack);
-   },
-   
--  safeFinalizer: function() { return getStorageClass(this._type).safeFinalizer(); },
--  canSkipFinalization: function() { return getStorageClass(this._type).canSkipFinalization(); }
-+  blockGC: function(stack) {
-+    let m = hasAnyAttribute(this._type, ["NS_stack", "NS_NoGC"]);
-+    if (m != false)
-+      return new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
-+    return this._forward('blockGC', stack);
-+  },
-+  
-+  safeFinalizer: function(stack) {
-+    return this._forward('safeFinalizer', stack);
-+  },
-+  canSkipFinalization: function(stack) {
-+    return this._forward('canSkipFinalization', stack);
-+  },
-+  toString: function() { return "TypedefStorageClass(%s)".format(this._type); }
- };
- 
- /**
-@@ -262,9 +310,16 @@ TypedefStorageClass.prototype = {
-  */
- function hasOwnDestructor(c)
- {
--  for (let member in c.members)
--    if (member.isFunction && /^~/(member.name))
-+  for each (let member in c.members) {
-+    // dehydra version
-+    if (member.isFunction && /::~/(member.name))
-       return true;
-+    
-+    // treehydra version
-+    if (member.isDestructor &&
-+        !DECL_ARTIFICIAL(member._type))
-+      return true;
-+  }
- 
-   return false;
- }
-@@ -292,9 +347,14 @@ StructStorageClass.prototype = {
-     return null;
-   },
-   
--  iterItems: function(fname) {
-+  iterItems: function(fname, stack) {
-+    if (this._type.isIncomplete)
-+      return null;
-+
-+    stack = stack.concat(this._type);
-+
-     for each (let base in this._type.bases) {
--      let r = getStorageClass(base)[fname]();
-+      let r = getStorageClass(base)[fname](stack);
-       if (r != null)
-         return new StorageReason(this._type.loc, "class %s is a base of %s".format([base.name, this._type.name]), r);
-     }
-@@ -302,21 +362,21 @@ StructStorageClass.prototype = {
-       if (member.isFunction || member.isStatic)
-         continue;
-       
--      let r = getStorageClass(member)[fname]();
-+      let r = getStorageClass(member)[fname](stack);
-       if (r != null)
-         return new StorageReason(member.loc, "due to member %s".format(member.name), r);
-     }
-     return null;
-   },
- 
--  blockStack: function() {
-+  blockStack: function(stack) {
-     let m = hasAnyAttribute(this._type, ["NS_GC"]);
-     if (m != false)
-       return new StorageReason(this._type.loc, "%s %s marked as %s".format(this._type.kind, this._type.name, m));
--    return this.iterItems('blockStack');
-+    return this.iterItems('blockStack', stack);
-   },
-   
--  blockGlobal: function() {
-+  blockGlobal: function(stack) {
-     let m = hasAnyAttribute(this._type, ["NS_stack"]);
-     if (m != false)
-       return new StorageReason(this._type.loc, "%s %s marked as %s".format(this._type.kind, this._type.name, m));
-@@ -325,10 +385,10 @@ StructStorageClass.prototype = {
-     if (m != null)
-       return m;
- 
--    return this.iterItems('blockGlobal');
-+    return this.iterItems('blockGlobal', stack);
-   },
-   
--  blockMalloc: function() {
-+  blockMalloc: function(stack) {
-     let m = hasAnyAttribute(this._type, ["NS_stack"]);
-     if (m != false) {
-       return new StorageReason(this._type.loc, "%s %s marked as %s".format(this._type.kind, this._type.name, m));
-@@ -338,19 +398,19 @@ StructStorageClass.prototype = {
-     if (m != null)
-       return m;
-     
--    return this.iterItems('blockMalloc');
-+    return this.iterItems('blockMalloc', stack);
-   },
-   
--  blockGC: function() {
-+  blockGC: function(stack) {
-     let m = hasAnyAttribute(this._type, ["NS_NoGC"]);
-     if (m != false)
-       return new StorageReason(this._type.loc, "%s %s marked as %s".format(this._type.kind, this._type.name, m));
-     
--    return this.iterItems('blockGC');
-+    return this.iterItems('blockGC', stack);
-   },
-   
--  safeFinalizer: function() {
--    let m = this.iterItems('safeFinalizer');
-+  safeFinalizer: function(stack) {
-+    let m = this.iterItems('safeFinalizer', stack);
-     if (m != null)
-       return m;
- 
-@@ -370,41 +430,56 @@ StructStorageClass.prototype = {
-     return null;
-   },
-   
--  canSkipFinalization: function() {
--    let m = this.iterItems('canSkipFinalization');
-+  canSkipFinalization: function(stack) {
-+    let m = this.iterItems('canSkipFinalization', stack);
-     if (m != null)
-       return m;
-     
-     if (hasAttribute(this._type, 'NS_finalizer_not_required'))
-       return null;
- 
-+    if (this._type.name == 'MMgc::GCObject' ||
-+        this._type.name == 'XPCOMGCObject')
-+      return null;
-+    
-     if (hasOwnDestructor(this._type))
-       return new StorageReason(this._type.loc, "%s %s has a destructor and is not annotated NS_FINALIZER_NOT_REQUIRED.".format(this._type.kind, this._type.name));
--    
-+
-     return null;
--  }
-+  },
-+  toString: function() { return "StructStorageClass(%s)".format(this._type); }
- };
- 
- function DeclStorageClass(decl)
- {
-   this._decl = decl;
--  print("Created decl storage class for %s".format(decl));
- }
- DeclStorageClass.prototype = {
--  blockStack: function() { return getStorageClass(this._decl.type).blockStack(); },
--  blockGlobal: function() {
-+  blockStack: function(stack) {
-+    return getStorageClass(this._decl.type).blockStack(stack);
-+  },
-+  blockGlobal: function(stack) {
-     if (hasAttribute(this._decl, "NS_unmanaged"))
-       return null;
-     
--    return getStorageClass(this._decl.type).blockGlobal();
-+    return getStorageClass(this._decl.type).blockGlobal(stack);
-   },
--  blockMalloc: function() {
-+  blockMalloc: function(stack) {
-     if (hasAttribute(this._decl, "NS_unmanaged"))
-       return null;
-     
--    return getStorageClass(this._decl.type).blockMalloc();
-+    return getStorageClass(this._decl.type).blockMalloc(stack);
-   },
--  blockGC: function() { return getStorageClass(this._decl.type).blockGC(); }
-+  blockGC: function(stack) {
-+    return getStorageClass(this._decl.type).blockGC(stack);
-+  },
-+  safeFinalizer: function(stack) {
-+    return getStorageClass(this._decl.type).safeFinalizer(stack);
-+  },
-+  canSkipFinalization: function(stack) {
-+    return getStorageClass(this._decl.type).canSkipFinalization(stack);
-+  },
-+  toString: function() { return "DeclStorageClass(%s)".format(this._type); }
- };
- 
- /**
-@@ -432,6 +507,8 @@ function getStorageClass(type)
-     case "union":
-       // TODO: what the hell should union rules be? For now, bail!
-       return ValueStorageClass;
-+    case "enum":
-+      return ValueStorageClass;
-     default:
-       throw Error("Unrecognized type.kind '%s'".format(type.kind));
-     }
-@@ -442,13 +519,16 @@ function getStorageClass(type)
-     return ValueStorageClass;
-   }
- 
--  if (type.precision) {
-+  if (type.precision || type.name == 'void') {
-     if (type.type)
-       throw Error("I thought this was a numeric type, but apparently it's not: %s".format(type));
-     
-     return ValueStorageClass;
-   }
--  
-+
-   // If we're here, whatever we have is probably a decl...
-+  if (type.type === undefined)
-+    throw Error("Analysis error: %s should be a decl but doesn't have a type".format(type));
-+
-   return new DeclStorageClass(type);
- }
-diff --git a/xpcom/tests/static-checker/GCPointersManaged.cpp b/xpcom/tests/static-checker/GCPointersManaged.cpp
-new file mode 100644
---- /dev/null
-+++ b/xpcom/tests/static-checker/GCPointersManaged.cpp
-@@ -0,0 +1,14 @@
-+#include "nscore.h"
-+
-+struct NS_GC_TYPE A;
-+
-+struct B
-+{
-+  // pointers to GC types imply that class B is a managed type
-+  A *a;
-+};
-+
-+void f()
-+{
-+  B *b = new B();
-+}
-diff --git a/xpcom/tests/static-checker/Makefile.in b/xpcom/tests/static-checker/Makefile.in
---- a/xpcom/tests/static-checker/Makefile.in
-+++ b/xpcom/tests/static-checker/Makefile.in
-@@ -55,6 +55,7 @@ GC_FAILURE_TESTCASES = \
-   GCNotStack-return.cpp \
-   GCInheritance1.cpp \
-   GCInheritance2.cpp \
-+  GCPointersManaged.cpp \
-   $(NULL)
- 
- GC_PASS_TESTCASES = \