Bug 784294 - Convert some array extras to self-hosted js implementations. r=Waldo

The following methods are converted:
- lastIndexOf
- indexOf
- forEach
- some
- every
- reduce
- reduceRight

--HG--
extra : rebase_source : 0c5964d0e55a5ead39805fbd3e485cdb07c53c3f
This commit is contained in:
Till Schneidereit 2012-08-28 14:35:15 +02:00
parent 926aae1659
commit 8c3fbc1224
2 changed files with 352 additions and 332 deletions

View File

@ -1 +1,338 @@
//this space intentionally left blank
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* ES5 15.4.4.14. */
function ArrayIndexOf(searchElement/*, fromIndex*/) {
/* Step 1. */
var O = %ToObject(this);
/* Steps 2-3. */
var len = TO_UINT32(O.length);
/* Step 4. */
if (len === 0)
return -1;
/* Step 5. */
var n = arguments.length > 1 ? %ToInteger(arguments[1]) : 0;
/* Step 6. */
if (n >= len)
return -1;
var k;
/* Step 7. */
if (n >= 0)
k = n;
/* Step 8. */
else {
/* Step a. */
k = len + n;
/* Step b. */
if (k < 0)
k = 0;
}
/* Step 9. */
for (; k < len; k++) {
if (k in O && O[k] === searchElement)
return k;
}
/* Step 10. */
return -1;
}
function ArrayStaticIndexOf(list, searchElement/*, fromIndex*/) {
if (arguments.length < 1)
%ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.indexOf');
var fromIndex = arguments.length > 2 ? arguments[2] : 0;
return %_CallFunction(list, searchElement, fromIndex, ArrayIndexOf);
}
/* ES5 15.4.4.15. */
function ArrayLastIndexOf(searchElement/*, fromIndex*/) {
/* Step 1. */
var O = %ToObject(this);
/* Steps 2-3. */
var len = TO_UINT32(O.length);
/* Step 4. */
if (len === 0)
return -1;
/* Step 5. */
var n = arguments.length > 1 ? %ToInteger(arguments[1]) : len - 1;
/* Steps 6-7. */
var k;
if (n > len - 1)
k = len - 1;
else if (n < 0)
k = len + n;
else
k = n;
/* Step 8. */
for (; k >= 0; k--) {
if (k in O && O[k] === searchElement)
return k;
}
/* Step 9. */
return -1;
}
function ArrayStaticLastIndexOf(list, searchElement/*, fromIndex*/) {
if (arguments.length < 1)
%ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.lastIndexOf');
var fromIndex;
if (arguments.length > 2) {
fromIndex = arguments[2];
} else {
var O = %ToObject(list);
var len = TO_UINT32(O.length);
fromIndex = len - 1;
}
return %_CallFunction(list, searchElement, fromIndex, ArrayLastIndexOf);
}
/* ES5 15.4.4.16. */
function ArrayEvery(callbackfn/*, thisArg*/) {
/* Step 1. */
var O = %ToObject(this);
/* Steps 2-3. */
var len = TO_UINT32(O.length);
/* Step 4. */
if (arguments.length === 0)
%ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.every');
if (!IsCallable(callbackfn))
%ThrowError(JSMSG_NOT_FUNCTION, callbackfn);
/* Step 5. */
var T = arguments.length > 1 ? arguments[1] : void 0;
/* Steps 6-7. */
/* Steps a (implicit), and d. */
for (var k = 0; k < len; k++) {
/* Step b */
if (k in O) {
/* Step c. */
if (!%_CallFunction(T, O[k], k, O, callbackfn))
return false;
}
}
/* Step 8. */
return true;
}
function ArrayStaticEvery(list, callbackfn/*, thisArg*/) {
if (arguments.length < 2)
%ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.every');
var T = arguments.length > 2 ? arguments[2] : void 0;
return %_CallFunction(list, callbackfn, T, ArrayEvery);
}
/* ES5 15.4.4.17. */
function ArraySome(callbackfn/*, thisArg*/) {
/* Step 1. */
var O = %ToObject(this);
/* Steps 2-3. */
var len = TO_UINT32(O.length);
/* Step 4. */
if (arguments.length === 0)
%ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.some');
if (!IsCallable(callbackfn))
%ThrowError(JSMSG_NOT_FUNCTION, callbackfn);
/* Step 5. */
var T = arguments.length > 1 ? arguments[1] : void 0;
/* Steps 6-7. */
/* Steps a (implicit), and d. */
for (var k = 0; k < len; k++) {
/* Step b */
if (k in O) {
/* Step c. */
if (%_CallFunction(T, O[k], k, O, callbackfn))
return true;
}
}
/* Step 8. */
return false;
}
function ArrayStaticSome(list, callbackfn/*, thisArg*/) {
if (arguments.length < 2)
%ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.some');
var T = arguments.length > 2 ? arguments[2] : void 0;
return %_CallFunction(list, callbackfn, T, ArraySome);
}
/* ES5 15.4.4.18. */
function ArrayForEach(callbackfn/*, thisArg*/) {
/* Step 1. */
var O = %ToObject(this);
/* Steps 2-3. */
var len = TO_UINT32(O.length);
/* Step 4. */
if (arguments.length === 0)
%ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.forEach');
if (!IsCallable(callbackfn))
%ThrowError(JSMSG_NOT_FUNCTION, callbackfn);
/* Step 5. */
var T = arguments.length > 1 ? arguments[1] : void 0;
/* Steps 6-7. */
/* Steps a (implicit), and d. */
for (var k = 0; k < len; k++) {
/* Step b */
if (k in O) {
/* Step c. */
%_CallFunction(T, O[k], k, O, callbackfn);
}
}
/* Step 8. */
return void 0;
}
function ArrayStaticForEach(list, callbackfn/*, thisArg*/) {
if (arguments.length < 2)
%ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.forEach');
var T = arguments.length > 2 ? arguments[2] : void 0;
%_CallFunction(list, callbackfn, T, ArrayForEach);
}
/* ES5 15.4.4.21. */
function ArrayReduce(callbackfn/*, initialValue*/) {
/* Step 1. */
var O = %ToObject(this);
/* Steps 2-3. */
var len = TO_UINT32(O.length);
/* Step 4. */
if (arguments.length === 0)
%ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.reduce');
if (!IsCallable(callbackfn))
%ThrowError(JSMSG_NOT_FUNCTION, callbackfn);
/* Step 6. */
var k = 0;
/* Steps 5, 7-8. */
var accumulator;
if (arguments.length > 1) {
accumulator = arguments[1];
} else {
/* Step 5. */
if (len === 0)
%ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
var kPresent = false;
for (; k < len; k++) {
if (k in O) {
accumulator = O[k];
kPresent = true;
k++;
break;
}
}
if (!kPresent)
%ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
}
/* Step 9. */
/* Steps a (implicit), and d. */
for (; k < len; k++) {
/* Step b */
if (k in O) {
/* Step c. */
accumulator = callbackfn(accumulator, O[k], k, O);
}
}
/* Step 10. */
return accumulator;
}
function ArrayStaticReduce(list, callbackfn) {
if (arguments.length < 2)
%ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.reduce');
if (arguments.length > 2)
%_CallFunction(list, callbackfn, arguments[2], ArrayReduce);
else
%_CallFunction(list, callbackfn, ArrayReduce);
}
/* ES5 15.4.4.22. */
function ArrayReduceRight(callbackfn/*, initialValue*/) {
/* Step 1. */
var O = %ToObject(this);
/* Steps 2-3. */
var len = TO_UINT32(O.length);
/* Step 4. */
if (arguments.length === 0)
%ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.reduce');
if (!IsCallable(callbackfn))
%ThrowError(JSMSG_NOT_FUNCTION, callbackfn);
/* Step 6. */
var k = len - 1;
/* Steps 5, 7-8. */
var accumulator;
if (arguments.length > 1) {
accumulator = arguments[1];
} else {
/* Step 5. */
if (len === 0)
%ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
var kPresent = false;
for (; k >= 0; k--) {
if (k in O) {
accumulator = O[k];
kPresent = true;
k--;
break;
}
}
if (!kPresent)
%ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
}
/* Step 9. */
/* Steps a (implicit), and d. */
for (; k >= 0; k--) {
/* Step b */
if (k in O) {
/* Step c. */
accumulator = callbackfn(accumulator, O[k], k, O);
}
}
/* Step 10. */
return accumulator;
}
function ArrayStaticReduceRight(list, callbackfn) {
if (arguments.length < 2)
%ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.reduceRight');
if (arguments.length > 2)
%_CallFunction(list, callbackfn, arguments[2], ArrayReduceRight);
else
%_CallFunction(list, callbackfn, ArrayReduceRight);
}

