This commit is contained in:
Robert Sayre 2010-09-08 10:36:32 -04:00
Родитель 7d6b688fcd ec2e441eff
Коммит 736367f15a
27 изменённых файлов: 769 добавлений и 486 удалений

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

@ -159,7 +159,6 @@ CPPSRCS = \
jsscope.cpp \
jsscript.cpp \
jsstr.cpp \
jstask.cpp \
jstypedarray.cpp \
jsutil.cpp \
jswrapper.cpp \
@ -223,7 +222,6 @@ INSTALLED_HEADERS = \
jsstaticcheck.h \
jsstdint.h \
jsstr.h \
jstask.h \
jstracer.h \
jstypedarray.h \
jstypes.h \

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

@ -80,7 +80,6 @@
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"
#include "jstask.h"
#include "jstracer.h"
#include "jsdbgapi.h"
#include "prmjtime.h"
@ -607,15 +606,6 @@ JSRuntime::init(uint32 maxbytes)
wrapObjectCallback = js::TransparentObjectWrapper;
#ifdef JS_THREADSAFE
gcLock = JS_NEW_LOCK();
if (!gcLock)
return false;
gcDone = JS_NEW_CONDVAR(gcLock);
if (!gcDone)
return false;
requestDone = JS_NEW_CONDVAR(gcLock);
if (!requestDone)
return false;
/* this is asymmetric with JS_ShutDown: */
if (!js_SetupLocks(8, 16))
return false;
@ -1846,7 +1836,7 @@ JS_free(JSContext *cx, void *p)
JS_PUBLIC_API(void)
JS_updateMallocCounter(JSContext *cx, size_t nbytes)
{
return cx->updateMallocCounter(nbytes);
return cx->runtime->updateMallocCounter(nbytes);
}
JS_PUBLIC_API(char *)
@ -2614,7 +2604,7 @@ JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type)
if (!str)
return NULL;
str->initFlat(chars, length);
cx->updateMallocCounter((length + 1) * sizeof(jschar));
cx->runtime->updateMallocCounter((length + 1) * sizeof(jschar));
return str;
}
@ -4825,7 +4815,10 @@ JS_TriggerOperationCallback(JSContext *cx)
JS_PUBLIC_API(void)
JS_TriggerAllOperationCallbacks(JSRuntime *rt)
{
js_TriggerAllOperationCallbacks(rt, JS_FALSE);
#ifdef JS_THREADSAFE
AutoLockGC lock(rt);
#endif
TriggerAllOperationCallbacks(rt);
}
JS_PUBLIC_API(JSBool)

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

@ -68,6 +68,7 @@ ASTDEF(AST_GRAPH_IDX_EXPR, "GraphIndexExpression")
ASTDEF(AST_COMP_EXPR, "ComprehensionExpression")
ASTDEF(AST_GENERATOR_EXPR, "GeneratorExpression")
ASTDEF(AST_YIELD_EXPR, "YieldExpression")
ASTDEF(AST_LET_EXPR, "LetExpression")
ASTDEF(AST_EMPTY_STMT, "EmptyStatement")
ASTDEF(AST_BLOCK_STMT, "BlockStatement")
@ -86,6 +87,7 @@ ASTDEF(AST_RETURN_STMT, "ReturnStatement")
ASTDEF(AST_TRY_STMT, "TryStatement")
ASTDEF(AST_THROW_STMT, "ThrowStatement")
ASTDEF(AST_DEBUGGER_STMT, "DebuggerStatement")
ASTDEF(AST_LET_STMT, "LetStatement")
ASTDEF(AST_CASE, "SwitchCase")
ASTDEF(AST_CATCH, "CatchClause")
@ -100,6 +102,7 @@ ASTDEF(AST_XMLESCAPE, "XMLEscape")
ASTDEF(AST_XMLFILTER, "XMLFilterExpression")
ASTDEF(AST_XMLDEFAULT, "XMLDefaultDeclaration")
ASTDEF(AST_XMLQUAL, "XMLQualifiedIdentifier")
ASTDEF(AST_XMLFUNCQUAL, "XMLFunctionQualifiedIdentifier")
ASTDEF(AST_XMLELEM, "XMLElement")
ASTDEF(AST_XMLTEXT, "XMLText")
ASTDEF(AST_XMLLIST, "XMLList")

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

