mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
backout merge, bug 633219
This commit is contained in:
commit
b844ddbe36
@ -1,63 +0,0 @@
|
|||||||
// Test that the watch handler is not called recursively for the same object
|
|
||||||
// and property.
|
|
||||||
(function() {
|
|
||||||
var obj1 = {}, obj2 = {};
|
|
||||||
var handler_entry_count = 0;
|
|
||||||
var handler_exit_count = 0;
|
|
||||||
|
|
||||||
obj1.watch('x', handler);
|
|
||||||
obj1.watch('y', handler);
|
|
||||||
obj2.watch('x', handler);
|
|
||||||
obj1.x = 1;
|
|
||||||
assertEq(handler_entry_count, 3);
|
|
||||||
assertEq(handler_exit_count, 3);
|
|
||||||
|
|
||||||
function handler(id) {
|
|
||||||
handler_entry_count++;
|
|
||||||
assertEq(handler_exit_count, 0);
|
|
||||||
switch (true) {
|
|
||||||
case this === obj1 && id === "x":
|
|
||||||
assertEq(handler_entry_count, 1);
|
|
||||||
obj2.x = 3;
|
|
||||||
assertEq(handler_exit_count, 2);
|
|
||||||
break;
|
|
||||||
case this === obj2 && id === "x":
|
|
||||||
assertEq(handler_entry_count, 2);
|
|
||||||
obj1.y = 4;
|
|
||||||
assertEq(handler_exit_count, 1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assertEq(this, obj1);
|
|
||||||
assertEq(id, "y");
|
|
||||||
assertEq(handler_entry_count, 3);
|
|
||||||
|
|
||||||
// We expect no more watch handler invocations
|
|
||||||
obj1.x = 5;
|
|
||||||
obj1.y = 6;
|
|
||||||
obj2.x = 7;
|
|
||||||
assertEq(handler_exit_count, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++handler_exit_count;
|
|
||||||
assertEq(handler_entry_count, 3);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
// Test that run-away recursion in watch handlers is properly handled.
|
|
||||||
(function() {
|
|
||||||
var obj = {};
|
|
||||||
var i = 0;
|
|
||||||
try {
|
|
||||||
handler();
|
|
||||||
throw new Error("Unreachable");
|
|
||||||
} catch(e) {
|
|
||||||
assertEq(e instanceof InternalError, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handler() {
|
|
||||||
var prop = "a" + ++i;
|
|
||||||
obj.watch(prop, handler);
|
|
||||||
obj[prop] = 2;
|
|
||||||
}
|
|
||||||
})();
|
|
@ -67,7 +67,6 @@ CPPSRCS = \
|
|||||||
testNewObject.cpp \
|
testNewObject.cpp \
|
||||||
testOps.cpp \
|
testOps.cpp \
|
||||||
testPropCache.cpp \
|
testPropCache.cpp \
|
||||||
testResolveRecursion.cpp \
|
|
||||||
testSameValue.cpp \
|
testSameValue.cpp \
|
||||||
testScriptObject.cpp \
|
testScriptObject.cpp \
|
||||||
testSetProperty.cpp \
|
testSetProperty.cpp \
|
||||||
|
@ -1,134 +0,0 @@
|
|||||||
/* -*- 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"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test that resolve hook recursion for the same object and property is
|
|
||||||
* prevented.
|
|
||||||
*/
|
|
||||||
|
|
||||||
BEGIN_TEST(testResolveRecursion)
|
|
||||||
{
|
|
||||||
static JSClass my_resolve_class = {
|
|
||||||
"MyResolve",
|
|
||||||
JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE,
|
|
||||||
|
|
||||||
JS_PropertyStub, // add
|
|
||||||
JS_PropertyStub, // delete
|
|
||||||
JS_PropertyStub, // get
|
|
||||||
JS_StrictPropertyStub, // set
|
|
||||||
JS_EnumerateStub,
|
|
||||||
(JSResolveOp) my_resolve,
|
|
||||||
JS_ConvertStub,
|
|
||||||
JS_FinalizeStub,
|
|
||||||
JSCLASS_NO_OPTIONAL_MEMBERS
|
|
||||||
};
|
|
||||||
|
|
||||||
obj1 = JS_NewObject(cx, &my_resolve_class, NULL, NULL);
|
|
||||||
CHECK(obj1);
|
|
||||||
obj2 = JS_NewObject(cx, &my_resolve_class, NULL, NULL);
|
|
||||||
CHECK(obj2);
|
|
||||||
CHECK(JS_SetPrivate(cx, obj1, this));
|
|
||||||
CHECK(JS_SetPrivate(cx, obj2, this));
|
|
||||||
|
|
||||||
CHECK(JS_DefineProperty(cx, global, "obj1", OBJECT_TO_JSVAL(obj1), NULL, NULL, 0));
|
|
||||||
CHECK(JS_DefineProperty(cx, global, "obj2", OBJECT_TO_JSVAL(obj2), NULL, NULL, 0));
|
|
||||||
|
|
||||||
resolveEntryCount = 0;
|
|
||||||
resolveExitCount = 0;
|
|
||||||
|
|
||||||
/* Start the essence of the test via invoking the first resolve hook. */
|
|
||||||
jsval v;
|
|
||||||
EVAL("obj1.x", &v);
|
|
||||||
CHECK(v == JSVAL_FALSE);
|
|
||||||
CHECK(resolveEntryCount == 4);
|
|
||||||
CHECK(resolveExitCount == 4);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSObject *obj1;
|
|
||||||
JSObject *obj2;
|
|
||||||
unsigned resolveEntryCount;
|
|
||||||
unsigned resolveExitCount;
|
|
||||||
|
|
||||||
struct AutoIncrCounters {
|
|
||||||
|
|
||||||
AutoIncrCounters(cls_testResolveRecursion *t) : t(t) {
|
|
||||||
t->resolveEntryCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
~AutoIncrCounters() {
|
|
||||||
t->resolveExitCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
cls_testResolveRecursion *t;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool
|
|
||||||
doResolve(JSObject *obj, jsid id, uintN flags, JSObject **objp)
|
|
||||||
{
|
|
||||||
CHECK(resolveExitCount == 0);
|
|
||||||
AutoIncrCounters incr(this);
|
|
||||||
CHECK(obj == obj1 || obj == obj2);
|
|
||||||
|
|
||||||
CHECK(JSID_IS_STRING(id));
|
|
||||||
|
|
||||||
JSFlatString *str = JS_FlattenString(cx, JSID_TO_STRING(id));
|
|
||||||
CHECK(str);
|
|
||||||
jsval v;
|
|
||||||
if (JS_FlatStringEqualsAscii(str, "x")) {
|
|
||||||
if (obj == obj1) {
|
|
||||||
/* First resolve hook invocation. */
|
|
||||||
CHECK(resolveEntryCount == 1);
|
|
||||||
EVAL("obj2.y = true", &v);
|
|
||||||
CHECK(v == JSVAL_TRUE);
|
|
||||||
CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_FALSE, NULL, NULL, 0));
|
|
||||||
*objp = obj;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (obj == obj2) {
|
|
||||||
CHECK(resolveEntryCount == 4);
|
|
||||||
*objp = NULL;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (JS_FlatStringEqualsAscii(str, "y")) {
|
|
||||||
if (obj == obj2) {
|
|
||||||
CHECK(resolveEntryCount == 2);
|
|
||||||
CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_NULL, NULL, NULL, 0));
|
|
||||||
EVAL("obj1.x", &v);
|
|
||||||
CHECK(JSVAL_IS_VOID(v));
|
|
||||||
EVAL("obj1.y", &v);
|
|
||||||
CHECK(v == JSVAL_ZERO);
|
|
||||||
*objp = obj;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (obj == obj1) {
|
|
||||||
CHECK(resolveEntryCount == 3);
|
|
||||||
EVAL("obj1.x", &v);
|
|
||||||
CHECK(JSVAL_IS_VOID(v));
|
|
||||||
EVAL("obj1.y", &v);
|
|
||||||
CHECK(JSVAL_IS_VOID(v));
|
|
||||||
EVAL("obj2.y", &v);
|
|
||||||
CHECK(JSVAL_IS_NULL(v));
|
|
||||||
EVAL("obj2.x", &v);
|
|
||||||
CHECK(JSVAL_IS_VOID(v));
|
|
||||||
EVAL("obj1.y = 0", &v);
|
|
||||||
CHECK(v == JSVAL_ZERO);
|
|
||||||
*objp = obj;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CHECK(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSBool
|
|
||||||
my_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
|
|
||||||
{
|
|
||||||
return static_cast<cls_testResolveRecursion *>(JS_GetPrivate(cx, obj))->
|
|
||||||
doResolve(obj, id, flags, objp);
|
|
||||||
}
|
|
||||||
|
|
||||||
END_TEST(testResolveRecursion)
|
|
@ -1385,6 +1385,37 @@ JS_SetGlobalObject(JSContext *cx, JSObject *obj)
|
|||||||
cx->resetCompartment();
|
cx->resetCompartment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AutoResolvingEntry {
|
||||||
|
public:
|
||||||
|
AutoResolvingEntry() : entry(NULL) {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns false on error. But N.B. if obj[id] was already being resolved,
|
||||||
|
* this is a no-op, and we silently treat that as success.
|
||||||
|
*/
|
||||||
|
bool start(JSContext *cx, JSObject *obj, jsid id, uint32 flag) {
|
||||||
|
JS_ASSERT(!entry);
|
||||||
|
this->cx = cx;
|
||||||
|
key.obj = obj;
|
||||||
|
key.id = id;
|
||||||
|
this->flag = flag;
|
||||||
|
bool ok = !!js_StartResolving(cx, &key, flag, &entry);
|
||||||
|
JS_ASSERT_IF(!ok, !entry);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
~AutoResolvingEntry() {
|
||||||
|
if (entry)
|
||||||
|
js_StopResolving(cx, &key, flag, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
JSContext *cx;
|
||||||
|
JSResolvingKey key;
|
||||||
|
uint32 flag;
|
||||||
|
JSResolvingEntry *entry;
|
||||||
|
};
|
||||||
|
|
||||||
JSObject *
|
JSObject *
|
||||||
js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj)
|
js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj)
|
||||||
{
|
{
|
||||||
@ -1395,11 +1426,14 @@ js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj)
|
|||||||
if (!cx->globalObject)
|
if (!cx->globalObject)
|
||||||
JS_SetGlobalObject(cx, obj);
|
JS_SetGlobalObject(cx, obj);
|
||||||
|
|
||||||
/* Record Function and Object in the resolving set. */
|
/* Record Function and Object in cx->resolvingTable. */
|
||||||
|
AutoResolvingEntry e1, e2;
|
||||||
JSAtom **classAtoms = cx->runtime->atomState.classAtoms;
|
JSAtom **classAtoms = cx->runtime->atomState.classAtoms;
|
||||||
AutoResolving resolving1(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Function]));
|
if (!e1.start(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Function]), JSRESFLAG_LOOKUP) ||
|
||||||
AutoResolving resolving2(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Object]));
|
!e2.start(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Object]), JSRESFLAG_LOOKUP)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize the function class first so constructors can be made. */
|
/* Initialize the function class first so constructors can be made. */
|
||||||
if (!js_GetClassPrototype(cx, obj, JSProto_Function, &fun_proto))
|
if (!js_GetClassPrototype(cx, obj, JSProto_Function, &fun_proto))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -112,6 +112,9 @@ using namespace js::gc;
|
|||||||
static const size_t ARENA_HEADER_SIZE_HACK = 40;
|
static const size_t ARENA_HEADER_SIZE_HACK = 40;
|
||||||
static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK;
|
static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK;
|
||||||
|
|
||||||
|
static void
|
||||||
|
FreeContext(JSContext *cx);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
JS_REQUIRES_STACK bool
|
JS_REQUIRES_STACK bool
|
||||||
StackSegment::contains(const JSStackFrame *fp) const
|
StackSegment::contains(const JSStackFrame *fp) const
|
||||||
@ -167,10 +170,9 @@ StackSpace::init()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
StackSpace::~StackSpace()
|
void
|
||||||
|
StackSpace::finish()
|
||||||
{
|
{
|
||||||
if (!base)
|
|
||||||
return;
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
VirtualFree(base, (commitEnd - base) * sizeof(Value), MEM_DECOMMIT);
|
VirtualFree(base, (commitEnd - base) * sizeof(Value), MEM_DECOMMIT);
|
||||||
VirtualFree(base, 0, MEM_RELEASE);
|
VirtualFree(base, 0, MEM_RELEASE);
|
||||||
@ -495,17 +497,88 @@ AllFramesIter::operator++()
|
|||||||
bool
|
bool
|
||||||
JSThreadData::init()
|
JSThreadData::init()
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
/* The data must be already zeroed. */
|
||||||
|
for (size_t i = 0; i != sizeof(*this); ++i)
|
||||||
|
JS_ASSERT(reinterpret_cast<uint8*>(this)[i] == 0);
|
||||||
|
#endif
|
||||||
|
if (!stackSpace.init())
|
||||||
|
return false;
|
||||||
|
dtoaState = js_NewDtoaState();
|
||||||
|
if (!dtoaState) {
|
||||||
|
finish();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
nativeStackBase = GetNativeStackBase();
|
nativeStackBase = GetNativeStackBase();
|
||||||
|
|
||||||
#ifdef JS_TRACER
|
#ifdef JS_TRACER
|
||||||
/* Set the default size for the code cache to 16MB. */
|
/* Set the default size for the code cache to 16MB. */
|
||||||
maxCodeCacheBytes = 16 * 1024 * 1024;
|
maxCodeCacheBytes = 16 * 1024 * 1024;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return stackSpace.init() && !!(dtoaState = js_NewDtoaState());
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JSThreadData::finish()
|
||||||
|
{
|
||||||
|
if (dtoaState)
|
||||||
|
js_DestroyDtoaState(dtoaState);
|
||||||
|
|
||||||
|
js_FinishGSNCache(&gsnCache);
|
||||||
|
propertyCache.~PropertyCache();
|
||||||
|
stackSpace.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JSThreadData::mark(JSTracer *trc)
|
||||||
|
{
|
||||||
|
stackSpace.mark(trc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JSThreadData::purge(JSContext *cx)
|
||||||
|
{
|
||||||
|
js_PurgeGSNCache(&gsnCache);
|
||||||
|
|
||||||
|
/* FIXME: bug 506341. */
|
||||||
|
propertyCache.purge(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
|
|
||||||
|
static JSThread *
|
||||||
|
NewThread(void *id)
|
||||||
|
{
|
||||||
|
JS_ASSERT(js_CurrentThreadId() == id);
|
||||||
|
JSThread *thread = (JSThread *) js_calloc(sizeof(JSThread));
|
||||||
|
if (!thread)
|
||||||
|
return NULL;
|
||||||
|
JS_INIT_CLIST(&thread->contextList);
|
||||||
|
thread->id = id;
|
||||||
|
if (!thread->data.init()) {
|
||||||
|
js_free(thread);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
DestroyThread(JSThread *thread)
|
||||||
|
{
|
||||||
|
/* The thread must have zero contexts. */
|
||||||
|
JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The conservative GC scanner should be disabled when the thread leaves
|
||||||
|
* the last request.
|
||||||
|
*/
|
||||||
|
JS_ASSERT(!thread->data.conservativeGC.hasStackToScan());
|
||||||
|
|
||||||
|
thread->data.finish();
|
||||||
|
js_free(thread);
|
||||||
|
}
|
||||||
|
|
||||||
JSThread *
|
JSThread *
|
||||||
js_CurrentThread(JSRuntime *rt)
|
js_CurrentThread(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
@ -531,21 +604,14 @@ js_CurrentThread(JSRuntime *rt)
|
|||||||
thread->data.nativeStackBase = GetNativeStackBase();
|
thread->data.nativeStackBase = GetNativeStackBase();
|
||||||
} else {
|
} else {
|
||||||
JS_UNLOCK_GC(rt);
|
JS_UNLOCK_GC(rt);
|
||||||
|
thread = NewThread(id);
|
||||||
void *threadMemory = js_calloc(sizeof(JSThread));
|
if (!thread)
|
||||||
if (!threadMemory)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
thread = new (threadMemory) JSThread(id);
|
|
||||||
if (!thread->init()) {
|
|
||||||
js_delete(thread);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
JS_LOCK_GC(rt);
|
JS_LOCK_GC(rt);
|
||||||
js_WaitForGC(rt);
|
js_WaitForGC(rt);
|
||||||
if (!rt->threads.relookupOrAdd(p, id, thread)) {
|
if (!rt->threads.relookupOrAdd(p, id, thread)) {
|
||||||
JS_UNLOCK_GC(rt);
|
JS_UNLOCK_GC(rt);
|
||||||
js_delete(thread);
|
DestroyThread(thread);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -623,7 +689,8 @@ js_FinishThreads(JSRuntime *rt)
|
|||||||
return;
|
return;
|
||||||
for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
|
for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
|
||||||
JSThread *thread = r.front().value;
|
JSThread *thread = r.front().value;
|
||||||
js_delete(thread);
|
JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
|
||||||
|
DestroyThread(thread);
|
||||||
}
|
}
|
||||||
rt->threads.clear();
|
rt->threads.clear();
|
||||||
#else
|
#else
|
||||||
@ -642,7 +709,8 @@ js_PurgeThreads(JSContext *cx)
|
|||||||
|
|
||||||
if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
|
if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
|
||||||
JS_ASSERT(cx->thread != thread);
|
JS_ASSERT(cx->thread != thread);
|
||||||
js_delete(thread);
|
|
||||||
|
DestroyThread(thread);
|
||||||
e.removeFront();
|
e.removeFront();
|
||||||
} else {
|
} else {
|
||||||
thread->data.purge(cx);
|
thread->data.purge(cx);
|
||||||
@ -687,13 +755,13 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
|||||||
JS_ASSERT(cx->resolveFlags == 0);
|
JS_ASSERT(cx->resolveFlags == 0);
|
||||||
|
|
||||||
if (!cx->busyArrays.init()) {
|
if (!cx->busyArrays.init()) {
|
||||||
js_delete(cx);
|
FreeContext(cx);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
if (!js_InitContextThread(cx)) {
|
if (!js_InitContextThread(cx)) {
|
||||||
js_delete(cx);
|
FreeContext(cx);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1025,7 +1093,41 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
|||||||
cx->dstOffsetCache.dumpStats();
|
cx->dstOffsetCache.dumpStats();
|
||||||
#endif
|
#endif
|
||||||
JS_UNLOCK_GC(rt);
|
JS_UNLOCK_GC(rt);
|
||||||
js_delete(cx);
|
FreeContext(cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
FreeContext(JSContext *cx)
|
||||||
|
{
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
JS_ASSERT(!cx->thread);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Free the stuff hanging off of cx. */
|
||||||
|
VOUCH_DOES_NOT_REQUIRE_STACK();
|
||||||
|
JS_FinishArenaPool(&cx->tempPool);
|
||||||
|
JS_FinishArenaPool(&cx->regExpPool);
|
||||||
|
|
||||||
|
if (cx->lastMessage)
|
||||||
|
js_free(cx->lastMessage);
|
||||||
|
|
||||||
|
/* Remove any argument formatters. */
|
||||||
|
JSArgumentFormatMap *map = cx->argumentFormatMap;
|
||||||
|
while (map) {
|
||||||
|
JSArgumentFormatMap *temp = map;
|
||||||
|
map = map->next;
|
||||||
|
cx->free(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Destroy the resolve recursion damper. */
|
||||||
|
if (cx->resolvingTable) {
|
||||||
|
JS_DHashTableDestroy(cx->resolvingTable);
|
||||||
|
cx->resolvingTable = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finally, free cx itself. */
|
||||||
|
cx->~JSContext();
|
||||||
|
js_free(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSContext *
|
JSContext *
|
||||||
@ -1056,21 +1158,107 @@ js_NextActiveContext(JSRuntime *rt, JSContext *cx)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace js {
|
static JSDHashNumber
|
||||||
|
resolving_HashKey(JSDHashTable *table, const void *ptr)
|
||||||
bool
|
|
||||||
AutoResolving::isDuplicate() const
|
|
||||||
{
|
{
|
||||||
JS_ASSERT(prev);
|
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
|
||||||
AutoResolving *cursor = prev;
|
|
||||||
do {
|
|
||||||
if (cursor->object == object && cursor->id == id && cursor->kind == kind)
|
|
||||||
return true;
|
|
||||||
} while (!!(cursor = cursor->prev));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} /* namespace js */
|
return (JSDHashNumber(uintptr_t(key->obj)) >> JS_GCTHING_ALIGN) ^ JSID_BITS(key->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSBool
|
||||||
|
resolving_MatchEntry(JSDHashTable *table,
|
||||||
|
const JSDHashEntryHdr *hdr,
|
||||||
|
const void *ptr)
|
||||||
|
{
|
||||||
|
const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
|
||||||
|
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
|
||||||
|
|
||||||
|
return entry->key.obj == key->obj && entry->key.id == key->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const JSDHashTableOps resolving_dhash_ops = {
|
||||||
|
JS_DHashAllocTable,
|
||||||
|
JS_DHashFreeTable,
|
||||||
|
resolving_HashKey,
|
||||||
|
resolving_MatchEntry,
|
||||||
|
JS_DHashMoveEntryStub,
|
||||||
|
JS_DHashClearEntryStub,
|
||||||
|
JS_DHashFinalizeStub,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
JSBool
|
||||||
|
js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
|
||||||
|
JSResolvingEntry **entryp)
|
||||||
|
{
|
||||||
|
JSDHashTable *table;
|
||||||
|
JSResolvingEntry *entry;
|
||||||
|
|
||||||
|
table = cx->resolvingTable;
|
||||||
|
if (!table) {
|
||||||
|
table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
|
||||||
|
sizeof(JSResolvingEntry),
|
||||||
|
JS_DHASH_MIN_SIZE);
|
||||||
|
if (!table)
|
||||||
|
goto outofmem;
|
||||||
|
cx->resolvingTable = table;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = (JSResolvingEntry *)
|
||||||
|
JS_DHashTableOperate(table, key, JS_DHASH_ADD);
|
||||||
|
if (!entry)
|
||||||
|
goto outofmem;
|
||||||
|
|
||||||
|
if (entry->flags & flag) {
|
||||||
|
/* An entry for (key, flag) exists already -- dampen recursion. */
|
||||||
|
entry = NULL;
|
||||||
|
} else {
|
||||||
|
/* Fill in key if we were the first to add entry, then set flag. */
|
||||||
|
if (!entry->key.obj)
|
||||||
|
entry->key = *key;
|
||||||
|
entry->flags |= flag;
|
||||||
|
}
|
||||||
|
*entryp = entry;
|
||||||
|
return JS_TRUE;
|
||||||
|
|
||||||
|
outofmem:
|
||||||
|
JS_ReportOutOfMemory(cx);
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
|
||||||
|
JSResolvingEntry *entry, uint32 generation)
|
||||||
|
{
|
||||||
|
JSDHashTable *table;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear flag from entry->flags and return early if other flags remain.
|
||||||
|
* We must take care to re-lookup entry if the table has changed since
|
||||||
|
* it was found by js_StartResolving.
|
||||||
|
*/
|
||||||
|
table = cx->resolvingTable;
|
||||||
|
if (!entry || table->generation != generation) {
|
||||||
|
entry = (JSResolvingEntry *)
|
||||||
|
JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
|
||||||
|
}
|
||||||
|
JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
|
||||||
|
entry->flags &= ~flag;
|
||||||
|
if (entry->flags)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do a raw remove only if fewer entries were removed than would cause
|
||||||
|
* alpha to be less than .5 (alpha is at most .75). Otherwise, we just
|
||||||
|
* call JS_DHashTableOperate to re-lookup the key and remove its entry,
|
||||||
|
* compressing or shrinking the table as needed.
|
||||||
|
*/
|
||||||
|
if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
|
||||||
|
JS_DHashTableRawRemove(table, &entry->hdr);
|
||||||
|
else
|
||||||
|
JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,
|
ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,
|
||||||
@ -1803,29 +1991,6 @@ JSContext::JSContext(JSRuntime *rt)
|
|||||||
busyArrays()
|
busyArrays()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
JSContext::~JSContext()
|
|
||||||
{
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
JS_ASSERT(!thread);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Free the stuff hanging off of cx. */
|
|
||||||
VOUCH_DOES_NOT_REQUIRE_STACK();
|
|
||||||
JS_FinishArenaPool(&tempPool);
|
|
||||||
JS_FinishArenaPool(®ExpPool);
|
|
||||||
|
|
||||||
if (lastMessage)
|
|
||||||
js_free(lastMessage);
|
|
||||||
|
|
||||||
/* Remove any argument formatters. */
|
|
||||||
JSArgumentFormatMap *map = argumentFormatMap;
|
|
||||||
while (map) {
|
|
||||||
JSArgumentFormatMap *temp = map;
|
|
||||||
map = map->next;
|
|
||||||
js_free(temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
JSContext::resetCompartment()
|
JSContext::resetCompartment()
|
||||||
{
|
{
|
||||||
|
176
js/src/jscntxt.h
176
js/src/jscntxt.h
@ -653,21 +653,9 @@ class StackSpace
|
|||||||
static const size_t STACK_QUOTA = (VALUES_PER_STACK_FRAME + 18) *
|
static const size_t STACK_QUOTA = (VALUES_PER_STACK_FRAME + 18) *
|
||||||
JS_MAX_INLINE_CALL_COUNT;
|
JS_MAX_INLINE_CALL_COUNT;
|
||||||
|
|
||||||
/* The constructor must be called over zeroed memory. */
|
/* Kept as a member of JSThreadData; cannot use constructor/destructor. */
|
||||||
StackSpace() {
|
|
||||||
JS_ASSERT(!base);
|
|
||||||
#ifdef XP_WIN
|
|
||||||
JS_ASSERT(!commitEnd);
|
|
||||||
#endif
|
|
||||||
JS_ASSERT(!end);
|
|
||||||
JS_ASSERT(!currentSegment);
|
|
||||||
JS_ASSERT(!invokeSegment);
|
|
||||||
JS_ASSERT(!invokeFrame);
|
|
||||||
JS_ASSERT(!invokeArgEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init();
|
bool init();
|
||||||
~StackSpace();
|
void finish();
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
template <class T>
|
template <class T>
|
||||||
@ -853,55 +841,6 @@ struct JSPendingProxyOperation {
|
|||||||
JSObject *object;
|
JSObject *object;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace js {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class to detect recursive invocation of Class::resolve hooks and watch
|
|
||||||
* handlers.
|
|
||||||
*
|
|
||||||
* We optimize for the case of just few entries in the resolving set and use a
|
|
||||||
* linked list of AutoResolving instances with the explicit search, not a hash
|
|
||||||
* set, to check for duplicated keys. We assume that cases like recursive
|
|
||||||
* resolving hooks or watch handlers will be dealt with a native stack
|
|
||||||
* recursion checks long before O(N) complexity of adding a new entry to the
|
|
||||||
* list will affect performance.
|
|
||||||
*
|
|
||||||
* The linked list may contain duplicated entries as the user of th class may
|
|
||||||
* not use the alreadyStarted method, see js_InitFunctionAndObjectClasses. It
|
|
||||||
* allows to skip any checks in the destructor making the common case of no
|
|
||||||
* dups in the list faster.
|
|
||||||
*/
|
|
||||||
class AutoResolving {
|
|
||||||
public:
|
|
||||||
enum Kind {
|
|
||||||
LOOKUP,
|
|
||||||
WATCH
|
|
||||||
};
|
|
||||||
|
|
||||||
JS_ALWAYS_INLINE AutoResolving(JSContext *cx, JSObject *obj, jsid id, Kind kind = LOOKUP
|
|
||||||
JS_GUARD_OBJECT_NOTIFIER_PARAM);
|
|
||||||
|
|
||||||
~AutoResolving() {
|
|
||||||
*lastp = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool alreadyStarted() const {
|
|
||||||
return prev && isDuplicate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool isDuplicate() const;
|
|
||||||
|
|
||||||
JSObject *const object;
|
|
||||||
jsid const id;
|
|
||||||
Kind const kind;
|
|
||||||
AutoResolving **const lastp;
|
|
||||||
AutoResolving *const prev;
|
|
||||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* namespace js */
|
|
||||||
|
|
||||||
struct JSThreadData {
|
struct JSThreadData {
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
/* The request depth for this thread. */
|
/* The request depth for this thread. */
|
||||||
@ -963,46 +902,10 @@ struct JSThreadData {
|
|||||||
|
|
||||||
js::ConservativeGCThreadData conservativeGC;
|
js::ConservativeGCThreadData conservativeGC;
|
||||||
|
|
||||||
js::AutoResolving *resolvingList;
|
|
||||||
|
|
||||||
/* The constructor must be called over zeroed memory. */
|
|
||||||
JSThreadData() {
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
JS_ASSERT(!requestDepth);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef JS_TRACER
|
|
||||||
JS_ASSERT(!onTraceCompartment);
|
|
||||||
JS_ASSERT(!recordingCompartment);
|
|
||||||
JS_ASSERT(!profilingCompartment);
|
|
||||||
JS_ASSERT(!maxCodeCacheBytes);
|
|
||||||
#endif
|
|
||||||
JS_ASSERT(!interruptFlags);
|
|
||||||
JS_ASSERT(!waiveGCQuota);
|
|
||||||
JS_ASSERT(!dtoaState);
|
|
||||||
JS_ASSERT(!nativeStackBase);
|
|
||||||
JS_ASSERT(!pendingProxyOperation);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init();
|
bool init();
|
||||||
|
void finish();
|
||||||
~JSThreadData() {
|
void mark(JSTracer *trc);
|
||||||
if (dtoaState)
|
void purge(JSContext *cx);
|
||||||
js_DestroyDtoaState(dtoaState);
|
|
||||||
|
|
||||||
js_FinishGSNCache(&gsnCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mark(JSTracer *trc) {
|
|
||||||
stackSpace.mark(trc);
|
|
||||||
}
|
|
||||||
|
|
||||||
void purge(JSContext *cx) {
|
|
||||||
js_PurgeGSNCache(&gsnCache);
|
|
||||||
|
|
||||||
/* FIXME: bug 506341. */
|
|
||||||
propertyCache.purge(cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This must be called with the GC lock held. */
|
/* This must be called with the GC lock held. */
|
||||||
inline void triggerOperationCallback(JSRuntime *rt);
|
inline void triggerOperationCallback(JSRuntime *rt);
|
||||||
@ -1035,30 +938,6 @@ struct JSThread {
|
|||||||
|
|
||||||
/* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */
|
/* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */
|
||||||
JSThreadData data;
|
JSThreadData data;
|
||||||
|
|
||||||
/* The constructor must be called over zeroed memory. */
|
|
||||||
JSThread(void *id)
|
|
||||||
: id(id)
|
|
||||||
{
|
|
||||||
JS_INIT_CLIST(&contextList);
|
|
||||||
JS_ASSERT(!suspendCount);
|
|
||||||
JS_ASSERT(!checkRequestDepth);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init() {
|
|
||||||
return data.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
~JSThread() {
|
|
||||||
/* The thread must have zero contexts. */
|
|
||||||
JS_ASSERT(JS_CLIST_IS_EMPTY(&contextList));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The conservative GC scanner should be disabled when the thread leaves
|
|
||||||
* the last request.
|
|
||||||
*/
|
|
||||||
JS_ASSERT(!data.conservativeGC.hasStackToScan());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define JS_THREAD_DATA(cx) (&(cx)->thread->data)
|
#define JS_THREAD_DATA(cx) (&(cx)->thread->data)
|
||||||
@ -1178,7 +1057,7 @@ struct JSRuntime {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Compartment that triggered GC. If more than one Compatment need GC,
|
* Compartment that triggered GC. If more than one Compatment need GC,
|
||||||
* gcTriggerCompartment is reset to NULL and a global GC is performed.
|
* gcTriggerCompartment is reset to NULL and a global GC is performed.
|
||||||
*/
|
*/
|
||||||
JSCompartment *gcTriggerCompartment;
|
JSCompartment *gcTriggerCompartment;
|
||||||
|
|
||||||
@ -1583,6 +1462,27 @@ struct JSArgumentFormatMap {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Key and entry types for the JSContext.resolvingTable hash table, typedef'd
|
||||||
|
* here because all consumers need to see these declarations (and not just the
|
||||||
|
* typedef names, as would be the case for an opaque pointer-to-typedef'd-type
|
||||||
|
* declaration), along with cx->resolvingTable.
|
||||||
|
*/
|
||||||
|
typedef struct JSResolvingKey {
|
||||||
|
JSObject *obj;
|
||||||
|
jsid id;
|
||||||
|
} JSResolvingKey;
|
||||||
|
|
||||||
|
typedef struct JSResolvingEntry {
|
||||||
|
JSDHashEntryHdr hdr;
|
||||||
|
JSResolvingKey key;
|
||||||
|
uint32 flags;
|
||||||
|
} JSResolvingEntry;
|
||||||
|
|
||||||
|
#define JSRESFLAG_LOOKUP 0x1 /* resolving id from lookup */
|
||||||
|
#define JSRESFLAG_WATCH 0x2 /* resolving id from watch */
|
||||||
|
#define JSRESOLVE_INFER 0xffff /* infer bits from current bytecode */
|
||||||
|
|
||||||
extern const JSDebugHooks js_NullDebugHooks; /* defined in jsdbgapi.cpp */
|
extern const JSDebugHooks js_NullDebugHooks; /* defined in jsdbgapi.cpp */
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
@ -1716,7 +1616,6 @@ typedef js::HashSet<JSObject *,
|
|||||||
struct JSContext
|
struct JSContext
|
||||||
{
|
{
|
||||||
explicit JSContext(JSRuntime *rt);
|
explicit JSContext(JSRuntime *rt);
|
||||||
~JSContext();
|
|
||||||
|
|
||||||
/* JSRuntime contextList linkage. */
|
/* JSRuntime contextList linkage. */
|
||||||
JSCList link;
|
JSCList link;
|
||||||
@ -1738,6 +1637,14 @@ struct JSContext
|
|||||||
/* Locale specific callbacks for string conversion. */
|
/* Locale specific callbacks for string conversion. */
|
||||||
JSLocaleCallbacks *localeCallbacks;
|
JSLocaleCallbacks *localeCallbacks;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cx->resolvingTable is non-null and non-empty if we are initializing
|
||||||
|
* standard classes lazily, or if we are otherwise recursing indirectly
|
||||||
|
* from js_LookupProperty through a Class.resolve hook. It is used to
|
||||||
|
* limit runaway recursion (see jsapi.c and jsobj.c).
|
||||||
|
*/
|
||||||
|
JSDHashTable *resolvingTable;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* True if generating an error, to prevent runaway recursion.
|
* True if generating an error, to prevent runaway recursion.
|
||||||
* NB: generatingError packs with throwing below.
|
* NB: generatingError packs with throwing below.
|
||||||
@ -1959,7 +1866,7 @@ struct JSContext
|
|||||||
/*
|
/*
|
||||||
* Return:
|
* Return:
|
||||||
* - The override version, if there is an override version.
|
* - The override version, if there is an override version.
|
||||||
* - The newest scripted frame's version, if there is such a frame.
|
* - The newest scripted frame's version, if there is such a frame.
|
||||||
* - The default verion.
|
* - The default verion.
|
||||||
*
|
*
|
||||||
* Note: if this ever shows up in a profile, just add caching!
|
* Note: if this ever shows up in a profile, just add caching!
|
||||||
@ -3069,6 +2976,17 @@ js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp);
|
|||||||
extern JS_FRIEND_API(JSContext *)
|
extern JS_FRIEND_API(JSContext *)
|
||||||
js_NextActiveContext(JSRuntime *, JSContext *);
|
js_NextActiveContext(JSRuntime *, JSContext *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class.resolve and watchpoint recursion damping machinery.
|
||||||
|
*/
|
||||||
|
extern JSBool
|
||||||
|
js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
|
||||||
|
JSResolvingEntry **entryp);
|
||||||
|
|
||||||
|
extern void
|
||||||
|
js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
|
||||||
|
JSResolvingEntry *entry, uint32 generation);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Report an exception, which is currently realized as a printf-style format
|
* Report an exception, which is currently realized as a printf-style format
|
||||||
* string and its arguments.
|
* string and its arguments.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
* vim: set ts=4 sw=4 et tw=78:
|
* vim: set ts=4 sw=4 et tw=78:
|
||||||
*
|
*
|
||||||
* ***** BEGIN LICENSE BLOCK *****
|
* ***** BEGIN LICENSE BLOCK *****
|
||||||
@ -523,19 +523,6 @@ class AutoNamespaceArray : protected AutoGCRooter {
|
|||||||
JSXMLArray array;
|
JSXMLArray array;
|
||||||
};
|
};
|
||||||
|
|
||||||
JS_ALWAYS_INLINE
|
|
||||||
AutoResolving::AutoResolving(JSContext *cx, JSObject *obj, jsid id, Kind kind
|
|
||||||
JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT)
|
|
||||||
: object(obj),
|
|
||||||
id(id),
|
|
||||||
kind(kind),
|
|
||||||
lastp(&JS_THREAD_DATA(cx)->resolvingList),
|
|
||||||
prev(*lastp)
|
|
||||||
{
|
|
||||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
|
||||||
*lastp = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
class CompartmentChecker
|
class CompartmentChecker
|
||||||
{
|
{
|
||||||
|
@ -112,18 +112,6 @@ class HashTable : AllocPolicy
|
|||||||
Ptr(Entry &entry) : entry(&entry) {}
|
Ptr(Entry &entry) : entry(&entry) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*
|
|
||||||
* Any method on Ptr instantiated with the default constructor should
|
|
||||||
* only be called after initializing the Ptr with assignment operator
|
|
||||||
* from another Ptr instance.
|
|
||||||
*/
|
|
||||||
Ptr() {
|
|
||||||
#ifdef DEBUG
|
|
||||||
/* Initialize to some small invalid address. */
|
|
||||||
entry = reinterpret_cast<Entry *>(0xBAD);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool found() const { return entry->isLive(); }
|
bool found() const { return entry->isLive(); }
|
||||||
operator ConvertibleToBool() const { return found() ? &Ptr::nonNull : 0; }
|
operator ConvertibleToBool() const { return found() ? &Ptr::nonNull : 0; }
|
||||||
bool operator==(const Ptr &rhs) const { JS_ASSERT(found() && rhs.found()); return entry == rhs.entry; }
|
bool operator==(const Ptr &rhs) const { JS_ASSERT(found() && rhs.found()); return entry == rhs.entry; }
|
||||||
|
232
js/src/jsobj.cpp
232
js/src/jsobj.cpp
@ -1320,35 +1320,53 @@ static JSBool
|
|||||||
obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
|
obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
|
||||||
jsval *nvp, void *closure)
|
jsval *nvp, void *closure)
|
||||||
{
|
{
|
||||||
JSObject *callable = (JSObject *) closure;
|
JSObject *callable;
|
||||||
|
JSSecurityCallbacks *callbacks;
|
||||||
JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
|
JSStackFrame *caller;
|
||||||
|
JSPrincipals *subject, *watcher;
|
||||||
|
JSResolvingKey key;
|
||||||
|
JSResolvingEntry *entry;
|
||||||
|
uint32 generation;
|
||||||
|
Value argv[3];
|
||||||
|
JSBool ok;
|
||||||
|
|
||||||
|
callable = (JSObject *) closure;
|
||||||
|
|
||||||
|
callbacks = JS_GetSecurityCallbacks(cx);
|
||||||
if (callbacks && callbacks->findObjectPrincipals) {
|
if (callbacks && callbacks->findObjectPrincipals) {
|
||||||
/* Skip over any obj_watch_* frames between us and the real subject. */
|
/* Skip over any obj_watch_* frames between us and the real subject. */
|
||||||
JSStackFrame *caller = js_GetScriptedCaller(cx, NULL);
|
caller = js_GetScriptedCaller(cx, NULL);
|
||||||
if (caller) {
|
if (caller) {
|
||||||
/*
|
/*
|
||||||
* Only call the watch handler if the watcher is allowed to watch
|
* Only call the watch handler if the watcher is allowed to watch
|
||||||
* the currently executing script.
|
* the currently executing script.
|
||||||
*/
|
*/
|
||||||
JSPrincipals *watcher = callbacks->findObjectPrincipals(cx, callable);
|
watcher = callbacks->findObjectPrincipals(cx, callable);
|
||||||
JSPrincipals *subject = js_StackFramePrincipals(cx, caller);
|
subject = js_StackFramePrincipals(cx, caller);
|
||||||
|
|
||||||
if (watcher && subject && !watcher->subsume(watcher, subject)) {
|
if (watcher && subject && !watcher->subsume(watcher, subject)) {
|
||||||
/* Silently don't call the watch handler. */
|
/* Silently don't call the watch handler. */
|
||||||
return true;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Avoid recursion on (obj, id) already being watched. */
|
/* Avoid recursion on (obj, id) already being watched on cx. */
|
||||||
AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
|
key.obj = obj;
|
||||||
if (resolving.alreadyStarted())
|
key.id = id;
|
||||||
return true;
|
if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
|
||||||
|
return JS_FALSE;
|
||||||
Value argv[] = { IdToValue(id), Valueify(old), Valueify(*nvp) };
|
if (!entry)
|
||||||
return ExternalInvoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable),
|
return JS_TRUE;
|
||||||
JS_ARRAY_LENGTH(argv), argv, Valueify(nvp));
|
generation = cx->resolvingTable->generation;
|
||||||
|
|
||||||
|
argv[0] = IdToValue(id);
|
||||||
|
argv[1] = Valueify(old);
|
||||||
|
argv[2] = Valueify(*nvp);
|
||||||
|
ok = ExternalInvoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), 3, argv,
|
||||||
|
Valueify(nvp));
|
||||||
|
js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSBool
|
static JSBool
|
||||||
@ -4161,36 +4179,52 @@ JSBool
|
|||||||
js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
||||||
JSObject **objp)
|
JSObject **objp)
|
||||||
{
|
{
|
||||||
|
JSObject *cobj;
|
||||||
|
JSResolvingKey rkey;
|
||||||
|
JSResolvingEntry *rentry;
|
||||||
|
uint32 generation;
|
||||||
|
JSObjectOp init;
|
||||||
|
Value v;
|
||||||
|
|
||||||
obj = obj->getGlobal();
|
obj = obj->getGlobal();
|
||||||
if (!obj->isGlobal()) {
|
if (!obj->isGlobal()) {
|
||||||
*objp = NULL;
|
*objp = NULL;
|
||||||
return true;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value v = obj->getReservedSlot(key);
|
v = obj->getReservedSlot(key);
|
||||||
if (v.isObject()) {
|
if (v.isObject()) {
|
||||||
*objp = &v.toObject();
|
*objp = &v.toObject();
|
||||||
return true;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoResolving resolving(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]));
|
rkey.obj = obj;
|
||||||
if (resolving.alreadyStarted()) {
|
rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
|
||||||
|
if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry))
|
||||||
|
return JS_FALSE;
|
||||||
|
if (!rentry) {
|
||||||
/* Already caching key in obj -- suppress recursion. */
|
/* Already caching key in obj -- suppress recursion. */
|
||||||
*objp = NULL;
|
*objp = NULL;
|
||||||
return true;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
generation = cx->resolvingTable->generation;
|
||||||
JSObject *cobj = NULL;
|
|
||||||
if (JSObjectOp init = lazy_prototype_init[key]) {
|
JSBool ok = true;
|
||||||
if (!init(cx, obj))
|
cobj = NULL;
|
||||||
return false;
|
init = lazy_prototype_init[key];
|
||||||
v = obj->getReservedSlot(key);
|
if (init) {
|
||||||
if (v.isObject())
|
if (!init(cx, obj)) {
|
||||||
cobj = &v.toObject();
|
ok = JS_FALSE;
|
||||||
|
} else {
|
||||||
|
v = obj->getReservedSlot(key);
|
||||||
|
if (v.isObject())
|
||||||
|
cobj = &v.toObject();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
|
||||||
*objp = cobj;
|
*objp = cobj;
|
||||||
return true;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSBool
|
JSBool
|
||||||
@ -4800,61 +4834,116 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &valu
|
|||||||
JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
|
JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call obj's resolve hook.
|
* Call obj's resolve hook. obj is a native object and the caller holds its
|
||||||
|
* scope lock.
|
||||||
|
*
|
||||||
|
* cx, start, id, and flags are the parameters initially passed to the ongoing
|
||||||
|
* lookup; objp and propp are its out parameters. obj is an object along
|
||||||
|
* start's prototype chain.
|
||||||
|
*
|
||||||
|
* There are four possible outcomes:
|
||||||
|
*
|
||||||
|
* - On failure, report an error or exception, unlock obj, and return false.
|
||||||
|
*
|
||||||
|
* - If we are alrady resolving a property of *curobjp, set *recursedp = true,
|
||||||
|
* unlock obj, and return true.
|
||||||
|
*
|
||||||
|
* - If the resolve hook finds or defines the sought property, set *objp and
|
||||||
|
* *propp appropriately, set *recursedp = false, and return true with *objp's
|
||||||
|
* lock held.
|
||||||
|
*
|
||||||
|
* - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
|
||||||
|
* and return true.
|
||||||
*/
|
*/
|
||||||
static JSBool
|
static JSBool
|
||||||
CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flags,
|
CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flags,
|
||||||
JSObject **objp, JSProperty **propp)
|
JSObject **objp, JSProperty **propp, bool *recursedp)
|
||||||
{
|
{
|
||||||
Class *clasp = obj->getClass();
|
Class *clasp = obj->getClass();
|
||||||
JSResolveOp resolve = clasp->resolve;
|
JSResolveOp resolve = clasp->resolve;
|
||||||
|
|
||||||
JSObject *obj2;
|
/*
|
||||||
|
* Avoid recursion on (obj, id) already being resolved on cx.
|
||||||
|
*
|
||||||
|
* Once we have successfully added an entry for (obj, key) to
|
||||||
|
* cx->resolvingTable, control must go through cleanup: before
|
||||||
|
* returning. But note that JS_DHASH_ADD may find an existing
|
||||||
|
* entry, in which case we bail to suppress runaway recursion.
|
||||||
|
*/
|
||||||
|
JSResolvingKey key = {obj, id};
|
||||||
|
JSResolvingEntry *entry;
|
||||||
|
if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry))
|
||||||
|
return false;
|
||||||
|
if (!entry) {
|
||||||
|
/* Already resolving id in obj -- suppress recursion. */
|
||||||
|
*recursedp = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint32 generation = cx->resolvingTable->generation;
|
||||||
|
*recursedp = false;
|
||||||
|
|
||||||
|
*propp = NULL;
|
||||||
|
|
||||||
|
JSBool ok;
|
||||||
|
const Shape *shape = NULL;
|
||||||
if (clasp->flags & JSCLASS_NEW_RESOLVE) {
|
if (clasp->flags & JSCLASS_NEW_RESOLVE) {
|
||||||
JSNewResolveOp newresolve = (JSNewResolveOp)resolve;
|
JSNewResolveOp newresolve = (JSNewResolveOp)resolve;
|
||||||
if (flags == JSRESOLVE_INFER)
|
if (flags == JSRESOLVE_INFER)
|
||||||
flags = js_InferFlags(cx, 0);
|
flags = js_InferFlags(cx, 0);
|
||||||
obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL;
|
JSObject *obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL;
|
||||||
|
|
||||||
{
|
{
|
||||||
/* Protect id and all atoms from a GC nested in resolve. */
|
/* Protect id and all atoms from a GC nested in resolve. */
|
||||||
AutoKeepAtoms keep(cx->runtime);
|
AutoKeepAtoms keep(cx->runtime);
|
||||||
if (!newresolve(cx, obj, id, flags, &obj2))
|
ok = newresolve(cx, obj, id, flags, &obj2);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
if (!ok)
|
||||||
/*
|
goto cleanup;
|
||||||
* We trust the new style resolve hook to set obj2 to NULL when
|
|
||||||
* the id cannot be resolved. But, when obj2 is not null, we do
|
if (obj2) {
|
||||||
* not assume that id must exist and lookup the property again
|
/* Resolved: lookup id again for backward compatibility. */
|
||||||
* for compatibility.
|
if (!obj2->isNative()) {
|
||||||
*/
|
/* Whoops, newresolve handed back a foreign obj2. */
|
||||||
if (!obj2) {
|
JS_ASSERT(obj2 != obj);
|
||||||
*propp = NULL;
|
ok = obj2->lookupProperty(cx, id, objp, propp);
|
||||||
return true;
|
if (!ok || *propp)
|
||||||
}
|
goto cleanup;
|
||||||
|
} else {
|
||||||
if (!obj2->isNative()) {
|
/*
|
||||||
/* Whoops, newresolve handed back a foreign obj2. */
|
* Require that obj2 not be empty now, as we do for old-style
|
||||||
JS_ASSERT(obj2 != obj);
|
* resolve. If it doesn't, then id was not truly resolved, and
|
||||||
return obj2->lookupProperty(cx, id, objp, propp);
|
* we'll find it in the proto chain, or miss it if obj2's proto
|
||||||
|
* is not on obj's proto chain. That last case is a "too bad!"
|
||||||
|
* case.
|
||||||
|
*/
|
||||||
|
if (!obj2->nativeEmpty())
|
||||||
|
shape = obj2->nativeLookup(id);
|
||||||
|
}
|
||||||
|
if (shape) {
|
||||||
|
JS_ASSERT(!obj2->nativeEmpty());
|
||||||
|
obj = obj2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!resolve(cx, obj, id))
|
/*
|
||||||
return false;
|
* Old resolve always requires id re-lookup if obj is not empty after
|
||||||
obj2 = obj;
|
* resolve returns.
|
||||||
}
|
*/
|
||||||
|
ok = resolve(cx, obj, id);
|
||||||
JS_ASSERT(obj2->isNative());
|
if (!ok)
|
||||||
if (const Shape *shape = obj2->nativeLookup(id)) {
|
goto cleanup;
|
||||||
*objp = obj2;
|
JS_ASSERT(obj->isNative());
|
||||||
*propp = (JSProperty *) shape;
|
if (!obj->nativeEmpty())
|
||||||
return true;
|
shape = obj->nativeLookup(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The id was not resolved. */
|
cleanup:
|
||||||
*propp = NULL;
|
if (ok && shape) {
|
||||||
return true;
|
*objp = obj;
|
||||||
|
*propp = (JSProperty *) shape;
|
||||||
|
}
|
||||||
|
js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JS_ALWAYS_INLINE int
|
static JS_ALWAYS_INLINE int
|
||||||
@ -4878,12 +4967,11 @@ js_LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, uintN fl
|
|||||||
|
|
||||||
/* Try obj's class resolve hook if id was not found in obj's scope. */
|
/* Try obj's class resolve hook if id was not found in obj's scope. */
|
||||||
if (!shape && obj->getClass()->resolve != JS_ResolveStub) {
|
if (!shape && obj->getClass()->resolve != JS_ResolveStub) {
|
||||||
/* Avoid recursion on (obj, id) already being resolved. */
|
bool recursed;
|
||||||
AutoResolving resolving(cx, obj, id);
|
if (!CallResolveOp(cx, start, obj, id, flags, objp, propp, &recursed))
|
||||||
if (resolving.alreadyStarted())
|
|
||||||
break;
|
|
||||||
if (!CallResolveOp(cx, start, obj, id, flags, objp, propp))
|
|
||||||
return -1;
|
return -1;
|
||||||
|
if (recursed)
|
||||||
|
break;
|
||||||
if (*propp) {
|
if (*propp) {
|
||||||
/* Recalculate protoIndex in case it was resolved on some other object. */
|
/* Recalculate protoIndex in case it was resolved on some other object. */
|
||||||
protoIndex = 0;
|
protoIndex = 0;
|
||||||
|
@ -1660,8 +1660,6 @@ extern int
|
|||||||
js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
||||||
JSObject **objp, JSProperty **propp);
|
JSObject **objp, JSProperty **propp);
|
||||||
|
|
||||||
/* Infer resolve flags from the current bytecode. */
|
|
||||||
#define JSRESOLVE_INFER 0xffff
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We cache name lookup results only for the global object or for native
|
* We cache name lookup results only for the global object or for native
|
||||||
|
@ -401,8 +401,6 @@ public:
|
|||||||
, const JSGuardObjectNotifier& _notifier = JSGuardObjectNotifier()
|
, const JSGuardObjectNotifier& _notifier = JSGuardObjectNotifier()
|
||||||
#define JS_GUARD_OBJECT_NOTIFIER_PARAM0 \
|
#define JS_GUARD_OBJECT_NOTIFIER_PARAM0 \
|
||||||
const JSGuardObjectNotifier& _notifier = JSGuardObjectNotifier()
|
const JSGuardObjectNotifier& _notifier = JSGuardObjectNotifier()
|
||||||
#define JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT \
|
|
||||||
, const JSGuardObjectNotifier& _notifier
|
|
||||||
#define JS_GUARD_OBJECT_NOTIFIER_INIT \
|
#define JS_GUARD_OBJECT_NOTIFIER_INIT \
|
||||||
JS_BEGIN_MACRO _mCheckNotUsedAsTemporary.Init(_notifier); JS_END_MACRO
|
JS_BEGIN_MACRO _mCheckNotUsedAsTemporary.Init(_notifier); JS_END_MACRO
|
||||||
|
|
||||||
@ -411,7 +409,6 @@ public:
|
|||||||
#define JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
#define JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
#define JS_GUARD_OBJECT_NOTIFIER_PARAM
|
#define JS_GUARD_OBJECT_NOTIFIER_PARAM
|
||||||
#define JS_GUARD_OBJECT_NOTIFIER_PARAM0
|
#define JS_GUARD_OBJECT_NOTIFIER_PARAM0
|
||||||
#define JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT
|
|
||||||
#define JS_GUARD_OBJECT_NOTIFIER_INIT JS_BEGIN_MACRO JS_END_MACRO
|
#define JS_GUARD_OBJECT_NOTIFIER_INIT JS_BEGIN_MACRO JS_END_MACRO
|
||||||
|
|
||||||
#endif /* !defined(DEBUG) */
|
#endif /* !defined(DEBUG) */
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "jsdbgapi.h"
|
#include "jsdbgapi.h"
|
||||||
#include "jsobj.h"
|
#include "jsobj.h"
|
||||||
|
#include "jsscript.h"
|
||||||
#include "jscntxt.h"
|
#include "jscntxt.h"
|
||||||
|
|
||||||
#include "mozilla/FunctionTimer.h"
|
#include "mozilla/FunctionTimer.h"
|
||||||
|
Loading…
Reference in New Issue
Block a user