View File

@ -2924,227 +2924,6 @@ array_slice(JSContext *cx, unsigned argc, Value *vp)
return JS_TRUE;
}
enum IndexOfKind {
IndexOf,
LastIndexOf
};
static JSBool
array_indexOfHelper(JSContext *cx, IndexOfKind mode, CallArgs &args)
{
uint32_t length, i, stop;
int direction;
JSBool hole;
RootedValue tosearch(cx), elt(cx);
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
if (!GetLengthProperty(cx, obj, &length))
return JS_FALSE;
if (length == 0)
goto not_found;
if (args.length() <= 1) {
i = (mode == LastIndexOf) ? length - 1 : 0;
tosearch = (args.length() != 0) ? args[0] : UndefinedValue();
} else {
double start;
tosearch = args[0];
if (!ToInteger(cx, args[1], &start))
return false;
if (start < 0) {
start += length;
if (start < 0) {
if (mode == LastIndexOf)
goto not_found;
i = 0;
} else {
i = (uint32_t)start;
}
} else if (start >= length) {
if (mode == IndexOf)
goto not_found;
i = length - 1;
} else {
i = (uint32_t)start;
}
}
if (mode == LastIndexOf) {
stop = 0;
direction = -1;
} else {
stop = length - 1;
direction = 1;
}
for (;;) {
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
!GetElement(cx, obj, (uint32_t)i, &hole, &elt)) {
return JS_FALSE;
}
if (!hole) {
bool equal;
if (!StrictlyEqual(cx, elt, tosearch, &equal))
return false;
if (equal) {
args.rval().setNumber(i);
return true;
}
}
if (i == stop)
goto not_found;
i += direction;
}
not_found:
args.rval().setInt32(-1);
return JS_TRUE;
}
static JSBool
array_indexOf(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return array_indexOfHelper(cx, IndexOf, args);
}
static JSBool
array_lastIndexOf(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return array_indexOfHelper(cx, LastIndexOf, args);
}
/* ECMA 15.4.4.16-15.4.4.18. */
class ArrayForEachBehavior
{
public:
static bool shouldExit(MutableHandleValue callbackRval, MutableHandleValue rval) { return false; }
static Value lateExitValue() { return UndefinedValue(); }
};
class ArrayEveryBehavior
{
public:
static bool shouldExit(MutableHandleValue callbackRval, MutableHandleValue rval)
{
if (!ToBoolean(callbackRval)) {
rval.setBoolean(false);
return true;
}
return false;
}
static Value lateExitValue() { return BooleanValue(true); }
};
class ArraySomeBehavior
{
public:
static bool shouldExit(MutableHandleValue callbackRval, MutableHandleValue rval)
{
if (ToBoolean(callbackRval)) {
rval.setBoolean(true);
return true;
}
return false;
}
static Value lateExitValue() { return BooleanValue(false); }
};
template <class Behavior>
static inline bool
array_readonlyCommon(JSContext *cx, CallArgs &args)
{
/* Step 1. */
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
/* Step 2-3. */
uint32_t len;
if (!GetLengthProperty(cx, obj, &len))
return false;
/* Step 4. */
if (args.length() == 0) {
js_ReportMissingArg(cx, args.calleev(), 0);
return false;
}
RootedObject callable(cx, ValueToCallable(cx, &args[0]));
if (!callable)
return false;
/* Step 5. */
Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
/* Step 6. */
uint32_t k = 0;
/* Step 7. */
RootedValue kValue(cx);
InvokeArgsGuard ag;
while (k < len) {
if (!JS_CHECK_OPERATION_LIMIT(cx))
return false;
/* Step a, b, and c.i. */
JSBool kNotPresent;
if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
return false;
/* Step c.ii-iii. */
if (!kNotPresent) {
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
return false;
ag.setCallee(ObjectValue(*callable));
ag.setThis(thisv);
ag[0] = kValue;
ag[1] = NumberValue(k);
ag[2] = ObjectValue(*obj);
if (!Invoke(cx, ag))
return false;
if (Behavior::shouldExit(ag.rval(), args.rval()))
return true;
}
/* Step d. */
k++;
}
/* Step 8. */
args.rval().set(Behavior::lateExitValue());
return true;
}
/* ES5 15.4.4.16. */
static JSBool
array_every(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return array_readonlyCommon<ArrayEveryBehavior>(cx, args);
}
/* ES5 15.4.4.17. */
static JSBool
array_some(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return array_readonlyCommon<ArraySomeBehavior>(cx, args);
}
/* ES5 15.4.4.18. */
static JSBool
array_forEach(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return array_readonlyCommon<ArrayForEachBehavior>(cx, args);
}
/* ES5 15.4.4.19. */
static JSBool
array_map(JSContext *cx, unsigned argc, Value *vp)
@ -3332,109 +3111,6 @@ class ArrayReduceRightBehavior
}
};
template<class Behavior>
static inline bool
array_reduceCommon(JSContext *cx, CallArgs &args)
{
/* Step 1. */
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
/* Step 2-3. */
uint32_t len;
if (!GetLengthProperty(cx, obj, &len))
return false;
/* Step 4. */
if (args.length() == 0) {
js_ReportMissingArg(cx, args.calleev(), 0);
return false;
}
RootedObject callable(cx, ValueToCallable(cx, &args[0]));
if (!callable)
return false;
/* Step 5. */
if (len == 0 && args.length() < 2) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE);
return false;
}
/* Step 6. */
uint32_t k, end;
int32_t step;
Behavior::initialize(len, &k, &end, &step);
/* Step 7-8. */
RootedValue accumulator(cx);
if (args.length() >= 2) {
accumulator = args[1];
} else {
JSBool kNotPresent = true;
while (kNotPresent && k != end) {
if (!GetElement(cx, obj, k, &kNotPresent, &accumulator))
return false;
k += step;
}
if (kNotPresent) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE);
return false;
}
}
/* Step 9. */
RootedValue kValue(cx);
InvokeArgsGuard ag;
while (k != end) {
if (!JS_CHECK_OPERATION_LIMIT(cx))
return false;
/* Step a, b, and c.i. */
JSBool kNotPresent;
if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
return false;
/* Step c.ii. */
if (!kNotPresent) {
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 4, &ag))
return false;
ag.setCallee(ObjectValue(*callable));
ag.setThis(UndefinedValue());
ag[0] = accumulator;
ag[1] = kValue;
ag[2] = NumberValue(k);
ag[3] = ObjectValue(*obj);
if (!Invoke(cx, ag))
return false;
accumulator = ag.rval();
}
/* Step d. */
k += step;
}
/* Step 10. */
args.rval().set(accumulator);
return true;
}
/* ES5 15.4.4.21. */
static JSBool
array_reduce(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return array_reduceCommon<ArrayReduceBehavior>(cx, args);
}
/* ES5 15.4.4.22. */
static JSBool
array_reduceRight(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return array_reduceCommon<ArrayReduceRightBehavior>(cx, args);
}
static JSBool
array_isArray(JSContext *cx, unsigned argc, Value *vp)
{
@ -3467,15 +3143,15 @@ static JSFunctionSpec array_methods[] = {
JS_FN("concat", array_concat, 1,JSFUN_GENERIC_NATIVE),
JS_FN("slice", array_slice, 2,JSFUN_GENERIC_NATIVE),
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),
{"lastIndexOf", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayLastIndexOf"},
{"indexOf", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayIndexOf"},
{"forEach", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayForEach"},
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),
{"reduce", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayReduce"},
{"reduceRight", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayReduceRight"},
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),
{"some", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArraySome"},
{"every", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayEvery"},
JS_FN("iterator", JS_ArrayIterator, 0,0),
JS_FS_END
@ -3483,6 +3159,13 @@ static JSFunctionSpec array_methods[] = {
static JSFunctionSpec array_static_methods[] = {
JS_FN("isArray", array_isArray, 1,0),
{"lastIndexOf", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayStaticLastIndexOf"},
{"indexOf", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayStaticIndexOf"},
{"forEach", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayStaticForEach"},
{"every", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayStaticEvery"},
{"some", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayStaticSome"},
{"reduce", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayStaticReduce"},
{"reduceRight", {NULL, NULL}, 1,JSFUN_INTERPRETED, "ArrayStaticReduceRight"},
JS_FS_END
};