/*
* Copyright (C) 1999-2000 Harri Porten ([email protected])
* Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2003 Peter Kelly ([email protected])
* Copyright (C) 2006 Alexey Proskuryakov ([email protected])
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*
*/
#include "config.h"
#include "ArrayPrototype.h"
#include "CachedCall.h"
#include "CodeBlock.h"
#include "Interpreter.h"
#include "JIT.h"
#include "JSStringBuilder.h"
#include "Lookup.h"
#include "ObjectPrototype.h"
#include "Operations.h"
#include
#include
#include
namespace JSC {
ASSERT_CLASS_FITS_IN_CELL(ArrayPrototype);
static JSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*, JSObject*, JSValue, const ArgList&);
}
#include "ArrayPrototype.lut.h"
namespace JSC {
static inline bool isNumericCompareFunction(ExecState* exec, CallType callType, const CallData& callData)
{
if (callType != CallTypeJS)
return false;
#if ENABLE(JIT)
// If the JIT is enabled then we need to preserve the invariant that every
// function with a CodeBlock also has JIT code.
CodeBlock* codeBlock = 0;
#if ENABLE(INTERPRETER)
if (!exec->globalData().canUseJIT())
codeBlock = &callData.js.functionExecutable->bytecode(exec, callData.js.scopeChain);
else
#endif
{
callData.js.functionExecutable->jitCode(exec, callData.js.scopeChain);
codeBlock = &callData.js.functionExecutable->generatedBytecode();
}
#else
CodeBlock* codeBlock = &callData.js.functionExecutable->bytecode(exec, callData.js.scopeChain);
#endif
return codeBlock->isNumericCompareFunction();
}
// ------------------------------ ArrayPrototype ----------------------------
const ClassInfo ArrayPrototype::info = {"Array", &JSArray::info, 0, ExecState::arrayTable};
/* Source for ArrayPrototype.lut.h
@begin arrayTable 16
toString arrayProtoFuncToString DontEnum|Function 0
toLocaleString arrayProtoFuncToLocaleString DontEnum|Function 0
concat arrayProtoFuncConcat DontEnum|Function 1
join arrayProtoFuncJoin DontEnum|Function 1
pop arrayProtoFuncPop DontEnum|Function 0
push arrayProtoFuncPush DontEnum|Function 1
reverse arrayProtoFuncReverse DontEnum|Function 0
shift arrayProtoFuncShift DontEnum|Function 0
slice arrayProtoFuncSlice DontEnum|Function 2
sort arrayProtoFuncSort DontEnum|Function 1
splice arrayProtoFuncSplice DontEnum|Function 2
unshift arrayProtoFuncUnShift DontEnum|Function 1
every arrayProtoFuncEvery DontEnum|Function 1
forEach arrayProtoFuncForEach DontEnum|Function 1
some arrayProtoFuncSome DontEnum|Function 1
indexOf arrayProtoFuncIndexOf DontEnum|Function 1
lastIndexOf arrayProtoFuncLastIndexOf DontEnum|Function 1
filter arrayProtoFuncFilter DontEnum|Function 1
reduce arrayProtoFuncReduce DontEnum|Function 1
reduceRight arrayProtoFuncReduceRight DontEnum|Function 1
map arrayProtoFuncMap DontEnum|Function 1
@end
*/
// ECMA 15.4.4
ArrayPrototype::ArrayPrototype(NonNullPassRefPtr structure)
: JSArray(structure)
{
}
bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
return getStaticFunctionSlot(exec, ExecState::arrayTable(exec), this, propertyName, slot);
}
bool ArrayPrototype::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
{
return getStaticFunctionDescriptor(exec, ExecState::arrayTable(exec), this, propertyName, descriptor);
}
// ------------------------------ Array Functions ----------------------------
// Helper function
static JSValue getProperty(ExecState* exec, JSObject* obj, unsigned index)
{
PropertySlot slot(obj);
if (!obj->getPropertySlot(exec, index, slot))
return JSValue();
return slot.getValue(exec, index);
}
static void putProperty(ExecState* exec, JSObject* obj, const Identifier& propertyName, JSValue value)
{
PutPropertySlot slot;
obj->put(exec, propertyName, value, slot);
}
JSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
{
bool isRealArray = isJSArray(&exec->globalData(), thisValue);
if (!isRealArray && !thisValue.inherits(&JSArray::info))
return throwError(exec, TypeError);
JSArray* thisObj = asArray(thisValue);
HashSet& arrayVisitedElements = exec->globalData().arrayVisitedElements;
if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) {
if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth)
return throwError(exec, RangeError, "Maximum call stack size exceeded.");
}
bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
if (alreadyVisited)
return jsEmptyString(exec); // return an empty string, avoiding infinite recursion.
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
unsigned totalSize = length ? length - 1 : 0;
Vector, 256> strBuffer(length);
for (unsigned k = 0; k canGetIndex(k))
element = thisObj->getIndex(k);
else
element = thisObj->get(exec, k);
if (element.isUndefinedOrNull())
continue;
UString str = element.toString(exec);
strBuffer[k] = str.rep();
totalSize += str.size();
if (!strBuffer.data()) {
throwOutOfMemoryError(exec);
}
if (exec->hadException())
break;
}
arrayVisitedElements.remove(thisObj);
if (!totalSize)
return jsEmptyString(exec);
Vector buffer;
buffer.reserveCapacity(totalSize);
if (!buffer.data())
return throwOutOfMemoryError(exec);
for (unsigned i = 0; i rep = strBuffer[i])
buffer.append(rep->characters(), rep->length());
}
ASSERT(buffer.size() == totalSize);
return jsString(exec, UString::adopt(buffer));
}
JSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
{
if (!thisValue.inherits(&JSArray::info))
return throwError(exec, TypeError);
JSObject* thisObj = asArray(thisValue);
HashSet& arrayVisitedElements = exec->globalData().arrayVisitedElements;
if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) {
if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth)
return throwError(exec, RangeError, "Maximum call stack size exceeded.");
}
bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
if (alreadyVisited)
return jsEmptyString(exec); // return an empty string, avoding infinite recursion.
JSStringBuilder strBuffer;
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
for (unsigned k = 0; k = 1)
strBuffer.append(',');
JSValue element = thisObj->get(exec, k);
if (!element.isUndefinedOrNull()) {
JSObject* o = element.toObject(exec);
JSValue conversionFunction = o->get(exec, exec->propertyNames().toLocaleString);
UString str;
CallData callData;
CallType callType = conversionFunction.getCallData(callData);
if (callType != CallTypeNone)
str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toString(exec);
else
str = element.toString(exec);
strBuffer.append(str);
}
}
arrayVisitedElements.remove(thisObj);
return strBuffer.build(exec);
}
JSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
JSObject* thisObj = thisValue.toThisObject(exec);
HashSet& arrayVisitedElements = exec->globalData().arrayVisitedElements;
if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) {
if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth)
return throwError(exec, RangeError, "Maximum call stack size exceeded.");
}
bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
if (alreadyVisited)
return jsEmptyString(exec); // return an empty string, avoding infinite recursion.
JSStringBuilder strBuffer;
UString separator;
if (!args.at(0).isUndefined())
separator = args.at(0).toString(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
unsigned k = 0;
if (isJSArray(&exec->globalData(), thisObj)) {
JSArray* array = asArray(thisObj);
for (; k canGetIndex(k))
break;
if (k >= 1) {
if (separator.isNull())
strBuffer.append(',');
else
strBuffer.append(separator);
}
JSValue element = array->getIndex(k);
if (!element.isUndefinedOrNull())
strBuffer.append(element.toString(exec));
}
}
for (; k = 1) {
if (separator.isNull())
strBuffer.append(',');
else
strBuffer.append(separator);
}
JSValue element = thisObj->get(exec, k);
if (!element.isUndefinedOrNull())
strBuffer.append(element.toString(exec));
}
arrayVisitedElements.remove(thisObj);
return strBuffer.build(exec);
}
JSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
JSArray* arr = constructEmptyArray(exec);
int n = 0;
JSValue curArg = thisValue.toThisObject(exec);
ArgList::const_iterator it = args.begin();
ArgList::const_iterator end = args.end();
while (1) {
if (curArg.inherits(&JSArray::info)) {
unsigned length = curArg.get(exec, exec->propertyNames().length).toUInt32(exec);
JSObject* curObject = curArg.toObject(exec);
for (unsigned k = 0; k put(exec, n, v);
n++;
}
} else {
arr->put(exec, n, curArg);
n++;
}
if (it == end)
break;
curArg = (*it);
++it;
}
arr->setLength(n);
return arr;
}
JSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
{
if (isJSArray(&exec->globalData(), thisValue))
return asArray(thisValue)->pop();
JSObject* thisObj = thisValue.toThisObject(exec);
JSValue result;
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (length == 0) {
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length));
result = jsUndefined();
} else {
result = thisObj->get(exec, length - 1);
thisObj->deleteProperty(exec, length - 1);
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - 1));
}
return result;
}
JSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
if (isJSArray(&exec->globalData(), thisValue) && args.size() == 1) {
JSArray* array = asArray(thisValue);
array->push(exec, *args.begin());
return jsNumber(exec, array->length());
}
JSObject* thisObj = thisValue.toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
for (unsigned n = 0; n put(exec, length + n, args.at(n));
length += args.size();
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length));
return jsNumber(exec, length);
}
JSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
{
JSObject* thisObj = thisValue.toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
unsigned middle = length / 2;
for (unsigned k = 0; k put(exec, k, obj2);
else
thisObj->deleteProperty(exec, k);
if (obj)
thisObj->put(exec, lk1, obj);
else
thisObj->deleteProperty(exec, lk1);
}
return thisObj;
}
JSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
{
JSObject* thisObj = thisValue.toThisObject(exec);
JSValue result;
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (length == 0) {
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length));
result = jsUndefined();
} else {
result = thisObj->get(exec, 0);
for (unsigned k = 1; k put(exec, k - 1, obj);
else
thisObj->deleteProperty(exec, k - 1);
}
thisObj->deleteProperty(exec, length - 1);
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - 1));
}
return result;
}
JSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
// http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
JSObject* thisObj = thisValue.toThisObject(exec);
// We return a new array
JSArray* resObj = constructEmptyArray(exec);
JSValue result = resObj;
double begin = args.at(0).toInteger(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (begin >= 0) {
if (begin > length)
begin = length;
} else {
begin += length;
if (begin length)
end = length;
}
}
int n = 0;
int b = static_cast(begin);
int e = static_cast(end);
for (int k = b; k put(exec, n, v);
}
resObj->setLength(n);
return result;
}
JSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
JSObject* thisObj = thisValue.toThisObject(exec);
JSValue function = args.at(0);
CallData callData;
CallType callType = function.getCallData(callData);
if (thisObj->classInfo() == &JSArray::info) {
if (isNumericCompareFunction(exec, callType, callData))
asArray(thisObj)->sortNumeric(exec, function, callType, callData);
else if (callType != CallTypeNone)
asArray(thisObj)->sort(exec, function, callType, callData);
else
asArray(thisObj)->sort(exec);
return thisObj;
}
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (!length)
return thisObj;
// "Min" sort. Not the fastest, but definitely less code than heapsort
// or quicksort, and much less swapping than bubblesort/insertionsort.
for (unsigned i = 0; i get(exec, i);
unsigned themin = i;
JSValue minObj = iObj;
for (unsigned j = i + 1; j get(exec, j);
double compareResult;
if (jObj.isUndefined())
compareResult = 1; // don't check minObj because there's no need to differentiate == (0) from > (1)
else if (minObj.isUndefined())
compareResult = -1;
else if (callType != CallTypeNone) {
MarkedArgumentBuffer l;
l.append(jObj);
l.append(minObj);
compareResult = call(exec, function, callType, callData, exec->globalThisValue(), l).toNumber(exec);
} else
compareResult = (jObj.toString(exec) i) {
thisObj->put(exec, i, minObj);
thisObj->put(exec, themin, iObj);
}
}
return thisObj;
}
JSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
JSObject* thisObj = thisValue.toThisObject(exec);
// 15.4.4.12
JSArray* resObj = constructEmptyArray(exec);
JSValue result = resObj;
// FIXME: Firefox returns an empty array.
if (!args.size())
return jsUndefined();
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
double relativeBegin = args.at(0).toInteger(exec);
unsigned begin;
if (relativeBegin (relativeBegin);
} else
begin = std::min(static_cast(relativeBegin), length);
unsigned deleteCount;
if (args.size() > 1)
deleteCount = std::min(std::max(args.at(1).toUInt32(exec), 0), length - begin);
else
deleteCount = length - begin;
for (unsigned k = 0; k put(exec, k, v);
}
resObj->setLength(deleteCount);
unsigned additionalArgs = std::max(args.size() - 2, 0);
if (additionalArgs != deleteCount) {
if (additionalArgs put(exec, k + additionalArgs, v);
else
thisObj->deleteProperty(exec, k + additionalArgs);
}
for (unsigned k = length; k > length - deleteCount + additionalArgs; --k)
thisObj->deleteProperty(exec, k - 1);
} else {
for (unsigned k = length - deleteCount; k > begin; --k) {
if (JSValue obj = getProperty(exec, thisObj, k + deleteCount - 1))
thisObj->put(exec, k + additionalArgs - 1, obj);
else
thisObj->deleteProperty(exec, k + additionalArgs - 1);
}
}
}
for (unsigned k = 0; k put(exec, k + begin, args.at(k + 2));
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - deleteCount + additionalArgs));
return result;
}
JSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
JSObject* thisObj = thisValue.toThisObject(exec);
// 15.4.4.13
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
unsigned nrArgs = args.size();
if (nrArgs) {
for (unsigned k = length; k > 0; --k) {
if (JSValue v = getProperty(exec, thisObj, k - 1))
thisObj->put(exec, k + nrArgs - 1, v);
else
thisObj->deleteProperty(exec, k + nrArgs - 1);
}
}
for (unsigned k = 0; k put(exec, k, args.at(k));
JSValue result = jsNumber(exec, length + nrArgs);
putProperty(exec, thisObj, exec->propertyNames().length, result);
return result;
}
JSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
JSObject* thisObj = thisValue.toThisObject(exec);
JSValue function = args.at(0);
CallData callData;
CallType callType = function.getCallData(callData);
if (callType == CallTypeNone)
return throwError(exec, TypeError);
JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec);
JSArray* resultArray = constructEmptyArray(exec);
unsigned filterIndex = 0;
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
unsigned k = 0;
if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
JSFunction* f = asFunction(function);
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3, exec->exceptionSlot());
for (; k hadException(); ++k) {
if (!array->canGetIndex(k))
break;
JSValue v = array->getIndex(k);
cachedCall.setThis(applyThis);
cachedCall.setArgument(0, v);
cachedCall.setArgument(1, jsNumber(exec, k));
cachedCall.setArgument(2, thisObj);
JSValue result = cachedCall.call();
if (result.toBoolean(exec))
resultArray->put(exec, filterIndex++, v);
}
if (k == length)
return resultArray;
}
for (; k hadException(); ++k) {
PropertySlot slot(thisObj);
if (!thisObj->getPropertySlot(exec, k, slot))
continue;
JSValue v = slot.getValue(exec, k);
MarkedArgumentBuffer eachArguments;
eachArguments.append(v);
eachArguments.append(jsNumber(exec, k));
eachArguments.append(thisObj);
JSValue result = call(exec, function, callType, callData, applyThis, eachArguments);
if (result.toBoolean(exec))
resultArray->put(exec, filterIndex++, v);
}
return resultArray;
}
JSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
JSObject* thisObj = thisValue.toThisObject(exec);
JSValue function = args.at(0);
CallData callData;
CallType callType = function.getCallData(callData);
if (callType == CallTypeNone)
return throwError(exec, TypeError);
JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
JSArray* resultArray = constructEmptyArray(exec, length);
unsigned k = 0;
if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
JSFunction* f = asFunction(function);
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3, exec->exceptionSlot());
for (; k hadException(); ++k) {
if (UNLIKELY(!array->canGetIndex(k)))
break;
cachedCall.setThis(applyThis);
cachedCall.setArgument(0, array->getIndex(k));
cachedCall.setArgument(1, jsNumber(exec, k));
cachedCall.setArgument(2, thisObj);
resultArray->JSArray::put(exec, k, cachedCall.call());
}
}
for (; k hadException(); ++k) {
PropertySlot slot(thisObj);
if (!thisObj->getPropertySlot(exec, k, slot))
continue;
JSValue v = slot.getValue(exec, k);
MarkedArgumentBuffer eachArguments;
eachArguments.append(v);
eachArguments.append(jsNumber(exec, k));
eachArguments.append(thisObj);
JSValue result = call(exec, function, callType, callData, applyThis, eachArguments);
resultArray->put(exec, k, result);
}
return resultArray;
}
// Documentation for these three is available at:
// http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every
// http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach
// http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some
JSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
JSObject* thisObj = thisValue.toThisObject(exec);
JSValue function = args.at(0);
CallData callData;
CallType callType = function.getCallData(callData);
if (callType == CallTypeNone)
return throwError(exec, TypeError);
JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec);
JSValue result = jsBoolean(true);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
unsigned k = 0;
if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
JSFunction* f = asFunction(function);
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3, exec->exceptionSlot());
for (; k hadException(); ++k) {
if (UNLIKELY(!array->canGetIndex(k)))
break;
cachedCall.setThis(applyThis);
cachedCall.setArgument(0, array->getIndex(k));
cachedCall.setArgument(1, jsNumber(exec, k));
cachedCall.setArgument(2, thisObj);
JSValue result = cachedCall.call();
if (!result.toBoolean(cachedCall.newCallFrame(exec)))
return jsBoolean(false);
}
}
for (; k hadException(); ++k) {
PropertySlot slot(thisObj);
if (!thisObj->getPropertySlot(exec, k, slot))
continue;
MarkedArgumentBuffer eachArguments;
eachArguments.append(slot.getValue(exec, k));
eachArguments.append(jsNumber(exec, k));
eachArguments.append(thisObj);
bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec);
if (!predicateResult) {
result = jsBoolean(false);
break;
}
}
return result;
}
JSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
JSObject* thisObj = thisValue.toThisObject(exec);
JSValue function = args.at(0);
CallData callData;
CallType callType = function.getCallData(callData);
if (callType == CallTypeNone)
return throwError(exec, TypeError);
JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
unsigned k = 0;
if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
JSFunction* f = asFunction(function);
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3, exec->exceptionSlot());
for (; k hadException(); ++k) {
if (UNLIKELY(!array->canGetIndex(k)))
break;
cachedCall.setThis(applyThis);
cachedCall.setArgument(0, array->getIndex(k));
cachedCall.setArgument(1, jsNumber(exec, k));
cachedCall.setArgument(2, thisObj);
cachedCall.call();
}
}
for (; k hadException(); ++k) {
PropertySlot slot(thisObj);
if (!thisObj->getPropertySlot(exec, k, slot))
continue;
MarkedArgumentBuffer eachArguments;
eachArguments.append(slot.getValue(exec, k));
eachArguments.append(jsNumber(exec, k));
eachArguments.append(thisObj);
call(exec, function, callType, callData, applyThis, eachArguments);
}
return jsUndefined();
}
JSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
JSObject* thisObj = thisValue.toThisObject(exec);
JSValue function = args.at(0);
CallData callData;
CallType callType = function.getCallData(callData);
if (callType == CallTypeNone)
return throwError(exec, TypeError);
JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec);
JSValue result = jsBoolean(false);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
unsigned k = 0;
if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
JSFunction* f = asFunction(function);
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3, exec->exceptionSlot());
for (; k hadException(); ++k) {
if (UNLIKELY(!array->canGetIndex(k)))
break;
cachedCall.setThis(applyThis);
cachedCall.setArgument(0, array->getIndex(k));
cachedCall.setArgument(1, jsNumber(exec, k));
cachedCall.setArgument(2, thisObj);
JSValue result = cachedCall.call();
if (result.toBoolean(cachedCall.newCallFrame(exec)))
return jsBoolean(true);
}
}
for (; k hadException(); ++k) {
PropertySlot slot(thisObj);
if (!thisObj->getPropertySlot(exec, k, slot))
continue;
MarkedArgumentBuffer eachArguments;
eachArguments.append(slot.getValue(exec, k));
eachArguments.append(jsNumber(exec, k));
eachArguments.append(thisObj);
bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec);
if (predicateResult) {
result = jsBoolean(true);
break;
}
}
return result;
}
JSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
JSObject* thisObj = thisValue.toThisObject(exec);
JSValue function = args.at(0);
CallData callData;
CallType callType = function.getCallData(callData);
if (callType == CallTypeNone)
return throwError(exec, TypeError);
unsigned i = 0;
JSValue rv;
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (!length && args.size() == 1)
return throwError(exec, TypeError);
JSArray* array = 0;
if (isJSArray(&exec->globalData(), thisObj))
array = asArray(thisObj);
if (args.size() >= 2)
rv = args.at(1);
else if (array && array->canGetIndex(0)){
rv = array->getIndex(0);
i = 1;
} else {
for (i = 0; i exceptionSlot());
for (; i hadException(); ++i) {
cachedCall.setThis(jsNull());
cachedCall.setArgument(0, rv);
JSValue v;
if (LIKELY(array->canGetIndex(i)))
v = array->getIndex(i);
else
break; // length has been made unsafe while we enumerate fallback to slow path
cachedCall.setArgument(1, v);
cachedCall.setArgument(2, jsNumber(exec, i));
cachedCall.setArgument(3, array);
rv = cachedCall.call();
}
if (i == length) // only return if we reached the end of the array
return rv;
}
for (; i hadException(); ++i) {
JSValue prop = getProperty(exec, thisObj, i);
if (!prop)
continue;
MarkedArgumentBuffer eachArguments;
eachArguments.append(rv);
eachArguments.append(prop);
eachArguments.append(jsNumber(exec, i));
eachArguments.append(thisObj);
rv = call(exec, function, callType, callData, jsNull(), eachArguments);
}
return rv;
}
JSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
JSObject* thisObj = thisValue.toThisObject(exec);
JSValue function = args.at(0);
CallData callData;
CallType callType = function.getCallData(callData);
if (callType == CallTypeNone)
return throwError(exec, TypeError);
unsigned i = 0;
JSValue rv;
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (!length && args.size() == 1)
return throwError(exec, TypeError);
JSArray* array = 0;
if (isJSArray(&exec->globalData(), thisObj))
array = asArray(thisObj);
if (args.size() >= 2)
rv = args.at(1);
else if (array && array->canGetIndex(length - 1)){
rv = array->getIndex(length - 1);
i = 1;
} else {
for (i = 0; i exceptionSlot());
for (; i hadException(); ++i) {
unsigned idx = length - i - 1;
cachedCall.setThis(jsNull());
cachedCall.setArgument(0, rv);
if (UNLIKELY(!array->canGetIndex(idx)))
break; // length has been made unsafe while we enumerate fallback to slow path
cachedCall.setArgument(1, array->getIndex(idx));
cachedCall.setArgument(2, jsNumber(exec, idx));
cachedCall.setArgument(3, array);
rv = cachedCall.call();
}
if (i == length) // only return if we reached the end of the array
return rv;
}
for (; i hadException(); ++i) {
unsigned idx = length - i - 1;
JSValue prop = getProperty(exec, thisObj, idx);
if (!prop)
continue;
MarkedArgumentBuffer eachArguments;
eachArguments.append(rv);
eachArguments.append(prop);
eachArguments.append(jsNumber(exec, idx));
eachArguments.append(thisObj);
rv = call(exec, function, callType, callData, jsNull(), eachArguments);
}
return rv;
}
JSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
{
// JavaScript 1.5 Extension by Mozilla
// Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf
JSObject* thisObj = thisValue.toThisObject(exec);
unsigned index = 0;
double d = args.at(1).toInteger(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (d 0) {
if (d > length)
index = length;
else
index = static_cast(d);
}
JSValue searchElement = args.at(0);
for (; index get(exec, exec->propertyNames().length).toUInt32(exec);
int index = length - 1;
double d = args.at(1).toIntegerPreserveNaN(exec);
if (d (d);
JSValue searchElement = args.at(0);
for (; index >= 0; --index) {
JSValue e = getProperty(exec, thisObj, index);
if (!e)
continue;
if (JSValue::strictEqual(exec, searchElement, e))
return jsNumber(exec, index);
}
return jsNumber(exec, -1);
}
} // namespace JSC