[INFER] Javascript type inference, bug 557407.
This commit is contained in:
@@ -107,9 +107,12 @@
|
||||
#include "jscntxtinlines.h"
|
||||
#include "jsinterpinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jscntxtinlines.h"
|
||||
#include "jsinferinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
using namespace js::types;
|
||||
|
||||
/* 2^32 - 1 as a number and a string */
|
||||
#define MAXINDEX 4294967295u
|
||||
@@ -409,7 +412,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->setArrayLength(idx + 1);
|
||||
obj->setArrayLength(cx, idx + 1);
|
||||
obj->setDenseArrayElement(idx, v);
|
||||
return JS_TRUE;
|
||||
}
|
||||
@@ -570,7 +573,7 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool str
|
||||
|
||||
vp->setNumber(newlen);
|
||||
if (oldlen < newlen) {
|
||||
obj->setArrayLength(newlen);
|
||||
obj->setArrayLength(cx, newlen);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -584,23 +587,23 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool str
|
||||
jsuint oldcap = obj->getDenseArrayCapacity();
|
||||
if (oldcap > newlen)
|
||||
obj->shrinkDenseArrayElements(cx, newlen);
|
||||
obj->setArrayLength(newlen);
|
||||
obj->setArrayLength(cx, newlen);
|
||||
} else if (oldlen - newlen < (1 << 24)) {
|
||||
do {
|
||||
--oldlen;
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx)) {
|
||||
obj->setArrayLength(oldlen + 1);
|
||||
obj->setArrayLength(cx, oldlen + 1);
|
||||
return false;
|
||||
}
|
||||
if (!DeleteArrayElement(cx, obj, oldlen, true)) {
|
||||
obj->setArrayLength(oldlen + 1);
|
||||
obj->setArrayLength(cx, oldlen + 1);
|
||||
if (strict)
|
||||
return false;
|
||||
JS_ClearPendingException(cx);
|
||||
return true;
|
||||
}
|
||||
} while (oldlen != newlen);
|
||||
obj->setArrayLength(newlen);
|
||||
obj->setArrayLength(cx, newlen);
|
||||
} else {
|
||||
/*
|
||||
* We are going to remove a lot of indexes in a presumably sparse
|
||||
@@ -627,7 +630,7 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool str
|
||||
return false;
|
||||
}
|
||||
}
|
||||
obj->setArrayLength(newlen);
|
||||
obj->setArrayLength(cx, newlen);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -743,7 +746,7 @@ slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||
return JS_TRUE;
|
||||
length = obj->getArrayLength();
|
||||
if (index >= length)
|
||||
obj->setArrayLength(index + 1);
|
||||
obj->setArrayLength(cx, index + 1);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@@ -775,7 +778,7 @@ array_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool stric
|
||||
return false;
|
||||
|
||||
if (i >= obj->getArrayLength())
|
||||
obj->setArrayLength(i + 1);
|
||||
obj->setArrayLength(cx, i + 1);
|
||||
obj->setDenseArrayElement(i, *vp);
|
||||
return true;
|
||||
}
|
||||
@@ -823,7 +826,7 @@ js_Array_dense_setelem_hole(JSContext* cx, JSObject* obj, jsint i)
|
||||
|
||||
jsuint u = jsuint(i);
|
||||
if (u >= obj->getArrayLength())
|
||||
obj->setArrayLength(u + 1);
|
||||
obj->setArrayLength(cx, u + 1);
|
||||
return true;
|
||||
}
|
||||
/* storeAccSet == ACCSET_OBJ_PRIVATE: because it can set 'length'. */
|
||||
@@ -1356,7 +1359,7 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Valu
|
||||
return JS_FALSE;
|
||||
|
||||
if (newlen > obj->getArrayLength())
|
||||
obj->setArrayLength(newlen);
|
||||
obj->setArrayLength(cx, newlen);
|
||||
|
||||
JS_ASSERT(count < uint32(-1) / sizeof(Value));
|
||||
memcpy(obj->getDenseArrayElements() + start, vector, sizeof(jsval) * count);
|
||||
@@ -1401,7 +1404,7 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector
|
||||
JS_ASSERT(obj->isArray());
|
||||
|
||||
JS_ASSERT(obj->isDenseArray());
|
||||
obj->setArrayLength(length);
|
||||
obj->setArrayLength(cx, length);
|
||||
if (!vector || !length)
|
||||
return true;
|
||||
if (!obj->ensureDenseArrayElements(cx, length))
|
||||
@@ -1730,12 +1733,12 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
}
|
||||
|
||||
JSObject *obj = ComputeThisFromVp(cx, vp);
|
||||
vp->setObject(*obj);
|
||||
|
||||
if (!obj || !js_GetLengthProperty(cx, obj, &len))
|
||||
return false;
|
||||
if (len == 0) {
|
||||
vp->setObject(*obj);
|
||||
if (len == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need a temporary array of 2 * len Value to hold the array elements
|
||||
@@ -1944,7 +1947,6 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx) || !DeleteArrayElement(cx, obj, --len, true))
|
||||
return false;
|
||||
}
|
||||
vp->setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1964,6 +1966,13 @@ array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *
|
||||
/* Per ECMA-262, return the new array length. */
|
||||
jsdouble newlength = length + jsdouble(argc);
|
||||
rval->setNumber(newlength);
|
||||
|
||||
/* watch for length overflowing to a double, and report to type inference. */
|
||||
if (!rval->isInt32()) {
|
||||
cx->markTypeCallerOverflow();
|
||||
cx->addTypeProperty(obj->getTypeObject(), "length", *rval);
|
||||
}
|
||||
|
||||
return js_SetLengthProperty(cx, obj, newlength);
|
||||
}
|
||||
|
||||
@@ -1980,7 +1989,7 @@ array_push1_dense(JSContext* cx, JSObject* obj, const Value &v, Value *rval)
|
||||
|
||||
if (!obj->ensureDenseArrayElements(cx, length + 1))
|
||||
return JS_FALSE;
|
||||
obj->setArrayLength(length + 1);
|
||||
obj->setArrayLength(cx, length + 1);
|
||||
|
||||
JS_ASSERT(obj->getDenseArrayElement(length).isMagic(JS_ARRAY_HOLE));
|
||||
obj->setDenseArrayElement(length, v);
|
||||
@@ -2005,7 +2014,7 @@ ArrayCompPushImpl(JSContext *cx, JSObject *obj, const Value &v)
|
||||
if (!obj->ensureDenseArrayElements(cx, length + 1))
|
||||
return JS_FALSE;
|
||||
}
|
||||
obj->setArrayLength(length + 1);
|
||||
obj->setArrayLength(cx, length + 1);
|
||||
obj->setDenseArrayElement(length, v);
|
||||
return JS_TRUE;
|
||||
}
|
||||
@@ -2075,7 +2084,7 @@ array_pop_dense(JSContext *cx, JSObject* obj, Value *vp)
|
||||
return JS_FALSE;
|
||||
if (!hole && !DeleteArrayElement(cx, obj, index, true))
|
||||
return JS_FALSE;
|
||||
obj->setArrayLength(index);
|
||||
obj->setArrayLength(cx, index);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@@ -2113,7 +2122,7 @@ array_shift(JSContext *cx, uintN argc, Value *vp)
|
||||
Value *elems = obj->getDenseArrayElements();
|
||||
memmove(elems, elems + 1, length * sizeof(jsval));
|
||||
obj->setDenseArrayElement(length, MagicValue(JS_ARRAY_HOLE));
|
||||
obj->setArrayLength(length);
|
||||
obj->setArrayLength(cx, length);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@@ -2198,13 +2207,29 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
|
||||
jsuint length, begin, end, count, delta, last;
|
||||
JSBool hole;
|
||||
|
||||
JSObject *obj = ComputeThisFromVp(cx, vp);
|
||||
|
||||
/* Get the type object for the returned array. */
|
||||
TypeObject *objType = obj ? obj->getTypeObject() : NULL;
|
||||
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
if (!objType || !objType->hasArrayPropagation) {
|
||||
/*
|
||||
* Make a new type object for the return value. This is an unexpected
|
||||
* result of the call so mark it at the callsite.
|
||||
*/
|
||||
objType = cx->getTypeCallerInitObject(true);
|
||||
cx->markTypeCallerUnexpected((jstype) objType);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Create a new array value to return. Our ECMA v2 proposal specs
|
||||
* that splice always returns an array value, even when given no
|
||||
* arguments. We think this is best because it eliminates the need
|
||||
* for callers to do an extra test to handle the empty splice case.
|
||||
*/
|
||||
JSObject *obj2 = js_NewArrayObject(cx, 0, NULL);
|
||||
JSObject *obj2 = js_NewArrayObject(cx, 0, NULL, objType);
|
||||
if (!obj2)
|
||||
return JS_FALSE;
|
||||
vp->setObject(*obj2);
|
||||
@@ -2213,7 +2238,6 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
|
||||
if (argc == 0)
|
||||
return JS_TRUE;
|
||||
Value *argv = JS_ARGV(cx, vp);
|
||||
JSObject *obj = ComputeThisFromVp(cx, vp);
|
||||
if (!obj || !js_GetLengthProperty(cx, obj, &length))
|
||||
return JS_FALSE;
|
||||
jsuint origlength = length;
|
||||
@@ -2296,7 +2320,7 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
|
||||
for (Value *src = srcbeg, *dst = dstbeg; src > srcend; --src, --dst)
|
||||
*dst = *src;
|
||||
|
||||
obj->setArrayLength(obj->getArrayLength() + delta);
|
||||
obj->setArrayLength(cx, obj->getArrayLength() + delta);
|
||||
} else {
|
||||
/* (uint) end could be 0, so we can't use a vanilla >= test. */
|
||||
while (last-- > end) {
|
||||
@@ -2351,6 +2375,9 @@ array_concat(JSContext *cx, uintN argc, Value *vp)
|
||||
/* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
|
||||
Value *p = JS_ARGV(cx, vp) - 1;
|
||||
|
||||
/* Get the type object to use for the result. */
|
||||
TypeObject *ntype = cx->getTypeCallerInitObject(true);
|
||||
|
||||
/* Create a new Array object and root it using *vp. */
|
||||
JSObject *aobj = ComputeThisFromVp(cx, vp);
|
||||
JSObject *nobj;
|
||||
@@ -2364,17 +2391,17 @@ 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());
|
||||
nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements(), ntype);
|
||||
if (!nobj)
|
||||
return JS_FALSE;
|
||||
nobj->setArrayLength(length);
|
||||
nobj->setArrayLength(cx, length);
|
||||
vp->setObject(*nobj);
|
||||
if (argc == 0)
|
||||
return JS_TRUE;
|
||||
argc--;
|
||||
p++;
|
||||
} else {
|
||||
nobj = js_NewArrayObject(cx, 0, NULL);
|
||||
nobj = js_NewArrayObject(cx, 0, NULL, ntype);
|
||||
if (!nobj)
|
||||
return JS_FALSE;
|
||||
vp->setObject(*nobj);
|
||||
@@ -2475,9 +2502,23 @@ array_slice(JSContext *cx, uintN argc, Value *vp)
|
||||
if (begin > end)
|
||||
begin = end;
|
||||
|
||||
/* Get the type object for the returned array. */
|
||||
TypeObject *objType = obj->getTypeObject();
|
||||
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
if (!objType->hasArrayPropagation) {
|
||||
/*
|
||||
* Make a new type object for the return value. This is an unexpected
|
||||
* result of the call so mark it at the callsite.
|
||||
*/
|
||||
objType = cx->getTypeCallerInitObject(true);
|
||||
cx->markTypeCallerUnexpected((jstype) objType);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() &&
|
||||
!js_PrototypeHasIndexedProperties(cx, obj)) {
|
||||
nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin);
|
||||
nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin, objType);
|
||||
if (!nobj)
|
||||
return JS_FALSE;
|
||||
vp->setObject(*nobj);
|
||||
@@ -2485,7 +2526,7 @@ array_slice(JSContext *cx, uintN argc, Value *vp)
|
||||
}
|
||||
|
||||
/* Create a new Array object and root it using *vp. */
|
||||
nobj = js_NewArrayObject(cx, 0, NULL);
|
||||
nobj = js_NewArrayObject(cx, 0, NULL, objType);
|
||||
if (!nobj)
|
||||
return JS_FALSE;
|
||||
vp->setObject(*nobj);
|
||||
@@ -2562,6 +2603,8 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp)
|
||||
}
|
||||
if (!hole && StrictlyEqual(cx, *vp, tosearch)) {
|
||||
vp->setNumber(i);
|
||||
if (!vp->isInt32())
|
||||
cx->markTypeCallerOverflow();
|
||||
return JS_TRUE;
|
||||
}
|
||||
if (i == stop)
|
||||
@@ -2626,6 +2669,7 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
|
||||
*/
|
||||
jsuint newlen;
|
||||
JSObject *newarr;
|
||||
TypeObject *newtype = NULL;
|
||||
#ifdef __GNUC__ /* quell GCC overwarning */
|
||||
newlen = 0;
|
||||
newarr = NULL;
|
||||
@@ -2662,7 +2706,8 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
|
||||
case MAP:
|
||||
case FILTER:
|
||||
newlen = (mode == MAP) ? length : 0;
|
||||
newarr = js_NewArrayObject(cx, newlen, NULL);
|
||||
newtype = cx->getTypeCallerInitObject(true);
|
||||
newarr = js_NewArrayObject(cx, newlen, NULL, newtype);
|
||||
if (!newarr)
|
||||
return JS_FALSE;
|
||||
vp->setObject(*newarr);
|
||||
@@ -2683,6 +2728,16 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
|
||||
|
||||
Value thisv = (argc > 1 && !REDUCE_MODE(mode)) ? argv[1] : UndefinedValue();
|
||||
|
||||
/*
|
||||
* If the callsite is being monitored for type inference, notify it of every
|
||||
* value added to the output array.
|
||||
*/
|
||||
TypeSet *types = NULL;
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
if (newtype && cx->isTypeCallerMonitored())
|
||||
types = newtype->indexTypes(cx);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For all but REDUCE, we call with 3 args (value, index, array). REDUCE
|
||||
* requires 4 args (accum, value, index, array).
|
||||
@@ -2741,6 +2796,8 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
|
||||
*vp = rval;
|
||||
break;
|
||||
case MAP:
|
||||
if (types)
|
||||
cx->addTypeProperty(newtype, NULL, rval);
|
||||
ok = SetArrayElement(cx, newarr, i, rval);
|
||||
if (!ok)
|
||||
goto out;
|
||||
@@ -2749,6 +2806,8 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
|
||||
if (!cond)
|
||||
break;
|
||||
/* The element passed the filter, so push it onto our result. */
|
||||
if (types)
|
||||
cx->addTypeProperty(newtype, NULL, tvr.value());
|
||||
ok = SetArrayElement(cx, newarr, newlen++, tvr.value());
|
||||
if (!ok)
|
||||
goto out;
|
||||
@@ -2817,6 +2876,239 @@ array_every(JSContext *cx, uintN argc, Value *vp)
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: these handlers only deal with receiver types of arrays.
|
||||
// need to generalize to other array-like objects (strings, what else?).
|
||||
|
||||
static void array_TypeSort(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
site->forceThisTypes(cx);
|
||||
|
||||
if (site->returnTypes)
|
||||
site->thisTypes->addSubset(cx, site->pool(), site->returnTypes);
|
||||
|
||||
if (site->argumentCount == 0)
|
||||
return;
|
||||
|
||||
TypeSet *funTypes = site->argumentTypes[0];
|
||||
|
||||
// make a callsite for the calls to the sorting function this will perform.
|
||||
jstype globalType = (jstype) cx->getGlobalTypeObject();
|
||||
TypeCallsite *sortSite = ArenaNew<TypeCallsite>(site->pool(), site->code, false, 2);
|
||||
sortSite->thisType = globalType;
|
||||
|
||||
// both arguments to the argument function are array elements.
|
||||
TypeSet *argTypes = sortSite->argumentTypes[0] = sortSite->argumentTypes[1] =
|
||||
TypeSet::make(cx, site->pool(), "ArraySort");
|
||||
site->thisTypes->addGetProperty(cx, site->code, argTypes, JSID_VOID);
|
||||
|
||||
funTypes->addCall(cx, sortSite);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void array_TypeInsert(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (site->returnTypes) {
|
||||
// the return type is an integer (array length).
|
||||
site->returnTypes->addType(cx, TYPE_INT32);
|
||||
}
|
||||
|
||||
site->forceThisTypes(cx);
|
||||
|
||||
for (size_t ind = 0; ind < site->argumentCount; ind++)
|
||||
site->thisTypes->addSetProperty(cx, site->code, site->argumentTypes[ind], JSID_VOID);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void array_TypeRemove(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (!site->returnTypes)
|
||||
return;
|
||||
|
||||
site->forceThisTypes(cx);
|
||||
|
||||
site->thisTypes->addGetProperty(cx, site->code, site->returnTypes, JSID_VOID);
|
||||
site->returnTypes->addType(cx, TYPE_UNDEFINED);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void array_TypeSplice(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
site->forceThisTypes(cx);
|
||||
|
||||
if (site->returnTypes) {
|
||||
// treat the returned array the same as the 'this' array.
|
||||
site->thisTypes->addSubset(cx, site->pool(), site->returnTypes);
|
||||
}
|
||||
|
||||
// all arguments beyond the first two are new array elements.
|
||||
for (size_t ind = 2; ind < site->argumentCount; ind++)
|
||||
site->thisTypes->addSetProperty(cx, site->code, site->argumentTypes[ind], JSID_VOID);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void array_TypeConcat(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
// treat the returned array as a new allocation site.
|
||||
// TODO: could use the 'this' array instead.
|
||||
TypeObject *object = site->getInitObject(cx, true);
|
||||
|
||||
site->forceThisTypes(cx);
|
||||
|
||||
if (site->returnTypes)
|
||||
site->returnTypes->addType(cx, (jstype) object);
|
||||
|
||||
// propagate elements of the 'this' array to the result.
|
||||
TypeSet *indexTypes = object->indexTypes(cx);
|
||||
site->thisTypes->addGetProperty(cx, site->code, indexTypes, JSID_VOID);
|
||||
|
||||
// ditto for all arguments to the call.
|
||||
for (size_t ind = 0; ind < site->argumentCount; ind++)
|
||||
site->argumentTypes[ind]->addGetProperty(cx, site->code, indexTypes, JSID_VOID);
|
||||
#endif
|
||||
}
|
||||
|
||||
// general purpose handler for all higher order array builtins.
|
||||
static void array_TypeExtra(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite,
|
||||
ArrayExtraMode mode)
|
||||
{
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
analyze::Bytecode *code = site->code;
|
||||
|
||||
JSArenaPool &pool = site->pool();
|
||||
|
||||
if (site->argumentCount == 0)
|
||||
return;
|
||||
TypeSet *funTypes = site->argumentTypes[0];
|
||||
|
||||
// get a type set of all possible element types of this array, and the
|
||||
// singleton type of array indexes.
|
||||
TypeSet *elemTypes = TypeSet::make(cx, pool, "array_extra");
|
||||
TypeSet *intTypes = TypeSet::make(cx, pool, "array_extra_int");
|
||||
intTypes->addType(cx, TYPE_INT32);
|
||||
|
||||
site->forceThisTypes(cx);
|
||||
site->forceReturnTypes(cx);
|
||||
|
||||
site->thisTypes->addGetProperty(cx, code, elemTypes, JSID_VOID);
|
||||
|
||||
// make the call site to use for the higher order function.
|
||||
TypeCallsite *extraSite = ArenaNew<TypeCallsite>(pool, code, false, REDUCE_MODE(mode) ? 4 : 3);
|
||||
|
||||
// figure out the 'this' type passed to the higher order function.
|
||||
if (site->argumentCount > 1 && !REDUCE_MODE(mode))
|
||||
extraSite->thisTypes = site->argumentTypes[1];
|
||||
else
|
||||
extraSite->thisType = (jstype) cx->getGlobalTypeObject();
|
||||
|
||||
switch (mode) {
|
||||
|
||||
case FOREACH:
|
||||
site->returnTypes->addType(cx, TYPE_UNDEFINED);
|
||||
break;
|
||||
|
||||
case REDUCE: {
|
||||
// the return value of the function is also the return value of the reduce call.
|
||||
extraSite->returnTypes = site->returnTypes;
|
||||
|
||||
// the first argument of the function is either its own return value,
|
||||
// the second argument of reduce, or the array element type (if there
|
||||
// is no second argument of reduce).
|
||||
extraSite->argumentTypes[0] = TypeSet::make(cx, pool, "ArrayReduce");
|
||||
site->returnTypes->addSubset(cx, pool, extraSite->argumentTypes[0]);
|
||||
|
||||
TypeSet *initialTypes = NULL;
|
||||
if (site->argumentCount >= 2)
|
||||
initialTypes = site->argumentTypes[1];
|
||||
else
|
||||
initialTypes = elemTypes;
|
||||
initialTypes->addSubset(cx, pool, extraSite->argumentTypes[0]);
|
||||
initialTypes->addSubset(cx, pool, site->returnTypes);
|
||||
break;
|
||||
}
|
||||
|
||||
case MAP: {
|
||||
// makes a new array whose element type is the return value of the
|
||||
// argument function.
|
||||
TypeObject *object = site->getInitObject(cx, true);
|
||||
extraSite->returnTypes = object->indexTypes(cx);
|
||||
|
||||
site->returnTypes->addType(cx, (jstype) object);
|
||||
break;
|
||||
}
|
||||
|
||||
case FILTER: {
|
||||
// makes a new array, whose element type is the same as the element
|
||||
// type of the 'this' array. TODO: could use the same type information
|
||||
// as the 'this' array, but might run into problems when we're able
|
||||
// to handle receiver types other than arrays.
|
||||
TypeObject *object = site->getInitObject(cx, true);
|
||||
elemTypes->addSubset(cx, pool, object->indexTypes(cx));
|
||||
|
||||
site->returnTypes->addType(cx, (jstype) object);
|
||||
break;
|
||||
}
|
||||
|
||||
case SOME:
|
||||
site->returnTypes->addType(cx, TYPE_BOOLEAN);
|
||||
break;
|
||||
|
||||
default:
|
||||
JS_NOT_REACHED("Unexpected ArrayExtraMode");
|
||||
}
|
||||
|
||||
// fill in the remaining argument types. regardless of mode, the last three
|
||||
// arguments are the element value, element index, and array itself.
|
||||
size_t argind = (mode == REDUCE) ? 1 : 0;
|
||||
extraSite->argumentTypes[argind++] = elemTypes;
|
||||
extraSite->argumentTypes[argind++] = intTypes;
|
||||
extraSite->argumentTypes[argind++] = site->thisTypes;
|
||||
JS_ASSERT(argind == extraSite->argumentCount);
|
||||
|
||||
funTypes->addCall(cx, extraSite);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void array_TypeExtraForEach(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
array_TypeExtra(cx, jsfun, jssite, FOREACH);
|
||||
}
|
||||
|
||||
static void array_TypeExtraMap(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
array_TypeExtra(cx, jsfun, jssite, MAP);
|
||||
}
|
||||
|
||||
static void array_TypeExtraReduce(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
array_TypeExtra(cx, jsfun, jssite, REDUCE);
|
||||
}
|
||||
|
||||
static void array_TypeExtraFilter(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
array_TypeExtra(cx, jsfun, jssite, FILTER);
|
||||
}
|
||||
|
||||
static void array_TypeExtraSome(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
array_TypeExtra(cx, jsfun, jssite, SOME);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_isArray(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
@@ -2828,53 +3120,55 @@ array_isArray(JSContext *cx, uintN argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
#define GENERIC JSFUN_GENERIC_NATIVE
|
||||
|
||||
static JSFunctionSpec array_methods[] = {
|
||||
#if JS_HAS_TOSOURCE
|
||||
JS_FN(js_toSource_str, array_toSource, 0,0),
|
||||
JS_FN_TYPE(js_toSource_str, array_toSource, 0,0, JS_TypeHandlerString),
|
||||
#endif
|
||||
JS_FN(js_toString_str, array_toString, 0,0),
|
||||
JS_FN(js_toLocaleString_str,array_toLocaleString,0,0),
|
||||
JS_FN_TYPE(js_toString_str, array_toString, 0,0, JS_TypeHandlerString),
|
||||
JS_FN_TYPE(js_toLocaleString_str,array_toLocaleString,0,0, JS_TypeHandlerString),
|
||||
|
||||
/* Perl-ish methods. */
|
||||
JS_FN("join", array_join, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("sort", array_sort, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("push", array_push, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("pop", array_pop, 0,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("shift", array_shift, 0,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("splice", array_splice, 2,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN_TYPE("join", array_join, 1,GENERIC, JS_TypeHandlerString),
|
||||
JS_FN_TYPE("reverse", array_reverse, 0,GENERIC, JS_TypeHandlerThis),
|
||||
JS_FN_TYPE("sort", array_sort, 1,GENERIC, array_TypeSort),
|
||||
JS_FN_TYPE("push", array_push, 1,GENERIC, array_TypeInsert),
|
||||
JS_FN_TYPE("pop", array_pop, 0,GENERIC, array_TypeRemove),
|
||||
JS_FN_TYPE("shift", array_shift, 0,GENERIC, array_TypeRemove),
|
||||
JS_FN_TYPE("unshift", array_unshift, 1,GENERIC, array_TypeInsert),
|
||||
JS_FN_TYPE("splice", array_splice, 2,GENERIC, array_TypeSplice),
|
||||
|
||||
/* Pythonic sequence methods. */
|
||||
JS_FN("concat", array_concat, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("slice", array_slice, 2,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN_TYPE("concat", array_concat, 1,GENERIC, array_TypeConcat),
|
||||
JS_FN_TYPE("slice", array_slice, 2,GENERIC, JS_TypeHandlerThis),
|
||||
|
||||
#if JS_HAS_ARRAY_EXTRAS
|
||||
JS_FN("indexOf", array_indexOf, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("lastIndexOf", array_lastIndexOf, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("forEach", array_forEach, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("map", array_map, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("reduce", array_reduce, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("reduceRight", array_reduceRight, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("filter", array_filter, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("some", array_some, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("every", array_every, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN_TYPE("indexOf", array_indexOf, 1,GENERIC, JS_TypeHandlerInt),
|
||||
JS_FN_TYPE("lastIndexOf", array_lastIndexOf, 1,GENERIC, JS_TypeHandlerInt),
|
||||
JS_FN_TYPE("forEach", array_forEach, 1,GENERIC, array_TypeExtraForEach),
|
||||
JS_FN_TYPE("map", array_map, 1,GENERIC, array_TypeExtraMap),
|
||||
JS_FN_TYPE("reduce", array_reduce, 1,GENERIC, array_TypeExtraReduce),
|
||||
JS_FN_TYPE("reduceRight", array_reduceRight, 1,GENERIC, array_TypeExtraReduce),
|
||||
JS_FN_TYPE("filter", array_filter, 1,GENERIC, array_TypeExtraFilter),
|
||||
JS_FN_TYPE("some", array_some, 1,GENERIC, array_TypeExtraSome),
|
||||
JS_FN_TYPE("every", array_every, 1,GENERIC, array_TypeExtraSome),
|
||||
#endif
|
||||
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static JSFunctionSpec array_static_methods[] = {
|
||||
JS_FN("isArray", array_isArray, 1,0),
|
||||
JS_FN_TYPE("isArray", array_isArray, 1,0, JS_TypeHandlerBool),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
/* The count here is a guess for the final capacity. */
|
||||
static inline JSObject *
|
||||
NewDenseArrayObject(JSContext *cx, jsuint count)
|
||||
NewDenseArrayObject(JSContext *cx, TypeObject *type, jsuint count)
|
||||
{
|
||||
gc::FinalizeKind kind = GuessObjectGCKind(count, true);
|
||||
return NewNonFunction<WithProto::Class>(cx, &js_ArrayClass, NULL, NULL, kind);
|
||||
return NewNonFunction<WithProto::Class>(cx, &js_ArrayClass, NULL, NULL, type, kind);
|
||||
}
|
||||
|
||||
JSBool
|
||||
@@ -2883,6 +3177,8 @@ js_Array(JSContext *cx, uintN argc, Value *vp)
|
||||
jsuint length;
|
||||
const Value *vector;
|
||||
|
||||
TypeObject *type = cx->getTypeCallerInitObject(true);
|
||||
|
||||
if (argc == 0) {
|
||||
length = 0;
|
||||
vector = NULL;
|
||||
@@ -2892,6 +3188,9 @@ js_Array(JSContext *cx, uintN argc, Value *vp)
|
||||
} else if (!vp[2].isNumber()) {
|
||||
length = 1;
|
||||
vector = vp + 2;
|
||||
|
||||
/* Unexpected case for type inference. */
|
||||
cx->addTypeProperty(type, NULL, vp[2]);
|
||||
} else {
|
||||
length = ValueIsLength(cx, vp + 2);
|
||||
if (vp[2].isNull())
|
||||
@@ -2900,7 +3199,7 @@ js_Array(JSContext *cx, uintN argc, Value *vp)
|
||||
}
|
||||
|
||||
/* Whether called with 'new' or not, use a new Array object. */
|
||||
JSObject *obj = NewDenseArrayObject(cx, length);
|
||||
JSObject *obj = NewDenseArrayObject(cx, type, length);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
vp->setObject(*obj);
|
||||
@@ -2921,9 +3220,11 @@ js_NewEmptyArray(JSContext* cx, JSObject* proto, int32 len)
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
/* Initialize all fields of JSObject. */
|
||||
obj->init(cx, &js_ArrayClass, proto, proto->getParent(),
|
||||
(void*) len, true);
|
||||
// TODO: pass type object to use in from tracer.
|
||||
TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_UNKNOWN_ARRAY);
|
||||
|
||||
/* Initialize all fields, calling init before setting obj->map. */
|
||||
obj->init(cx, &js_ArrayClass, proto, proto->getParent(), type, (void*) len, true);
|
||||
obj->setSharedNonNativeMap();
|
||||
return obj;
|
||||
}
|
||||
@@ -2947,11 +3248,35 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewPreallocatedArray, CONTEXT, OBJECT, I
|
||||
0, nanojit::ACCSET_STORE_ANY)
|
||||
#endif
|
||||
|
||||
// specialized handler for Array() that propagates arguments into indexes
|
||||
// of the resulting array.
|
||||
static void array_TypeNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
TypeObject *object = site->getInitObject(cx, true);
|
||||
if (site->returnTypes)
|
||||
site->returnTypes->addType(cx, (jstype) object);
|
||||
|
||||
TypeSet *indexTypes = object->indexTypes(cx);
|
||||
|
||||
// ignore the case where the call is passed a single argument. this is
|
||||
// expected to be the array length, but if it isn't we will catch it
|
||||
// in the Array native itself.
|
||||
if (site->argumentCount > 1) {
|
||||
for (size_t ind = 0; ind < site->argumentCount; ind++)
|
||||
site->argumentTypes[ind]->addSubset(cx, site->pool(), indexTypes);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JSObject* JS_FASTCALL
|
||||
js_InitializerArray(JSContext* cx, int32 count)
|
||||
{
|
||||
TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_UNKNOWN_ARRAY);
|
||||
gc::FinalizeKind kind = GuessObjectGCKind(count, true);
|
||||
return NewArrayWithKind(cx, kind);
|
||||
return NewArrayWithKind(cx, type, kind);
|
||||
}
|
||||
#ifdef JS_TRACER
|
||||
JS_DEFINE_CALLINFO_2(extern, OBJECT, js_InitializerArray, CONTEXT, INT32, 0,
|
||||
@@ -2961,7 +3286,7 @@ JS_DEFINE_CALLINFO_2(extern, OBJECT, js_InitializerArray, CONTEXT, INT32, 0,
|
||||
JSObject *
|
||||
js_InitArrayClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *proto = js_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1,
|
||||
JSObject *proto = js_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1, array_TypeNew,
|
||||
NULL, array_methods, NULL, array_static_methods);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
@@ -2972,14 +3297,15 @@ js_InitArrayClass(JSContext *cx, JSObject *obj)
|
||||
*/
|
||||
JS_ASSERT(proto->emptyShapes && proto->emptyShapes[0]->getClass() == proto->getClass());
|
||||
|
||||
proto->setArrayLength(0);
|
||||
JS_AddTypeProperty(cx, proto, "length", INT_TO_JSVAL(0));
|
||||
proto->setArrayLength(cx, 0);
|
||||
return proto;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector)
|
||||
js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector, TypeObject *type)
|
||||
{
|
||||
JSObject *obj = NewDenseArrayObject(cx, length);
|
||||
JSObject *obj = NewDenseArrayObject(cx, type, length);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
@@ -2993,11 +3319,11 @@ js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector)
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_NewSlowArrayObject(JSContext *cx)
|
||||
js_NewSlowArrayObject(JSContext *cx, TypeObject *type)
|
||||
{
|
||||
JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_SlowArrayClass, NULL, NULL);
|
||||
JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_SlowArrayClass, NULL, NULL, type);
|
||||
if (obj)
|
||||
obj->setArrayLength(0);
|
||||
obj->setArrayLength(cx, 0);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -3155,10 +3481,10 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone)
|
||||
vector.append(val);
|
||||
}
|
||||
|
||||
*clone = js_NewArrayObject(cx, jsvalCount, vector.begin());
|
||||
*clone = js_NewArrayObject(cx, jsvalCount, vector.begin(), obj->getTypeObject());
|
||||
if (!*clone)
|
||||
return JS_FALSE;
|
||||
(*clone)->setArrayLength(length);
|
||||
(*clone)->setArrayLength(cx, length);
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user