mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 22:55:23 +00:00
[JAEGER] Merge from tracemonkey.
This commit is contained in:
commit
4c5e8d41db
@ -222,26 +222,20 @@ nsFrameMessageManager::SendSyncMessage()
|
||||
JSAutoRequest ar(ctx);
|
||||
|
||||
PRUint32 len = retval.Length();
|
||||
jsval* dest = nsnull;
|
||||
JSObject* dataArray = js_NewArrayObjectWithCapacity(ctx, len, &dest);
|
||||
JSObject* dataArray = JS_NewArrayObject(ctx, len, NULL);
|
||||
NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
|
||||
nsAutoGCRoot arrayGCRoot(&dataArray, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (PRUint32 i = 0; i < len; ++i) {
|
||||
dest[i] = JSVAL_NULL;
|
||||
if (!retval[i].Length())
|
||||
continue;
|
||||
|
||||
jsval ret = JSVAL_VOID;
|
||||
nsAutoGCRoot root(&ret, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
JSONParser* parser = JS_BeginJSONParse(ctx, &ret);
|
||||
JSBool ok = JS_ConsumeJSONText(ctx, parser, (jschar*)retval[i].get(),
|
||||
(uint32)retval[i].Length());
|
||||
ok = JS_FinishJSONParse(ctx, parser, JSVAL_NULL) && ok;
|
||||
if (ok) {
|
||||
dest[i] = ret;
|
||||
NS_ENSURE_TRUE(JS_SetElement(ctx, dataArray, i, &ret), NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
}
|
||||
|
||||
@ -350,10 +344,9 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
||||
// To keep compatibility with e10s message manager,
|
||||
// define empty objects array.
|
||||
if (!aObjectsArray) {
|
||||
jsval* dest = nsnull;
|
||||
// Because we want JS messages to have always the same properties,
|
||||
// create array even if len == 0.
|
||||
aObjectsArray = js_NewArrayObjectWithCapacity(ctx, 0, &dest);
|
||||
aObjectsArray = JS_NewArrayObject(ctx, 0, NULL);
|
||||
if (!aObjectsArray) {
|
||||
return false;
|
||||
}
|
||||
|
@ -3825,14 +3825,6 @@ nsCanvasRenderingContext2D::AsyncDrawXULElement(nsIDOMXULElement* aElem, float a
|
||||
//
|
||||
// device pixel getting/setting
|
||||
//
|
||||
extern "C" {
|
||||
#include "jstypes.h"
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_CoerceArrayToCanvasImageData(JSObject *obj, jsuint offset, jsuint count,
|
||||
JSUint8 *dest);
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, jsval **vector);
|
||||
}
|
||||
|
||||
void
|
||||
nsCanvasRenderingContext2D::EnsureUnpremultiplyTable() {
|
||||
|
@ -903,20 +903,6 @@ DumpString(const nsAString &str)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
MaybeGC(JSContext *cx)
|
||||
{
|
||||
size_t bytes = cx->runtime->gcBytes;
|
||||
size_t lastBytes = cx->runtime->gcLastBytes;
|
||||
if ((bytes > 8192 && bytes > lastBytes * 16)
|
||||
#ifdef DEBUG
|
||||
|| cx->runtime->gcZeal > 0
|
||||
#endif
|
||||
) {
|
||||
JS_GC(cx);
|
||||
}
|
||||
}
|
||||
|
||||
static already_AddRefed<nsIPrompt>
|
||||
GetPromptFromContext(nsJSContext* ctx)
|
||||
{
|
||||
@ -955,7 +941,7 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
|
||||
PRTime callbackTime = ctx->mOperationCallbackTime;
|
||||
PRTime modalStateTime = ctx->mModalStateTime;
|
||||
|
||||
MaybeGC(cx);
|
||||
JS_MaybeGC(cx);
|
||||
|
||||
// Now restore the callback time and count, in case they got reset.
|
||||
ctx->mOperationCallbackTime = callbackTime;
|
||||
@ -2184,6 +2170,8 @@ nsJSContext::CallEventHandler(nsISupports* aTarget, void *aScope, void *aHandler
|
||||
NS_TIME_FUNCTION_FMT(1.0, "%s (line %d) (function: %s)", MOZ_FUNCTION_NAME,
|
||||
__LINE__, JS_GetFunctionName(static_cast<JSFunction *>(JS_GetPrivate(mContext, static_cast<JSObject *>(aHandler)))));
|
||||
|
||||
|
||||
JSAutoRequest ar(mContext);
|
||||
JSObject* target = nsnull;
|
||||
nsresult rv = JSObjectFromInterface(aTarget, aScope, &target);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -2227,7 +2215,6 @@ nsJSContext::CallEventHandler(nsISupports* aTarget, void *aScope, void *aHandler
|
||||
}
|
||||
|
||||
jsval funval = OBJECT_TO_JSVAL(static_cast<JSObject *>(aHandler));
|
||||
JSAutoRequest ar(mContext);
|
||||
JSAutoCrossCompartmentCall accc;
|
||||
if (!accc.enter(mContext, target)) {
|
||||
stack->Pop(nsnull);
|
||||
@ -2259,7 +2246,6 @@ nsJSContext::CallEventHandler(nsISupports* aTarget, void *aScope, void *aHandler
|
||||
|
||||
// Convert to variant before calling ScriptEvaluated, as it may GC, meaning
|
||||
// we would need to root rval.
|
||||
JSAutoRequest ar(mContext);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (rval == JSVAL_NULL)
|
||||
*arv = nsnull;
|
||||
@ -3523,12 +3509,12 @@ nsJSContext::ScriptEvaluated(PRBool aTerminated)
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
if (mContext->runtime->gcZeal >= 2) {
|
||||
MaybeGC(mContext);
|
||||
JS_MaybeGC(mContext);
|
||||
} else
|
||||
#endif
|
||||
if (mNumEvaluations > 20) {
|
||||
mNumEvaluations = 0;
|
||||
MaybeGC(mContext);
|
||||
JS_MaybeGC(mContext);
|
||||
}
|
||||
|
||||
if (aTerminated) {
|
||||
|
@ -524,20 +524,14 @@ TabParent::ReceiveMessage(const nsString& aMessage,
|
||||
nsFrameMessageManager* manager = frameLoader->GetFrameMessageManager();
|
||||
JSContext* ctx = manager->GetJSContext();
|
||||
JSAutoRequest ar(ctx);
|
||||
jsval* dest;
|
||||
PRUint32 len = 0; //TODO: obtain a real value in bug 572685
|
||||
// Because we want JS messages to have always the same properties,
|
||||
// create array even if len == 0.
|
||||
JSObject* objectsArray =
|
||||
js_NewArrayObjectWithCapacity(ctx, len, &dest);
|
||||
JSObject* objectsArray = JS_NewArrayObject(ctx, len, NULL);
|
||||
if (!objectsArray) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
nsAutoGCRoot arrayGCRoot(&objectsArray, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
manager->ReceiveMessage(mFrameElement,
|
||||
aMessage,
|
||||
aSync,
|
||||
|
@ -3911,16 +3911,15 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj)
|
||||
return name;
|
||||
}
|
||||
|
||||
// For a struct field with 'name' and 'type', add an element to field
|
||||
// descriptor array 'arrayObj' of the form { name : type }.
|
||||
// For a struct field with 'name' and 'type', add an element of the form
|
||||
// { name : type }.
|
||||
static JSBool
|
||||
AddFieldToArray(JSContext* cx,
|
||||
JSObject* arrayObj,
|
||||
jsval* element,
|
||||
JSString* name,
|
||||
JSObject* typeObj)
|
||||
{
|
||||
JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, arrayObj);
|
||||
JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, NULL);
|
||||
if (!fieldObj)
|
||||
return false;
|
||||
|
||||
@ -4325,22 +4324,22 @@ StructType::BuildFieldsArray(JSContext* cx, JSObject* obj)
|
||||
size_t len = fields->count();
|
||||
|
||||
// Prepare a new array for the 'fields' property of the StructType.
|
||||
jsval* fieldsVec;
|
||||
JSObject* fieldsProp =
|
||||
js_NewArrayObjectWithCapacity(cx, len, &fieldsVec);
|
||||
if (!fieldsProp)
|
||||
Array<jsval, 16> fieldsVec;
|
||||
if (!fieldsVec.resize(len))
|
||||
return NULL;
|
||||
js::AutoObjectRooter root(cx, fieldsProp);
|
||||
JS_ASSERT(len == 0 || fieldsVec);
|
||||
|
||||
for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
|
||||
const FieldInfoHash::Entry& entry = r.front();
|
||||
// Add the field descriptor to the array.
|
||||
if (!AddFieldToArray(cx, fieldsProp, &fieldsVec[entry.value.mIndex],
|
||||
entry.key, entry.value.mType))
|
||||
if (!AddFieldToArray(cx, &fieldsVec[entry.value.mIndex],
|
||||
entry.key, entry.value.mType))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSObject* fieldsProp = JS_NewArrayObject(cx, len, fieldsVec.begin());
|
||||
if (!fieldsProp)
|
||||
return NULL;
|
||||
|
||||
// Seal the fields array.
|
||||
if (!JS_SealObject(cx, fieldsProp, JS_FALSE))
|
||||
return NULL;
|
||||
@ -5039,17 +5038,17 @@ FunctionType::ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp
|
||||
size_t len = fninfo->mArgTypes.length();
|
||||
|
||||
// Prepare a new array.
|
||||
jsval* vec;
|
||||
JSObject* argTypes =
|
||||
js_NewArrayObjectWithCapacity(cx, len, &vec);
|
||||
if (!argTypes)
|
||||
Array<jsval, 16> vec;
|
||||
if (!vec.resize(len))
|
||||
return JS_FALSE;
|
||||
js::AutoObjectRooter argsroot(cx, argTypes);
|
||||
JS_ASSERT(len == 0 || vec);
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
vec[i] = OBJECT_TO_JSVAL(fninfo->mArgTypes[i]);
|
||||
|
||||
JSObject* argTypes = JS_NewArrayObject(cx, len, vec.begin());
|
||||
if (!argTypes)
|
||||
return JS_FALSE;
|
||||
|
||||
// Seal and cache it.
|
||||
if (!JS_SealObject(cx, argTypes, JS_FALSE) ||
|
||||
!JS_SetReservedSlot(cx, obj, SLOT_ARGS_T, OBJECT_TO_JSVAL(argTypes)))
|
||||
|
@ -121,6 +121,10 @@ JS_PUBLIC_DATA(jsval) JSVAL_TRUE = { BUILD_JSVAL(JSVAL_TAG_BOOLEAN, JS_TRUE)
|
||||
JS_PUBLIC_DATA(jsval) JSVAL_VOID = { BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0) };
|
||||
#endif
|
||||
|
||||
/* Make sure that jschar is two bytes unsigned integer */
|
||||
JS_STATIC_ASSERT((jschar)-1 > 0);
|
||||
JS_STATIC_ASSERT(sizeof(jschar) == 2);
|
||||
|
||||
JS_PUBLIC_API(int64)
|
||||
JS_Now()
|
||||
{
|
||||
@ -1845,7 +1849,7 @@ JS_free(JSContext *cx, void *p)
|
||||
JS_PUBLIC_API(void)
|
||||
JS_updateMallocCounter(JSContext *cx, size_t nbytes)
|
||||
{
|
||||
return cx->updateMallocCounter(nbytes);
|
||||
cx->runtime->updateMallocCounter(nbytes);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(char *)
|
||||
@ -2441,65 +2445,16 @@ JS_MaybeGC(JSContext *cx)
|
||||
|
||||
rt = cx->runtime;
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
if (rt->gcZeal > 0) {
|
||||
JS_GC(cx);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
bytes = rt->gcBytes;
|
||||
lastBytes = rt->gcLastBytes;
|
||||
|
||||
/*
|
||||
* We run the GC if we used all available free GC cells and had to
|
||||
* allocate extra 1/3 of GC arenas since the last run of GC, or if
|
||||
* we have malloc'd more bytes through JS_malloc than we were told
|
||||
* to allocate by JS_NewRuntime.
|
||||
*
|
||||
* The reason for
|
||||
* bytes > 4/3 lastBytes
|
||||
* condition is the following. Bug 312238 changed bytes and lastBytes
|
||||
* to mean the total amount of memory that the GC uses now and right
|
||||
* after the last GC.
|
||||
*
|
||||
* Before the bug the variables meant the size of allocated GC things
|
||||
* now and right after the last GC. That size did not include the
|
||||
* memory taken by free GC cells and the condition was
|
||||
* bytes > 3/2 lastBytes.
|
||||
* That is, we run the GC if we have half again as many bytes of
|
||||
* GC-things as the last time we GC'd. To be compatible we need to
|
||||
* express that condition through the new meaning of bytes and
|
||||
* lastBytes.
|
||||
*
|
||||
* We write the original condition as
|
||||
* B*(1-F) > 3/2 Bl*(1-Fl)
|
||||
* where B is the total memory size allocated by GC and F is the free
|
||||
* cell density currently and Sl and Fl are the size and the density
|
||||
* right after GC. The density by definition is memory taken by free
|
||||
* cells divided by total amount of memory. In other words, B and Bl
|
||||
* are bytes and lastBytes with the new meaning and B*(1-F) and
|
||||
* Bl*(1-Fl) are bytes and lastBytes with the original meaning.
|
||||
*
|
||||
* Our task is to exclude F and Fl from the last statement. According
|
||||
* to the stats from bug 331966 comment 23, Fl is about 10-25% for a
|
||||
* typical run of the browser. It means that the original condition
|
||||
* implied that we did not run GC unless we exhausted the pool of
|
||||
* free cells. Indeed if we still have free cells, then B == Bl since
|
||||
* we did not yet allocated any new arenas and the condition means
|
||||
* 1 - F > 3/2 (1-Fl) or 3/2Fl > 1/2 + F
|
||||
* That implies 3/2 Fl > 1/2 or Fl > 1/3. That cannot be fulfilled
|
||||
* for the state described by the stats. So we can write the original
|
||||
* condition as:
|
||||
* F == 0 && B > 3/2 Bl(1-Fl)
|
||||
* Again using the stats we see that Fl is about 11% when the browser
|
||||
* starts up and when we are far from hitting rt->gcMaxBytes. With
|
||||
* this F we have
|
||||
* F == 0 && B > 3/2 Bl(1-0.11)
|
||||
* or approximately F == 0 && B > 4/3 Bl.
|
||||
*/
|
||||
if ((bytes > 8192 && bytes > lastBytes + lastBytes / 3) ||
|
||||
rt->isGCMallocLimitReached()) {
|
||||
if (rt->gcIsNeeded ||
|
||||
#ifdef JS_GC_ZEAL
|
||||
rt->gcZeal > 0 ||
|
||||
#endif
|
||||
(bytes > 8192 && bytes > lastBytes * 16) ||
|
||||
bytes >= rt->gcTriggerBytes ||
|
||||
rt->overQuota()) {
|
||||
JS_GC(cx);
|
||||
}
|
||||
}
|
||||
@ -2620,7 +2575,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;
|
||||
}
|
||||
|
||||
@ -2773,7 +2728,7 @@ JS_PUBLIC_API(JSBool)
|
||||
JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
|
||||
{
|
||||
assertSameCompartment(cx, obj, v);
|
||||
return js_HasInstance(cx, obj, Valueify(&v), bp);
|
||||
return HasInstance(cx, obj, Valueify(&v), bp);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void *)
|
||||
@ -4854,13 +4809,21 @@ JS_PUBLIC_API(void)
|
||||
JS_TriggerOperationCallback(JSContext *cx)
|
||||
{
|
||||
/*
|
||||
* Use JS_ATOMIC_SET_MASK in the hope that it will make sure the write
|
||||
* will become immediately visible to other processors polling
|
||||
* cx->interruptFlag. Note that we only care about visibility here,
|
||||
* not read/write ordering.
|
||||
* We allow for cx to come from another thread. Thus we must deal with
|
||||
* possible JS_ClearContextThread calls when accessing cx->thread. But we
|
||||
* assume that the calling thread is in a request so JSThread cannot be
|
||||
* GC-ed.
|
||||
*/
|
||||
JS_ATOMIC_SET_MASK(const_cast<jsword*>(&cx->interruptFlags),
|
||||
JSContext::INTERRUPT_OPERATION_CALLBACK);
|
||||
JSThreadData *td;
|
||||
#ifdef JS_THREADSAFE
|
||||
JSThread *thread = cx->thread;
|
||||
if (!thread)
|
||||
return;
|
||||
td = &thread->data;
|
||||
#else
|
||||
td = JS_THREAD_DATA(cx);
|
||||
#endif
|
||||
td->triggerOperationCallback();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
|
@ -48,20 +48,12 @@
|
||||
*
|
||||
* We track these pieces of metadata for arrays in dense mode:
|
||||
* - The array's length property as a uint32, accessible with
|
||||
* getArrayLength(), setDenseArrayLength().
|
||||
* - The number of indices that are filled (non-holes), accessible with
|
||||
* {get,set}DenseArrayCount().
|
||||
* getArrayLength(), setArrayLength().
|
||||
* - The number of element slots (capacity), gettable with
|
||||
* getDenseArrayCapacity().
|
||||
* - The minimum of length and capacity (minLenCap). There are no explicit
|
||||
* setters, it's updated automatically by setDenseArrayLength() and
|
||||
* setDenseArrayCapacity(). There are also no explicit getters, the only
|
||||
* user is TraceRecorder which can access it directly because it's a
|
||||
* friend. The function isDenseArrayMinLenCapOk() checks that it is set
|
||||
* correctly; a call to it should be put in an assertion at use points.
|
||||
*
|
||||
* In dense mode, holes in the array are represented by (JS_ARRAY_HOLE) invalid
|
||||
* values. The final slot in fslots is unused.
|
||||
* values. The final two slot in fslots are unused.
|
||||
*
|
||||
* NB: the capacity and length of a dense array are entirely unrelated! The
|
||||
* length may be greater than, less than, or equal to the capacity. See
|
||||
@ -131,13 +123,26 @@ INDEX_TOO_BIG(jsuint index)
|
||||
return index > JS_BIT(29) - 1;
|
||||
}
|
||||
|
||||
#define INDEX_TOO_SPARSE(array, index) \
|
||||
(INDEX_TOO_BIG(index) || \
|
||||
((index) > array->getDenseArrayCapacity() && (index) >= MIN_SPARSE_INDEX && \
|
||||
(index) > ((array)->getDenseArrayCount() + 1) * 4))
|
||||
static inline bool
|
||||
INDEX_TOO_SPARSE(JSObject *array, jsuint index)
|
||||
{
|
||||
/* Small arrays with less than 256 elements are dense, no matter what. */
|
||||
if (index < 256)
|
||||
return false;
|
||||
|
||||
#define ENSURE_SLOW_ARRAY(cx, obj) \
|
||||
(obj->getClass() == &js_SlowArrayClass || obj->makeDenseArraySlow(cx))
|
||||
/*
|
||||
* Otherwise if the index becomes too large or is more than 256 past
|
||||
* the current capacity, we have to slowify.
|
||||
*/
|
||||
return INDEX_TOO_BIG(index) || (index > array->getDenseArrayCapacity() + 256);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ENSURE_SLOW_ARRAY(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
return obj->getClass() == &js_SlowArrayClass ||
|
||||
obj->makeDenseArraySlow(cx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if the id represents an array index or an XML property index.
|
||||
@ -304,21 +309,19 @@ BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
|
||||
}
|
||||
|
||||
bool
|
||||
JSObject::resizeDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap,
|
||||
bool initializeAllSlots)
|
||||
JSObject::growDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap)
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
|
||||
if (newcap == 0) {
|
||||
freeDenseArrayElements(cx);
|
||||
return JS_TRUE;
|
||||
}
|
||||
JS_ASSERT(newcap >= ARRAY_CAPACITY_MIN);
|
||||
JS_ASSERT(newcap >= oldcap);
|
||||
|
||||
if (newcap > MAX_DSLOTS_LENGTH32) {
|
||||
js_ReportAllocationOverflow(cx);
|
||||
if (!JS_ON_TRACE(cx))
|
||||
js_ReportAllocationOverflow(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* dslots can be briefly NULL during array creation */
|
||||
Value *slots = dslots ? dslots - 1 : NULL;
|
||||
Value *newslots = (Value *) cx->realloc(slots, (size_t(newcap) + 1) * sizeof(Value));
|
||||
if (!newslots)
|
||||
@ -327,17 +330,15 @@ JSObject::resizeDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap,
|
||||
dslots = newslots + 1;
|
||||
setDenseArrayCapacity(newcap);
|
||||
|
||||
if (initializeAllSlots) {
|
||||
Value *base = addressOfDenseArrayElement(0);
|
||||
for (Value *vp = base + oldcap, *end = base + newcap; vp < end; ++vp)
|
||||
vp->setMagic(JS_ARRAY_HOLE);
|
||||
}
|
||||
Value *base = addressOfDenseArrayElement(0);
|
||||
for (Value *vp = base + oldcap, *end = base + newcap; vp < end; ++vp)
|
||||
vp->setMagic(JS_ARRAY_HOLE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JSObject::ensureDenseArrayElements(JSContext *cx, uint32 newcap, bool initializeAllSlots)
|
||||
JSObject::ensureDenseArrayElements(JSContext *cx, uint32 newcap)
|
||||
{
|
||||
/*
|
||||
* When a dense array with CAPACITY_DOUBLING_MAX or fewer slots needs to
|
||||
@ -354,7 +355,8 @@ JSObject::ensureDenseArrayElements(JSContext *cx, uint32 newcap, bool initialize
|
||||
*/
|
||||
static const size_t CAPACITY_CHUNK = 1024 * 1024 / sizeof(Value);
|
||||
|
||||
uint32 oldcap = getDenseArrayCapacity();
|
||||
/* While creating arrays, dslots can be NULL. */
|
||||
uint32 oldcap = dslots ? getDenseArrayCapacity() : 0;
|
||||
|
||||
if (newcap > oldcap) {
|
||||
/*
|
||||
@ -377,21 +379,39 @@ JSObject::ensureDenseArrayElements(JSContext *cx, uint32 newcap, bool initialize
|
||||
else if (actualCapacity < ARRAY_CAPACITY_MIN)
|
||||
actualCapacity = ARRAY_CAPACITY_MIN;
|
||||
|
||||
if (!resizeDenseArrayElements(cx, oldcap, actualCapacity, initializeAllSlots))
|
||||
if (!growDenseArrayElements(cx, oldcap, actualCapacity))
|
||||
return false;
|
||||
|
||||
if (!initializeAllSlots) {
|
||||
/*
|
||||
* Initialize the slots caller didn't actually ask for.
|
||||
*/
|
||||
for (uint32 i = newcap; i < actualCapacity; i++) {
|
||||
setDenseArrayElement(i, MagicValue(JS_ARRAY_HOLE));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JSObject::shrinkDenseArrayElements(JSContext *cx, uint32 newcap)
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
JS_ASSERT(newcap < getDenseArrayCapacity());
|
||||
JS_ASSERT(dslots);
|
||||
|
||||
uint32 fill = newcap;
|
||||
|
||||
if (newcap < ARRAY_CAPACITY_MIN)
|
||||
newcap = ARRAY_CAPACITY_MIN;
|
||||
|
||||
Value *newslots = (Value *) cx->realloc(dslots - 1, (size_t(newcap) + 1) * sizeof(Value));
|
||||
if (!newslots)
|
||||
return false;
|
||||
|
||||
dslots = newslots + 1;
|
||||
setDenseArrayCapacity(newcap);
|
||||
|
||||
/* we refuse to shrink below a minimum value, so we have to clear the excess space */
|
||||
Value *base = addressOfDenseArrayElement(0);
|
||||
while (fill < newcap)
|
||||
base[fill++].setMagic(JS_ARRAY_HOLE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp)
|
||||
{
|
||||
@ -478,9 +498,7 @@ SetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, const Value &v)
|
||||
if (!obj->ensureDenseArrayElements(cx, idx + 1))
|
||||
return JS_FALSE;
|
||||
if (idx >= obj->getArrayLength())
|
||||
obj->setDenseArrayLength(idx + 1);
|
||||
if (obj->getDenseArrayElement(idx).isMagic(JS_ARRAY_HOLE))
|
||||
obj->incDenseArrayCountBy(1);
|
||||
obj->setArrayLength(idx + 1);
|
||||
obj->setDenseArrayElement(idx, v);
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -507,9 +525,7 @@ DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index)
|
||||
if (obj->isDenseArray()) {
|
||||
if (index <= jsuint(-1)) {
|
||||
jsuint idx = jsuint(index);
|
||||
if (!INDEX_TOO_SPARSE(obj, idx) && idx < obj->getDenseArrayCapacity()) {
|
||||
if (!obj->getDenseArrayElement(idx).isMagic(JS_ARRAY_HOLE))
|
||||
obj->decDenseArrayCountBy(1);
|
||||
if (idx < obj->getDenseArrayCapacity()) {
|
||||
obj->setDenseArrayElement(idx, MagicValue(JS_ARRAY_HOLE));
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -623,26 +639,28 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||
|
||||
vp->setNumber(newlen);
|
||||
if (oldlen < newlen) {
|
||||
if (obj->isDenseArray())
|
||||
obj->setDenseArrayLength(newlen);
|
||||
else
|
||||
obj->setSlowArrayLength(newlen);
|
||||
obj->setArrayLength(newlen);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj->isDenseArray()) {
|
||||
/* Don't reallocate if we're not actually shrinking our slots. */
|
||||
jsuint capacity = obj->getDenseArrayCapacity();
|
||||
if (capacity > newlen && !obj->resizeDenseArrayElements(cx, capacity, newlen))
|
||||
/*
|
||||
* Don't reallocate if we're not actually shrinking our slots. If we do
|
||||
* shrink slots here, resizeDenseArrayElements will fill all slots to the
|
||||
* right of newlen with JS_ARRAY_HOLE. This permits us to disregard
|
||||
* length when reading from arrays as long we are within the capacity.
|
||||
*/
|
||||
jsuint oldcap = obj->getDenseArrayCapacity();
|
||||
if (oldcap > newlen && !obj->shrinkDenseArrayElements(cx, newlen))
|
||||
return false;
|
||||
obj->setDenseArrayLength(newlen);
|
||||
obj->setArrayLength(newlen);
|
||||
} else if (oldlen - newlen < (1 << 24)) {
|
||||
do {
|
||||
--oldlen;
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx) || !DeleteArrayElement(cx, obj, oldlen))
|
||||
return false;
|
||||
} while (oldlen != newlen);
|
||||
obj->setSlowArrayLength(newlen);
|
||||
obj->setArrayLength(newlen);
|
||||
} else {
|
||||
/*
|
||||
* We are going to remove a lot of indexes in a presumably sparse
|
||||
@ -669,7 +687,7 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
obj->setSlowArrayLength(newlen);
|
||||
obj->setArrayLength(newlen);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -786,7 +804,7 @@ slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||
return JS_TRUE;
|
||||
length = obj->getArrayLength();
|
||||
if (index >= length)
|
||||
obj->setSlowArrayLength(index + 1);
|
||||
obj->setArrayLength(index + 1);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -817,9 +835,7 @@ array_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||
return JS_FALSE;
|
||||
|
||||
if (i >= obj->getArrayLength())
|
||||
obj->setDenseArrayLength(i + 1);
|
||||
if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE))
|
||||
obj->incDenseArrayCountBy(1);
|
||||
obj->setArrayLength(i + 1);
|
||||
obj->setDenseArrayElement(i, *vp);
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -879,8 +895,7 @@ dense_grow(JSContext* cx, JSObject* obj, jsint i, const Value &v)
|
||||
return JS_FALSE;
|
||||
|
||||
if (u >= obj->getArrayLength())
|
||||
obj->setDenseArrayLength(u + 1);
|
||||
obj->incDenseArrayCountBy(1);
|
||||
obj->setArrayLength(u + 1);
|
||||
}
|
||||
|
||||
obj->setDenseArrayElement(u, v);
|
||||
@ -962,11 +977,8 @@ array_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
if (js_IdIsIndex(id, &i) && i < obj->getDenseArrayCapacity() &&
|
||||
!obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
|
||||
obj->decDenseArrayCountBy(1);
|
||||
if (js_IdIsIndex(id, &i) && i < obj->getDenseArrayCapacity())
|
||||
obj->setDenseArrayElement(i, MagicValue(JS_ARRAY_HOLE));
|
||||
}
|
||||
|
||||
if (!js_SuppressDeletedProperty(cx, obj, id))
|
||||
return false;
|
||||
@ -987,9 +999,25 @@ array_trace(JSTracer *trc, JSObject *obj)
|
||||
JS_ASSERT(obj->isDenseArray());
|
||||
obj->traceProtoAndParent(trc);
|
||||
|
||||
if (!obj->dslots)
|
||||
return;
|
||||
|
||||
size_t holes = 0;
|
||||
uint32 capacity = obj->getDenseArrayCapacity();
|
||||
for (uint32 i = 0; i < capacity; i++)
|
||||
MarkValue(trc, obj->getDenseArrayElement(i), "dense_array_elems");
|
||||
for (uint32 i = 0; i < capacity; i++) {
|
||||
Value v = obj->getDenseArrayElement(i);
|
||||
if (v.isMagic(JS_ARRAY_HOLE))
|
||||
++holes;
|
||||
else
|
||||
MarkValue(trc, obj->getDenseArrayElement(i), "dense_array_elems");
|
||||
}
|
||||
|
||||
if (trc == trc->context->runtime->gcMarkingTracer &&
|
||||
holes > MIN_SPARSE_INDEX &&
|
||||
holes > capacity / 4 * 3) {
|
||||
/* This might fail, in which case we don't slowify it. */
|
||||
reinterpret_cast<JSGCTracer *>(trc)->arraysToSlowify.append(obj);
|
||||
}
|
||||
}
|
||||
|
||||
extern JSObjectOps js_ArrayObjectOps;
|
||||
@ -1064,7 +1092,8 @@ JSObject::makeDenseArraySlow(JSContext *cx)
|
||||
if (!scope)
|
||||
return JS_FALSE;
|
||||
|
||||
uint32 capacity = obj->getDenseArrayCapacity();
|
||||
/* For a brief moment the class object has NULL dslots until we slowify it during construction. */
|
||||
uint32 capacity = dslots ? obj->getDenseArrayCapacity() : 0;
|
||||
if (capacity) {
|
||||
scope->freeslot = obj->numSlots() + JS_INITIAL_NSLOTS;
|
||||
// XXX: changing the capacity like this is awful. Bug 558263 will remove
|
||||
@ -1381,19 +1410,8 @@ array_toLocaleString(JSContext *cx, uintN argc, Value *vp)
|
||||
return array_toString_sub(cx, obj, JS_TRUE, NULL, vp);
|
||||
}
|
||||
|
||||
enum TargetElementsType {
|
||||
TargetElementsAllHoles,
|
||||
TargetElementsMayContainValues
|
||||
};
|
||||
|
||||
enum SourceVectorType {
|
||||
SourceVectorAllValues,
|
||||
SourceVectorMayContainHoles
|
||||
};
|
||||
|
||||
static JSBool
|
||||
InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Value *vector,
|
||||
TargetElementsType targetType, SourceVectorType vectorType)
|
||||
InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Value *vector)
|
||||
{
|
||||
JS_ASSERT(count < MAXINDEX);
|
||||
|
||||
@ -1404,54 +1422,16 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Valu
|
||||
if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
|
||||
start <= MAXINDEX - count && !INDEX_TOO_BIG(start + count)) {
|
||||
|
||||
#ifdef DEBUG_jwalden
|
||||
{
|
||||
/* Verify that overwriteType and writeType were accurate. */
|
||||
AutoIdRooter idr(cx);
|
||||
for (jsuint i = 0; i < count; i++) {
|
||||
JS_ASSERT_IF(vectorType == SourceVectorAllValues, !vector[i].isMagic(JS_ARRAY_HOLE));
|
||||
|
||||
jsdouble index = jsdouble(start) + i;
|
||||
if (targetType == TargetElementsAllHoles && index < jsuint(-1)) {
|
||||
JS_ASSERT(ReallyBigIndexToId(cx, index, idr.addr()));
|
||||
JSObject* obj2;
|
||||
JSProperty* prop;
|
||||
JS_ASSERT(obj->lookupProperty(cx, idr.id(), &obj2, &prop));
|
||||
JS_ASSERT(!prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
jsuint newlen = start + count;
|
||||
JS_ASSERT(jsdouble(start) + count == jsdouble(newlen));
|
||||
if (!obj->ensureDenseArrayElements(cx, newlen))
|
||||
return JS_FALSE;
|
||||
|
||||
if (newlen > obj->getArrayLength())
|
||||
obj->setDenseArrayLength(newlen);
|
||||
obj->setArrayLength(newlen);
|
||||
|
||||
JS_ASSERT(count < uint32(-1) / sizeof(Value));
|
||||
if (targetType == TargetElementsMayContainValues) {
|
||||
jsuint valueCount = 0;
|
||||
for (jsuint i = 0; i < count; i++) {
|
||||
if (!obj->getDenseArrayElement(start + i).isMagic(JS_ARRAY_HOLE))
|
||||
valueCount++;
|
||||
}
|
||||
JS_ASSERT(obj->getDenseArrayCount() >= valueCount);
|
||||
obj->decDenseArrayCountBy(valueCount);
|
||||
}
|
||||
memcpy(obj->getDenseArrayElements() + start, vector, sizeof(jsval) * count);
|
||||
if (vectorType == SourceVectorAllValues) {
|
||||
obj->incDenseArrayCountBy(count);
|
||||
} else {
|
||||
jsuint valueCount = 0;
|
||||
for (jsuint i = 0; i < count; i++) {
|
||||
if (!obj->getDenseArrayElement(start + i).isMagic(JS_ARRAY_HOLE))
|
||||
valueCount++;
|
||||
}
|
||||
obj->incDenseArrayCountBy(valueCount);
|
||||
}
|
||||
JS_ASSERT_IF(count != 0, !obj->getDenseArrayElement(newlen - 1).isMagic(JS_ARRAY_HOLE));
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -1488,37 +1468,18 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Valu
|
||||
}
|
||||
|
||||
static JSBool
|
||||
InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector,
|
||||
bool holey = false)
|
||||
InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector)
|
||||
{
|
||||
JS_ASSERT(obj->isArray());
|
||||
|
||||
if (vector) {
|
||||
JS_ASSERT(obj->isDenseArray());
|
||||
obj->setDenseArrayLength(length);
|
||||
if (!obj->ensureDenseArrayElements(cx, length))
|
||||
return JS_FALSE;
|
||||
|
||||
jsuint count = length;
|
||||
if (!holey) {
|
||||
memcpy(obj->getDenseArrayElements(), vector, length * sizeof(Value));
|
||||
} else {
|
||||
for (jsuint i = 0; i < length; i++) {
|
||||
if (vector[i].isMagic(JS_ARRAY_HOLE))
|
||||
--count;
|
||||
obj->setDenseArrayElement(i, vector[i]);
|
||||
}
|
||||
}
|
||||
obj->setDenseArrayCount(count);
|
||||
} else {
|
||||
if (obj->isDenseArray()) {
|
||||
obj->setDenseArrayLength(length);
|
||||
obj->setDenseArrayCount(0);
|
||||
} else {
|
||||
obj->setSlowArrayLength(length);
|
||||
}
|
||||
}
|
||||
return JS_TRUE;
|
||||
JS_ASSERT(obj->isDenseArray());
|
||||
obj->setArrayLength(length);
|
||||
if (!vector || !length)
|
||||
return obj->ensureDenseArrayElements(cx, ARRAY_CAPACITY_MIN);
|
||||
if (!obj->ensureDenseArrayElements(cx, length))
|
||||
return false;
|
||||
memcpy(obj->getDenseArrayElements(), vector, length * sizeof(Value));
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2043,10 +2004,8 @@ array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
* InitArrayElements easier.
|
||||
*/
|
||||
tvr.changeLength(newlen);
|
||||
if (!InitArrayElements(cx, obj, 0, newlen, vec, TargetElementsMayContainValues,
|
||||
SourceVectorAllValues)) {
|
||||
if (!InitArrayElements(cx, obj, 0, newlen, vec))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set undefs that sorted after the rest of elements. */
|
||||
@ -2077,10 +2036,8 @@ array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *
|
||||
|
||||
if (!js_GetLengthProperty(cx, obj, &length))
|
||||
return JS_FALSE;
|
||||
if (!InitArrayElements(cx, obj, length, argc, argv, TargetElementsMayContainValues,
|
||||
SourceVectorAllValues)) {
|
||||
if (!InitArrayElements(cx, obj, length, argc, argv))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* Per ECMA-262, return the new array length. */
|
||||
jsdouble newlength = length + jsdouble(argc);
|
||||
@ -2101,10 +2058,9 @@ array_push1_dense(JSContext* cx, JSObject* obj, const Value &v, Value *rval)
|
||||
|
||||
if (!obj->ensureDenseArrayElements(cx, length + 1))
|
||||
return JS_FALSE;
|
||||
obj->setDenseArrayLength(length + 1);
|
||||
obj->setArrayLength(length + 1);
|
||||
|
||||
JS_ASSERT(obj->getDenseArrayElement(length).isMagic(JS_ARRAY_HOLE));
|
||||
obj->incDenseArrayCountBy(1);
|
||||
obj->setDenseArrayElement(length, v);
|
||||
rval->setNumber(obj->getArrayLength());
|
||||
return JS_TRUE;
|
||||
@ -2127,8 +2083,7 @@ ArrayCompPushImpl(JSContext *cx, JSObject *obj, const Value &v)
|
||||
if (!obj->ensureDenseArrayElements(cx, length + 1))
|
||||
return JS_FALSE;
|
||||
}
|
||||
obj->setDenseArrayLength(length + 1);
|
||||
obj->incDenseArrayCountBy(1);
|
||||
obj->setArrayLength(length + 1);
|
||||
obj->setDenseArrayElement(length, v);
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -2198,7 +2153,7 @@ array_pop_dense(JSContext *cx, JSObject* obj, Value *vp)
|
||||
return JS_FALSE;
|
||||
if (!hole && !DeleteArrayElement(cx, obj, index))
|
||||
return JS_FALSE;
|
||||
obj->setDenseArrayLength(index);
|
||||
obj->setArrayLength(index);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -2233,12 +2188,10 @@ array_shift(JSContext *cx, uintN argc, Value *vp)
|
||||
*vp = obj->getDenseArrayElement(0);
|
||||
if (vp->isMagic(JS_ARRAY_HOLE))
|
||||
vp->setUndefined();
|
||||
else
|
||||
obj->decDenseArrayCountBy(1);
|
||||
Value *elems = obj->getDenseArrayElements();
|
||||
memmove(elems, elems + 1, length * sizeof(jsval));
|
||||
obj->setDenseArrayElement(length, MagicValue(JS_ARRAY_HOLE));
|
||||
obj->setDenseArrayLength(length);
|
||||
obj->setArrayLength(length);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -2304,7 +2257,7 @@ array_unshift(JSContext *cx, uintN argc, Value *vp)
|
||||
}
|
||||
|
||||
/* Copy from argv to the bottom of the array. */
|
||||
if (!InitArrayElements(cx, obj, 0, argc, argv, TargetElementsAllHoles, SourceVectorAllValues))
|
||||
if (!InitArrayElements(cx, obj, 0, argc, argv))
|
||||
return JS_FALSE;
|
||||
|
||||
newlen += argc;
|
||||
@ -2384,10 +2337,8 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
|
||||
if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
|
||||
!js_PrototypeHasIndexedProperties(cx, obj2) &&
|
||||
end <= obj->getDenseArrayCapacity()) {
|
||||
if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin,
|
||||
obj->getDenseArrayCount() != obj->getArrayLength())) {
|
||||
if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin))
|
||||
return JS_FALSE;
|
||||
}
|
||||
} else {
|
||||
for (last = begin; last < end; last++) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
|
||||
@ -2419,14 +2370,10 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
|
||||
Value *srcbeg = arraybeg + last - 1;
|
||||
Value *srcend = arraybeg + end - 1;
|
||||
Value *dstbeg = srcbeg + delta;
|
||||
for (Value *src = srcbeg, *dst = dstbeg; src > srcend; --src, --dst) {
|
||||
Value srcval = *src;
|
||||
if (JS_UNLIKELY(!srcval.isMagic(JS_ARRAY_HOLE) && dst->isMagic(JS_ARRAY_HOLE)))
|
||||
obj->incDenseArrayCountBy(1);
|
||||
*dst = srcval;
|
||||
}
|
||||
for (Value *src = srcbeg, *dst = dstbeg; src > srcend; --src, --dst)
|
||||
*dst = *src;
|
||||
|
||||
obj->setDenseArrayLength(obj->getArrayLength() + delta);
|
||||
obj->setArrayLength(obj->getArrayLength() + delta);
|
||||
} else {
|
||||
/* (uint) end could be 0, so we can't use a vanilla >= test. */
|
||||
while (last-- > end) {
|
||||
@ -2447,12 +2394,8 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
|
||||
Value *srcbeg = arraybeg + end;
|
||||
Value *srcend = arraybeg + length;
|
||||
Value *dstbeg = srcbeg - delta;
|
||||
for (Value *src = srcbeg, *dst = dstbeg; src < srcend; ++src, ++dst) {
|
||||
Value srcval = *src;
|
||||
if (JS_UNLIKELY(!srcval.isMagic(JS_ARRAY_HOLE) && dst->isMagic(JS_ARRAY_HOLE)))
|
||||
obj->incDenseArrayCountBy(1);
|
||||
*dst = srcval;
|
||||
}
|
||||
for (Value *src = srcbeg, *dst = dstbeg; src < srcend; ++src, ++dst)
|
||||
*dst = *src;
|
||||
} else {
|
||||
for (last = end; last < length; last++) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
|
||||
@ -2469,8 +2412,7 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
|
||||
* Copy from argv into the hole to complete the splice, and update length in
|
||||
* case we deleted elements from the end.
|
||||
*/
|
||||
return InitArrayElements(cx, obj, begin, argc, argv, TargetElementsMayContainValues,
|
||||
SourceVectorAllValues) &&
|
||||
return InitArrayElements(cx, obj, begin, argc, argv) &&
|
||||
js_SetLengthProperty(cx, obj, length);
|
||||
}
|
||||
|
||||
@ -2498,11 +2440,10 @@ array_concat(JSContext *cx, uintN argc, Value *vp)
|
||||
*/
|
||||
length = aobj->getArrayLength();
|
||||
jsuint capacity = aobj->getDenseArrayCapacity();
|
||||
nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements(),
|
||||
aobj->getDenseArrayCount() != length);
|
||||
nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements());
|
||||
if (!nobj)
|
||||
return JS_FALSE;
|
||||
nobj->setDenseArrayLength(length);
|
||||
nobj->setArrayLength(length);
|
||||
vp->setObject(*nobj);
|
||||
if (argc == 0)
|
||||
return JS_TRUE;
|
||||
@ -2614,8 +2555,7 @@ array_slice(JSContext *cx, uintN argc, Value *vp)
|
||||
|
||||
if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() &&
|
||||
!js_PrototypeHasIndexedProperties(cx, obj)) {
|
||||
nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin,
|
||||
obj->getDenseArrayCount() != obj->getArrayLength());
|
||||
nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin);
|
||||
if (!nobj)
|
||||
return JS_FALSE;
|
||||
vp->setObject(*nobj);
|
||||
@ -3058,7 +2998,7 @@ js_Array(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
|
||||
}
|
||||
|
||||
JSObject* JS_FASTCALL
|
||||
js_NewEmptyArray(JSContext* cx, JSObject* proto)
|
||||
js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len)
|
||||
{
|
||||
JS_ASSERT(proto->isArray());
|
||||
|
||||
@ -3068,40 +3008,9 @@ js_NewEmptyArray(JSContext* cx, JSObject* proto)
|
||||
|
||||
/* Initialize all fields of JSObject. */
|
||||
obj->map = const_cast<JSObjectMap *>(&SharedArrayMap);
|
||||
|
||||
obj->init(&js_ArrayClass, proto, proto->getParent(), NullValue());
|
||||
obj->setDenseArrayLength(0);
|
||||
obj->setDenseArrayCount(0);
|
||||
return obj;
|
||||
}
|
||||
#ifdef JS_TRACER
|
||||
JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, nanojit::ACC_STORE_ANY)
|
||||
#endif
|
||||
|
||||
JSObject* JS_FASTCALL
|
||||
js_NewEmptyArrayWithLength(JSContext* cx, JSObject* proto, int32 len)
|
||||
{
|
||||
if (len < 0)
|
||||
return NULL;
|
||||
JSObject *obj = js_NewEmptyArray(cx, proto);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
obj->setDenseArrayLength(len);
|
||||
return obj;
|
||||
}
|
||||
#ifdef JS_TRACER
|
||||
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewEmptyArrayWithLength, CONTEXT, OBJECT, INT32, 0,
|
||||
nanojit::ACC_STORE_ANY)
|
||||
#endif
|
||||
|
||||
JSObject* JS_FASTCALL
|
||||
js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len)
|
||||
{
|
||||
JSObject* obj = js_NewEmptyArray(cx, proto);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
obj->setDenseArrayLength(len);
|
||||
if (!obj->resizeDenseArrayElements(cx, 0, JS_MAX(len, ARRAY_CAPACITY_MIN)))
|
||||
obj->setArrayLength(len);
|
||||
if (!obj->growDenseArrayElements(cx, 0, JS_MAX(len, ARRAY_CAPACITY_MIN)))
|
||||
return NULL;
|
||||
return obj;
|
||||
}
|
||||
@ -3110,20 +3019,29 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewArrayWithSlots, CONTEXT, OBJECT, UINT
|
||||
nanojit::ACC_STORE_ANY)
|
||||
#endif
|
||||
|
||||
JSObject* JS_FASTCALL
|
||||
js_NewEmptyArray(JSContext* cx, JSObject* proto)
|
||||
{
|
||||
return js_NewArrayWithSlots(cx, proto, 0);
|
||||
}
|
||||
#ifdef JS_TRACER
|
||||
JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, nanojit::ACC_STORE_ANY)
|
||||
#endif
|
||||
|
||||
JSObject *
|
||||
js_InitArrayClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *proto = js_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1,
|
||||
NULL, array_methods, NULL, array_static_methods);
|
||||
|
||||
/* Initialize the Array prototype object so it gets a length property. */
|
||||
if (!proto || !InitArrayObject(cx, proto, 0, NULL))
|
||||
if (!proto)
|
||||
return NULL;
|
||||
proto->setArrayLength(0);
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector, bool holey)
|
||||
js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector)
|
||||
{
|
||||
JSObject *obj = NewDenseArrayObject(cx);
|
||||
if (!obj)
|
||||
@ -3137,7 +3055,7 @@ js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector, bool holey)
|
||||
|
||||
{
|
||||
AutoObjectRooter tvr(cx, obj);
|
||||
if (!InitArrayObject(cx, obj, length, vector, holey))
|
||||
if (!InitArrayObject(cx, obj, length, vector))
|
||||
obj = NULL;
|
||||
}
|
||||
|
||||
@ -3151,7 +3069,7 @@ js_NewSlowArrayObject(JSContext *cx)
|
||||
{
|
||||
JSObject *obj = NewObject(cx, &js_SlowArrayClass, NULL, NULL);
|
||||
if (obj)
|
||||
obj->setSlowArrayLength(0);
|
||||
obj->setArrayLength(0);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -3178,8 +3096,7 @@ js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
|
||||
array->isDenseArray()) ? "dense" : "sparse",
|
||||
array->getArrayLength());
|
||||
if (array->isDenseArray()) {
|
||||
fprintf(stderr, ", count %lu, capacity %lu",
|
||||
array->getDenseArrayCount(),
|
||||
fprintf(stderr, ", capacity %lu",
|
||||
array->getDenseArrayCapacity());
|
||||
}
|
||||
fputs(")\n", stderr);
|
||||
@ -3247,27 +3164,6 @@ js_CoerceArrayToCanvasImageData(JSObject *obj, jsuint offset, jsuint count,
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js_NewArrayObjectWithCapacity(JSContext *cx, uint32_t capacity, jsval **vector)
|
||||
{
|
||||
JSObject *obj = js_NewArrayObject(cx, capacity, NULL);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
AutoObjectRooter tvr(cx, obj);
|
||||
if (!obj->ensureDenseArrayElements(cx, capacity, JS_FALSE))
|
||||
obj = NULL;
|
||||
|
||||
/* Set/clear newborn root, in case we lost it. */
|
||||
cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj;
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
obj->setDenseArrayCount(capacity);
|
||||
*vector = Jsvalify(obj->getDenseArrayElements());
|
||||
return obj;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_IsDensePrimitiveArray(JSObject *obj)
|
||||
{
|
||||
@ -3311,8 +3207,6 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone)
|
||||
if (!vector.reserve(jsvalCount))
|
||||
return JS_FALSE;
|
||||
|
||||
jsuint holeCount = 0;
|
||||
|
||||
for (jsuint i = 0; i < jsvalCount; i++) {
|
||||
const Value &val = obj->dslots[i];
|
||||
|
||||
@ -3320,8 +3214,6 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone)
|
||||
// Strings must be made immutable before being copied to a clone.
|
||||
if (!js_MakeStringImmutable(cx, val.toString()))
|
||||
return JS_FALSE;
|
||||
} else if (val.isMagic(JS_ARRAY_HOLE)) {
|
||||
holeCount++;
|
||||
} else if (val.isObject()) {
|
||||
/*
|
||||
* This wasn't an array of primitives. Return JS_TRUE but a null
|
||||
@ -3334,16 +3226,10 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone)
|
||||
vector.append(val);
|
||||
}
|
||||
|
||||
jsval *buffer;
|
||||
*clone = js_NewArrayObjectWithCapacity(cx, jsvalCount, &buffer);
|
||||
*clone = js_NewArrayObject(cx, jsvalCount, vector.begin());
|
||||
if (!*clone)
|
||||
return JS_FALSE;
|
||||
|
||||
AutoObjectRooter cloneRoot(cx, *clone);
|
||||
|
||||
memcpy(buffer, vector.begin(), jsvalCount * sizeof (jsval));
|
||||
(*clone)->setDenseArrayLength(length);
|
||||
(*clone)->setDenseArrayCount(length - holeCount);
|
||||
(*clone)->setArrayLength(length);
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ extern JSObject * JS_FASTCALL
|
||||
js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len);
|
||||
|
||||
extern JSObject *
|
||||
js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector, bool holey = false);
|
||||
js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector);
|
||||
|
||||
/* Create an array object that starts out already made slow/sparse. */
|
||||
extern JSObject *
|
||||
|
@ -577,7 +577,6 @@ JS_DECLARE_CALLINFO(js_Array_dense_setelem)
|
||||
JS_DECLARE_CALLINFO(js_Array_dense_setelem_int)
|
||||
JS_DECLARE_CALLINFO(js_Array_dense_setelem_double)
|
||||
JS_DECLARE_CALLINFO(js_NewEmptyArray)
|
||||
JS_DECLARE_CALLINFO(js_NewEmptyArrayWithLength)
|
||||
JS_DECLARE_CALLINFO(js_NewArrayWithSlots)
|
||||
JS_DECLARE_CALLINFO(js_ArrayCompPush_tn)
|
||||
|
||||
|
@ -747,7 +747,6 @@ js_PurgeThreads(JSContext *cx)
|
||||
e.removeFront();
|
||||
} else {
|
||||
thread->data.purge(cx);
|
||||
thread->gcThreadMallocBytes = JS_GC_THREAD_MALLOC_LIMIT;
|
||||
}
|
||||
}
|
||||
#else
|
||||
@ -1888,54 +1887,48 @@ js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
|
||||
JSBool
|
||||
js_InvokeOperationCallback(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(cx->interruptFlags & JSContext::INTERRUPT_OPERATION_CALLBACK);
|
||||
JS_ASSERT_REQUEST_DEPTH(cx);
|
||||
JS_ASSERT(JS_THREAD_DATA(cx)->interruptFlags & JSThreadData::INTERRUPT_OPERATION_CALLBACK);
|
||||
|
||||
/*
|
||||
* Reset the callback flag first, then yield. If another thread is racing
|
||||
* us here we will accumulate another callback request which will be
|
||||
* serviced at the next opportunity.
|
||||
*/
|
||||
JS_ATOMIC_CLEAR_MASK((jsword*)&cx->interruptFlags,
|
||||
JSContext::INTERRUPT_OPERATION_CALLBACK);
|
||||
JS_ATOMIC_CLEAR_MASK((jsword*)&JS_THREAD_DATA(cx)->interruptFlags,
|
||||
JSThreadData::INTERRUPT_OPERATION_CALLBACK);
|
||||
|
||||
/*
|
||||
* Unless we are going to run the GC, we automatically yield the current
|
||||
* context every time the operation callback is hit since we might be
|
||||
* called as a result of an impending GC, which would deadlock if we do
|
||||
* not yield. Operation callbacks are supposed to happen rarely (seconds,
|
||||
* not milliseconds) so it is acceptable to yield at every callback.
|
||||
* Ideally this should never be hit. The embedding should call JS_MaybeGC()
|
||||
* to schedule preemptive GCs.
|
||||
*/
|
||||
JSRuntime *rt = cx->runtime;
|
||||
if (rt->gcIsNeeded) {
|
||||
js_GC(cx, GC_NORMAL);
|
||||
|
||||
/*
|
||||
* On trace we can exceed the GC quota, see comments in NewGCArena. So
|
||||
* we check the quota and report OOM here when we are off trace.
|
||||
*/
|
||||
bool delayedOutOfMemory;
|
||||
JS_LOCK_GC(rt);
|
||||
delayedOutOfMemory = (rt->gcBytes > rt->gcMaxBytes);
|
||||
JS_UNLOCK_GC(rt);
|
||||
if (delayedOutOfMemory) {
|
||||
if (rt->gcIsNeeded || rt->overQuota()) {
|
||||
JS_GC(cx);
|
||||
/* If we are still over quota after the GC, report an error. */
|
||||
if (rt->overQuota()) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef JS_THREADSAFE
|
||||
else {
|
||||
JS_YieldRequest(cx);
|
||||
}
|
||||
#endif
|
||||
|
||||
JSOperationCallback cb = cx->operationCallback;
|
||||
/*
|
||||
* Automatically yield the current context every time the operation callback
|
||||
* is hit since we might be called as a result of an impending GC, which
|
||||
* would deadlock if we do not yield. Operation callbacks are supposed to
|
||||
* happen rarely (seconds, not milliseconds) so it is acceptable to yield at
|
||||
* every callback.
|
||||
*/
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_YieldRequest(cx);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Important: Additional callbacks can occur inside the callback handler
|
||||
* if it re-enters the JS engine. The embedding must ensure that the
|
||||
* callback is disconnected before attempting such re-entry.
|
||||
*/
|
||||
|
||||
JSOperationCallback cb = cx->operationCallback;
|
||||
return !cb || cb(cx);
|
||||
}
|
||||
|
||||
@ -1943,7 +1936,7 @@ JSBool
|
||||
js_HandleExecutionInterrupt(JSContext *cx)
|
||||
{
|
||||
JSBool result = JS_TRUE;
|
||||
if (cx->interruptFlags & JSContext::INTERRUPT_OPERATION_CALLBACK)
|
||||
if (JS_THREAD_DATA(cx)->interruptFlags & JSThreadData::INTERRUPT_OPERATION_CALLBACK)
|
||||
result = js_InvokeOperationCallback(cx) && result;
|
||||
return result;
|
||||
}
|
||||
@ -1954,9 +1947,8 @@ js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked)
|
||||
#ifdef JS_THREADSAFE
|
||||
Conditionally<AutoLockGC> lockIf(!gcLocked, rt);
|
||||
#endif
|
||||
JSContext *iter = NULL;
|
||||
while (JSContext *acx = js_ContextIterator(rt, JS_FALSE, &iter))
|
||||
JS_TriggerOperationCallback(acx);
|
||||
for (ThreadDataIter i(rt); !i.empty(); i.popFront())
|
||||
i.threadData()->triggerOperationCallback();
|
||||
}
|
||||
|
||||
JSStackFrame *
|
||||
@ -2174,47 +2166,6 @@ JSContext::containingSegment(const JSStackFrame *target)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
JSContext::checkMallocGCPressure(void *p)
|
||||
{
|
||||
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;
|
||||
|
||||
/*
|
||||
* Trigger the GC on memory pressure but only if we are inside a request
|
||||
* and not inside a GC.
|
||||
*/
|
||||
if (runtime->isGCMallocLimitReached() && requestDepth != 0)
|
||||
#endif
|
||||
{
|
||||
if (!runtime->gcRunning) {
|
||||
JS_ASSERT(runtime->isGCMallocLimitReached());
|
||||
runtime->gcMallocBytes = -1;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
JSContext::isConstructing()
|
||||
{
|
||||
|
184
js/src/jscntxt.h
184
js/src/jscntxt.h
@ -989,18 +989,17 @@ struct JSPendingProxyOperation {
|
||||
};
|
||||
|
||||
struct JSThreadData {
|
||||
/*
|
||||
* If this flag is set, we were asked to call back the operation callback
|
||||
* as soon as possible.
|
||||
*/
|
||||
volatile int32 interruptFlags;
|
||||
|
||||
JSGCFreeLists gcFreeLists;
|
||||
|
||||
/* Keeper of the contiguous stack used by all contexts in this thread. */
|
||||
js::StackSpace stackSpace;
|
||||
|
||||
/*
|
||||
* Flag indicating that we are waiving any soft limits on the GC heap
|
||||
* because we want allocations to be infallible (except when we hit
|
||||
* a hard quota).
|
||||
*/
|
||||
bool waiveGCQuota;
|
||||
|
||||
/*
|
||||
* The GSN cache is per thread since even multi-cx-per-thread embeddings
|
||||
* do not interleave js_GetSrcNote calls.
|
||||
@ -1055,6 +1054,18 @@ struct JSThreadData {
|
||||
void finish();
|
||||
void mark(JSTracer *trc);
|
||||
void purge(JSContext *cx);
|
||||
|
||||
static const jsword INTERRUPT_OPERATION_CALLBACK = 0x1;
|
||||
|
||||
void triggerOperationCallback() {
|
||||
/*
|
||||
* Use JS_ATOMIC_SET in the hope that it will make sure the write will
|
||||
* become immediately visible to other processors polling the flag.
|
||||
* Note that we only care about visibility here, not read/write
|
||||
* ordering.
|
||||
*/
|
||||
JS_ATOMIC_SET_MASK((jsword *) (&interruptFlags), INTERRUPT_OPERATION_CALLBACK);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
@ -1078,12 +1089,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;
|
||||
@ -1102,13 +1107,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 *
|
||||
@ -1231,6 +1229,7 @@ struct JSCompartment {
|
||||
|
||||
struct JSGCTracer : public JSTracer {
|
||||
uint32 color;
|
||||
js::Vector<JSObject *, 0, js::SystemAllocPolicy> arraysToSlowify;
|
||||
};
|
||||
|
||||
struct JSRuntime {
|
||||
@ -1316,7 +1315,7 @@ struct JSRuntime {
|
||||
* Malloc counter to measure memory pressure for GC scheduling. It runs
|
||||
* from gcMaxMallocBytes down to zero.
|
||||
*/
|
||||
ptrdiff_t gcMallocBytes;
|
||||
volatile ptrdiff_t gcMallocBytes;
|
||||
|
||||
/* See comments before DelayMarkingChildren is jsgc.cpp. */
|
||||
JSGCArena *gcUnmarkedArenaStackTop;
|
||||
@ -1598,16 +1597,35 @@ struct JSRuntime {
|
||||
void setGCTriggerFactor(uint32 factor);
|
||||
void setGCLastBytes(size_t lastBytes);
|
||||
|
||||
void* malloc(size_t bytes) { return ::js_malloc(bytes); }
|
||||
bool gcQuotaReached() {
|
||||
return gcBytes >= gcMaxBytes;
|
||||
}
|
||||
|
||||
void* calloc(size_t bytes) { return ::js_calloc(bytes); }
|
||||
void updateMallocCounter(size_t bytes) {
|
||||
gcMallocBytes -= bytes; /* We tolerate races and lost counts here. */
|
||||
}
|
||||
|
||||
void* realloc(void* p, size_t bytes) { return ::js_realloc(p, bytes); }
|
||||
bool mallocQuotaReached() {
|
||||
return gcMallocBytes <= 0;
|
||||
}
|
||||
|
||||
bool overQuota() {
|
||||
return gcQuotaReached() || mallocQuotaReached();
|
||||
}
|
||||
|
||||
void* malloc(size_t bytes) { updateMallocCounter(bytes); return ::js_malloc(bytes); }
|
||||
|
||||
void* calloc(size_t bytes) { updateMallocCounter(bytes); return ::js_calloc(bytes); }
|
||||
|
||||
void* realloc(void* p, size_t bytes) {
|
||||
void* q = ::js_realloc(p, bytes);
|
||||
if (p != q)
|
||||
updateMallocCounter(bytes);
|
||||
return q;
|
||||
}
|
||||
|
||||
void free(void* p) { ::js_free(p); }
|
||||
|
||||
bool isGCMallocLimitReached() const { return gcMallocBytes <= 0; }
|
||||
|
||||
void resetGCMallocBytes() { gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); }
|
||||
|
||||
void setGCMaxMallocBytes(size_t value) {
|
||||
@ -1702,18 +1720,13 @@ struct JSRegExpStatics {
|
||||
void clear();
|
||||
};
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
js_ReportOutOfMemory(JSContext *cx);
|
||||
|
||||
struct JSContext
|
||||
{
|
||||
explicit JSContext(JSRuntime *rt);
|
||||
|
||||
/*
|
||||
* If this flag is non-zero, we were asked to interrupt execution as soon
|
||||
* as possible. The bits below describe the reason.
|
||||
*/
|
||||
volatile jsword interruptFlags;
|
||||
|
||||
static const jsword INTERRUPT_OPERATION_CALLBACK = 0x1;
|
||||
|
||||
/* JSRuntime contextList linkage. */
|
||||
JSCList link;
|
||||
|
||||
@ -1982,76 +1995,40 @@ struct JSContext
|
||||
js::BackgroundSweepTask *gcSweepTask;
|
||||
#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 triggerGC() {
|
||||
if (!runtime->gcIsNeeded) {
|
||||
runtime->gcIsNeeded = true;
|
||||
JS_TriggerAllOperationCallbacks(runtime);
|
||||
}
|
||||
}
|
||||
|
||||
inline void *reportIfOutOfMemory(void *p) {
|
||||
if (p) {
|
||||
if (runtime->mallocQuotaReached())
|
||||
triggerGC();
|
||||
return p;
|
||||
}
|
||||
js_ReportOutOfMemory(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline void* malloc(size_t bytes) {
|
||||
JS_ASSERT(bytes != 0);
|
||||
void *p = runtime->malloc(bytes);
|
||||
updateMallocCounter(p, bytes);
|
||||
return p;
|
||||
return reportIfOutOfMemory(runtime->malloc(bytes));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
inline void* calloc(size_t bytes) {
|
||||
JS_ASSERT(bytes != 0);
|
||||
void *p = runtime->calloc(bytes);
|
||||
updateMallocCounter(p, bytes);
|
||||
return p;
|
||||
return reportIfOutOfMemory(runtime->calloc(bytes));
|
||||
}
|
||||
|
||||
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 reportIfOutOfMemory(runtime->realloc(p, bytes));
|
||||
}
|
||||
|
||||
inline void free(void* p) {
|
||||
@ -2118,16 +2095,6 @@ struct JSContext
|
||||
#else
|
||||
void assertValidStackDepth(uintN /*depth*/) {}
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* The allocation code calls the function to indicate either OOM failure
|
||||
* when p is null or that a memory pressure counter has reached some
|
||||
* threshold when p is not null. The function takes the pointer and not
|
||||
* a boolean flag to minimize the amount of code in its inlined callers.
|
||||
*/
|
||||
JS_FRIEND_API(void) checkMallocGCPressure(void *p);
|
||||
};
|
||||
|
||||
JS_ALWAYS_INLINE JSObject *
|
||||
@ -2236,11 +2203,17 @@ class AutoGCRooter {
|
||||
: down(cx->autoGCRooters), tag(tag), context(cx)
|
||||
{
|
||||
JS_ASSERT(this != cx->autoGCRooters);
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(cx->requestDepth != 0);
|
||||
#endif
|
||||
cx->autoGCRooters = this;
|
||||
}
|
||||
|
||||
~AutoGCRooter() {
|
||||
JS_ASSERT(this == context->autoGCRooters);
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(context->requestDepth != 0);
|
||||
#endif
|
||||
context->autoGCRooters = down;
|
||||
}
|
||||
|
||||
@ -2880,9 +2853,6 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
|
||||
bool charArgs, va_list ap);
|
||||
#endif
|
||||
|
||||
extern void
|
||||
js_ReportOutOfMemory(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Report that cx->scriptStackQuota is exhausted.
|
||||
*/
|
||||
@ -2959,14 +2929,20 @@ extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
|
||||
# define JS_CHECK_STACK_SIZE(cx, lval) ((jsuword)&(lval) > (cx)->stackLimit)
|
||||
#endif
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
# define JS_ASSERT_REQUEST_DEPTH(cx) JS_ASSERT((cx)->requestDepth >= 1)
|
||||
#else
|
||||
# define JS_ASSERT_REQUEST_DEPTH(cx) ((void) 0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If the operation callback flag was set, call the operation callback.
|
||||
* This macro can run the full GC. Return true if it is OK to continue and
|
||||
* false otherwise.
|
||||
*/
|
||||
#define JS_CHECK_OPERATION_LIMIT(cx) \
|
||||
(!((cx)->interruptFlags & JSContext::INTERRUPT_OPERATION_CALLBACK) || \
|
||||
js_InvokeOperationCallback(cx))
|
||||
#define JS_CHECK_OPERATION_LIMIT(cx) \
|
||||
(JS_ASSERT_REQUEST_DEPTH(cx), \
|
||||
(!(JS_THREAD_DATA(cx)->interruptFlags & JSThreadData::INTERRUPT_OPERATION_CALLBACK) || js_InvokeOperationCallback(cx)))
|
||||
|
||||
/*
|
||||
* Invoke the operation callback and return false if the current execution
|
||||
|
144
js/src/jsgc.cpp
144
js/src/jsgc.cpp
@ -641,19 +641,8 @@ static JSGCArena *
|
||||
NewGCArena(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
if (!JS_THREAD_DATA(cx)->waiveGCQuota && rt->gcBytes >= rt->gcMaxBytes) {
|
||||
/*
|
||||
* FIXME bug 524051 We cannot run a last-ditch GC on trace for now, so
|
||||
* just pretend we are out of memory which will throw us off trace and
|
||||
* we will re-try this code path from the interpreter.
|
||||
*/
|
||||
if (!JS_ON_TRACE(cx))
|
||||
return NULL;
|
||||
js_TriggerGC(cx, true);
|
||||
}
|
||||
|
||||
size_t nchunks = rt->gcChunks.length();
|
||||
|
||||
JSGCChunkInfo *ci;
|
||||
for (;; ++rt->gcChunkCursor) {
|
||||
if (rt->gcChunkCursor == nchunks) {
|
||||
@ -664,6 +653,7 @@ NewGCArena(JSContext *cx)
|
||||
if (ci->numFreeArenas != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ci) {
|
||||
if (!rt->gcChunks.reserve(nchunks + 1))
|
||||
return NULL;
|
||||
@ -1723,41 +1713,6 @@ JSGCFreeLists::moveTo(JSGCFreeLists *another)
|
||||
JS_ASSERT(isEmpty());
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsGCThresholdReached(JSRuntime *rt)
|
||||
{
|
||||
#ifdef JS_GC_ZEAL
|
||||
if (rt->gcZeal >= 1)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Since the initial value of the gcLastBytes parameter is not equal to
|
||||
* zero (see the js_InitGC function) the return value is false when
|
||||
* the gcBytes value is close to zero at the JS engine start.
|
||||
*/
|
||||
return rt->isGCMallocLimitReached() || rt->gcBytes >= rt->gcTriggerBytes;
|
||||
}
|
||||
|
||||
static void
|
||||
LastDitchGC(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(!JS_ON_TRACE(cx));
|
||||
|
||||
/* The last ditch GC preserves weak roots and all atoms. */
|
||||
AutoPreserveWeakRoots save(cx);
|
||||
AutoKeepAtoms keep(cx->runtime);
|
||||
|
||||
/*
|
||||
* Keep rt->gcLock across the call into the GC so we don't starve and
|
||||
* lose to racing threads who deplete the heap just after the GC has
|
||||
* replenished it (or has synchronized with a racing GC that collected a
|
||||
* bunch of garbage). This unfair scheduling can happen on certain
|
||||
* operating systems. For the gory details, see bug 162779.
|
||||
*/
|
||||
js_GC(cx, GC_LOCK_HELD);
|
||||
}
|
||||
|
||||
static JSGCThing *
|
||||
RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
|
||||
{
|
||||
@ -1774,45 +1729,28 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool canGC = !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota;
|
||||
bool doGC = canGC && IsGCThresholdReached(rt);
|
||||
arenaList = &rt->gcArenaList[thingKind];
|
||||
for (;;) {
|
||||
if (doGC) {
|
||||
LastDitchGC(cx);
|
||||
METER(cx->runtime->gcStats.arenaStats[thingKind].retry++);
|
||||
canGC = false;
|
||||
|
||||
/*
|
||||
* The JSGC_END callback can legitimately allocate new GC
|
||||
* things and populate the free list. If that happens, just
|
||||
* return that list head.
|
||||
*/
|
||||
JSGCThing *freeList = JS_THREAD_DATA(cx)->gcFreeLists.finalizables[thingKind];
|
||||
if (freeList)
|
||||
return freeList;
|
||||
while ((a = arenaList->cursor) != NULL) {
|
||||
JSGCArenaInfo *ainfo = a->getInfo();
|
||||
arenaList->cursor = ainfo->prev;
|
||||
JSGCThing *freeList = ainfo->freeList;
|
||||
if (freeList) {
|
||||
ainfo->freeList = NULL;
|
||||
return freeList;
|
||||
}
|
||||
|
||||
while ((a = arenaList->cursor) != NULL) {
|
||||
JSGCArenaInfo *ainfo = a->getInfo();
|
||||
arenaList->cursor = ainfo->prev;
|
||||
JSGCThing *freeList = ainfo->freeList;
|
||||
if (freeList) {
|
||||
ainfo->freeList = NULL;
|
||||
return freeList;
|
||||
}
|
||||
}
|
||||
|
||||
a = NewGCArena(cx);
|
||||
if (a)
|
||||
break;
|
||||
if (!canGC) {
|
||||
METER(cx->runtime->gcStats.arenaStats[thingKind].fail++);
|
||||
return NULL;
|
||||
}
|
||||
doGC = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have to allocate a new arena, check whether are bumping
|
||||
* against our GC heap quota. If so, request a GC to happen soon.
|
||||
*/
|
||||
if (rt->gcQuotaReached())
|
||||
cx->triggerGC();
|
||||
|
||||
a = NewGCArena(cx);
|
||||
if (!a)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Do only minimal initialization of the arena inside the GC lock. We
|
||||
* can do the rest outside the lock because no other threads will see
|
||||
@ -2527,26 +2465,6 @@ js_TraceRuntime(JSTracer *trc)
|
||||
rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData);
|
||||
}
|
||||
|
||||
void
|
||||
js_TriggerGC(JSContext *cx, JSBool gcLocked)
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(cx->requestDepth > 0);
|
||||
#endif
|
||||
JS_ASSERT(!rt->gcRunning);
|
||||
if (rt->gcIsNeeded)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Trigger the GC when it is safe to call an operation callback on any
|
||||
* thread.
|
||||
*/
|
||||
rt->gcIsNeeded = JS_TRUE;
|
||||
js_TriggerAllOperationCallbacks(rt, gcLocked);
|
||||
}
|
||||
|
||||
void
|
||||
js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data)
|
||||
{
|
||||
@ -3161,6 +3079,16 @@ GC(JSContext *cx GCTIMER_PARAM)
|
||||
*/
|
||||
js_SweepScriptFilenames(rt);
|
||||
|
||||
/*
|
||||
* Slowify arrays we have accumulated.
|
||||
*/
|
||||
while (!trc.arraysToSlowify.empty()) {
|
||||
JSObject *obj = trc.arraysToSlowify.back();
|
||||
trc.arraysToSlowify.popBack();
|
||||
if (IsMarkedGCThing(obj))
|
||||
obj->makeDenseArraySlow(cx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy arenas after we finished the sweeping so finalizers can safely
|
||||
* use js_IsAboutToBeFinalized().
|
||||
@ -3317,12 +3245,16 @@ BeginGCSession(JSContext *cx)
|
||||
rt->gcThread = cx->thread;
|
||||
|
||||
/*
|
||||
* Notify all operation callbacks, which will give them a chance to yield
|
||||
* their current request. Contexts that are not currently executing will
|
||||
* perform their callback at some later point, which then will be
|
||||
* unnecessary, but harmless.
|
||||
* Notify operation callbacks on other threads, which will give them a
|
||||
* chance to yield their requests. Threads without requests perform their
|
||||
* callback at some later point, which then will be unnecessary, but
|
||||
* harmless.
|
||||
*/
|
||||
js_NudgeOtherContexts(cx);
|
||||
for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
|
||||
JSThread *thread = r.front().value;
|
||||
if (thread != cx->thread)
|
||||
thread->data.triggerOperationCallback();
|
||||
}
|
||||
|
||||
/*
|
||||
* Discount the request on the current thread from contributing to
|
||||
|
@ -167,16 +167,6 @@ js_TraceRuntime(JSTracer *trc);
|
||||
extern JS_REQUIRES_STACK JS_FRIEND_API(void)
|
||||
js_TraceContext(JSTracer *trc, JSContext *acx);
|
||||
|
||||
/*
|
||||
* Schedule the GC call at a later safe point.
|
||||
*/
|
||||
#ifndef JS_THREADSAFE
|
||||
# define js_TriggerGC(cx, gcLocked) js_TriggerGC (cx)
|
||||
#endif
|
||||
|
||||
extern void
|
||||
js_TriggerGC(JSContext *cx, JSBool gcLocked);
|
||||
|
||||
/*
|
||||
* Kinds of js_GC invocation.
|
||||
*/
|
||||
|
@ -1072,7 +1072,7 @@ CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
|
||||
HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
|
||||
{
|
||||
Class *clasp = obj->getClass();
|
||||
if (clasp->hasInstance)
|
||||
@ -2347,7 +2347,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount)
|
||||
*/
|
||||
#define CHECK_BRANCH() \
|
||||
JS_BEGIN_MACRO \
|
||||
if (cx->interruptFlags && !js_HandleExecutionInterrupt(cx)) \
|
||||
if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \
|
||||
goto error; \
|
||||
JS_END_MACRO
|
||||
|
||||
@ -4523,8 +4523,7 @@ BEGIN_CASE(JSOP_GETELEM)
|
||||
if (obj->isDenseArray()) {
|
||||
jsuint idx = jsuint(i);
|
||||
|
||||
if (idx < obj->getArrayLength() &&
|
||||
idx < obj->getDenseArrayCapacity()) {
|
||||
if (idx < obj->getDenseArrayCapacity()) {
|
||||
copyFrom = obj->addressOfDenseArrayElement(idx);
|
||||
if (!copyFrom->isMagic())
|
||||
goto end_getelem;
|
||||
@ -4615,8 +4614,7 @@ BEGIN_CASE(JSOP_SETELEM)
|
||||
if (js_PrototypeHasIndexedProperties(cx, obj))
|
||||
break;
|
||||
if ((jsuint)i >= obj->getArrayLength())
|
||||
obj->setDenseArrayLength(i + 1);
|
||||
obj->incDenseArrayCountBy(1);
|
||||
obj->setArrayLength(i + 1);
|
||||
}
|
||||
obj->setDenseArrayElement(i, regs.sp[-1]);
|
||||
goto end_setelem;
|
||||
@ -6035,7 +6033,7 @@ BEGIN_CASE(JSOP_NEWARRAY)
|
||||
{
|
||||
len = GET_UINT16(regs.pc);
|
||||
cx->assertValidStackDepth(len);
|
||||
JSObject *obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE);
|
||||
JSObject *obj = js_NewArrayObject(cx, len, regs.sp - len);
|
||||
if (!obj)
|
||||
goto error;
|
||||
regs.sp -= len - 1;
|
||||
@ -6432,7 +6430,7 @@ BEGIN_CASE(JSOP_INSTANCEOF)
|
||||
JSObject *obj = &rref.toObject();
|
||||
const Value &lref = regs.sp[-2];
|
||||
JSBool cond = JS_FALSE;
|
||||
if (!js_HasInstance(cx, obj, &lref, &cond))
|
||||
if (!HasInstance(cx, obj, &lref, &cond))
|
||||
goto error;
|
||||
regs.sp--;
|
||||
regs.sp[-1].setBoolean(cond);
|
||||
|
@ -401,7 +401,7 @@ InstanceOf(JSContext *cx, JSObject *obj, Class *clasp, Value *argv)
|
||||
}
|
||||
|
||||
extern JSBool
|
||||
js_HasInstance(JSContext *cx, JSObject *obj, const js::Value *v, JSBool *bp);
|
||||
HasInstance(JSContext *cx, JSObject *obj, const js::Value *v, JSBool *bp);
|
||||
|
||||
inline void *
|
||||
GetInstancePrivate(JSContext *cx, JSObject *obj, Class *clasp, Value *argv)
|
||||
|
@ -206,7 +206,7 @@ Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
|
||||
JS_ASSERT(JSID_IS_INT(id) || JSID_IS_ATOM(id));
|
||||
|
||||
IdSet::AddPtr p = ht.lookupForAdd(id);
|
||||
JS_ASSERT_IF(obj == pobj, !p);
|
||||
JS_ASSERT_IF(obj == pobj && !obj->isProxy(), !p);
|
||||
|
||||
/* If we've already seen this, we definitely won't add it. */
|
||||
if (JS_UNLIKELY(!!p))
|
||||
@ -214,9 +214,10 @@ Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
|
||||
|
||||
/*
|
||||
* It's not necessary to add properties to the hash table at the end of the
|
||||
* prototype chain.
|
||||
* prototype chain -- but a proxy might return duplicated properties, so
|
||||
* always add for them.
|
||||
*/
|
||||
if (pobj->getProto() && !ht.add(p, id))
|
||||
if ((pobj->getProto() || pobj->isProxy()) && !ht.add(p, id))
|
||||
return false;
|
||||
|
||||
if (JS_UNLIKELY(flags & JSITER_OWNONLY)) {
|
||||
@ -277,7 +278,7 @@ EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uint
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pobj->getDenseArrayCount() > 0) {
|
||||
if (pobj->getArrayLength() > 0) {
|
||||
size_t capacity = pobj->getDenseArrayCapacity();
|
||||
Value *vp = pobj->dslots;
|
||||
for (size_t i = 0; i < capacity; ++i, ++vp) {
|
||||
|
@ -529,43 +529,6 @@ FinishSharingTitle(JSContext *cx, JSTitle *title)
|
||||
JS_RUNTIME_METER(cx->runtime, sharedTitles);
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify all contexts that are currently in a request, which will give them a
|
||||
* chance to yield their current request.
|
||||
*/
|
||||
void
|
||||
js_NudgeOtherContexts(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
JSContext *acx = NULL;
|
||||
|
||||
while ((acx = js_NextActiveContext(rt, acx)) != NULL) {
|
||||
if (cx != acx)
|
||||
JS_TriggerOperationCallback(acx);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify all contexts that are currently in a request and execute on this
|
||||
* specific thread.
|
||||
*/
|
||||
static void
|
||||
NudgeThread(JSRuntime *rt, JSThread *thread)
|
||||
{
|
||||
JS_ASSERT(thread);
|
||||
|
||||
/*
|
||||
* We cannot walk here over thread->contextList as that is manipulated
|
||||
* outside the GC lock and must be accessed only from the the thread that
|
||||
* owns JSThread.
|
||||
*/
|
||||
JSContext *acx = NULL;
|
||||
while ((acx = js_NextActiveContext(rt, acx)) != NULL) {
|
||||
if (acx->thread == thread)
|
||||
JS_TriggerOperationCallback(acx);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a title with apparently non-null ownercx different from cx, try to
|
||||
* set ownercx to cx, claiming exclusive (single-threaded) ownership of title.
|
||||
@ -658,7 +621,7 @@ ClaimTitle(JSTitle *title, JSContext *cx)
|
||||
* But before waiting, we force the operation callback for that other
|
||||
* thread so it can quickly suspend.
|
||||
*/
|
||||
NudgeThread(rt, ownercx->thread);
|
||||
JS_THREAD_DATA(ownercx)->triggerOperationCallback();
|
||||
|
||||
JS_ASSERT(!cx->thread->titleToShare);
|
||||
cx->thread->titleToShare = title;
|
||||
|
@ -215,9 +215,6 @@ extern void js_FinishLock(JSThinLock *);
|
||||
extern void
|
||||
js_ShareWaitingTitles(JSContext *cx);
|
||||
|
||||
extern void
|
||||
js_NudgeOtherContexts(JSContext *cx);
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define JS_IS_RUNTIME_LOCKED(rt) js_IsRuntimeLocked(rt)
|
||||
|
@ -1863,7 +1863,7 @@ obj_keys(JSContext *cx, uintN argc, Value *vp)
|
||||
}
|
||||
|
||||
JS_ASSERT(props.length() <= UINT32_MAX);
|
||||
JSObject *aobj = js_NewArrayObject(cx, jsuint(vals.length()), vals.begin(), false);
|
||||
JSObject *aobj = js_NewArrayObject(cx, jsuint(vals.length()), vals.begin());
|
||||
if (!aobj)
|
||||
return JS_FALSE;
|
||||
vp->setObject(*aobj);
|
||||
@ -2342,7 +2342,7 @@ DefinePropertyOnArray(JSContext *cx, JSObject *obj, const PropDesc &desc,
|
||||
|
||||
if (index >= oldLen) {
|
||||
JS_ASSERT(index != UINT32_MAX);
|
||||
obj->setSlowArrayLength(index + 1);
|
||||
obj->setArrayLength(index + 1);
|
||||
}
|
||||
|
||||
*rval = true;
|
||||
|
@ -474,45 +474,28 @@ struct JSObject {
|
||||
// Used by dense and slow arrays.
|
||||
static const uint32 JSSLOT_ARRAY_LENGTH = JSSLOT_PRIVATE;
|
||||
|
||||
private:
|
||||
// Used only by dense arrays.
|
||||
static const uint32 JSSLOT_DENSE_ARRAY_COUNT = JSSLOT_PRIVATE + 1;
|
||||
static const uint32 JSSLOT_DENSE_ARRAY_MINLENCAP = JSSLOT_PRIVATE + 2;
|
||||
|
||||
// This assertion must remain true; see comment in js_MakeArraySlow().
|
||||
// (Nb: This method is never called, it just contains a static assertion.
|
||||
// The static assertion isn't inline because that doesn't work on Mac.)
|
||||
inline void staticAssertArrayLengthIsInPrivateSlot();
|
||||
|
||||
inline uint32 uncheckedGetArrayLength() const;
|
||||
inline uint32 uncheckedGetDenseArrayCapacity() const;
|
||||
|
||||
public:
|
||||
static const uint32 DENSE_ARRAY_FIXED_RESERVED_SLOTS = 3;
|
||||
|
||||
inline uint32 getArrayLength() const;
|
||||
inline void setDenseArrayLength(uint32 length);
|
||||
inline void setSlowArrayLength(uint32 length);
|
||||
|
||||
inline uint32 getDenseArrayCount() const;
|
||||
inline void setDenseArrayCount(uint32 count);
|
||||
inline void incDenseArrayCountBy(uint32 posDelta);
|
||||
inline void decDenseArrayCountBy(uint32 negDelta);
|
||||
inline void setArrayLength(uint32 length);
|
||||
|
||||
inline uint32 getDenseArrayCapacity() const;
|
||||
inline void setDenseArrayCapacity(uint32 capacity); // XXX: bug 558263 will remove this
|
||||
|
||||
inline bool isDenseArrayMinLenCapOk(bool strictAboutLength = true) const;
|
||||
|
||||
inline const js::Value &getDenseArrayElement(uint32 i) const;
|
||||
inline js::Value *addressOfDenseArrayElement(uint32 i);
|
||||
inline void setDenseArrayElement(uint32 i, const js::Value &v);
|
||||
|
||||
inline js::Value *getDenseArrayElements() const; // returns pointer to the Array's elements array
|
||||
bool resizeDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap,
|
||||
bool initializeAllSlots = true);
|
||||
bool ensureDenseArrayElements(JSContext *cx, uint32 newcap,
|
||||
bool initializeAllSlots = true);
|
||||
bool growDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap);
|
||||
bool ensureDenseArrayElements(JSContext *cx, uint32 newcap);
|
||||
bool shrinkDenseArrayElements(JSContext *cx, uint32 newcap);
|
||||
inline void freeDenseArrayElements(JSContext *cx);
|
||||
|
||||
inline void voidDenseOnlyArraySlots(); // used when converting a dense array to a slow array
|
||||
@ -586,6 +569,7 @@ struct JSObject {
|
||||
|
||||
inline const js::Value &getRegExpLastIndex() const;
|
||||
inline void setRegExpLastIndex(const js::Value &v);
|
||||
inline void setRegExpLastIndex(jsdouble d);
|
||||
inline void zeroRegExpLastIndex();
|
||||
|
||||
/*
|
||||
|
@ -132,96 +132,26 @@ JSObject::staticAssertArrayLengthIsInPrivateSlot()
|
||||
JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH == JSSLOT_PRIVATE);
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSObject::isDenseArrayMinLenCapOk(bool strictAboutLength) const
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
|
||||
// This function can be called while the LENGTH and MINLENCAP slots are
|
||||
// still set to JSVAL_VOID and there are no dslots (ie. the capacity is
|
||||
// zero). If 'strictAboutLength' is false we allow this.
|
||||
if (!strictAboutLength &&
|
||||
fslots[JSSLOT_ARRAY_LENGTH].isUndefined() &&
|
||||
uncheckedGetDenseArrayCapacity() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 length = uncheckedGetArrayLength();
|
||||
uint32 capacity = uncheckedGetDenseArrayCapacity();
|
||||
uint32 minLenCap = fslots[JSSLOT_DENSE_ARRAY_MINLENCAP].toPrivateUint32();
|
||||
return minLenCap == JS_MIN(length, capacity);
|
||||
}
|
||||
|
||||
inline uint32
|
||||
JSObject::uncheckedGetArrayLength() const
|
||||
{
|
||||
return fslots[JSSLOT_ARRAY_LENGTH].toPrivateUint32();
|
||||
}
|
||||
|
||||
inline uint32
|
||||
JSObject::getArrayLength() const
|
||||
{
|
||||
JS_ASSERT(isArray());
|
||||
JS_ASSERT_IF(isDenseArray(), isDenseArrayMinLenCapOk());
|
||||
return uncheckedGetArrayLength();
|
||||
return fslots[JSSLOT_ARRAY_LENGTH].toPrivateUint32();
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::setDenseArrayLength(uint32 length)
|
||||
JSObject::setArrayLength(uint32 length)
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
JS_ASSERT(isArray());
|
||||
fslots[JSSLOT_ARRAY_LENGTH].setPrivateUint32(length);
|
||||
uint32 capacity = uncheckedGetDenseArrayCapacity();
|
||||
fslots[JSSLOT_DENSE_ARRAY_MINLENCAP].setPrivateUint32(JS_MIN(length, capacity));
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::setSlowArrayLength(uint32 length)
|
||||
{
|
||||
JS_ASSERT(isSlowArray());
|
||||
fslots[JSSLOT_ARRAY_LENGTH].setPrivateUint32(length);
|
||||
}
|
||||
|
||||
inline uint32
|
||||
JSObject::getDenseArrayCount() const
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
return fslots[JSSLOT_DENSE_ARRAY_COUNT].toPrivateUint32();
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::setDenseArrayCount(uint32 count)
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
fslots[JSSLOT_DENSE_ARRAY_COUNT].setPrivateUint32(count);
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::incDenseArrayCountBy(uint32 posDelta)
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
fslots[JSSLOT_DENSE_ARRAY_COUNT].getPrivateUint32Ref() += posDelta;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::decDenseArrayCountBy(uint32 negDelta)
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
fslots[JSSLOT_DENSE_ARRAY_COUNT].getPrivateUint32Ref() -= negDelta;
|
||||
}
|
||||
|
||||
inline uint32
|
||||
JSObject::uncheckedGetDenseArrayCapacity() const
|
||||
{
|
||||
return dslots ? dslots[-1].toPrivateUint32() : 0;
|
||||
}
|
||||
|
||||
inline uint32
|
||||
JSObject::getDenseArrayCapacity() const
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
JS_ASSERT(isDenseArrayMinLenCapOk(/* strictAboutLength = */false));
|
||||
return uncheckedGetDenseArrayCapacity();
|
||||
JS_ASSERT(dslots);
|
||||
return dslots[-1].toPrivateUint32();
|
||||
}
|
||||
|
||||
inline void
|
||||
@ -230,8 +160,6 @@ JSObject::setDenseArrayCapacity(uint32 capacity)
|
||||
JS_ASSERT(isDenseArray());
|
||||
JS_ASSERT(dslots);
|
||||
dslots[-1].setPrivateUint32(capacity);
|
||||
uint32 length = uncheckedGetArrayLength();
|
||||
fslots[JSSLOT_DENSE_ARRAY_MINLENCAP].setPrivateUint32(JS_MIN(length, capacity));
|
||||
}
|
||||
|
||||
inline const js::Value &
|
||||
@ -273,16 +201,12 @@ JSObject::freeDenseArrayElements(JSContext *cx)
|
||||
cx->free(dslots - 1);
|
||||
dslots = NULL;
|
||||
}
|
||||
fslots[JSSLOT_DENSE_ARRAY_MINLENCAP].setPrivateUint32(0);
|
||||
JS_ASSERT(isDenseArrayMinLenCapOk(/* strictAboutLength = */false));
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::voidDenseOnlyArraySlots()
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
fslots[JSSLOT_DENSE_ARRAY_COUNT].setUndefined();
|
||||
fslots[JSSLOT_DENSE_ARRAY_MINLENCAP].setUndefined();
|
||||
}
|
||||
|
||||
inline void
|
||||
@ -384,27 +308,6 @@ JSObject::setDateUTCTime(const js::Value &time)
|
||||
fslots[JSSLOT_DATE_UTC_TIME] = time;
|
||||
}
|
||||
|
||||
inline const js::Value &
|
||||
JSObject::getRegExpLastIndex() const
|
||||
{
|
||||
JS_ASSERT(isRegExp());
|
||||
return fslots[JSSLOT_REGEXP_LAST_INDEX];
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::setRegExpLastIndex(const js::Value &v)
|
||||
{
|
||||
JS_ASSERT(isRegExp());
|
||||
fslots[JSSLOT_REGEXP_LAST_INDEX] = v;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::zeroRegExpLastIndex()
|
||||
{
|
||||
JS_ASSERT(isRegExp());
|
||||
fslots[JSSLOT_REGEXP_LAST_INDEX].setInt32(0);
|
||||
}
|
||||
|
||||
inline NativeIterator *
|
||||
JSObject::getNativeIterator() const
|
||||
{
|
||||
|
@ -49,12 +49,18 @@
|
||||
JS_BEGIN_EXTERN_C
|
||||
|
||||
/* Scalar typedefs. */
|
||||
typedef uint16 jschar;
|
||||
typedef int32 jsint;
|
||||
typedef uint32 jsuint;
|
||||
typedef float64 jsdouble;
|
||||
typedef int32 jsrefcount; /* PRInt32 if JS_THREADSAFE, see jslock.h */
|
||||
|
||||
#ifdef WIN32
|
||||
typedef wchar_t jschar;
|
||||
#else
|
||||
typedef uint16 jschar;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Run-time version enumeration. See jsversion.h for compile-time counterparts
|
||||
* to these values that may be selected by the JS_VERSION macro, and tested by
|
||||
|
@ -5086,13 +5086,6 @@ out:
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
static void
|
||||
SetRegExpLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex)
|
||||
{
|
||||
JS_ASSERT(obj->isRegExp());
|
||||
obj->setRegExpLastIndex(NumberValue(lastIndex));
|
||||
}
|
||||
|
||||
#define DEFINE_GETTER(name, code) \
|
||||
static JSBool \
|
||||
name(JSContext *cx, JSObject *obj, jsid id, Value *vp) \
|
||||
@ -5125,11 +5118,7 @@ lastIndex_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||
if (!obj)
|
||||
return true;
|
||||
}
|
||||
jsdouble lastIndex;
|
||||
if (!ValueToNumber(cx, *vp, &lastIndex))
|
||||
return false;
|
||||
lastIndex = js_DoubleToInteger(lastIndex);
|
||||
SetRegExpLastIndex(cx, obj, lastIndex);
|
||||
obj->setRegExpLastIndex(*vp);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5653,7 +5642,16 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv,
|
||||
HOLD_REGEXP(cx, re);
|
||||
sticky = (re->flags & JSREG_STICKY) != 0;
|
||||
if (re->flags & (JSREG_GLOB | JSREG_STICKY)) {
|
||||
lastIndex = obj->getRegExpLastIndex().toNumber();
|
||||
const Value &v = obj->getRegExpLastIndex();
|
||||
if (v.isInt32()) {
|
||||
lastIndex = v.toInt32();
|
||||
} else {
|
||||
if (v.isDouble())
|
||||
lastIndex = v.toDouble();
|
||||
else if (!ValueToNumber(cx, v, &lastIndex))
|
||||
return JS_FALSE;
|
||||
lastIndex = js_DoubleToInteger(lastIndex);
|
||||
}
|
||||
} else {
|
||||
lastIndex = 0;
|
||||
}
|
||||
@ -5697,7 +5695,7 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv,
|
||||
if (rval->isNull())
|
||||
obj->zeroRegExpLastIndex();
|
||||
else
|
||||
SetRegExpLastIndex(cx, obj, i);
|
||||
obj->setRegExpLastIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,34 @@
|
||||
#include "jsdhash.h"
|
||||
#endif
|
||||
|
||||
inline const js::Value &
|
||||
JSObject::getRegExpLastIndex() const
|
||||
{
|
||||
JS_ASSERT(isRegExp());
|
||||
return fslots[JSSLOT_REGEXP_LAST_INDEX];
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::setRegExpLastIndex(const js::Value &v)
|
||||
{
|
||||
JS_ASSERT(isRegExp());
|
||||
fslots[JSSLOT_REGEXP_LAST_INDEX] = v;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::setRegExpLastIndex(jsdouble d)
|
||||
{
|
||||
JS_ASSERT(isRegExp());
|
||||
fslots[JSSLOT_REGEXP_LAST_INDEX] = js::NumberValue(d);
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::zeroRegExpLastIndex()
|
||||
{
|
||||
JS_ASSERT(isRegExp());
|
||||
fslots[JSSLOT_REGEXP_LAST_INDEX].setInt32(0);
|
||||
}
|
||||
|
||||
namespace js { class AutoStringRooter; }
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
|
@ -86,7 +86,7 @@ js_GenerateShape(JSContext *cx, bool gcLocked)
|
||||
*/
|
||||
rt->shapeGen = SHAPE_OVERFLOW_BIT;
|
||||
shape = SHAPE_OVERFLOW_BIT;
|
||||
js_TriggerGC(cx, gcLocked);
|
||||
cx->triggerGC();
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
@ -200,7 +200,7 @@ JSScope::createTable(JSContext *cx, bool report)
|
||||
METER(tableAllocFails);
|
||||
return false;
|
||||
}
|
||||
cx->updateMallocCounter(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));
|
||||
cx->runtime->updateMallocCounter(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));
|
||||
|
||||
hashShift = JS_DHASH_BITS - sizeLog2;
|
||||
for (sprop = lastProp; sprop; sprop = sprop->parent) {
|
||||
@ -516,7 +516,7 @@ JSScope::changeTable(JSContext *cx, int change)
|
||||
table = 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++) {
|
||||
|
@ -752,15 +752,18 @@ extern const bool js_alnum[];
|
||||
|
||||
#define JS_ISDIGIT(c) (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER)
|
||||
|
||||
const jschar BYTE_ORDER_MARK = 0xFEFF;
|
||||
const jschar NO_BREAK_SPACE = 0x00A0;
|
||||
|
||||
static inline bool
|
||||
JS_ISSPACE(jschar c)
|
||||
{
|
||||
unsigned w = c;
|
||||
|
||||
if (w < 256)
|
||||
return (w <= ' ' && (w == ' ' || (9 <= w && w <= 0xD))) || w == 0xA0;
|
||||
return (w <= ' ' && (w == ' ' || (9 <= w && w <= 0xD))) || w == NO_BREAK_SPACE;
|
||||
|
||||
return (JS_CCODE(w) & 0x00070000) == 0x00040000;
|
||||
return w == BYTE_ORDER_MARK || (JS_CCODE(w) & 0x00070000) == 0x00040000;
|
||||
}
|
||||
|
||||
#define JS_ISPRINT(c) ((c) < 128 && isprint(c))
|
||||
|
@ -2343,12 +2343,13 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag
|
||||
if (fragment == fragment->root) {
|
||||
/*
|
||||
* We poll the operation callback request flag. It is updated asynchronously whenever
|
||||
* the callback is to be invoked.
|
||||
* the callback is to be invoked. We can use INS_CONSTPTR here as JIT-ed code is per
|
||||
* thread and cannot outlive the corresponding JSThreaData.
|
||||
*/
|
||||
// XXX: this load is volatile. If bug 545406 (loop-invariant code
|
||||
// hoisting) is implemented this fact will need to be made explicit.
|
||||
LIns* x =
|
||||
lir->insLoad(LIR_ldi, cx_ins, offsetof(JSContext, interruptFlags), ACC_LOAD_ANY);
|
||||
LIns* flagptr = INS_CONSTPTR((void *) &JS_THREAD_DATA(cx)->interruptFlags);
|
||||
LIns* x = lir->insLoad(LIR_ldi, flagptr, 0, ACC_LOAD_ANY);
|
||||
guard(true, lir->insEqI_0(x), snapshot(TIMEOUT_EXIT));
|
||||
}
|
||||
|
||||
@ -2961,7 +2962,6 @@ public:
|
||||
JS_REQUIRES_STACK JS_ALWAYS_INLINE void
|
||||
visitGlobalSlot(Value *vp, unsigned n, unsigned slot) {
|
||||
debug_only_printf(LC_TMTracer, "global%d=", n);
|
||||
JS_ASSERT(JS_THREAD_DATA(mCx)->waiveGCQuota);
|
||||
NativeToValue(mCx, *vp, *mTypeMap++, &mGlobal[slot]);
|
||||
}
|
||||
};
|
||||
@ -2995,7 +2995,6 @@ public:
|
||||
|
||||
JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
|
||||
visitStackSlots(Value *vp, size_t count, JSStackFrame* fp) {
|
||||
JS_ASSERT(JS_THREAD_DATA(mCx)->waiveGCQuota);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (vp == mStop)
|
||||
return false;
|
||||
@ -3011,7 +3010,6 @@ public:
|
||||
|
||||
JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
|
||||
visitFrameObjPtr(JSObject **p, JSStackFrame* fp) {
|
||||
JS_ASSERT(JS_THREAD_DATA(mCx)->waiveGCQuota);
|
||||
if ((Value *)p == mStop)
|
||||
return false;
|
||||
debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), 0);
|
||||
@ -6705,21 +6703,6 @@ ExecuteTree(JSContext* cx, TreeFragment* f, uintN& inlineCallCount,
|
||||
return ok;
|
||||
}
|
||||
|
||||
class Guardian {
|
||||
bool *flagp;
|
||||
public:
|
||||
Guardian(bool *flagp) {
|
||||
this->flagp = flagp;
|
||||
JS_ASSERT(!*flagp);
|
||||
*flagp = true;
|
||||
}
|
||||
|
||||
~Guardian() {
|
||||
JS_ASSERT(*flagp);
|
||||
*flagp = false;
|
||||
}
|
||||
};
|
||||
|
||||
static JS_FORCES_STACK void
|
||||
LeaveTree(TraceMonitor *tm, TracerState& state, VMSideExit* lr)
|
||||
{
|
||||
@ -6727,9 +6710,6 @@ LeaveTree(TraceMonitor *tm, TracerState& state, VMSideExit* lr)
|
||||
|
||||
JSContext* cx = state.cx;
|
||||
|
||||
/* Temporary waive the soft GC quota to make sure LeaveTree() doesn't fail. */
|
||||
Guardian waiver(&JS_THREAD_DATA(cx)->waiveGCQuota);
|
||||
|
||||
FrameInfo** callstack = state.callstackBase;
|
||||
double* stack = state.stackBase;
|
||||
|
||||
@ -7116,7 +7096,7 @@ MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, RecordReason reason)
|
||||
}
|
||||
|
||||
/* Do not enter the JIT code with a pending operation callback. */
|
||||
if (cx->interruptFlags) {
|
||||
if (JS_THREAD_DATA(cx)->interruptFlags) {
|
||||
#ifdef MOZ_TRACEVIS
|
||||
tvso.r = R_CALLBACK_PENDING;
|
||||
#endif
|
||||
@ -11023,7 +11003,7 @@ TraceRecorder::newArray(JSObject* ctor, uint32 argc, Value* argv, Value* rval)
|
||||
} else if (argc == 1 && argv[0].isNumber()) {
|
||||
// arr_ins = js_NewEmptyArray(cx, Array.prototype, length)
|
||||
LIns *args[] = { d2i(get(argv)), proto_ins, cx_ins }; // FIXME: is this 64-bit safe?
|
||||
arr_ins = lir->insCall(&js_NewEmptyArrayWithLength_ci, args);
|
||||
arr_ins = lir->insCall(&js_NewArrayWithSlots_ci, args);
|
||||
guard(false, lir->insEqP_0(arr_ins), OOM_EXIT);
|
||||
} else {
|
||||
// arr_ins = js_NewArrayWithSlots(cx, Array.prototype, argc)
|
||||
@ -11036,9 +11016,6 @@ TraceRecorder::newArray(JSObject* ctor, uint32 argc, Value* argv, Value* rval)
|
||||
for (uint32 i = 0; i < argc && !outOfMemory(); i++) {
|
||||
stobj_set_dslot(arr_ins, i, dslots_ins, argv[i], get(&argv[i]));
|
||||
}
|
||||
|
||||
if (argc > 0)
|
||||
set_array_fslot(arr_ins, JSObject::JSSLOT_DENSE_ARRAY_COUNT, argc);
|
||||
}
|
||||
|
||||
set(rval, arr_ins);
|
||||
@ -13657,23 +13634,24 @@ TraceRecorder::denseArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_
|
||||
LIns* idx_ins = makeNumberInt32(get(&ival));
|
||||
|
||||
VMSideExit* exit = snapshot(BRANCH_EXIT);
|
||||
/* check that the index is within bounds */
|
||||
|
||||
/*
|
||||
* Arrays have both a length and a capacity, but we only need to check
|
||||
* |index < capacity|; in the case where |length < index < capacity|
|
||||
* the entries [length..capacity-1] will have already been marked as
|
||||
* holes by resizeDenseArrayElements() so we can read them and get
|
||||
* the correct value.
|
||||
*/
|
||||
LIns* dslots_ins =
|
||||
addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACC_OTHER), "dslots");
|
||||
jsuint capacity = obj->getDenseArrayCapacity();
|
||||
bool within = (jsuint(idx) < obj->getArrayLength() && jsuint(idx) < capacity);
|
||||
if (!within) {
|
||||
/* If not idx < min(length, capacity), stay on trace (and read value as undefined). */
|
||||
JS_ASSERT(obj->isDenseArrayMinLenCapOk());
|
||||
LIns* minLenCap =
|
||||
addName(stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_DENSE_ARRAY_MINLENCAP), "minLenCap");
|
||||
LIns* br = lir->insBranch(LIR_jf,
|
||||
lir->ins2(LIR_ltui, idx_ins, minLenCap),
|
||||
NULL);
|
||||
LIns* capacity_ins =
|
||||
addName(lir->insLoad(LIR_ldi, dslots_ins, -int(sizeof(jsval)), ACC_OTHER), "capacity");
|
||||
|
||||
lir->insGuard(LIR_x, NULL, createGuardRecord(exit));
|
||||
LIns* label = lir->ins0(LIR_label);
|
||||
br->setTarget(label);
|
||||
jsuint capacity = obj->getDenseArrayCapacity();
|
||||
bool within = (jsuint(idx) < capacity);
|
||||
if (!within) {
|
||||
/* If not idx < capacity, stay on trace (and read value as undefined). */
|
||||
guard(true, lir->ins2(LIR_geui, idx_ins, capacity_ins), exit);
|
||||
|
||||
CHECK_STATUS(guardPrototypeHasNoIndexedProperties(obj, obj_ins, MISMATCH_EXIT));
|
||||
|
||||
@ -13683,11 +13661,8 @@ TraceRecorder::denseArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_
|
||||
return RECORD_CONTINUE;
|
||||
}
|
||||
|
||||
/* Guard array min(length, capacity). */
|
||||
JS_ASSERT(obj->isDenseArrayMinLenCapOk());
|
||||
LIns* minLenCap =
|
||||
addName(stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_DENSE_ARRAY_MINLENCAP), "minLenCap");
|
||||
guard(true, lir->ins2(LIR_ltui, idx_ins, minLenCap), exit);
|
||||
/* Guard that index is within capacity. */
|
||||
guard(true, lir->ins2(LIR_ltui, idx_ins, capacity_ins), exit);
|
||||
|
||||
/* Load the value and guard on its type to unbox it. */
|
||||
vp = &obj->dslots[jsuint(idx)];
|
||||
@ -14671,15 +14646,16 @@ TraceRecorder::record_JSOP_IN()
|
||||
}
|
||||
|
||||
static JSBool FASTCALL
|
||||
HasInstance(JSContext* cx, JSObject* ctor, ValueArgType arg)
|
||||
HasInstanceOnTrace(JSContext* cx, JSObject* ctor, ValueArgType arg)
|
||||
{
|
||||
const Value &argref = ValueArgToConstRef(arg);
|
||||
JSBool result = JS_FALSE;
|
||||
if (!js_HasInstance(cx, ctor, &argref, &result))
|
||||
if (!HasInstance(cx, ctor, &argref, &result))
|
||||
SetBuiltinError(cx);
|
||||
return result;
|
||||
}
|
||||
JS_DEFINE_CALLINFO_3(static, BOOL_FAIL, HasInstance, CONTEXT, OBJECT, VALUE, 0, ACC_STORE_ANY)
|
||||
JS_DEFINE_CALLINFO_3(static, BOOL_FAIL, HasInstanceOnTrace, CONTEXT, OBJECT, VALUE, 0,
|
||||
ACC_STORE_ANY)
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_INSTANCEOF()
|
||||
@ -14694,7 +14670,7 @@ TraceRecorder::record_JSOP_INSTANCEOF()
|
||||
|
||||
enterDeepBailCall();
|
||||
LIns* args[] = {val_ins, get(&ctor), cx_ins};
|
||||
stack(-2, lir->insCall(&HasInstance_ci, args));
|
||||
stack(-2, lir->insCall(&HasInstanceOnTrace_ci, args));
|
||||
LIns* status_ins = lir->insLoad(LIR_ldi,
|
||||
lirbuf->state,
|
||||
offsetof(TracerState, builtinStatus), ACC_OTHER);
|
||||
@ -15721,9 +15697,6 @@ TraceRecorder::record_JSOP_NEWARRAY()
|
||||
stobj_set_dslot(v_ins, i, dslots_ins, v, get(&v));
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
set_array_fslot(v_ins, JSObject::JSSLOT_DENSE_ARRAY_COUNT, count);
|
||||
|
||||
stack(-int(len), v_ins);
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
@ -1278,7 +1278,13 @@ mjit::Compiler::generateMethod()
|
||||
if (analysis[PC].nincoming > 0) {
|
||||
RegisterID cxreg = frame.allocReg();
|
||||
masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), cxreg);
|
||||
Address flag(cxreg, offsetof(JSContext, interruptFlags));
|
||||
#ifdef JS_THREADSAFE
|
||||
masm.loadPtr(Address(cxreg, offsetof(JSContext, thread)), cxreg);
|
||||
Address flag(cxreg, offsetof(JSThread, data.interruptFlags));
|
||||
#else
|
||||
masm.loadPtr(Address(cxreg, offsetof(JSContext, runtime)), cxreg);
|
||||
Address flag(cxreg, offsetof(JSRuntime, threadData.interruptFlags));
|
||||
#endif
|
||||
Jump jump = masm.branchTest32(Assembler::NonZero, flag);
|
||||
frame.freeReg(cxreg);
|
||||
stubcc.linkExit(jump, Uses(0));
|
||||
|
@ -615,27 +615,24 @@ stubs::SetElem(VMFrame &f)
|
||||
if (!FetchElementId(f, obj, idval, id, ®s.sp[-2]))
|
||||
THROW();
|
||||
|
||||
if (obj->isDenseArray() && JSID_IS_INT(id)) {
|
||||
jsuint length = obj->getDenseArrayCapacity();
|
||||
jsint i = JSID_TO_INT(id);
|
||||
|
||||
if ((jsuint)i < length) {
|
||||
if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
|
||||
if (js_PrototypeHasIndexedProperties(cx, obj))
|
||||
goto mid_setelem;
|
||||
if ((jsuint)i >= obj->getArrayLength())
|
||||
obj->setDenseArrayLength(i + 1);
|
||||
obj->incDenseArrayCountBy(1);
|
||||
do {
|
||||
if (obj->isDenseArray() && JSID_IS_INT(id)) {
|
||||
jsuint length = obj->getDenseArrayCapacity();
|
||||
jsint i = JSID_TO_INT(id);
|
||||
if ((jsuint)i < length) {
|
||||
if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
|
||||
if (js_PrototypeHasIndexedProperties(cx, obj))
|
||||
break;
|
||||
if ((jsuint)i >= obj->getArrayLength())
|
||||
obj->setArrayLength(i + 1);
|
||||
}
|
||||
obj->setDenseArrayElement(i, regs.sp[-1]);
|
||||
goto end_setelem;
|
||||
}
|
||||
obj->setDenseArrayElement(i, regs.sp[-1]);
|
||||
goto end_setelem;
|
||||
}
|
||||
}
|
||||
|
||||
mid_setelem:
|
||||
} while (0);
|
||||
if (!obj->setProperty(cx, id, ®s.sp[-1]))
|
||||
THROW();
|
||||
|
||||
end_setelem:
|
||||
/* :FIXME: Moving the assigned object into the lowest stack slot
|
||||
* is a temporary hack. What we actually want is an implementation
|
||||
@ -1305,7 +1302,7 @@ stubs::Mod(VMFrame &f)
|
||||
JSObject *JS_FASTCALL
|
||||
stubs::NewArray(VMFrame &f, uint32 len)
|
||||
{
|
||||
JSObject *obj = js_NewArrayObject(f.cx, len, f.regs.sp - len, JS_TRUE);
|
||||
JSObject *obj = js_NewArrayObject(f.cx, len, f.regs.sp - len);
|
||||
if (!obj)
|
||||
THROWV(NULL);
|
||||
return obj;
|
||||
@ -2267,7 +2264,7 @@ stubs::InstanceOf(VMFrame &f)
|
||||
JSObject *obj = &rref.toObject();
|
||||
const Value &lref = regs.sp[-2];
|
||||
JSBool cond = JS_FALSE;
|
||||
if (!js_HasInstance(cx, obj, &lref, &cond))
|
||||
if (!HasInstance(cx, obj, &lref, &cond))
|
||||
THROWV(JS_FALSE);
|
||||
f.regs.sp[-2].setBoolean(cond);
|
||||
return cond;
|
||||
|
@ -218,6 +218,8 @@ for ( var space = " ", HEX_STRING = "0x0", HEX_VALUE = 0, POWER = 0;
|
||||
HEX_VALUE += Math.pow(16,POWER)*15;
|
||||
}
|
||||
|
||||
new TestCase(SECTION, "parseInt(BOM + '123', 10)", 123, parseInt("\uFEFF" + "123", 10));
|
||||
|
||||
// a few tests with negative numbers
|
||||
for ( HEX_STRING = "-0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) {
|
||||
new TestCase( SECTION, "parseInt("+HEX_STRING+")", HEX_VALUE, parseInt(HEX_STRING) );
|
||||
|
72
js/src/tests/ecma_5/RegExp/15.10.7.5-01.js
Normal file
72
js/src/tests/ecma_5/RegExp/15.10.7.5-01.js
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
var gTestfile = '15.10.7.5-01.js';
|
||||
var BUGNUMBER = 465199;
|
||||
var summary = "RegExp lastIndex property set should not coerce type to number";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
var called = false;
|
||||
var o = { valueOf: function() { called = true; return 1; } };
|
||||
var r = /a/g;
|
||||
var desc, m;
|
||||
|
||||
assertEq(r.lastIndex, 0);
|
||||
|
||||
desc = Object.getOwnPropertyDescriptor(r, "lastIndex");
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.configurable, false);
|
||||
assertEq(desc.value, 0);
|
||||
assertEq(desc.writable, true);
|
||||
|
||||
r.lastIndex = o;
|
||||
|
||||
assertEq(r.lastIndex, o);
|
||||
|
||||
desc = Object.getOwnPropertyDescriptor(r, "lastIndex");
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.configurable, false);
|
||||
assertEq(desc.value, o);
|
||||
assertEq(desc.writable, true);
|
||||
|
||||
assertEq(called, false);
|
||||
assertEq(r.exec("aaaa").length, 1);
|
||||
assertEq(called, true);
|
||||
assertEq(r.lastIndex, 2);
|
||||
|
||||
desc = Object.getOwnPropertyDescriptor(r, "lastIndex");
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.configurable, false);
|
||||
assertEq(desc.value, 2);
|
||||
assertEq(desc.writable, true);
|
||||
|
||||
|
||||
r.lastIndex = -0.2;
|
||||
assertEq(r.lastIndex, -0.2);
|
||||
|
||||
m = r.exec("aaaa");
|
||||
assertEq(Array.isArray(m), true);
|
||||
assertEq(m.length, 1);
|
||||
assertEq(m[0], "a");
|
||||
assertEq(r.lastIndex, 1);
|
||||
|
||||
m = r.exec("aaaa");
|
||||
assertEq(Array.isArray(m), true);
|
||||
assertEq(m.length, 1);
|
||||
assertEq(m[0], "a");
|
||||
assertEq(r.lastIndex, 2);
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
||||
print("All tests passed!");
|
1
js/src/tests/ecma_5/RegExp/browser.js
Normal file
1
js/src/tests/ecma_5/RegExp/browser.js
Normal file
@ -0,0 +1 @@
|
||||
|
2
js/src/tests/ecma_5/RegExp/jstests.list
Normal file
2
js/src/tests/ecma_5/RegExp/jstests.list
Normal file
@ -0,0 +1,2 @@
|
||||
url-prefix ../../jsreftest.html?test=ecma_5/RegExp/
|
||||
script 15.10.7.5-01.js
|
1
js/src/tests/ecma_5/RegExp/shell.js
Normal file
1
js/src/tests/ecma_5/RegExp/shell.js
Normal file
@ -0,0 +1 @@
|
||||
gTestsubsuite='RegExp';
|
@ -3,6 +3,7 @@ include Date/jstests.list
|
||||
include Expressions/jstests.list
|
||||
include JSON/jstests.list
|
||||
include Object/jstests.list
|
||||
include RegExp/jstests.list
|
||||
include Types/jstests.list
|
||||
include extensions/jstests.list
|
||||
include misc/jstests.list
|
||||
|
@ -10,3 +10,4 @@ skip-if(!xulRuntime.shell) script worker-terminate.js
|
||||
skip-if(!xulRuntime.shell) script worker-timeout.js
|
||||
script scripted-proxies.js
|
||||
script array-length-protochange.js
|
||||
script proxy-enumerateOwn-duplicates.js
|
||||
|
@ -0,0 +1,32 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
var gTestfile = 'proxy-enumerateOwn-duplicates.js';
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 580200;
|
||||
var summary =
|
||||
'Assertion failure enumerating own properties of proxy returning ' +
|
||||
'duplicated own property name';
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
var x = Proxy.create({ enumerateOwn: function() { return ["0","0"]; } }, [1,2]);
|
||||
var ax = Object.getOwnPropertyNames(x);
|
||||
assertEq(ax.length, 1, "array: " + ax);
|
||||
assertEq(ax[0], "0");
|
||||
|
||||
var p = Proxy.create({ enumerateOwn: function() { return ["1","1"]; } }, null);
|
||||
var ap = Object.getOwnPropertyNames(p);
|
||||
assertEq(ap.length, 1, "array: " + ap);
|
||||
assertEq(ap[0], "1");
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
||||
print("All tests passed!");
|
@ -1560,6 +1560,8 @@ _evaluate(NPP npp, NPObject* npobj, NPString *script, NPVariant *result)
|
||||
JSContext *cx = GetJSContextFromDoc(doc);
|
||||
NS_ENSURE_TRUE(cx, false);
|
||||
|
||||
JSAutoRequest req(cx);
|
||||
|
||||
nsCOMPtr<nsIScriptContext> scx = GetScriptContextFromJSContext(cx);
|
||||
NS_ENSURE_TRUE(scx, false);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user