Hoo!
authorBenjamin Smedberg <benjamin@smedbergs.us>
Thu, 17 Jul 2008 15:07:03 -0400
changeset 158 3bc262b4ce40
parent 157 8daebed024b6
child 159 969d8d4050b8
push id34
push userbsmedberg@mozilla.com
push dateThu, 17 Jul 2008 19:07:23 +0000
Hoo!
NS_FINALIZER_NOT_REQUIRED-xpcom
TestPageLoad-namespace
nsChainedEventQueue-gcobject
proxy-create-namespace
proxy-create-temporary-hack
series
static-check-storage-classes2
new file mode 100644
--- /dev/null
+++ b/NS_FINALIZER_NOT_REQUIRED-xpcom
@@ -0,0 +1,11 @@
+diff --git a/xpcom/glue/nsCOMPtr.h b/xpcom/glue/nsCOMPtr.h
+--- a/xpcom/glue/nsCOMPtr.h
++++ b/xpcom/glue/nsCOMPtr.h
+@@ -98,6 +98,7 @@ class
+ class
+   NS_FINAL_CLASS
+   NS_GC_TYPE
++  NS_FINALIZER_NOT_REQUIRED
+ nsCOMPtr
+ {
+ public:
new file mode 100644
--- /dev/null
+++ b/TestPageLoad-namespace
@@ -0,0 +1,43 @@
+diff --git a/netwerk/test/TestPageLoad.cpp b/netwerk/test/TestPageLoad.cpp
+--- a/netwerk/test/TestPageLoad.cpp
++++ b/netwerk/test/TestPageLoad.cpp
+@@ -156,6 +156,8 @@ static NS_METHOD streamParse (nsIInputSt
+ // nsIStreamListener implementation
+ //-----------------------------------------------------------------------------
+ 
++namespace TestPageLoad {
++
+ class MyListener : public nsIStreamListener
+ {
+ public:
+@@ -166,6 +168,10 @@ public:
+     MyListener() { }
+     virtual ~MyListener() {}
+ };
++
++}
++
++using namespace TestPageLoad;
+ 
+ NS_IMPL_ISUPPORTS2(MyListener,
+                    nsIRequestObserver,
+@@ -221,6 +227,8 @@ MyListener::OnDataAvailable(nsIRequest *
+ // NotificationCallbacks implementation
+ //-----------------------------------------------------------------------------
+ 
++namespace TestPageLoad {
++
+ class MyNotifications : public XPCOMGCFinalizedObject
+                       , public nsIInterfaceRequestor
+                       , public nsIProgressEventSink
+@@ -233,6 +241,10 @@ public:
+     MyNotifications() { }
+     virtual ~MyNotifications() {}
+ };
++
++}
++
++using namespace TestPageLoad;
+ 
+ NS_IMPL_THREADSAFE_ISUPPORTS2(MyNotifications,
+                               nsIInterfaceRequestor,
new file mode 100644
--- /dev/null
+++ b/nsChainedEventQueue-gcobject
@@ -0,0 +1,35 @@
+diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp
+--- a/xpcom/threads/nsThread.cpp
++++ b/xpcom/threads/nsThread.cpp
+@@ -622,7 +622,6 @@ nsThread::PushEventQueue(nsIThreadEventF
+ {
+   nsChainedEventQueue *queue = new nsChainedEventQueue(filter);
+   if (!queue || !queue->IsInitialized()) {
+-    delete queue;
+     return NS_ERROR_OUT_OF_MEMORY;
+   }
+ 
+@@ -647,8 +646,6 @@ nsThread::PopEventQueue()
+   while (queue->GetEvent(PR_FALSE, &event))
+     mEvents->PutEvent(event);
+ 
+-  delete queue;
+-  
+   return NS_OK;
+ }
+ 
+diff --git a/xpcom/threads/nsThread.h b/xpcom/threads/nsThread.h
+--- a/xpcom/threads/nsThread.h
++++ b/xpcom/threads/nsThread.h
+@@ -98,7 +98,10 @@ private:
+   nsresult PutEvent(nsIRunnable *event);
+ 
+   // Wrapper for nsEventQueue that supports chaining.
+-  class nsChainedEventQueue {
++  class nsChainedEventQueue
++    : public XPCOMGCFinalizedObject
++    , MMgc::GCFinalizable
++  {
+   public:
+     nsChainedEventQueue(nsIThreadEventFilter *filter = nsnull)
+       : mNext(nsnull), mFilter(filter) {
--- a/proxy-create-namespace
+++ b/proxy-create-namespace
@@ -1,17 +1,37 @@
+* * *
+
 diff --git a/xpcom/proxy/tests/proxy-create-threadsafety.cpp b/xpcom/proxy/tests/proxy-create-threadsafety.cpp
 --- a/xpcom/proxy/tests/proxy-create-threadsafety.cpp
 +++ b/xpcom/proxy/tests/proxy-create-threadsafety.cpp
-@@ -60,6 +60,8 @@
- #include "nsServiceManagerUtils.h"
- #include "nsThreadUtils.h"
- #include "nsISupportsUtils.h"
+@@ -90,6 +90,8 @@ 1		proxy event object released
+ /***************************************************************************/
+ /* ProxyTest                                                               */
+ /***************************************************************************/
 +
 +namespace proxy_create_threadsafety {
  
- /*
+ class ProxyTest : public nsIRunnable,
+                   public nsITestProxy,
+@@ -230,6 +232,8 @@ private:
+     nsCOMPtr<nsIThread> mThreadTwo;
+ };
  
-@@ -250,3 +252,4 @@ main(int argc, char **argv)
++} // namespace
++
+ int
+ main(int argc, char **argv)
+ {
+@@ -241,7 +245,7 @@ main(int argc, char **argv)
+         NS_GetComponentRegistrar(&registrar);
+         registrar->AutoRegister(nsnull);
+ 
+-        nsITestProxy* tester = new ProxyTest();
++        nsITestProxy* tester = new proxy_create_threadsafety::ProxyTest();
+         tester->Test(0, 0, nsnull);
+     }
+ 
+@@ -249,4 +253,3 @@ main(int argc, char **argv)
+ 
      return 0;
  }
- 
-+} // namespace
+-
deleted file mode 100644
--- a/proxy-create-temporary-hack
+++ /dev/null
@@ -1,45 +0,0 @@
-diff --git a/xpcom/proxy/tests/proxy-create-threadsafety.cpp b/xpcom/proxy/tests/proxy-create-threadsafety.cpp
---- a/xpcom/proxy/tests/proxy-create-threadsafety.cpp
-+++ b/xpcom/proxy/tests/proxy-create-threadsafety.cpp
-@@ -61,8 +61,6 @@
- #include "nsThreadUtils.h"
- #include "nsISupportsUtils.h"
- 
--namespace proxy_create_threadsafety {
--
- /*
- 
- A quick diagram of how this test works:
-@@ -92,6 +90,8 @@ 1		proxy event object released
- /***************************************************************************/
- /* ProxyTest                                                               */
- /***************************************************************************/
-+
-+namespace proxy_create_threadsafety {
- 
- class ProxyTest : public nsIRunnable,
-                   public nsITestProxy,
-@@ -232,6 +232,8 @@ private:
-     nsCOMPtr<nsIThread> mThreadTwo;
- };
- 
-+} // namespace
-+
- int
- main(int argc, char **argv)
- {
-@@ -243,7 +245,7 @@ main(int argc, char **argv)
-         NS_GetComponentRegistrar(&registrar);
-         registrar->AutoRegister(nsnull);
- 
--        nsITestProxy* tester = new ProxyTest();
-+        nsITestProxy* tester = new proxy_create_threadsafety::ProxyTest();
-         tester->Test(0, 0, nsnull);
-     }
- 
-@@ -251,5 +253,3 @@ main(int argc, char **argv)
- 
-     return 0;
- }
--
--} // namespace
--- a/series
+++ b/series
@@ -191,12 +191,14 @@ BookmarkImportFrame-notonstack
 automatic-garburator
 automatic-gcobject
 automatic-remove-addrefs
 nsCSSValue-Array-comptr
 nsCSSShadowArray-refcounting
 static-check-storage-classes
 storage-class-annotations
 proxy-create-namespace
-proxy-create-temporary-hack
 nsHashtable-annotation
 nsSegmentedBuffer-allocator-unmanaged
 static-check-storage-classes2
+NS_FINALIZER_NOT_REQUIRED-xpcom
+nsChainedEventQueue-gcobject
+TestPageLoad-namespace
--- a/static-check-storage-classes2
+++ b/static-check-storage-classes2
@@ -133,208 +133,497 @@ diff --git a/xpcom/analysis/gc.js b/xpco
 -
 -  if (!c.hasOwnProperty('okOnStack'))
 -    c.okOnStack = calculate();
 -
 -  return c.okOnStack;
  }
  
  /**
-@@ -255,15 +125,16 @@ function process_function(f, stmts)
+@@ -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();
++        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
-@@ -157,7 +157,11 @@ function StorageReason(loc, message, pre
+@@ -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;
-@@ -180,7 +184,8 @@ ValueStorageClass =  {
+@@ -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)
-@@ -193,17 +198,18 @@ PointerStorageClass.prototype = {
-     if (!subtypeClass.blockGC()) {
-       let reason = subtypeClass.blockMalloc();
+@@ -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() { return this.blockNotManaged(); },
-+  blockMalloc: function() { return this.blockNotManaged(); },
++  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 +218,13 @@ 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(); }
-+  blockStack: function() { return getStorageClass(this._type.type).blockStack(); },
-+  blockGlobal: function() { return getStorageClass(this._type.type).blockGlobal(); },
-+  blockMalloc: function() { return getStorageClass(this._type.type).blockMalloc(); },
-+  blockGC: function() { return getStorageClass(this._type.type).blockGC(); },
-+  safeFinalizer: function() { return getStorageClass(this._type.type).safeFinalizer(); },
-+  canSkipFinalization: function() { return getStorageClass(this._type.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)
-@@ -227,34 +234,35 @@ TypedefStorageClass.prototype = {
+@@ -225,36 +263,46 @@ function TypedefStorageClass(type)
+   this._type = type;
+ }
  TypedefStorageClass.prototype = {
-   blockStack: function() {
-     let m = hasAnyAttribute(this._type, ["NS_GC"]);
+-  blockStack: function() {
+-    let m = hasAnyAttribute(this._type, ["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 new StorageReason(this._type.loc, "typedef %s marked with annotation %s".format(this._type.name, m));
 -    return getStorageClass(this._type).blockStack();
-+    return getStorageClass(this._type.typedef).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"]);
+-  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 getStorageClass(this._type.typedef).blockGlobal();
++    return this._forward('blockStack', stack);
    },
    
-   blockMalloc: function() {
+-  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 getStorageClass(this._type.typedef).blockMalloc();
++    return this._forward('blockGlobal', stack);
    },
    
-   blockGC: function() {
-     let m = hasAnyAttribute(this._type, ["NS_stack", "NS_NoGC"]);
+-  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 getStorageClass(this._type.typedef).blockGC();
++    return this._forward('blockMalloc', stack);
    },
    
 -  safeFinalizer: function() { return getStorageClass(this._type).safeFinalizer(); },
 -  canSkipFinalization: function() { return getStorageClass(this._type).canSkipFinalization(); }
-+  safeFinalizer: function() { return getStorageClass(this._type.typedef).safeFinalizer(); },
-+  canSkipFinalization: function() { return getStorageClass(this._type.typedef).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); }
  };
  
  /**
-@@ -293,6 +301,9 @@ StructStorageClass.prototype = {
+@@ -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) {
++  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]();
++      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)
-@@ -382,13 +393,13 @@ StructStorageClass.prototype = {
+         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(); },
-@@ -404,7 +415,10 @@ 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).blockMalloc();
+-    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() { return getStorageClass(this._decl.type).blockGC(); },
-+  safeFinalizer: function() { return getStorageClass(this._decl.type).safeFinalizer(); },
-+  canSkipFinalization: function() { return getStorageClass(this._decl.type).canSkipFinalization(); },
++  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 +446,8 @@ function getStorageClass(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,7 +458,7 @@ function getStorageClass(type)
+@@ -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;