Bug 1321014 - Respect MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS for the purpose of identifying GC types and pointers, r=jonco
authorSteve Fink <sfink@mozilla.com>
Fri, 20 Jul 2018 18:36:20 -0700
changeset 443323 22dd687dfbd20c801af28af8fc40e89dc09c4bc9
parent 443322 d3aa3bd3881a27ee683583570aa897d6a6073fd1
child 443324 113e7326a16aa1ac9e639319e94dab5e514cc044
push id109352
push usersfink@mozilla.com
push dateMon, 29 Oct 2018 18:53:52 +0000
treeherdermozilla-inbound@0f742a491f96 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1321014
milestone65.0a1
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 1321014 - Respect MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS for the purpose of identifying GC types and pointers, r=jonco
browser/config/mozconfigs/linux64/hazards
js/public/GCAnnotations.h
js/public/StableStringChars.h
js/public/Utility.h
js/public/Value.h
js/src/devtools/rootAnalysis/computeGCTypes.js
js/src/devtools/rootAnalysis/mozconfig.haz
js/src/devtools/rootAnalysis/t/hazards/source.cpp
js/src/devtools/rootAnalysis/t/hazards/test.py
layout/base/AccessibleCaret.h
layout/base/gtest/TestAccessibleCaretEventHub.cpp
layout/base/gtest/TestAccessibleCaretManager.cpp
mfbt/Attributes.h
mfbt/CheckedInt.h
taskcluster/scripts/builder/hazard-browser.sh
--- a/browser/config/mozconfigs/linux64/hazards
+++ b/browser/config/mozconfigs/linux64/hazards
@@ -31,15 +31,18 @@ mk_add_options MOZ_OBJDIR=obj-analyzed
 ac_add_options --enable-debug
 ac_add_options --enable-tests
 ac_add_options --enable-optimize
 ac_add_options --with-compiler-wrapper=$TOOLTOOL_DIR/sixgill/usr/libexec/sixgill/scripts/wrap_gcc/basecc
 ac_add_options --without-ccache
 
 ac_add_options --disable-replace-malloc
 
-CFLAGS="$CFLAGS -Wno-attributes"
-CPPFLAGS="$CPPFLAGS -Wno-attributes"
-CXXFLAGS="$CXXFLAGS -Wno-attributes"
+# -Wattributes is very verbose due to attributes being ignored on template
+# instantiations. -Wignored-attributes is very verbose due to attributes being
+# ignored on template parameters.
+CFLAGS="$CFLAGS -Wno-attributes -Wno-ignored-attributes"
+CPPFLAGS="$CPPFLAGS -Wno-attributes -Wno-ignored-attributes"
+CXXFLAGS="$CXXFLAGS -Wno-attributes -Wno-ignored-attributes"
 
 NODEJS="$TOOLTOOL_DIR/node/bin/node"
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/js/public/GCAnnotations.h
+++ b/js/public/GCAnnotations.h
@@ -10,25 +10,28 @@
 // Set of annotations for the rooting hazard analysis, used to categorize types
 // and functions.
 #ifdef XGILL_PLUGIN
 
 // Mark a type as being a GC thing (eg js::gc::Cell has this annotation).
 # define JS_HAZ_GC_THING __attribute__((annotate("GC Thing")))
 
 // Mark a type as holding a pointer to a GC thing (eg JS::Value has this
-// annotation.)
+// annotation.) "Inherited" by templatized types with
+// MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS.
 # define JS_HAZ_GC_POINTER __attribute__((annotate("GC Pointer")))
 
 // Mark a type as a rooted pointer, suitable for use on the stack (eg all
-// Rooted<T> instantiations should have this.)
+// Rooted<T> instantiations should have this.) "Inherited" by templatized types with
+// MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS.
 # define JS_HAZ_ROOTED __attribute__((annotate("Rooted Pointer")))
 
 // Mark a type as something that should not be held live across a GC, but which
-// is not itself a GC pointer.
+// is not itself a GC pointer. Note that this property is *not* inherited by
+// templatized types with MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS.
 # define JS_HAZ_GC_INVALIDATED __attribute__((annotate("Invalidated by GC")))
 
 // Mark a class as a base class of rooted types, eg CustomAutoRooter. All
 // descendants of this class will be considered rooted, though classes that
 // merely contain these as a field member will not be. "Inherited" by
 // templatized types with MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS
 # define JS_HAZ_ROOTED_BASE __attribute__((annotate("Rooted Base")))
 
--- a/js/public/StableStringChars.h
+++ b/js/public/StableStringChars.h
@@ -51,17 +51,17 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Auto
      * When copying string char, use this many bytes of inline storage.  This is
      * chosen to allow the inline string types to be copied without allocating.
      * This is asserted in AutoStableStringChars::allocOwnChars.
      */
     static const size_t InlineCapacity = 24;
 
     /* Ensure the string is kept alive while we're using its chars. */
     Rooted<JSString*> s_;
