Bug 624878 - Remove dangerous uses of vanilla (throw-on-failure) |operator new|. r=lw.

This commit is contained in:
Nicholas Nethercote 2011-01-17 19:44:10 -08:00
parent cc51b5ed72
commit ed55695802
23 changed files with 426 additions and 151 deletions

80
config/find_vanilla_new_calls Executable file
View File

@ -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

View File

@ -572,6 +572,14 @@ check-valgrind::
$(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::

View File

@ -113,7 +113,7 @@ private:
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;
@ -132,14 +132,16 @@ public:
{
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;
@ -207,7 +209,9 @@ public:
// 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;
@ -215,7 +219,7 @@ public:
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());
@ -226,7 +230,7 @@ public:
~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

View File

@ -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

View File

@ -1844,7 +1844,7 @@ ImplicitConvert(JSContext* cx,
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;
@ -1861,7 +1861,7 @@ ImplicitConvert(JSContext* cx,
// 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;
@ -1946,7 +1946,7 @@ ImplicitConvert(JSContext* cx,
// 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;
@ -1983,7 +1983,7 @@ ImplicitConvert(JSContext* cx,
// 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;
@ -2702,7 +2702,7 @@ CType::Finalize(JSContext* cx, JSObject* obj)
// 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;
}
@ -2711,7 +2711,7 @@ CType::Finalize(JSContext* cx, JSObject* obj)
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));
}
}
@ -2721,8 +2721,8 @@ CType::Finalize(JSContext* cx, JSObject* obj)
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;
@ -3700,7 +3700,7 @@ ArrayType::BuildFFIType(JSContext* cx, JSObject* obj)
// 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;
@ -3709,7 +3709,7 @@ ArrayType::BuildFFIType(JSContext* cx, JSObject* obj)
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;
@ -4032,7 +4032,7 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj
// 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);
@ -4141,7 +4141,7 @@ StructType::BuildFFIType(JSContext* cx, JSObject* obj)
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;
@ -4150,7 +4150,7 @@ StructType::BuildFFIType(JSContext* cx, JSObject* obj)
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;
@ -4169,7 +4169,7 @@ StructType::BuildFFIType(JSContext* cx, JSObject* obj)
// 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;
@ -4510,14 +4510,14 @@ struct AutoValue
~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;
@ -4722,7 +4722,7 @@ NewFunctionInfo(JSContext* cx,
jsval* argTypes,
uintN argLength)
{
AutoPtr<FunctionInfo> fninfo(new FunctionInfo());
AutoPtr<FunctionInfo> fninfo(js_new<FunctionInfo>());
if (!fninfo) {
JS_ReportOutOfMemory(cx);
return NULL;
@ -5198,7 +5198,7 @@ CClosure::Create(JSContext* cx,
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;
@ -5311,7 +5311,7 @@ CClosure::Finalize(JSContext* cx, JSObject* obj)
if (cinfo->closure)
ffi_closure_free(cinfo->closure);
delete cinfo;
js_delete(cinfo);
}
void
@ -5488,7 +5488,7 @@ CData::Create(JSContext* cx,
// 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;
@ -5500,11 +5500,11 @@ CData::Create(JSContext* cx,
} 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;
}
@ -5517,8 +5517,8 @@ CData::Create(JSContext* cx,
*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;
}
@ -5540,8 +5540,8 @@ CData::Finalize(JSContext* cx, JSObject* obj)
char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot));
if (owns)
delete[] *buffer;
delete buffer;
js_array_delete(*buffer);
js_delete(buffer);
}
JSObject*
@ -5825,14 +5825,14 @@ Int64Base::Construct(JSContext* cx,
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;
}
@ -5849,7 +5849,7 @@ Int64Base::Finalize(JSContext* cx, JSObject* obj)
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

View File

@ -56,25 +56,25 @@ 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) { }

View File

