bug 724810 - storing finalizer in external strings directly. r=luke

--HG--
extra : rebase_source : d3222c685190bbdbe9446628f2f73f2a6f81eb11
This commit is contained in:
Igor Bukanov 2012-02-07 19:44:54 +01:00
parent fe418cb25f
commit 146c8750f5
10 changed files with 68 additions and 231 deletions

View File

@ -1,37 +1,39 @@
#include "tests.h"
#include "jsutil.h"
const jschar arr[] = { 'h', 'i', ',', 'd', 'o', 'n', '\'', 't', ' ', 'd', 'e', 'l', 'e', 't', 'e', ' ', 'm', 'e', '\0' };
size_t arrlen = sizeof(arr) / sizeof(arr[0]) - 1;
void *magic = (void *)0x42;
static const jschar arr[] = {
'h', 'i', ',', 'd', 'o', 'n', '\'', 't', ' ', 'd', 'e', 'l', 'e', 't', 'e', ' ', 'm', 'e', '\0'
};
static const size_t arrlen = sizeof(arr) / sizeof(arr[0]) - 1;
int finalized_noclosure = 0;
int finalized_closure = 0;
static int finalized1 = 0;
static int finalized2 = 0;
void finalize_str(JSContext *cx, JSString *str)
static void
finalize_str(const JSStringFinalizer *fin, jschar *chars);
static const JSStringFinalizer finalizer1 = { finalize_str };
static const JSStringFinalizer finalizer2 = { finalize_str };
static void
finalize_str(const JSStringFinalizer *fin, jschar *chars)
{
size_t len;
const jschar *chars = JS_GetStringCharsAndLength(cx, str, &len);
if (chars && len == arrlen && js::PodEqual(chars, arr, len)) {
void *closure = JS_GetExternalStringClosure(cx, str);
if (closure) {
if (closure == magic)
++finalized_closure;
} else {
++finalized_noclosure;
if (chars && js::PodEqual(const_cast<const jschar *>(chars), arr, arrlen)) {
if (fin == &finalizer1) {
++finalized1;
} else if (fin == &finalizer2) {
++finalized2;
}
}
}
BEGIN_TEST(testExternalStrings)
{
intN op = JS_AddExternalStringFinalizer(finalize_str);
const unsigned N = 1000;
for (unsigned i = 0; i < N; ++i) {
CHECK(JS_NewExternalString(cx, arr, arrlen, op));
CHECK(JS_NewExternalStringWithClosure(cx, arr, arrlen, op, magic));
CHECK(JS_NewExternalString(cx, arr, arrlen, &finalizer1));
CHECK(JS_NewExternalString(cx, arr, arrlen, &finalizer2));
}
// clear that newborn root
@ -42,8 +44,8 @@ BEGIN_TEST(testExternalStrings)
// a generous fudge factor to account for strings rooted by conservative gc
const unsigned epsilon = 10;
CHECK((N - finalized_noclosure) < epsilon);
CHECK((N - finalized_closure) < epsilon);
CHECK((N - finalized1) < epsilon);
CHECK((N - finalized2) < epsilon);
return true;
}

View File

@ -2966,51 +2966,27 @@ JS_FlushCaches(JSContext *cx)
{
}
JS_PUBLIC_API(intN)
JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer)
{
return JSExternalString::changeFinalizer(NULL, finalizer);
}
JS_PUBLIC_API(intN)
JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer)
{
return JSExternalString::changeFinalizer(finalizer, NULL);
}
JS_PUBLIC_API(JSString *)
JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length, intN type)
JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length,
const JSStringFinalizer *fin)
{
AssertNoGC(cx);
CHECK_REQUEST(cx);
JSString *s = JSExternalString::new_(cx, chars, length, type, NULL);
JSString *s = JSExternalString::new_(cx, chars, length, fin);
Probes::createString(cx, s, length);
return s;
}
extern JS_PUBLIC_API(JSString *)
JS_NewExternalStringWithClosure(JSContext *cx, const jschar *chars, size_t length,
intN type, void *closure)
{
AssertNoGC(cx);
CHECK_REQUEST(cx);
return JSExternalString::new_(cx, chars, length, type, closure);
}
extern JS_PUBLIC_API(JSBool)
JS_IsExternalString(JSContext *cx, JSString *str)
JS_IsExternalString(JSString *str)
{
AssertNoGC(cx);
CHECK_REQUEST(cx);
return str->isExternal();
}
extern JS_PUBLIC_API(void *)
JS_GetExternalStringClosure(JSContext *cx, JSString *str)
extern JS_PUBLIC_API(const JSStringFinalizer *)
JS_GetExternalStringFinalizer(JSString *str)
{
AssertNoGCOrFlatString(cx, str);
CHECK_REQUEST(cx);
return str->asExternal().externalClosure();
return str->asExternal().externalFinalizer();
}
JS_PUBLIC_API(void)