-    MOZ_INIT_OUTSIDE_CTOR union {
+    union MOZ_INIT_OUTSIDE_CTOR {
         const char16_t* twoByteChars_;
         const Latin1Char* latin1Chars_;
     };
     mozilla::Maybe<js::Vector<uint8_t, InlineCapacity>> ownChars_;
     enum State { Uninitialized, Latin1, TwoByte };
     State state_;
 
   public:
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -475,17 +475,17 @@ static inline void js_free(void* p)
  * JS_DECLARE_NEW_METHODS (see js::MallocProvider for an example).
  *
  * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_METHODS,
  * or the build will break.
  */
 #define JS_DECLARE_NEW_METHODS(NEWNAME, ALLOCATOR, QUALIFIERS) \
     template <class T, typename... Args> \
     QUALIFIERS T * \
-    NEWNAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
+    MOZ_HEAP_ALLOCATOR NEWNAME(Args&&... args) { \
         void* memory = ALLOCATOR(sizeof(T)); \
         return MOZ_LIKELY(memory) \
             ? new(memory) T(std::forward<Args>(args)...) \
             : nullptr; \
     }
 
 /*
  * Given a class which should provide 'make' methods, add
@@ -495,17 +495,17 @@ static inline void js_free(void* p)
  * ownership of the created object.
  *
  * Note: Do not add a ; at the end of a use of JS_DECLARE_MAKE_METHODS,
  * or the build will break.
  */
 #define JS_DECLARE_MAKE_METHODS(MAKENAME, NEWNAME, QUALIFIERS)\
     template <class T, typename... Args> \
     QUALIFIERS mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
+    MOZ_HEAP_ALLOCATOR MAKENAME(Args&&... args) { \
         T* ptr = NEWNAME<T>(std::forward<Args>(args)...); \
         return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr); \
     }
 
 JS_DECLARE_NEW_METHODS(js_new, js_malloc, static MOZ_ALWAYS_INLINE)
 
 namespace js {
 
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -311,17 +311,17 @@ CanonicalizeNaN(double d)
  *   that toString, toObject, toSymbol will return an invalid pointer (because
  *   some high bits will be set) when called on a Value with a different type
  *   tag.
  *
  * - On 32-bit platforms,when unboxing an object/string/symbol Value, we use a
  *   conditional move (not speculated) to zero the payload register if the type
  *   doesn't match.
  */
-union MOZ_NON_PARAM alignas(8) Value
+union alignas(8) Value
 {
   private:
     uint64_t asBits_;
     double asDouble_;
 
 #if defined(JS_PUNBOX64) && !defined(_WIN64)
     // MSVC doesn't pack these correctly :-(
     struct {
@@ -924,17 +924,17 @@ union MOZ_NON_PARAM alignas(8) Value
         MOZ_ASSERT((((uintptr_t)cell) >> JSVAL_TAG_SHIFT) == 0);
 #endif
         asBits_ = bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING, PayloadType(cell));
     }
 
     bool isPrivateGCThing() const {
         return toTag() == JSVAL_TAG_PRIVATE_GCTHING;
     }
-} JS_HAZ_GC_POINTER;
+} JS_HAZ_GC_POINTER MOZ_NON_PARAM;
 
 static_assert(sizeof(Value) == 8,
               "Value size must leave three tag bits, be a binary power, and "
               "is ubiquitously depended upon everywhere");
 
 inline bool
 IsOptimizedPlaceholderMagicValue(const Value& v)
 {
--- a/js/src/devtools/rootAnalysis/computeGCTypes.js
+++ b/js/src/devtools/rootAnalysis/computeGCTypes.js
@@ -6,21 +6,23 @@ loadRelativeToScript('utility.js');
 loadRelativeToScript('annotations.js');
 
 var gcTypes_filename = scriptArgs[0] || "gcTypes.txt";
 var typeInfo_filename = scriptArgs[1] || "typeInfo.txt";
 
 var typeInfo = {
     'GCPointers': [],
     'GCThings': [],
+    'GCInvalidated': [],
     'NonGCTypes': {}, // unused
     'NonGCPointers': {},
     'RootedGCThings': {},
     'RootedPointers': {},
     'RootedBases': {'JS::AutoGCRooter': true},
+    'InheritFromTemplateArgs': {},
 
     // RAII types within which we should assume GC is suppressed, eg
     // AutoSuppressGC.
     'GCSuppressors': {},
 };
 
 var gDescriptors = new Map; // Map from descriptor string => Set of typeName
 
@@ -39,27 +41,29 @@ function processCSU(csu, body)
 {
     for (let { 'Name': [ annType, tag ] } of (body.Annotation || [])) {
         if (annType != 'annotate')
             continue;
 
         if (tag == 'GC Pointer')
             typeInfo.GCPointers.push(csu);
         else if (tag == 'Invalidated by GC')
-            typeInfo.GCPointers.push(csu);
+            typeInfo.GCInvalidated.push(csu);
         else if (tag == 'GC Thing')
             typeInfo.GCThings.push(csu);
         else if (tag == 'Suppressed GC Pointer')
             typeInfo.NonGCPointers[csu] = true;
         else if (tag == 'Rooted Pointer')
             typeInfo.RootedPointers[csu] = true;
         else if (tag == 'Rooted Base')
             typeInfo.RootedBases[csu] = true;
         else if (tag == 'Suppress GC')
             typeInfo.GCSuppressors[csu] = true;
+        else if (tag == 'moz_inherit_type_annotations_from_template_args')
+            typeInfo.InheritFromTemplateArgs[csu] = true;
     }
 
     for (let { 'Base': base } of (body.CSUBaseClass || []))
         addBaseClass(csu, base);
 
     for (let field of (body.DataField || [])) {
         var type = field.Field.Type;
         var fieldName = field.Field.Name[0];
@@ -134,16 +138,57 @@ for (const typename of extraRootedPointe
     typeInfo.RootedPointers[typename] = true;
 
 // Now that we have the whole hierarchy set up, add all the types and propagate
 // info.
 for (const csu of typeInfo.GCThings)
     addGCType(csu);
 for (const csu of typeInfo.GCPointers)
     addGCPointer(csu);
+for (const csu of typeInfo.GCInvalidated)
+    addGCPointer(csu);
+
+// GC Thing and GC Pointer annotations can be inherited from template args if
+// this annotation is used. Think of Maybe<T> for example: Maybe<JSObject*> has
+// the same GC rules as JSObject*. But this needs to be done in a conservative
+// direction: Maybe<AutoSuppressGC> should not be regarding as suppressing GC
+// (because it might still be None).
+//
+// Note that there is an order-dependence here that is being mostly ignored (eg
+// Maybe<Maybe<Cell*>> -- if that is processed before Maybe<Cell*> is
+// processed, we won't get the right answer). We'll at least sort by string
+// length to make it hard to hit that case.
+var inheritors = Object.keys(typeInfo.InheritFromTemplateArgs).sort((a, b) => a.length - b.length);
+for (const csu of inheritors) {
+    // Unfortunately, we just have a string type name, not the full structure
+    // of a templatized type, so we will have to resort to loose (buggy)
+    // pattern matching.
+    //
+    // Currently, the simplest ways I know of to break this are:
+    //
+    //   foo<T>::bar<U>
+    //   foo<bar<T,U>>
+    //
+    const [_, params_str] = csu.match(/<(.*)>/);
+    for (let param of params_str.split(",")) {
+        param = param.replace(/^\s+/, '')
+        param = param.replace(/\s+$/, '')
+        const pieces = param.split("*");
+        const core_type = pieces[0];
+        const ptrdness = pieces.length - 1;
+        if (ptrdness > 1)
+            continue;
+        const paramDesc = 'template-param-' + param;
+        const why = '(inherited annotations from ' + param + ')';
+        if (typeInfo.GCThings.indexOf(core_type) != -1)
+            markGCType(csu, paramDesc, why, ptrdness, 0, "");
+        if (typeInfo.GCPointers.indexOf(core_type) != -1)
+            markGCType(csu, paramDesc, why, ptrdness + 1, 0, "");
+    }
+}
 
 // Everything that inherits from a "Rooted Base" is considered to be rooted.
 // This is for things like CustomAutoRooter and its subclasses.
 var basework = Object.keys(typeInfo.RootedBases);
 while (basework.length) {
     const base = basework.pop();
     typeInfo.RootedPointers[base] = true;
     if (base in subClasses)
new file mode 100644
--- /dev/null
+++ b/js/src/devtools/rootAnalysis/mozconfig.haz
@@ -0,0 +1,46 @@
+# This mozconfig is used when compiling the browser for the rooting hazard
+# analysis build (labeled H on treeherder). See
+# https://wiki.mozilla.org/Javascript:SpiderMonkey:ExactStackRooting
+
+# Do NOT include build/unix/mozconfig.linux because it points directly at the
+# tooltool-installed gcc, and the analysis works by wrapping the gcc invocation
+# with a script that invokes the real gcc with -fplugin and its configuration
+# directives. Instead, duplicate the contents of that mozconfig here:
+
+MOZ_HAZARD=1
+
+# Skip as many non-compile steps as we can.
+MOZ_AUTOMATION_BUILD_SYMBOLS=0
+MOZ_AUTOMATION_L10N_CHECK=0
+MOZ_AUTOMATION_PACKAGE=0
+MOZ_AUTOMATION_PACKAGE_TESTS=0
+MOZ_AUTOMATION_UPLOAD=0
+
+ac_add_options --enable-js-shell
+
+# The objdir must be at a known location so its path can be stripped from the
+# filenames stored by the analysis
+mk_add_options MOZ_OBJDIR=obj-analyzed
+
+export LLVM_CONFIG=$TOOLTOOL_DIR/clang/bin/llvm-config
+export CBINDGEN="${TOOLTOOL_DIR}/cbindgen/cbindgen"
+
+# The configuration options are chosen to compile the most code
+# (--enable-debug, --enable-tests) in the trickiest way possible
+# (--enable-optimize) to maximize the chance of seeing tricky static orderings.
+ac_add_options --enable-debug
+ac_add_options --enable-tests
+ac_add_options --enable-optimize
+ac_add_options --with-compiler-wrapper=$TOOLTOOL_DIR/sixgill/usr/libexec/sixgill/scripts/wrap_gcc/basecc
+ac_add_options --without-ccache
+
+ac_add_options --disable-replace-malloc
+
+# -Wattributes is very verbose due to attributes being ignored on template
+# instantiations. -Wignored-attributes is very verbose due to attributes being
+# ignored on template parameters.
+CFLAGS="$CFLAGS -Wno-attributes -Wno-ignored-attributes"
+CPPFLAGS="$CPPFLAGS -Wno-attributes -Wno-ignored-attributes"
+CXXFLAGS="$CXXFLAGS -Wno-attributes -Wno-ignored-attributes"
+
+NODEJS="$TOOLTOOL_DIR/node/bin/node"
--- a/js/src/devtools/rootAnalysis/t/hazards/source.cpp
+++ b/js/src/devtools/rootAnalysis/t/hazards/source.cpp
@@ -1,12 +1,17 @@
 #define ANNOTATE(property) __attribute__((annotate(property)))
 
 struct Cell { int f; } ANNOTATE("GC Thing");
 
+template<typename T, typename U>
+struct UntypedContainer {
+    char data[sizeof(T) + sizeof(U)];
+} ANNOTATE("moz_inherit_type_annotations_from_template_args");
+
 struct RootedCell { RootedCell(Cell*) {} } ANNOTATE("Rooted Pointer");
 
 class AutoSuppressGC_Base {
   public:
     AutoSuppressGC_Base() {}
     ~AutoSuppressGC_Base() {}
 } ANNOTATE("Suppress GC");
 
@@ -52,16 +57,22 @@ struct GCInDestructor {
     ~GCInDestructor() {
         invisible();
         asm("");
         *xp = 4;
         GC();
     }
 };
 
+template <typename T>
+void
+usecontainer(T* value) {
+    if (value) asm("");
+}
+
 Cell*
 f()
 {
     GCInDestructor kaboom;
 
     Cell cell;
     Cell* cell1 = &cell;
     Cell* cell2 = &cell;
@@ -80,16 +91,33 @@ f()
         // Old bug: it would look from the first AutoSuppressGC constructor it
         // found to the last destructor. This statement *should* have no effect.
         AutoSuppressGC nogc;
     }
     usecell(cell3);
     Cell* cell5 = &cell;
     usecell(cell5);
 
+    {
+        // Templatized container that inherits attributes from Cell*, should
+        // report a hazard.
+        UntypedContainer<int, Cell*> container1;
+        usecontainer(&container1);
+        GC();
+        usecontainer(&container1);
+    }
+
+    {
+        // As above, but with a non-GC type.
+        UntypedContainer<int, double> container2;
+        usecontainer(&container2);
+        GC();
+        usecontainer(&container2);
+    }
+
     // Hazard in return value due to ~GCInDestructor
     Cell* cell6 = &cell;
     return cell6;
 }
 
 Cell* copy_and_gc(Cell* src)
 {
     GC();
--- a/js/src/devtools/rootAnalysis/t/hazards/test.py
+++ b/js/src/devtools/rootAnalysis/t/hazards/test.py
@@ -30,16 +30,19 @@ assert(len(set(haz.function for haz in h
 # Check that the correct GC call is reported for each hazard. (cell3 has a
 # hazard from two different GC calls; it doesn't really matter which is
 # reported.)
 assert(hazmap['cell2'].GCFunction == 'void halfSuppressedFunction()')
 assert(hazmap['cell3'].GCFunction in (
     'void halfSuppressedFunction()', 'void unsuppressedFunction()'))
 assert(hazmap['<returnvalue>'].GCFunction == 'void GCInDestructor::~GCInDestructor()')
 
+assert('container1' in hazmap);
+assert('container2' not in hazmap);
+
 # Type names are handy to have in the report.
 assert(hazmap['cell2'].type == 'Cell*')
 assert(hazmap['<returnvalue>'].type == 'Cell*')
 
 # loopy hazards. See comments in source.
 assert('haz1' not in hazmap)
 assert('haz2' not in hazmap)
 assert('haz3' in hazmap)
--- a/layout/base/AccessibleCaret.h
+++ b/layout/base/AccessibleCaret.h
@@ -209,17 +209,17 @@ protected:
 
   // Member variables
   Appearance mAppearance = Appearance::None;
 
   // AccessibleCaretManager owns us by a UniquePtr. When it's terminated by
   // AccessibleCaretEventHub::Terminate() which is called in
   // PresShell::Destroy(), it frees us automatically. No need to worry if we
   // outlive mPresShell.
-  nsIPresShell* MOZ_NON_OWNING_REF const mPresShell = nullptr;
+  nsIPresShell* const MOZ_NON_OWNING_REF mPresShell = nullptr;
 
   RefPtr<dom::AnonymousContent> mCaretElementHolder;
 
   // mImaginaryCaretRect is relative to root frame.
   nsRect mImaginaryCaretRect;
 
   // Cache current zoom level to determine whether position is changed.
   float mZoomLevel = 0.0f;
--- a/layout/base/gtest/TestAccessibleCaretEventHub.cpp
+++ b/layout/base/gtest/TestAccessibleCaretEventHub.cpp
@@ -257,23 +257,23 @@ public:
     ReleaseEventCreator aReleaseEventCreator);
 
   // Member variables
   RefPtr<MockAccessibleCaretEventHub> mHub{new MockAccessibleCaretEventHub()};
 
 }; // class AccessibleCaretEventHubTester
 
 TEST_F(AccessibleCaretEventHubTester, TestMousePressReleaseOnNoCaret)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestPressReleaseOnNoCaret(CreateMousePressEvent, CreateMouseReleaseEvent);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestTouchPressReleaseOnNoCaret)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestPressReleaseOnNoCaret(CreateTouchStartEvent, CreateTouchEndEvent);
 }
 
 template <typename PressEventCreator, typename ReleaseEventCreator>
 void
 AccessibleCaretEventHubTester::TestPressReleaseOnNoCaret(
   PressEventCreator aPressEventCreator,
@@ -291,23 +291,23 @@ AccessibleCaretEventHubTester::TestPress
                            nsEventStatus_eIgnore);
 
   HandleEventAndCheckState(aReleaseEventCreator(0, 0),
                            MockAccessibleCaretEventHub::NoActionState(),
                            nsEventStatus_eIgnore);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestMousePressReleaseOnCaret)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestPressReleaseOnCaret(CreateMousePressEvent, CreateMouseReleaseEvent);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestTouchPressReleaseOnCaret)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestPressReleaseOnCaret(CreateTouchStartEvent, CreateTouchEndEvent);
 }
 
 template <typename PressEventCreator, typename ReleaseEventCreator>
 void
 AccessibleCaretEventHubTester::TestPressReleaseOnCaret(
   PressEventCreator aPressEventCreator,
@@ -335,24 +335,24 @@ AccessibleCaretEventHubTester::TestPress
                            nsEventStatus_eConsumeNoDefault);
 
   HandleEventAndCheckState(aReleaseEventCreator(0, 0),
                            MockAccessibleCaretEventHub::NoActionState(),
                            nsEventStatus_eConsumeNoDefault);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestMousePressMoveReleaseOnNoCaret)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestPressMoveReleaseOnNoCaret(CreateMousePressEvent, CreateMouseMoveEvent,
                                 CreateMouseReleaseEvent);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestTouchPressMoveReleaseOnNoCaret)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestPressMoveReleaseOnNoCaret(CreateTouchStartEvent, CreateTouchMoveEvent,
                                 CreateTouchEndEvent);
 }
 
 template <typename PressEventCreator, typename MoveEventCreator,
           typename ReleaseEventCreator>
 void
