Bug 624878 - Remove dangerous uses of vanilla (throw-on-failure) |operator new|. r=lw.
authorNicholas Nethercote <nnethercote@mozilla.com>
Mon, 17 Jan 2011 19:44:10 -0800
changeset 60811 4046ef71ddc29da07aaf3c5c77c74e181274e5a3
parent 60810 d7e0e1cf933406a78cc596adcf3465a798b0f431
child 60812 68a2e71034b2690f19b46a495a810c9c001b5e86
child 61043 d6182fbb43bd30400682a184a5971345c719f309
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslw
bugs624878
milestone2.0b10pre
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 624878 - Remove dangerous uses of vanilla (throw-on-failure) |operator new|. r=lw.
config/find_vanilla_new_calls
js/src/Makefile.in
js/src/assembler/jit/ExecutableAllocator.h
js/src/config/find_vanilla_new_calls
js/src/ctypes/CTypes.cpp
js/src/ctypes/CTypes.h
js/src/jsapi.cpp
js/src/jscompartment.cpp
js/src/jsgc.cpp
js/src/jsparse.h
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jsutil.h
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/PolyIC.h
js/src/perf/jsperf.cpp
js/src/perf/pm_linux.cpp
js/src/yarr/jswtfbridge.h
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/find_vanilla_new_calls
@@ -0,0 +1,80 @@
+# /bin/bash
+
+#----------------------------------------------------------------------------
+# We must avoid using the vanilla new/new[] operators (and consequently, the
+# vanilla delete/delete[] operators) in SpiderMonkey, see bug 624878 for why.
+#
+# This script:
+# - Detects if any of the vanilla new/new[] operators are used in a file.
+#   Its exit code is 1 if it found some, and 0 if it didn't.
+# - Doesn't detect delete/delete[] because it appears they can be present
+#   somehow due to virtual destructors, but this is ok because vanilla
+#   delete/delete[] calls don't make sense without corresponding new/new[]
+#   calls, and any explicit calls will be caught by Valgrind's mismatched
+#   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().
+# - 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
+#  0000000f R_386_GOTPC       _GLOBAL_OFFSET_TABLE_
+#  0000001b R_386_PLT32       operator new(unsigned int)
+#  0000002e R_386_PC32        JSC::ExecutablePool::ExecutablePool(unsigned int)
+#  0000004a R_386_PC32        JSC::ExecutablePool::~ExecutablePool()
+#  00000052 R_386_PLT32       operator delete(void*)
+#
+#   This says that vanilla 'new' and 'delete' are both used in
+#   JSC::ExecutablePool::create(unsigned int).  This doesn't always work,
+#   though.  (Nb: use 'c++filt' to demangle names like
+#   _ZN3JSC14ExecutablePool6createEj.)
+#
+# If that doesn't work, use grep.
+#----------------------------------------------------------------------------
+
+if [ -z $1 ] ; then
+    echo "usage: find_vanilla_new_calls <file>"
+    exit 1
+fi
+
+file=$1
+
+if [ ! -f $file ] ; then
+    echo "TEST-UNEXPECTED-FAIL | find_vanilla_new_calls | file '$file' not found"
+    exit 1
+fi
+
+tmpfile1=`mktemp`
+tmpfile2=`mktemp`
+nm -C $file > $tmpfile1
+
+# Need to double-escape '[' and ']' to stop grep from interpreting them
+# specially.
+grep 'operator new(unsigned int)'        $tmpfile1 >> $tmpfile2
+grep 'operator new(unsigned long)'       $tmpfile1 >> $tmpfile2
+grep 'operator new\\[\\](unsigned int)'  $tmpfile1 >> $tmpfile2
+grep 'operator new\\[\\](unsigned long)' $tmpfile1 >> $tmpfile2
+rm -f $tmpfile1
+
+if [ -s $tmpfile2 ] ; then
+    echo "TEST-UNEXPECTED-FAIL | find_vanilla_new_calls | found calls are listed below"
+    cat $tmpfile2
+    echo
+    rm -f $tmpfile2
+    exit 1
+fi
+
+echo "TEST-PASS | find_vanilla_new_calls | ok"
+echo
+
+exit 0
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -567,16 +567,24 @@ check::
 	$(check-sync-dirs) $(srcdir)/config $(MOZ_SYNC_BUILD_FILES)/config
 	$(check-sync-dirs) $(srcdir)/build $(MOZ_SYNC_BUILD_FILES)/build
 
 check-valgrind::
 	$(check-sync-dirs) $(srcdir)/config $(MOZ_SYNC_BUILD_FILES)/config
 	$(check-sync-dirs) $(srcdir)/build $(MOZ_SYNC_BUILD_FILES)/build
 endif
 
