Bug 677773 - Allow JS crash diagnostics to be disabled; simplify them (r=dmandelin,ted)
☠☠ backed out by 9567cfee0f57 ☠ ☠
authorBill McCloskey <wmccloskey@mozilla.com>
Fri, 12 Aug 2011 06:57:45 -0700
changeset 74345 cd7f694c9f2f099a31158fb5d292a97a49d3cad4
parent 74344 9e269bcacf43e00a3e7f9905f58dbf66afbdd236
child 74346 af01921fc49352323e65e943ab4a3f46b0b3d59a
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewersdmandelin, ted
bugs677773
milestone8.0a1
Bug 677773 - Allow JS crash diagnostics to be disabled; simplify them (r=dmandelin,ted)
configure.in
js/src/configure.in
js/src/jscompartment.cpp
js/src/jscrashreport.cpp
js/src/jscrashreport.h
js/src/jsgc.cpp
js/src/jsgcmark.cpp
js/src/jsobj.cpp
js/src/jsproxy.cpp
js/src/jsproxy.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsutil.h
js/src/jswrapper.h
--- a/configure.in
+++ b/configure.in
@@ -7588,16 +7588,28 @@ dnl ====================================
 MOZ_ARG_ENABLE_BOOL(gczeal,
 [  --enable-gczeal         Enable zealous JavaScript GCing],
     JS_GC_ZEAL=1,
     JS_GC_ZEAL= )
 if test -n "$JS_GC_ZEAL"; then
     AC_DEFINE(JS_GC_ZEAL)
 fi
 
+dnl ========================================================
+dnl JS opt-mode assertions and minidump instrumentation
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(js-diagnostics,
+[  --enable-js-diagnostics
+                          Enable JS diagnostic assertions and breakpad data],
+    JS_CRASH_DIAGNOSTICS=1,
+    JS_CRASH_DIAGNOSTICS= )
+if test -n "$JS_CRASH_DIAGNOSTICS"; then
+    AC_DEFINE(JS_CRASH_DIAGNOSTICS)
+fi
+
 dnl ======================================================
 dnl = Enable compiling with ccache
 dnl ======================================================
 MOZ_ARG_WITH_STRING(ccache,
 [  --with-ccache[=path/to/ccache]
                           Enable compiling with ccache],
     CCACHE=$withval, CCACHE="no")
 
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -5017,16 +5017,28 @@ dnl ====================================
 MOZ_ARG_ENABLE_BOOL(gczeal,
 [  --enable-gczeal         Enable zealous GCing],
     JS_GC_ZEAL=1,
     JS_GC_ZEAL= )
 if test -n "$JS_GC_ZEAL"; then
     AC_DEFINE(JS_GC_ZEAL)
 fi
 
+dnl ========================================================
+dnl JS opt-mode assertions and minidump instrumentation
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(js-diagnostics,
+[  --enable-js-diagnostics
+                          Enable JS diagnostic assertions and breakpad data],
+    JS_CRASH_DIAGNOSTICS=1,
+    JS_CRASH_DIAGNOSTICS= )
+if test -n "$JS_CRASH_DIAGNOSTICS"; then
+    AC_DEFINE(JS_CRASH_DIAGNOSTICS)
+fi
+
 dnl ======================================================
 dnl = Enable compiling with ccache
 dnl ======================================================
 MOZ_ARG_WITH_STRING(ccache,
 [  --with-ccache[=path/to/ccache]
                           Enable compiling with ccache],
     CCACHE=$withval, CCACHE="no")
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -557,18 +557,16 @@ JSCompartment::purge(JSContext *cx)
      * which will eventually abort any current recording.
      */
     if (cx->runtime->gcRegenShapes)
         if (hasTraceMonitor())
             traceMonitor()->needFlush = JS_TRUE;
 #endif
 
 #ifdef JS_METHODJIT