@ -176,6 +176,7 @@ const char *const js_common_atom_names[] = {
js_configurable_str, /* configurableAtom */
js_writable_str, /* writableAtom */
js_value_str, /* valueAtom */
js_test_str, /* testAtom */
"use strict", /* useStrictAtom */
#if JS_HAS_XML_SUPPORT
@ -265,6 +266,7 @@ const char js_enumerable_str[] = "enumerable";
const char js_configurable_str[] = "configurable";
const char js_writable_str[] = "writable";
const char js_value_str[] = "value";
const char js_test_str[] = "test";
#if JS_HAS_XML_SUPPORT
const char js_etago_str[] = "</";

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

@ -368,6 +368,7 @@ struct JSAtomState
JSAtom *configurableAtom;
JSAtom *writableAtom;
JSAtom *valueAtom;
JSAtom *testAtom;
JSAtom *useStrictAtom;
#if JS_HAS_XML_SUPPORT
@ -524,6 +525,7 @@ extern const char js_enumerable_str[];
extern const char js_configurable_str[];
extern const char js_writable_str[];
extern const char js_value_str[];
extern const char js_test_str[];
/*
* Initialize atom state. Return true on success, false on failure to allocate

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

@ -676,7 +676,6 @@ js_PurgeThreads(JSContext *cx)
e.removeFront();
} else {
thread->data.purge(cx);
thread->gcThreadMallocBytes = JS_GC_THREAD_MALLOC_LIMIT;
}
}
#else
@ -1901,16 +1900,17 @@ js_HandleExecutionInterrupt(JSContext *cx)
return result;
}
namespace js {
void
js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked)
TriggerAllOperationCallbacks(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
Conditionally<AutoLockGC> lockIf(!gcLocked, rt);
#endif
for (ThreadDataIter i(rt); !i.empty(); i.popFront())
i.threadData()->triggerOperationCallback();
}
} /* namespace js */
JSStackFrame *
js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
{
@ -2121,45 +2121,38 @@ JSContext::containingSegment(const JSStackFrame *target)
return NULL;
}
void
JSContext::checkMallocGCPressure(void *p)
JS_FRIEND_API(void)
JSRuntime::onTooMuchMalloc()
{
if (!p) {
js_ReportOutOfMemory(this);
return;
}
#ifdef JS_THREADSAFE
JS_ASSERT(thread);
JS_ASSERT(thread->gcThreadMallocBytes <= 0);
ptrdiff_t n = JS_GC_THREAD_MALLOC_LIMIT - thread->gcThreadMallocBytes;
thread->gcThreadMallocBytes = JS_GC_THREAD_MALLOC_LIMIT;
AutoLockGC lock(runtime);
runtime->gcMallocBytes -= n;
AutoLockGC lock(this);
/*
* Trigger the GC on memory pressure but only if we are inside a request
* and not inside a GC.
* We can be called outside a request and can race against a GC that
* mutates the JSThread set during the sweeping phase.
*/
if (runtime->isGCMallocLimitReached() && thread->requestDepth != 0)
js_WaitForGC(this);
#endif
{
if (!runtime->gcRunning) {
JS_ASSERT(runtime->isGCMallocLimitReached());
runtime->gcMallocBytes = -1;
TriggerGC(this);
}
/*
* Empty the GC free lists to trigger a last-ditch GC when any GC
* thing is allocated later on this thread. This makes unnecessary
* to check for the memory pressure on the fast path of the GC
* allocator. We cannot touch the free lists on other threads as
* their manipulation is not thread-safe.
*/
JS_THREAD_DATA(this)->gcFreeLists.purge();
js_TriggerGC(this, true);
}
}
JS_FRIEND_API(void *)
JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
{
#ifdef JS_THREADSAFE
gcHelperThread.waitBackgroundSweepEnd(this);
if (!p)
p = ::js_malloc(nbytes);
else if (p == reinterpret_cast<void *>(1))
p = ::js_calloc(nbytes);
else
p = ::js_realloc(p, nbytes);
if (p)
return p;
#endif
if (cx)
js_ReportOutOfMemory(cx);
return NULL;
}
/*

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

@ -70,7 +70,6 @@
#include "jsregexp.h"
#include "jsutil.h"
#include "jsarray.h"
#include "jstask.h"
#include "jsvector.h"
#include "prmjtime.h"
@ -187,7 +186,7 @@ class ContextAllocPolicy
};
/* Holds the execution state during trace execution. */
struct TracerState
struct TracerState
{
JSContext* cx; // current VM context handle
double* stackBase; // native stack base
@ -1111,7 +1110,7 @@ struct JSThreadData {
/* State used by dtoa.c. */
DtoaState *dtoaState;
/*
/*
* State used to cache some double-to-string conversions. A stupid
* optimization aimed directly at v8-splay.js, which stupidly converts
* many doubles multiple times in a row.
@ -1175,12 +1174,6 @@ struct JSThread {
/* Indicates that the thread is waiting in ClaimTitle from jslock.cpp. */
JSTitle *titleToShare;
/*
* Thread-local version of JSRuntime.gcMallocBytes to avoid taking
* locks on each JS_malloc.
*/
ptrdiff_t gcThreadMallocBytes;
/*
* This thread is inside js_GC, either waiting until it can start GC, or
* waiting for GC to finish on another thread. This thread holds no locks;
@ -1198,7 +1191,7 @@ struct JSThread {
# ifdef DEBUG
unsigned checkRequestDepth;
# endif
# endif
/* Weak ref, for low-cost sealed title locking */
JSTitle *lockedSealedTitle;
@ -1207,13 +1200,6 @@ struct JSThread {
JSThreadData data;
};
/*
* Only when JSThread::gcThreadMallocBytes exhausts the following limit we
* update JSRuntime::gcMallocBytes.
* .
*/
const size_t JS_GC_THREAD_MALLOC_LIMIT = 1 << 19;
#define JS_THREAD_DATA(cx) (&(cx)->thread->data)
extern JSThread *
@ -1260,7 +1246,7 @@ namespace js {
struct GCPtrHasher
{
typedef void *Lookup;
static HashNumber hash(void *key) {
return HashNumber(uintptr_t(key) >> JS_GCTHING_ZEROBITS);
}
@ -1286,7 +1272,7 @@ typedef js::HashMap<void *,
/* If HashNumber grows, need to change WrapperHasher. */
JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
struct WrapperHasher
{
typedef Value Lookup;
@ -1431,18 +1417,16 @@ struct JSRuntime {
JSGCCallback gcCallback;
private:
/*
* Malloc counter to measure memory pressure for GC scheduling. It runs
* from gcMaxMallocBytes down to zero.
*/
ptrdiff_t gcMallocBytes;
#ifdef JS_THREADSAFE
JSBackgroundThread gcHelperThread;
#endif
volatile ptrdiff_t gcMallocBytes;
public:
js::GCChunkAllocator *gcChunkAllocator;
void setCustomGCChunkAllocator(js::GCChunkAllocator *allocator) {
JS_ASSERT(allocator);
JS_ASSERT(state == JSRTS_DOWN);
@ -1494,6 +1478,8 @@ struct JSRuntime {
uint32 requestCount;
JSThread *gcThread;
js::GCHelperThread gcHelperThread;
/* Lock and owning thread pointer for JS_LOCK_RUNTIME. */
PRLock *rtLock;
#ifdef DEBUG
@ -1744,11 +1730,36 @@ struct JSRuntime {
void setGCTriggerFactor(uint32 factor);
void setGCLastBytes(size_t lastBytes);
void* malloc(size_t bytes) { return ::js_malloc(bytes); }
/*
* Call the system malloc while checking for GC memory pressure and
* reporting OOM error when cx is not null.
*/
void* malloc(size_t bytes, JSContext *cx = NULL) {
updateMallocCounter(bytes);
void *p = ::js_malloc(bytes);
return JS_LIKELY(!!p) ? p : onOutOfMemory(NULL, bytes, cx);
}
void* calloc(size_t bytes) { return ::js_calloc(bytes); }
/*
* Call the system calloc while checking for GC memory pressure and
* reporting OOM error when cx is not null.
*/
void* calloc(size_t bytes, JSContext *cx = NULL) {
updateMallocCounter(bytes);
void *p = ::js_calloc(bytes);
return JS_LIKELY(!!p) ? p : onOutOfMemory(reinterpret_cast<void *>(1), bytes, cx);
}
void* realloc(void* p, size_t bytes) { return ::js_realloc(p, bytes); }
void* realloc(void* p, size_t bytes, JSContext *cx = NULL) {
/*
* For compatibility we do not account for realloc that increases
* previously allocated memory.
*/
if (!p)
updateMallocCounter(bytes);
void *p2 = ::js_realloc(p, bytes);
return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, bytes, cx);
}
void free(void* p) { ::js_free(p); }
@ -1764,6 +1775,38 @@ struct JSRuntime {
gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
resetGCMallocBytes();
}
/*
* Call this after allocating memory held by GC things, to update memory
* pressure counters or report the OOM error if necessary. If oomError and
* cx is not null the function also reports OOM error.
*
* The function must be called outside the GC lock and in case of OOM error
* the caller must ensure that no deadlock possible during OOM reporting.
*/
void updateMallocCounter(size_t nbytes) {
/* We tolerate any thread races when updating gcMallocBytes. */
ptrdiff_t newCount = gcMallocBytes - ptrdiff_t(nbytes);
gcMallocBytes = newCount;
if (JS_UNLIKELY(newCount <= 0))
onTooMuchMalloc();
}
private:
/*
* The function must be called outside the GC lock.
*/
JS_FRIEND_API(void) onTooMuchMalloc();
/*
* This should be called after system malloc/realloc returns NULL to try
* to recove some memory or to report an error. Failures in malloc and
* calloc are signaled by p == null and p == reinterpret_cast<void *>(1).
* Other values of p mean a realloc failure.
*
* The function must be called outside the GC lock.
*/
JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx);
};
/* Common macros to access thread-local caches in JSThread or JSRuntime. */
@ -2119,7 +2162,7 @@ struct JSContext
}
return fp;
}
#ifdef JS_THREADSAFE
JSThread *thread;
unsigned outstandingRequests;/* number of JS_BeginRequest calls
@ -2226,87 +2269,34 @@ struct JSContext
#ifdef JS_THREADSAFE
/*
* The sweep task for this context.
* When non-null JSContext::free delegates the job to the background
* thread.
*/
js::BackgroundSweepTask *gcSweepTask;
js::GCHelperThread *gcBackgroundFree;
#endif
ptrdiff_t &getMallocCounter() {
#ifdef JS_THREADSAFE
return thread->gcThreadMallocBytes;
#else
return runtime->gcMallocBytes;
#endif
}
/*
* Call this after allocating memory held by GC things, to update memory
* pressure counters or report the OOM error if necessary.
*/
inline void updateMallocCounter(void *p, size_t nbytes) {
JS_ASSERT(ptrdiff_t(nbytes) >= 0);
ptrdiff_t &counter = getMallocCounter();
counter -= ptrdiff_t(nbytes);
if (!p || counter <= 0)
checkMallocGCPressure(p);
}
/*
* Call this after successfully allocating memory held by GC things, to
* update memory pressure counters.
*/
inline void updateMallocCounter(size_t nbytes) {
JS_ASSERT(ptrdiff_t(nbytes) >= 0);
ptrdiff_t &counter = getMallocCounter();
counter -= ptrdiff_t(nbytes);
if (counter <= 0) {
/*
* Use 1 as an arbitrary non-null pointer indicating successful
* allocation.
*/
checkMallocGCPressure(reinterpret_cast<void *>(jsuword(1)));
}
}
inline void* malloc(size_t bytes) {
JS_ASSERT(bytes != 0);
void *p = runtime->malloc(bytes);
updateMallocCounter(p, bytes);
return p;
return runtime->malloc(bytes, this);
}
inline void* mallocNoReport(size_t bytes) {
JS_ASSERT(bytes != 0);
void *p = runtime->malloc(bytes);
if (!p)
return NULL;
updateMallocCounter(bytes);
return p;
return runtime->malloc(bytes, NULL);
}
inline void* calloc(size_t bytes) {
JS_ASSERT(bytes != 0);
void *p = runtime->calloc(bytes);
updateMallocCounter(p, bytes);
return p;
return runtime->calloc(bytes, this);
}
inline void* realloc(void* p, size_t bytes) {
void *orig = p;
p = runtime->realloc(p, bytes);
/*
* For compatibility we do not account for realloc that increases
* previously allocated memory.
*/
updateMallocCounter(p, orig ? 0 : bytes);
return p;
return runtime->realloc(p, bytes, this);
}
inline void free(void* p) {
#ifdef JS_THREADSAFE
if (gcSweepTask) {
gcSweepTask->freeLater(p);
if (gcBackgroundFree) {
gcBackgroundFree->freeLater(p);
return;
}
#endif
@ -3247,14 +3237,13 @@ js_InvokeOperationCallback(JSContext *cx);
extern JSBool
js_HandleExecutionInterrupt(JSContext *cx);
namespace js {
#ifndef JS_THREADSAFE
# define js_TriggerAllOperationCallbacks(rt, gcLocked) \
js_TriggerAllOperationCallbacks (rt)
#endif
/* Must be called with GC lock taken. */
void
js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked);
TriggerAllOperationCallbacks(JSRuntime *rt);
} /* namespace js */
extern JSStackFrame *
js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp);
@ -3433,7 +3422,7 @@ class AutoValueVector : private AutoGCRooter
const Value &back() const { return vector.back(); }
friend void AutoGCRooter::trace(JSTracer *trc);
private:
Vector<Value, 8> vector;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
@ -3493,7 +3482,7 @@ class AutoIdVector : private AutoGCRooter
const jsid &back() const { return vector.back(); }
friend void AutoGCRooter::trace(JSTracer *trc);
private:
Vector<jsid, 8> vector;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER

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

@ -78,7 +78,6 @@
#include "jsscript.h"
#include "jsstaticcheck.h"
#include "jsstr.h"
#include "jstask.h"
#include "jstracer.h"
#include "methodjit/MethodJIT.h"
@ -634,8 +633,8 @@ static JSGCArena *
NewGCArena(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
if (!JS_THREAD_DATA(cx)->waiveGCQuota &&
(rt->gcBytes >= rt->gcMaxBytes ||
if (!JS_THREAD_DATA(cx)->waiveGCQuota &&
(rt->gcBytes >= rt->gcMaxBytes ||
rt->gcBytes > GC_HEAP_GROWTH_FACTOR * rt->gcNewArenaTriggerBytes)) {
/*
* FIXME bug 524051 We cannot run a last-ditch GC on trace for now, so
@ -644,7 +643,7 @@ NewGCArena(JSContext *cx)
*/
if (!JS_ON_TRACE(cx))
return NULL;
js_TriggerGC(cx, true);
TriggerGC(rt);
}
if (rt->gcFreeArenaChunks.empty()) {
@ -804,7 +803,7 @@ GetFinalizableTraceKind(size_t thingKind)
JSTRACE_OBJECT, /* FINALIZE_FUNCTION */
#if JS_HAS_XML_SUPPORT /* FINALIZE_XML */
JSTRACE_XML,
#endif
#endif
JSTRACE_STRING, /* FINALIZE_SHORT_STRING */
JSTRACE_STRING, /* FINALIZE_STRING */
JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING0 */
@ -931,7 +930,16 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes)
return false;
#ifdef JS_THREADSAFE
if (!rt->gcHelperThread.init())
rt->gcLock = JS_NEW_LOCK();
if (!rt->gcLock)
return false;
rt->gcDone = JS_NEW_CONDVAR(rt->gcLock);
if (!rt->gcDone)
return false;
rt->requestDone = JS_NEW_CONDVAR(rt->gcLock);
if (!rt->requestDone)
return false;
if (!rt->gcHelperThread.init(rt))
return false;
#endif
@ -1102,7 +1110,7 @@ MarkWordConservatively(JSTracer *trc, jsuword w)
}
#endif
}
#if defined JS_DUMP_CONSERVATIVE_GC_ROOTS || defined JS_GCMETER
if (IS_GC_MARKING_TRACER(trc))
static_cast<GCMarker *>(trc)->conservativeStats.counter[test]++;
@ -1172,7 +1180,7 @@ MarkConservativeStackRoots(JSTracer *trc)
}
#else
MarkThreadDataConservatively(trc, &trc->context->runtime->threadData);
#endif
#endif
}
JS_NEVER_INLINE void
@ -1198,7 +1206,7 @@ static inline void
RecordNativeStackTopForGC(JSContext *cx)
{
ConservativeGCThreadData *ctd = &JS_THREAD_DATA(cx)->conservativeGC;
#ifdef JS_THREADSAFE
/* Record the stack top here only if we are called from a request. */
JS_ASSERT(cx->thread->requestDepth >= ctd->requestThreshold);
@ -1227,7 +1235,7 @@ js_FinishGC(JSRuntime *rt)
#endif
#ifdef JS_THREADSAFE
rt->gcHelperThread.cancel();
rt->gcHelperThread.finish(rt);
#endif
FinishGCArenaLists(rt);
@ -1923,7 +1931,7 @@ Mark(JSTracer *trc, void *thing, uint32 kind)
}
str = iter.next();
} while (str);
} else if (MarkIfUnmarkedGCThing(thing, gcmarker->getMarkColor())) {
/*
* With JS_GC_ASSUME_LOW_C_STACK defined the mark phase of GC
@ -1953,7 +1961,7 @@ void
MarkGCThing(JSTracer *trc, void *thing)
{
JS_ASSERT(size_t(thing) % JS_GCTHING_ALIGN == 0);
if (!thing)
return;
@ -2300,16 +2308,9 @@ MarkRuntime(JSTracer *trc)
#endif
}
} /* namespace js */
void
js_TriggerGC(JSContext *cx, JSBool gcLocked)
TriggerGC(JSRuntime *rt)
{
JSRuntime *rt = cx->runtime;
#ifdef JS_THREADSAFE
JS_ASSERT(cx->thread->requestDepth > 0);
#endif
JS_ASSERT(!rt->gcRunning);
if (rt->gcIsNeeded)
return;
@ -2318,10 +2319,12 @@ js_TriggerGC(JSContext *cx, JSBool gcLocked)
* Trigger the GC when it is safe to call an operation callback on any
* thread.
*/
rt->gcIsNeeded = JS_TRUE;
js_TriggerAllOperationCallbacks(rt, gcLocked);
rt->gcIsNeeded = true;
TriggerAllOperationCallbacks(rt);
}
} /* namespace js */
void
js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data)
{
@ -2591,8 +2594,90 @@ FinalizeArenaList(JSContext *cx, unsigned thingKind)
namespace js {
bool
GCHelperThread::init(JSRuntime *rt)
{
if (!(wakeup = PR_NewCondVar(rt->gcLock)))
return false;
if (!(sweepingDone = PR_NewCondVar(rt->gcLock)))
return false;
thread = PR_CreateThread(PR_USER_THREAD, threadMain, rt, PR_PRIORITY_NORMAL,
PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
return !!thread;
}
void
GCHelperThread::finish(JSRuntime *rt)
{
PRThread *join = NULL;
{
AutoLockGC lock(rt);
if (thread && !shutdown) {
shutdown = true;
PR_NotifyCondVar(wakeup);
join = thread;
}
}
if (join) {
/* PR_DestroyThread is not necessary. */
PR_JoinThread(join);
}
if (wakeup)
PR_DestroyCondVar(wakeup);
if (sweepingDone)
PR_DestroyCondVar(sweepingDone);
}
/* static */
void
GCHelperThread::threadMain(void *arg)
{
JSRuntime *rt = static_cast<JSRuntime *>(arg);
rt->gcHelperThread.threadLoop(rt);
}
void
GCHelperThread::threadLoop(JSRuntime *rt)
{
AutoLockGC lock(rt);
while (!shutdown) {
/*
* Sweeping can be true here on the first iteration if a GC and the
* corresponding startBackgroundSweep call happen before this thread
* has a chance to run.
*/
if (!sweeping)
PR_WaitCondVar(wakeup, PR_INTERVAL_NO_TIMEOUT);
if (sweeping) {
AutoUnlockGC unlock(rt);
doSweep();
}
sweeping = false;
PR_NotifyAllCondVar(sweepingDone);
}
}
void
GCHelperThread::startBackgroundSweep(JSRuntime *rt)
{
/* The caller takes the GC lock. */
JS_ASSERT(!sweeping);
sweeping = true;
PR_NotifyCondVar(wakeup);
}
void
GCHelperThread::waitBackgroundSweepEnd(JSRuntime *rt)
{
AutoLockGC lock(rt);
while (sweeping)
PR_WaitCondVar(sweepingDone, PR_INTERVAL_NO_TIMEOUT);
}
JS_FRIEND_API(void)
BackgroundSweepTask::replenishAndFreeLater(void *ptr)
GCHelperThread::replenishAndFreeLater(void *ptr)
{
JS_ASSERT(freeCursor == freeCursorEnd);
do {
@ -2611,7 +2696,7 @@ BackgroundSweepTask::replenishAndFreeLater(void *ptr)
}
void
BackgroundSweepTask::run()
GCHelperThread::doSweep()
{
if (freeCursor) {
void **array = freeCursorEnd - FREE_ARRAY_LENGTH;
@ -2624,6 +2709,7 @@ BackgroundSweepTask::run()
void **array = *iter;
freeElementsAndArray(array, array + FREE_ARRAY_LENGTH);
}
freeVector.resize(0);
}
}
@ -2638,10 +2724,10 @@ SweepCompartments(JSContext *cx)
JSCompartment **read = rt->compartments.begin();
JSCompartment **end = rt->compartments.end();
JSCompartment **write = read;
/* Delete defaultCompartment only during runtime shutdown */
rt->defaultCompartment->marked = true;
while (read < end) {
JSCompartment *compartment = (*read++);
if (compartment->marked) {
@ -2725,10 +2811,10 @@ MarkAndSweep(JSContext *cx GCTIMER_PARAM)
JS_ASSERT(IS_GC_MARKING_TRACER(&gcmarker));
JS_ASSERT(gcmarker.getMarkColor() == BLACK);
rt->gcMarkingTracer = &gcmarker;
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
GCChunkInfo::fromChunk(r.front())->clearMarkBitmap();
MarkRuntime(&gcmarker);
js_MarkScriptFilenames(rt);
@ -2744,9 +2830,15 @@ MarkAndSweep(JSContext *cx GCTIMER_PARAM)
(void) rt->gcCallback(cx, JSGC_MARK_END);
#ifdef JS_THREADSAFE
JS_ASSERT(!cx->gcSweepTask);
if (!rt->gcHelperThread.busy())
cx->gcSweepTask = new js::BackgroundSweepTask();
/*
* cx->gcBackgroundFree is set if we need several mark-and-sweep loops to
* finish the GC.
*/
if(!cx->gcBackgroundFree) {
/* Wait until the sweeping from the previois GC finishes. */
rt->gcHelperThread.waitBackgroundSweepEnd(rt);
cx->gcBackgroundFree = &rt->gcHelperThread;
}
#endif
/*
@ -2806,7 +2898,7 @@ MarkAndSweep(JSContext *cx GCTIMER_PARAM)
++i) {
FinalizeArenaList<JSString, FinalizeExternalString>(cx, i);
}
rt->gcNewArenaTriggerBytes = rt->gcBytes < GC_ARENA_ALLOCATION_TRIGGER ?
GC_ARENA_ALLOCATION_TRIGGER :
rt->gcBytes;
@ -2839,13 +2931,6 @@ MarkAndSweep(JSContext *cx GCTIMER_PARAM)
FreeGCChunks(rt);
TIMESTAMP(sweepDestroyEnd);
#ifdef JS_THREADSAFE
if (cx->gcSweepTask) {
rt->gcHelperThread.schedule(cx->gcSweepTask);
cx->gcSweepTask = NULL;
}
#endif
if (rt->gcCallback)
(void) rt->gcCallback(cx, JSGC_FINALIZE_END);
#ifdef DEBUG_srcnotesize
@ -3067,13 +3152,16 @@ GCUntilDone(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
#endif
return;
}
AutoGCSession gcsession(cx);
METER(rt->gcStats.poke++);
bool firstRun = true;
rt->gcMarkAndSweep = true;
#ifdef JS_THREADSAFE
JS_ASSERT(!cx->gcBackgroundFree);
#endif
do {
rt->gcPoke = false;
@ -3091,6 +3179,12 @@ GCUntilDone(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
// - a finalizer called js_RemoveRoot or js_UnlockGCThingRT.
} while (rt->gcPoke);
#ifdef JS_THREADSAFE
JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
cx->gcBackgroundFree = NULL;
rt->gcHelperThread.startBackgroundSweep(rt);
#endif
rt->gcMarkAndSweep = false;
rt->gcRegenShapes = false;
rt->setGCLastBytes(rt->gcBytes);
@ -3204,7 +3298,7 @@ TraceRuntime(JSTracer *trc)
JSContext *cx = trc->context;
JSRuntime *rt = cx->runtime;
AutoLockGC lock(rt);
if (rt->gcThread != cx->thread) {
AutoGCSession gcsession(cx);
AutoUnlockGC unlock(rt);

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

@ -51,7 +51,6 @@
#include "jsbit.h"
#include "jsgcchunk.h"
#include "jsutil.h"
#include "jstask.h"
#include "jsvector.h"
#include "jsversion.h"
#include "jsobj.h"
@ -188,17 +187,11 @@ TraceRuntime(JSTracer *trc);
extern JS_REQUIRES_STACK JS_FRIEND_API(void)
MarkContext(JSTracer *trc, JSContext *acx);
} /* namespace js */
/*
* Schedule the GC call at a later safe point.
*/
#ifndef JS_THREADSAFE
# define js_TriggerGC(cx, gcLocked) js_TriggerGC (cx)
#endif
/* Must be called with GC lock taken. */
extern void
js_TriggerGC(JSContext *cx, JSBool gcLocked);
TriggerGC(JSRuntime *rt);
} /* namespace js */
/*
* Kinds of js_GC invocation.
@ -365,18 +358,24 @@ namespace js {
/*
* During the finalization we do not free immediately. Rather we add the
* corresponding pointers to a buffer which we later release on the
* background thread.
* corresponding pointers to a buffer which we later release on a separated
* thread.
*
* The buffer is implemented as a vector of 64K arrays of pointers, not as a
* simple vector, to avoid realloc calls during the vector growth and to not
* bloat the binary size of the inlined freeLater method. Any OOM during
* buffer growth results in the pointer being freed immediately.
*/
class BackgroundSweepTask : public JSBackgroundTask {
class GCHelperThread {
static const size_t FREE_ARRAY_SIZE = size_t(1) << 16;
static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *);
PRThread* thread;
PRCondVar* wakeup;
PRCondVar* sweepingDone;
bool shutdown;
bool sweeping;
Vector<void **, 16, js::SystemAllocPolicy> freeVector;
void **freeCursor;
void **freeCursorEnd;
@ -391,18 +390,37 @@ class BackgroundSweepTask : public JSBackgroundTask {
js_free(array);
}
public:
BackgroundSweepTask()
: freeCursor(NULL), freeCursorEnd(NULL) { }
static void threadMain(void* arg);
void threadLoop(JSRuntime *rt);
void doSweep();
public:
GCHelperThread()
: thread(NULL),
wakeup(NULL),
sweepingDone(NULL),
shutdown(false),
sweeping(false),
freeCursor(NULL),
freeCursorEnd(NULL) { }
bool init(JSRuntime *rt);
void finish(JSRuntime *rt);
/* Must be called with GC lock taken. */
void startBackgroundSweep(JSRuntime *rt);
/* Must be called outside the GC lock. */
void waitBackgroundSweepEnd(JSRuntime *rt);
void freeLater(void *ptr) {
JS_ASSERT(!sweeping);
if (freeCursor != freeCursorEnd)
*freeCursor++ = ptr;
else
replenishAndFreeLater(ptr);
}
virtual void run();
};
#endif /* JS_THREADSAFE */
@ -462,7 +480,7 @@ struct ConservativeGCThreadData {
nativeStackTop = NULL;
}
#endif
bool hasStackToScan() const {
return !!nativeStackTop;
}
@ -483,7 +501,7 @@ struct GCMarker : public JSTracer {
#if defined(JS_DUMP_CONSERVATIVE_GC_ROOTS) || defined(JS_GCMETER)
ConservativeGCStats conservativeStats;
#endif
#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
struct ConservativeRoot { void *thing; uint32 traceKind; };
Vector<ConservativeRoot, 0, SystemAllocPolicy> conservativeRoots;

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

@ -6320,7 +6320,7 @@ BEGIN_CASE(JSOP_XMLNAME)
goto error;
regs.sp[-1] = rval;
if (op == JSOP_CALLXMLNAME)
PUSH_OBJECT(*obj);
SLOW_PUSH_THISV(cx, obj);
}
END_CASE(JSOP_XMLNAME)

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

@ -514,7 +514,7 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp)
if (!chars) {
/* If outermost, allocate 4 + 1 for "({})" and the terminator. */
chars = (jschar *) js_malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
chars = (jschar *) cx->runtime->malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
nchars = 0;
if (!chars)
goto error;

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

@ -376,6 +376,8 @@ class NodeBuilder
bool debuggerStatement(TokenPos *pos, Value *dst);
bool letStatement(NodeVector &head, Value stmt, TokenPos *pos, Value *dst);
/*
* expressions
*/
@ -421,6 +423,8 @@ class NodeBuilder
bool graphIndexExpression(jsint idx, TokenPos *pos, Value *dst);
bool letExpression(NodeVector &head, Value expr, TokenPos *pos, Value *dst);
/*
* declarations
*/
@ -453,6 +457,8 @@ class NodeBuilder
bool xmlQualifiedIdentifier(Value left, Value right, bool computed, TokenPos *pos, Value *dst);
bool xmlFunctionQualifiedIdentifier(Value right, bool computed, TokenPos *pos, Value *dst);
bool xmlElement(NodeVector &elts, TokenPos *pos, Value *dst);
bool xmlText(Value text, TokenPos *pos, Value *dst);
@ -491,8 +497,9 @@ NodeBuilder::newNode(ASTType type, TokenPos *pos, JSObject **dst)
if (!node ||
!setNodeLoc(node, pos) ||
!atomValue(nodeTypeNames[type], &tv) ||
!setProperty(node, "type", tv))
!setProperty(node, "type", tv)) {
return false;
}
*dst = node;
return true;
@ -940,6 +947,30 @@ NodeBuilder::graphIndexExpression(jsint idx, TokenPos *pos, Value *dst)
return newNode(AST_GRAPH_IDX_EXPR, pos, "index", NumberValue(idx), dst);
}
bool
NodeBuilder::letExpression(NodeVector &head, Value expr, TokenPos *pos, Value *dst)
{
Value array;
return newArray(head, &array) &&
newNode(AST_LET_EXPR, pos,
"head", array,
"body", expr,
dst);
}
bool
NodeBuilder::letStatement(NodeVector &head, Value stmt, TokenPos *pos, Value *dst)
{
Value array;
return newArray(head, &array) &&
newNode(AST_LET_STMT, pos,
"head", array,
"body", stmt,
dst);
}
bool
NodeBuilder::variableDeclaration(NodeVector &elts, VarDeclKind kind, TokenPos *pos, Value *dst)
{
@ -1059,6 +1090,15 @@ NodeBuilder::xmlAttributeSelector(Value expr, TokenPos *pos, Value *dst)
return newNode(AST_XMLATTR_SEL, pos, "attribute", expr, dst);
}
bool
NodeBuilder::xmlFunctionQualifiedIdentifier(Value right, bool computed, TokenPos *pos, Value *dst)
{
return newNode(AST_XMLFUNCQUAL, pos,
"right", right,
"computed", BooleanValue(computed),
dst);
}
bool
NodeBuilder::xmlQualifiedIdentifier(Value left, Value right, bool computed,
TokenPos *pos, Value *dst)
@ -1185,6 +1225,7 @@ class ASTSerializer
bool declaration(JSParseNode *pn, Value *dst);
bool variableDeclaration(JSParseNode *pn, bool let, Value *dst);
bool variableDeclarator(JSParseNode *pn, VarDeclKind *pkind, Value *dst);
bool letHead(JSParseNode *pn, NodeVector &dtors);
bool optStatement(JSParseNode *pn, Value *dst) {
if (!pn) {
@ -1392,7 +1433,7 @@ ASTSerializer::statements(JSParseNode *pn, NodeVector &elts)
Value elt;
if (!sourceElement(next, &elt))
return false;
(void)elts.append(elt); /* space check above */
JS_ALWAYS_TRUE(elts.append(elt)); /* space check above */
}
return true;
@ -1408,7 +1449,7 @@ ASTSerializer::expressions(JSParseNode *pn, NodeVector &elts)
Value elt;
if (!expression(next, &elt))
return false;
(void)elts.append(elt); /* space check above */
JS_ALWAYS_TRUE(elts.append(elt)); /* space check above */
}
return true;
@ -1424,7 +1465,7 @@ ASTSerializer::xmls(JSParseNode *pn, NodeVector &elts)
Value elt;
if (!xml(next, &elt))
return false;
(void)elts.append(elt); /* space check above */
JS_ALWAYS_TRUE(elts.append(elt)); /* space check above */
}
return true;
@ -1505,7 +1546,7 @@ ASTSerializer::variableDeclaration(JSParseNode *pn, bool let, Value *dst)
Value child;
if (!variableDeclarator(next, &kind, &child))
return false;
(void)dtors.append(child); /* space check above */
JS_ALWAYS_TRUE(dtors.append(child)); /* space check above */
}
return builder.variableDeclaration(dtors, kind, &pn->pn_pos, dst);
@ -1535,6 +1576,28 @@ ASTSerializer::variableDeclarator(JSParseNode *pn, VarDeclKind *pkind, Value *ds
builder.variableDeclarator(left, right, &pn->pn_pos, dst);
}
bool
ASTSerializer::letHead(JSParseNode *pn, NodeVector &dtors)
{
if (!dtors.reserve(pn->pn_count))
return false;
VarDeclKind kind = VARDECL_LET_HEAD;
for (JSParseNode *next = pn->pn_head; next; next = next->pn_next) {
Value child;
/*
* Unlike in |variableDeclaration|, this does not update |kind|; since let-heads do
* not contain const declarations, declarators should never have PND_CONST set.
*/
if (!variableDeclarator(next, &kind, &child))
return false;
JS_ALWAYS_TRUE(dtors.append(child)); /* space check above */
}
return true;
}
bool
ASTSerializer::switchCase(JSParseNode *pn, Value *dst)
{
@ -1574,7 +1637,7 @@ ASTSerializer::switchStatement(JSParseNode *pn, Value *dst)
Value child;
if (!switchCase(next, &child))
return false;
(void)cases.append(child); /* space check above */
JS_ALWAYS_TRUE(cases.append(child)); /* space check above */
}
return builder.switchStatement(disc, cases, lexical, &pn->pn_pos, dst);
@ -1607,7 +1670,7 @@ ASTSerializer::tryStatement(JSParseNode *pn, Value *dst)
Value clause;
if (!catchClause(next->pn_expr, &clause))
return false;
(void)clauses.append(clause); /* space check above */
JS_ALWAYS_TRUE(clauses.append(clause)); /* space check above */
}
}
@ -1640,6 +1703,10 @@ ASTSerializer::statement(JSParseNode *pn, Value *dst)
case TOK_LET:
return declaration(pn, dst);
case TOK_NAME:
LOCAL_ASSERT(pn->pn_used);
return statement(pn->pn_lexdef, dst);
case TOK_SEMI:
if (pn->pn_kid) {
Value expr;
@ -1650,6 +1717,15 @@ ASTSerializer::statement(JSParseNode *pn, Value *dst)
case TOK_LEXICALSCOPE:
pn = pn->pn_expr;
if (PN_TYPE(pn) == TOK_LET) {
NodeVector dtors(cx);
Value stmt;
return letHead(pn->pn_left, dtors) &&
statement(pn->pn_right, &stmt) &&
builder.letStatement(dtors, stmt, &pn->pn_pos, dst);
}
if (PN_TYPE(pn) != TOK_LC)
return statement(pn, dst);
/* FALL THROUGH */
@ -1724,6 +1800,59 @@ ASTSerializer::statement(JSParseNode *pn, Value *dst)
builder.forStatement(init, test, update, stmt, &pn->pn_pos, dst);
}
/* Synthesized by the parser when a for-in loop contains a variable initializer. */
case TOK_SEQ:
{
LOCAL_ASSERT(pn->pn_count == 2);
JSParseNode *prelude = pn->pn_head;
JSParseNode *body = prelude->pn_next;
LOCAL_ASSERT((PN_TYPE(prelude) == TOK_VAR && PN_TYPE(body) == TOK_FOR) ||
(PN_TYPE(prelude) == TOK_SEMI && PN_TYPE(body) == TOK_LEXICALSCOPE));
JSParseNode *loop;
Value var;
if (PN_TYPE(prelude) == TOK_VAR) {
loop = body;
if (!variableDeclaration(prelude, false, &var))
return false;
} else {
loop = body->pn_expr;
LOCAL_ASSERT(PN_TYPE(loop->pn_left) == TOK_IN &&
PN_TYPE(loop->pn_left->pn_left) == TOK_LET &&
loop->pn_left->pn_left->pn_count == 1);
JSParseNode *pnlet = loop->pn_left->pn_left;
VarDeclKind kind = VARDECL_LET;
NodeVector dtors(cx);
Value patt, init, dtor;
if (!pattern(pnlet->pn_head, &kind, &patt) ||
!expression(prelude->pn_kid, &init) ||
!builder.variableDeclarator(patt, init, &pnlet->pn_pos, &dtor) ||
!dtors.append(dtor) ||
!builder.variableDeclaration(dtors, kind, &pnlet->pn_pos, &var)) {
return false;
}
}
JSParseNode *head = loop->pn_left;
JS_ASSERT(PN_TYPE(head) == TOK_IN);
bool isForEach = loop->pn_iflags & JSITER_FOREACH;
Value expr, stmt;
return expression(head->pn_right, &expr) &&
statement(loop->pn_right, &stmt) &&
builder.forInStatement(var, expr, stmt, isForEach, &pn->pn_pos, dst);
}
case TOK_BREAK:
case TOK_CONTINUE:
{
@ -1761,11 +1890,11 @@ ASTSerializer::statement(JSParseNode *pn, Value *dst)
#if JS_HAS_XML_SUPPORT
case TOK_DEFAULT:
{
LOCAL_ASSERT(pn->pn_arity == PN_UNARY && PN_TYPE(pn->pn_kid) == TOK_STRING);
LOCAL_ASSERT(pn->pn_arity == PN_UNARY);
Value ns;
return literal(pn->pn_kid, &ns) &&
return expression(pn->pn_kid, &ns) &&
builder.xmlDefaultNamespace(ns, &pn->pn_pos, dst);
}
#endif
@ -1793,7 +1922,7 @@ ASTSerializer::leftAssociate(JSParseNode *pn, Value *dst)
return false;
for (JSParseNode *next = pn->pn_head; next; next = next->pn_next) {
(void)list.append(next); /* space check above */
JS_ALWAYS_TRUE(list.append(next)); /* space check above */
}
TokenKind tk = PN_TYPE(pn);
@ -1877,8 +2006,7 @@ ASTSerializer::comprehension(JSParseNode *pn, Value *dst)
JSParseNode *next = pn;
while (PN_TYPE(next) == TOK_FOR) {
Value block;
if (!comprehensionBlock(next, &block) ||
!blocks.append(block))
if (!comprehensionBlock(next, &block) || !blocks.append(block))
return false;
next = next->pn_right;
}
@ -1913,8 +2041,7 @@ ASTSerializer::generatorExpression(JSParseNode *pn, Value *dst)
JSParseNode *next = pn;
while (PN_TYPE(next) == TOK_FOR) {
Value block;
if (!comprehensionBlock(next, &block) ||
!blocks.append(block))
if (!comprehensionBlock(next, &block) || !blocks.append(block))
return false;
next = next->pn_right;
}
@ -2022,7 +2149,9 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
case TOK_DELETE:
case TOK_UNARYOP:
#if JS_HAS_XML_SUPPORT
if (PN_OP(pn) == JSOP_XMLNAME)
if (PN_OP(pn) == JSOP_XMLNAME ||
PN_OP(pn) == JSOP_SETXMLNAME ||
PN_OP(pn) == JSOP_BINDXMLNAME)
return expression(pn->pn_kid, dst);
#endif
@ -2057,7 +2186,7 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
Value arg;
if (!expression(next, &arg))
return false;
(void)args.append(arg); /* space check above */
JS_ALWAYS_TRUE(args.append(arg)); /* space check above */
}
return PN_TYPE(pn) == TOK_NEW
@ -2089,12 +2218,12 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
for (JSParseNode *next = pn->pn_head; next; next = next->pn_next) {
if (PN_TYPE(next) == TOK_COMMA) {
(void)elts.append(MagicValue(JS_SERIALIZE_NO_NODE)); /* space check above */
JS_ALWAYS_TRUE(elts.append(MagicValue(JS_SERIALIZE_NO_NODE))); /* space check above */
} else {
Value expr;
if (!expression(next, &expr))
return false;
(void)elts.append(expr); /* space check above */
JS_ALWAYS_TRUE(elts.append(expr)); /* space check above */
}
}
@ -2111,7 +2240,7 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
Value prop;
if (!property(next, &prop))
return false;
(void)elts.append(prop); /* space check above */
JS_ALWAYS_TRUE(elts.append(prop)); /* space check above */
}
return builder.objectExpression(elts, &pn->pn_pos, dst);
@ -2150,25 +2279,50 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
return comprehension(pn->pn_head->pn_expr, dst);
case TOK_LEXICALSCOPE:
{
pn = pn->pn_expr;
NodeVector dtors(cx);
Value expr;
return letHead(pn->pn_left, dtors) &&
expression(pn->pn_right, &expr) &&
builder.letExpression(dtors, expr, &pn->pn_pos, dst);
}
#ifdef JS_HAS_XML_SUPPORT
case TOK_ANYNAME:
return builder.xmlAnyName(&pn->pn_pos, dst);
case TOK_DBLCOLON:
{
Value left, right;
Value right;
LOCAL_ASSERT(pn->pn_arity == PN_NAME || pn->pn_arity == PN_BINARY);
bool computed = pn->pn_arity == PN_BINARY;
JSParseNode *pnleft;
bool computed;
return (computed
? (expression(pn->pn_left, &left) &&
expression(pn->pn_right, &right))
: (expression(pn->pn_expr, &left) &&
identifier(pn->pn_atom, NULL, &right))) &&
builder.xmlQualifiedIdentifier(left, right, computed,
&pn->pn_pos, dst);
if (pn->pn_arity == PN_BINARY) {
computed = true;
pnleft = pn->pn_left;
if (!expression(pn->pn_right, &right))
return false;
} else {
JS_ASSERT(pn->pn_arity == PN_NAME);
computed = false;
pnleft = pn->pn_expr;
if (!identifier(pn->pn_atom, NULL, &right))
return false;
}
if (PN_TYPE(pnleft) == TOK_FUNCTION)
return builder.xmlFunctionQualifiedIdentifier(right, computed, &pn->pn_pos, dst);
Value left;
return expression(pnleft, &left) &&
builder.xmlQualifiedIdentifier(left, right, computed, &pn->pn_pos, dst);
}
case TOK_AT:
@ -2380,12 +2534,12 @@ ASTSerializer::arrayPattern(JSParseNode *pn, VarDeclKind *pkind, Value *dst)
for (JSParseNode *next = pn->pn_head; next; next = next->pn_next) {
if (PN_TYPE(next) == TOK_COMMA) {
(void)elts.append(MagicValue(JS_SERIALIZE_NO_NODE)); /* space check above */
JS_ALWAYS_TRUE(elts.append(MagicValue(JS_SERIALIZE_NO_NODE))); /* space check above */
} else {
Value patt;
if (!pattern(next, pkind, &patt))
return false;
(void)elts.append(patt); /* space check above */
JS_ALWAYS_TRUE(elts.append(patt)); /* space check above */
}
}
@ -2407,10 +2561,11 @@ ASTSerializer::objectPattern(JSParseNode *pn, VarDeclKind *pkind, Value *dst)
Value key, patt, prop;
if (!propertyName(next->pn_left, &key) ||
!pattern(next->pn_right, pkind, &patt) ||
!builder.propertyPattern(key, patt, &next->pn_pos, &prop))
!builder.propertyPattern(key, patt, &next->pn_pos, &prop)) {
return false;
}
(void)elts.append(prop); /* space check above */
JS_ALWAYS_TRUE(elts.append(prop)); /* space check above */
}
return builder.objectPattern(elts, &pn->pn_pos, dst);
@ -2561,16 +2716,25 @@ ASTSerializer::functionArgs(JSParseNode *pn, JSParseNode *pnargs, JSParseNode *p
* both.
*/
while ((arg && arg != pnbody) || destruct) {
if (arg && arg != pnbody && arg->frameSlot() == i) {
if (!identifier(arg, &node) ||
!args.append(node))
return false;
arg = arg->pn_next;
} else if (destruct && destruct->pn_right->frameSlot() == i) {
if (!pattern(destruct->pn_left, NULL, &node) ||
!args.append(node))
if (destruct && destruct->pn_right->frameSlot() == i) {
if (!pattern(destruct->pn_left, NULL, &node) || !args.append(node))
return false;
destruct = destruct->pn_next;
} else if (arg && arg != pnbody) {
/*
* We don't check that arg->frameSlot() == i since we
* can't call that method if the arg def has been turned
* into a use, e.g.:
*
* function(a) { function a() { } }
*
* There's no other way to ask a non-destructuring arg its
* index in the formals list, so we rely on the ability to
* ask destructuring args their index above.
*/
if (!identifier(arg, &node) || !args.append(node))
return false;
arg = arg->pn_next;
} else {
LOCAL_NOT_REACHED("missing function argument");
}
@ -2588,8 +2752,7 @@ ASTSerializer::functionBody(JSParseNode *pn, TokenPos *pos, Value *dst)
/* We aren't sure how many elements there are up front, so we'll check each append. */
for (JSParseNode *next = pn; next; next = next->pn_next) {
Value child;
if (!sourceElement(next, &child) ||
!elts.append(child))
if (!sourceElement(next, &child) || !elts.append(child))
return false;
}
@ -2681,8 +2844,9 @@ js_InitReflectClass(JSContext *cx, JSObject *obj)
return NULL;
if (!JS_DefineProperty(cx, obj, js_Reflect_str, OBJECT_TO_JSVAL(Reflect),
JS_PropertyStub, JS_PropertyStub, 0))
JS_PropertyStub, JS_PropertyStub, 0)) {
return NULL;
}
if (!JS_DefineFunctions(cx, Reflect, static_methods))
return NULL;

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

@ -111,6 +111,7 @@ enum VarDeclKind {
VARDECL_VAR = 0,
VARDECL_CONST,
VARDECL_LET,
VARDECL_LET_HEAD,
VARDECL_LIMIT
};

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

@ -86,7 +86,11 @@ js_GenerateShape(JSContext *cx, bool gcLocked)
*/
rt->shapeGen = SHAPE_OVERFLOW_BIT;
shape = SHAPE_OVERFLOW_BIT;
js_TriggerGC(cx, gcLocked);
#ifdef JS_THREADSAFE
Conditionally<AutoLockGC> lockIf(!gcLocked, rt);
#endif
TriggerGC(rt);
}
return shape;
}
@ -158,7 +162,7 @@ PropertyTable::init(JSContext *cx, Shape *lastProp)
METER(tableAllocFails);
return false;
}
cx->updateMallocCounter(JS_BIT(sizeLog2) * sizeof(Shape *));
cx->runtime->updateMallocCounter(JS_BIT(sizeLog2) * sizeof(Shape *));
hashShift = JS_DHASH_BITS - sizeLog2;
for (Shape::Range r = lastProp->all(); !r.empty(); r.popFront()) {
@ -402,7 +406,7 @@ PropertyTable::change(JSContext *cx, int change)
entries = newTable;
/* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */
cx->updateMallocCounter(nbytes);
cx->runtime->updateMallocCounter(nbytes);
/* Copy only live entries, leaving removed and free ones behind. */
for (oldspp = oldTable; oldsize != 0; oldspp++) {

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

@ -1,130 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99 ft=cpp:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9.1 code, released
* June 30, 2009.
*
* The Initial Developer of the Original Code is
* Andreas Gal <gal@mozilla.com>
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "jstask.h"
#ifdef JS_THREADSAFE
static void start(void* arg) {
((JSBackgroundThread*)arg)->work();
}
JSBackgroundThread::JSBackgroundThread()
: thread(NULL), stack(NULL), lock(NULL), wakeup(NULL), shutdown(false)
{
}
JSBackgroundThread::~JSBackgroundThread()
{
if (wakeup)
PR_DestroyCondVar(wakeup);
if (lock)
PR_DestroyLock(lock);
/* PR_DestroyThread is not necessary. */
}
bool
JSBackgroundThread::init()
{
if (!(lock = PR_NewLock()))
return false;
if (!(wakeup = PR_NewCondVar(lock)))
return false;
thread = PR_CreateThread(PR_USER_THREAD, start, this, PR_PRIORITY_LOW,
PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
return !!thread;
}
void
JSBackgroundThread::cancel()
{
/* We allow to call the cancel method after failed init. */
if (!thread)
return;
PR_Lock(lock);
if (shutdown) {
PR_Unlock(lock);
return;
}
shutdown = true;
PR_NotifyCondVar(wakeup);
PR_Unlock(lock);
PR_JoinThread(thread);
}
void
JSBackgroundThread::work()
{
PR_Lock(lock);
while (!shutdown) {
PR_WaitCondVar(wakeup, PR_INTERVAL_NO_TIMEOUT);
JSBackgroundTask* t;
while ((t = stack) != NULL) {
stack = t->next;
PR_Unlock(lock);
t->run();
delete t;
PR_Lock(lock);
}
}
PR_Unlock(lock);
}
bool
JSBackgroundThread::busy()
{
return !!stack; // we tolerate some racing here
}
void
JSBackgroundThread::schedule(JSBackgroundTask* task)
{
PR_Lock(lock);
if (shutdown) {
PR_Unlock(lock);
task->run();
delete task;
return;
}
task->next = stack;
stack = task;
PR_NotifyCondVar(wakeup);
PR_Unlock(lock);
}
#endif

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

@ -1,85 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99 ft=cpp:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
* June 30, 2009.
*
* The Initial Developer of the Original Code is
* Andreas Gal <gal@mozilla.com>
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef jstask_h___
#define jstask_h___
class JSBackgroundTask {
friend class JSBackgroundThread;
JSBackgroundTask* next;
public:
virtual ~JSBackgroundTask() {}
virtual void run() = 0;
};
#ifdef JS_THREADSAFE
#include "prthread.h"
#include "prlock.h"
#include "prcvar.h"
class JSBackgroundThread {
PRThread* thread;
JSBackgroundTask* stack;
PRLock* lock;
PRCondVar* wakeup;
bool shutdown;
public:
JSBackgroundThread();
~JSBackgroundThread();
bool init();
void cancel();
void work();
bool busy();
void schedule(JSBackgroundTask* task);
};
#else
class JSBackgroundThread {
public:
void schedule(JSBackgroundTask* task) {
task->run();
}
};
#endif
#endif /* jstask_h___ */

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

@ -11148,7 +11148,8 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
* - call / not / ifeq
* - call / trace / not / ifeq
*
* In either case, we call RegExp.test() because "r.exec(s) !=
* In either case, we replace the call to RegExp.exec() on the
* stack with a call to RegExp.test() because "r.exec(s) !=
* null" is equivalent to "r.test(s)". This avoids building
* the result array, which can be expensive.
*/
@ -11165,7 +11166,22 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
pc[JSOP_CALL_LENGTH + JSOP_TRACE_LENGTH] == JSOP_NOT &&
pc[JSOP_CALL_LENGTH + JSOP_TRACE_LENGTH + JSOP_NOT_LENGTH] == JSOP_IFEQ))
{
fun->u.n.native = js_regexp_test;
/*
* FIXME: Bug 594205 -- RegExp.prototype.test() may
* have been overwritten.
*/
JSObject* proto;
Value test;
jsid testId = ATOM_TO_JSID(cx->runtime->atomState.testAtom);
if (js_GetClassPrototype(cx, funobj->getParent(), JSProto_RegExp, &proto) &&
js_GetProperty(cx, proto, testId, &test))
{
vp[0] = test;
/* Recompute these values from the new vp[0] value. */
funobj = &vp[0].toObject();
fun = GET_FUNCTION_PRIVATE(cx, funobj);
native = fun->u.n.native;
}
}
}
}

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