+# The "find any vanilla new/new[] calls" script is tailored to Linux, so
+# only run it there.  That should be enough to catch any such calls that
+# creep in.
+ifeq ($(OS_ARCH),Linux)
+check::
+	$(srcdir)/config/find_vanilla_new_calls $(LIBRARY)
+endif
+
 ifdef ENABLE_TRACEJIT
 ifndef WINCE
 check::
 	$(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/jit-test/jit_test.py \
 	        --no-slow --no-progress --tinderbox --jitflags=m,j,mj,mjp,mjd $(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
@@ -108,17 +108,17 @@ namespace JSC {
 private:
     struct Allocation {
         char* pages;
         size_t size;
 #if WTF_PLATFORM_SYMBIAN
         RChunk* chunk;
 #endif
     };
-    typedef js::Vector<Allocation, 2 ,js::SystemAllocPolicy > AllocationList;
+    typedef js::Vector<Allocation, 2, js::SystemAllocPolicy> AllocationList;
 
     // Reference count for automatic reclamation.
     unsigned m_refCount;
 
 public:
     // 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.
@@ -127,24 +127,26 @@ public:
         JS_ASSERT(m_refCount);
         ++m_refCount;
     }
 
     void release()
     { 
         JS_ASSERT(m_refCount != 0);
         if (--m_refCount == 0)
-            delete this;
+            js_delete(this);
     }
 
     static ExecutablePool* create(size_t n)
     {
-        ExecutablePool *pool = new ExecutablePool(n);
-        if (!pool->m_freePtr) {
-            delete pool;
+        /* We can't (easily) use js_new() here because the constructor is private. */
+        void *memory = js_malloc(sizeof(ExecutablePool));
+        ExecutablePool *pool = memory ? new(memory) ExecutablePool(n) : NULL;
+        if (!pool || !pool->m_freePtr) {
+            js_delete(pool);
             return NULL;
         }
         return pool;
     }
 
     void* alloc(size_t n)
     {
         JS_ASSERT(m_freePtr <= m_end);
@@ -202,36 +204,38 @@ class ExecutableAllocator {
     // Initialization can fail so we use a create method instead.
     ExecutableAllocator() {}
 public:
     static size_t pageSize;
 
     // Returns NULL on OOM.
     static ExecutableAllocator *create()
     {
-        ExecutableAllocator *allocator = new ExecutableAllocator();
+        /* We can't (easily) use js_new() here because the constructor is private. */
+        void *memory = js_malloc(sizeof(ExecutableAllocator));
+        ExecutableAllocator *allocator = memory ? new(memory) ExecutableAllocator() : NULL;
         if (!allocator)
             return allocator;
 
         if (!pageSize)
             intializePageSize();
         ExecutablePool *pool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
         if (!pool) {
-            delete allocator;
+            js_delete(allocator);
             return NULL;
         }
         JS_ASSERT(allocator->m_smallAllocationPools.empty());
         allocator->m_smallAllocationPools.append(pool);
         return allocator;
     }
 
     ~ExecutableAllocator()
     {
         for (size_t i = 0; i < m_smallAllocationPools.length(); i++)
-            delete m_smallAllocationPools[i];
+            js_delete(m_smallAllocationPools[i]);
     }
 
     // poolForSize returns reference-counted objects. The caller owns a reference
     // to the object; i.e., poolForSize increments the count before returning the
     // object.
 
     ExecutablePool* poolForSize(size_t n)
     {
new file mode 100755
--- /dev/null
+++ b/js/src/config/find_vanilla_new_calls
@@ -0,0 +1,80 @@
+# /bin/bash
+
+#----------------------------------------------------------------------------
+# We must avoid using the vanilla new/new[] operators (and consequently, the
+# vanilla delete/delete[] operators) in SpiderMonkey, see bug 624878 for why.
+#
+# This script:
+# - Detects if any of the vanilla new/new[] operators are used in a file.
+#   Its exit code is 1 if it found some, and 0 if it didn't.
+# - Doesn't detect delete/delete[] because it appears they can be present
+#   somehow due to virtual destructors, but this is ok because vanilla
+#   delete/delete[] calls don't make sense without corresponding new/new[]
+#   calls, and any explicit calls will be caught by Valgrind's mismatched
+#   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().
+# - 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
+#  0000000f R_386_GOTPC       _GLOBAL_OFFSET_TABLE_
+#  0000001b R_386_PLT32       operator new(unsigned int)
+#  0000002e R_386_PC32        JSC::ExecutablePool::ExecutablePool(unsigned int)
+#  0000004a R_386_PC32        JSC::ExecutablePool::~ExecutablePool()
+#  00000052 R_386_PLT32       operator delete(void*)
+#
+#   This says that vanilla 'new' and 'delete' are both used in
+#   JSC::ExecutablePool::create(unsigned int).  This doesn't always work,
+#   though.  (Nb: use 'c++filt' to demangle names like
+#   _ZN3JSC14ExecutablePool6createEj.)
+#
+# If that doesn't work, use grep.
+#----------------------------------------------------------------------------
+
+if [ -z $1 ] ; then
+    echo "usage: find_vanilla_new_calls <file>"
+    exit 1
+fi
+
+file=$1
+
+if [ ! -f $file ] ; then
+    echo "TEST-UNEXPECTED-FAIL | find_vanilla_new_calls | file '$file' not found"
+    exit 1
+fi
+
+tmpfile1=`mktemp`
+tmpfile2=`mktemp`
+nm -C $file > $tmpfile1
+
+# Need to double-escape '[' and ']' to stop grep from interpreting them
+# specially.
+grep 'operator new(unsigned int)'        $tmpfile1 >> $tmpfile2
+grep 'operator new(unsigned long)'       $tmpfile1 >> $tmpfile2
+grep 'operator new\\[\\](unsigned int)'  $tmpfile1 >> $tmpfile2
+grep 'operator new\\[\\](unsigned long)' $tmpfile1 >> $tmpfile2
+rm -f $tmpfile1
+
+if [ -s $tmpfile2 ] ; then
+    echo "TEST-UNEXPECTED-FAIL | find_vanilla_new_calls | found calls are listed below"
+    cat $tmpfile2
+    echo
+    rm -f $tmpfile2
+    exit 1
+fi
+
+echo "TEST-PASS | find_vanilla_new_calls | ok"
+echo
+
+exit 0
--- 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 = new char[nbytes + 1];
+        *charBuffer = js_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 = new jschar[sourceLength + 1];
+        *jscharBuffer = js_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(new char[arraySize]);
+      AutoPtr<char>::Array intermediate(js_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(new char[structSize]);
+      AutoPtr<char>::Array intermediate(js_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))
-      delete static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
+      js_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);
-      delete static_cast<FieldInfoHash*>(info);
+      js_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));
-      delete[] ffiType->elements;
-      delete ffiType;
+      js_array_delete(ffiType->elements);
+      js_delete(ffiType);
     }
 
     break;
   }
   default:
     // Nothing to do here.
     break;
   }