-    js::CheckCompartmentScripts(this);
-
     for (JSScript *script = (JSScript *)scripts.next;
          &script->links != &scripts;
          script = (JSScript *)script->links.next) {
         if (script->hasJITCode()) {
 # if defined JS_POLYIC
             mjit::ic::PurgePICs(cx, script);
 # endif
 # if defined JS_MONOIC
--- a/js/src/jscrashreport.cpp
+++ b/js/src/jscrashreport.cpp
@@ -240,47 +240,47 @@ Ring::copyBytes(void *data, size_t size)
 }
 
 static bool gInitialized;
 
 static Stack gGCStack(JS_CRASH_STACK_GC);
 static Stack gErrorStack(JS_CRASH_STACK_ERROR);
 static Ring gRingBuffer(JS_CRASH_RING);
 
+void
+SnapshotGCStack()
+{
+    if (gInitialized)
+        gGCStack.snapshot();
+}
+
+void
+SnapshotErrorStack()
+{
+    if (gInitialized)
+        gErrorStack.snapshot();
+}
+
+void
+SaveCrashData(uint64 tag, void *ptr, size_t size)
+{
+    if (gInitialized)
+        gRingBuffer.push(tag, ptr, size);
+}
+
 } /* namespace crash */
 } /* namespace js */
 
 using namespace js;
 using namespace js::crash;
 
-JS_FRIEND_API(void)
-js_SnapshotGCStack()
-{
-    if (gInitialized)
-        gGCStack.snapshot();
-}
-
-JS_FRIEND_API(void)
-js_SnapshotErrorStack()
-{
-    if (gInitialized)
-        gErrorStack.snapshot();
-}
-
-JS_FRIEND_API(void)
-js_SaveCrashData(uint64 tag, void *ptr, size_t size)
-{
-    if (gInitialized)
-        gRingBuffer.push(tag, ptr, size);
-}
-
 JS_PUBLIC_API(void)
 JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback)
 {
-#if 1
+#ifdef JS_CRASH_DIAGNOSTICS
     if (!gInitialized) {
         gInitialized = true;
         (*callback)(&gGCStack, sizeof(gGCStack));
         (*callback)(&gErrorStack, sizeof(gErrorStack));
         (*callback)(&gRingBuffer, sizeof(gRingBuffer));
     }
 #endif
 }
--- a/js/src/jscrashreport.h
+++ b/js/src/jscrashreport.h
@@ -37,23 +37,51 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jscrashreport_h___
 #define jscrashreport_h___
 
 #include "jstypes.h"
+#include "jsutil.h"
 
-JS_BEGIN_EXTERN_C
+namespace js {
+namespace crash {
+
+void
+SnapshotGCStack();
 
-JS_FRIEND_API(void)
-js_SnapshotGCStack();
+void
+SnapshotErrorStack();
+
+void
+SaveCrashData(uint64 tag, void *ptr, size_t size);
+
+template<size_t size, char marker>
+class StackBuffer {
+  private:
+    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
+    volatile char buffer[size + 4];
 
-JS_FRIEND_API(void)
-js_SnapshotErrorStack();
+  public:
+    StackBuffer(void *data JS_GUARD_OBJECT_NOTIFIER_PARAM) {
+        JS_GUARD_OBJECT_NOTIFIER_INIT;
+
+        buffer[0] = marker;
+        buffer[1] = '[';
 
-JS_FRIEND_API(void)
-js_SaveCrashData(uint64 tag, void *ptr, size_t size);
+        for (size_t i = 0; i < size; i++) {
+            if (data)
+                buffer[i + 2] = ((char *)data)[i];
+            else
+                buffer[i + 2] = 0;
+        }
 
-JS_END_EXTERN_C
+        buffer[size - 2] = ']';
+        buffer[size - 1] = marker;
+    }
+};
+
+} /* namespace crash */
+} /* namespace js */
 
 #endif /* jscrashreport_h___ */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -236,17 +236,17 @@ Arena::finalize(JSContext *cx)
                     newListTail->last = thing - sizeof(T);
                     newListTail = newListTail->nextSpanUnchecked(sizeof(T));
                     newFreeSpanStart = 0;
                 }
             } else {
                 if (!newFreeSpanStart)
                     newFreeSpanStart = thing;
                 t->finalize(cx);
-                memset(t, JS_FREE_PATTERN, sizeof(T));
+                JS_POISON(t, JS_FREE_PATTERN, sizeof(T));
             }
         }
     }
 
     if (allClear) {
         JS_ASSERT(newListTail == &newListHead);
         JS_ASSERT(newFreeSpanStart == thingsStart(sizeof(T)));
         return true;
@@ -2394,19 +2394,16 @@ MarkAndSweep(JSContext *cx, JSCompartmen
     if (rt->gcCallback)
         (void) rt->gcCallback(cx, JSGC_FINALIZE_END);
 #ifdef DEBUG_srcnotesize
   { extern void DumpSrcNoteSizeHist();
     DumpSrcNoteSizeHist();
     printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes);
   }
 #endif
-
-    for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++)
-        js::CheckCompartmentScripts(*c);
 }
 
 #ifdef JS_THREADSAFE
 
 /*
  * If the GC is running and we're called on another thread, wait for this GC
  * activation to finish. We can safely wait here without fear of deadlock (in
  * the case where we are called within a request on another thread's context)
@@ -2688,17 +2685,17 @@ js_GC(JSContext *cx, JSCompartment *comp
         return;
     }
 
     RecordNativeStackTopForGC(cx);
 
     GCCrashData crashData;
     crashData.isRegen = rt->shapeGen & SHAPE_OVERFLOW_BIT;
     crashData.isCompartment = !!comp;
-    js_SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
+    crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
 
     GCTIMER_BEGIN(rt, comp);
 
     struct AutoGCProbe {
         JSCompartment *comp;
         AutoGCProbe(JSCompartment *comp) : comp(comp) {
             Probes::GCStart(comp);
         }
@@ -2739,17 +2736,17 @@ js_GC(JSContext *cx, JSCompartment *comp
          */
     } while (gckind == GC_LAST_CONTEXT && rt->gcPoke);
 
     rt->gcNextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN;
 
     rt->gcChunkAllocationSinceLastGC = false;
     GCTIMER_END(gckind == GC_LAST_CONTEXT);
 