@@ -391,24 +391,24 @@ AccessibleCaretEventHubTester::TestPress
                            nsEventStatus_eIgnore);
 
   HandleEventAndCheckState(aReleaseEventCreator(x3, y3),
                            MockAccessibleCaretEventHub::NoActionState(),
                            nsEventStatus_eIgnore);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestMousePressMoveReleaseOnCaret)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestPressMoveReleaseOnCaret(CreateMousePressEvent, CreateMouseMoveEvent,
                               CreateMouseReleaseEvent);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestTouchPressMoveReleaseOnCaret)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestPressMoveReleaseOnCaret(CreateTouchStartEvent, CreateTouchMoveEvent,
                               CreateTouchEndEvent);
 }
 
 template <typename PressEventCreator, typename MoveEventCreator,
           typename ReleaseEventCreator>
 void
@@ -460,17 +460,17 @@ AccessibleCaretEventHubTester::TestPress
 
   HandleEventAndCheckState(aReleaseEventCreator(x3, y3),
                            MockAccessibleCaretEventHub::NoActionState(),
                            nsEventStatus_eConsumeNoDefault);
 }
 
 TEST_F(AccessibleCaretEventHubTester,
        TestTouchStartMoveEndOnCaretWithTouchCancelIgnored)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   nscoord x0 = 0, y0 = 0;
   nscoord x1 = 100, y1 = 100;
   nscoord x2 = 300, y2 = 300;
   nscoord x3 = 400, y3 = 400;
 
   {
     InSequence dummy;
@@ -519,24 +519,24 @@ MOZ_CAN_RUN_SCRIPT
                            MockAccessibleCaretEventHub::NoActionState(),
                            nsEventStatus_eConsumeNoDefault);
 
   HandleEventAndCheckState(CreateTouchCancelEvent(x3, y3),
                            MockAccessibleCaretEventHub::NoActionState(),
                            nsEventStatus_eIgnore);}
 
 TEST_F(AccessibleCaretEventHubTester, TestMouseLongTapWithSelectWordSuccessful)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestLongTapWithSelectWordSuccessful(CreateMousePressEvent,
                                       CreateMouseReleaseEvent);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestTouchLongTapWithSelectWordSuccessful)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestLongTapWithSelectWordSuccessful(CreateTouchStartEvent,
                                       CreateTouchEndEvent);
 }
 
 template <typename PressEventCreator, typename ReleaseEventCreator>
 void
 AccessibleCaretEventHubTester::TestLongTapWithSelectWordSuccessful(
@@ -601,24 +601,24 @@ AccessibleCaretEventHubTester::TestLongT
   EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
 
   HandleEventAndCheckState(aReleaseEventCreator(1, 1),
                            MockAccessibleCaretEventHub::NoActionState(),
                            nsEventStatus_eIgnore);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestMouseLongTapWithSelectWordFailed)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestLongTapWithSelectWordFailed(CreateMousePressEvent,
                                   CreateMouseReleaseEvent);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestTouchLongTapWithSelectWordFailed)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestLongTapWithSelectWordFailed(CreateTouchStartEvent,
                                   CreateTouchEndEvent);
 }
 
 template <typename PressEventCreator, typename ReleaseEventCreator>
 void
 AccessibleCaretEventHubTester::TestLongTapWithSelectWordFailed(
@@ -644,24 +644,24 @@ AccessibleCaretEventHubTester::TestLongT
                            nsEventStatus_eIgnore);
 
   HandleEventAndCheckState(aReleaseEventCreator(0, 0),
                            MockAccessibleCaretEventHub::NoActionState(),
                            nsEventStatus_eIgnore);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestTouchEventDrivenAsyncPanZoomScroll)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestEventDrivenAsyncPanZoomScroll(CreateTouchStartEvent, CreateTouchMoveEvent,
                                     CreateTouchEndEvent);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestMouseEventDrivenAsyncPanZoomScroll)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestEventDrivenAsyncPanZoomScroll(CreateMousePressEvent, CreateMouseMoveEvent,
                                     CreateMouseReleaseEvent);
 }
 
 template <typename PressEventCreator, typename MoveEventCreator,
           typename ReleaseEventCreator>
 void