@ -638,7 +638,7 @@ JSRuntime::init(uint32 maxbytes)
}
#endif
if (!(atomsCompartment = new JSCompartment(this)) ||
if (!(atomsCompartment = js_new<JSCompartment>(this)) ||
!atomsCompartment->init() ||
!compartments.append(atomsCompartment)) {
return false;
@ -1161,11 +1161,11 @@ 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);
@ -1193,7 +1193,7 @@ JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call)
AutoCompartment *realcall = reinterpret_cast<AutoCompartment *>(call);
CHECK_REQUEST(realcall->context);
realcall->leave();
delete realcall;
js_delete(realcall);
}
bool

View File

@ -76,17 +76,17 @@ JSCompartment::JSCompartment(JSRuntime *rt)
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)
@ -121,7 +121,7 @@ JSCompartment::init()
#endif
#ifdef JS_METHODJIT
if (!(jaegerCompartment = new mjit::JaegerCompartment)) {
if (!(jaegerCompartment = js_new<mjit::JaegerCompartment>())) {
#ifdef JS_TRACER
FinishJIT(&traceMonitor);
#endif
@ -492,7 +492,7 @@ MathCache *
JSCompartment::allocMathCache(JSContext *cx)
{
JS_ASSERT(!mathCache);
mathCache = new MathCache;
mathCache = js_new<MathCache>();
if (!mathCache)
js_ReportOutOfMemory(cx);
return mathCache;

View File

@ -858,7 +858,7 @@ js_FinishGC(JSRuntime *rt)
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;
@ -2207,7 +2207,7 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind)
(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;
@ -2864,9 +2864,9 @@ 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;
}

View File

@ -1002,11 +1002,11 @@ struct JSFunctionBoxQueue {
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) {

View File

@ -2240,6 +2240,9 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag
guardedShapeTable(cx),
initDepth(0),
hadNewInit(false),
#ifdef DEBUG
addPropShapeBefore(NULL),
#endif
rval_ins(NULL),
native_rval_ins(NULL),
newobj_ins(NULL),
@ -2439,12 +2442,14 @@ TraceRecorder::finishSuccessfully()
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)) {
@ -2492,12 +2497,16 @@ TraceRecorder::finishAbort(const char* reason)
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;
@ -5564,9 +5573,13 @@ TraceRecorder::startRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* f,
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);
@ -7619,7 +7632,8 @@ InitJIT(TraceMonitor *tm)
}
/* 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;
@ -7653,27 +7667,30 @@ InitJIT(TraceMonitor *tm)
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();
return false;
tm->loopProfiles = new LoopProfileMap;
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; )
@ -7747,9 +7764,9 @@ FinishJIT(TraceMonitor *tm)
}
#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
@ -7772,7 +7789,7 @@ FinishJIT(TraceMonitor *tm)
}
FragProfiling_showResults(tm);
delete tm->profAlloc;
js_delete(tm->profAlloc);
} else {
NanoAssert(!tm->profTab);
@ -7782,37 +7799,25 @@ FinishJIT(TraceMonitor *tm)
PodArrayZero(tm->vmfragments);
if (tm->frameCache) {
delete tm->frameCache;
tm->frameCache = NULL;
}
js_delete(tm->frameCache);
tm->frameCache = NULL;
if (tm->codeAlloc) {
delete tm->codeAlloc;
tm->codeAlloc = NULL;
}
js_delete(tm->codeAlloc);
tm->codeAlloc = NULL;
if (tm->dataAlloc) {
delete tm->dataAlloc;
tm->dataAlloc = NULL;
}
js_delete(tm->dataAlloc);
tm->dataAlloc = NULL;
if (tm->traceAlloc) {
delete tm->traceAlloc;
tm->traceAlloc = NULL;
}
js_delete(tm->traceAlloc);
tm->traceAlloc = NULL;
if (tm->tempAlloc) {
delete tm->tempAlloc;
tm->tempAlloc = NULL;
}
js_delete(tm->tempAlloc);
tm->tempAlloc = NULL;
if (tm->storage) {
delete tm->storage;
tm->storage = NULL;
}
js_delete(tm->storage);
tm->storage = NULL;
delete tm->cachedTempTypeMap;
js_delete(tm->cachedTempTypeMap);
tm->cachedTempTypeMap = NULL;
}

View File

@ -1575,9 +1575,6 @@ class TraceRecorder
# 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,

View File

@ -226,6 +226,93 @@ 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,

View File

@ -712,7 +712,7 @@ 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) \

View File

@ -157,7 +157,7 @@ class BasePolyIC : public BaseIC {
~BasePolyIC() {
releasePools();
if (areMultiplePools())
delete multiplePools();
js_delete(multiplePools());
}
void reset() {
@ -192,11 +192,11 @@ class BasePolyIC : public BaseIC {
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);

View File

@ -63,7 +63,7 @@ pm_construct(JSContext* cx, uintN argc, jsval* vp)
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;
@ -77,7 +77,7 @@ pm_construct(JSContext* cx, uintN argc, jsval* vp)
static void
pm_finalize(JSContext* cx, JSObject* obj)
{
delete (PerfMeasurement*) JS_GetPrivate(cx, obj);
js_delete((PerfMeasurement*) JS_GetPrivate(cx, obj));
}
// Property access

View File

@ -45,6 +45,7 @@
*/
#include <linux/perf_event.h>
#include <new>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <errno.h>
@ -264,7 +265,7 @@ 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)),
@ -285,7 +286,7 @@ PerfMeasurement::PerfMeasurement(PerfMeasurement::EventMask toMeasure)
PerfMeasurement::~PerfMeasurement()
{
delete static_cast<Impl*>(impl);
js_delete(static_cast<Impl*>(impl));
}
void