-    js_SnapshotGCStack();
+    crash::SnapshotGCStack();
 }
 
 namespace js {
 
 class AutoCopyFreeListToArenas {
     JSRuntime *rt;
 
   public:
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -95,47 +95,34 @@ static inline void
 PushMarkStack(GCMarker *gcmarker, const Shape *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSShortString *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSString *thing);
 
-static void
-volatile_memcpy(volatile unsigned char *dst, const void *src, size_t n)
-{
-    for (size_t i = 0; i < n; i++)
-        dst[i] = ((char *)src)[i];
-}
-
 template<typename T>
 void
 Mark(JSTracer *trc, T *thing)
 {
     JS_ASSERT(thing);
     JS_ASSERT(JS_IS_VALID_TRACE_KIND(GetGCThingTraceKind(thing)));
     JS_ASSERT(trc->debugPrinter || trc->debugPrintArg);
 
     JS_ASSERT(!JSAtom::isStatic(thing));
     JS_ASSERT(thing->isAligned());
 
     JSRuntime *rt = trc->context->runtime;
     JS_ASSERT(thing->compartment());
     JS_ASSERT(thing->compartment()->rt == rt);
 
-    if (rt->gcCheckCompartment && thing->compartment() != rt->gcCheckCompartment &&
-        thing->compartment() != rt->atomsCompartment)
-    {
-        volatile unsigned char dbg[sizeof(T) + 2];
-        dbg[0] = 0xab;
-        dbg[1] = 0xcd;
-        volatile_memcpy(dbg + 2, thing, sizeof(T));
-        JS_Assert("compartment mismatch in GC", __FILE__, __LINE__);
-    }
+    JS_OPT_ASSERT_IF(rt->gcCheckCompartment,
+                     thing->compartment() == rt->gcCheckCompartment ||
+                     thing->compartment() == rt->atomsCompartment);
 
     /*
      * Don't mark things outside a compartment if we are in a per-compartment
      * GC.
      */
     if (!rt->gcCurrentCompartment || thing->compartment() == rt->gcCurrentCompartment) {
         if (IS_GC_MARKING_TRACER(trc))
             PushMarkStack(static_cast<GCMarker *>(trc), thing);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3412,34 +3412,16 @@ CopySlots(JSContext *cx, JSObject *from,
         Value v = from->getSlot(n);
         if (!cx->compartment->wrap(cx, &v))
             return false;
         to->setSlot(n, v);
     }
     return true;
 }
 
-static void
-CheckProxy(JSObject *obj)
-{
-    if (!obj->isProxy())
-        return;
-
-    JSProxyHandler *handler = obj->getProxyHandler();
-    if (handler->isCrossCompartment())
-        return;
-
-    Value priv = obj->getProxyPrivate();
-    if (!priv.isObject())
-        return;
-
-    if (priv.toObject().compartment() != obj->compartment())
-        JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__);
-}
-
 JSObject *
 JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent)
 {
     /*
      * We can only clone native objects and proxies. Dense arrays are slowified if
      * we try to clone them.
      */
     if (!isNative()) {
@@ -3467,18 +3449,16 @@ JSObject::clone(JSContext *cx, JSObject 
         if (getClass()->flags & JSCLASS_HAS_PRIVATE)
             clone->setPrivate(getPrivate());
     } else {
         JS_ASSERT(isProxy());
         if (!CopySlots(cx, this, clone))
             return NULL;
     }
 
-    CheckProxy(clone);
-
     return clone;
 }
 
 static void
 TradeGuts(JSObject *a, JSObject *b)
 {
     JS_ASSERT(a->compartment() == b->compartment());
     JS_ASSERT(a->isFunction() == b->isFunction());
@@ -3581,21 +3561,16 @@ JSObject::swap(JSContext *cx, JSObject *
             return false;
         otherClone = other->clone(cx, other->getProto(), other->getParent());
         if (!otherClone || !otherClone->copyPropertiesFrom(cx, other))
             return false;
     }
     TradeGuts(this, otherClone);
     TradeGuts(other, thisClone);
 
-    CheckProxy(this);
-    CheckProxy(other);
-    CheckProxy(thisClone);
-    CheckProxy(otherClone);
-
     return true;
 }
 
 #if JS_HAS_XDR
 
 #define NO_PARENT_INDEX ((uint32)-1)
 
 uint32
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1179,21 +1179,16 @@ NewProxyObject(JSContext *cx, JSProxyHan
     JS_ASSERT_IF(parent, cx->compartment == parent->compartment());
     bool fun = call || construct;
     Class *clasp;
     if (fun)
         clasp = &FunctionProxyClass;
     else
         clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass;
 
-    if (!handler->isCrossCompartment() && priv.isObject()) {
-        if (priv.toObject().compartment() != cx->compartment)
-            JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__);
-    }
-
     JSObject *obj = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent);
     if (!obj || !obj->ensureInstanceReservedSlots(cx, 0))
         return NULL;
     obj->setSlot(JSSLOT_PROXY_HANDLER, PrivateValue(handler));
     obj->setSlot(JSSLOT_PROXY_PRIVATE, priv);
     if (fun) {
         obj->setSlot(JSSLOT_PROXY_CALL, call ? ObjectValue(*call) : UndefinedValue());
         if (construct) {
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -87,20 +87,16 @@ class JS_FRIEND_API(JSProxyHandler) {
     virtual bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, js::Value *vp);
     virtual void finalize(JSContext *cx, JSObject *proxy);
     virtual void trace(JSTracer *trc, JSObject *proxy);
 
     virtual bool isOuterWindow() {
         return false;
     }
 
-    virtual bool isCrossCompartment() {
-        return false;
-    }
-
     inline void *family() {
         return mFamily;
     }
 };
 
 /* Dispatch point for handlers that executes the appropriate C++ or scripted traps. */
 class JSProxy {
   public:
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -41,16 +41,17 @@
 
 /*
  * JS script operations.
  */
 #include <string.h>
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsutil.h"
+#include "jscrashreport.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
 #include "jsemit.h"
 #include "jsfun.h"
@@ -283,53 +284,35 @@ Bindings::trace(JSTracer *trc)
 {
     if (lastBinding)
         MarkShape(trc, lastBinding, "shape");
 }
 
 } /* namespace js */
 
 static void
-volatile_memcpy(volatile char *dst, void *src, size_t n)
-{
-    for (size_t i = 0; i < n; i++)
-        dst[i] = ((char *)src)[i];
-}
-
-static void
 CheckScript(JSScript *script, JSScript *prev)
 {
-    volatile char dbg1[sizeof(JSScript)], dbg2[sizeof(JSScript)];
+#ifdef JS_CRASH_DIAGNOSTICS
     if (script->cookie1 != JS_SCRIPT_COOKIE || script->cookie2 != JS_SCRIPT_COOKIE) {
-        volatile_memcpy(dbg1, script, sizeof(JSScript));
-        if (prev)
-            volatile_memcpy(dbg2, prev, sizeof(JSScript));
+        crash::StackBuffer<sizeof(JSScript), 0x87> buf1(script);
+        crash::StackBuffer<sizeof(JSScript), 0x88> buf2(prev);
+        JS_OPT_ASSERT(false);
     }
-    JS_OPT_ASSERT(script->cookie1 == JS_SCRIPT_COOKIE && script->cookie2 == JS_SCRIPT_COOKIE);
+#endif
 }
 
 static void
 CheckScriptOwner(JSScript *script, JSObject *owner)
 {
-    if (script->ownerObject != owner) {
-        volatile char scriptData[sizeof(JSScript)];
-        volatile char owner1Data[sizeof(JSObject)], owner2Data[sizeof(JSObject)];
-        volatile char savedOwner[sizeof(JSObject *)];
-
-        volatile_memcpy(scriptData, script, sizeof(JSScript));
-        volatile_memcpy(savedOwner, &owner, sizeof(JSObject *));
-        if (script->ownerObject != JS_NEW_SCRIPT && script->ownerObject != JS_CACHED_SCRIPT)
-            volatile_memcpy(owner1Data, script->ownerObject, sizeof(JSObject));
-        if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT)
-            volatile_memcpy(owner2Data, owner, sizeof(JSObject));
-    }
+#ifdef JS_CRASH_DIAGNOSTICS
     JS_OPT_ASSERT(script->ownerObject == owner);
-
     if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT)
         JS_OPT_ASSERT(script->compartment == owner->compartment());
+#endif
 }
 
 #if JS_HAS_XDR
 
 enum ScriptBits {
     NoScriptRval,
     SavedCallerFun,
     HasSharps,
@@ -976,18 +959,20 @@ JSScript::NewScript(JSContext *cx, uint3
     size += length * sizeof(jsbytecode) +
             nsrcnotes * sizeof(jssrcnote);
 
     script = (JSScript *) cx->malloc_(size);
     if (!script)
         return NULL;
 
     PodZero(script);
+#ifdef JS_CRASH_DIAGNOSTICS
     script->cookie1 = script->cookie2 = JS_SCRIPT_COOKIE;
     script->ownerObject = JS_NEW_SCRIPT;
+#endif
     script->length = length;
     script->version = version;
     new (&script->bindings) Bindings(cx, emptyCallShape);
 
     if (cx->hasRunOption(JSOPTION_PCCOUNT))
         (void) script->pcCounters.init(cx, length);
 
     uint8 *scriptEnd = reinterpret_cast<uint8 *>(script + 1);
@@ -1281,18 +1266,20 @@ JSScript::totalSize()
            length * sizeof(jsbytecode) +
            numNotes() * sizeof(jssrcnote) -
            (uint8 *) this;
 }
 
 void
 JSScript::setOwnerObject(JSObject *owner)
 {
+#ifdef JS_CRASH_DIAGNOSTICS
     CheckScriptOwner(this, JS_NEW_SCRIPT);
     ownerObject = owner;
+#endif
 }
 
 /*
  * Nb: srcnotes are variable-length.  This function computes the number of
  * srcnote *slots*, which may be greater than the number of srcnotes.
  */
 uint32
 JSScript::numNotes()
@@ -1323,32 +1310,16 @@ js_CallDestroyScriptHook(JSContext *cx, 
     JSDestroyScriptHook hook;
 
     hook = cx->debugHooks->destroyScriptHook;
     if (hook)
         hook(cx, script, cx->debugHooks->destroyScriptHookData);
     JS_ClearScriptTraps(cx, script);
 }
 
-namespace js {
-
-void
-CheckCompartmentScripts(JSCompartment *comp)
-{
-    JSScript *prev = NULL;
-    for (JSScript *script = (JSScript *)comp->scripts.next;
-         &script->links != &comp->scripts;
-         prev = script, script = (JSScript *)script->links.next)
-    {
-        CheckScript(script, prev);
-    }
-}
-
-} /* namespace js */
-
 static void
 DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller)
 {
     CheckScript(script, NULL);
     CheckScriptOwner(script, owner);
 
     if (script->principals)
         JSPRINCIPALS_DROP(cx, script->principals);
@@ -1403,17 +1374,17 @@ DestroyScript(JSContext *cx, JSScript *s
 #endif
     JS_REMOVE_LINK(&script->links);
 
     script->pcCounters.destroy(cx);
 
     if (script->sourceMap)
         cx->free_(script->sourceMap);
 
-    memset(script, 0xdb, script->totalSize());
+    JS_POISON(script, 0xdb, sizeof(JSScript));
     *(uint32 *)script = caller;
     cx->free_(script);
 }
 
 void
 js_DestroyScript(JSContext *cx, JSScript *script, uint32 caller)
 {
     JS_ASSERT(!cx->runtime->gcRunning);
@@ -1438,19 +1409,18 @@ js_DestroyCachedScript(JSContext *cx, JS
 
 void
 js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner)
 {
     CheckScript(script, NULL);
     if (owner)
         CheckScriptOwner(script, owner);
 
-    JSRuntime *rt = trc->context->runtime;
-    if (rt->gcCheckCompartment && script->compartment != rt->gcCheckCompartment)
-        JS_Assert("compartment mismatch in GC", __FILE__, __LINE__);
+    DebugOnly<JSRuntime *> rt = trc->context->runtime;
+    JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment == rt->gcCheckCompartment);
 
     JSAtomMap *map = &script->atomMap;
     MarkAtomRange(trc, map->length, map->vector, "atomMap");
 
     if (JSScript::isValidOffset(script->objectsOffset)) {
         JSObjectArray *objarray = script->objects();
         MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
     }
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -444,17 +444,19 @@ struct JSScript {
 
     static JSScript *NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg);
 
     /* FIXME: bug 586181 */
     JSCList         links;      /* Links for compartment script list */
     jsbytecode      *code;      /* bytecodes and their immediate operands */
     uint32          length;     /* length of code vector */
 
+#ifdef JS_CRASH_DIAGNOSTICS
     uint32          cookie1;
+#endif
 
   private:
     uint16          version;    /* JS version under which script was compiled */
 
   public:
     uint16          nfixed;     /* number of slots besides stack operands in
                                    slot array */
   private:
@@ -502,17 +504,19 @@ struct JSScript {
     uint16          staticLevel;/* static level for display maintenance */
     uint16          nClosedArgs; /* number of args which are closed over. */
     uint16          nClosedVars; /* number of vars which are closed over. */
     js::Bindings    bindings;   /* names of top-level variables in this script
                                    (and arguments if this is a function script) */
     JSPrincipals    *principals;/* principals for this script */
     jschar          *sourceMap; /* source map file or null */
 
+#ifdef JS_CRASH_DIAGNOSTICS
     JSObject        *ownerObject;
+#endif
 
     void setOwnerObject(JSObject *owner);
 
     union {
         /*
          * A script object of class js_ScriptClass, to ensure the script is GC'd.
          * - All scripts returned by JSAPI functions (JS_CompileScript,
          *   JS_CompileFile, etc.) have these objects.
@@ -536,17 +540,19 @@ struct JSScript {
     JSThread        *owner;     /* for thread-safe life-cycle assertions */
 #endif
 
     uint32          *closedSlots; /* vector of closed slots; args first, then vars. */
 
     /* array of execution counters for every JSOp in the script, by runmode */
     JSPCCounters    pcCounters;
 
+#ifdef JS_CRASH_DIAGNOSTICS
     uint32          cookie2;
+#endif
 
   public:
 #ifdef JS_METHODJIT
     // Fast-cached pointers to make calls faster. These are also used to
     // quickly test whether there is JIT code; a NULL value means no
     // compilation has been attempted. A JS_UNJITTABLE_SCRIPT value means
     // compilation failed. Any value is the arity-check entry point.
     void *jitArityCheckNormal;
@@ -742,29 +748,16 @@ js_DestroyScriptFromGC(JSContext *cx, JS
  * Script objects may be cached and reused, in which case their JSD-visible
  * lifetimes may be shorter than their actual lifetimes. Destroy one such
  * script for real as part of a GC pass. From JSD's point of view, the script
  * is already dead.
  */
 extern void
 js_DestroyCachedScript(JSContext *cx, JSScript *script);
 
-namespace js {
-
-/*
- * This diagnostic function checks that a compartment's list of scripts
- * contains only valid scripts. It also searches for the target script
- * in the list. If expected is true, it asserts that the target script
- * is found. If expected is false, it asserts that it's not found.
- */
-void
-CheckCompartmentScripts(JSCompartment *comp);
-
-} /* namespace js */
-
 extern void
 js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner);
 
 extern JSObject *
 js_NewScriptObject(JSContext *cx, JSScript *script);
 
 /*
  * To perturb as little code as possible, we introduce a js_GetSrcNote lookup
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -40,39 +40,50 @@
 /*
  * PR assertion checker.
  */
 
 #ifndef jsutil_h___
 #define jsutil_h___
 
 #include "jstypes.h"
-#include "jscrashreport.h"
 #include "mozilla/Util.h"
 #include <stdlib.h>
 #include <string.h>
 
 JS_BEGIN_EXTERN_C
 
 #define JS_CRASH_UNLESS(__cond)                                                 \
     JS_BEGIN_MACRO                                                              \
         if (!(__cond)) {                                                        \
             *(int *)(uintptr_t)0xccadbeef = 0;                                  \
             ((void(*)())0)(); /* More reliable, but doesn't say CCADBEEF */     \
         }                                                                       \
     JS_END_MACRO
 
 #define JS_FREE_PATTERN 0xDA
 
+#ifdef JS_CRASH_DIAGNOSTICS
+
+#define JS_POISON(p, val, size) memset((p), (val), (size))
+
 #define JS_OPT_ASSERT(expr)                                                   \
     ((expr) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__))
 
 #define JS_OPT_ASSERT_IF(cond, expr)                                          \
     ((!(cond) || (expr)) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__))
 
+#else
+
+#define JS_POISON(p, val, size) ((void) 0)
+#define JS_OPT_ASSERT(expr) ((void) 0)
+#define JS_OPT_ASSERT_IF(cond, expr) ((void) 0)
+
+#endif /* JS_CRASH_DIAGNOSTICS */
+
 #ifdef DEBUG
 
 #define JS_ASSERT(expr)                                                       \
     ((expr) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__))
 
 #define JS_ASSERT_IF(cond, expr)                                              \
     ((!(cond) || (expr)) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__))
 