View File

@ -1330,11 +1330,13 @@ typedef void
(* JSFinalizeOp)(JSContext *cx, JSObject *obj);
/*
* Used by JS_AddExternalStringFinalizer and JS_RemoveExternalStringFinalizer
* to extend and reduce the set of string types finalized by the GC.
* Finalizes external strings created by JS_NewExternalString.
*/
typedef void
(* JSStringFinalizeOp)(JSContext *cx, JSString *str);
typedef struct JSStringFinalizer JSStringFinalizer;
struct JSStringFinalizer {
void (*finalize)(const JSStringFinalizer *fin, jschar *chars);
};
/*
* JSClass.checkAccess type: check whether obj[id] may be accessed per mode,
@ -3305,70 +3307,27 @@ JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key);
extern JS_PUBLIC_API(void)
JS_FlushCaches(JSContext *cx);
/*
* Add a finalizer for external strings created by JS_NewExternalString (see
* below) using a type-code returned from this function, and that understands
* how to free or release the memory pointed at by JS_GetStringChars(str).
*
* Return a nonnegative type index if there is room for finalizer in the
* global GC finalizers table, else return -1. If the engine is compiled
* JS_THREADSAFE and used in a multi-threaded environment, this function must
* be invoked on the primordial thread only, at startup -- or else the entire
* program must single-thread itself while loading a module that calls this
* function.
*/
extern JS_PUBLIC_API(intN)
JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer);
/*
* Remove finalizer from the global GC finalizers table, returning its type
* code if found, -1 if not found.
*
* As with JS_AddExternalStringFinalizer, there is a threading restriction
* if you compile the engine JS_THREADSAFE: this function may be called for a
* given finalizer pointer on only one thread; different threads may call to
* remove distinct finalizers safely.
*
* You must ensure that all strings with finalizer's type have been collected
* before calling this function. Otherwise, string data will be leaked by the
* GC, for want of a finalizer to call.
*/
extern JS_PUBLIC_API(intN)
JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer);
/*
* Create a new JSString whose chars member refers to external memory, i.e.,
* memory requiring type-specific finalization. The type code must be a
* nonnegative return value from JS_AddExternalStringFinalizer.
* memory requiring application-specific finalization.
*/
extern JS_PUBLIC_API(JSString *)
JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length, intN type);
/*
* Like JS_NewExternalString, except that 'closure' can be retrieved later via
* JS_GetExternalStringClosure. This closure data is a black blox to the JS
* engine and may be used by the embedding to associate extra data with an
* external string. E.g., an embedding may want to associate a pointer to the
* object that owns the chars of an external string so that, when this external
* string is finalized, the owner object can be deleted.
*/
extern JS_PUBLIC_API(JSString *)
JS_NewExternalStringWithClosure(JSContext *cx, const jschar *chars, size_t length,
intN type, void *closure);
JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length,
const JSStringFinalizer *fin);
/*
* Return whether 'str' was created with JS_NewExternalString or
* JS_NewExternalStringWithClosure.
*/
extern JS_PUBLIC_API(JSBool)
JS_IsExternalString(JSContext *cx, JSString *str);
JS_IsExternalString(JSString *str);
/*
* Return the 'closure' arg passed to JS_NewExternalStringWithClosure or NULL
* if the external string was created via JS_NewExternalString.
*/
extern JS_PUBLIC_API(void *)
JS_GetExternalStringClosure(JSContext *cx, JSString *str);
extern JS_PUBLIC_API(const JSStringFinalizer *)
JS_GetExternalStringFinalizer(JSString *str);
/*
* Set the size of the native stack that should not be exceed. To disable

View File

@ -229,18 +229,19 @@ JSShortString::initAtOffsetInBuffer(const jschar *chars, size_t length)
}
JS_ALWAYS_INLINE void
JSExternalString::init(const jschar *chars, size_t length, intN type, void *closure)
JSExternalString::init(const jschar *chars, size_t length, const JSStringFinalizer *fin)
{
JS_ASSERT(fin);
JS_ASSERT(fin->finalize);
d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
d.u1.chars = chars;
d.s.u2.externalType = type;
d.s.u3.externalClosure = closure;
d.s.u2.externalFinalizer = fin;
}
JS_ALWAYS_INLINE JSExternalString *
JSExternalString::new_(JSContext *cx, const jschar *chars, size_t length, intN type, void *closure)
JSExternalString::new_(JSContext *cx, const jschar *chars, size_t length,
const JSStringFinalizer *fin)
{
JS_ASSERT(uintN(type) < JSExternalString::TYPE_LIMIT);
JS_ASSERT(chars[length] == 0);
if (!validateLength(cx, length))
@ -248,7 +249,7 @@ JSExternalString::new_(JSContext *cx, const jschar *chars, size_t length, intN t
JSExternalString *str = js_NewGCExternalString(cx);
if (!str)
return NULL;
str->init(chars, length, type, closure);
str->init(chars, length, fin);
cx->runtime->updateMallocCounter(cx, (length + 1) * sizeof(jschar));
return str;
}
@ -408,21 +409,14 @@ JSAtom::finalize(JSRuntime *rt)
inline void
JSExternalString::finalize(JSContext *cx, bool background)
{
if (JSStringFinalizeOp finalizer = str_finalizers[externalType()])
finalizer(cx, this);
finalize();
}
inline void
JSExternalString::finalize()
{
JSStringFinalizeOp finalizer = str_finalizers[externalType()];
if (finalizer) {
/*
* Assume that the finalizer for the permanently interned
* string knows how to deal with null context.
*/
finalizer(NULL, this);
}
const JSStringFinalizer *fin = externalFinalizer();
fin->finalize(fin, const_cast<jschar *>(chars()));
}
#endif