@@ -3695,26 +3695,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(new ffi_type);
+  AutoPtr<ffi_type> ffiType(js_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 = new ffi_type*[length + 1];
+  ffiType->elements = js_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;
@@ -4027,17 +4027,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(new FieldInfoHash);
+  AutoPtr<FieldInfoHash> fields(js_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());
 
@@ -4136,26 +4136,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(new ffi_type);
+  AutoPtr<ffi_type> ffiType(js_new<ffi_type>());
   if (!ffiType) {
     JS_ReportOutOfMemory(cx);
     return NULL;
   }
   ffiType->type = FFI_TYPE_STRUCT;
 
   AutoPtr<ffi_type*>::Array elements;
   if (len != 0) {
-    elements = new ffi_type*[len + 1];
+    elements = js_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();
@@ -4164,17 +4164,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 = new ffi_type*[2];
+    elements = js_array_new<ffi_type*>(2);
     if (!elements) {
       JS_ReportOutOfMemory(cx);
       return NULL;
     }
     elements[0] = &ffi_type_uint8;
     elements[1] = NULL;
   }
 
@@ -4505,24 +4505,24 @@ StructType::AddressOfField(JSContext* cx
 
 // Helper class for handling allocation of function arguments.
 struct AutoValue
 {
   AutoValue() : mData(NULL) { }
 
   ~AutoValue()
   {
-    delete[] static_cast<char*>(mData);
+    js_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 = new char[size];
+    mData = js_array_new<char>(size);
     if (mData)
       memset(mData, 0, size);
     return mData != NULL;
   }
 
   void* mData;
 };
 
@@ -4717,17 +4717,17 @@ FunctionType::BuildSymbolName(JSContext*
 
 static FunctionInfo*
 NewFunctionInfo(JSContext* cx,
                 jsval abiType,
                 jsval returnType,
                 jsval* argTypes,
                 uintN argLength)
 {
-  AutoPtr<FunctionInfo> fninfo(new FunctionInfo());
+  AutoPtr<FunctionInfo> fninfo(js_new<FunctionInfo>());
   if (!fninfo) {
     JS_ReportOutOfMemory(cx);
     return NULL;
   }
 
   ffi_abi abi;
   if (!GetABI(cx, abiType, &abi)) {
     JS_ReportError(cx, "Invalid ABI specification");
@@ -5193,17 +5193,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(new ClosureInfo());
+  AutoPtr<ClosureInfo> cinfo(js_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);
@@ -5306,17 +5306,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);
 
-  delete cinfo;
+  js_delete(cinfo);
 }
 
 void
 CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
 {
   JS_ASSERT(cif);
   JS_ASSERT(result);
   JS_ASSERT(args);
@@ -5483,47 +5483,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 = new char*;
+  char** buffer = js_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 = new char[size];
+    data = js_array_new<char>(size);
     if (!data) {
       // Report a catchable allocation error.
       JS_ReportAllocationOverflow(cx);
-      delete buffer;
+      js_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)
-      delete[] data;
-    delete buffer;
+      js_array_delete(data);
+    js_delete(buffer);
     return NULL;
   }
 
   return dataObj;
 }
 
 void
 CData::Finalize(JSContext* cx, JSObject* obj)
@@ -5535,18 +5535,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)
-    delete[] *buffer;
-  delete buffer;
+    js_array_delete(*buffer);
+  js_delete(buffer);
 }
 
 JSObject*
 CData::GetCType(JSContext* cx, JSObject* dataObj)
 {
   JS_ASSERT(CData::IsCData(cx, dataObj));
 
   jsval slot;
@@ -5820,41 +5820,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 = new JSUint64(data);
+  JSUint64* buffer = js_new<JSUint64>(data);
   if (!buffer) {
     JS_ReportOutOfMemory(cx);
     return NULL;
   }
 
   if (!JS_SetReservedSlot(cx, result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer))) {
-    delete buffer;
+    js_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;
 
-  delete static_cast<JSUint64*>(JSVAL_TO_PRIVATE(slot));
+  js_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,35 +51,35 @@ namespace ctypes {
 /*******************************************************************************
 ** Utility classes
 *******************************************************************************/
 
 template<class T>
 class OperatorDelete
 {
 public:
-  static void destroy(T* ptr) { delete ptr; }
+  static void destroy(T* ptr) { js_delete(ptr); }
 };
 
 template<class T>
 class OperatorArrayDelete
 {
 public:
-  static void destroy(T* ptr) { delete[] ptr; }
+  static void destroy(T* ptr) { js_array_delete(ptr); }
 };
 
-// Class that takes ownership of a pointer T*, and calls operator delete or
-// operator delete[] upon destruction.
+// Class that takes ownership of a pointer T*, and calls js_delete() or
+// js_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 operator delete[] instead.
+  // An AutoPtr variant that calls js_array_delete() instead.
   typedef AutoPtr<T, OperatorArrayDelete<T> > Array;
 
   AutoPtr() : mPtr(NULL) { }
   explicit AutoPtr(T* ptr) : mPtr(ptr) { }
   ~AutoPtr() { DeleteTraits::destroy(mPtr); }
 
   T*   operator->()         { return mPtr; }
   bool operator!()          { return mPtr == NULL; }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -633,17 +633,17 @@ JSRuntime::init(uint32 maxbytes)
     propTreeStatFilename = getenv("JS_PROPTREE_STATFILE");
     propTreeDumpFilename = getenv("JS_PROPTREE_DUMPFILE");
     if (meterEmptyShapes()) {
         if (!emptyShapes.init())
             return false;
     }
 #endif
 
-    if (!(atomsCompartment = new JSCompartment(this)) ||
+    if (!(atomsCompartment = js_new<JSCompartment>(this)) ||
         !atomsCompartment->init() ||
         !compartments.append(atomsCompartment)) {
         return false;
     }
 
     if (!js_InitGC(this, maxbytes) || !js_InitAtomState(this))
         return false;
 
@@ -1156,21 +1156,21 @@ JS_SetWrapObjectCallbacks(JSRuntime *rt,
 }
 
 JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target)
 {
     CHECK_REQUEST(cx);
 
     JS_ASSERT(target);
-    AutoCompartment *call = new AutoCompartment(cx, target);
+    AutoCompartment *call = js_new<AutoCompartment>(cx, target);
     if (!call)
         return NULL;
     if (!call->enter()) {
-        delete call;
+        js_delete(call);
         return NULL;
     }
     return reinterpret_cast<JSCrossCompartmentCall *>(call);
 }
 
 JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target)
 {
@@ -1188,17 +1188,17 @@ JS_EnterCrossCompartmentCallScript(JSCon
 }
 
 JS_PUBLIC_API(void)
 JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call)
 {
     AutoCompartment *realcall = reinterpret_cast<AutoCompartment *>(call);
     CHECK_REQUEST(realcall->context);
     realcall->leave();
-    delete realcall;
+    js_delete(realcall);
 }
 
 bool
 JSAutoEnterCompartment::enter(JSContext *cx, JSObject *target)
 {
     JS_ASSERT(!call);
     if (cx->compartment == target->getCompartment()) {
         call = reinterpret_cast<JSCrossCompartmentCall*>(1);
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -71,27 +71,27 @@ JSCompartment::JSCompartment(JSRuntime *
     JS_INIT_CLIST(&scripts);
 
     PodArrayZero(scriptsToGC);
 }
 
 JSCompartment::~JSCompartment()
 {
 #if ENABLE_YARR_JIT
-    delete regExpAllocator;
+    js_delete(regExpAllocator);
 #endif
 
 #if defined JS_TRACER
     FinishJIT(&traceMonitor);
 #endif
 #ifdef JS_METHODJIT
-    delete jaegerCompartment;
+    js_delete(jaegerCompartment);
 #endif
 
-    delete mathCache;
+    js_delete(mathCache);
 
 #ifdef DEBUG
     for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
         JS_ASSERT(!scriptsToGC[i]);
 #endif
 }
 
 bool
@@ -116,17 +116,17 @@ JSCompartment::init()
 
 #if ENABLE_YARR_JIT
     regExpAllocator = JSC::ExecutableAllocator::create();
     if (!regExpAllocator)
         return false;
 #endif
 
 #ifdef JS_METHODJIT
-    if (!(jaegerCompartment = new mjit::JaegerCompartment)) {
+    if (!(jaegerCompartment = js_new<mjit::JaegerCompartment>())) {
 #ifdef JS_TRACER
         FinishJIT(&traceMonitor);
 #endif
         return false;
     }
     return jaegerCompartment->Initialize();
 #else
     return true;
@@ -487,13 +487,13 @@ JSCompartment::purge(JSContext *cx)
     }
 #endif
 }
 
 MathCache *
 JSCompartment::allocMathCache(JSContext *cx)
 {
     JS_ASSERT(!mathCache);
-    mathCache = new MathCache;
+    mathCache = js_new<MathCache>();
     if (!mathCache)
         js_ReportOutOfMemory(cx);
     return mathCache;
 }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -853,17 +853,17 @@ js_FinishGC(JSRuntime *rt)
     if (JS_WANT_GC_METER_PRINT)
         js_DumpGCStats(rt, stdout);
 #endif
 
     /* Delete all remaining Compartments. Ideally only the atomsCompartment should be left. */
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
         JSCompartment *comp = *c;
         comp->finishArenaLists();
-        delete comp;
+        js_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();
 
@@ -2202,17 +2202,17 @@ SweepCompartments(JSContext *cx, JSGCInv
             *write++ = compartment;
         } else {
             JS_ASSERT(compartment->freeLists.isEmpty());
             if (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT) {
                 if (callback)
                     (void) callback(cx, compartment, JSCOMPARTMENT_DESTROY);
                 if (compartment->principals)
                     JSPRINCIPALS_DROP(cx, compartment->principals);
-                delete compartment;
+                js_delete(compartment);
             } else {
                 compartment->marked = false;
                 *write++ = compartment;
             }
         }
     }
     rt->compartments.resize(write - rt->compartments.begin());
 }