@@ -745,17 +745,18 @@ AccessibleCaretEventHubTester::TestEvent
   mHub->AsyncPanZoomStopped();
   EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
 
   HandleEventAndCheckState(aReleaseEventCreator(310, 310),
                            MockAccessibleCaretEventHub::NoActionState(),
                            nsEventStatus_eIgnore);
 }
 
-TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScroll) MOZ_CAN_RUN_SCRIPT
+TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScroll)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   TestAsyncPanZoomScroll();
 }
 
 void
 AccessibleCaretEventHubTester::TestAsyncPanZoomScroll()
 {
   MockFunction<void(::std::string aCheckPointName)> check;
@@ -796,17 +797,17 @@ AccessibleCaretEventHubTester::TestAsync
   mHub->ScrollPositionChanged();
   EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
   mHub->AsyncPanZoomStopped();
   EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScrollStartedThenBlur)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   {
     InSequence dummy;
 
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart());
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd()).Times(0);
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnBlur());
   }
@@ -817,17 +818,17 @@ MOZ_CAN_RUN_SCRIPT
   mHub->ScrollPositionChanged();
   EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
   mHub->NotifyBlur(true);
   EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScrollEndedThenBlur)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   {
     InSequence dummy;
 
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart());
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd());
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnBlur());
   }
