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
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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;