@@ -2859,19 +2859,19 @@ SetProtoCheckingForCycles(JSContext *cx,
 
     return !cycle;
 }
 
 JSCompartment *
 NewCompartment(JSContext *cx, JSPrincipals *principals)
 {
     JSRuntime *rt = cx->runtime;
-    JSCompartment *compartment = new JSCompartment(rt);
+    JSCompartment *compartment = js_new<JSCompartment>(rt);
     if (!compartment || !compartment->init()) {
-        delete compartment;
+        js_delete(compartment);
         JS_ReportOutOfMemory(cx);
         return NULL;
     }
 
     if (principals) {
         compartment->principals = principals;
         JSPRINCIPALS_HOLD(cx, principals);
     }
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -997,21 +997,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 = new JSFunctionBox*[length()];
+        vector = js_array_new<JSFunctionBox*>(length());
         return !!vector;
     }
 
-    ~JSFunctionBoxQueue() { delete[] vector; }
+    ~JSFunctionBoxQueue() { js_array_delete(vector); }
 
     void push(JSFunctionBox *funbox) {
         if (!funbox->queued) {
             JS_ASSERT(count() < length());
             vector[head++ & lengthMask] = funbox;
             funbox->queued = true;
         }
     }
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2235,16 +2235,19 @@ TraceRecorder::TraceRecorder(JSContext* 
            : NULL),
     strictModeCode_ins(NULL),
     cfgMerges(&tempAlloc()),
     trashSelf(false),
     whichTreesToTrash(&tempAlloc()),
     guardedShapeTable(cx),
     initDepth(0),
     hadNewInit(false),