View File

@ -52,20 +52,6 @@
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); }

View File

@ -2588,7 +2588,8 @@ JSRegExp* jsRegExpCompile(const UChar* pattern, int patternLength,
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);
@ -2644,7 +2645,7 @@ JSRegExp* jsRegExpCompile(const UChar* pattern, int patternLength,
/* 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);
}
@ -2699,5 +2700,5 @@ JSRegExp* jsRegExpCompile(const UChar* pattern, int patternLength,
void jsRegExpFree(JSRegExp* re)
{
delete [] reinterpret_cast<char*>(re);
js_array_delete(reinterpret_cast<char*>(re));
}

View File

@ -388,7 +388,8 @@ struct MatchStack {
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;
}
@ -412,7 +413,7 @@ struct MatchStack {
MatchFrame* oldFrame = currentFrame;
currentFrame = currentFrame->previousFrame;
if (size > numFramesOnStack)
delete oldFrame;
js_delete(oldFrame);
size--;
}

View File

@ -2629,14 +2629,16 @@ static const char _wordcharData[65536] = {
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));
@ -2645,7 +2647,8 @@ CharacterClass* nondigitsCreate()
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);
@ -2655,7 +2658,8 @@ CharacterClass* newlineCreate()
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);
@ -2672,7 +2676,8 @@ CharacterClass* spacesCreate()
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));
@ -2690,7 +2695,8 @@ CharacterClass* nonspacesCreate()
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));
@ -2702,7 +2708,8 @@ CharacterClass* nonwordcharCreate()
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);

View File

@ -139,7 +139,8 @@ public:
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);
@ -344,7 +345,8 @@ public:
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();
@ -352,7 +354,8 @@ public:
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();
@ -397,7 +400,8 @@ public:
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) {
@ -467,7 +471,8 @@ public:
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);
}

View File

@ -66,11 +66,15 @@ struct CharacterClassTable {
/* 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)
@ -263,6 +267,14 @@ 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);
}
struct PatternDisjunction {
PatternDisjunction(PatternAlternative* parent = 0)
: m_parent(parent)
@ -277,7 +289,8 @@ struct PatternDisjunction {
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;
}