gecko/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp

137 lines
3.5 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99:
*/
#include "tests.h"
#include "jsstr.h"
static JSGCCallback oldGCCallback;
static void **checkPointers;
static jsuint checkPointersLength;
static size_t checkPointersStaticStrings;
static JSBool
TestAboutToBeFinalizedCallback(JSContext *cx, JSGCStatus status)
{
if (status == JSGC_MARK_END && checkPointers) {
for (jsuint i = 0; i != checkPointersLength; ++i) {
void *p = checkPointers[i];
JS_ASSERT(p);
if (JS_IsAboutToBeFinalized(cx, p))
checkPointers[i] = NULL;
}
}
return !oldGCCallback || oldGCCallback(cx, status);
}
/*
* NativeFrameCleaner write to sink so a compiler nwould not be able to optimze out
* the buffer memset there.
*/
volatile void *ptrSink;
static JS_NEVER_INLINE void
NativeFrameCleaner()
{
char buffer[1 << 16];
memset(buffer, 0, sizeof buffer);
ptrSink = buffer;
}
BEGIN_TEST(testIsAboutToBeFinalized_bug528645)
{
/*
* Due to the conservative GC we use separated never-inline function to
* test rooted elements.
*/
CHECK(createAndTestRooted());
NativeFrameCleaner();
JS_GC(cx);
/* Everything is unrooted except unit strings. */
for (jsuint i = 0; i != checkPointersLength; ++i) {
void *p = checkPointers[i];
if (p) {
CHECK(JSString::isStatic(p));
CHECK(checkPointersStaticStrings != 0);
--checkPointersStaticStrings;
}
}
CHECK(checkPointersStaticStrings == 0);
free(checkPointers);
checkPointers = NULL;
JS_SetGCCallback(cx, oldGCCallback);
return true;
}
JS_NEVER_INLINE bool createAndTestRooted();
END_TEST(testIsAboutToBeFinalized_bug528645)
JS_NEVER_INLINE bool
cls_testIsAboutToBeFinalized_bug528645::createAndTestRooted()
{
jsvalRoot root(cx);
/*
* Check various types of GC things against JS_IsAboutToBeFinalized.
* Make sure to include unit and numeric strings to the set.
*/
EVAL("var x = 1.1; "
"[''+x, 'a', '123456789', 'something'.substring(1), "
"{}, [], new Function('return 10;'), <xml/>];",
root.addr());
JSObject *array = JSVAL_TO_OBJECT(root.value());
JS_ASSERT(JS_IsArrayObject(cx, array));
JSBool ok = JS_GetArrayLength(cx, array, &checkPointersLength);
CHECK(ok);
checkPointers = (void **) malloc(sizeof(void *) * checkPointersLength);
CHECK(checkPointers);
checkPointersStaticStrings = 0;
for (jsuint i = 0; i != checkPointersLength; ++i) {
jsval v;
ok = JS_GetElement(cx, array, i, &v);
CHECK(ok);
JS_ASSERT(JSVAL_IS_GCTHING(v));
JS_ASSERT(!JSVAL_IS_NULL(v));
checkPointers[i] = JSVAL_TO_GCTHING(v);
if (JSString::isStatic(checkPointers[i]))
++checkPointersStaticStrings;
}
oldGCCallback = JS_SetGCCallback(cx, TestAboutToBeFinalizedCallback);
JS_GC(cx);
/*
* All GC things are rooted via the root holding the array containing them
* and TestAboutToBeFinalizedCallback must keep them as is.
*/
for (jsuint i = 0; i != checkPointersLength; ++i)
CHECK(checkPointers[i]);
/*
* Overwrite the registers and stack with new GC things to avoid false
* positives with the finalization test.
*/
EVAL("[]", root.addr());
array = JSVAL_TO_OBJECT(root.value());
JS_ASSERT(JS_IsArrayObject(cx, array));
jsuint tmp;
CHECK(JS_GetArrayLength(cx, array, &tmp));
CHECK(ok);
return true;
}