+#ifdef DEBUG
+    addPropShapeBefore(NULL),
+#endif
     rval_ins(NULL),
     native_rval_ins(NULL),
     newobj_ins(NULL),
     pendingSpecializedNative(NULL),
     pendingUnboxSlot(NULL),
     pendingGuardCondition(NULL),
     pendingGlobalSlotsToSet(cx),
     pendingLoop(true),
@@ -2434,22 +2437,24 @@ TraceRecorder::finishSuccessfully()
 {
     JS_ASSERT(!traceMonitor->profile);
     JS_ASSERT(traceMonitor->recorder == this);
     JS_ASSERT(fragment->lastIns && fragment->code());
 
     AUDIT(traceCompleted);
     mark.commit();
 
-    /* Grab local copies of members needed after |delete this|. */
+    /* Grab local copies of members needed after destruction of |this|. */
     JSContext* localcx = cx;
     TraceMonitor* localtm = traceMonitor;
 
     localtm->recorder = NULL;
-    delete this;
+    /* We can't (easily) use js_delete() here because the constructor is private. */
+    this->~TraceRecorder();
+    js_free(this);
 
     /* Catch OOM that occurred during recording. */
     if (localtm->outOfMemory() || OverfullJITCache(localcx, localtm)) {
         ResetJIT(localcx, FR_OOM);
         return ARECORD_ABORTED;
     }
     return ARECORD_COMPLETED;
 }
@@ -2487,22 +2492,26 @@ TraceRecorder::finishAbort(const char* r
      */
     if (fragment->root == fragment) {
         TrashTree(fragment->toTreeFragment());
     } else {
         JS_ASSERT(numSideExitsBefore <= fragment->root->sideExits.length());
         fragment->root->sideExits.setLength(numSideExitsBefore);
     }
 
-    /* Grab local copies of members needed after |delete this|. */
+    /* Grab local copies of members needed after destruction of |this|. */
     JSContext* localcx = cx;
     TraceMonitor* localtm = traceMonitor;
 
     localtm->recorder = NULL;
-    delete this;
+    /* We can't (easily) use js_delete() here because the constructor is private. */
+    this->~TraceRecorder();
+    js_free(this);
+
+    /* Catch OOM that occurred during recording. */
     if (localtm->outOfMemory() || OverfullJITCache(localcx, localtm)) {
         ResetJIT(localcx, FR_OOM);
         return JIT_RESET;
     }
     return NORMAL_ABORT;
 }
 
 inline LIns*
