Bug 787703. r=terrence.
authorJason Orendorff <jorendorff@mozilla.com>
Fri, 02 Nov 2012 10:12:45 -0500
changeset 112203 7c56dd925c253a2d8e255fe70f8c0e7548e8e39f
parent 112202 47711ed9454d572acc9a210d341fc24358a384e5
child 112204 63defe9bc7d5d7fb5a4596a5621871ec2b09f8ee
push id17454
push userjorendorff@mozilla.com
push dateFri, 02 Nov 2012 21:50:51 +0000
treeherdermozilla-inbound@7c56dd925c25 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs787703
milestone19.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 787703. r=terrence.
js/src/gc/Root.h
js/src/jit-test/tests/gc/bug-787703.js
js/src/jsapi.h
js/src/jspubtd.h
js/src/vm/String.h
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -626,16 +626,22 @@ class Rooted : public RootedBase<T>
     Rooted<T> **stack, *prev;
 #endif
     T ptr;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     Rooted(const Rooted &) MOZ_DELETE;
 };
 
+#if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING))
+// Defined in vm/String.h.
+template <>
+class Rooted<JSStableString *>;
+#endif
+
 template <typename T>
 bool
 Return<T>::operator==(const Rooted<T> &other)
 {
     return ptr_ == other.get();
 }
 
 typedef Rooted<JSObject*>    RootedObject;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-787703.js
@@ -0,0 +1,5 @@
+eval(" function x() {}" + Array(241).join(" "));
+for (var i = 0; i < 100; i++) {
+    gczeal(4, 2);
+    String(x);
+}
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1192,16 +1192,20 @@ class AutoStringRooter : private AutoGCR
     JSString * string() const {
         return str;
     }
 
     JSString ** addr() {
         return &str;
     }
 
+    JSString * const * addr() const {
+        return &str;
+    }
+
     friend void AutoGCRooter::trace(JSTracer *trc);
 
   private:
     JSString *str;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AutoArrayRooter : private AutoGCRooter {
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -196,16 +196,17 @@ typedef struct JSStackFrame             
 typedef struct JSScript          JSScript;
 typedef struct JSStructuredCloneCallbacks   JSStructuredCloneCallbacks;
 typedef struct JSStructuredCloneReader      JSStructuredCloneReader;
 typedef struct JSStructuredCloneWriter      JSStructuredCloneWriter;
 typedef struct JSTracer                     JSTracer;
 
 #ifdef __cplusplus
 class                                       JSFlatString;
+class                                       JSStableString;  // long story
 class                                       JSString;
 #else
 typedef struct JSFlatString                 JSFlatString;
 typedef struct JSString                     JSString;
 #endif /* !__cplusplus */
 
 #ifdef JS_THREADSAFE
 typedef struct PRCallOnceType    JSCallOnceType;
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -552,16 +552,74 @@ class JSStableString : public JSFlatStri
     JS::StableCharPtr chars() const {
         JS_ASSERT(!JSString::isInline());
         return JS::StableCharPtr(d.u1.chars, length());
     }
 };
 
 JS_STATIC_ASSERT(sizeof(JSStableString) == sizeof(JSString));
 
+#if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING))
+namespace js {
+/*
+ * Specialization of Rooted<T> to explicitly root the string rather than
+ * relying on conservative stack scanning.
+ *
+ * In exact-gc builds, Rooted<T> already keeps the T reachable, so this hack is
+ * ifdef'd out. In non-exact-gc builds, conservative scanning would ordinarily
+ * pick up the slack. However in the case where the Rooted pointer is no longer
+ * used, but some subobject or malloc'd memory with the same lifetime may be
+ * used, conservative scanning can fail. JSStableString's chars() method makes
+ * it particularly attractive to use that way, so we explicitly keep the
+ * JSString gc-reachable for the full lifetime of the Rooted<JSStableString *>.
+ *
+ * It would suffice simply to force the pointer to remain on the stack, a la
+ * JS::Anchor<T>, but for some reason using that voodoo here seems to cause
+ * some compilers (clang, VC++ with PGO) to generate incorrect code.
+ */
+template <>
+class Rooted<JSStableString *>
+{
+  public:
+    Rooted(JSContext *cx, JSStableString *initial = NULL
+           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : rooter(cx, initial)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    operator JSStableString *() const { return get(); }
+    JSStableString * operator ->() const { return get(); }
+    JSStableString ** address() { return reinterpret_cast<JSStableString **>(rooter.addr()); }
+    JSStableString * const * address() const {
+        return reinterpret_cast<JSStableString * const *>(rooter.addr());
+    }
+    JSStableString * get() const { return static_cast<JSStableString *>(rooter.string()); }
+
+    Rooted & operator =(JSStableString *value)
+    {
+        JS_ASSERT(!RootMethods<JSStableString *>::poisoned(value));
+        rooter.setString(value);
+        return *this;
+    }
+
+    Rooted & operator =(const Rooted &value)
+    {
+        rooter.setString(value.get());
+        return *this;
+    }
+
+  private:
+    JS::AutoStringRooter rooter;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+    Rooted(const Rooted &) MOZ_DELETE;
+};
+}
+#endif
+
 class JSExtensibleString : public JSFlatString
 {
     /* Vacuous and therefore unimplemented. */
     bool isExtensible() const MOZ_DELETE;
     JSExtensibleString &asExtensible() const MOZ_DELETE;
 
   public:
     JS_ALWAYS_INLINE