--- a/layout/base/gtest/TestAccessibleCaretManager.cpp
+++ b/layout/base/gtest/TestAccessibleCaretManager.cpp
@@ -133,17 +133,17 @@ public:
   }
 
   // Member variables
   MockAccessibleCaretManager mManager;
 
 }; // class AccessibleCaretManagerTester
 
 TEST_F(AccessibleCaretManagerTester, TestUpdatesInSelectionMode)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Selection));
 
   EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
                 CaretChangedReason::Updateposition)).Times(3);
 
   mManager.UpdateCarets();
@@ -155,17 +155,17 @@ MOZ_CAN_RUN_SCRIPT
   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
 
   mManager.OnScrollPositionChanged();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
 }
 
 TEST_F(AccessibleCaretManagerTester, TestSingleTapOnNonEmptyInput)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Cursor));
 
   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
     .WillRepeatedly(Return(true));
 
   MockFunction<void(std::string aCheckPointName)> check;
@@ -226,17 +226,17 @@ MOZ_CAN_RUN_SCRIPT
   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
   check.Call("reflow2");
 
   mManager.OnScrollPositionChanged();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
 }
 
 TEST_F(AccessibleCaretManagerTester, TestSingleTapOnEmptyInput)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Cursor));
 
   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
     .WillRepeatedly(Return(false));
 
   MockFunction<void(std::string aCheckPointName)> check;