@@ -5559,19 +5568,23 @@ TraceRecorder::startRecorder(JSContext* 
                              JSScript* outerScript, jsbytecode* outerPC, uint32 outerArgc,
                              bool speculate)
 {
     TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
     JS_ASSERT(!tm->profile);
     JS_ASSERT(!tm->needFlush);
     JS_ASSERT_IF(cx->fp()->hasImacropc(), f->root != f);
 
-    tm->recorder = new TraceRecorder(cx, anchor, f, stackSlots, ngslots, typeMap,
-                                     expectedInnerExit, outerScript, outerPC, outerArgc,
-                                     speculate);
+    /* 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, anchor, f, stackSlots, ngslots, typeMap,
+                                             expectedInnerExit, outerScript, outerPC, outerArgc,
+                                             speculate)
+                 : NULL;
 
     if (!tm->recorder || tm->outOfMemory() || OverfullJITCache(cx, tm)) {
         ResetJIT(cx, FR_OOM);
         return false;
     }
 
     return true;
 }
@@ -7614,17 +7627,18 @@ InitJIT(TraceMonitor *tm)
     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 = new VMAllocator();
+        tm->profAlloc = js_new<VMAllocator>();
+        JS_ASSERT(tm->profAlloc);
         tm->profTab = new (*tm->profAlloc) FragStatsMap(*tm->profAlloc);
     }
     tm->lastFragID = 0;
 #else
     PodZero(&LogController);
 #endif
 
     if (!did_we_check_processor_features) {
@@ -7648,37 +7662,40 @@ 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;
     }
 
-    tm->oracle = new Oracle();
+    #define CHECK_ALLOC(lhs, rhs) \
+        do { lhs = (rhs); if (!lhs) return false; } while (0)
+
+    CHECK_ALLOC(tm->oracle, js_new<Oracle>());
 
     tm->profile = NULL;
     
-    tm->recordAttempts = new RecordAttemptMap;
+    CHECK_ALLOC(tm->recordAttempts, js_new<RecordAttemptMap>());
     if (!tm->recordAttempts->init(PC_HASH_COUNT))
-        abort();
-
-    tm->loopProfiles = new LoopProfileMap;
+        return false;
+
+    CHECK_ALLOC(tm->loopProfiles, js_new<LoopProfileMap>());
     if (!tm->loopProfiles->init(PC_HASH_COUNT))
-        abort();
+        return false;
 
     tm->flushEpoch = 0;
     
-    tm->dataAlloc = new VMAllocator();
-    tm->traceAlloc = new VMAllocator();
-    tm->tempAlloc = new VMAllocator();
-    tm->codeAlloc = new CodeAlloc();
-    tm->frameCache = new FrameInfoCache(tm->dataAlloc);
-    tm->storage = new TraceNativeStorage();
-    tm->cachedTempTypeMap = new TypeMap(0);
+    CHECK_ALLOC(tm->dataAlloc, js_new<VMAllocator>());
+    CHECK_ALLOC(tm->traceAlloc, js_new<VMAllocator>());
+    CHECK_ALLOC(tm->tempAlloc, js_new<VMAllocator>());
+    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->flush();
     verbose_only( tm->branches = NULL; )
 
 #if !defined XP_WIN
     debug_only(PodZero(&jitstats));
 #endif
 
 #ifdef JS_JIT_SPEW
@@ -7742,19 +7759,19 @@ FinishJIT(TraceMonitor *tm)
 #define JITSTAT(x) /* nothing */
 #include "jitstats.tbl"
 #undef JITSTAT
 #undef MONITOR_JITSTAT
         debug_only_print0(LC_TMStats, "\n");
     }
 #endif
 
-    delete tm->recordAttempts;
-    delete tm->loopProfiles;
-    delete tm->oracle;
+    js_delete(tm->recordAttempts);
+    js_delete(tm->loopProfiles);
+    js_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);
         }
@@ -7767,57 +7784,45 @@ FinishJIT(TraceMonitor *tm)
         }
         REHashMap::Iter iter(*(tm->reFragments));
         while (iter.next()) {
             VMFragment* frag = (VMFragment*)iter.value();
             FragProfiling_FragFinalizer(frag, tm);
         }
 
         FragProfiling_showResults(tm);
-        delete tm->profAlloc;
+        js_delete(tm->profAlloc);
 
     } else {
         NanoAssert(!tm->profTab);
         NanoAssert(!tm->profAlloc);
     }
 #endif
 
     PodArrayZero(tm->vmfragments);
 
-    if (tm->frameCache) {
-        delete tm->frameCache;
-        tm->frameCache = NULL;
-    }
-
-    if (tm->codeAlloc) {
-        delete tm->codeAlloc;
-        tm->codeAlloc = NULL;
-    }
-
-    if (tm->dataAlloc) {
-        delete tm->dataAlloc;
-        tm->dataAlloc = NULL;
-    }
-
-    if (tm->traceAlloc) {
-        delete tm->traceAlloc;
-        tm->traceAlloc = NULL;
-    }
-
-    if (tm->tempAlloc) {
-        delete tm->tempAlloc;
-        tm->tempAlloc = NULL;
-    }
-
-    if (tm->storage) {
-        delete tm->storage;
-        tm->storage = NULL;
-    }
-
-    delete tm->cachedTempTypeMap;
+    js_delete(tm->frameCache);
+    tm->frameCache = NULL;
+
+    js_delete(tm->codeAlloc);
+    tm->codeAlloc = NULL;
+
+    js_delete(tm->dataAlloc);
+    tm->dataAlloc = NULL;
+
+    js_delete(tm->traceAlloc);
+    tm->traceAlloc = NULL;
+
+    js_delete(tm->tempAlloc);
+    tm->tempAlloc = NULL;
+
+    js_delete(tm->storage);
+    tm->storage = NULL;
+
+    js_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
@@ -1570,19 +1570,16 @@ class TraceRecorder
     VMAllocator& dataAlloc() const { return *traceMonitor->dataAlloc; }
 
     /* Member declarations for each opcode, to be called before interpreting the opcode. */
 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)               \
     JS_REQUIRES_STACK AbortableRecordingStatus record_##op();
 # include "jsopcode.tbl"
 #undef OPDEF
 