@@ -222,40 +233,34 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_coun
             return NULL; \
         } \
     } while (0)
 
 #else
 #define JS_OOM_POSSIBLY_FAIL() do {} while(0)
 #endif
 
-static JS_INLINE void *js_record_oom(void *p) {
-    if (!p)
-        js_SnapshotErrorStack();
-    return p;
-}
-
 /*
  * SpiderMonkey code should not be calling these allocation functions directly.
  * Instead, all calls should go through JSRuntime, JSContext or OffTheBooks.
  * However, js_free() can be called directly.
  */
 static JS_INLINE void* js_malloc(size_t bytes) {
     JS_OOM_POSSIBLY_FAIL();
-    return js_record_oom(malloc(bytes));
+    return malloc(bytes);
 }
 
 static JS_INLINE void* js_calloc(size_t bytes) {
     JS_OOM_POSSIBLY_FAIL();
-    return js_record_oom(calloc(bytes, 1));
+    return calloc(bytes, 1);
 }
 
 static JS_INLINE void* js_realloc(void* p, size_t bytes) {
     JS_OOM_POSSIBLY_FAIL();
-    return js_record_oom(realloc(p, bytes));
+    return realloc(p, bytes);
 }
 
 static JS_INLINE void js_free(void* p) {
     free(p);
 }
 #endif/* JS_USE_CUSTOM_ALLOCATOR */
 
 JS_END_EXTERN_C
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -150,20 +150,16 @@ class JS_FRIEND_API(JSCrossCompartmentWr
                            uintN argc, js::Value *argv, js::Value *rval);
     virtual bool hasInstance(JSContext *cx, JSObject *wrapper, const js::Value *vp, bool *bp);
     virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper);
     virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent);
     virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, js::Value *vp);
 
     virtual void trace(JSTracer *trc, JSObject *wrapper);
 
-    virtual bool isCrossCompartment() {
-        return true;
-    }
-
     static JSCrossCompartmentWrapper singleton;
 };
 
 namespace js {
 
 // A hacky class that lets a friend force a fake frame. We must already be
 // in the compartment of |target| when we enter the forced frame.
 class JS_FRIEND_API(ForceFrame)