Add a static analysis pass to verify that for any do_QueryFrame<Interface> there is an Interface::kFrameIID declared. NPODB, static-checking only.
authorBenjamin Smedberg <benjamin@smedbergs.us>
Mon, 12 Jan 2009 14:21:11 -0500
changeset 23555 54cf0cbe42e80714ec9cca64771d5e51f7e51f86
parent 23554 61ca5667346845391496df48802cb4a83a8f87e0
child 23556 a4cc5f89569d33cd78d23180b53c7a78c2c66ddd
child 23569 52be68be1534d061f0f4c50d5134e9940b419174
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.2a1pre
Add a static analysis pass to verify that for any do_QueryFrame<Interface> there is an Interface::kFrameIID declared. NPODB, static-checking only.
config/static-checking-config.mk
config/static-checking.js
js/src/config/static-checking.js
layout/generic/frame-graph.py
layout/generic/frame-verify.js
--- a/config/static-checking-config.mk
+++ b/config/static-checking-config.mk
@@ -1,15 +1,16 @@
 # The entire tree should be subject to static analysis using the XPCOM
 # script. Additional scripts may be added by specific subdirectories.
 
 DEHYDRA_SCRIPT = $(topsrcdir)/config/static-checking.js
 
 DEHYDRA_MODULES = \
   $(topsrcdir)/xpcom/analysis/final.js \
+  $(topsrcdir)/layout/generic/frame-verify.js \
   $(NULL)
 
 TREEHYDRA_MODULES = \
   $(topsrcdir)/xpcom/analysis/outparams.js \
   $(topsrcdir)/xpcom/analysis/stack.js \
   $(topsrcdir)/xpcom/analysis/flow.js \
   $(topsrcdir)/js/src/jsstack.js \
   $(NULL)
--- a/config/static-checking.js
+++ b/config/static-checking.js
@@ -68,17 +68,17 @@ function process_tree(fndecl)
   for each (let module in modules)
     if (module.hasOwnProperty('process_tree'))
       module.process_tree(fndecl);
 }
 
 function process_decl(decl)
 {
   for each (let module in modules)
-    if (module.hasOwnProperty('process_var'))
+    if (module.hasOwnProperty('process_decl'))
       module.process_decl(decl);
 }
 
 function process_cp_pre_genericize(fndecl)
 {
   for each (let module in modules)
     if (module.hasOwnProperty('process_cp_pre_genericize'))
       module.process_cp_pre_genericize(fndecl);
--- a/js/src/config/static-checking.js
+++ b/js/src/config/static-checking.js
@@ -68,17 +68,17 @@ function process_tree(fndecl)
   for each (let module in modules)
     if (module.hasOwnProperty('process_tree'))
       module.process_tree(fndecl);
 }
 
 function process_decl(decl)
 {
   for each (let module in modules)
-    if (module.hasOwnProperty('process_var'))
+    if (module.hasOwnProperty('process_decl'))
       module.process_decl(decl);
 }
 
 function process_cp_pre_genericize(fndecl)
 {
   for each (let module in modules)
     if (module.hasOwnProperty('process_cp_pre_genericize'))
       module.process_cp_pre_genericize(fndecl);
new file mode 100644
--- /dev/null
+++ b/layout/generic/frame-graph.py
@@ -0,0 +1,37 @@
+"""
+Take the *.framedata files from graph-frameclasses.js and combine them
+into a single graphviz file.
+
+stdin: a list of .framedata file names (e.g. from xargs)
+stdout: a graphviz file
+
+e.g. `find <objdir> -name "*.framedata" | python aggregate-frameclasses.py |
+  dot -Tpng -o frameclasses-graph.png -`
+"""
+
+import sys
+
+classdict = {}
+
+for line in sys.stdin:
+    file = line.strip()
+    fd = open(file)
+
+    output = None
+    for line in fd:
+        if line.startswith('CLASS-DEF: '):
+            cname = line[11:-1]
+            if cname not in classdict:
+                output = classdict[cname] = []
+            else:
+                output = None
+        elif output is not None:
+            output.append(line)
+
+sys.stdout.write('digraph g {\n')
+
+for olist in classdict.itervalues():
+    for line in olist:
+        sys.stdout.write(line)
+
+sys.stdout.write('}\n')
new file mode 100644
--- /dev/null
+++ b/layout/generic/frame-verify.js
@@ -0,0 +1,86 @@
+/**
+ * graph-frameclasses.js: a dehydra script to collect information about
+ * the class hierarchy of frame types.
+ */
+
+function inheritsFrom(t, baseName)
+{
+  let name = t.name;
+  if (name == baseName)
+    return true;
+  
+  for each (let base in t.bases)
+    if (inheritsFrom(base.type, baseName))
+      return true;
+    
+  return false;
+}  
+
+let output = [];
+
+function process_type(t)
+{
+  if ((t.kind == "class" || t.kind == "struct")) {
+    if (!t.isIncomplete && inheritsFrom(t, 'nsIFrame')) {
+      if (inheritsFrom(t, 'nsISupports'))
+        warning("nsIFrame derivative %s inherits from nsISupports but is not refcounted.".format(t.name));
+      
+      let nonFrameBases = [];
+      
+      output.push('CLASS-DEF: %s'.format(t.name));
+
+      for each (let base in t.bases) {
+        if (inheritsFrom(base.type, 'nsIFrame')) {
+          output.push('%s -> %s;'.format(base.type.name, t.name));
+        }
+        else if (base.type.name != 'nsQueryFrame') {
+          nonFrameBases.push(base.type.name);
+        }
+      }
+      
+      output.push('%s [label="%s%s"];'.format(t.name, t.name,
+                                              ["\\n(%s)".format(b) for each (b in nonFrameBases)].join('')));
+    }
+  }
+}
+
+let frameIIDRE = /::kFrameIID$/;
+let queryFrameRE = /^do_QueryFrame::operator/;
+
+/* A list of class names T that have do_QueryFrame<T> used */
+let needIDs = [];
+
+/* A map of class names that have a kFrameIID declared */
+let haveIDs = {};
+
+// We match up needIDs with haveIDs at the end because static variables are
+// not present in the .members array of a type
+
+function process_decl(d)
+{
+  if (d.name) {
+    if (frameIIDRE(d.name)) {
+      haveIDs[d.memberOf.name] = 1;
+    }
+    else if (queryFrameRE(d.name) && d.template === undefined) {
+      let templtype = d.type.type.type;
+      while (templtype.typedef !== undefined)
+        templtype = templtype.typedef;
+      
+      needIDs.push([templtype.name, d.loc]);
+    }
+  }
+}
+
+function input_end()
+{
+  for each (let [name, loc] in needIDs) {
+    if (!haveIDs.hasOwnProperty(name)) {
+      error("nsQueryFrame<%s> found, but %s::kFrameIID is not declared".format(name, name), loc);
+    }
+  }
+
+  if (output.length > 0) {
+    write_file(sys.aux_base_name + '.framedata', output.join('\n'));
+  }
+}