@ -1314,9 +1314,20 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_LINENO)
BEGIN_CASE(JSOP_DEFFUN)
{
uint32 index = fullAtomIndex(PC);
JSFunction *inner = script->getFunction(index);
if (fun) {
JSLocalKind localKind = fun->lookupLocal(cx, inner->atom, NULL);
if (localKind != JSLOCAL_NONE)
frame.forgetEverything();
}
prepareStubCall(Uses(0));
masm.move(Imm32(fullAtomIndex(PC)), Registers::ArgReg1);
masm.move(ImmPtr(inner), Registers::ArgReg1);
stubCall(stubs::DefFun);
}
END_CASE(JSOP_DEFFUN)
BEGIN_CASE(JSOP_DEFLOCALFUN_FC)

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

@ -383,6 +383,7 @@ class Compiler
STUB_CALL_TYPE(VoidVpStub);
STUB_CALL_TYPE(VoidStubPC);
STUB_CALL_TYPE(BoolStubUInt32);
STUB_CALL_TYPE(VoidStubFun);
#undef STUB_CALL_TYPE
void prepareStubCall(Uses uses);

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

@ -161,6 +161,7 @@ typedef void * (JS_FASTCALL *VoidPtrStubUInt32)(VMFrame &, uint32);
typedef JSObject * (JS_FASTCALL *JSObjStub)(VMFrame &);
typedef JSObject * (JS_FASTCALL *JSObjStubUInt32)(VMFrame &, uint32);
typedef JSObject * (JS_FASTCALL *JSObjStubFun)(VMFrame &, JSFunction *);
typedef void (JS_FASTCALL *VoidStubFun)(VMFrame &, JSFunction *);
typedef JSObject * (JS_FASTCALL *JSObjStubJSObj)(VMFrame &, JSObject *);
typedef void (JS_FASTCALL *VoidStubAtom)(VMFrame &, JSAtom *);
typedef JSString * (JS_FASTCALL *JSStrStub)(VMFrame &);

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

