mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 14:15:30 +00:00
Backed out changeset 510c42c0d472 - bug 633219
This commit is contained in:
parent
604a770f16
commit
47a2ecace5
@ -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 \
|
||||
testOps.cpp \
|
||||
testPropCache.cpp \
|
||||
testResolveRecursion.cpp \
|
||||
testSameValue.cpp \
|
||||
testScriptObject.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();
|
||||
}
|
||||
|
||||
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 *
|
||||
js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
@ -1395,10 +1426,13 @@ js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj)
|
||||
if (!cx->globalObject)
|
||||
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;
|
||||
AutoResolving resolving1(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Function]));
|
||||
AutoResolving resolving2(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Object]));
|
||||
if (!e1.start(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Function]), JSRESFLAG_LOOKUP) ||
|
||||
!e2.start(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Object]), JSRESFLAG_LOOKUP)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize the function class first so constructors can be made. */
|
||||
if (!js_GetClassPrototype(cx, obj, JSProto_Function, &fun_proto))
|
||||
|
@ -112,6 +112,9 @@ using namespace js::gc;
|
||||
static const size_t ARENA_HEADER_SIZE_HACK = 40;
|
||||
static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK;
|
||||
|
||||
static void
|
||||
FreeContext(JSContext *cx);
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_REQUIRES_STACK bool
|
||||
StackSegment::contains(const JSStackFrame *fp) const
|
||||
@ -167,10 +170,9 @@ StackSpace::init()
|
||||
return true;
|
||||
}
|
||||
|
||||
StackSpace::~StackSpace()
|
||||
void
|
||||
StackSpace::finish()
|
||||
{
|
||||
if (!base)
|
||||
return;
|
||||
#ifdef XP_WIN
|
||||
VirtualFree(base, (commitEnd - base) * sizeof(Value), MEM_DECOMMIT);
|
||||
VirtualFree(base, 0, MEM_RELEASE);
|
||||
@ -495,17 +497,88 @@ AllFramesIter::operator++()
|
||||
bool
|
||||
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();
|
||||
|
||||
#ifdef JS_TRACER
|
||||
/* Set the default size for the code cache to 16MB. */
|
||||
maxCodeCacheBytes = 16 * 1024 * 1024;
|
||||
#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
|
||||
|
||||
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 *
|
||||
js_CurrentThread(JSRuntime *rt)
|
||||
{
|
||||
@ -531,21 +604,14 @@ js_CurrentThread(JSRuntime *rt)
|
||||
thread->data.nativeStackBase = GetNativeStackBase();
|
||||
} else {
|
||||
JS_UNLOCK_GC(rt);
|
||||
|
||||
void *threadMemory = js_calloc(sizeof(JSThread));
|
||||
if (!threadMemory)
|
||||
thread = NewThread(id);
|
||||
if (!thread)
|
||||
return NULL;
|
||||
thread = new (threadMemory) JSThread(id);
|
||||
if (!thread->init()) {
|
||||
js_delete(thread);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JS_LOCK_GC(rt);
|
||||
js_WaitForGC(rt);
|
||||
if (!rt->threads.relookupOrAdd(p, id, thread)) {
|
||||
JS_UNLOCK_GC(rt);
|
||||
js_delete(thread);
|
||||
DestroyThread(thread);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -623,7 +689,8 @@ js_FinishThreads(JSRuntime *rt)
|
||||
return;
|
||||
for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
|
||||
JSThread *thread = r.front().value;
|
||||
js_delete(thread);
|
||||
JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
|
||||
DestroyThread(thread);
|
||||
}
|
||||
rt->threads.clear();
|
||||
#else
|
||||
@ -642,7 +709,8 @@ js_PurgeThreads(JSContext *cx)
|
||||
|
||||
if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
|
||||
JS_ASSERT(cx->thread != thread);
|
||||
js_delete(thread);
|
||||
|
||||
DestroyThread(thread);
|
||||
e.removeFront();
|
||||
} else {
|
||||
thread->data.purge(cx);
|
||||
@ -687,13 +755,13 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||
JS_ASSERT(cx->resolveFlags == 0);
|
||||
|
||||
if (!cx->busyArrays.init()) {
|
||||
js_delete(cx);
|
||||
FreeContext(cx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
if (!js_InitContextThread(cx)) {
|
||||
js_delete(cx);
|
||||
FreeContext(cx);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
@ -1025,7 +1093,41 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||
cx->dstOffsetCache.dumpStats();
|
||||
#endif
|
||||
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 *
|
||||
@ -1056,21 +1158,107 @@ js_NextActiveContext(JSRuntime *rt, JSContext *cx)
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
bool
|
||||
AutoResolving::isDuplicate() const
|
||||
static JSDHashNumber
|
||||
resolving_HashKey(JSDHashTable *table, const void *ptr)
|
||||
{
|
||||
JS_ASSERT(prev);
|
||||
AutoResolving *cursor = prev;
|
||||
do {
|
||||
if (cursor->object == object && cursor->id == id && cursor->kind == kind)
|
||||
return true;
|
||||
} while (!!(cursor = cursor->prev));
|
||||
return false;
|
||||
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
|
||||
|
||||
return (JSDHashNumber(uintptr_t(key->obj)) >> JS_GCTHING_ALIGN) ^ JSID_BITS(key->id);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
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
|
||||
ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,
|
||||
@ -1803,29 +1991,6 @@ JSContext::JSContext(JSRuntime *rt)
|
||||
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
|
||||
JSContext::resetCompartment()
|
||||
{
|
||||
|
172
js/src/jscntxt.h
172
js/src/jscntxt.h
@ -653,21 +653,9 @@ class StackSpace
|
||||
static const size_t STACK_QUOTA = (VALUES_PER_STACK_FRAME + 18) *
|
||||
JS_MAX_INLINE_CALL_COUNT;
|
||||
|
||||
/* The constructor must be called over zeroed memory. */
|
||||
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);
|
||||
}
|
||||
|
||||
/* Kept as a member of JSThreadData; cannot use constructor/destructor. */
|
||||
bool init();
|
||||
~StackSpace();
|
||||
void finish();
|
||||
|
||||
#ifdef DEBUG
|
||||
template <class T>
|
||||
@ -853,55 +841,6 @@ struct JSPendingProxyOperation {
|
||||
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 {
|
||||
#ifdef JS_THREADSAFE
|
||||
/* The request depth for this thread. */
|
||||
@ -963,46 +902,10 @@ struct JSThreadData {
|
||||
|
||||
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();
|
||||
|
||||
~JSThreadData() {
|
||||
if (dtoaState)
|
||||
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);
|
||||
}
|
||||
void finish();
|
||||
void mark(JSTracer *trc);
|
||||
void purge(JSContext *cx);
|
||||
|
||||
/* This must be called with the GC lock held. */
|
||||
inline void triggerOperationCallback(JSRuntime *rt);
|
||||
@ -1035,30 +938,6 @@ struct JSThread {
|
||||
|
||||
/* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */
|
||||
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)
|
||||
@ -1583,6 +1462,27 @@ struct JSArgumentFormatMap {
|
||||
};
|
||||
#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 */
|
||||
|
||||
namespace js {
|
||||
@ -1716,7 +1616,6 @@ typedef js::HashSet<JSObject *,
|
||||
struct JSContext
|
||||
{
|
||||
explicit JSContext(JSRuntime *rt);
|
||||
~JSContext();
|
||||
|
||||
/* JSRuntime contextList linkage. */
|
||||
JSCList link;
|
||||
@ -1738,6 +1637,14 @@ struct JSContext
|
||||
/* Locale specific callbacks for string conversion. */
|
||||
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.
|
||||
* NB: generatingError packs with throwing below.
|
||||
@ -3069,6 +2976,17 @@ js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp);
|
||||
extern JS_FRIEND_API(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
|
||||
* 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:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
@ -523,19 +523,6 @@ class AutoNamespaceArray : protected AutoGCRooter {
|
||||
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
|
||||
class CompartmentChecker
|
||||
{
|
||||
|
@ -112,18 +112,6 @@ class HashTable : AllocPolicy
|
||||
Ptr(Entry &entry) : entry(&entry) {}
|
||||
|
||||
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(); }
|
||||
operator ConvertibleToBool() const { return found() ? &Ptr::nonNull : 0; }
|
||||
bool operator==(const Ptr &rhs) const { JS_ASSERT(found() && rhs.found()); return entry == rhs.entry; }
|
||||
|
204
js/src/jsobj.cpp
204
js/src/jsobj.cpp
@ -1320,35 +1320,53 @@ static JSBool
|
||||
obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
|
||||
jsval *nvp, void *closure)
|
||||
{
|
||||
JSObject *callable = (JSObject *) closure;
|
||||
JSObject *callable;
|
||||
JSSecurityCallbacks *callbacks;
|
||||
JSStackFrame *caller;
|
||||
JSPrincipals *subject, *watcher;
|
||||
JSResolvingKey key;
|
||||
JSResolvingEntry *entry;
|
||||
uint32 generation;
|
||||
Value argv[3];
|
||||
JSBool ok;
|
||||
|
||||
JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
|
||||
callable = (JSObject *) closure;
|
||||
|
||||
callbacks = JS_GetSecurityCallbacks(cx);
|
||||
if (callbacks && callbacks->findObjectPrincipals) {
|
||||
/* 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) {
|
||||
/*
|
||||
* Only call the watch handler if the watcher is allowed to watch
|
||||
* the currently executing script.
|
||||
*/
|
||||
JSPrincipals *watcher = callbacks->findObjectPrincipals(cx, callable);
|
||||
JSPrincipals *subject = js_StackFramePrincipals(cx, caller);
|
||||
watcher = callbacks->findObjectPrincipals(cx, callable);
|
||||
subject = js_StackFramePrincipals(cx, caller);
|
||||
|
||||
if (watcher && subject && !watcher->subsume(watcher, subject)) {
|
||||
/* Silently don't call the watch handler. */
|
||||
return true;
|
||||
return JS_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Avoid recursion on (obj, id) already being watched. */
|
||||
AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
|
||||
if (resolving.alreadyStarted())
|
||||
return true;
|
||||
/* Avoid recursion on (obj, id) already being watched on cx. */
|
||||
key.obj = obj;
|
||||
key.id = id;
|
||||
if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
|
||||
return JS_FALSE;
|
||||
if (!entry)
|
||||
return JS_TRUE;
|
||||
generation = cx->resolvingTable->generation;
|
||||
|
||||
Value argv[] = { IdToValue(id), Valueify(old), Valueify(*nvp) };
|
||||
return ExternalInvoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable),
|
||||
JS_ARRAY_LENGTH(argv), argv, Valueify(nvp));
|
||||
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
|
||||
@ -4161,36 +4179,52 @@ JSBool
|
||||
js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
||||
JSObject **objp)
|
||||
{
|
||||
JSObject *cobj;
|
||||
JSResolvingKey rkey;
|
||||
JSResolvingEntry *rentry;
|
||||
uint32 generation;
|
||||
JSObjectOp init;
|
||||
Value v;
|
||||
|
||||
obj = obj->getGlobal();
|
||||
if (!obj->isGlobal()) {
|
||||
*objp = NULL;
|
||||
return true;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
Value v = obj->getReservedSlot(key);
|
||||
v = obj->getReservedSlot(key);
|
||||
if (v.isObject()) {
|
||||
*objp = &v.toObject();
|
||||
return true;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
AutoResolving resolving(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]));
|
||||
if (resolving.alreadyStarted()) {
|
||||
rkey.obj = obj;
|
||||
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. */
|
||||
*objp = NULL;
|
||||
return true;
|
||||
return JS_TRUE;
|
||||
}
|
||||
generation = cx->resolvingTable->generation;
|
||||
|
||||
JSObject *cobj = NULL;
|
||||
if (JSObjectOp init = lazy_prototype_init[key]) {
|
||||
if (!init(cx, obj))
|
||||
return false;
|
||||
JSBool ok = true;
|
||||
cobj = NULL;
|
||||
init = lazy_prototype_init[key];
|
||||
if (init) {
|
||||
if (!init(cx, obj)) {
|
||||
ok = JS_FALSE;
|
||||
} else {
|
||||
v = obj->getReservedSlot(key);
|
||||
if (v.isObject())
|
||||
cobj = &v.toObject();
|
||||
}
|
||||
}
|
||||
|
||||
js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
|
||||
*objp = cobj;
|
||||
return true;
|
||||
return ok;
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
/*
|
||||
* 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
|
||||
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();
|
||||
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) {
|
||||
JSNewResolveOp newresolve = (JSNewResolveOp)resolve;
|
||||
if (flags == JSRESOLVE_INFER)
|
||||
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. */
|
||||
AutoKeepAtoms keep(cx->runtime);
|
||||
if (!newresolve(cx, obj, id, flags, &obj2))
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* not assume that id must exist and lookup the property again
|
||||
* for compatibility.
|
||||
*/
|
||||
if (!obj2) {
|
||||
*propp = NULL;
|
||||
return true;
|
||||
ok = newresolve(cx, obj, id, flags, &obj2);
|
||||
}
|
||||
if (!ok)
|
||||
goto cleanup;
|
||||
|
||||
if (obj2) {
|
||||
/* Resolved: lookup id again for backward compatibility. */
|
||||
if (!obj2->isNative()) {
|
||||
/* Whoops, newresolve handed back a foreign obj2. */
|
||||
JS_ASSERT(obj2 != obj);
|
||||
return obj2->lookupProperty(cx, id, objp, propp);
|
||||
ok = obj2->lookupProperty(cx, id, objp, propp);
|
||||
if (!ok || *propp)
|
||||
goto cleanup;
|
||||
} else {
|
||||
/*
|
||||
* Require that obj2 not be empty now, as we do for old-style
|
||||
* resolve. If it doesn't, then id was not truly resolved, and
|
||||
* 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 {
|
||||
if (!resolve(cx, obj, id))
|
||||
return false;
|
||||
obj2 = obj;
|
||||
/*
|
||||
* Old resolve always requires id re-lookup if obj is not empty after
|
||||
* resolve returns.
|
||||
*/
|
||||
ok = resolve(cx, obj, id);
|
||||
if (!ok)
|
||||
goto cleanup;
|
||||
JS_ASSERT(obj->isNative());
|
||||
if (!obj->nativeEmpty())
|
||||
shape = obj->nativeLookup(id);
|
||||
}
|
||||
|
||||
JS_ASSERT(obj2->isNative());
|
||||
if (const Shape *shape = obj2->nativeLookup(id)) {
|
||||
*objp = obj2;
|
||||
cleanup:
|
||||
if (ok && shape) {
|
||||
*objp = obj;
|
||||
*propp = (JSProperty *) shape;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* The id was not resolved. */
|
||||
*propp = NULL;
|
||||
return true;
|
||||
js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
|
||||
return ok;
|
||||
}
|
||||
|
||||
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. */
|
||||
if (!shape && obj->getClass()->resolve != JS_ResolveStub) {
|
||||
/* Avoid recursion on (obj, id) already being resolved. */
|
||||
AutoResolving resolving(cx, obj, id);
|
||||
if (resolving.alreadyStarted())
|
||||
break;
|
||||
if (!CallResolveOp(cx, start, obj, id, flags, objp, propp))
|
||||
bool recursed;
|
||||
if (!CallResolveOp(cx, start, obj, id, flags, objp, propp, &recursed))
|
||||
return -1;
|
||||
if (recursed)
|
||||
break;
|
||||
if (*propp) {
|
||||
/* Recalculate protoIndex in case it was resolved on some other object. */
|
||||
protoIndex = 0;
|
||||
|
@ -1660,8 +1660,6 @@ extern int
|
||||
js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
||||
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
|
||||
|
@ -401,8 +401,6 @@ public:
|
||||
, const JSGuardObjectNotifier& _notifier = JSGuardObjectNotifier()
|
||||
#define JS_GUARD_OBJECT_NOTIFIER_PARAM0 \
|
||||
const JSGuardObjectNotifier& _notifier = JSGuardObjectNotifier()
|
||||
#define JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT \
|
||||
, const JSGuardObjectNotifier& _notifier
|
||||
#define JS_GUARD_OBJECT_NOTIFIER_INIT \
|
||||
JS_BEGIN_MACRO _mCheckNotUsedAsTemporary.Init(_notifier); JS_END_MACRO
|
||||
|
||||
@ -411,7 +409,6 @@ public:
|
||||
#define JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
#define JS_GUARD_OBJECT_NOTIFIER_PARAM
|
||||
#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
|
||||
|
||||
#endif /* !defined(DEBUG) */
|
||||
|
@ -62,6 +62,7 @@
|
||||
#include "jsapi.h"
|
||||
#include "jsdbgapi.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsscript.h"
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "mozilla/FunctionTimer.h"
|
||||
|
Loading…
Reference in New Issue
Block a user