View File

@ -371,10 +371,6 @@ JSDependentString::undepend(JSContext *cx)
return &this->asFixed();
}
JSStringFinalizeOp JSExternalString::str_finalizers[JSExternalString::TYPE_LIMIT] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
bool
JSFlatString::isIndex(uint32_t *indexp) const
{

View File

@ -174,11 +174,10 @@ class JSString : public js::gc::Cell
JSLinearString *base; /* JSDependentString */
JSString *right; /* JSRope */
size_t capacity; /* JSFlatString (extensible) */
size_t externalType; /* JSExternalString */
const JSStringFinalizer *externalFinalizer;/* JSExternalString */
} u2;
union {
JSString *parent; /* JSRope (temporary) */
void *externalClosure; /* JSExternalString */
size_t reserved; /* may use for bug 615290 */
} u3;
} s;
@ -628,55 +627,27 @@ class JSShortString : public JSInlineString
JS_STATIC_ASSERT(sizeof(JSShortString) == 2 * sizeof(JSString));
/*
* The externalClosure stored in an external string is a black box to the JS
* engine; see JS_NewExternalStringWithClosure.
*/
class JSExternalString : public JSFixedString
{
static void staticAsserts() {
JS_STATIC_ASSERT(TYPE_LIMIT == 8);
}
void init(const jschar *chars, size_t length, intN type, void *closure);
void init(const jschar *chars, size_t length, const JSStringFinalizer *fin);
/* Vacuous and therefore unimplemented. */
bool isExternal() const MOZ_DELETE;
JSExternalString &asExternal() const MOZ_DELETE;
public:
static inline JSExternalString *new_(JSContext *cx, const jschar *chars,
size_t length, intN type, void *closure);
static inline JSExternalString *new_(JSContext *cx, const jschar *chars, size_t length,
const JSStringFinalizer *fin);
intN externalType() const {
const JSStringFinalizer *externalFinalizer() const {
JS_ASSERT(JSString::isExternal());
JS_ASSERT(d.s.u2.externalType < TYPE_LIMIT);
return intN(d.s.u2.externalType);
}
void *externalClosure() const {
JS_ASSERT(JSString::isExternal());
return d.s.u3.externalClosure;
}
static const uintN TYPE_LIMIT = 8;
static JSStringFinalizeOp str_finalizers[TYPE_LIMIT];
static intN changeFinalizer(JSStringFinalizeOp oldop,
JSStringFinalizeOp newop) {
for (uintN i = 0; i < mozilla::ArrayLength(str_finalizers); i++) {
if (str_finalizers[i] == oldop) {
str_finalizers[i] = newop;
return intN(i);
}
}
return -1;
return d.s.u2.externalFinalizer;
}
/* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
void finalize(JSContext *cx, bool background);
void finalize();
inline void finalize(JSContext *cx, bool background);
inline void finalize();
};
JS_STATIC_ASSERT(sizeof(JSExternalString) == sizeof(JSString));

View File

@ -73,8 +73,6 @@ using namespace mozilla;
#define ILLEGAL_CHAR_RANGE(c) (0!=((c) & 0x80))
static intN sXPCOMUCStringFinalizerIndex = -1;
/***********************************************************/
// static
@ -116,38 +114,12 @@ XPCConvert::GetISupportsFromJSObject(JSObject* obj, nsISupports** iface)
/***************************************************************************/
static void
FinalizeXPCOMUCString(JSContext *cx, JSString *str)
FinalizeXPCOMUCString(const JSStringFinalizer *fin, jschar *chars)
{
NS_ASSERTION(sXPCOMUCStringFinalizerIndex != -1,
"XPCConvert: XPCOM Unicode string finalizer called uninitialized!");
jschar* buffer = const_cast<jschar *>(JS_GetStringCharsZ(cx, str));
NS_ASSERTION(buffer, "How could this OOM if we allocated the memory?");
nsMemory::Free(buffer);
nsMemory::Free(chars);
}
static JSBool
AddXPCOMUCStringFinalizer()
{
sXPCOMUCStringFinalizerIndex =
JS_AddExternalStringFinalizer(FinalizeXPCOMUCString);
if (sXPCOMUCStringFinalizerIndex == -1) {
return false;
}
return true;
}
//static
void
XPCConvert::RemoveXPCOMUCStringFinalizer()
{
JS_RemoveExternalStringFinalizer(FinalizeXPCOMUCString);
sXPCOMUCStringFinalizerIndex = -1;
}
static const JSStringFinalizer sXPCOMUCStringFinalizer = { FinalizeXPCOMUCString };
// static
JSBool
@ -319,13 +291,9 @@ XPCConvert::NativeData2JS(XPCLazyCallContext& lccx, jsval* d, const void* s,
if (!p)
return false;
if (sXPCOMUCStringFinalizerIndex == -1 &&
!AddXPCOMUCStringFinalizer())
return false;
JSString* jsString =
JS_NewExternalString(cx, p, len,
sXPCOMUCStringFinalizerIndex);
&sXPCOMUCStringFinalizer);
if (!jsString) {
nsMemory::Free(p);
@ -350,14 +318,10 @@ XPCConvert::NativeData2JS(XPCLazyCallContext& lccx, jsval* d, const void* s,
if (!unicodeString)
return false;
if (sXPCOMUCStringFinalizerIndex == -1 &&
!AddXPCOMUCStringFinalizer())
return false;
JSString* jsString = JS_NewExternalString(cx,
(jschar*)unicodeString,
cString->Length(),
sXPCOMUCStringFinalizerIndex);
&sXPCOMUCStringFinalizer);
if (!jsString) {
nsMemory::Free(unicodeString);

View File

@ -1179,11 +1179,6 @@ XPCJSRuntime::~XPCJSRuntime()
delete mExplicitNativeWrapperMap;
}
// unwire the readable/JSString sharing magic
XPCStringConvert::ShutdownDOMStringFinalizer();
XPCConvert::RemoveXPCOMUCStringFinalizer();
if (mJSHolders.ops) {
JS_DHashTableFinish(&mJSHolders);
mJSHolders.ops = nsnull;

View File

@ -58,22 +58,13 @@
static int sDOMStringFinalizerIndex = -1;
static void
DOMStringFinalizer(JSContext *cx, JSString *str)
FinalizeDOMString(const JSStringFinalizer *fin, jschar *chars)
{
jschar *chars = const_cast<jschar *>(JS_GetStringCharsZ(cx, str));
NS_ASSERTION(chars, "How could this OOM if we allocated the memory?");
nsStringBuffer::FromData(chars)->Release();
}
void
XPCStringConvert::ShutdownDOMStringFinalizer()
{
if (sDOMStringFinalizerIndex == -1)
return;
static const JSStringFinalizer sDOMStringFinalizer = { FinalizeDOMString };
JS_RemoveExternalStringFinalizer(DOMStringFinalizer);
sDOMStringFinalizerIndex = -1;
}
// convert a readable to a JSString, copying string data
// static
@ -94,16 +85,9 @@ XPCStringConvert::ReadableToJSVal(JSContext *cx,
if (buf) {
// yay, we can share the string's buffer!
if (sDOMStringFinalizerIndex == -1) {
sDOMStringFinalizerIndex =
JS_AddExternalStringFinalizer(DOMStringFinalizer);
if (sDOMStringFinalizerIndex == -1)
return JSVAL_NULL;
}
str = JS_NewExternalString(cx,
reinterpret_cast<jschar *>(buf->Data()),
length, sDOMStringFinalizerIndex);
length, &sDOMStringFinalizer);
if (str) {
*sharedBuffer = buf;

View File

@ -3343,8 +3343,6 @@ public:
JSContext* cx,
jsval *jsExceptionPtr);
static void RemoveXPCOMUCStringFinalizer();
private:
XPCConvert(); // not implemented
@ -3363,8 +3361,6 @@ public:
static jsval ReadableToJSVal(JSContext *cx, const nsAString &readable,
nsStringBuffer** sharedBuffer);
static void ShutdownDOMStringFinalizer();
private:
XPCStringConvert(); // not implemented
};