зеркало из https://github.com/mozilla/gecko-dev.git
Merge.
This commit is contained in:
Коммит
736367f15a
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
187
js/src/jscntxt.h
187
js/src/jscntxt.h
|
@ -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
|
||||
|
|
176
js/src/jsgc.cpp
176
js/src/jsgc.cpp
|
@ -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. */
|
||||
|
Загрузка…
Ссылка в новой задаче