[JAEGER] Merge from tracemonkey.

This commit is contained in:
David Mandelin 2010-07-23 18:47:24 -07:00
commit 4c5e8d41db
38 changed files with 590 additions and 958 deletions

View File

@ -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;
}

View File

@ -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() {

View File

@ -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) {

View File

@ -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,

View File

@ -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)))

View File

@ -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)

View File

@ -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;
}

View File

@ -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 *

View File

@ -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)

View File

@ -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()
{

View File

@ -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

View File

@ -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

View File

@ -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.
*/

View File

@ -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);

View File

@ -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)

View File

@ -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) {

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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();
/*

View File

@ -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
{

View File

@ -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

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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++) {

View File

@ -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))

View File

@ -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;
}

View File

@ -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));

View File

@ -615,27 +615,24 @@ stubs::SetElem(VMFrame &f)
if (!FetchElementId(f, obj, idval, id, &regs.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, &regs.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;

View File

@ -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) );

View 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!");

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,2 @@
url-prefix ../../jsreftest.html?test=ecma_5/RegExp/
script 15.10.7.5-01.js

View File

@ -0,0 +1 @@
gTestsubsuite='RegExp';

View File

@ -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

View File

@ -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

View File

@ -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!");

View File

@ -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);