@@ -296,17 +296,18 @@ MOZ_CAN_RUN_SCRIPT
   mManager.OnReflow();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
   check.Call("reflow2");
 
   mManager.OnScrollPositionChanged();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
 }
 
-TEST_F(AccessibleCaretManagerTester, TestTypingAtEndOfInput) MOZ_CAN_RUN_SCRIPT
+TEST_F(AccessibleCaretManagerTester, TestTypingAtEndOfInput)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Cursor));
 
   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
     .WillRepeatedly(Return(true));
 
   MockFunction<void(std::string aCheckPointName)> check;
@@ -339,17 +340,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
                               nsISelectionListener::NO_REASON);
   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
 
   mManager.OnScrollPositionChanged();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
 }
 
 TEST_F(AccessibleCaretManagerTester, TestScrollInSelectionMode)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Selection));
 
   MockFunction<void(std::string aCheckPointName)> check;
   {
     InSequence dummy;
 
@@ -425,17 +426,17 @@ MOZ_CAN_RUN_SCRIPT
   mManager.OnScrollEnd();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
   check.Call("scrollend2");
 }
 
 TEST_F(AccessibleCaretManagerTester,
        TestScrollInSelectionModeWithAlwaysTiltPref)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   // Simulate Firefox Android preference.
   bool oldPref = StaticPrefs::layout_accessiblecaret_always_tilt();
   Preferences::SetBool("layout.accessiblecaret.always_tilt", true);
 
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Selection));
 
@@ -532,17 +533,17 @@ MOZ_CAN_RUN_SCRIPT
   EXPECT_EQ(FirstCaretAppearance(), Appearance::Left);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::Right);
   check.Call("scrollend2");
 
   Preferences::SetBool("layout.accessiblecaret.always_tilt", oldPref);
 }
 
 TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenLogicallyVisible)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Cursor));
 
   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
     .WillRepeatedly(Return(true));
 
   MockFunction<void(std::string aCheckPointName)> check;
