Bug 634155: Account for NewCompartment's memory, and change allocation APIs (r=nnethercote)
authorPaul Biggar <pbiggar@mozilla.com>
Thu, 31 Mar 2011 01:13:49 -0700
changeset 64559 f949c8533aa03f15fb568d5b5ea2f2c7a9005e74
parent 64558 11d72b25348dd0c9eb03caad786c2a0663e46633
child 64560 d796fb18f555b76abfa6ac287e8c3f3760c6346e
push idunknown
push userunknown
push dateunknown
reviewersnnethercote
bugs634155
milestone2.2a1pre
Bug 634155: Account for NewCompartment's memory, and change allocation APIs (r=nnethercote) This changes the allocation API, in the following way: js_malloc -> {cx->,rt->,OffTheBooks::}malloc js_calloc -> {cx->,rt->,OffTheBooks::}calloc js_realloc -> {cx->,rt->,OffTheBooks::}realloc js_free -> {cx->,rt->,Foreground::,UnwantedForeground::}free js_new -> {cx->,rt->,OffTheBooks::}new_ js_new_array -> {cx->,rt->,OffTheBooks::}new_array js_delete -> {cx->,rt->,Foreground::,UnwantedForeground::}delete_ This is to move as many allocations as possible through a JSContext (so that they may be aken into account by gcMallocBytes) and to move as many deallocations to the background as possible (except on error paths).
config/check_source_count.py
config/find_vanilla_new_calls
js/src/Makefile.in
js/src/assembler/jit/ExecutableAllocator.h
js/src/config/check_source_count.py
js/src/config/find_vanilla_new_calls
js/src/ctypes/CTypes.cpp
js/src/ctypes/CTypes.h
js/src/js.mdp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarena.cpp
js/src/jsarena.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jsdate.cpp
js/src/jsdhash.cpp
js/src/jsdtoa.cpp
js/src/jsdtoa.h
js/src/jsemit.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jshash.cpp
js/src/jsinterp.cpp
js/src/jslock.cpp
js/src/jsnum.cpp
js/src/jsnum.h
js/src/jsobj.cpp
js/src/json.cpp
js/src/jsopcode.cpp
js/src/jsparse.h
js/src/jsprf.cpp
js/src/jspropertytree.cpp
js/src/jsregexp.cpp
js/src/jsregexpinlines.h
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jsscript.cpp
js/src/jsstr.cpp
js/src/jstl.h
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jstypedarray.cpp
js/src/jsutil.cpp
js/src/jsutil.h
js/src/jsxml.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/PolyIC.h
js/src/perf/jsperf.cpp
js/src/perf/pm_linux.cpp
js/src/yarr/pcre/pcre_compile.cpp
js/src/yarr/pcre/pcre_exec.cpp
js/src/yarr/yarr/RegExpJitTables.h
js/src/yarr/yarr/RegexCompiler.cpp
js/src/yarr/yarr/RegexPattern.h
new file mode 100755
--- /dev/null
+++ b/config/check_source_count.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+# Usage: check_source_count.py SEARCH_TERM COUNT ERROR_LOCATION REPLACEMENT [FILES...]
+#   Checks that FILES contains exactly COUNT matches of SEARCH_TERM. If it does
+#   not, an error message is printed, quoting ERROR_LOCATION, which should
+#   probably be the filename and line number of the erroneous call to
+#   check_source_count.py.
+
+import sys
+import os
+import re
+
+search_string = sys.argv[1]
+expected_count = int(sys.argv[2])
+error_location = sys.argv[3]
+replacement = sys.argv[4]
+files = sys.argv[5:]
+
+details = {}
+
+count = 0
+for f in files:
+    text = file(f).read()
+    match = re.findall(search_string, text)
+    if match:
+        num = len(match)
+        count += num
+        details[f] = num
+
+if count == expected_count:
+    print "TEST-PASS | check_source_count.py %s | %d" % (search_string, expected_count)
+
+else:
+    print "TEST-UNEXPECTED-FAIL | check_source_count.py %s | " % (search_string),
+    if count < expected_count:
+        print "There are less occurences of /%s/ than expected. This may mean that you have removed some, but forgotten to account for it %s." % (search_string, error_location)
+    else:
+        print "There are more occurences of /%s/ than expected. We're trying to prevent an increase in the number of %s's, using %s if possible. If it in unavoidable, you should update the expected count %s." % (search_string, search_string, replacement, error_location)
+
+    print "Expected: %d; found: %d" % (expected_count, count)
+    for k in sorted(details):
+        print "Found %d occurences in %s" % (details[k],k)
+    sys.exit(-1)
+
--- a/config/find_vanilla_new_calls
+++ b/config/find_vanilla_new_calls
@@ -14,17 +14,17 @@
 #   alloc/free checking.
 # - Doesn't detect the 'nothrow' variants, which are ok but probably still
 #   best avoided.
 # - Is designed to only run on Linux (though it may also work on Mac);  one
 #   platform will be enough to catch any violations.
 #
 # If this script fails:
 # - You need to find the uses of vanilla new/delete and replace them with
-#   js_new()/js_delete().
+#   {js::OffTheBooks,JSContext,JSRuntime}::{new_,/array_new}.
 # - Run this script on each of the .o files, that should narrow it down.
 # - After that, one way to find them is to run 'objdump -r -C' on the
 #   relevant .o files.  For example, you might search for 'operator new' and
 #   find a record like this:
 #
 #  RELOCATION RECORDS FOR [.text._ZN3JSC14ExecutablePool6createEj]:
 #  OFFSET   TYPE              VALUE
 #  00000009 R_386_PC32        __i686.get_pc_thunk.bx
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -587,21 +587,50 @@ check::
 	$(srcdir)/config/find_vanilla_new_calls $(LIBRARY)
 endif
 
 # Help ensure that the number of OOM errors in SpiderMonkey doesn't increase.
 # If the number of OOM errors changes, update the number below. We intend this
 # number to go down over time, by fixing OOMs.
 ifdef DEBUG
 check-ooms:
-	$(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/config/find_OOM_errors.py --regression 129
+	$(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/config/find_OOM_errors.py --regression 125
 
 check:: check-ooms
 endif
 
+## Prevent regressing in our deprecation of non-preferred memory management functions.
+# We use all the files in the distribution so that different configurations
+# don't give different results. We skip the contents of objdirs using |find|
+# (it can't be done with %-expansion, because the files we want to skip aren't
+# in the vpath).
+ALL_FILES=$(shell find $(srcdir) \( -name "*.cpp" -o -name "*.h" \) -not -path "*/dist/*")
+check-malloc-function-usage: $(filter-out %jsutil.h %jscntxt.h, $(ALL_FILES))
+
+   # js_malloc and friends are only used by other memory managers, and should
+   # never be used elsewhere directly.
+   $(srcdir)/config/check_source_count.py "\bjs_malloc\b" 0 \
+       "in Makefile.in" "cx->malloc_ or rt->malloc_" $^
+   $(srcdir)/config/check_source_count.py "\bjs_calloc\b" 0 \
+       "in Makefile.in" "cx->calloc_ or rt->calloc_" $^
+   $(srcdir)/config/check_source_count.py "\bjs_realloc\b" 0 \
+       "in Makefile.in" "cx->realloc_ or rt->realloc_" $^
+   $(srcdir)/config/check_source_count.py "\bjs_free\b" 0 \
+       "in Makefile.in" "cx->free_" $^
+
+   # We desire these numbers to go down, not up. See "User guide to memory
+   # management within SpiderMonkey" in jsutil.h.
+   $(srcdir)/config/check_source_count.py OffTheBooks:: 54 \
+       "in Makefile.in" "{cx,rt}->{new_,new_array,malloc_,calloc_,realloc_}" $^
+   # This should go to zero, if possible.
+   $(srcdir)/config/check_source_count.py UnwantedForeground:: 36 \
+       "in Makefile.in" "{cx,rt}->{free_,delete_,array_delete}" $^
+
+check:: check-malloc-function-usage
+
 ifndef WINCE
 JITFLAGS = ,m,j,mj,mjp,am,amj,amjp,amd
 check::
 	$(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/jit-test/jit_test.py \
 	        --no-slow --no-progress --tinderbox --jitflags=$(JITFLAGS) $(DIST)/bin/js$(BIN_SUFFIX)
 
 check-valgrind::
 	$(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/jit-test/jit_test.py \
--- a/js/src/assembler/jit/ExecutableAllocator.h
+++ b/js/src/assembler/jit/ExecutableAllocator.h
@@ -65,16 +65,18 @@ extern "C" __declspec(dllimport) void Ca
 #if ENABLE_ASSEMBLER
 
 //#define DEBUG_STRESS_JSC_ALLOCATOR
 
 namespace JSC {
 
   // These are reference-counted. A new one starts with a count of 1. 
   class ExecutablePool {
+
+    JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
     friend class ExecutableAllocator;
 private:
     struct Allocation {
         char* pages;
         size_t size;
 #if WTF_PLATFORM_SYMBIAN
         RChunk* chunk;
 #endif
@@ -95,19 +97,17 @@ public:
     // remember whether m_destroy was computed for the currently active GC.
     size_t m_gcNumber;
 
     void release(bool willDestroy = false)
     { 
         JS_ASSERT(m_refCount != 0);
         JS_ASSERT_IF(willDestroy, m_refCount = 1);
         if (--m_refCount == 0) {
-            /* We can't (easily) use js_delete() here because the destructor is private. */
-            this->~ExecutablePool();
-            js_free(this);
+            js::UnwantedForeground::delete_(this);
         }
     }
 
 private:
     // It should be impossible for us to roll over, because only small
     // pools have multiple holders, and they have one holder per chunk
     // of generated code, and they only hold 16KB or so of code.
     void addRef()
@@ -231,19 +231,17 @@ private:
 #ifdef DEBUG_STRESS_JSC_ALLOCATOR
         ExecutablePool::Allocation a = ExecutablePool::systemAlloc(size_t(4294967291));
 #else
         ExecutablePool::Allocation a = ExecutablePool::systemAlloc(allocSize);
 #endif
         if (!a.pages)
             return NULL;
 
-        /* We can't (easily) use js_new() here because the constructor is private. */
-        void *memory = js_malloc(sizeof(ExecutablePool));
-        return memory ? new(memory) ExecutablePool(a) : NULL;
+        return js::OffTheBooks::new_<ExecutablePool>(a);
     }
 
     ExecutablePool* poolForSize(size_t n)
     {
 #ifndef DEBUG_STRESS_JSC_ALLOCATOR
         // Try to fit in an existing small allocator.  Use the pool with the
         // least available space that is big enough (best-fit).  This is the
         // best strategy because (a) it maximizes the chance of the next
new file mode 100755
--- /dev/null
+++ b/js/src/config/check_source_count.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+# Usage: check_source_count.py SEARCH_TERM COUNT ERROR_LOCATION REPLACEMENT [FILES...]
+#   Checks that FILES contains exactly COUNT matches of SEARCH_TERM. If it does
+#   not, an error message is printed, quoting ERROR_LOCATION, which should
+#   probably be the filename and line number of the erroneous call to
+#   check_source_count.py.
+
+import sys
+import os
+import re
+
+search_string = sys.argv[1]
+expected_count = int(sys.argv[2])
+error_location = sys.argv[3]
+replacement = sys.argv[4]
+files = sys.argv[5:]
+
+details = {}
+
+count = 0
+for f in files:
+    text = file(f).read()
+    match = re.findall(search_string, text)
+    if match:
+        num = len(match)
+        count += num
+        details[f] = num
+
+if count == expected_count:
+    print "TEST-PASS | check_source_count.py %s | %d" % (search_string, expected_count)
+
+else:
+    print "TEST-UNEXPECTED-FAIL | check_source_count.py %s | " % (search_string),
+    if count < expected_count:
+        print "There are less occurences of /%s/ than expected. This may mean that you have removed some, but forgotten to account for it %s." % (search_string, error_location)
+    else:
+        print "There are more occurences of /%s/ than expected. We're trying to prevent an increase in the number of %s's, using %s if possible. If it in unavoidable, you should update the expected count %s." % (search_string, search_string, replacement, error_location)
+
+    print "Expected: %d; found: %d" % (expected_count, count)
+    for k in sorted(details):
+        print "Found %d occurences in %s" % (details[k],k)
+    sys.exit(-1)
+
--- a/js/src/config/find_vanilla_new_calls
+++ b/js/src/config/find_vanilla_new_calls
@@ -14,17 +14,17 @@
 #   alloc/free checking.
 # - Doesn't detect the 'nothrow' variants, which are ok but probably still
 #   best avoided.
 # - Is designed to only run on Linux (though it may also work on Mac);  one
 #   platform will be enough to catch any violations.
 #
 # If this script fails:
 # - You need to find the uses of vanilla new/delete and replace them with
-#   js_new()/js_delete().
+#   {js::OffTheBooks,JSContext,JSRuntime}::{new_,/array_new}.
 # - Run this script on each of the .o files, that should narrow it down.
 # - After that, one way to find them is to run 'objdump -r -C' on the
 #   relevant .o files.  For example, you might search for 'operator new' and
 #   find a record like this:
 #
 #  RELOCATION RECORDS FOR [.text._ZN3JSC14ExecutablePool6createEj]:
 #  OFFSET   TYPE              VALUE
 #  00000009 R_386_PC32        __i686.get_pc_thunk.bx
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -1839,34 +1839,34 @@ ImplicitConvert(JSContext* cx,
       case TYPE_unsigned_char: {
         // Convert from UTF-16 to UTF-8.
         size_t nbytes =
           js_GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
         if (nbytes == (size_t) -1)
           return false;
 
         char** charBuffer = static_cast<char**>(buffer);
-        *charBuffer = js_array_new<char>(nbytes + 1);
+        *charBuffer = cx->array_new<char>(nbytes + 1);
         if (!*charBuffer) {
           JS_ReportAllocationOverflow(cx);
           return false;
         }
 
         ASSERT_OK(js_DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
                     *charBuffer, &nbytes));
         (*charBuffer)[nbytes] = 0;
         *freePointer = true;
         break;
       }
       case TYPE_jschar: {
         // Copy the jschar string data. (We could provide direct access to the
         // JSString's buffer, but this approach is safer if the caller happens
         // to modify the string.)
         jschar** jscharBuffer = static_cast<jschar**>(buffer);
-        *jscharBuffer = js_array_new<jschar>(sourceLength + 1);
+        *jscharBuffer = cx->array_new<jschar>(sourceLength + 1);
         if (!*jscharBuffer) {
           JS_ReportAllocationOverflow(cx);
           return false;
         }
 
         *freePointer = true;
         memcpy(*jscharBuffer, sourceChars, sourceLength * sizeof(jschar));
         (*jscharBuffer)[sourceLength] = 0;
@@ -1941,17 +1941,17 @@ ImplicitConvert(JSContext* cx,
           targetLength != size_t(sourceLength)) {
         JS_ReportError(cx, "ArrayType length does not match source array length");
         return false;
       }
 
       // Convert into an intermediate, in case of failure.
       size_t elementSize = CType::GetSize(cx, baseType);
       size_t arraySize = elementSize * targetLength;
-      AutoPtr<char>::Array intermediate(js_array_new<char>(arraySize));
+      AutoPtr<char>::Array intermediate(cx->array_new<char>(arraySize));
       if (!intermediate) {
         JS_ReportAllocationOverflow(cx);
         return false;
       }
 
       for (jsuint i = 0; i < sourceLength; ++i) {
         js::AutoValueRooter item(cx);
         if (!JS_GetElement(cx, sourceArray, i, item.jsval_addr()))
@@ -1978,17 +1978,17 @@ ImplicitConvert(JSContext* cx,
       JSObject* obj = JSVAL_TO_OBJECT(val);
       JSObject* iter = JS_NewPropertyIterator(cx, obj);
       if (!iter)
         return false;
       js::AutoObjectRooter iterroot(cx, iter);
 
       // Convert into an intermediate, in case of failure.
       size_t structSize = CType::GetSize(cx, targetType);
-      AutoPtr<char>::Array intermediate(js_array_new<char>(structSize));
+      AutoPtr<char>::Array intermediate(cx->array_new<char>(structSize));
       if (!intermediate) {
         JS_ReportAllocationOverflow(cx);
         return false;
       }
 
       jsid id;
       size_t i = 0;
       while (1) {
@@ -2697,37 +2697,37 @@ CType::Finalize(JSContext* cx, JSObject*
     return;
 
   // The contents of our slots depends on what kind of type we are.
   switch (TypeCode(JSVAL_TO_INT(slot))) {
   case TYPE_function: {
     // Free the FunctionInfo.
     ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FNINFO, &slot));
     if (!JSVAL_IS_VOID(slot))
-      js_delete(static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot)));
+      cx->delete_(static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot)));
     break;
   }
 
   case TYPE_struct: {
     // Free the FieldInfoHash table.
     ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot));
     if (!JSVAL_IS_VOID(slot)) {
       void* info = JSVAL_TO_PRIVATE(slot);
-      js_delete(static_cast<FieldInfoHash*>(info));
+      cx->delete_(static_cast<FieldInfoHash*>(info));
     }
   }
 
     // Fall through.
   case TYPE_array: {
     // Free the ffi_type info.
     ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FFITYPE, &slot));
     if (!JSVAL_IS_VOID(slot)) {
       ffi_type* ffiType = static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
-      js_array_delete(ffiType->elements);
-      js_delete(ffiType);
+      cx->array_delete(ffiType->elements);
+      cx->delete_(ffiType);
     }
 
     break;
   }
   default:
     // Nothing to do here.
     break;
   }
@@ -3696,26 +3696,26 @@ ArrayType::BuildFFIType(JSContext* cx, J
 
   // Create an ffi_type to represent the array. This is necessary for the case
   // where the array is part of a struct. Since libffi has no intrinsic
   // support for array types, we approximate it by creating a struct type
   // with elements of type 'baseType' and with appropriate size and alignment
   // values. It would be nice to not do all the work of setting up 'elements',
   // but some libffi platforms currently require that it be meaningful. I'm
   // looking at you, x86_64.
-  AutoPtr<ffi_type> ffiType(js_new<ffi_type>());
+  AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>());
   if (!ffiType) {
     JS_ReportOutOfMemory(cx);
     return NULL;
   }
 
   ffiType->type = FFI_TYPE_STRUCT;
   ffiType->size = CType::GetSize(cx, obj);
   ffiType->alignment = CType::GetAlignment(cx, obj);
-  ffiType->elements = js_array_new<ffi_type*>(length + 1);
+  ffiType->elements = cx->array_new<ffi_type*>(length + 1);
   if (!ffiType->elements) {
     JS_ReportAllocationOverflow(cx);
     return NULL;
   }
 
   for (size_t i = 0; i < length; ++i)
     ffiType->elements[i] = ffiBaseType;
   ffiType->elements[length] = NULL;
@@ -4028,17 +4028,17 @@ StructType::DefineInternal(JSContext* cx
   if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
          NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
     return JS_FALSE;
 
   // Create a FieldInfoHash to stash on the type object, and an array to root
   // its constituents. (We cannot simply stash the hash in a reserved slot now
   // to get GC safety for free, since if anything in this function fails we
   // do not want to mutate 'typeObj'.)
-  AutoPtr<FieldInfoHash> fields(js_new<FieldInfoHash>());
+  AutoPtr<FieldInfoHash> fields(cx->new_<FieldInfoHash>());
   Array<jsval, 16> fieldRootsArray;
   if (!fields || !fields->init(len) || !fieldRootsArray.appendN(JSVAL_VOID, len)) {
     JS_ReportOutOfMemory(cx);
     return JS_FALSE;
   }
   js::AutoArrayRooter fieldRoots(cx, fieldRootsArray.length(), 
     fieldRootsArray.begin());
 
@@ -4137,26 +4137,26 @@ StructType::BuildFFIType(JSContext* cx, 
   JS_ASSERT(CType::IsSizeDefined(cx, obj));
 
   const FieldInfoHash* fields = GetFieldInfo(cx, obj);
   size_t len = fields->count();
 
   size_t structSize = CType::GetSize(cx, obj);
   size_t structAlign = CType::GetAlignment(cx, obj);
 
-  AutoPtr<ffi_type> ffiType(js_new<ffi_type>());
+  AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>());
   if (!ffiType) {
     JS_ReportOutOfMemory(cx);
     return NULL;
   }
   ffiType->type = FFI_TYPE_STRUCT;
 
   AutoPtr<ffi_type*>::Array elements;
   if (len != 0) {
-    elements = js_array_new<ffi_type*>(len + 1);
+    elements = cx->array_new<ffi_type*>(len + 1);
     if (!elements) {
       JS_ReportOutOfMemory(cx);
       return NULL;
     }
     elements[len] = NULL;
 
     for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
       const FieldInfoHash::Entry& entry = r.front();
@@ -4165,17 +4165,17 @@ StructType::BuildFFIType(JSContext* cx, 
         return NULL;
       elements[entry.value.mIndex] = fieldType;
     }
 
   } else {
     // Represent an empty struct as having a size of 1 byte, just like C++.
     JS_ASSERT(structSize == 1);
     JS_ASSERT(structAlign == 1);
-    elements = js_array_new<ffi_type*>(2);
+    elements = cx->array_new<ffi_type*>(2);
     if (!elements) {
       JS_ReportOutOfMemory(cx);
       return NULL;
     }
     elements[0] = &ffi_type_uint8;
     elements[1] = NULL;
   }
 
@@ -4506,24 +4506,24 @@ StructType::AddressOfField(JSContext* cx
 
 // Helper class for handling allocation of function arguments.
 struct AutoValue
 {
   AutoValue() : mData(NULL) { }
 
   ~AutoValue()
   {
-    js_array_delete(static_cast<char*>(mData));
+    UnwantedForeground::array_delete(static_cast<char*>(mData));
   }
 
   bool SizeToType(JSContext* cx, JSObject* type)
   {
     // Allocate a minimum of sizeof(ffi_arg) to handle small integers.
     size_t size = Align(CType::GetSize(cx, type), sizeof(ffi_arg));
-    mData = js_array_new<char>(size);
+    mData = cx->array_new<char>(size);
     if (mData)
       memset(mData, 0, size);
     return mData != NULL;
   }
 
   void* mData;
 };
 
@@ -4718,17 +4718,17 @@ FunctionType::BuildSymbolName(JSContext*
 
 static FunctionInfo*
 NewFunctionInfo(JSContext* cx,
                 jsval abiType,
                 jsval returnType,
                 jsval* argTypes,
                 uintN argLength)
 {
-  AutoPtr<FunctionInfo> fninfo(js_new<FunctionInfo>());
+  AutoPtr<FunctionInfo> fninfo(cx->new_<FunctionInfo>());
   if (!fninfo) {
     JS_ReportOutOfMemory(cx);
     return NULL;
   }
 
   ffi_abi abi;
   if (!GetABI(cx, abiType, &abi)) {
     JS_ReportError(cx, "Invalid ABI specification");
@@ -5194,17 +5194,17 @@ CClosure::Create(JSContext* cx,
     return NULL;
   js::AutoObjectRooter root(cx, result);
 
   // Get the FunctionInfo from the FunctionType.
   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
   JS_ASSERT(!fninfo->mIsVariadic);
   JS_ASSERT(GetABICode(cx, fninfo->mABI) != ABI_WINAPI);
 
-  AutoPtr<ClosureInfo> cinfo(js_new<ClosureInfo>());
+  AutoPtr<ClosureInfo> cinfo(cx->new_<ClosureInfo>());
   if (!cinfo) {
     JS_ReportOutOfMemory(cx);
     return NULL;
   }
 
   // Get the prototype of the FunctionType object, of class CTypeProto,
   // which stores our JSContext for use with the closure.
   JSObject* proto = JS_GetPrototype(cx, typeObj);
@@ -5307,17 +5307,17 @@ CClosure::Finalize(JSContext* cx, JSObje
   if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSUREINFO, &slot) ||
       JSVAL_IS_VOID(slot))
     return;
 
   ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
   if (cinfo->closure)
     ffi_closure_free(cinfo->closure);
 
-  js_delete(cinfo);
+  cx->delete_(cinfo);
 }
 
 void
 CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
 {
   JS_ASSERT(cif);
   JS_ASSERT(result);
   JS_ASSERT(args);
@@ -5484,47 +5484,47 @@ CData::Create(JSContext* cx,
     return NULL;
 
   // Set our ownership flag.
   if (!JS_SetReservedSlot(cx, dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult)))
     return NULL;
 
   // attach the buffer. since it might not be 2-byte aligned, we need to
   // allocate an aligned space for it and store it there. :(
-  char** buffer = js_new<char*>();
+  char** buffer = cx->new_<char*>();
   if (!buffer) {
     JS_ReportOutOfMemory(cx);
     return NULL;
   }
 
   char* data;
   if (!ownResult) {
     data = static_cast<char*>(source);
   } else {
     // Initialize our own buffer.
     size_t size = CType::GetSize(cx, typeObj);
-    data = js_array_new<char>(size);
+    data = cx->array_new<char>(size);
     if (!data) {
       // Report a catchable allocation error.
       JS_ReportAllocationOverflow(cx);
-      js_delete(buffer);
+      Foreground::delete_(buffer);
       return NULL;
     }
 
     if (!source)
       memset(data, 0, size);
     else
       memcpy(data, source, size);
   }
 
   *buffer = data;
   if (!JS_SetReservedSlot(cx, dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer))) {
     if (ownResult)
-      js_array_delete(data);
-    js_delete(buffer);
+      Foreground::array_delete(data);
+    Foreground::delete_(buffer);
     return NULL;
   }
 
   return dataObj;
 }
 
 void
 CData::Finalize(JSContext* cx, JSObject* obj)
@@ -5536,18 +5536,18 @@ CData::Finalize(JSContext* cx, JSObject*
 
   JSBool owns = JSVAL_TO_BOOLEAN(slot);
 
   if (!JS_GetReservedSlot(cx, obj, SLOT_DATA, &slot) || JSVAL_IS_VOID(slot))
     return;
   char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot));
 
   if (owns)
-    js_array_delete(*buffer);
-  js_delete(buffer);
+    cx->array_delete(*buffer);
+  cx->delete_(buffer);
 }
 
 JSObject*
 CData::GetCType(JSContext* cx, JSObject* dataObj)
 {
   JS_ASSERT(CData::IsCData(cx, dataObj));
 
   jsval slot;
@@ -5821,41 +5821,41 @@ Int64Base::Construct(JSContext* cx,
 {
   JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class;
   JSObject* result = JS_NewObject(cx, clasp, proto, JS_GetParent(cx, proto));
   if (!result)
     return NULL;
   js::AutoObjectRooter root(cx, result);
 
   // attach the Int64's data
-  JSUint64* buffer = js_new<JSUint64>(data);
+  JSUint64* buffer = cx->new_<JSUint64>(data);
   if (!buffer) {
     JS_ReportOutOfMemory(cx);
     return NULL;
   }
 
   if (!JS_SetReservedSlot(cx, result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer))) {
-    js_delete(buffer);
+    Foreground::delete_(buffer);
     return NULL;
   }
 
   if (!JS_FreezeObject(cx, result))
     return NULL;
 
   return result;
 }
 
 void
 Int64Base::Finalize(JSContext* cx, JSObject* obj)
 {
   jsval slot;
   if (!JS_GetReservedSlot(cx, obj, SLOT_INT64, &slot) || JSVAL_IS_VOID(slot))
     return;
 
-  js_delete(static_cast<JSUint64*>(JSVAL_TO_PRIVATE(slot)));
+  cx->delete_(static_cast<JSUint64*>(JSVAL_TO_PRIVATE(slot)));
 }
 
 JSUint64
 Int64Base::GetInt(JSContext* cx, JSObject* obj) {
   JS_ASSERT(Int64::IsInt64(cx, obj) || UInt64::IsUInt64(cx, obj));
 
   jsval slot;
   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_INT64, &slot));
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -51,28 +51,28 @@ namespace ctypes {
 /*******************************************************************************
 ** Utility classes
 *******************************************************************************/
 
 template<class T>
 class OperatorDelete
 {
 public:
-  static void destroy(T* ptr) { js_delete(ptr); }
+  static void destroy(T* ptr) { UnwantedForeground::delete_(ptr); }
 };
 
 template<class T>
 class OperatorArrayDelete
 {
 public:
-  static void destroy(T* ptr) { js_array_delete(ptr); }
+  static void destroy(T* ptr) { UnwantedForeground::array_delete(ptr); }
 };
 
-// Class that takes ownership of a pointer T*, and calls js_delete() or
-// js_array_delete() upon destruction.
+// Class that takes ownership of a pointer T*, and calls cx->delete_() or
+// cx->array_delete() upon destruction.
 template<class T, class DeleteTraits = OperatorDelete<T> >
 class AutoPtr {
 private:
   typedef AutoPtr<T, DeleteTraits> self_type;
 
 public:
   // An AutoPtr variant that calls js_array_delete() instead.
   typedef AutoPtr<T, OperatorArrayDelete<T> > Array;
index 8da64fb6b61a5a047ac54c88020cf397fa7eaa25..14fb02a255f4cfa8300c9b73209116184f2fbf4f
GIT binary patch
literal 37
sc${;}%u6h)R7lIrNmbA&E=kNwNi0fH$jmD!EzwlSO)N>yNG;|90RKh}M*si-
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -664,23 +664,29 @@ JSRuntime::init(uint32 maxbytes)
             return false;
         if (!unjoinedFunctionCountMap.init())
             return false;
     }
     propTreeStatFilename = getenv("JS_PROPTREE_STATFILE");
     propTreeDumpFilename = getenv("JS_PROPTREE_DUMPFILE");
 #endif
 
-    if (!(atomsCompartment = js_new<JSCompartment>(this)) ||
+    if (!js_InitGC(this, maxbytes))
+        return false;
+
+    if (!(atomsCompartment = this->new_<JSCompartment>(this)) ||
         !atomsCompartment->init() ||
         !compartments.append(atomsCompartment)) {
+        Foreground::delete_(atomsCompartment);
         return false;
     }
 
-    if (!js_InitGC(this, maxbytes) || !js_InitAtomState(this))
+    atomsCompartment->setGCLastBytes(8192);
+
+    if (!js_InitAtomState(this))
         return false;
 
     wrapObjectCallback = js::TransparentObjectWrapper;
 
 #ifdef JS_THREADSAFE
     /* this is asymmetric with JS_ShutDown: */
     if (!js_SetupLocks(8, 16))
         return false;
@@ -769,35 +775,33 @@ JS_NewRuntime(uint32 maxbytes)
     JS_END_MACRO;
 #include "js.msg"
 #undef MSG_DEF
 
         js_NewRuntimeWasCalled = JS_TRUE;
     }
 #endif /* DEBUG */
 
-    void *mem = js_calloc(sizeof(JSRuntime));
+    void *mem = OffTheBooks::calloc(sizeof(JSRuntime));
     if (!mem)
         return NULL;
 
     JSRuntime *rt = new (mem) JSRuntime();
     if (!rt->init(maxbytes)) {
         JS_DestroyRuntime(rt);
         return NULL;
     }
 
     return rt;
 }
 
 JS_PUBLIC_API(void)
 JS_DestroyRuntime(JSRuntime *rt)
 {
-    rt->~JSRuntime();
-
-    js_free(rt);
+    Foreground::delete_(rt);
 }
 
 #ifdef JS_REPRMETER
 namespace reprmeter {
     extern void js_DumpReprMeter();
 }
 #endif
 
