Bug 611652: clear values instead of scope when global window is cleared, r=brendan,enn,jeff,mrbkap,smaug

--HG--
extra : rebase_source : 77e3844d35de229817bee767256a96d030a78bf9
This commit is contained in:
David Mandelin 2011-01-21 18:37:30 -08:00
Родитель e1c1d97d41
Коммит 97fa2f9d5d
9 изменённых файлов: 61 добавлений и 2 удалений

Просмотреть файл

@ -3185,7 +3185,18 @@ nsJSContext::ClearScope(void *aGlobalObj, PRBool aClearFromProtoChain)
JS_ClearPendingException(mContext);
}
JS_ClearScope(mContext, obj);
// Hack fix for bug 611653. Originally, this always called JS_ClearScope,
// which was required to avoid leaks. But for native objects, the JS
// engine has an optimization that requires that permanent properties of
// the global object are never deleted. So instead, we call a new special
// API that clears the values of the global, thus avoiding leaks without
// deleting any properties.
if (obj->isNative()) {
js_UnbrandAndClearSlots(mContext, obj);
} else {
JS_ClearScope(mContext, obj);
}
if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
JS_ClearScope(mContext, &obj->getProxyExtra().toObject());
}

Просмотреть файл

@ -1979,7 +1979,7 @@ struct JSClass {
*/
#define JSCLASS_GLOBAL_FLAGS \
(JSCLASS_IS_GLOBAL | \
JSCLASS_HAS_RESERVED_SLOTS(JSProto_LIMIT * 3 + JSRESERVED_GLOBAL_SLOTS_COUNT))
JSCLASS_HAS_RESERVED_SLOTS(JSRESERVED_GLOBAL_THIS + JSRESERVED_GLOBAL_SLOTS_COUNT))
/* Fast access to the original value of each standard class's prototype. */
#define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 8)

Просмотреть файл

@ -4447,6 +4447,40 @@ JSObject::freeSlot(JSContext *cx, uint32 slot)
return false;
}
JS_FRIEND_API(void)
js_UnbrandAndClearSlots(JSContext *cx, JSObject *obj)
{
JS_ASSERT(obj->isNative());
JS_ASSERT(obj->isGlobal());
/* This can return false but that doesn't mean it failed. */
obj->unbrand(cx);
/*
* Clear the prototype cache. We must not clear the other global
* reserved slots, as other code will crash if they are arbitrarily
* reset (e.g., regexp statics).
*/
for (int key = JSProto_Null; key < JSRESERVED_GLOBAL_THIS; key++)
JS_SetReservedSlot(cx, obj, key, JSVAL_VOID);
/*
* Clear the non-reserved slots.
*/
ClearValueRange(obj->slots + JSCLASS_RESERVED_SLOTS(obj->clasp),
obj->capacity - JSCLASS_RESERVED_SLOTS(obj->clasp),
obj->clasp == &js_ArrayClass);
/*
* We just overwrote all slots to undefined, so the freelist has
* been trashed. We need to clear the head pointer or else we will
* crash later. This leaks slots but the object is all but dead
* anyway.
*/
if (obj->hasPropertyTable())
obj->lastProperty()->table->freelist = SHAPE_INVALID_SLOT;
}
/* JSBOXEDWORD_INT_MAX as a string */
#define JSBOXEDWORD_INT_MAX_STRING "1073741823"

Просмотреть файл

@ -1740,6 +1740,15 @@ extern JSBool
js_SetNativeAttributes(JSContext *cx, JSObject *obj, js::Shape *shape,
uintN attrs);
/*
* Hack fix for bug 611653: Do not use for any other purpose.
*
* Unbrand and set all slot values to undefined (except reserved slots that
* are not used for cached prototypes).
*/
JS_FRIEND_API(void)
js_UnbrandAndClearSlots(JSContext *cx, JSObject *obj);
namespace js {
/*

Просмотреть файл

@ -297,6 +297,7 @@ struct Shape : public JSObjectMap
friend class js::PropertyTree;
friend class js::Bindings;
friend bool IsShapeAboutToBeFinalized(JSContext *cx, const js::Shape *shape);
friend JS_FRIEND_API(void) ::js_UnbrandAndClearSlots(JSContext *cx, JSObject *obj);
protected:
mutable uint32 numSearches; /* Only updated until it reaches HASH_MIN_SEARCHES. */

Просмотреть файл

@ -48,6 +48,7 @@ function keyCaretTest()
autocomplete.selectionEnd = 4;
checkKeyCaretTest("VK_DOWN", 6, 6, true, "value down with selection");
autocomplete.controller.stopSearch();
SimpleTest.finish();
}

Просмотреть файл

@ -169,6 +169,7 @@ function checkResult() {
setTimeout(function() {
// Unregister the factory so that we don't get in the way of other tests
componentManager.unregisterFactory(autoCompleteSimpleID, autoCompleteSimple);
autocomplete.controller.stopSearch();
SimpleTest.finish();
}, 0);
}

Просмотреть файл

@ -169,6 +169,7 @@ function checkResult() {
setTimeout(function() {
// Unregister the factory so that we don't get in the way of other tests
componentManager.unregisterFactory(autoCompleteSimpleID, autoCompleteSimple);
autocomplete.controller.stopSearch();
SimpleTest.finish();
}, 0);
}

Просмотреть файл

@ -150,6 +150,7 @@ function nextTest() {
if (!tests.length) {
// No more tests to run, finish.
setTimeout(function() {
$("autocomplete").controller.stopSearch();
// Unregister the factory so that we don't get in the way of other tests
componentManager.unregisterFactory(autoCompleteSimpleID, autoCompleteSimple);
SimpleTest.finish();