@ -824,13 +824,12 @@ stubs::DecLocal(VMFrame &f, uint32 slot)
}
void JS_FASTCALL
stubs::DefFun(VMFrame &f, uint32 index)
stubs::DefFun(VMFrame &f, JSFunction *fun)
{
JSObject *obj2;
JSContext *cx = f.cx;
JSStackFrame *fp = f.fp();
JSScript *script = fp->getScript();
/*
* A top-level function defined in Global or Eval code (see ECMA-262
@ -838,7 +837,6 @@ stubs::DefFun(VMFrame &f, uint32 index)
* a compound statement (not at the top statement level of global code, or
* at the top level of a function body).
*/
JSFunction *fun = script->getFunction(index);
JSObject *obj = FUN_OBJECT(fun);
if (FUN_NULL_CLOSURE(fun)) {

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

@ -115,7 +115,7 @@ void JS_FASTCALL IncElem(VMFrame &f);
void JS_FASTCALL DecElem(VMFrame &f);
void JS_FASTCALL CallProp(VMFrame &f, JSAtom *atom);
void JS_FASTCALL DefFun(VMFrame &f, uint32 index);
void JS_FASTCALL DefFun(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL DefLocalFun(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL DefLocalFun_FC(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL RegExp(VMFrame &f, JSObject *regex);

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

@ -93,3 +93,4 @@ script regress-473709.js
script regress-474319.js
script regress-477053.js
script regress-561031.js
script regress-587434.js

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

@ -0,0 +1,12 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
(function() {
function::a(eval("false"), true);
function a({}) {}
})()
/* Don't crash because of bad |this| value. */
reportCompare(0, 0, "ok");

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

@ -28,7 +28,6 @@ function genFunDecl(id, params, body) Pattern({ type: "FunctionDeclaration",
function varDecl(decls) Pattern({ type: "VariableDeclaration", declarations: decls, kind: "var" })
function letDecl(decls) Pattern({ type: "VariableDeclaration", declarations: decls, kind: "let" })
function constDecl(decls) Pattern({ type: "VariableDeclaration", declarations: decls, kind: "const" })
function blockStmt(body) Pattern({ type: "BlockStatement", body: body })
function ident(name) Pattern({ type: "Identifier", name: name })
function dotExpr(obj, id) Pattern({ type: "MemberExpression", computed: false, object: obj, property: id })
function memExpr(obj, id) Pattern({ type: "MemberExpression", computed: true, object: obj, property: id })
@ -37,7 +36,7 @@ function forInStmt(lhs, rhs, body) Pattern({ type: "ForInStatement", left: lhs,
function forEachInStmt(lhs, rhs, body) Pattern({ type: "ForInStatement", left: lhs, right: rhs, body: body, each: true })
function breakStmt(lab) Pattern({ type: "BreakStatement", label: lab })
function continueStmt(lab) Pattern({ type: "ContinueStatement", label: lab })
function blockStmt(stmts) Pattern({ type: "BlockStatement", body: stmts })
function blockStmt(body) Pattern({ type: "BlockStatement", body: body })
var emptyStmt = Pattern({ type: "EmptyStatement" })
function ifStmt(test, cons, alt) Pattern({ type: "IfStatement", test: test, alternate: alt, consequent: cons })
function labStmt(lab, stmt) Pattern({ type: "LabeledStatement", label: lab, body: stmt })
@ -49,6 +48,7 @@ function caseClause(test, stmts) Pattern({ type: "SwitchCase", test: test, conse
function defaultClause(stmts) Pattern({ type: "SwitchCase", test: null, consequent: stmts })
function catchClause(id, guard, body) Pattern({ type: "CatchClause", param: id, guard: guard, body: body })
function tryStmt(body, catches, fin) Pattern({ type: "TryStatement", block: body, handler: catches, finalizer: fin })
function letStmt(head, body) Pattern({ type: "LetStatement", head: head, body: body })
function funExpr(id, args, body, gen) Pattern({ type: "FunctionExpression",
id: id,
params: args,
@ -75,6 +75,7 @@ function objExpr(elts) Pattern({ type: "ObjectExpression", properties: elts })
function compExpr(body, blocks, filter) Pattern({ type: "ComprehensionExpression", body: body, blocks: blocks, filter: filter })
function genExpr(body, blocks, filter) Pattern({ type: "GeneratorExpression", body: body, blocks: blocks, filter: filter })
function graphExpr(idx, body) Pattern({ type: "GraphExpression", index: idx, expression: body })
function letExpr(head, body) Pattern({ type: "LetExpression", head: head, body: body })
function idxExpr(idx) Pattern({ type: "GraphIndexExpression", index: idx })
function compBlock(left, right) Pattern({ type: "ComprehensionBlock", left: left, right: right, each: false })
@ -91,6 +92,7 @@ function blockPatt(patt) program([exprStmt(funExpr(null, [], blockStmt([blockStm
var xmlAnyName = Pattern({ type: "XMLAnyName" });
function xmlQualId(left, right, computed) Pattern({ type: "XMLQualifiedIdentifier", left: left, right: right, computed: computed })
function xmlFuncQualId(right, computed) Pattern({ type: "XMLFunctionQualifiedIdentifier", right: right, computed: computed })
function xmlAttrSel(id) Pattern({ type: "XMLAttributeSelector", attribute: id })
function xmlFilter(left, right) Pattern({ type: "XMLFilterExpression", left: left, right: right })
function xmlPointTag(contents) Pattern({ type: "XMLPointTag", contents: contents })
@ -101,6 +103,7 @@ function xmlElt(contents) Pattern({ type: "XMLElement", contents: contents })
function xmlAttr(value) Pattern({ type: "XMLAttribute", value: value })
function xmlText(text) Pattern({ type: "XMLText", text: text })
function xmlPI(target, contents) Pattern({ type: "XMLProcessingInstruction", target: target, contents: contents })
function xmlDefNS(ns) Pattern({ type: "XMLDefaultDeclaration", namespace: ns })
function assertBlockStmt(src, patt) {
blockPatt(patt).assert(Reflect.parse(blockSrc(src)));
@ -156,6 +159,15 @@ function assertDecl(src, patt) {
assertBlockDecl(src, patt);
}
function assertError(src, errorType) {
try {
Reflect.parse(src);
} catch (expected if expected instanceof errorType) {
return;
}
throw new Error("expected " + errorType.name + " for " + uneval(src));
}
// general tests
// NB: These are useful but for now trace-test doesn't do I/O reliably.
@ -183,6 +195,23 @@ assertDecl("function foo() { return 42 }",
funDecl(ident("foo"), [], blockStmt([returnStmt(lit(42))])));
// Bug 591437: rebound args have their defs turned into uses
assertDecl("function f(a) { function a() { } }",
funDecl(ident("f"), [ident("a")], blockStmt([funDecl(ident("a"), [], blockStmt([]))])));
assertDecl("function f(a,b,c) { function b() { } }",
funDecl(ident("f"), [ident("a"),ident("b"),ident("c")], blockStmt([funDecl(ident("b"), [], blockStmt([]))])));
assertDecl("function f(a,[x,y]) { function a() { } }",
funDecl(ident("f"),
[ident("a"), arrPatt([ident("x"), ident("y")])],
blockStmt([funDecl(ident("a"), [], blockStmt([]))])));
// Bug 591450: this test currently crashes because of a bug in jsparse
// assertDecl("function f(a,[x,y],b,[w,z],c) { function b() { } }",
// funDecl(ident("f"),
// [ident("a"), arrPatt([ident("x"), ident("y")]), ident("b"), arrPatt([ident("w"), ident("z")]), ident("c")],
// blockStmt([funDecl(ident("b"), [], blockStmt([]))])));
// expressions
assertExpr("true", lit(true));
@ -356,6 +385,21 @@ assertStmt("try { } catch (e if foo) { } catch (e if bar) { } catch (e) { } fina
catchClause(ident("e"), null, blockStmt([])) ],
blockStmt([])));
// redeclarations (TOK_NAME nodes with lexdef)
assertStmt("function f() { function g() { } function g() { } }",
funDecl(ident("f"), [], blockStmt([funDecl(ident("g"), [], blockStmt([])),
funDecl(ident("g"), [], blockStmt([]))])));
assertStmt("function f() { function g() { } function g() { return 42 } }",
funDecl(ident("f"), [], blockStmt([funDecl(ident("g"), [], blockStmt([])),
funDecl(ident("g"), [], blockStmt([returnStmt(lit(42))]))])));
assertStmt("function f() { var x = 42; var x = 43; }",
funDecl(ident("f"), [], blockStmt([varDecl([{ id: ident("x"), init: lit(42) }]),
varDecl([{ id: ident("x"), init: lit(43) }])])));
assertDecl("var {x:y} = foo;", varDecl([{ id: objPatt([{ key: ident("x"), value: ident("y") }]),
init: ident("foo") }]));
@ -539,6 +583,32 @@ assertStmt("for each ({a:x,b:y,c:z} in foo);", forEachInStmt(axbycz, ident("foo"
assertStmt("for each (var [x,y,z] in foo);", forEachInStmt(varDecl([{ id: xyz, init: null }]), ident("foo"), emptyStmt));
assertStmt("for each (let [x,y,z] in foo);", forEachInStmt(letDecl([{ id: xyz, init: null }]), ident("foo"), emptyStmt));
assertStmt("for each ([x,y,z] in foo);", forEachInStmt(xyz, ident("foo"), emptyStmt));
assertError("for (const x in foo);", SyntaxError);
assertError("for (const {a:x,b:y,c:z} in foo);", SyntaxError);
assertError("for (const [x,y,z] in foo);", SyntaxError);
assertError("for each (const x in foo);", SyntaxError);
assertError("for each (const {a:x,b:y,c:z} in foo);", SyntaxError);
assertError("for each (const [x,y,z] in foo);", SyntaxError);
// destructuring in for-in and for-each-in loop heads with initializers
assertStmt("for (var {a:x,b:y,c:z} = 22 in foo);", forInStmt(varDecl([{ id: axbycz, init: lit(22) }]), ident("foo"), emptyStmt));
assertStmt("for (let {a:x,b:y,c:z} = 22 in foo);", forInStmt(letDecl([{ id: axbycz, init: lit(22) }]), ident("foo"), emptyStmt));
assertStmt("for (var [x,y,z] = 22 in foo);", forInStmt(varDecl([{ id: xyz, init: lit(22) }]), ident("foo"), emptyStmt));
assertStmt("for (let [x,y,z] = 22 in foo);", forInStmt(letDecl([{ id: xyz, init: lit(22) }]), ident("foo"), emptyStmt));
assertStmt("for each (var {a:x,b:y,c:z} = 22 in foo);", forEachInStmt(varDecl([{ id: axbycz, init: lit(22) }]), ident("foo"), emptyStmt));
assertStmt("for each (let {a:x,b:y,c:z} = 22 in foo);", forEachInStmt(letDecl([{ id: axbycz, init: lit(22) }]), ident("foo"), emptyStmt));
assertStmt("for each (var [x,y,z] = 22 in foo);", forEachInStmt(varDecl([{ id: xyz, init: lit(22) }]), ident("foo"), emptyStmt));
assertStmt("for each (let [x,y,z] = 22 in foo);", forEachInStmt(letDecl([{ id: xyz, init: lit(22) }]), ident("foo"), emptyStmt));
assertError("for (x = 22 in foo);", SyntaxError);
assertError("for ({a:x,b:y,c:z} = 22 in foo);", SyntaxError);
assertError("for ([x,y,z] = 22 in foo);", SyntaxError);
assertError("for (const x = 22 in foo);", SyntaxError);
assertError("for (const {a:x,b:y,c:z} = 22 in foo);", SyntaxError);
assertError("for (const [x,y,z] = 22 in foo);", SyntaxError);
assertError("for each (const x = 22 in foo);", SyntaxError);
assertError("for each (const {a:x,b:y,c:z} = 22 in foo);", SyntaxError);
assertError("for each (const [x,y,z] = 22 in foo);", SyntaxError);
// expression closures
@ -644,7 +714,53 @@ assertExpr("( [x,y,z] for each (x in foo) for each (y in bar) for each (z in baz
// sharp variables
assertExpr("#1={me:#1#}", graphExpr(1, objExpr([{ key: ident("me"), value: idxExpr(1) }])))
assertExpr("#1={me:#1#}", graphExpr(1, objExpr([{ key: ident("me"), value: idxExpr(1) }])));
// let expressions
assertExpr("(let (x=1) x)", letExpr([{ id: ident("x"), init: lit(1) }], ident("x")));
assertExpr("(let (x=1,y=2) y)", letExpr([{ id: ident("x"), init: lit(1) },
{ id: ident("y"), init: lit(2) }],
ident("y")));
assertExpr("(let (x=1,y=2,z=3) z)", letExpr([{ id: ident("x"), init: lit(1) },
{ id: ident("y"), init: lit(2) },
{ id: ident("z"), init: lit(3) }],
ident("z")));
assertExpr("(let (x) x)", letExpr([{ id: ident("x"), init: null }], ident("x")));
assertExpr("(let (x,y) y)", letExpr([{ id: ident("x"), init: null },
{ id: ident("y"), init: null }],
ident("y")));
assertExpr("(let (x,y,z) z)", letExpr([{ id: ident("x"), init: null },
{ id: ident("y"), init: null },
{ id: ident("z"), init: null }],
ident("z")));
assertExpr("(let (x = 1, y = x) y)", letExpr([{ id: ident("x"), init: lit(1) },
{ id: ident("y"), init: ident("x") }],
ident("y")));
assertError("(let (x = 1, x = 2) x)", TypeError);
// let statements
assertStmt("let (x=1) { }", letStmt([{ id: ident("x"), init: lit(1) }], blockStmt([])));
assertStmt("let (x=1,y=2) { }", letStmt([{ id: ident("x"), init: lit(1) },
{ id: ident("y"), init: lit(2) }],
blockStmt([])));
assertStmt("let (x=1,y=2,z=3) { }", letStmt([{ id: ident("x"), init: lit(1) },
{ id: ident("y"), init: lit(2) },
{ id: ident("z"), init: lit(3) }],
blockStmt([])));
assertStmt("let (x) { }", letStmt([{ id: ident("x"), init: null }], blockStmt([])));
assertStmt("let (x,y) { }", letStmt([{ id: ident("x"), init: null },
{ id: ident("y"), init: null }],
blockStmt([])));
assertStmt("let (x,y,z) { }", letStmt([{ id: ident("x"), init: null },
{ id: ident("y"), init: null },
{ id: ident("z"), init: null }],
blockStmt([])));
assertStmt("let (x = 1, y = x) { }", letStmt([{ id: ident("x"), init: lit(1) },
{ id: ident("y"), init: ident("x") }],
blockStmt([])));
assertError("let (x = 1, x = 2) { }", TypeError);
// E4X
@ -658,6 +774,8 @@ assertExpr("x::[foo()]", xmlQualId(ident("x"), callExpr(ident("foo"), []), true)
assertExpr("*::*", xmlQualId(xmlAnyName, ident("*"), false));
assertExpr("*::[foo]", xmlQualId(xmlAnyName, ident("foo"), true));
assertExpr("*::[foo()]", xmlQualId(xmlAnyName, callExpr(ident("foo"), []), true));
assertExpr("function::x", xmlFuncQualId(ident("x"), false));
assertExpr("function::[foo]", xmlFuncQualId(ident("foo"), true));
assertExpr("@foo", xmlAttrSel(ident("foo")));
assertExpr("x.(p)", xmlFilter(ident("x"), ident("p")));
assertExpr("<{foo}/>", xmlPointTag([xmlEscape(ident("foo"))]));
@ -671,6 +789,66 @@ assertExpr("<{foo}>text</{foo}>", xmlElt([xmlStartTag([xmlEscape(ident("foo"))])
xmlEndTag([xmlEscape(ident("foo"))])]));
assertExpr("<?xml?>", xmlPI("xml", ""));
assertExpr("<?xml version='1.0'?>", xmlPI("xml", "version='1.0'"));
assertDecl("default xml namespace = 'js';", xmlDefNS(lit("js")));
assertDecl("default xml namespace = foo;", xmlDefNS(ident("foo")));
// The parser turns these into TOK_UNARY nodes with pn_op == JSOP_SETXMLNAME.
assertExpr("x::y = foo", aExpr("=", xmlQualId(ident("x"), ident("y"), false), ident("foo")));
assertExpr("function::x = foo", aExpr("=", xmlFuncQualId(ident("x"), false), ident("foo")));
assertExpr("@x = foo", aExpr("=", xmlAttrSel(ident("x")), ident("foo")));
assertExpr("x::* = foo", aExpr("=", xmlQualId(ident("x"), ident("*"), false), ident("foo")));
assertExpr("*::* = foo", aExpr("=", xmlQualId(xmlAnyName, ident("*"), false), ident("foo")));
assertExpr("x.* = foo", aExpr("=", memExpr(ident("x"), xmlAnyName), ident("foo")));
assertExpr("x[*] = foo", aExpr("=", memExpr(ident("x"), xmlAnyName), ident("foo")));
assertExpr("x::y += foo", aExpr("+=", xmlQualId(ident("x"), ident("y"), false), ident("foo")));
assertExpr("function::x += foo", aExpr("+=", xmlFuncQualId(ident("x"), false), ident("foo")));
assertExpr("@x += foo", aExpr("+=", xmlAttrSel(ident("x")), ident("foo")));
assertExpr("x::* += foo", aExpr("+=", xmlQualId(ident("x"), ident("*"), false), ident("foo")));
assertExpr("*::* += foo", aExpr("+=", xmlQualId(xmlAnyName, ident("*"), false), ident("foo")));
assertExpr("x.* += foo", aExpr("+=", memExpr(ident("x"), xmlAnyName), ident("foo")));
assertExpr("x[*] += foo", aExpr("+=", memExpr(ident("x"), xmlAnyName), ident("foo")));
assertExpr("x::y++", updExpr("++", xmlQualId(ident("x"), ident("y"), false), false));
assertExpr("function::x++", updExpr("++", xmlFuncQualId(ident("x"), false), false));
assertExpr("@x++", updExpr("++", xmlAttrSel(ident("x")), false));
assertExpr("x::*++", updExpr("++", xmlQualId(ident("x"), ident("*"), false), false));
assertExpr("*::*++", updExpr("++", xmlQualId(xmlAnyName, ident("*"), false), false));
assertExpr("x.*++", updExpr("++", memExpr(ident("x"), xmlAnyName), false));
assertExpr("x[*]++", updExpr("++", memExpr(ident("x"), xmlAnyName), false));
assertExpr("++x::y", updExpr("++", xmlQualId(ident("x"), ident("y"), false), true));
assertExpr("++function::x", updExpr("++", xmlFuncQualId(ident("x"), false), true));
assertExpr("++@x", updExpr("++", xmlAttrSel(ident("x")), true));
assertExpr("++x::*", updExpr("++", xmlQualId(ident("x"), ident("*"), false), true));
assertExpr("++*::*", updExpr("++", xmlQualId(xmlAnyName, ident("*"), false), true));
assertExpr("++x.*", updExpr("++", memExpr(ident("x"), xmlAnyName), true));
assertExpr("++x[*]", updExpr("++", memExpr(ident("x"), xmlAnyName), true));
// The parser turns these into TOK_UNARY nodes with pn_op == JSOP_BINDXMLNAME.
function singletonObjPatt(name, val) objPatt([{ key: ident(name), value: val }])
assertExpr("({a:x::y}) = foo", aExpr("=", singletonObjPatt("a", xmlQualId(ident("x"), ident("y"), false)), ident("foo")));
assertExpr("({a:function::x}) = foo", aExpr("=", singletonObjPatt("a", xmlFuncQualId(ident("x"), false)), ident("foo")));
assertExpr("({a:@x}) = foo", aExpr("=", singletonObjPatt("a", xmlAttrSel(ident("x"))), ident("foo")));
assertExpr("({a:x::*}) = foo", aExpr("=", singletonObjPatt("a", xmlQualId(ident("x"), ident("*"), false)), ident("foo")));
assertExpr("({a:*::*}) = foo", aExpr("=", singletonObjPatt("a", xmlQualId(xmlAnyName, ident("*"), false)), ident("foo")));
assertExpr("({a:x.*}) = foo", aExpr("=", singletonObjPatt("a", memExpr(ident("x"), xmlAnyName)), ident("foo")));
assertExpr("({a:x[*]}) = foo", aExpr("=", singletonObjPatt("a", memExpr(ident("x"), xmlAnyName)), ident("foo")));
function emptyForInPatt(val, rhs) forInStmt(val, rhs, emptyStmt)
assertStmt("for (x::y in foo);", emptyForInPatt(xmlQualId(ident("x"), ident("y"), false), ident("foo")));
assertStmt("for (function::x in foo);", emptyForInPatt(xmlFuncQualId(ident("x"), false), ident("foo")));
assertStmt("for (@x in foo);", emptyForInPatt(xmlAttrSel(ident("x")), ident("foo")));
assertStmt("for (x::* in foo);", emptyForInPatt(xmlQualId(ident("x"), ident("*"), false), ident("foo")));
assertStmt("for (*::* in foo);", emptyForInPatt(xmlQualId(xmlAnyName, ident("*"), false), ident("foo")));
assertStmt("for (x.* in foo);", emptyForInPatt(memExpr(ident("x"), xmlAnyName), ident("foo")));
assertStmt("for (x[*] in foo);", emptyForInPatt(memExpr(ident("x"), xmlAnyName), ident("foo")));
// NOTE: We appear to be unable to test XMLNAME, XMLCDATA, and XMLCOMMENT.

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

@ -0,0 +1,8 @@
var res;
for (var i = 0; i < 10; i++) {
var re = /a(b)c/;
var b = (re.exec(""), v = re.exec("abc")) !== null;
assertEq(v[0], "abc");
assertEq(v[1], "b");
}

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

@ -0,0 +1,11 @@
eval("\
(function(){\
for(var w in [0]) {\
function w(){}\
print(w)\
}\
})\
")()
/* Don't crash. */