@@ -1193,21 +1197,21 @@ JS_SetWrapObjectCallbacks(JSRuntime *rt,
 }
 
 JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target)
 {
     CHECK_REQUEST(cx);
 
     JS_ASSERT(target);
-    AutoCompartment *call = js_new<AutoCompartment>(cx, target);
+    AutoCompartment *call = cx->new_<AutoCompartment>(cx, target);
     if (!call)
         return NULL;
     if (!call->enter()) {
-        js_delete(call);
+        Foreground::delete_(call);
         return NULL;
     }
     return reinterpret_cast<JSCrossCompartmentCall *>(call);
 }
 
 JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target)
 {
@@ -1235,17 +1239,17 @@ JS_EnterCrossCompartmentCallScript(JSCon
 }
 
 JS_PUBLIC_API(void)
 JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call)
 {
     AutoCompartment *realcall = reinterpret_cast<AutoCompartment *>(call);
     CHECK_REQUEST(realcall->context);
     realcall->leave();
-    js_delete(realcall);
+    Foreground::delete_(realcall);
 }
 
 bool
 JSAutoEnterCompartment::enter(JSContext *cx, JSObject *target)
 {
     JS_ASSERT(!call);
     if (cx->compartment == target->getCompartment()) {
         call = reinterpret_cast<JSCrossCompartmentCall*>(1);
@@ -2467,17 +2471,17 @@ DumpNotify(JSTracer *trc, void *thing, u
                     (const char *)dtrc->base.debugPrintArg,
                     dtrc->base.debugPrintIndex);
         edgeName = dtrc->buffer;
     } else {
         edgeName = (const char*)dtrc->base.debugPrintArg;
     }
 
     edgeNameSize = strlen(edgeName) + 1;
-    node = (JSHeapDumpNode *) js_malloc(offsetof(JSHeapDumpNode, edgeName) + edgeNameSize);
+    node = (JSHeapDumpNode *) cx->malloc(offsetof(JSHeapDumpNode, edgeName) + edgeNameSize);
     if (!node) {
         dtrc->ok = JS_FALSE;
         return;
     }
 
     node->thing = thing;
     node->kind = kind;
     node->next = NULL;
@@ -2619,17 +2623,17 @@ JS_DumpHeap(JSContext *cx, FILE *fp, voi
                 }
             }
         }
 
         /* Move to next or parents next and free the node. */
         for (;;) {
             next = node->next;
             parent = node->parent;
-            js_free(node);
+            cx->free(node);
             node = next;
             if (node)
                 break;
             if (!parent)
                 goto dump_out;
             JS_ASSERT(depth > 1);
             --depth;
             node = parent;
@@ -4665,48 +4669,48 @@ CompileFileHelper(JSContext *cx, JSObjec
     /* Read in the whole file, then compile it. */
     if (fp == stdin) {
         JS_ASSERT(len == 0);
         len = 8;  /* start with a small buffer, expand as necessary */
         int c;
         bool hitEOF = false;
         while (!hitEOF) {
             len *= 2;
-            jschar* tmpbuf = (jschar *) js_realloc(buf, len * sizeof(jschar));
+            jschar* tmpbuf = (jschar *) cx->realloc(buf, len * sizeof(jschar));
             if (!tmpbuf) {
                 cx->free(buf);
                 return NULL;
             }
             buf = tmpbuf;
 
             while (i < len) {
                 c = fast_getc(fp);
                 if (c == EOF) {
                     hitEOF = true;
                     break;
                 }
                 buf[i++] = (jschar) (unsigned char) c;
             }
         }
     } else {
-        buf = (jschar *) js_malloc(len * sizeof(jschar));
+        buf = (jschar *) cx->malloc(len * sizeof(jschar));
         if (!buf)
             return NULL;
 
         int c;
         while ((c = fast_getc(fp)) != EOF)
             buf[i++] = (jschar) (unsigned char) c;
     }
 
     JS_ASSERT(i <= len);
     len = i;
     uint32 tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT;
     script = Compiler::compileScript(cx, obj, NULL, principals, tcflags, buf, len, filename, 1,
                                      cx->findVersion());
-    js_free(buf);
+    cx->free(buf);
     if (!script)
         return NULL;
 
     JSObject *scriptObj = js_NewScriptObject(cx, script);
     if (!scriptObj)
         js_DestroyScript(cx, script);
     
     return scriptObj;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -42,17 +42,16 @@
 #define jsapi_h___
 /*
  * JavaScript API.
  */
 #include <stddef.h>
 #include <stdio.h>
 #include "js-config.h"
 #include "jspubtd.h"
-#include "jsutil.h"
 
 JS_BEGIN_EXTERN_C
 
 /*
  * In release builds, jsval and jsid are defined to be integral types. This
  * prevents many bugs from being caught at compile time. E.g.:
  *
  *  jsval v = ...
@@ -3233,34 +3232,34 @@ class JSAutoByteString {
     }
 
     JSAutoByteString(JS_GUARD_OBJECT_NOTIFIER_PARAM0)
       : mBytes(NULL) {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     ~JSAutoByteString() {
-        js_free(mBytes);
+        js::UnwantedForeground::free(mBytes);
     }
 
     /* Take ownership of the given byte array. */
     void initBytes(char *bytes) {
         JS_ASSERT(!mBytes);
         mBytes = bytes;
     }
 
     char *encode(JSContext *cx, JSString *str) {
         JS_ASSERT(!mBytes);
         JS_ASSERT(cx);
         mBytes = JS_EncodeString(cx, str);
         return mBytes;
     }
 
     void clear() {
-        js_free(mBytes);
+        js::UnwantedForeground::free(mBytes);
         mBytes = NULL;
     }
 
     char *ptr() const {
         return mBytes;
     }
 
     bool operator!() const {
@@ -3323,17 +3322,17 @@ struct JSStructuredCloneCallbacks {
 };
 
 JS_PUBLIC_API(JSBool)
 JS_ReadStructuredClone(JSContext *cx, const uint64 *data, size_t nbytes,
                        uint32 version, jsval *vp,
                        const JSStructuredCloneCallbacks *optionalCallbacks,
                        void *closure);
 
-/* Note: On success, the caller is responsible for calling js_free(*datap). */
+/* Note: On success, the caller is responsible for calling js::Foreground::free(*datap). */
 JS_PUBLIC_API(JSBool)
 JS_WriteStructuredClone(JSContext *cx, jsval v, uint64 **datap, size_t *nbytesp,
                         const JSStructuredCloneCallbacks *optionalCallbacks,
                         void *closure);
 
 JS_PUBLIC_API(JSBool)
 JS_StructuredClone(JSContext *cx, jsval v, jsval *vp,
                    const JSStructuredCloneCallbacks *optionalCallbacks,
--- a/js/src/jsarena.cpp
+++ b/js/src/jsarena.cpp
@@ -45,16 +45,18 @@
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsbit.h"
 #include "jsarena.h"
 #include "jsprvtd.h"
 
+using namespace js;
+
 #ifdef JS_ARENAMETER
 static JSArenaStats *arena_stats_list;
 
 #define COUNT(pool,what)  (pool)->stats.what++
 #else
 #define COUNT(pool,what)  /* nothing */
 #endif
 
@@ -155,22 +157,22 @@ JS_ArenaAllocate(JSArenaPool *pool, size
             extra = (nb > pool->arenasize) ? HEADER_SIZE(pool) : 0;
             hdrsz = sizeof *a + extra + pool->mask;
             gross = hdrsz + JS_MAX(nb, pool->arenasize);
             if (gross < nb)
                 return NULL;
             if (pool->quotap) {
                 if (gross > *pool->quotap)
                     return NULL;
-                b = (JSArena *) js_malloc(gross);
+                b = (JSArena *) OffTheBooks::malloc(gross);
                 if (!b)
                     return NULL;
                 *pool->quotap -= gross;
             } else {
-                b = (JSArena *) js_malloc(gross);
+                b = (JSArena *) OffTheBooks::malloc(gross);
                 if (!b)
                     return NULL;
             }
 
             b->next = NULL;
             b->limit = (jsuword)b + gross;
             JS_COUNT_ARENA(pool,++);
             COUNT(pool, nmallocs);
@@ -222,22 +224,22 @@ JS_ArenaRealloc(JSArenaPool *pool, void 
     extra = HEADER_SIZE(pool);                  /* oversized header holds ap */
     hdrsz = sizeof *a + extra + pool->mask;     /* header and alignment slop */
     gross = hdrsz + aoff;
     JS_ASSERT(gross > aoff);
     if (pool->quotap) {
         growth = gross - (a->limit - (jsuword) a);
         if (growth > *pool->quotap)
             return NULL;
-        a = (JSArena *) js_realloc(a, gross);
+        a = (JSArena *) OffTheBooks::realloc(a, gross);
         if (!a)
             return NULL;
         *pool->quotap -= growth;
     } else {
-        a = (JSArena *) js_realloc(a, gross);
+        a = (JSArena *) OffTheBooks::realloc(a, gross);
         if (!a)
             return NULL;
     }
 #ifdef JS_ARENAMETER
     pool->stats.nreallocs++;
 #endif
 
     if (a != *ap) {
@@ -310,17 +312,17 @@ FreeArenaList(JSArenaPool *pool, JSArena
 #endif
 
     do {
         *ap = a->next;
         if (pool->quotap)
             *pool->quotap += a->limit - (jsuword) a;
         JS_CLEAR_ARENA(a);
         JS_COUNT_ARENA(pool,--);
-        js_free(a);
+        UnwantedForeground::free(a);
     } while ((a = *ap) != NULL);
 
     pool->current = head;
 }
 
 JS_PUBLIC_API(void)
 JS_ArenaRelease(JSArenaPool *pool, char *mark)
 {
@@ -349,17 +351,17 @@ JS_PUBLIC_API(void)
 JS_FinishArenaPool(JSArenaPool *pool)
 {
     FreeArenaList(pool, &pool->first);
 #ifdef JS_ARENAMETER
     {
         JSArenaStats *stats, **statsp;
 
         if (pool->stats.name) {
-            js_free(pool->stats.name);
+            UnwantedForeground::free(pool->stats.name);
             pool->stats.name = NULL;
         }
         for (statsp = &arena_stats_list; (stats = *statsp) != 0;
              statsp = &stats->next) {
             if (stats == &pool->stats) {
                 *statsp = stats->next;
                 return;
             }
--- a/js/src/jsarena.h
+++ b/js/src/jsarena.h
@@ -197,17 +197,17 @@ struct JSArenaPool {
 #endif
 
 #define JS_ARENA_DESTROY(pool, a, pnext)                                      \
     JS_BEGIN_MACRO                                                            \
         JS_COUNT_ARENA(pool,--);                                              \
         if ((pool)->current == (a)) (pool)->current = &(pool)->first;         \
         *(pnext) = (a)->next;                                                 \
         JS_CLEAR_ARENA(a);                                                    \
-        js_free(a);                                                              \
+        js::UnwantedForeground::free(a);                                      \
         (a) = NULL;                                                           \
     JS_END_MACRO
 
 /*
  * Initialize an arena pool with a minimum size per arena of size bytes.
  */
 extern JS_PUBLIC_API(void)
 JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size,
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -560,23 +560,23 @@ JSThreadData::purge(JSContext *cx)
 }
 
 #ifdef JS_THREADSAFE
 
 static JSThread *
 NewThread(void *id)
 {
     JS_ASSERT(js_CurrentThreadId() == id);
-    JSThread *thread = (JSThread *) js_calloc(sizeof(JSThread));
+    JSThread *thread = (JSThread *) OffTheBooks::calloc(sizeof(JSThread));
     if (!thread)
         return NULL;
     JS_INIT_CLIST(&thread->contextList);
     thread->id = id;
     if (!thread->data.init()) {
-        js_free(thread);
+        Foreground::free(thread);
         return NULL;
     }
     return thread;
 }
 
 static void
 DestroyThread(JSThread *thread)
 {
@@ -585,17 +585,17 @@ DestroyThread(JSThread *thread)
 
     /*
      * The conservative GC scanner should be disabled when the thread leaves
      * the last request.
      */
     JS_ASSERT(!thread->data.conservativeGC.hasStackToScan());
 
     thread->data.finish();
-    js_free(thread);
+    Foreground::free(thread);
 }
 
 JSThread *
 js_CurrentThread(JSRuntime *rt)
 {
     void *id = js_CurrentThreadId();
     JS_LOCK_GC(rt);
 
@@ -742,17 +742,17 @@ js_NewContext(JSRuntime *rt, size_t stac
     JSBool ok, first;
     JSContextCallback cxCallback;
 
     /*
      * We need to initialize the new context fully before adding it to the
      * runtime list. After that it can be accessed from another thread via
      * js_ContextIterator.
      */
-    void *mem = js_calloc(sizeof *cx);
+    void *mem = OffTheBooks::calloc(sizeof *cx);
     if (!mem)
         return NULL;
 
     cx = new (mem) JSContext(rt);
     cx->debugHooks = &rt->globalDebugHooks;
 #if JS_STACK_GROWTH_DIRECTION > 0
     cx->stackLimit = (jsuword) -1;
 #endif
@@ -1118,31 +1118,30 @@ FreeContext(JSContext *cx)
 #endif
 
     /* Free the stuff hanging off of cx. */
     VOUCH_DOES_NOT_REQUIRE_STACK();
     JS_FinishArenaPool(&cx->tempPool);
     JS_FinishArenaPool(&cx->regExpPool);
 
     if (cx->lastMessage)
-        js_free(cx->lastMessage);
+        cx->free(cx->lastMessage);
 
     /* Remove any argument formatters. */
     JSArgumentFormatMap *map = cx->argumentFormatMap;
     while (map) {
         JSArgumentFormatMap *temp = map;
         map = map->next;
         cx->free(temp);
     }
 
     JS_ASSERT(!cx->resolvingList);
 
     /* Finally, free cx itself. */
-    cx->~JSContext();
-    js_free(cx);
+    Foreground::delete_(cx);
 }
 
 JSContext *
 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
 {
     JSContext *cx = *iterp;
 
     Conditionally<AutoLockGC> lockIf(!!unlocked, rt);
@@ -1363,18 +1362,18 @@ js_ReportErrorVA(JSContext *cx, uintN fl
     report.flags = flags;
     report.errorNumber = JSMSG_USER_DEFINED_ERROR;
     report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);
     PopulateReportBlame(cx, &report);
 
     warning = JSREPORT_IS_WARNING(report.flags);
 
     ReportError(cx, message, &report, NULL, NULL);
-    js_free(message);
-    cx->free(ucmessage);
+    Foreground::free(message);
+    Foreground::free(ucmessage);
     return warning;
 }
 
 /*
  * The arguments from ap need to be packaged up into an array and stored
  * into the report struct.
  *
  * The format string addressed by the error number may contain operands
@@ -1587,17 +1586,17 @@ JS_FRIEND_API(void)
 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
 {
     JSErrorReporter onError;
 
     if (!message)
         return;
 
     if (cx->lastMessage)
-        js_free(cx->lastMessage);
+        Foreground::free(cx->lastMessage);
     cx->lastMessage = JS_strdup(cx, message);
     if (!cx->lastMessage)
         return;
     onError = cx->errorReporter;
 
     /*
      * If debugErrorHook is present then we give it a chance to veto
      * sending the error on to the regular ErrorReporter.
@@ -2095,21 +2094,21 @@ JSRuntime::onTooMuchMalloc()
 }
 
 JS_FRIEND_API(void *)
 JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
 {
 #ifdef JS_THREADSAFE
     gcHelperThread.waitBackgroundSweepEnd(this);
     if (!p)
-        p = ::js_malloc(nbytes);
+        p = OffTheBooks::malloc(nbytes);
     else if (p == reinterpret_cast<void *>(1))
-        p = ::js_calloc(nbytes);
+        p = OffTheBooks::calloc(nbytes);
     else
-      p = ::js_realloc(p, nbytes);
+      p = OffTheBooks::realloc(p, nbytes);
     if (p)
         return p;
 #endif
     if (cx)
         js_ReportOutOfMemory(cx);
     return NULL;
 }
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1378,17 +1378,23 @@ struct JSRuntime {
          * previously allocated memory.
          */
         if (!p)
             updateMallocCounter(bytes);
         void *p2 = ::js_realloc(p, bytes);
         return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, bytes, cx);
     }
 
-    void free(void* p) { ::js_free(p); }
+    inline void free(void* p) {
+        /* FIXME: Making this free in the background is buggy. Can it work? */
+        js::Foreground::free(p);
+    }
+
+    JS_DECLARE_NEW_METHODS(malloc, JS_ALWAYS_INLINE)
+    JS_DECLARE_DELETE_METHODS(free, JS_ALWAYS_INLINE)
 
     bool isGCMallocLimitReached() const { return gcMallocBytes <= 0; }
 
     void resetGCMallocBytes() { gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); }
 
     void setGCMaxMallocBytes(size_t value) {
         /*
          * For compatibility treat any value that exceeds PTRDIFF_T_MAX to
@@ -2016,53 +2022,18 @@ struct JSContext
         if (gcBackgroundFree) {
             gcBackgroundFree->freeLater(p);
             return;
         }
 #endif
         runtime->free(p);
     }
 
-    /*
-     * In the common case that we'd like to allocate the memory for an object
-     * with cx->malloc/free, we cannot use overloaded C++ operators (no
-     * placement delete).  Factor the common workaround into one place.
-     */
-#define CREATE_BODY(parms)                                                    \
-    void *memory = this->malloc(sizeof(T));                                   \
-    if (!memory)                                                              \
-        return NULL;                                                          \
-    return new(memory) T parms;
-
-    template <class T>
-    JS_ALWAYS_INLINE T *create() {
-        CREATE_BODY(())
-    }
-
-    template <class T, class P1>
-    JS_ALWAYS_INLINE T *create(const P1 &p1) {
-        CREATE_BODY((p1))
-    }
-
-    template <class T, class P1, class P2>
-    JS_ALWAYS_INLINE T *create(const P1 &p1, const P2 &p2) {
-        CREATE_BODY((p1, p2))
-    }
-
-    template <class T, class P1, class P2, class P3>
-    JS_ALWAYS_INLINE T *create(const P1 &p1, const P2 &p2, const P3 &p3) {
-        CREATE_BODY((p1, p2, p3))
-    }
-#undef CREATE_BODY
-
-    template <class T>
-    JS_ALWAYS_INLINE void destroy(T *p) {
-        p->~T();
-        this->free(p);
-    }
+    JS_DECLARE_NEW_METHODS(malloc, inline)
+    JS_DECLARE_DELETE_METHODS(free, inline)
 
     void purge();
 
     js::StackSpace &stack() const {
         return JS_THREAD_DATA(this)->stackSpace;
     }
 
 #ifdef DEBUG
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -95,28 +95,28 @@ JSCompartment::JSCompartment(JSRuntime *
 #endif
 
     PodArrayZero(scriptsToGC);
 }
 
 JSCompartment::~JSCompartment()
 {
 #if ENABLE_YARR_JIT
-    js_delete(regExpAllocator);
+    Foreground::delete_(regExpAllocator);
 #endif
 
 #if defined JS_TRACER
     FinishJIT(&traceMonitor);
 #endif
 
 #ifdef JS_METHODJIT
-    js_delete(jaegerCompartment);
+    Foreground::delete_(jaegerCompartment);
 #endif
 
-    js_delete(mathCache);
+    Foreground::delete_(mathCache);
 
 #ifdef DEBUG
     for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
         JS_ASSERT(!scriptsToGC[i]);
 #endif
 }
 
 bool
@@ -136,31 +136,31 @@ JSCompartment::init()
 #ifdef DEBUG
     if (rt->meterEmptyShapes()) {
         if (!emptyShapes.init())
             return false;
     }
 #endif
 
 #ifdef JS_TRACER
-    if (!InitJIT(&traceMonitor))
+    if (!InitJIT(&traceMonitor, rt))
         return false;
 #endif
 
 #if ENABLE_YARR_JIT
-    regExpAllocator = js_new<JSC::ExecutableAllocator>();
+    regExpAllocator = rt->new_<JSC::ExecutableAllocator>();
     if (!regExpAllocator)
         return false;
 #endif
 
     if (!backEdgeTable.init())
         return false;
 
 #ifdef JS_METHODJIT
-    if (!(jaegerCompartment = js_new<mjit::JaegerCompartment>()))
+    if (!(jaegerCompartment = rt->new_<mjit::JaegerCompartment>()))
         return false;
     return jaegerCompartment->Initialize();
 #else
     return true;
 #endif
 }
 
 bool
@@ -569,17 +569,17 @@ JSCompartment::purge(JSContext *cx)
     }
 #endif
 }
 
 MathCache *
 JSCompartment::allocMathCache(JSContext *cx)
 {
     JS_ASSERT(!mathCache);
-    mathCache = js_new<MathCache>();
+    mathCache = cx->new_<MathCache>();
     if (!mathCache)
         js_ReportOutOfMemory(cx);
     return mathCache;
 }
 
 size_t
 JSCompartment::backEdgeCount(jsbytecode *pc) const
 {
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2401,17 +2401,17 @@ date_toSource(JSContext *cx, uintN argc,
 
     char *bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
     if (!bytes) {
         JS_ReportOutOfMemory(cx);
         return false;
     }
 
     JSString *str = JS_NewStringCopyZ(cx, bytes);
-    js_free(bytes);
+    cx->free(bytes);
     if (!str)
         return false;
     vp->setString(str);
     return true;
 }
 #endif
 
 static JSBool
--- a/js/src/jsdhash.cpp
+++ b/js/src/jsdhash.cpp
@@ -42,17 +42,19 @@
  * Double hashing implementation.
  */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "jsstdint.h"
 #include "jsbit.h"
 #include "jsdhash.h"
-#include "jsutil.h"     /* for JS_ASSERT */
+#include "jsutil.h"
+
+using namespace js;
 
 #ifdef JS_DHASHMETER
 # if defined MOZILLA_CLIENT && defined DEBUG_XXXbrendan
 #  include "nsTraceMalloc.h"
 # endif
 # define METER(x)       x
 #else
 # define METER(x)       /* nothing */
@@ -106,23 +108,23 @@
 #define INCREMENT_RECURSION_LEVEL(table_)   JS_BEGIN_MACRO JS_END_MACRO
 #define DECREMENT_RECURSION_LEVEL(table_)   JS_BEGIN_MACRO JS_END_MACRO
 
 #endif /* defined(DEBUG) */
 
 JS_PUBLIC_API(void *)
 JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes)
 {
-    return js_malloc(nbytes);
+    return OffTheBooks::malloc(nbytes);
 }
 
 JS_PUBLIC_API(void)
 JS_DHashFreeTable(JSDHashTable *table, void *ptr)
 {
-    js_free(ptr);
+    UnwantedForeground::free(ptr);
 }
 
 JS_PUBLIC_API(JSDHashNumber)
 JS_DHashStringKey(JSDHashTable *table, const void *key)
 {
     JSDHashNumber h;
     const unsigned char *s;
 
@@ -175,17 +177,17 @@ JS_DHashClearEntryStub(JSDHashTable *tab
     memset(entry, 0, table->entrySize);
 }
 
 JS_PUBLIC_API(void)
 JS_DHashFreeStringKey(JSDHashTable *table, JSDHashEntryHdr *entry)
 {
     const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry;
 
-    js_free((void *) stub->key);
+    UnwantedForeground::free((void *) stub->key);
     memset(entry, 0, table->entrySize);
 }
 
 JS_PUBLIC_API(void)
 JS_DHashFinalizeStub(JSDHashTable *table)
 {
 }
 
@@ -207,31 +209,31 @@ JS_DHashGetStubOps(void)
 }
 
 JS_PUBLIC_API(JSDHashTable *)
 JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize,
                  uint32 capacity)
 {
     JSDHashTable *table;
 
-    table = (JSDHashTable *) js_malloc(sizeof *table);
+    table = (JSDHashTable *) OffTheBooks::malloc(sizeof *table);
     if (!table)
         return NULL;
     if (!JS_DHashTableInit(table, ops, data, entrySize, capacity)) {
-        js_free(table);
+        Foreground::free(table);
         return NULL;
     }
     return table;
 }
 
 JS_PUBLIC_API(void)
 JS_DHashTableDestroy(JSDHashTable *table)
 {
     JS_DHashTableFinish(table);
-    js_free(table);
+    UnwantedForeground::free(table);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data,
                   uint32 entrySize, uint32 capacity)
 {
     int log2;
     uint32 nbytes;
--- a/js/src/jsdtoa.cpp
+++ b/js/src/jsdtoa.cpp
@@ -48,16 +48,18 @@
 #include "jsprvtd.h"
 #include "jsnum.h"
 #include "jsbit.h"
 #include "jslibmath.h"
 #include "jscntxt.h"
 
 #include "jsobjinlines.h"
 
+using namespace js;
+
 #ifdef IS_LITTLE_ENDIAN
 #define IEEE_8087
 #else
 #define IEEE_MC68k
 #endif
 
 #ifndef Long
 #define Long int32
@@ -72,19 +74,26 @@
 #define Llong JSInt64
 #endif
 
 #ifndef ULlong
 #define ULlong JSUint64
 #endif
 */
 
+/*
+ * MALLOC gets declared external, and that doesn't work for class members, so
+ * wrap.
+ */
+inline void* dtoa_malloc(size_t size) { return OffTheBooks::malloc(size); }
+inline void dtoa_free(void* p) { return UnwantedForeground::free(p); }
+
 #define NO_GLOBAL_STATE
-#define MALLOC js_malloc
-#define FREE js_free
+#define MALLOC dtoa_malloc
+#define FREE dtoa_free
 #include "dtoa.c"
 
 /* Mapping of JSDToStrMode -> js_dtoa mode */
 static const uint8 dtoaModes[] = {
     0,   /* DTOSTR_STANDARD */
     0,   /* DTOSTR_STANDARD_EXPONENTIAL, */
     3,   /* DTOSTR_FIXED, */
     2,   /* DTOSTR_EXPONENTIAL, */
@@ -322,17 +331,17 @@ js_dtobasestr(DtoaState *state, int base
     char *q;
     uint32 digit;
     U di;                /* d truncated to an integer */
     U df;                /* The fractional part of d */
 
     JS_ASSERT(base >= 2 && base <= 36);
 
     dval(d) = dinput;
-    buffer = (char*) js_malloc(DTOBASESTR_BUFFER_SIZE);
+    buffer = (char*) OffTheBooks::malloc(DTOBASESTR_BUFFER_SIZE);
     if (!buffer)
         return NULL;
     p = buffer;
 
     if (dval(d) < 0.0
 #if defined(XP_WIN) || defined(XP_OS2)
         && !((word0(d) & Exp_mask) == Exp_mask && ((word0(d) & Frac_mask) || word1(d))) /* Visual C++ doesn't know how to compare against NaN */
 #endif
@@ -366,17 +375,17 @@ js_dtobasestr(DtoaState *state, int base
         int bits;  /* Number of significant bits in di; not used. */
         Bigint *b = d2b(PASS_STATE di, &e, &bits);
         if (!b)
             goto nomem1;
         b = lshift(PASS_STATE b, e);
         if (!b) {
           nomem1:
             Bfree(PASS_STATE b);
-            js_free(buffer);
+            UnwantedForeground::free(buffer);
             return NULL;
         }
         do {
             digit = divrem(b, base);
             JS_ASSERT(digit < (uint32)base);
             *p++ = BASEDIGIT(digit);
         } while (b->wds);
         Bfree(PASS_STATE b);
@@ -402,17 +411,17 @@ js_dtobasestr(DtoaState *state, int base
         b = d2b(PASS_STATE df, &e, &bbits);
         if (!b) {
           nomem2:
             Bfree(PASS_STATE b);
             Bfree(PASS_STATE s);
             if (mlo != mhi)
                 Bfree(PASS_STATE mlo);
             Bfree(PASS_STATE mhi);
-            js_free(buffer);
+            UnwantedForeground::free(buffer);
             return NULL;
         }
         JS_ASSERT(e < 0);
         /* At this point df = b * 2^e.  e must be less than zero because 0 < df < 1. */
 
         s2 = -(int32)(word0(d) >> Exp_shift1 & Exp_mask>>Exp_shift1);
 #ifndef Sudden_Underflow
         if (!s2)
--- a/js/src/jsdtoa.h
+++ b/js/src/jsdtoa.h
@@ -133,16 +133,16 @@ js_dtostr(DtoaState *state, char *buffer
  * round-trip property (analogous to that of printing decimal numbers).  In
  * other words, if one were to read the resulting string in via a hypothetical
  * base-number-reading routine that rounds to the nearest IEEE double (and to
  * an even significand if there are two equally near doubles), then the result
  * would equal d (except for -0.0, which converts to "0", and NaN, which is
  * not equal to itself).
  *
  * Return NULL if out of memory.  If the result is not NULL, it must be
- * released via js_free().
+ * released via cx->free().
  */
 char *
 js_dtobasestr(DtoaState *state, int base, double d);
 
 JS_END_EXTERN_C
 
 #endif /* jsdtoa_h___ */
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -2296,17 +2296,17 @@ BindNameToSlot(JSContext *cx, JSCodeGene
             if (!ale)
                 return JS_FALSE;
             index = ALE_INDEX(ale);
             JS_ASSERT(index == cg->upvarList.count - 1);
 
             UpvarCookie *vector = cg->upvarMap.vector;
             uint32 length = cg->lexdeps.count;
             if (!vector || cg->upvarMap.length != length) {
-                vector = (UpvarCookie *) js_realloc(vector, length * sizeof *vector);
+                vector = (UpvarCookie *) cx->realloc(vector, length * sizeof *vector);
                 if (!vector) {
                     JS_ReportOutOfMemory(cx);
                     return JS_FALSE;
                 }
                 cg->upvarMap.vector = vector;
                 cg->upvarMap.length = length;
             }
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -567,18 +567,16 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes
      */
     rt->gcMaxBytes = maxbytes;
     rt->setGCMaxMallocBytes(maxbytes);
 
     rt->gcEmptyArenaPoolLifespan = 30000;
 
     rt->gcTriggerFactor = uint32(100.0f * GC_HEAP_GROWTH_FACTOR);
 
-    rt->atomsCompartment->setGCLastBytes(8192);
-    
     /*
      * The assigned value prevents GC from running when GC memory is too low
      * (during JS engine start).
      */
     rt->setGCLastBytes(8192);
 
     rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_EIGHTH_LIFETIME;
 
@@ -864,17 +862,17 @@ js_FinishGC(JSRuntime *rt)
     if (JS_WANT_GC_METER_PRINT)
         js_DumpGCStats(rt, stdout);
 #endif
 
     /* Delete all remaining Compartments. */
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
         JSCompartment *comp = *c;
         comp->finishArenaLists();
-        js_delete(comp);
+        Foreground::delete_(comp);
     }
     rt->compartments.clear();
     rt->atomsCompartment = NULL;
 
     for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
         ReleaseGCChunk(rt, r.front());
     rt->gcChunkSet.clear();
 
@@ -2045,26 +2043,26 @@ GCHelperThread::waitBackgroundSweepEnd(J
 
 JS_FRIEND_API(void)
 GCHelperThread::replenishAndFreeLater(void *ptr)
 {
     JS_ASSERT(freeCursor == freeCursorEnd);
     do {
         if (freeCursor && !freeVector.append(freeCursorEnd - FREE_ARRAY_LENGTH))
             break;
-        freeCursor = (void **) js_malloc(FREE_ARRAY_SIZE);
+        freeCursor = (void **) OffTheBooks::malloc(FREE_ARRAY_SIZE);
         if (!freeCursor) {
             freeCursorEnd = NULL;
             break;
         }
         freeCursorEnd = freeCursor + FREE_ARRAY_LENGTH;
         *freeCursor++ = ptr;
         return;
     } while (false);
-    js_free(ptr);
+    Foreground::free(ptr);
 }
 
 void
 GCHelperThread::doSweep()
 {
     if (freeCursor) {
         void **array = freeCursorEnd - FREE_ARRAY_LENGTH;
         freeElementsAndArray(array, freeCursor);
@@ -2127,17 +2125,17 @@ SweepCompartments(JSContext *cx, JSGCInv
         if (!compartment->hold &&
             (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT))
         {
             JS_ASSERT(compartment->freeLists.isEmpty());
             if (callback)
                 (void) callback(cx, compartment, JSCOMPARTMENT_DESTROY);
             if (compartment->principals)
                 JSPRINCIPALS_DROP(cx, compartment->principals);
-            js_delete(compartment);
+            cx->delete_(compartment);
             continue;
         }
         *write++ = compartment;
     }
     rt->compartments.resize(write - rt->compartments.begin());
 }
 
 /*
@@ -2719,45 +2717,46 @@ SetProtoCheckingForCycles(JSContext *cx,
 
     return !cycle;
 }
 
 JSCompartment *
 NewCompartment(JSContext *cx, JSPrincipals *principals)
 {
     JSRuntime *rt = cx->runtime;
-    JSCompartment *compartment = js_new<JSCompartment>(rt);
+    JSCompartment *compartment = cx->new_<JSCompartment>(rt);
     if (!compartment || !compartment->init()) {
-        js_delete(compartment);
+        Foreground::delete_(compartment);
         JS_ReportOutOfMemory(cx);
         return NULL;
     }
 
     if (principals) {
         compartment->principals = principals;
         JSPRINCIPALS_HOLD(cx, principals);
     }
 
     compartment->setGCLastBytes(8192);
 
     {
         AutoLockGC lock(rt);
 
         if (!rt->compartments.append(compartment)) {
             AutoUnlockGC unlock(rt);
+            Foreground::delete_(compartment);
             JS_ReportOutOfMemory(cx);
             return NULL;
         }
     }
 
     JSCompartmentCallback callback = rt->compartmentCallback;
     if (callback && !callback(cx, compartment, JSCOMPARTMENT_NEW)) {
         AutoLockGC lock(rt);
         rt->compartments.popBack();
-        js_delete(compartment);
+        Foreground::delete_(compartment);
         return NULL;
     }
     return compartment;
 }
 
 } /* namespace gc */
 
 void
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -899,18 +899,18 @@ class GCHelperThread {
     void            **freeCursorEnd;
 
     JS_FRIEND_API(void)
     replenishAndFreeLater(void *ptr);
 
     static void freeElementsAndArray(void **array, void **end) {
         JS_ASSERT(array <= end);
         for (void **p = array; p != end; ++p)
-            js_free(*p);
-        js_free(array);
+            js::Foreground::free(*p);
+        js::Foreground::free(array);
     }
 
     static void threadMain(void* arg);
 
     void threadLoop(JSRuntime *rt);
     void doSweep();
 
   public:
--- a/js/src/jshash.cpp
+++ b/js/src/jshash.cpp
@@ -43,16 +43,18 @@
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsbit.h"
 #include "jsutil.h"
 #include "jshash.h"
 
+using namespace js;
+
 /* Compute the number of buckets in ht */
 #define NBUCKETS(ht)    JS_BIT(JS_HASH_BITS - (ht)->shift)
 
 /* The smallest table has 16 buckets */
 #define MINBUCKETSLOG2  4
 #define MINBUCKETS      JS_BIT(MINBUCKETSLOG2)
 
 /* Compute the maximum entries given n buckets that we will tolerate, ~90% */
@@ -62,36 +64,36 @@
 #define UNDERLOADED(n)  (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
 
 /*
 ** Stubs for default hash allocator ops.
 */
 static void *
 DefaultAllocTable(void *pool, size_t size)
 {
-    return js_malloc(size);
+    return OffTheBooks::malloc(size);
 }
 
 static void
 DefaultFreeTable(void *pool, void *item, size_t size)
 {
-    js_free(item);
+    UnwantedForeground::free(item);
 }
 
 static JSHashEntry *
 DefaultAllocEntry(void *pool, const void *key)
 {
-    return (JSHashEntry*) js_malloc(sizeof(JSHashEntry));
+    return (JSHashEntry*) OffTheBooks::malloc(sizeof(JSHashEntry));
 }
 
 static void
 DefaultFreeEntry(void *pool, JSHashEntry *he, uintN flag)
 {
     if (flag == HT_FREE_ENTRY)
-        js_free(he);
+        UnwantedForeground::free(he);
 }
 
 static JSHashAllocOps defaultHashAllocOps = {
     DefaultAllocTable, DefaultFreeTable,
     DefaultAllocEntry, DefaultFreeEntry
 };
 
 JS_PUBLIC_API(JSHashTable *)
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1718,17 +1718,17 @@ js_DumpOpMeters()
                 total += count;
                 ++nedges;
             }
         }
     }
 
 # define SIGNIFICANT(count,total) (200. * (count) >= (total))
 
-    graph = (Edge *) js_calloc(nedges * sizeof graph[0]);
+    graph = (Edge *) OffTheBooks::calloc(nedges * sizeof graph[0]);
     if (!graph)
         return;
     for (i = nedges = 0; i < JSOP_LIMIT; i++) {
         from = js_CodeName[i];
         for (j = 0; j < JSOP_LIMIT; j++) {
             count = succeeds[i][j];
             if (count != 0 && SIGNIFICANT(count, total)) {
                 graph[nedges].from = from;
@@ -1749,17 +1749,17 @@ js_DumpOpMeters()
             style = (i > nedges * .75) ? "dotted" :
                     (i > nedges * .50) ? "dashed" :
                     (i > nedges * .25) ? "solid" : "bold";
         }
         fprintf(fp, "  %s -> %s [label=\"%lu\" style=%s]\n",
                 graph[i].from, graph[i].to,
                 (unsigned long)graph[i].count, style);
     }
-    js_free(graph);
+    cx->free(graph);
     fputs("}\n", fp);
     fclose(fp);
 
     name = getenv("JS_OPMETER_HIST");
     if (!name)
         name = "/tmp/ops.hist";
     fp = fopen(name, "w");
     if (!fp) {
--- a/js/src/jslock.cpp
+++ b/js/src/jslock.cpp
@@ -359,32 +359,32 @@ js_FinishLock(JSThinLock *tl)
 #endif
 }
 
 #ifndef NSPR_LOCK
 
 static JSFatLock *
 NewFatlock()
 {
-    JSFatLock *fl = (JSFatLock *)js_malloc(sizeof(JSFatLock)); /* for now */
+    JSFatLock *fl = (JSFatLock *) OffTheBooks::malloc(sizeof(JSFatLock)); /* for now */
     if (!fl) return NULL;
     fl->susp = 0;
     fl->next = NULL;
     fl->prevp = NULL;
     fl->slock = PR_NewLock();
     fl->svar = PR_NewCondVar(fl->slock);
     return fl;
 }
 
 static void
 DestroyFatlock(JSFatLock *fl)
 {
     PR_DestroyLock(fl->slock);
     PR_DestroyCondVar(fl->svar);
-    js_free(fl);
+    UnwantedForeground::free(fl);
 }
 
 static JSFatLock *
 ListOfFatlocks(int listc)
 {
     JSFatLock *m;
     JSFatLock *m0;
     int i;
@@ -468,28 +468,28 @@ js_SetupLocks(int listc, int globc)
     if (listc > 10000 || listc < 0) /* listc == fat lock list chunk length */
         printf("Bad number %d in js_SetupLocks()!\n", listc);
     if (globc > 100 || globc < 0)   /* globc == number of global locks */
         printf("Bad number %d in js_SetupLocks()!\n", listc);
 #endif
     global_locks_log2 = JS_CeilingLog2(globc);
     global_locks_mask = JS_BITMASK(global_locks_log2);
     global_lock_count = JS_BIT(global_locks_log2);
-    global_locks = (PRLock **) js_malloc(global_lock_count * sizeof(PRLock*));
+    global_locks = (PRLock **) OffTheBooks::malloc(global_lock_count * sizeof(PRLock*));
     if (!global_locks)
         return JS_FALSE;
     for (i = 0; i < global_lock_count; i++) {
         global_locks[i] = PR_NewLock();
         if (!global_locks[i]) {
             global_lock_count = i;
             js_CleanupLocks();
             return JS_FALSE;
         }
     }
-    fl_list_table = (JSFatLockTable *) js_malloc(i * sizeof(JSFatLockTable));
+    fl_list_table = (JSFatLockTable *) OffTheBooks::malloc(i * sizeof(JSFatLockTable));
     if (!fl_list_table) {
         js_CleanupLocks();
         return JS_FALSE;
     }
     fl_list_table_len = global_lock_count;
     for (i = 0; i < global_lock_count; i++)
         fl_list_table[i].free = fl_list_table[i].taken = NULL;
     fl_list_chunk_len = listc;
@@ -501,30 +501,30 @@ void
 js_CleanupLocks()
 {
 #ifndef NSPR_LOCK
     uint32 i;
 
     if (global_locks) {
         for (i = 0; i < global_lock_count; i++)
             PR_DestroyLock(global_locks[i]);
-        js_free(global_locks);
+        UnwantedForeground::free(global_locks);
         global_locks = NULL;
         global_lock_count = 1;
         global_locks_log2 = 0;
         global_locks_mask = 0;
     }
     if (fl_list_table) {
         for (i = 0; i < fl_list_table_len; i++) {
             DeleteListOfFatlocks(fl_list_table[i].free);
             fl_list_table[i].free = NULL;
             DeleteListOfFatlocks(fl_list_table[i].taken);
             fl_list_table[i].taken = NULL;
         }
-        js_free(fl_list_table);
+        UnwantedForeground::free(fl_list_table);
         fl_list_table = NULL;
         fl_list_table_len = 0;
     }
 #endif /* !NSPR_LOCK */
 }
 
 #ifdef NSPR_LOCK
 
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -592,17 +592,17 @@ num_toSource(JSContext *cx, uintN argc, 
 ToCStringBuf::ToCStringBuf() :dbuf(NULL)
 {
     JS_STATIC_ASSERT(sbufSize >= DTOSTR_STANDARD_BUFFER_SIZE);
 }
 
 ToCStringBuf::~ToCStringBuf()
 {
     if (dbuf)
-        js_free(dbuf);
+        UnwantedForeground::free(dbuf);
 }
 
 JSString * JS_FASTCALL
 js_IntToString(JSContext *cx, int32 si)
 {
     uint32 ui;
     if (si >= 0) {
         if (JSAtom::hasIntStatic(si))
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -219,17 +219,17 @@ struct ToCStringBuf
 {
     /*
      * The longest possible result that would need to fit in sbuf is
      * (-0x80000000).toString(2), which has length 33.  Longer cases are
      * possible, but they'll go in dbuf.
      */
     static const size_t sbufSize = 34;
     char sbuf[sbufSize];
-    char *dbuf;     /* must be allocated with js_malloc() */
+    char *dbuf;
 
     ToCStringBuf();
     ~ToCStringBuf();
 };
 
 /*
  * Convert a number to a C string.  When base==10, this function implements
  * ToString() as specified by ECMA-262-5 section 9.8.1.  It handles integral
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -520,30 +520,30 @@ obj_toSource(JSContext *cx, uintN argc, 
 #endif
         goto make_string;
     }
     JS_ASSERT(ida);
     ok = JS_TRUE;
 
     if (!chars) {
         /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
-        chars = (jschar *) cx->runtime->malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
+        chars = (jschar *) cx->malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
         nchars = 0;
         if (!chars)
             goto error;
         if (outermost)
             chars[nchars++] = '(';
     } else {
         /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
         MAKE_SHARP(he);
         nchars = js_strlen(chars);
         chars = (jschar *)
-            js_realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
+            cx->realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
         if (!chars) {
-            js_free(ochars);
+            Foreground::free(ochars);
             goto error;
         }
         if (outermost) {
             /*
              * No need for parentheses around the whole shebang, because #n=
              * unambiguously begins an object initializer, and never a block
              * statement.
              */
@@ -734,17 +734,17 @@ obj_toSource(JSContext *cx, uintN argc, 
             /* Account for the trailing null. */
             SAFE_ADD((outermost ? 2 : 1) + 1);
 #undef SAFE_ADD
 
             if (curlen > size_t(-1) / sizeof(jschar))
                 goto overflow;
 
             /* Allocate 1 + 1 at end for closing brace and terminating 0. */
-            chars = (jschar *) js_realloc((ochars = chars), curlen * sizeof(jschar));
+            chars = (jschar *) cx->realloc((ochars = chars), curlen * sizeof(jschar));
             if (!chars) {
                 chars = ochars;
                 goto overflow;
             }
 
             if (comma) {
                 chars[nchars++] = comma[0];
                 chars[nchars++] = comma[1];
@@ -782,40 +782,40 @@ obj_toSource(JSContext *cx, uintN argc, 
         chars[nchars++] = ')';
     chars[nchars] = 0;
 
   error:
     js_LeaveSharpObject(cx, &ida);
 
     if (!ok) {
         if (chars)
-            js_free(chars);
+            Foreground::free(chars);
         goto out;
     }
 
     if (!chars) {
         JS_ReportOutOfMemory(cx);
         ok = JS_FALSE;
         goto out;
     }
   make_string:
     str = js_NewString(cx, chars, nchars);
     if (!str) {
-        js_free(chars);
+        cx->free(chars);
         ok = JS_FALSE;
         goto out;
     }
     vp->setString(str);
     ok = JS_TRUE;
   out:
     return ok;
 
   overflow:
     cx->free(vsharp);
-    js_free(chars);
+    cx->free(chars);
     chars = NULL;
     goto error;
 }
 #endif /* JS_HAS_TOSOURCE */
 
 namespace js {
 
 JSString *
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -710,17 +710,17 @@ js_BeginJSONParse(JSContext *cx, Value *
 {
     if (!cx)
         return NULL;
 
     JSObject *arr = NewDenseEmptyArray(cx);
     if (!arr)
         return NULL;
 
-    JSONParser *jp = cx->create<JSONParser>(cx);
+    JSONParser *jp = cx->new_<JSONParser>(cx);
     if (!jp)
         return NULL;
 
     jp->objectStack = arr;
     if (!JS_AddNamedObjectRoot(cx, &jp->objectStack, "JSON parse stack"))
         goto bad;
 
     jp->statep = jp->stateStack;
@@ -766,17 +766,17 @@ js_FinishJSONParse(JSContext *cx, JSONPa
     if (!early_ok) {
         ok = false;
     } else if (!ok) {
         JSONParseError(jp, cx);
     } else if (reviver.isObject() && reviver.toObject().isCallable()) {
         ok = Revive(cx, reviver, vp);
     }
 
-    cx->destroy(jp);
+    cx->delete_(jp);
 
     return ok;
 }
 
 static JSBool
 PushState(JSContext *cx, JSONParser *jp, JSONParserState state)
 {
     if (*jp->statep == JSON_PARSE_STATE_FINISHED) {
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -692,17 +692,17 @@ Sprint(Sprinter *sp, const char *format,
     va_start(ap, format);
     bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
     va_end(ap);
     if (!bp) {
         JS_ReportOutOfMemory(sp->context);
         return -1;
     }
     offset = SprintCString(sp, bp);
-    js_free(bp);
+    sp->context->free_(bp);
     return offset;
 }
 
 } // namespace js
 
 const char js_EscapeMap[] = {
     '\b', 'b',
     '\f', 'f',
@@ -936,17 +936,17 @@ js_printf(JSPrinter *jp, const char *for
         JS_ReportOutOfMemory(jp->sprinter.context);
         va_end(ap);
         return -1;
     }
 
     cc = strlen(bp);
     if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
         cc = -1;
-    js_free(bp);
+    jp->sprinter.context->free(bp);
 
     va_end(ap);
     return cc;
 }
 
 JSBool
 js_puts(JSPrinter *jp, const char *s)
 {
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -1011,21 +1011,21 @@ struct JSFunctionBoxQueue {
     size_t count()  { return head - tail; }
     size_t length() { return lengthMask + 1; }
 
     JSFunctionBoxQueue()
       : vector(NULL), head(0), tail(0), lengthMask(0) { }
 
     bool init(uint32 count) {
         lengthMask = JS_BITMASK(JS_CeilingLog2(count));
-        vector = js_array_new<JSFunctionBox*>(length());
+        vector = js::OffTheBooks::array_new<JSFunctionBox*>(length());
         return !!vector;
     }
 
-    ~JSFunctionBoxQueue() { js_array_delete(vector); }
+    ~JSFunctionBoxQueue() { js::UnwantedForeground::array_delete(vector); }
 
     void push(JSFunctionBox *funbox) {
         if (!funbox->queued) {
             JS_ASSERT(count() < length());
             vector[head++ & lengthMask] = funbox;
             funbox->queued = true;
         }
     }
--- a/js/src/jsprf.cpp
+++ b/js/src/jsprf.cpp
@@ -47,16 +47,18 @@
 #include <stdlib.h>
 #include "jsprf.h"
 #include "jsstdint.h"
 #include "jslong.h"
 #include "jsutil.h"
 #include "jspubtd.h"
 #include "jsstr.h"
 
+using namespace js;
+
 /*
 ** Note: on some platforms va_list is defined as an array,
 ** and requires array notation.
 */
 #ifdef HAVE_VA_COPY
 #define VARARGS_ASSIGN(foo, bar)        VA_COPY(foo,bar)
 #elif defined(HAVE_VA_LIST_AS_ARRAY)
 #define VARARGS_ASSIGN(foo, bar)        foo[0] = bar[0]
@@ -407,17 +409,17 @@ static int cvt_ws(SprintfState *ss, cons
      * and malloc() is used to allocate the buffer buffer.
      */
     if (ws) {
         int slen = js_strlen(ws);
         char *s = js_DeflateString(NULL, ws, slen);
         if (!s)
             return -1; /* JSStuffFunc error indicator. */
         result = cvt_s(ss, s, width, prec, flags);
-        js_free(s);
+        UnwantedForeground::free(s);
     } else {
         result = cvt_s(ss, NULL, width, prec, flags);
     }
     return result;
 }
 
 /*
 ** BuildArgArray stands for Numbered Argument list Sprintf
@@ -625,17 +627,17 @@ static struct NumArgState* BuildArgArray
 
     /*
     ** third pass
     ** fill the nas[cn].ap
     */
 
     if( *rv < 0 ){
         if( nas != nasArray )
-            js_free( nas );
+            UnwantedForeground::free( nas );
         return NULL;
     }
 
     cn = 0;
     while( cn < number ){
         if( nas[cn].type == TYPE_UNKNOWN ){
             cn++;
             continue;
@@ -662,17 +664,17 @@ static struct NumArgState* BuildArgArray
         case TYPE_WSTRING:      (void)va_arg( ap, jschar* );            break;
 
         case TYPE_INTSTR:       (void)va_arg( ap, JSIntn* );            break;
 
         case TYPE_DOUBLE:       (void)va_arg( ap, double );             break;
 
         default:
             if( nas != nasArray )
-                js_free( nas );
+                UnwantedForeground::free( nas );
             *rv = -1;
             return NULL;
         }
 
         cn++;
     }
 
 
@@ -751,17 +753,17 @@ static int dosprintf(SprintfState *ss, c
             i = 0;
             while( c && c != '$' ){         /* should imporve error check later */
                 i = ( i * 10 ) + ( c - '0' );
                 c = *fmt++;
             }
 
             if( nas[i-1].type == TYPE_UNKNOWN ){
                 if( nas && ( nas != nasArray ) )
-                    js_free( nas );
+                    UnwantedForeground::free( nas );
                 return -1;
             }
 
             ap = nas[i-1].ap;
             dolPt = fmt;
             c = *fmt++;
         }
 
@@ -1032,17 +1034,17 @@ static int dosprintf(SprintfState *ss, c
             }
         }
     }
 
     /* Stuff trailing NUL */
     rv = (*ss->stuff)(ss, "\0", 1);
 
     if( nas && ( nas != nasArray ) ){
-        js_free( nas );
+        UnwantedForeground::free( nas );
     }
 
     return rv;
 }
 
 /************************************************************************/
 
 static int FuncStuff(SprintfState *ss, const char *sp, JSUint32 len)
@@ -1093,19 +1095,19 @@ static int GrowStuff(SprintfState *ss, c
     char *newbase;
     JSUint32 newlen;
 
     off = ss->cur - ss->base;
     if (off + len >= ss->maxlen) {
         /* Grow the buffer */
         newlen = ss->maxlen + ((len > 32) ? len : 32);
         if (ss->base) {
-            newbase = (char*) js_realloc(ss->base, newlen);
+            newbase = (char*) OffTheBooks::realloc(ss->base, newlen);
         } else {
-            newbase = (char*) js_malloc(newlen);
+            newbase = (char*) OffTheBooks::malloc(newlen);
         }
         if (!newbase) {
             /* Ran out of memory */
             return -1;
         }
         ss->base = newbase;
         ss->maxlen = newlen;
         ss->cur = ss->base + off;
@@ -1134,32 +1136,32 @@ JS_PUBLIC_API(char *) JS_smprintf(const 
     return rv;
 }
 
 /*
 ** Free memory allocated, for the caller, by JS_smprintf
 */
 JS_PUBLIC_API(void) JS_smprintf_free(char *mem)
 {
-        js_free(mem);
+        Foreground::free(mem);
 }
 
 JS_PUBLIC_API(char *) JS_vsmprintf(const char *fmt, va_list ap)
 {
     SprintfState ss;
     int rv;
 
     ss.stuff = GrowStuff;
     ss.base = 0;
     ss.cur = 0;
     ss.maxlen = 0;
     rv = dosprintf(&ss, fmt, ap);
     if (rv < 0) {
         if (ss.base) {
-            js_free(ss.base);
+            Foreground::free(ss.base);
         }
         return 0;
     }
     return ss.base;
 }
 
 /*
 ** Stuff routine that discards overflow data
@@ -1248,15 +1250,15 @@ JS_PUBLIC_API(char *) JS_vsprintf_append
     } else {
         ss.base = 0;
         ss.cur = 0;
         ss.maxlen = 0;
     }
     rv = dosprintf(&ss, fmt, ap);
     if (rv < 0) {
         if (ss.base) {
-            js_free(ss.base);
+            Foreground::free(ss.base);
         }
         return 0;
     }
     return ss.base;
 }
 
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -75,23 +75,19 @@ PropertyTree::newShape(JSContext *cx)
     JS_COMPARTMENT_METER(compartment->livePropTreeNodes++);
     JS_COMPARTMENT_METER(compartment->totalPropTreeNodes++);
     return shape;
 }
 
 static KidsHash *
 HashChildren(Shape *kid1, Shape *kid2)
 {
-    void *mem = js_malloc(sizeof(KidsHash));
-    if (!mem)
-        return NULL;
-
-    KidsHash *hash = new (mem) KidsHash();
-    if (!hash->init(2)) {
-        js_free(hash);
+    KidsHash *hash = OffTheBooks::new_<KidsHash>();
+    if (!hash || !hash->init(2)) {
+        Foreground::delete_(hash);
         return NULL;
     }
 
     JS_ALWAYS_TRUE(hash->putNew(kid1));
     JS_ALWAYS_TRUE(hash->putNew(kid2));
     return hash;
 }
 
@@ -339,17 +335,17 @@ Shape::finalize(JSContext *cx)
 
     if (inDictionary()) {
         JS_COMPARTMENT_METER(compartment()->liveDictModeNodes--);
     } else {
         if (parent && parent->isMarked())
             parent->removeChild(this);
 
         if (kids.isHash())
-            js_delete(kids.toHash());
+            cx->delete_(kids.toHash());
     }
 
     freeTable(cx);
     JS_COMPARTMENT_METER(compartment()->livePropTreeNodes--);
 }
 
 void
 js::PropertyTree::dumpShapeStats()
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -74,17 +74,17 @@ using namespace js::gc;
  * provide an appropriate finalizer. We store an instance of that js::Class in
  * a global reserved slot.
  */
 
 static void
 resc_finalize(JSContext *cx, JSObject *obj)
 {
     RegExpStatics *res = static_cast<RegExpStatics *>(obj->getPrivate());
-    cx->destroy<RegExpStatics>(res);
+    cx->delete_(res);
 }
 
 static void
 resc_trace(JSTracer *trc, JSObject *obj)
 {
     void *pdata = obj->getPrivate();
     JS_ASSERT(pdata);
     RegExpStatics *res = static_cast<RegExpStatics *>(pdata);
--- a/js/src/jsregexpinlines.h
+++ b/js/src/jsregexpinlines.h
@@ -65,17 +65,17 @@ namespace js {
 extern Class regexp_statics_class;
 
 static inline JSObject *
 regexp_statics_construct(JSContext *cx, JSObject *parent)
 {
     JSObject *obj = NewObject<WithProto::Given>(cx, &regexp_statics_class, NULL, parent);
     if (!obj)
         return NULL;
-    RegExpStatics *res = cx->create<RegExpStatics>();
+    RegExpStatics *res = cx->new_<RegExpStatics>();
     if (!res)
         return NULL;
     obj->setPrivate(static_cast<void *>(res));
     return obj;
 }
 
 /*
  * The "meat" of the builtin regular expression objects: it contains the
@@ -112,26 +112,25 @@ class RegExp
 
     RegExp(JSLinearString *source, uint32 flags, JSCompartment *compartment)
       : compiled(), source(source), refCount(1), parenCount(0), flags(flags)
 #ifdef DEBUG
         , compartment(compartment)
 #endif
     { }
 
+    JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
+
     ~RegExp() {
 #if !ENABLE_YARR_JIT
         if (compiled)
             jsRegExpFree(compiled);
 #endif
     }
 
-    /* Constructor/destructor are hidden; called by cx->create/destroy. */
-    friend struct ::JSContext;
-
     bool compileHelper(JSContext *cx, JSLinearString &pattern);
     bool compile(JSContext *cx);
     static const uint32 allFlags = JSREG_FOLD | JSREG_GLOB | JSREG_MULTILINE | JSREG_STICKY;
     void handlePCREError(JSContext *cx, int error);
     void handleYarrError(JSContext *cx, int error);
     static inline bool initArena(JSContext *cx);
     static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount);
     static JSObject *createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount);
@@ -414,21 +413,21 @@ RegExp::executeInternal(JSContext *cx, R
 
 inline AlreadyIncRefed<RegExp>
 RegExp::create(JSContext *cx, JSString *source, uint32 flags)
 {
     typedef AlreadyIncRefed<RegExp> RetType;
     JSLinearString *flatSource = source->ensureLinear(cx);
     if (!flatSource)
         return RetType(NULL);
-    RegExp *self = cx->create<RegExp>(flatSource, flags, cx->compartment);
+    RegExp *self = cx->new_<RegExp>(flatSource, flags, cx->compartment);
     if (!self)
         return RetType(NULL);
     if (!self->compile(cx)) {
-        cx->destroy<RegExp>(self);
+        Foreground::delete_(self);
         return RetType(NULL);
     }
     return RetType(self);
 }
 
 inline JSObject *
 RegExp::createObject(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
                      uint32 flags)
@@ -571,17 +570,17 @@ RegExp::incref(JSContext *cx)
 
 inline void
 RegExp::decref(JSContext *cx)
 {
 #ifdef DEBUG
     assertSameCompartment(cx, compartment);
 #endif
     if (--refCount == 0)
-        cx->destroy<RegExp>(this);
+        cx->delete_(this);
 }
 
 inline RegExp *
 RegExp::extractFrom(JSObject *obj)
 {
     JS_ASSERT_IF(obj, obj->isRegExp());
     RegExp *re = static_cast<RegExp *>(obj->getPrivate());
 #ifdef DEBUG
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -182,20 +182,20 @@ PropertyTable::init(JSRuntime *rt, Shape
     }
     return true;
 }
 
 bool
 Shape::hashify(JSRuntime *rt)
 {
     JS_ASSERT(!hasTable());
-    void* mem = rt->malloc(sizeof(PropertyTable));
-    if (!mem)
+    PropertyTable *table = rt->new_<PropertyTable>(entryCount());
+    if (!table)
         return false;
-    setTable(new(mem) PropertyTable(entryCount()));
+    setTable(table);
     return getTable()->init(rt, this);
 }
 
 #ifdef DEBUG
 # include "jsprf.h"
 # define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr)
 #else
 # define LIVE_SCOPE_METER(cx,expr) /* nothing */
@@ -318,27 +318,24 @@ PropertyTable::change(int log2Delta, JSC
 {
     int oldlog2, newlog2;
     uint32 oldsize, newsize, nbytes;
     Shape **newTable, **oldTable, **spp, **oldspp, *shape;
 
     JS_ASSERT(entries);
 
     /*
-     * Grow, shrink, or compress by changing this->entries. Here, we prefer
-     * cx->runtime->calloc to js_calloc, which on OOM waits for a background
-     * thread to finish sweeping and retry, if appropriate. Avoid cx->calloc
-     * so our caller can be in charge of whether to JS_ReportOutOfMemory.
+     * Grow, shrink, or compress by changing this->entries.
      */
     oldlog2 = JS_DHASH_BITS - hashShift;
     newlog2 = oldlog2 + log2Delta;
     oldsize = JS_BIT(oldlog2);
     newsize = JS_BIT(newlog2);
     nbytes = PROPERTY_TABLE_NBYTES(newsize);
-    newTable = (Shape **) cx->runtime->calloc(nbytes);
+    newTable = (Shape **) cx->calloc(nbytes);
     if (!newTable) {
         METER(tableAllocFails);
         return false;
     }
 
     /* Now that we have newTable allocated, update members. */
     hashShift = JS_DHASH_BITS - newlog2;
     removedCount = 0;
@@ -353,22 +350,18 @@ PropertyTable::change(int log2Delta, JSC
             METER(changeSearches);
             spp = search(shape->id, true);
             JS_ASSERT(SHAPE_IS_FREE(*spp));
             *spp = shape;
         }
         oldsize--;
     }
 
-    /*
-     * Finally, free the old entries storage. Note that cx->runtime->free just
-     * calls js_free. Use js_free here to match PropertyTable::~PropertyTable,
-     * which cannot have a cx or rt parameter.
-     */
-    js_free(oldTable);
+    /* Finally, free the old entries storage. */
+    cx->free(oldTable);
     return true;
 }
 
 bool
 PropertyTable::grow(JSContext *cx)
 {
     JS_ASSERT(needsToGrow());
 
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -237,17 +237,17 @@ struct PropertyTable {
         entryCount(nentries),
         removedCount(0),
         freelist(SHAPE_INVALID_SLOT)
     {
         /* NB: entries is set by init, which must be called. */
     }
 
     ~PropertyTable() {
-        js_free(entries);
+        js::UnwantedForeground::free(entries);
     }
 
     /* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */
     uint32 capacity() const { return JS_BIT(JS_DHASH_BITS - hashShift); }
 
     /* Whether we need to grow.  We want to do this if the load factor is >= 0.75 */
     bool needsToGrow() const {
         uint32 size = capacity();
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -52,17 +52,17 @@
 #include "jsgcinlines.h"
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
 inline void
 js::Shape::freeTable(JSContext *cx)
 {
     if (hasTable()) {
-        cx->destroy(getTable());
+        cx->delete_(getTable());
         setTable(NULL);
     }
 }
 
 inline js::EmptyShape *
 JSObject::getEmptyShape(JSContext *cx, js::Class *aclasp,
                         /* gc::FinalizeKind */ unsigned kind)
 {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -798,40 +798,40 @@ typedef struct ScriptFilenameEntry {
     uint32              flags;          /* user-defined filename prefix flags */
     JSPackedBool        mark;           /* GC mark flag */
     char                filename[3];    /* two or more bytes, NUL-terminated */
 } ScriptFilenameEntry;
 
 static void *
 js_alloc_table_space(void *priv, size_t size)
 {
-    return js_malloc(size);
+    return OffTheBooks::malloc(size);
 }
 
 static void
 js_free_table_space(void *priv, void *item, size_t size)
 {
-    js_free(item);
+    UnwantedForeground::free(item);
 }
 
 static JSHashEntry *
 js_alloc_sftbl_entry(void *priv, const void *key)
 {
     size_t nbytes = offsetof(ScriptFilenameEntry, filename) +
                     strlen((const char *) key) + 1;
 
-    return (JSHashEntry *) js_malloc(JS_MAX(nbytes, sizeof(JSHashEntry)));
+    return (JSHashEntry *) OffTheBooks::malloc(JS_MAX(nbytes, sizeof(JSHashEntry)));
 }
 
 static void
 js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag)
 {
     if (flag != HT_FREE_ENTRY)
         return;
-    js_free(he);
+    UnwantedForeground::free(he);
 }
 
 static JSHashAllocOps sftbl_alloc_ops = {
     js_alloc_table_space,   js_free_table_space,
     js_alloc_sftbl_entry,   js_free_sftbl_entry
 };
 
 static void
@@ -882,17 +882,17 @@ js_FreeRuntimeScriptState(JSRuntime *rt)
 {
     if (!rt->scriptFilenameTable)
         return;
 
     while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) {
         ScriptFilenamePrefix *sfp = (ScriptFilenamePrefix *)
                                     rt->scriptFilenamePrefixes.next;
         JS_REMOVE_LINK(&sfp->links);
-        js_free(sfp);
+        UnwantedForeground::free(sfp);
     }
     FinishRuntimeScriptState(rt);
 }
 
 #ifdef DEBUG_brendan
 #define DEBUG_SFTBL
 #endif
 #ifdef DEBUG_SFTBL
@@ -945,17 +945,17 @@ SaveScriptFilename(JSRuntime *rt, const 
                 sfp = NULL;
                 break;
             }
             sfp = NULL;
         }
 
         if (!sfp) {
             /* No such prefix: add one now. */
-            sfp = (ScriptFilenamePrefix *) js_malloc(sizeof(ScriptFilenamePrefix));
+            sfp = (ScriptFilenamePrefix *) rt->malloc(sizeof(ScriptFilenamePrefix));
             if (!sfp)
                 return NULL;
             JS_INSERT_AFTER(&sfp->links, link);
             sfp->name = sfe->filename;
             sfp->length = length;
             sfp->flags = 0;
         }
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -212,17 +212,17 @@ RopeCapacityFor(size_t length)
 static JS_ALWAYS_INLINE jschar *
 AllocChars(JSContext *maybecx, size_t wholeCapacity)
 {
     /* +1 for the null char at the end. */
     JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(jschar) < UINT32_MAX);
     size_t bytes = (wholeCapacity + 1) * sizeof(jschar);
     if (maybecx)
         return (jschar *)maybecx->malloc(bytes);
-    return (jschar *)js_malloc(bytes);
+    return (jschar *)OffTheBooks::malloc(bytes);
 }
 
 JSFlatString *
 JSRope::flatten(JSContext *maybecx)
 {
     /*
      * Perform a depth-first dag traversal, splatting each node's characters
      * into a contiguous buffer. Visit each rope node three times:
@@ -3118,17 +3118,17 @@ tagify(JSContext *cx, const char *begin,
     for (size_t i = 0; i < endlen; i++)
         tagbuf[j++] = (jschar)end[i];
     tagbuf[j++] = '>';
     JS_ASSERT(j == taglen);
     tagbuf[j] = 0;
 
     JSString *retstr = js_NewString(cx, tagbuf, taglen);
     if (!retstr) {
-        js_free((char *)tagbuf);
+        Foreground::free((char *)tagbuf);
         return false;
     }
     vp->setString(retstr);
     return true;
 }
 
 static JSBool
 tagify_value(JSContext *cx, uintN argc, Value *vp,
@@ -4075,27 +4075,27 @@ js_DeflateString(JSContext *cx, const js
 #ifdef DEBUG
     JSBool ok;
 #endif
 
     if (js_CStringsAreUTF8) {
         nbytes = js_GetDeflatedStringLength(cx, chars, nchars);
         if (nbytes == (size_t) -1)
             return NULL;
-        bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1));
+        bytes = (char *) (cx ? cx->malloc(nbytes + 1) : OffTheBooks::malloc(nbytes + 1));
         if (!bytes)
             return NULL;
 #ifdef DEBUG
         ok =
 #endif
             js_DeflateStringToBuffer(cx, chars, nchars, bytes, &nbytes);
         JS_ASSERT(ok);
     } else {
         nbytes = nchars;
-        bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1));
+        bytes = (char *) (cx ? cx->malloc(nbytes + 1) : OffTheBooks::malloc(nbytes + 1));
         if (!bytes)
             return NULL;
         for (i = 0; i < nbytes; i++)
             bytes[i] = (char) chars[i];
     }
     bytes[nbytes] = 0;
     return bytes;
 }
--- a/js/src/jstl.h
+++ b/js/src/jstl.h
@@ -250,19 +250,19 @@ PointerRangeSize(T *begin, T *end)
  *  - reportAllocOverflow()
  *      Called on overflow before the container returns NULL.
  */
 
 /* Policy for using system memory functions and doing no error reporting. */
 class SystemAllocPolicy
 {
   public:
-    void *malloc(size_t bytes) { return js_malloc(bytes); }
-    void *realloc(void *p, size_t bytes) { return js_realloc(p, bytes); }
-    void free(void *p) { js_free(p); }
+    void *malloc(size_t bytes) { return js::OffTheBooks::malloc(bytes); }
+    void *realloc(void *p, size_t bytes) { return js::OffTheBooks::realloc(p, bytes); }
+    void free(void *p) { js::UnwantedForeground::free(p); }
     void reportAllocOverflow() const {}
 };
 
 /*
  * This utility pales in comparison to Boost's aligned_storage. The utility
  * simply assumes that JSUint64 is enough alignment for anyone. This may need
  * to be extended one day...
  *
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -143,23 +143,23 @@ using namespace js::tjit;
  *
  * FIXME: bug 624590 is open to fix these problems.
  */
 #define OUT_OF_MEMORY_ABORT(msg)    JS_Assert(msg, __FILE__, __LINE__);
 
 /* Implement embedder-specific nanojit members. */
 
 /* 
- * Nanojit requires infallible allocations most of the time.  We satisfy this
- * by reserving some space in each allocator which is used as a fallback if
- * js_calloc() fails.  Ideallly this reserve space should be big enough to
- * allow for all infallible requests made to the allocator until the next OOM
- * check occurs, but it turns out that's impossible to guarantee (though it
- * should be unlikely).  So we abort if the reserve runs out;  this is better
- * than allowing memory errors to occur.
+ * Nanojit requires infallible allocations most of the time. We satisfy this by
+ * reserving some space in each allocator which is used as a fallback if
+ * rt->calloc() fails. Ideally this reserve space should be big enough to allow
+ * for all infallible requests made to the allocator until the next OOM check
+ * occurs, but it turns out that's impossible to guarantee (though it should be
+ * unlikely). So we abort if the reserve runs out;  this is better than
+ * allowing memory errors to occur.
  *
  * The space calculations are as follows... between OOM checks, each
  * VMAllocator can do (ie. has been seen to do) the following maximum
  * allocations on 64-bits:
  *
  * - dataAlloc: 31 minimum-sized chunks (MIN_CHUNK_SZB) in assm->compile()
  *   (though arbitrarily more could occur due to LabelStateMap additions done
  *   when handling labels):  62,248 bytes.  This one is the most likely to
@@ -185,17 +185,17 @@ nanojit::Allocator::allocChunk(size_t nb
 {
     VMAllocator *vma = (VMAllocator*)this;
     /*
      * Nb: it's conceivable that request 1 might fail (in which case
      * mOutOfMemory will be set) and then request 2 succeeds.  The subsequent
      * OOM check will still fail, which is what we want, and the success of
      * request 2 makes it less likely that the reserve space will overflow.
      */
-    void *p = js_calloc(nbytes);
+    void *p = vma->mRt->calloc(nbytes);
     if (p) {
         vma->mSize += nbytes;
     } else {
         vma->mOutOfMemory = true;
         if (!fallible) {
             p = (void *)vma->mReserveCurr;
             vma->mReserveCurr += nbytes;
             if (vma->mReserveCurr > vma->mReserveLimit)
@@ -206,17 +206,17 @@ nanojit::Allocator::allocChunk(size_t nb
     }
     return p;
 }
 
 void
 nanojit::Allocator::freeChunk(void *p) {
     VMAllocator *vma = (VMAllocator*)this;
     if (p < vma->mReserve || uintptr_t(p) >= vma->mReserveLimit)
-        js_free(p);
+        UnwantedForeground::free(p);
 }
 
 void
 nanojit::Allocator::postReset() {
     VMAllocator *vma = (VMAllocator*)this;
     vma->mOutOfMemory = false;
     vma->mSize = 0;
     vma->mReserveCurr = uintptr_t(vma->mReserve);
@@ -1079,17 +1079,18 @@ void
 TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3, LIns *ins4,
                       LIns *ins5, LIns *ins6)
 {
     LIns* insa[] = { ins1, ins2, ins3, ins4, ins5, ins6 };
     tprint(format, 6, insa);
 }
 #endif
 
-Tracker::Tracker()
+Tracker::Tracker(JSContext *cx)
+    : cx(cx)
 {
     pagelist = NULL;
 }
 
 Tracker::~Tracker()
 {
     clear();
 }
@@ -1118,30 +1119,30 @@ Tracker::findTrackerPage(const void* v) 
     }
     return NULL;
 }
 
 struct Tracker::TrackerPage*
 Tracker::addTrackerPage(const void* v)
 {
     jsuword base = getTrackerPageBase(v);
-    struct TrackerPage* p = (struct TrackerPage*) js_calloc(sizeof(*p));
+    struct TrackerPage* p = (struct TrackerPage*) cx->calloc(sizeof(*p));
     p->base = base;
     p->next = pagelist;
     pagelist = p;
     return p;
 }
 
 void
 Tracker::clear()
 {
     while (pagelist) {
         TrackerPage* p = pagelist;
         pagelist = pagelist->next;
-        js_free(p);
+        cx->free(p);
     }
 }
 
 bool
 Tracker::has(const void *v) const
 {
     return get(v) != NULL;
 }
@@ -2239,18 +2240,18 @@ TraceRecorder::TraceRecorder(JSContext* 
     cx_ins(NULL),
     eos_ins(NULL),
     eor_ins(NULL),
     loopLabel(NULL),
     importTypeMap(&tempAlloc(), tm->oracle),
     lirbuf(new (tempAlloc()) LirBuffer(tempAlloc())),
     mark(*traceMonitor->traceAlloc),
     numSideExitsBefore(tree->sideExits.length()),
-    tracker(),
-    nativeFrameTracker(),
+    tracker(cx),
+    nativeFrameTracker(cx),
     global_slots(NULL),
     callDepth(anchor ? anchor->calldepth : 0),
     atoms(FrameAtomBase(cx, cx->fp())),
     consts(JSScript::isValidOffset(cx->fp()->script()->constOffset)
            ? cx->fp()->script()->consts()->vector
            : NULL),
     strictModeCode_ins(NULL),
     cfgMerges(&tempAlloc()),
@@ -2475,19 +2476,17 @@ TraceRecorder::finishSuccessfully()
     AUDIT(traceCompleted);
     mark.commit();
 
     /* Grab local copies of members needed after destruction of |this|. */
     JSContext* localcx = cx;
     TraceMonitor* localtm = traceMonitor;
 
     localtm->recorder = NULL;
-    /* We can't (easily) use js_delete() here because the destructor is private. */
-    this->~TraceRecorder();
-    js_free(this);
+    cx->delete_(this);
 
     /* Catch OOM that occurred during recording. */
     if (localtm->outOfMemory() || OverfullJITCache(localcx, localtm)) {
         ResetJIT(localcx, localtm, FR_OOM);
         return ARECORD_ABORTED;
     }
     return ARECORD_COMPLETED;
 }
@@ -2529,19 +2528,17 @@ TraceRecorder::finishAbort(const char* r
         fragment->root->sideExits.setLength(numSideExitsBefore);
     }
 
     /* Grab local copies of members needed after destruction of |this|. */
     JSContext* localcx = cx;
     TraceMonitor* localtm = traceMonitor;
 
     localtm->recorder = NULL;
-    /* We can't (easily) use js_delete() here because the destructor is private. */
-    this->~TraceRecorder();
-    js_free(this);
+    cx->delete_(this);
 
     /* Catch OOM that occurred during recording. */
     if (localtm->outOfMemory() || OverfullJITCache(localcx, localtm)) {
         ResetJIT(localcx, localtm, FR_OOM);
         return JIT_RESET;
     }
     return NORMAL_ABORT;
 }
@@ -4498,22 +4495,22 @@ TraceRecorder::compile()
         ++tree->branchCount;
     if (outOfMemory())
         return ARECORD_STOP;
 
     /* :TODO: windows support */
 #if defined DEBUG && !defined WIN32
     /* Associate a filename and line number with the fragment. */
     const char* filename = cx->fp()->script()->filename;
-    char* label = (char*)js_malloc((filename ? strlen(filename) : 7) + 16);
+    char* label = (char*) cx->malloc((filename ? strlen(filename) : 7) + 16);
     if (label) {
         sprintf(label, "%s:%u", filename ? filename : "<stdin>",
                 js_FramePCToLineNumber(cx, cx->fp()));
         lirbuf->printer->addrNameMap->addAddrRange(fragment, sizeof(Fragment), 0, label);
-        js_free(label);
+        cx->free(label);
     }
 #endif
 
     Assembler *assm = traceMonitor->assembler;
     JS_ASSERT(!assm->error());
     assm->compile(fragment, tempAlloc(), /*optimize*/true verbose_only(, lirbuf->printer));
 
     if (assm->error()) {
@@ -5566,23 +5563,19 @@ TraceRecorder::startRecorder(JSContext* 
                              unsigned stackSlots, unsigned ngslots,
                              JSValueType* typeMap, VMSideExit* expectedInnerExit,
                              JSScript* outerScript, jsbytecode* outerPC, uint32 outerArgc,
                              bool speculate)
 {
     JS_ASSERT(!tm->needFlush);
     JS_ASSERT_IF(cx->fp()->hasImacropc(), f->root != f);
 
-    /* We can't (easily) use js_new() here because the constructor is private. */
-    void *memory = js_malloc(sizeof(TraceRecorder));
-    tm->recorder = memory
-                 ? new(memory) TraceRecorder(cx, tm, anchor, f, stackSlots, ngslots, typeMap,
+    tm->recorder = cx->new_<TraceRecorder>(cx, tm, anchor, f, stackSlots, ngslots, typeMap,
                                              expectedInnerExit, outerScript, outerPC, outerArgc,
-                                             speculate)
-                 : NULL;
+                                             speculate);
 
     if (!tm->recorder || tm->outOfMemory() || OverfullJITCache(cx, tm)) {
         ResetJIT(cx, tm, FR_OOM);
         return false;
     }
 
     return true;
 }
@@ -7627,28 +7620,28 @@ SetMaxCodeCacheBytes(JSContext* cx, uint
     if (bytes > 1 G)
         bytes = 1 G;
     if (bytes < 128 K)
         bytes = 128 K;
     JS_THREAD_DATA(cx)->maxCodeCacheBytes = bytes;
 }
 
 bool
-InitJIT(TraceMonitor *tm)
+InitJIT(TraceMonitor *tm, JSRuntime* rt)
 {
 #if defined JS_JIT_SPEW
     tm->profAlloc = NULL;
     /* Set up debug logging. */
     if (!did_we_set_up_debug_logging) {
         InitJITLogController();
         did_we_set_up_debug_logging = true;
     }
     /* Set up fragprofiling, if required. */
     if (LogController.lcbits & LC_FragProfile) {
-        tm->profAlloc = js_new<VMAllocator>((char*)NULL, 0); /* no reserve needed in debug builds */
+        tm->profAlloc = rt->new_<VMAllocator>(rt, (char*)NULL, 0); /* no reserve needed in debug builds */
         if (!tm->profAlloc)
             goto error;
         tm->profTab = new (*tm->profAlloc) FragStatsMap(*tm->profAlloc);
     }
     tm->lastFragID = 0;
 #else
     PodZero(&LogController);
 #endif
@@ -7673,44 +7666,46 @@ InitJIT(TraceMonitor *tm)
 
         // Sanity-check the configuration detection.
         //  * We don't understand architectures prior to ARMv4.
         JS_ASSERT(arm_arch >= 4);
 #endif
         did_we_check_processor_features = true;
     }
 
-    #define CHECK_ALLOC(lhs, rhs) \
-        do { lhs = (rhs); if (!lhs) goto error; } while (0)
-
-    CHECK_ALLOC(tm->oracle, js_new<Oracle>());
+    #define CHECK_NEW(lhs, type, args) \
+        do { lhs = rt->new_<type> args; if (!lhs) goto error; } while (0)
+    #define CHECK_MALLOC(lhs, conversion, size) \
+        do { lhs = (conversion)(rt->malloc(size)); if (!lhs) goto error; } while (0)
+
+    CHECK_NEW(tm->oracle, Oracle, ());
 
     tm->profile = NULL;
     
-    CHECK_ALLOC(tm->recordAttempts, js_new<RecordAttemptMap>());
+    CHECK_NEW(tm->recordAttempts, RecordAttemptMap, ());
     if (!tm->recordAttempts->init(PC_HASH_COUNT))
         goto error;
 
-    CHECK_ALLOC(tm->loopProfiles, js_new<LoopProfileMap>());
+    CHECK_NEW(tm->loopProfiles, LoopProfileMap, ());
     if (!tm->loopProfiles->init(PC_HASH_COUNT))
         goto error;
 
     tm->flushEpoch = 0;
     
     char *dataReserve, *traceReserve, *tempReserve;
-    CHECK_ALLOC(dataReserve, (char *)js_malloc(DataReserveSize));
-    CHECK_ALLOC(traceReserve, (char *)js_malloc(TraceReserveSize));
-    CHECK_ALLOC(tempReserve, (char *)js_malloc(TempReserveSize));
-    CHECK_ALLOC(tm->dataAlloc, js_new<VMAllocator>(dataReserve, DataReserveSize));
-    CHECK_ALLOC(tm->traceAlloc, js_new<VMAllocator>(traceReserve, TraceReserveSize));
-    CHECK_ALLOC(tm->tempAlloc, js_new<VMAllocator>(tempReserve, TempReserveSize));
-    CHECK_ALLOC(tm->codeAlloc, js_new<CodeAlloc>());
-    CHECK_ALLOC(tm->frameCache, js_new<FrameInfoCache>(tm->dataAlloc));
-    CHECK_ALLOC(tm->storage, js_new<TraceNativeStorage>());
-    CHECK_ALLOC(tm->cachedTempTypeMap, js_new<TypeMap>((Allocator*)NULL, tm->oracle));
+    CHECK_MALLOC(dataReserve, char*, DataReserveSize);
+    CHECK_MALLOC(traceReserve, char*, TraceReserveSize);
+    CHECK_MALLOC(tempReserve, char*, TempReserveSize);
+    CHECK_NEW(tm->dataAlloc, VMAllocator, (rt, dataReserve, DataReserveSize));
+    CHECK_NEW(tm->traceAlloc, VMAllocator, (rt, traceReserve, TraceReserveSize));
+    CHECK_NEW(tm->tempAlloc, VMAllocator, (rt, tempReserve, TempReserveSize));
+    CHECK_NEW(tm->codeAlloc, CodeAlloc, ());
+    CHECK_NEW(tm->frameCache, FrameInfoCache, (tm->dataAlloc));
+    CHECK_NEW(tm->storage, TraceNativeStorage, ());
+    CHECK_NEW(tm->cachedTempTypeMap, TypeMap, ((Allocator*)NULL, tm->oracle));
     tm->flush();
     verbose_only( tm->branches = NULL; )
 
 #if !defined XP_WIN
     debug_only(PodZero(&jitstats));
 #endif
 
 #ifdef JS_JIT_SPEW
@@ -7783,19 +7778,19 @@ FinishJIT(TraceMonitor *tm)
 #define JITSTAT(x) /* nothing */
 #include "jitstats.tbl"
 #undef JITSTAT
 #undef MONITOR_JITSTAT
         debug_only_print0(LC_TMStats, "\n");
     }
 #endif
 
-    js_delete(tm->recordAttempts);
-    js_delete(tm->loopProfiles);
-    js_delete(tm->oracle);
+    Foreground::delete_(tm->recordAttempts);
+    Foreground::delete_(tm->loopProfiles);
+    Foreground::delete_(tm->oracle);
 
 #ifdef DEBUG
     // Recover profiling data from expiring Fragments, and display
     // final results.
     if (LogController.lcbits & LC_FragProfile) {
 
         for (Seq<Fragment*>* f = tm->branches; f; f = f->tail)
             FragProfiling_FragFinalizer(f->head, tm);
@@ -7805,45 +7800,45 @@ FinishJIT(TraceMonitor *tm)
                 JS_ASSERT(f->root == f);
                 for (TreeFragment *p = f; p; p = p->peer)
                     FragProfiling_FragFinalizer(p, tm);
             }
         }
 
         if (tm->profTab)
             FragProfiling_showResults(tm);
-        js_delete(tm->profAlloc);
+        Foreground::delete_(tm->profAlloc);
 
     } else {
         NanoAssert(!tm->profTab);
         NanoAssert(!tm->profAlloc);
     }
 #endif
 
     PodArrayZero(tm->vmfragments);
 
-    js_delete(tm->frameCache);
+    Foreground::delete_(tm->frameCache);
     tm->frameCache = NULL;
 
-    js_delete(tm->codeAlloc);
+    Foreground::delete_(tm->codeAlloc);
     tm->codeAlloc = NULL;
 
-    js_delete(tm->dataAlloc);
+    Foreground::delete_(tm->dataAlloc);
     tm->dataAlloc = NULL;
 
-    js_delete(tm->traceAlloc);
+    Foreground::delete_(tm->traceAlloc);
     tm->traceAlloc = NULL;
 
-    js_delete(tm->tempAlloc);
+    Foreground::delete_(tm->tempAlloc);
     tm->tempAlloc = NULL;
 
-    js_delete(tm->storage);
+    Foreground::delete_(tm->storage);
     tm->storage = NULL;
 
-    js_delete(tm->cachedTempTypeMap);
+    Foreground::delete_(tm->cachedTempTypeMap);
     tm->cachedTempTypeMap = NULL;
 }
 
 JS_REQUIRES_STACK void
 PurgeScriptFragments(TraceMonitor* tm, JSScript* script)
 {
     debug_only_printf(LC_TMTracer,
                       "Purging fragments for JSScript %p.\n", (void*)script);
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -71,34 +71,34 @@ public:
         if (!_max)
             _max = 8;
         _max = JS_MAX(_max * 2, size);
         if (alloc) {
             T* tmp = new (*alloc) T[_max];
             memcpy(tmp, _data, _len * sizeof(T));
             _data = tmp;
         } else {
-            _data = (T*)js_realloc(_data, _max * sizeof(T));
+            _data = (T*) js::OffTheBooks::realloc(_data, _max * sizeof(T));
         }
 #if defined(DEBUG)
         memset(&_data[_len], 0xcd, _max - _len);
 #endif
     }
 
     Queue(nanojit::Allocator* alloc)
         : alloc(alloc)
     {
         this->_max =
         this->_len = 0;
         this->_data = NULL;
     }
 
     ~Queue() {
         if (!alloc)
-            js_free(_data);
+            js::UnwantedForeground::free(_data);
     }
 
     bool contains(T a) {
         for (unsigned n = 0; n < _len; ++n) {
             if (_data[n] == a)
                 return true;
         }
         return false;
@@ -194,22 +194,25 @@ class Tracker {
 
     struct TrackerPage {
         struct TrackerPage* next;
         jsuword             base;
         nanojit::LIns*      map[TRACKER_PAGE_ENTRIES];
     };
     struct TrackerPage* pagelist;
 
+    /* Keep track of memory allocation. */
+    JSContext* cx;
+
     jsuword             getTrackerPageBase(const void* v) const;
     jsuword             getTrackerPageOffset(const void* v) const;
     struct TrackerPage* findTrackerPage(const void* v) const;
     struct TrackerPage* addTrackerPage(const void* v);
 public:
-    Tracker();
+    Tracker(JSContext* cx);
     ~Tracker();
 
     bool            has(const void* v) const;
     nanojit::LIns*  get(const void* v) const;
     void            set(const void* v, nanojit::LIns* ins);
     void            clear();
 };
 
@@ -402,25 +405,28 @@ struct VMSideExit : public nanojit::Side
 
     inline TreeFragment* root() {
         return fromFrag()->root;
     }
 };
 
 class VMAllocator : public nanojit::Allocator
 {
-
 public:
-    VMAllocator(char* reserve, size_t reserveSize)
-      : mOutOfMemory(false), mSize(0), mReserve(reserve),
-        mReserveCurr(uintptr_t(reserve)), mReserveLimit(uintptr_t(reserve + reserveSize))
+    VMAllocator(JSRuntime *rt, char* reserve, size_t reserveSize)
+      : mOutOfMemory(false)
+      , mSize(0)
+      , mReserve(reserve)
+      , mReserveCurr(uintptr_t(reserve))
+      , mReserveLimit(uintptr_t(reserve + reserveSize))
+      , mRt(rt)
     {}
 
     ~VMAllocator() {
-        js_free(mReserve);
+        js::UnwantedForeground::free(mReserve);
     }
 
     size_t size() {
         return mSize;
     }
 
     bool outOfMemory() {
         return mOutOfMemory;
@@ -467,16 +473,19 @@ public:
 
     bool mOutOfMemory;
     size_t mSize;
 
     /* See nanojit::Allocator::allocChunk() for details on these. */
     char* mReserve;
     uintptr_t mReserveCurr;
     uintptr_t mReserveLimit;
+
+    /* To keep track of allocation. */
+    JSRuntime* mRt;
 };
 
 struct FrameInfo {
     JSObject*       block;      // caller block chain head
     jsbytecode*     pc;         // caller fp->regs->pc
     jsbytecode*     imacpc;     // caller fp->imacpc
     uint32          spdist;     // distance from fp->slots to fp->regs->sp at JSOP_CALL
 
@@ -979,16 +988,18 @@ typedef HashMap<nanojit::LIns*, JSObject
 # define AbortRecording(cx, reason) AbortRecordingImpl(cx)
 #endif
 
 void
 AbortProfiling(JSContext *cx);
 
 class TraceRecorder
 {
+    JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
+
     /*************************************************************** Recording session constants */
 
     /* The context in which recording started. */
     JSContext* const                cx;
 
     /* Cached value of JS_TRACE_MONITOR(cx). */
     TraceMonitor* const             traceMonitor;
 
@@ -1686,17 +1697,17 @@ RecordTracePoint(JSContext*, uintN& inli
 extern JS_REQUIRES_STACK TracePointAction
 MonitorTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist,
                   void** traceData, uintN *traceEpoch, uint32 *loopCounter, uint32 hits);
 
 extern JS_REQUIRES_STACK TraceRecorder::AbortResult
 AbortRecording(JSContext* cx, const char* reason);
 
 extern bool
-InitJIT(TraceMonitor *tm);
+InitJIT(TraceMonitor *tm, JSRuntime *rt);
 
 extern void
 FinishJIT(TraceMonitor *tm);
 
 extern void
 PurgeScriptFragments(TraceMonitor* tm, JSScript* script);
 
 extern bool
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -118,17 +118,17 @@ ArrayBuffer::prop_getByteLength(JSContex
 }
 
 void
 ArrayBuffer::class_finalize(JSContext *cx, JSObject *obj)
 {
     ArrayBuffer *abuf = ArrayBuffer::fromJSObject(obj);
     if (abuf) {
         abuf->freeStorage(cx);
-        cx->destroy<ArrayBuffer>(abuf);
+        cx->delete_(abuf);
     }
 }
 
 /*
  * new ArrayBuffer(byteLength)
  */
 JSBool
 ArrayBuffer::class_constructor(JSContext *cx, uintN argc, Value *vp)
@@ -156,22 +156,22 @@ ArrayBuffer::create(JSContext *cx, int32
          * We're just not going to support arrays that are bigger than what will fit
          * as an integer value; if someone actually ever complains (validly), then we
          * can fix.
          */
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
         return NULL;
     }
 
-    ArrayBuffer *abuf = cx->create<ArrayBuffer>();
+    ArrayBuffer *abuf = cx->new_<ArrayBuffer>();
     if (!abuf)
         return NULL;
 
     if (!abuf->allocateStorage(cx, nbytes)) {
-        cx->destroy<ArrayBuffer>(abuf);
+        Foreground::delete_(abuf);
         return NULL;
     }
 
     obj->setPrivate(abuf);
     return obj;
 }
 
 bool
@@ -722,17 +722,17 @@ class TypedArrayTemplate
 
     static JSObject *
     createTypedArray(JSContext *cx, JSObject *bufobj, uint32 byteOffset, uint32 len)
     {
         JSObject *obj = NewBuiltinClassInstance(cx, slowClass());
         if (!obj)
             return NULL;
 
-        ThisTypeArray *tarray = cx->create<ThisTypeArray>(bufobj, byteOffset, len);
+        ThisTypeArray *tarray = cx->new_<ThisTypeArray>(bufobj, byteOffset, len);
         if (!tarray)
             return NULL;
 
         JS_ASSERT(obj->getClass() == slowClass());
         obj->setSharedNonNativeMap();
         obj->clasp = fastClass();
         obj->setPrivate(tarray);
 
@@ -827,17 +827,17 @@ class TypedArrayTemplate
         return createTypedArrayWithOffsetLength(cx, dataObj, byteOffset, length);
     }
 
     static void
     class_finalize(JSContext *cx, JSObject *obj)
     {
         ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
         if (tarray)
-            cx->destroy<ThisTypeArray>(tarray);
+            cx->delete_(tarray);
     }
 
     /* subarray(start[, end]) */
     static JSBool
     fun_subarray(JSContext *cx, uintN argc, Value *vp)
     {
         JSObject *obj = ToObject(cx, &vp[1]);
         if (!obj)
@@ -1319,17 +1319,17 @@ class TypedArrayTemplate
                 *dest++ = NativeType(*src++);
             break;
           }
           default:
             JS_NOT_REACHED("copyFromWithOverlap with a TypedArray of unknown type");
             break;
         }
 
-        js_free(srcbuf);
+        UnwantedForeground::free(srcbuf);
         return true;
     }
 
     static JSObject *
     createBufferWithSizeAndCount(JSContext *cx, uint32 count)
     {
         size_t size = sizeof(NativeType);
         if (size != 0 && count >= INT32_MAX / size) {
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -316,17 +316,17 @@ CallTree(void **bp)
                  ? strdup(symbol)
                  : JS_smprintf("%s+%X",
                                info.dli_fname ? info.dli_fname : "main",
                                offset);
         if (!method)
             return NULL;
 
         /* Create a new callsite record. */
-        site = (JSCallsite *) js_malloc(sizeof(JSCallsite));
+        site = (JSCallsite *) OffTheBooks::malloc(sizeof(JSCallsite));
         if (!site)
             return NULL;
 
         /* Insert the new site into the tree. */
         site->pc = (uint32)pc;
         site->name = method;
         site->library = info.dli_fname;
         site->offset = offset;
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -223,17 +223,21 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_coun
             return NULL; \
         } \
     } while (0)
 
 #else
 #define JS_OOM_POSSIBLY_FAIL() do {} while(0)
 #endif
 
-
+/*
+ * 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 malloc(bytes);
 }
 
 static JS_INLINE void* js_calloc(size_t bytes) {
     JS_OOM_POSSIBLY_FAIL();
     return calloc(bytes, 1);
@@ -246,104 +250,277 @@ static JS_INLINE void* js_realloc(void* 
 
 static JS_INLINE void js_free(void* p) {
     free(p);
 }
 #endif/* JS_USE_CUSTOM_ALLOCATOR */
 
 JS_END_EXTERN_C
 
+
+
 #ifdef __cplusplus
 
 /* 
- * Using vanilla new/new[] is unsafe in SpiderMonkey because they throw on
- * failure instead of returning NULL, which is what SpiderMonkey expects.
- * js_new()/js_array_new() should be used instead, and memory allocated with
- * them should be deallocated with js_delete()/js_array_delete().
+ * User guide to memory memangement within SpiderMonkey:
+ *
+ * Quick tips:
+ *
+ *   Allocation:
+ *   - Prefer to allocate using JSContext:
+ *       cx->{malloc,realloc,calloc,new_,new_array}
+ *
+ *   - If no JSContext is available, use a JSRuntime:
+ *       rt->{malloc,realloc,calloc,new_,new_array}
+ *
+ *   - As a last resort, use unaccounted allocation ("OffTheBooks"):
+ *       js::OffTheBooks::{malloc,realloc,calloc,new_,new_array}
+ *
+ *   Deallocation:
+ *   - When the deallocation occurs on a slow path, use:
+ *       Foreground::{free,delete_,array_delete}
+ *
+ *   - Otherwise deallocate on a background thread using a JSContext:
+ *       cx->{free,delete_,array_delete}
+ *  
+ *   - If no JSContext is available, use a JSRuntime:
+ *       rt->{free,delete_,array_delete}
+ *
+ *   - As a last resort, use UnwantedForeground deallocation:
+ *       js::UnwantedForeground::{free,delete_,array_delete}
+ *
+ * General tips:
+ *
+ *   - Mixing and matching these allocators is allowed (you may free memory
+ *     allocated by any allocator, with any deallocator).
+ * 
+ *   - Never, ever use normal C/C++ memory management:
+ *       malloc, free, new, new[], delete, operator new, etc.
  *
- * If you have a class with a private constructor or destructor, you can
- * make js_new/js_delete a friend.  This can be fiddly, and the interaction of
- * template functions, friend functions and namespaces can overwhelm even
- * modern compilers.  Manual inlining is probably easier.
+ *   - Never, ever use low-level SpiderMonkey allocators:
+ *       js_malloc(), js_free(), js_calloc(), js_realloc()
+ *     Their use is reserved for the other memory managers.
+ *
+ *   - Classes which have private constructors or destructors should have
+ *     JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR added to their
+ *     declaration.
+ * 
+ * Details:
+ *
+ *   Using vanilla new/new[] is unsafe in SpiderMonkey because they throw on
+ *   failure instead of returning NULL, which is what SpiderMonkey expects.
+ *   (Even overriding them is unsafe, as the system's C++ runtime library may
+ *   throw, which we do not support. We also can't just use the 'nothrow'
+ *   variant of new/new[], because we want to mediate *all* allocations
+ *   within SpiderMonkey, to satisfy any embedders using
+ *   JS_USE_CUSTOM_ALLOCATOR.)
  *
- * (If you're wondering why we can't just use the 'nothrow' variant of
- * new/new[], it's because we want to mediate *all* allocations within
- * SpiderMonkey, to satisfy any embedders using JS_USE_CUSTOM_ALLOCATOR.)
+ *   JSContexts and JSRuntimes keep track of memory allocated, and use this
+ *   accounting to schedule GC. OffTheBooks does not. We'd like to remove
+ *   OffTheBooks allocations as much as possible (bug 636558).
+ *
+ *   On allocation failure, a JSContext correctly reports an error, which a
+ *   JSRuntime and OffTheBooks does not.
+ *
+ *   A JSContext deallocates in a background thread. A JSRuntime might
+ *   deallocate in the background in the future, but does not now. Foreground
+ *   deallocation is preferable on slow paths. UnwantedForeground deallocations
+ *   occur where we have no JSContext or JSRuntime, and the deallocation is not
+ *   on a slow path. We want to remove UnwantedForeground deallocations (bug
+ *   636561).
+ *
+ *   JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR makes the allocation
+ *   classes friends with your class, giving them access to private
+ *   constructors and destructors.
+ *
+ *   |make check| does a source level check on the number of uses OffTheBooks,
+ *   UnwantedForeground, js_malloc, js_free etc, to prevent regressions. If you
+ *   really must add one, update Makefile.in, and run |make check|.
+ *
+ *   |make check| also statically prevents the use of vanilla new/new[].
  */
 
-#define JS_NEW_BODY(t, parms)                                                 \
-    void *memory = js_malloc(sizeof(t));                                      \
+#define JS_NEW_BODY(allocator, t, parms)                                       \
+    void *memory = allocator(sizeof(t));                                       \
     return memory ? new(memory) t parms : NULL;
 
-template <class T>
-JS_ALWAYS_INLINE T *js_new() {
-    JS_NEW_BODY(T, ())
-}
-
-template <class T, class P1>
-JS_ALWAYS_INLINE T *js_new(const P1 &p1) {
-    JS_NEW_BODY(T, (p1))
-}
-
-template <class T, class P1, class P2>
-JS_ALWAYS_INLINE T *js_new(const P1 &p1, const P2 &p2) {
-    JS_NEW_BODY(T, (p1, p2))
-}
-
-template <class T, class P1, class P2, class P3>
-JS_ALWAYS_INLINE T *js_new(const P1 &p1, const P2 &p2, const P3 &p3) {
-    JS_NEW_BODY(T, (p1, p2, p3))
-}
-
-template <class T, class P1, class P2, class P3, class P4>
-JS_ALWAYS_INLINE T *js_new(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4) {
-    JS_NEW_BODY(T, (p1, p2, p3, p4))
-}
-
-/* ...add additional js_new()s as necessary... */
-
-#undef JS_NEW_BODY
+/*
+ * Given a class which should provide new_() methods, add
+ * JS_DECLARE_NEW_METHODS (see JSContext for a usage example). This
+ * adds new_()s with up to 12 parameters. Add more versions of new_ below if
+ * you need more than 12 parameters.  
+ *
+ * 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(ALLOCATOR, QUALIFIERS)\
+    template <class T>\
+    QUALIFIERS T *new_() {\
+        JS_NEW_BODY(ALLOCATOR, T, ())\
+    }\
+\
+    template <class T, class P1>\
+    QUALIFIERS T *new_(const P1 &p1) {\
+        JS_NEW_BODY(ALLOCATOR, T, (p1))\
+    }\
+\
+    template <class T, class P1, class P2>\
+    QUALIFIERS T *new_(const P1 &p1, const P2 &p2) {\
+        JS_NEW_BODY(ALLOCATOR, T, (p1, p2))\
+    }\
+\
+    template <class T, class P1, class P2, class P3>\
+    QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3) {\
+        JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3))\
+    }\
+\
+    template <class T, class P1, class P2, class P3, class P4>\
+    QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4) {\
+        JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4))\
+    }\
+\
+    template <class T, class P1, class P2, class P3, class P4, class P5>\
+    QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5) {\
+        JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5))\
+    }\
+\
+    template <class T, class P1, class P2, class P3, class P4, class P5, class P6>\
+    QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6) {\
+        JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6))\
+    }\
+\
+    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7>\
+    QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6, const P7 &p7) {\
+        JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7))\
+    }\
+\
+    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8>\
+    QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6, const P7 &p7, const P8 &p8) {\
+        JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8))\
+    }\
+\
+    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9>\
+    QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6, const P7 &p7, const P8 &p8, const P9 &p9) {\
+        JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8, p9))\
+    }\
+\
+    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10>\
+    QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6, const P7 &p7, const P8 &p8, const P9 &p9, const P10 &p10) {\
+        JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10))\
+    }\
+\
+    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11>\
+    QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6, const P7 &p7, const P8 &p8, const P9 &p9, const P10 &p10, const P11 &p11) {\
+        JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11))\
+    }\
+\
+    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11, class P12>\
+    QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6, const P7 &p7, const P8 &p8, const P9 &p9, const P10 &p10, const P11 &p11, const P12 &p12) {\
+        JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12))\
+    }\
+    static const int JSMinAlignment = 8;\
+    template <class T>\
+    QUALIFIERS T *array_new(size_t n) {\
+        /* The length is stored just before the vector memory. */\
+        uint64 numBytes64 = uint64(JSMinAlignment) + uint64(sizeof(T)) * uint64(n);\
+        size_t numBytes = size_t(numBytes64);\
+        if (numBytes64 != numBytes) {\
+            JS_ASSERT(0);   /* we want to know if this happens in debug builds */\
+            return NULL;\
+        }\
+        void *memory = ALLOCATOR(numBytes);\
+        if (!memory)\
+            return NULL;\
+        *(size_t *)memory = n;\
+        memory = (void*)(uintptr_t(memory) + JSMinAlignment);\
+        return new(memory) T[n];\
+    }\
 
-template <class T>
-JS_ALWAYS_INLINE void js_delete(T *p) {
-    if (p) {
-        p->~T();
-        js_free(p);
-    }
-}
 
-static const int JSMinAlignment = 8;
-
-template <class T>
-JS_ALWAYS_INLINE T *js_array_new(size_t n) {
-	/* The length is stored just before the vector memory. */
-    uint64 numBytes64 = uint64(JSMinAlignment) + uint64(sizeof(T)) * uint64(n);
-    size_t numBytes = size_t(numBytes64);
-    if (numBytes64 != numBytes) {
-        JS_ASSERT(0);   /* we want to know if this happens in debug builds */
-        return NULL;
+#define JS_DECLARE_DELETE_METHODS(DEALLOCATOR, QUALIFIERS)\
+    template <class T>\
+    QUALIFIERS void delete_(T *p) {\
+        if (p) {\
+            p->~T();\
+            DEALLOCATOR(p);\
+        }\
+    }\
+\
+    template <class T>\
+    QUALIFIERS void array_delete(T *p) {\
+        if (p) {\
+            void* p0 = (void *)(uintptr_t(p) - js::OffTheBooks::JSMinAlignment);\
+            size_t n = *(size_t *)p0;\
+            for (size_t i = 0; i < n; i++)\
+                (p + i)->~T();\
+            DEALLOCATOR(p0);\
+        }\
     }
-    void *memory = js_malloc(numBytes);
-    if (!memory)
-        return NULL;
-	*(size_t *)memory = n;
-	memory = (void*)(uintptr_t(memory) + JSMinAlignment);
-    return new(memory) T[n];
-}
+
+
+/*
+ * In general, all allocations should go through a JSContext or JSRuntime, so
+ * that the garbage collector knows how much memory has been allocated. In
+ * cases where it is difficult to use a JSContext or JSRuntime, OffTheBooks can
+ * be used, though this is undesirable.
+ */
+namespace js {
+class OffTheBooks {
+public:
+    JS_DECLARE_NEW_METHODS(::js_malloc, JS_ALWAYS_INLINE static)
+
+    /*
+     * The parentheses around the following function names prevent the names
+     * from being expanded if they are defined in the system headers as macros
+     * (function-style macros only expand if followed by an open paratheses).
+     * This doesn't appear to be a problem for call-sites (ie cx->malloc()).
+     */
+    static JS_INLINE void* (malloc)(size_t bytes) {
+        return ::js_malloc(bytes);
+    }
+
+    static JS_INLINE void* (calloc)(size_t bytes) {
+        return ::js_calloc(bytes);
+    }
 
-template <class T>
-JS_ALWAYS_INLINE void js_array_delete(T *p) {
-    if (p) {
-		void* p0 = (void *)(uintptr_t(p) - JSMinAlignment);
-		size_t n = *(size_t *)p0;
-		for (size_t i = 0; i < n; i++)
-			(p + i)->~T();
-		js_free(p0);
+    static JS_INLINE void* (realloc)(void* p, size_t bytes) {
+        return ::js_realloc(p, bytes);
+    }
+};
+
+/*
+ * We generally prefer deallocating using JSContext because it can happen in
+ * the background. On slow paths, we may prefer foreground allocation.
+ */
+class Foreground {
+public:
+    /* See parentheses comment above. */
+    static JS_ALWAYS_INLINE void (free)(void* p) {
+        ::js_free(p);
     }
-}
+
+    JS_DECLARE_DELETE_METHODS(::js_free, JS_ALWAYS_INLINE static)
+};
+
+class UnwantedForeground : public Foreground {
+};
+
+} /* namespace js */
+
+/*
+ * Note lack of ; in JSRuntime below. This is intentional so "calling" this
+ * looks "normal".
+ */
+#define JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR \
+    friend class js::OffTheBooks;\
+    friend class js::Foreground;\
+    friend class js::UnwantedForeground;\
+    friend struct ::JSContext;\
+    friend struct ::JSRuntime
+
 
 /**
  * The following classes are designed to cause assertions to detect
  * inadvertent use of guard objects as temporaries.  In other words,
  * when we have a guard object whose only purpose is its constructor and
  * destructor (and is never otherwise referenced), the intended use
  * might be:
  *     JSAutoTempValueRooter tvr(cx, 1, &val);
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -886,27 +886,27 @@ bool
 JSXMLArray::setCapacity(JSContext *cx, uint32 newCapacity)
 {
     if (newCapacity == 0) {
         /* We could let realloc(p, 0) free this, but purify gets confused. */
         if (vector) {
             if (cx)
                 cx->free(vector);
             else
-                js_free(vector);
+                Foreground::free(vector);
         }
         vector = NULL;
     } else {
         void **tmp;
 
         if (
 #if JS_BITS_PER_WORD == 32
             (size_t)newCapacity > ~(size_t)0 / sizeof(void *) ||
 #endif
-            !(tmp = (void **) js_realloc(vector, newCapacity * sizeof(void *)))) {
+            !(tmp = (void **) OffTheBooks::realloc(vector, newCapacity * sizeof(void *)))) {
             if (cx)
                 JS_ReportOutOfMemory(cx);
             return false;
         }
         vector = tmp;
     }
     capacity = JSXML_PRESET_CAPACITY | newCapacity;
     return true;
@@ -983,17 +983,17 @@ XMLArrayAddMember(JSContext *cx, JSXMLAr
                 JS_CEILING_LOG2(log2, capacity);
                 capacity = JS_BIT(log2);
             }
             if (
 #if JS_BITS_PER_WORD == 32
                 (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
 #endif
                 !(vector = (void **)
-                           js_realloc(array->vector, capacity * sizeof(void *)))) {
+                           cx->realloc(array->vector, capacity * sizeof(void *)))) {
                 JS_ReportOutOfMemory(cx);
                 return JS_FALSE;
             }
             array->capacity = capacity;
             array->vector = vector;
             for (i = array->length; i < index; i++)
                 vector[i] = NULL;
         }
@@ -1067,17 +1067,17 @@ XMLArrayTruncate(JSContext *cx, JSXMLArr
     if (length >= array->length)
         return;
 
     if (length == 0) {
         if (array->vector)
             cx->free(array->vector);
         vector = NULL;
     } else {
-        vector = (void **) js_realloc(array->vector, length * sizeof(void *));
+        vector = (void **) cx->realloc(array->vector, length * sizeof(void *));
         if (!vector)
             return;
     }
 
     if (array->length > length)
         array->length = length;
     array->capacity = length;
     array->vector = vector;
@@ -4881,17 +4881,17 @@ xml_enumerate(JSContext *cx, JSObject *o
     length = JSXML_LENGTH(xml);
 
     switch (enum_op) {
       case JSENUMERATE_INIT:
       case JSENUMERATE_INIT_ALL:
         if (length == 0) {
             statep->setInt32(0);
         } else {
-            cursor = cx->create<JSXMLArrayCursor>(&xml->xml_kids);
+            cursor = cx->new_<JSXMLArrayCursor>(&xml->xml_kids);
             if (!cursor)
                 return JS_FALSE;
             statep->setPrivate(cursor);
         }
         if (idp)
             *idp = INT_TO_JSID(length);
         break;
 
@@ -4907,17 +4907,17 @@ xml_enumerate(JSContext *cx, JSObject *o
             break;
         }
         /* FALL THROUGH */
 
       case JSENUMERATE_DESTROY:
         if (!statep->isInt32(0)) {
             cursor = (JSXMLArrayCursor *) statep->toPrivate();
             if (cursor)
-                cx->destroy(cursor);
+                cx->delete_(cursor);
         }
         statep->setNull();
         break;
     }
     return JS_TRUE;
 }
 
 static JSType
@@ -7577,17 +7577,17 @@ xmlfilter_trace(JSTracer *trc, JSObject 
 
 static void
 xmlfilter_finalize(JSContext *cx, JSObject *obj)
 {
     JSXMLFilter *filter = (JSXMLFilter *) obj->getPrivate();
     if (!filter)
         return;
 
-    cx->destroy(filter);
+    cx->delete_(filter);
 }
 
 Class js_XMLFilterClass = {
     "XMLFilter",
     JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS,
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
@@ -7647,17 +7647,17 @@ js_StepXMLListFilter(JSContext *cx, JSBo
         filterobj = NewNonFunction<WithProto::Given>(cx, &js_XMLFilterClass, NULL, NULL);
         if (!filterobj)
             return JS_FALSE;
 
         /*
          * Init all filter fields before setPrivate exposes it to
          * xmlfilter_trace or xmlfilter_finalize.
          */
-        filter = cx->create<JSXMLFilter>(list, &list->xml_kids);
+        filter = cx->new_<JSXMLFilter>(list, &list->xml_kids);
         if (!filter)
             return JS_FALSE;
         filterobj->setPrivate(filter);
 
         /* Store filterobj to use in the later iterations. */
         sp[-2] = OBJECT_TO_JSVAL(filterobj);
 
         resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -618,17 +618,17 @@ JS_STATIC_ASSERT(offsetof(VMFrame, regs.
 JS_STATIC_ASSERT(JSVAL_TAG_MASK == 0xFFFF800000000000LL);
 JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0x00007FFFFFFFFFFFLL);
 
 #endif                   /* _WIN64 */
 
 bool
 JaegerCompartment::Initialize()
 {
-    execAlloc_ = js_new<JSC::ExecutableAllocator>();
+    execAlloc_ = js::OffTheBooks::new_<JSC::ExecutableAllocator>();
     if (!execAlloc_)
         return false;
     
     TrampolineCompiler tc(execAlloc_, &trampolines);
     if (!tc.compile()) {
         delete execAlloc_;
         return false;
     }
@@ -642,17 +642,17 @@ JaegerCompartment::Initialize()
 
     return true;
 }
 
 void
 JaegerCompartment::Finish()
 {
     TrampolineCompiler::release(&trampolines);
-    js_delete(execAlloc_);
+    Foreground::delete_(execAlloc_);
 #ifdef JS_METHODJIT_PROFILE_STUBS
     FILE *fp = fopen("/tmp/stub-profiling", "wt");
 # define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) \
     fprintf(fp, "%03d %s %d\n", val, #op, StubCallsForOp[val]);
 # include "jsopcode.tbl"
 # undef OPDEF
     fclose(fp);
 #endif
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -152,17 +152,17 @@ class BasePolyIC : public BaseIC {
   public:
     BasePolyIC() {
         u.execPool = NULL;
     }
 
     ~BasePolyIC() {
         releasePools();
         if (areMultiplePools())
-            js_delete(multiplePools());
+            Foreground::delete_(multiplePools());
     }
 
     void reset() {
         BaseIC::reset();
         releasePools();
         if (areZeroPools()) {
             // Common case:  do nothing.
         } else if (isOnePool()) {
@@ -187,21 +187,21 @@ class BasePolyIC : public BaseIC {
     bool addPool(JSContext *cx, JSC::ExecutablePool *pool) {
         if (areZeroPools()) {
             u.execPool = pool;
             return true;
         }
         if (isOnePool()) {
             JSC::ExecutablePool *oldPool = u.execPool;
             JS_ASSERT(!isTagged(oldPool));
-            ExecPoolVector *execPools = js_new<ExecPoolVector>(SystemAllocPolicy()); 
+            ExecPoolVector *execPools = cx->new_<ExecPoolVector>(SystemAllocPolicy()); 
             if (!execPools)
                 return false;
             if (!execPools->append(oldPool) || !execPools->append(pool)) {
-                js_delete(execPools);
+                Foreground::delete_(execPools);
                 return false;
             }
             u.taggedExecPools = tag(execPools);
             return true;
         }
         return multiplePools()->append(pool); 
     }
 };
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -58,31 +58,31 @@ pm_construct(JSContext* cx, uintN argc, 
 
     JSObject *obj = JS_NewObjectForConstructor(cx, vp);
     if (!obj)
         return JS_FALSE;
 
     if (!JS_FreezeObject(cx, obj))
         return JS_FALSE;
 
-    PerfMeasurement* p = js_new<PerfMeasurement>(PerfMeasurement::EventMask(mask));
+    PerfMeasurement* p = cx->new_<PerfMeasurement>(PerfMeasurement::EventMask(mask));
     if (!p) {
         JS_ReportOutOfMemory(cx);
         return JS_FALSE;
     }
 
     JS_SetPrivate(cx, obj, p);
     *vp = OBJECT_TO_JSVAL(obj);
     return JS_TRUE;
 }
 
 static void
 pm_finalize(JSContext* cx, JSObject* obj)
 {
-    js_delete((PerfMeasurement*) JS_GetPrivate(cx, obj));
+    cx->delete_((PerfMeasurement*) JS_GetPrivate(cx, obj));
 }
 
 // Property access
 
 #define GETTER(name)                                                    \
     static JSBool                                                       \
     pm_get_##name(JSContext* cx, JSObject* obj, jsid /*unused*/, jsval* vp) \
     {                                                                   \
--- a/js/src/perf/pm_linux.cpp
+++ b/js/src/perf/pm_linux.cpp
@@ -31,16 +31,18 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+using namespace js;
+
 #include "jsperf.h"
 #include "jsutil.h"
 
 /* This variant of nsIPerfMeasurement uses the perf_event interface
  * added in Linux 2.6.31.  We key compilation of this file off the
  * existence of <linux/perf_event.h>.
  */
 
@@ -260,17 +262,17 @@ Impl::stop(PerfMeasurement* counters)
 } // anonymous namespace
 
 
 namespace JS {
 
 #define initCtr(flag) ((eventsMeasured & flag) ? 0 : -1)
 
 PerfMeasurement::PerfMeasurement(PerfMeasurement::EventMask toMeasure)
-  : impl(js_new<Impl>()),
+  : impl(OffTheBooks::new_<Impl>()),
     eventsMeasured(impl ? static_cast<Impl*>(impl)->init(toMeasure)
                    : EventMask(0)),
     cpu_cycles(initCtr(CPU_CYCLES)),
     instructions(initCtr(INSTRUCTIONS)),
     cache_references(initCtr(CACHE_REFERENCES)),
     cache_misses(initCtr(CACHE_MISSES)),
     branch_instructions(initCtr(BRANCH_INSTRUCTIONS)),
     branch_misses(initCtr(BRANCH_MISSES)),
@@ -281,17 +283,17 @@ PerfMeasurement::PerfMeasurement(PerfMea
     cpu_migrations(initCtr(CPU_MIGRATIONS))
 {
 }
 
 #undef initCtr
 
 PerfMeasurement::~PerfMeasurement()
 {
-    js_delete(static_cast<Impl*>(impl));
+    js::Foreground::delete_(static_cast<Impl*>(impl));
 }
 
 void
 PerfMeasurement::start()
 {
     if (impl)
         static_cast<Impl*>(impl)->start();
 }
--- a/js/src/yarr/pcre/pcre_compile.cpp
+++ b/js/src/yarr/pcre/pcre_compile.cpp
@@ -2583,19 +2583,17 @@ JSRegExp* jsRegExpCompile(const UChar* p
     int length = calculateCompiledPatternLength(pattern, patternLength, ignoreCase, cd, errorcode);
     if (errorcode)
         return returnError(errorcode, error);
     
     if (length > MAX_PATTERN_SIZE)
         return returnError(ERR16, error);
     
     size_t size = length + sizeof(JSRegExp);
-    // FIXME: bug 574459 -- no NULL check
-    JSRegExp* re = reinterpret_cast<JSRegExp*>(js_array_new<char>(size));
-    
+    JSRegExp* re = reinterpret_cast<JSRegExp*>(js::OffTheBooks::array_new<char>(size));
     if (!re)
         return returnError(ERR13, error);
     
     re->options = (ignoreCase ? IgnoreCaseOption : 0) | (multiline ? MatchAcrossMultipleLinesOption : 0);
     
     /* The starting points of the name/number translation table and of the code are
      passed around in the compile data block. */
     
@@ -2640,17 +2638,17 @@ JSRegExp* jsRegExpCompile(const UChar* p
      subpattern. */
     
     if (re->topBackref > re->topBracket)
         errorcode = ERR15;
     
     /* Failed to compile, or error while post-processing */
     
     if (errorcode != ERR0) {
-        js_array_delete(reinterpret_cast<char*>(re));
+        js::Foreground::array_delete(reinterpret_cast<char*>(re));
         return returnError(errorcode, error);
     }
     
     /* If the anchored option was not passed, set the flag if we can determine that
      the pattern is anchored by virtue of ^ characters or \A or anything else (such
      as starting with .* when DOTALL is set).
      
      Otherwise, if we know what the first character has to be, save it, because that
@@ -2695,10 +2693,10 @@ JSRegExp* jsRegExpCompile(const UChar* p
     if (numSubpatterns)
         *numSubpatterns = re->topBracket;
 
     return re;
 }
 
 void jsRegExpFree(JSRegExp* re)
 {
-    js_array_delete(reinterpret_cast<char*>(re));
+    js::Foreground::array_delete(reinterpret_cast<char*>(re));
 }
--- a/js/src/yarr/pcre/pcre_exec.cpp
+++ b/js/src/yarr/pcre/pcre_exec.cpp
@@ -389,17 +389,17 @@ struct MatchStack {
     bool canUseStackBufferForNextFrame() {
         return size < numFramesOnStack;
     }
     
     MatchFrame* allocateNextFrame() {
         if (canUseStackBufferForNextFrame())
             return currentFrame + 1;
         // FIXME: bug 574459 -- no NULL check
-        MatchFrame *frame = js_new<MatchFrame>();
+        MatchFrame *frame = js::OffTheBooks::new_<MatchFrame>();
         frame->init(regExpPool);
         return frame;
     }
     
     void pushNewFrame(const unsigned char* instructionPtr, BracketChainNode* bracketChain, ReturnLocation returnLocation) {
         MatchFrame* newframe = allocateNextFrame();
         newframe->previousFrame = currentFrame;
 
@@ -412,17 +412,17 @@ struct MatchStack {
 
         currentFrame = newframe;
     }
     
     void popCurrentFrame() {
         MatchFrame* oldFrame = currentFrame;
         currentFrame = currentFrame->previousFrame;
         if (size > numFramesOnStack)
-            js_delete(oldFrame);
+            js::Foreground::delete_(oldFrame);
         size--;
     }
 
     void popAllFrames() {
         while (size)
             popCurrentFrame();
     }
 };
--- a/js/src/yarr/yarr/RegExpJitTables.h
+++ b/js/src/yarr/yarr/RegExpJitTables.h
@@ -2625,46 +2625,46 @@ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 };
 
 
 CharacterClass* digitsCreate()
 {
     // FIXME: bug 574459 -- no NULL check
-    CharacterClass* characterClass = js_new<CharacterClass>((CharacterClassTable*)NULL);
+    CharacterClass* characterClass = js::OffTheBooks::new_<CharacterClass>((CharacterClassTable*)NULL);
     characterClass->m_ranges.append(CharacterRange(0x30, 0x39));
     return characterClass;
 }
 
 CharacterClass* nondigitsCreate()
 {
     // FIXME: bug 574459 -- no NULL check
-    CharacterClass* characterClass = js_new<CharacterClass>((CharacterClassTable*)NULL);
+    CharacterClass* characterClass = js::OffTheBooks::new_<CharacterClass>((CharacterClassTable*)NULL);
     characterClass->m_ranges.append(CharacterRange(0x00, 0x2f));
     characterClass->m_ranges.append(CharacterRange(0x3a, 0x7f));
     characterClass->m_rangesUnicode.append(CharacterRange(0x0080, 0xffff));
     return characterClass;
 }
 
 CharacterClass* newlineCreate()
 {
     // FIXME: bug 574459 -- no NULL check
-    CharacterClass* characterClass = js_new<CharacterClass>((CharacterClassTable*)NULL);
+    CharacterClass* characterClass = js::OffTheBooks::new_<CharacterClass>((CharacterClassTable*)NULL);
     characterClass->m_matches.append(0x0a);
     characterClass->m_matches.append(0x0d);
     characterClass->m_matchesUnicode.append(0x2028);
     characterClass->m_matchesUnicode.append(0x2029);
     return characterClass;
 }
 
 CharacterClass* spacesCreate()
 {
     // FIXME: bug 574459 -- no NULL check
-    CharacterClass* characterClass = js_new<CharacterClass>(CharacterClassTable::create(_spacesData, false));
+    CharacterClass* characterClass = js::OffTheBooks::new_<CharacterClass>(CharacterClassTable::create(_spacesData, false));
     characterClass->m_ranges.append(CharacterRange(0x09, 0x0d));
     characterClass->m_matches.append(0x20);
     characterClass->m_matchesUnicode.append(0x00a0);
     characterClass->m_matchesUnicode.append(0x1680);
     characterClass->m_matchesUnicode.append(0x180e);
     characterClass->m_rangesUnicode.append(CharacterRange(0x2000, 0x200a));
     characterClass->m_matchesUnicode.append(0x2028);
     characterClass->m_matchesUnicode.append(0x2029);
@@ -2672,17 +2672,17 @@ CharacterClass* spacesCreate()
     characterClass->m_matchesUnicode.append(0x205f);
     characterClass->m_matchesUnicode.append(0x3000);
     return characterClass;
 }
 
 CharacterClass* nonspacesCreate()
 {
     // FIXME: bug 574459 -- no NULL check
-    CharacterClass* characterClass = js_new<CharacterClass>(CharacterClassTable::create(_spacesData, true));
+    CharacterClass* characterClass = js::OffTheBooks::new_<CharacterClass>(CharacterClassTable::create(_spacesData, true));
     characterClass->m_ranges.append(CharacterRange(0x00, 0x08));
     characterClass->m_ranges.append(CharacterRange(0x0e, 0x1f));
     characterClass->m_ranges.append(CharacterRange(0x21, 0x7f));
     characterClass->m_rangesUnicode.append(CharacterRange(0x0080, 0x009f));
     characterClass->m_rangesUnicode.append(CharacterRange(0x00a1, 0x167f));
     characterClass->m_rangesUnicode.append(CharacterRange(0x1681, 0x180d));
     characterClass->m_rangesUnicode.append(CharacterRange(0x180f, 0x1fff));
     characterClass->m_rangesUnicode.append(CharacterRange(0x200b, 0x2027));
@@ -2691,30 +2691,30 @@ CharacterClass* nonspacesCreate()
     characterClass->m_rangesUnicode.append(CharacterRange(0x2060, 0x2fff));
     characterClass->m_rangesUnicode.append(CharacterRange(0x3001, 0xffff));
     return characterClass;
 }
 
 CharacterClass* nonwordcharCreate()
 {
     // FIXME: bug 574459 -- no NULL check
-    CharacterClass* characterClass = js_new<CharacterClass>(CharacterClassTable::create(_wordcharData, true));
+    CharacterClass* characterClass = js::OffTheBooks::new_<CharacterClass>(CharacterClassTable::create(_wordcharData, true));
     characterClass->m_ranges.append(CharacterRange(0x00, 0x2f));
     characterClass->m_ranges.append(CharacterRange(0x3a, 0x40));
     characterClass->m_ranges.append(CharacterRange(0x5b, 0x5e));
     characterClass->m_matches.append(0x60);
     characterClass->m_ranges.append(CharacterRange(0x7b, 0x7f));
     characterClass->m_rangesUnicode.append(CharacterRange(0x0080, 0xffff));
     return characterClass;
 }
 
 CharacterClass* wordcharCreate()
 {
     // FIXME: bug 574459 -- no NULL check
-    CharacterClass* characterClass = js_new<CharacterClass>(CharacterClassTable::create(_wordcharData, false));
+    CharacterClass* characterClass = js::OffTheBooks::new_<CharacterClass>(CharacterClassTable::create(_wordcharData, false));
     characterClass->m_ranges.append(CharacterRange(0x30, 0x39));
     characterClass->m_ranges.append(CharacterRange(0x41, 0x5a));
     characterClass->m_matches.append(0x5f);
     characterClass->m_ranges.append(CharacterRange(0x61, 0x7a));
     return characterClass;
 }
 
 
--- a/js/src/yarr/yarr/RegexCompiler.cpp
+++ b/js/src/yarr/yarr/RegexCompiler.cpp
@@ -135,17 +135,17 @@ public:
                 }
             }
         }
     }
 
     CharacterClass* charClass()
     {
         // FIXME: bug 574459 -- no NULL check
-        CharacterClass* characterClass = js_new<CharacterClass>((CharacterClassTable*)NULL);
+        CharacterClass* characterClass = js::OffTheBooks::new_<CharacterClass>((CharacterClassTable*)NULL);
 
         characterClass->m_matches.append(m_matches);
         characterClass->m_ranges.append(m_ranges);
         characterClass->m_matchesUnicode.append(m_matchesUnicode);
         characterClass->m_rangesUnicode.append(m_rangesUnicode);
 
         reset();
 
@@ -341,26 +341,26 @@ public:
 
     void atomParenthesesSubpatternBegin(bool capture = true)
     {
         unsigned subpatternId = m_pattern.m_numSubpatterns + 1;
         if (capture)
             m_pattern.m_numSubpatterns++;
 
         // FIXME: bug 574459 -- no NULL check
-        PatternDisjunction* parenthesesDisjunction = js_new<PatternDisjunction>(m_alternative);
+        PatternDisjunction* parenthesesDisjunction = js::OffTheBooks::new_<PatternDisjunction>(m_alternative);
         m_pattern.m_disjunctions.append(parenthesesDisjunction);
         m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture));
         m_alternative = parenthesesDisjunction->addNewAlternative();
     }
 
     void atomParentheticalAssertionBegin(bool invert = false)
     {
         // FIXME: bug 574459 -- no NULL check
-        PatternDisjunction* parenthesesDisjunction = js_new<PatternDisjunction>(m_alternative);
+        PatternDisjunction* parenthesesDisjunction = js::OffTheBooks::new_<PatternDisjunction>(m_alternative);
         m_pattern.m_disjunctions.append(parenthesesDisjunction);
         m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParentheticalAssertion, m_pattern.m_numSubpatterns + 1, parenthesesDisjunction, invert));
         m_alternative = parenthesesDisjunction->addNewAlternative();
     }
 
     void atomParenthesesEnd()
     {
         JS_ASSERT(m_alternative->m_parent);
@@ -396,17 +396,17 @@ public:
         }
 
         m_alternative->m_terms.append(PatternTerm(subpatternId));
     }
 
     PatternDisjunction* copyDisjunction(PatternDisjunction* disjunction)
     {
         // FIXME: bug 574459 -- no NULL check
-        PatternDisjunction* newDisjunction = js_new<PatternDisjunction>();
+        PatternDisjunction* newDisjunction = js::OffTheBooks::new_<PatternDisjunction>();
 
         newDisjunction->m_parent = disjunction->m_parent;
         for (unsigned alt = 0; alt < disjunction->m_alternatives.length(); ++alt) {
             PatternAlternative* alternative = disjunction->m_alternatives[alt];
             PatternAlternative* newAlternative = newDisjunction->addNewAlternative();
             for (unsigned i = 0; i < alternative->m_terms.length(); ++i)
                 newAlternative->m_terms.append(copyTerm(alternative->m_terms[i]));
         }
@@ -467,17 +467,17 @@ public:
     void disjunction()
     {
         m_alternative = m_alternative->m_parent->addNewAlternative();
     }
 
     void regexBegin()
     {
         // FIXME: bug 574459 -- no NULL check
-        m_pattern.m_body = js_new<PatternDisjunction>();
+        m_pattern.m_body = js::OffTheBooks::new_<PatternDisjunction>();
         m_alternative = m_pattern.m_body->addNewAlternative();
         m_pattern.m_disjunctions.append(m_pattern.m_body);
     }
     void regexEnd()
     {
     }
     void regexError()
     {
--- a/js/src/yarr/yarr/RegexPattern.h
+++ b/js/src/yarr/yarr/RegexPattern.h
@@ -55,32 +55,33 @@ struct CharacterRange {
     }
 };
 
 /*
  * Wraps a table and indicates inversion. Can be efficiently borrowed
  * between character classes, so it's refcounted.
  */
 struct CharacterClassTable {
+
+    JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
+
     const char* m_table;
     bool m_inverted;
     jsrefcount m_refcount;
 
     /* Ownership transferred to caller. */
     static CharacterClassTable *create(const char* table, bool inverted)
     {
         // FIXME: bug 574459 -- no NULL checks done by any of the callers, all
         // of which are in RegExpJitTables.h.
-        /* We can't (easily) use js_new() here because the constructor is private. */
-        void *memory = js_malloc(sizeof(CharacterClassTable));
-        return memory ? new(memory) CharacterClassTable(table, inverted) : NULL;
+        return js::OffTheBooks::new_<CharacterClassTable>(table, inverted);
     }
 
     void incref() { JS_ATOMIC_INCREMENT(&m_refcount); }
-    void decref() { if (JS_ATOMIC_DECREMENT(&m_refcount) == 0) js_delete(this); }
+    void decref() { if (JS_ATOMIC_DECREMENT(&m_refcount) == 0) js::Foreground::delete_(this); }
 
 private:
     CharacterClassTable(const char* table, bool inverted)
         : m_table(table)
         , m_inverted(inverted)
         , m_refcount(0)
     {
     }
@@ -225,16 +226,19 @@ struct PatternTerm {
     void quantify(unsigned count, QuantifierType type)
     {
         quantityCount = count;
         quantityType = type;
     }
 };
 
 struct PatternAlternative {
+
+    JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
+
     PatternAlternative(PatternDisjunction* disjunction)
         : m_parent(disjunction)
         , m_onceThrough(false)
         , m_hasFixedSize(false)
         , m_startsWithBOL(false)
         , m_containsBOL(false)
     {
     }
@@ -270,35 +274,38 @@ struct PatternAlternative {
     bool m_containsBOL : 1;
 };
 
 template<typename T, size_t N, class AP>
 static inline void
 deleteAllValues(js::Vector<T*,N,AP> &vector)
 {
     for (T** t = vector.begin(); t < vector.end(); ++t)
-        js_delete(*t);
+        js::Foreground::delete_(*t);
 }
 
 struct PatternDisjunction {
+
+    JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
+
     PatternDisjunction(PatternAlternative* parent = 0)
         : m_parent(parent)
         , m_hasFixedSize(false)
     {
     }
     
     ~PatternDisjunction()
     {
         deleteAllValues(m_alternatives);
     }
 
     PatternAlternative* addNewAlternative()
     {
         // FIXME: bug 574459 -- no NULL check
-        PatternAlternative* alternative = js_new<PatternAlternative>(this);
+        PatternAlternative* alternative = js::OffTheBooks::new_<PatternAlternative>(this);
         m_alternatives.append(alternative);
         return alternative;
     }
 
     js::Vector<PatternAlternative*, 0, js::SystemAllocPolicy> m_alternatives;
     PatternAlternative* m_parent;
     unsigned m_minimumSize;
     unsigned m_callFrameSize;