-    inline void* operator new(size_t size) { return js_calloc(size); }
-    inline void operator delete(void *p) { js_free(p); }
-
     JS_REQUIRES_STACK
     TraceRecorder(JSContext* cx, VMSideExit*, VMFragment*,
                   unsigned stackSlots, unsigned ngslots, JSValueType* typeMap,
                   VMSideExit* expectedInnerExit, JSScript* outerScript, jsbytecode* outerPC,
                   uint32 outerArgc, bool speculate);
 
     /* The destructor should only be called through finish*, not directly. */
     ~TraceRecorder();
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -221,16 +221,103 @@ 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().
+ *
+ * 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.
+ *
+ * (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.)
+ */
+
+#define JS_NEW_BODY(t, parms)                                                 \
+    void *memory = js_malloc(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
+
+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;
+    }
+    void *memory = js_malloc(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_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);
+    }
+}
+
 /**
  * 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);
  * but is is easy to accidentally write:
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -707,17 +707,17 @@ JaegerCompartment::Initialize()
 
     return true;
 }
 
 void
 JaegerCompartment::Finish()
 {
     TrampolineCompiler::release(&trampolines);
-    delete execAlloc;
+    js_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())
-            delete multiplePools();
+            js_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 = new ExecPoolVector(SystemAllocPolicy()); 
+            ExecPoolVector *execPools = js_new<ExecPoolVector>(SystemAllocPolicy()); 
             if (!execPools)
                 return false;
             if (!execPools->append(oldPool) || !execPools->append(pool)) {
-                delete execPools;
+                js_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 = new PerfMeasurement(PerfMeasurement::EventMask(mask));
+    PerfMeasurement* p = js_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)
 {
-    delete (PerfMeasurement*) JS_GetPrivate(cx, obj);
+    js_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
@@ -40,16 +40,17 @@
 #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>.
  */
 
 #include <linux/perf_event.h>
+#include <new>
 #include <sys/syscall.h>
 #include <sys/ioctl.h>
 #include <errno.h>
 #include <unistd.h>
 #include <string.h>
 
 // As of July 2010, this system call has not been added to the
 // C library, so we have to provide our own wrapper function.
@@ -259,17 +260,17 @@ Impl::stop(PerfMeasurement* counters)
 } // anonymous namespace
 
 
 namespace JS {
 
 #define initCtr(flag) ((eventsMeasured & flag) ? 0 : -1)
 
 PerfMeasurement::PerfMeasurement(PerfMeasurement::EventMask toMeasure)
-  : impl(new Impl),
+  : impl(js_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)),
@@ -280,17 +281,17 @@ PerfMeasurement::PerfMeasurement(PerfMea
     cpu_migrations(initCtr(CPU_MIGRATIONS))
 {
 }
 
 #undef initCtr
 
 PerfMeasurement::~PerfMeasurement()
 {
-    delete static_cast<Impl*>(impl);
+    js_delete(static_cast<Impl*>(impl));
 }
 
 void
 PerfMeasurement::start()
 {
     if (impl)
         static_cast<Impl*>(impl)->start();
 }
--- a/js/src/yarr/jswtfbridge.h
+++ b/js/src/yarr/jswtfbridge.h
@@ -47,29 +47,15 @@
 #include "assembler/wtf/Platform.h"
 #include "jsstr.h"
 #include "jsprvtd.h"
 #include "jstl.h"
 
 typedef jschar UChar;
 typedef JSLinearString UString;
 
-template <typename T>
-class ValueDeleter
-{
-  public:
-    void operator()(T &t) { delete t; }
-};
-
-template<typename T, size_t N, class AP>
-static inline void
-deleteAllValues(js::Vector<T,N,AP> &vector)
-{
-    js::ForEach(vector.begin(), vector.end(), ValueDeleter<T>());
-}
-
 class Unicode {
   public:
     static UChar toUpper(UChar c) { return JS_TOUPPER(c); }
     static UChar toLower(UChar c) { return JS_TOLOWER(c); }
 };
 
 #endif
--- a/js/src/yarr/pcre/pcre_compile.cpp
+++ b/js/src/yarr/pcre/pcre_compile.cpp
@@ -2583,17 +2583,18 @@ 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);
-    JSRegExp* re = reinterpret_cast<JSRegExp*>(new char[size]);
+    // FIXME: bug 574459 -- no NULL check
+    JSRegExp* re = reinterpret_cast<JSRegExp*>(js_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. */
@@ -2639,17 +2640,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) {
-        delete [] reinterpret_cast<char*>(re);
+        js_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
@@ -2694,10 +2695,10 @@ JSRegExp* jsRegExpCompile(const UChar* p
     if (numSubpatterns)
         *numSubpatterns = re->topBracket;
 
     return re;
 }
 
 void jsRegExpFree(JSRegExp* re)
 {
-    delete [] reinterpret_cast<char*>(re);
+    js_array_delete(reinterpret_cast<char*>(re));
 }
--- a/js/src/yarr/pcre/pcre_exec.cpp
+++ b/js/src/yarr/pcre/pcre_exec.cpp
@@ -383,17 +383,18 @@ struct MatchStack {
     
     bool canUseStackBufferForNextFrame() {
         return size < numFramesOnStack;
     }
     
     MatchFrame* allocateNextFrame() {
         if (canUseStackBufferForNextFrame())
             return currentFrame + 1;
-        MatchFrame *frame = new MatchFrame;
+        // FIXME: bug 574459 -- no NULL check
+        MatchFrame *frame = js_new<MatchFrame>();
         frame->init(regExpPool);
         return frame;
     }
     
     void pushNewFrame(const unsigned char* instructionPtr, BracketChainNode* bracketChain, ReturnLocation returnLocation, bool groupMatched) {
         MatchFrame* newframe = allocateNextFrame();
         newframe->previousFrame = currentFrame;
 
@@ -407,17 +408,17 @@ struct MatchStack {
 
         currentFrame = newframe;
     }
     
     void popCurrentFrame() {
         MatchFrame* oldFrame = currentFrame;
         currentFrame = currentFrame->previousFrame;
         if (size > numFramesOnStack)
-            delete oldFrame;
+            js_delete(oldFrame);
         size--;
     }
 
     void popAllFrames() {
         while (size)
             popCurrentFrame();
     }
 };
--- a/js/src/yarr/yarr/RegExpJitTables.h
+++ b/js/src/yarr/yarr/RegExpJitTables.h
@@ -2624,60 +2624,65 @@ 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,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()
 {
-    CharacterClass* characterClass = new CharacterClass(0);
+    // FIXME: bug 574459 -- no NULL check
+    CharacterClass* characterClass = js_new<CharacterClass>((CharacterClassTable*)NULL);
     characterClass->m_ranges.append(CharacterRange(0x30, 0x39));
     return characterClass;
 }
 
 CharacterClass* nondigitsCreate()
 {
-    CharacterClass* characterClass = new CharacterClass(0);
+    // FIXME: bug 574459 -- no NULL check
+    CharacterClass* characterClass = js_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()
 {
-    CharacterClass* characterClass = new CharacterClass(0);
+    // FIXME: bug 574459 -- no NULL check
+    CharacterClass* characterClass = js_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()
 {
-    CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_spacesData, false));
+    // FIXME: bug 574459 -- no NULL check
+    CharacterClass* characterClass = js_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);
     characterClass->m_matchesUnicode.append(0x202f);
     characterClass->m_matchesUnicode.append(0x205f);
     characterClass->m_matchesUnicode.append(0x3000);
     return characterClass;
 }
 
 CharacterClass* nonspacesCreate()
 {
-    CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_spacesData, true));
+    // FIXME: bug 574459 -- no NULL check
+    CharacterClass* characterClass = js_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));
@@ -2685,29 +2690,31 @@ CharacterClass* nonspacesCreate()
     characterClass->m_rangesUnicode.append(CharacterRange(0x2030, 0x205e));
     characterClass->m_rangesUnicode.append(CharacterRange(0x2060, 0x2fff));
     characterClass->m_rangesUnicode.append(CharacterRange(0x3001, 0xffff));
     return characterClass;
 }
 
 CharacterClass* nonwordcharCreate()
 {
-    CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_wordcharData, true));
+    // FIXME: bug 574459 -- no NULL check
+    CharacterClass* characterClass = js_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()
 {
-    CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_wordcharData, false));
+    // FIXME: bug 574459 -- no NULL check
+    CharacterClass* characterClass = js_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
@@ -134,17 +134,18 @@ public:
                         ++unicodeCurr;
                 }
             }
         }
     }
 
     CharacterClass* charClass()
     {
-        CharacterClass* characterClass = new CharacterClass(0);
+        // FIXME: bug 574459 -- no NULL check
+        CharacterClass* characterClass = js_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();
 
@@ -339,25 +340,27 @@ public:
     }
 
     void atomParenthesesSubpatternBegin(bool capture = true)
     {
         unsigned subpatternId = m_pattern.m_numSubpatterns + 1;
         if (capture)
             m_pattern.m_numSubpatterns++;
 
-        PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative);
+        // FIXME: bug 574459 -- no NULL check
+        PatternDisjunction* parenthesesDisjunction = js_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)
     {
-        PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative);
+        // FIXME: bug 574459 -- no NULL check
+        PatternDisjunction* parenthesesDisjunction = js_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);
@@ -392,17 +395,18 @@ public:
             }
         }
 
         m_alternative->m_terms.append(PatternTerm(subpatternId));
     }
 
     PatternDisjunction* copyDisjunction(PatternDisjunction* disjunction)
     {
-        PatternDisjunction* newDisjunction = new PatternDisjunction();
+        // FIXME: bug 574459 -- no NULL check
+        PatternDisjunction* newDisjunction = js_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]));
         }
