Bug 573786 - Script to detect static initializers r=taras
authorEhren Metcalfe <ehren.m@gmail.com>
Wed, 18 Aug 2010 18:04:39 -0700
changeset 50850 b240b82abfafd07924d5baa551a7d863efcc7804
parent 50849 156a7aa7108ec609b0acee6e6a935817ba5dbc89
child 50851 b7daee6451bdb3be8654b896c440f88f00a17c0f
push id15179
push userdwitte@mozilla.com
push dateThu, 19 Aug 2010 01:09:19 +0000
treeherdermozilla-central@fbbdd0c89a9f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstaras
bugs573786
milestone2.0b5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 573786 - Script to detect static initializers r=taras
config/static-checking-config.mk
xpcom/analysis/static-init.js
xpcom/tests/static-checker/Makefile.in
xpcom/tests/static-checker/TestStaticInitAttr.cpp
xpcom/tests/static-checker/TestStaticInitConstructor.cpp
xpcom/tests/static-checker/TestStaticInitGlobal.cpp
xpcom/tests/static-checker/TestStaticInitGlobalConst.cpp
xpcom/tests/static-checker/TestStaticInitStructOK.cpp
--- a/config/static-checking-config.mk
+++ b/config/static-checking-config.mk
@@ -8,16 +8,17 @@ DEHYDRA_MODULES = \
   $(topsrcdir)/xpcom/analysis/override.js \
   $(topsrcdir)/xpcom/analysis/must-override.js \
   $(NULL)
 
 TREEHYDRA_MODULES = \
   $(topsrcdir)/xpcom/analysis/outparams.js \
   $(topsrcdir)/xpcom/analysis/stack.js \
   $(topsrcdir)/xpcom/analysis/flow.js \
+  $(topsrcdir)/xpcom/analysis/static-init.js \
   $(topsrcdir)/js/src/jsstack.js \
   $(topsrcdir)/layout/generic/frame-verify.js \
   $(NULL)
 
 DEHYDRA_ARGS = \
   --topsrcdir=$(topsrcdir) \
   --objdir=$(DEPTH) \
   --dehydra-modules=$(subst $(NULL) ,$(COMMA),$(strip $(DEHYDRA_MODULES))) \
new file mode 100644
--- /dev/null
+++ b/xpcom/analysis/static-init.js
@@ -0,0 +1,54 @@
+/**
+ * Detects static initializers i.e. functions called during static initialization.
+ */
+
+require({ after_gcc_pass: "cfg" });
+
+function process_tree(fn) {
+  for each (let attr in translate_attributes(DECL_ATTRIBUTES(fn)))
+    if (attr.name == "constructor")
+      warning(pretty_func(fn) + " marked with constructor attribute\n");
+
+  if (decl_name_string(fn) != "__static_initialization_and_destruction_0")
+    return;
+
+  let cfg = function_decl_cfg(fn);
+  for (let isn in cfg_isn_iterator(cfg)) {
+    if (isn.tree_code() != GIMPLE_CALL)
+      continue;
+    let decl = gimple_call_fndecl(isn);
+    let lhs = gimple_call_lhs(isn);
+    if (lhs) {
+      warning(pretty_var(lhs) + " defined by call to " + pretty_func(decl) +
+              " during static initialization", location_of(lhs));
+    } else {
+      let arg = constructorArg(isn);
+      if (arg)
+        warning(pretty_var(arg) + " defined by call to constructor " + pretty_func(decl) +
+                " during static initialization", location_of(arg));
+      else
+        warning(pretty_func(decl) + " called during static initialization", location_of(decl));
+    }
+  }
+}
+
+function constructorArg(call) {
+  let decl = gimple_call_fndecl(call);
+
+  if (!DECL_CONSTRUCTOR_P(decl))
+    return null;
+
+  let arg = gimple_call_arg_iterator(call).next();
+  if (TYPE_MAIN_VARIANT(TREE_TYPE(TREE_TYPE(arg))) != DECL_CONTEXT(decl))
+    throw new Error("malformed constructor call?!");
+
+  return arg.tree_code() == ADDR_EXPR ? TREE_OPERAND(arg, 0) : arg;
+}
+
+function pretty_func(fn) {
+  return rfunc_string(rectify_function_decl(fn));
+}
+
+function pretty_var(v) {
+  return type_string(TREE_TYPE(v)) + " " + decl_name_string(v);
+}
--- a/xpcom/tests/static-checker/Makefile.in
+++ b/xpcom/tests/static-checker/Makefile.in
@@ -132,35 +132,48 @@ OVERRIDE_PASS_TESTCASES = \
 
 OVERRIDE_FAILURE_TESTCASES = \
   override-global.cpp \
   override-signature.cpp \
   override-static.cpp \
   override-virtual.cpp \
   $(NULL)
 
+STATIC_INIT_PASS_TESTCASES = \
+  TestStaticInitStructOK.cpp \
+  $(NULL)
+
+STATIC_INIT_WARNING_TESTCASES = \
+  TestStaticInitAttr.cpp \
+  TestStaticInitConstructor.cpp \
+  TestStaticInitGlobal.cpp \
+  TestStaticInitGlobalConst.cpp \
+  $(NULL)
+
 STATIC_FAILURE_TESTCASES = \
   $(FINAL_FAILURE_TESTCASES) \
   $(FLOW_FAILURE_TESTCASES) \
   $(MUST_OVERRIDE_FAILURE_TESTCASES) \
   $(OVERRIDE_FAILURE_TESTCASES) \
   $(NULL)
 
 STATIC_WARNING_TESTCASES = \
   $(OUTPARAMS_WARNING_TESTCASES) \
   $(STACK_FAILURE_TESTCASES) \
+  $(STATIC_INIT_WARNING_TESTCASES) \
   $(NULL)
 
 STATIC_PASS_TESTCASES = \
   $(OUTPARAMS_NS_FAILED_TESTCASES) \
   $(OUTPARAMS_PASS_TESTCASES) \
   $(STACK_PASS_TESTCASES) \
   $(FLOW_PASS_TESTCASES) \
   $(MUST_OVERRIDE_PASS_TESTCASES) \
   $(OVERRIDE_PASS_TESTCASES) \
+  $(STATIC_INIT_PASS_TESTCASES) \
   $(NULL)
 
 
 include $(topsrcdir)/config/rules.mk
 
 # We want to compile each file and invert the result to ensure that
 # compilation failed.
 check:: \
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/static-checker/TestStaticInitAttr.cpp
@@ -0,0 +1,5 @@
+int foo() __attribute__((constructor));
+
+int foo() {
+  return 0;
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/static-checker/TestStaticInitConstructor.cpp
@@ -0,0 +1,7 @@
+struct Blah {
+  public:
+    Blah() { }
+   ~Blah() { } // raises call to __cxa_atexit
+};
+
+Blah b;
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/static-checker/TestStaticInitGlobal.cpp
@@ -0,0 +1,5 @@
+int foo() {
+  return 0;
+}
+ 
+int x = foo();
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/static-checker/TestStaticInitGlobalConst.cpp
@@ -0,0 +1,5 @@
+int foo() {
+  return 0;
+}
+ 
+const static int x = foo();
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/static-checker/TestStaticInitStructOK.cpp
@@ -0,0 +1,5 @@
+struct Blah {
+  int i;
+};
+
+Blah b = { 3 };