@@ -593,17 +594,17 @@ MOZ_CAN_RUN_SCRIPT
   check.Call("scrollstart2");
 
   mManager.OnScrollEnd();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
   check.Call("scrollend2");
 }
 
 TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenHidden)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Cursor));
 
   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
     .WillRepeatedly(Return(true));
 
   MockFunction<void(std::string aCheckPointName)> check;
@@ -648,17 +649,17 @@ MOZ_CAN_RUN_SCRIPT
   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
 
   mManager.OnScrollEnd();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
   check.Call("scrollend2");
 }
 
 TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeOnEmptyContent)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Cursor));
 
   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
     .WillRepeatedly(Return(false));
 
   MockFunction<void(std::string aCheckPointName)> check;
@@ -720,17 +721,17 @@ MOZ_CAN_RUN_SCRIPT
   check.Call("scrollstart3");
   mManager.OnScrollEnd();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
   check.Call("scrollend3");
 }
 
 TEST_F(AccessibleCaretManagerTester,
        TestScrollInCursorModeWithCaretShownWhenLongTappingOnEmptyContentPref)
-MOZ_CAN_RUN_SCRIPT
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
 {
   // Simulate Firefox Android preference.
   bool oldPref = StaticPrefs::layout_accessiblecaret_caret_shown_when_long_tapping_on_empty_content();
   Preferences::SetBool("layout.accessiblecaret.caret_shown_when_long_tapping_on_empty_content", true);
 
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Cursor));
 
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -531,17 +531,23 @@
  *     goto target;
  *   MOZ_CASE_ATTRIBUTE case 5:
  *   MOZ_DEFAULT_ATTRIBUTE default:
  *
  * The static analyses that are performed by the plugin are as follows:
  *
  * MOZ_CAN_RUN_SCRIPT: Applies to functions which can run script. Callers of
  *   this function must also be marked as MOZ_CAN_RUN_SCRIPT, and all refcounted
- *   arguments must be strongly held in the caller.
+ *   arguments must be strongly held in the caller. Note that MOZ_CAN_RUN_SCRIPT
+ *   should only be applied to function declarations, not definitions. If you
+ *   need to apply it to a definition (eg because both are generated by a macro)
+ *   use MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION.
+ * MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION: Same as MOZ_CAN_RUN_SCRIPT, but usable on
+ *   a definition. If the declaration is in a header file, users of that header
+ *   file may not see the annotation.
  * MOZ_CAN_RUN_SCRIPT_BOUNDARY: Applies to functions which need to call
  *   MOZ_CAN_RUN_SCRIPT functions, but should not themselves be considered
  *   MOZ_CAN_RUN_SCRIPT. This is important for some bindings and low level code
  *   which need to opt out of the safety checks performed by MOZ_CAN_RUN_SCRIPT.
  * MOZ_MUST_OVERRIDE: Applies to all C++ member functions. All immediate
  *   subclasses must provide an exact override of this method; if a subclass
  *   does not override this method, the compiler will emit an error. This
  *   attribute is not limited to virtual methods, so if it is applied to a
@@ -709,18 +715,39 @@
  *   within the calling block using an explicit `return` statement.
  *   Only calls to Constructors, references to local and member variables,
  *   and calls to functions or methods marked as MOZ_MAY_CALL_AFTER_MUST_RETURN
  *   may be made after the MUST_RETURN_FROM_CALLER call.
  * MOZ_MAY_CALL_AFTER_MUST_RETURN: Applies to function or method declarations.
  *   Calls to these methods may be made in functions after calls a
  *   MOZ_MUST_RETURN_FROM_CALLER function or method.
  */
-#ifdef MOZ_CLANG_PLUGIN
+
+// gcc emits a nuisance warning -Wignored-attributes because attributes do not
+// affect mangled names, and therefore template arguments do not propagate
+// their attributes. It is rare that this would affect anything in practice,
+// and most compilers are silent about it. Similarly, -Wattributes complains
+// about attributes being ignored during template instantiation.
+//
+// Be conservative and only suppress the warning when running in a
+// configuration where it would be emitted, namely when compiling with the
+// XGILL_PLUGIN for the rooting hazard analysis (which runs under gcc.) If we
+// end up wanting these attributes in general GCC builds, change this to
+// something like
+//
+//     #if defined(__GNUC__) && ! defined(__clang__)
+//
+#ifdef XGILL_PLUGIN
+#pragma GCC diagnostic ignored "-Wignored-attributes"
+#pragma GCC diagnostic ignored "-Wattributes"
+#endif
+
+#if defined(MOZ_CLANG_PLUGIN) || defined(XGILL_PLUGIN)
 #  define MOZ_CAN_RUN_SCRIPT __attribute__((annotate("moz_can_run_script")))
+#  define MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION __attribute__((annotate("moz_can_run_script")))
 #  define MOZ_CAN_RUN_SCRIPT_BOUNDARY __attribute__((annotate("moz_can_run_script_boundary")))
 #  define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override")))
 #  define MOZ_STATIC_CLASS __attribute__((annotate("moz_global_class")))
 #  define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
 #  define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class")))
 #  define MOZ_HEAP_CLASS __attribute__((annotate("moz_heap_class")))
 #  define MOZ_NON_TEMPORARY_CLASS __attribute__((annotate("moz_non_temporary_class")))
 #  define MOZ_TEMPORARY_CLASS __attribute__((annotate("moz_temporary_class")))