@@ -462,17 +466,18 @@ public:
 
     void disjunction()
     {
         m_alternative = m_alternative->m_parent->addNewAlternative();
     }
 
     void regexBegin()
     {
-        m_pattern.m_body = new PatternDisjunction();
+        // FIXME: bug 574459 -- no NULL check
+        m_pattern.m_body = js_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
@@ -61,21 +61,25 @@ struct CharacterRange {
 struct CharacterClassTable {
     const char* m_table;
     bool m_inverted;
     jsrefcount m_refcount;
 
     /* Ownership transferred to caller. */
     static CharacterClassTable *create(const char* table, bool inverted)
     {
-        return new CharacterClassTable(table, 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;
     }
 
     void incref() { JS_ATOMIC_INCREMENT(&m_refcount); }
-    void decref() { if (JS_ATOMIC_DECREMENT(&m_refcount) == 0) delete this; }
+    void decref() { if (JS_ATOMIC_DECREMENT(&m_refcount) == 0) js_delete(this); }
 
 private:
     CharacterClassTable(const char* table, bool inverted)
         : m_table(table)
         , m_inverted(inverted)
         , m_refcount(0)
     {
     }
@@ -258,31 +262,40 @@ struct PatternAlternative {
     PatternDisjunction* m_parent;
     unsigned m_minimumSize;
     bool m_onceThrough : 1;
     bool m_hasFixedSize : 1;
     bool m_startsWithBOL : 1;
     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);
+}
+
 struct PatternDisjunction {
     PatternDisjunction(PatternAlternative* parent = 0)
         : m_parent(parent)
         , m_hasFixedSize(false)
     {
     }
     
     ~PatternDisjunction()
     {
         deleteAllValues(m_alternatives);
     }
 
     PatternAlternative* addNewAlternative()
     {
-        PatternAlternative* alternative = new PatternAlternative(this);
+        // FIXME: bug 574459 -- no NULL check
+        PatternAlternative* alternative = js_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;