@@ -757,26 +784,30 @@
 #  define MOZ_REQUIRED_BASE_METHOD \
     __attribute__((annotate("moz_required_base_method")))
 #  define MOZ_MUST_RETURN_FROM_CALLER \
     __attribute__((annotate("moz_must_return_from_caller")))
 #  define MOZ_MAY_CALL_AFTER_MUST_RETURN \
     __attribute__((annotate("moz_may_call_after_must_return")))
 /*
  * It turns out that clang doesn't like void func() __attribute__ {} without a
- * warning, so use pragmas to disable the warning. This code won't work on GCC
- * anyways, so the warning is safe to ignore.
+ * warning, so use pragmas to disable the warning.
  */
-#  define MOZ_HEAP_ALLOCATOR \
-    _Pragma("clang diagnostic push") \
-    _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
-    __attribute__((annotate("moz_heap_allocator"))) \
-    _Pragma("clang diagnostic pop")
+#  ifdef __clang__
+#    define MOZ_HEAP_ALLOCATOR \
+       _Pragma("clang diagnostic push") \
+       _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+       __attribute__((annotate("moz_heap_allocator"))) \
+       _Pragma("clang diagnostic pop")
+#  else
+#    define MOZ_HEAP_ALLOCATOR __attribute__((annotate("moz_heap_allocator")))
+#  endif
 #else
 #  define MOZ_CAN_RUN_SCRIPT /* nothing */
+#  define MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION /* nothing */
 #  define MOZ_CAN_RUN_SCRIPT_BOUNDARY /* nothing */
 #  define MOZ_MUST_OVERRIDE /* nothing */
 #  define MOZ_STATIC_CLASS /* nothing */
 #  define MOZ_STACK_CLASS /* nothing */
 #  define MOZ_NONHEAP_CLASS /* nothing */
 #  define MOZ_HEAP_CLASS /* nothing */
 #  define MOZ_NON_TEMPORARY_CLASS /* nothing */
 #  define MOZ_TEMPORARY_CLASS /* nothing */
@@ -801,20 +832,31 @@
 #  define MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS /* nothing */
 #  define MOZ_INIT_OUTSIDE_CTOR /* nothing */
 #  define MOZ_IS_CLASS_INIT /* nothing */
 #  define MOZ_NON_PARAM /* nothing */
 #  define MOZ_NON_AUTOABLE /* nothing */
 #  define MOZ_REQUIRED_BASE_METHOD /* nothing */
 #  define MOZ_MUST_RETURN_FROM_CALLER /* nothing */
 #  define MOZ_MAY_CALL_AFTER_MUST_RETURN /* nothing */
-#endif /* MOZ_CLANG_PLUGIN */
+#endif /* defined(MOZ_CLANG_PLUGIN) || defined(XGILL_PLUGIN) */
 
 #define MOZ_RAII MOZ_NON_TEMPORARY_CLASS MOZ_STACK_CLASS
 
+// gcc has different rules governing attribute placement. Since none of these
+// attributes are actually used by the gcc-based static analysis, just
+// eliminate them rather than updating all of the code.
+
+#ifdef XGILL_PLUGIN
+#  undef MOZ_MUST_OVERRIDE
+#  define MOZ_MUST_OVERRIDE /* nothing */
+#  undef MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
+#  define MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION /* nothing */
+#endif
+
 #endif /* __cplusplus */
 
 /**
  * Printf style formats.  MOZ_FORMAT_PRINTF can be used to annotate a
  * function or method that is "printf-like"; this will let (some)
  * compilers check that the arguments match the template string.
  *
  * This macro takes two arguments.  The first argument is the argument
--- a/mfbt/CheckedInt.h
+++ b/mfbt/CheckedInt.h
@@ -553,17 +553,17 @@ public:
    *
    * This constructor is not explicit. Instead, the type of its argument is a
    * separate template parameter, ensuring that no conversion is performed
    * before this constructor is actually called. As explained in the above
    * documentation for class CheckedInt, this constructor checks that its
    * argument is valid.
    */
   template<typename U>
-  MOZ_IMPLICIT constexpr CheckedInt(U aValue) MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT
+  MOZ_IMPLICIT MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT constexpr CheckedInt(U aValue)
     : mValue(T(aValue)),
       mIsValid(detail::IsInRange<T>(aValue))
   {
     static_assert(detail::IsSupported<T>::value &&
                   detail::IsSupported<U>::value,
                   "This type is not supported by CheckedInt");
   }
 
--- a/taskcluster/scripts/builder/hazard-browser.sh
+++ b/taskcluster/scripts/builder/hazard-browser.sh
@@ -1,10 +1,10 @@
 #!/bin/bash -e
 
 cd $SOURCE
 TOP=$(cd ..; pwd)
 export MOZBUILD_STATE_PATH=$TOP/mozbuild-state
 [ -d $MOZBUILD_STATE_PATH ] || mkdir $MOZBUILD_STATE_PATH
 
-export MOZCONFIG=$SOURCE/browser/config/mozconfigs/linux64/hazards
+export MOZCONFIG=$SOURCE/js/src/devtools/rootAnalysis/mozconfig.haz
 
 exec ./mach build -v -j8