blob: 29caecea2b455cb12a103ad910cef5de52c5f299 [file] [log] [blame]
/*
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* Copyright (C) 2003, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
* Copyright (C) 2003 Peter Kelly (pmk@post.com)
* Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
*
* 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 "StringRecursionChecker.h"
#include <algorithm>
#include <wtf/Assertions.h>
#include <wtf/HashSet.h>
namespace JSC {
ASSERT_CLASS_FITS_IN_CELL(ArrayPrototype);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*);
}
#include "ArrayPrototype.lut.h"
namespace JSC {
static inline bool isNumericCompareFunction(ExecState* exec, CallType callType, const CallData& callData)
{
if (callType != CallTypeJS)
return false;
FunctionExecutable* executable = callData.js.functionExecutable;
JSObject* error = executable->compileForCall(exec, callData.js.scopeChain);
if (error)
return false;
return executable->generatedBytecodeForCall().isNumericCompareFunction();
}
// ------------------------------ ArrayPrototype ----------------------------
const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_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(JSGlobalObject* globalObject, Structure* structure)
: JSArray(globalObject->globalData(), structure)
{
ASSERT(inherits(&s_info));
putAnonymousValue(globalObject->globalData(), 0, globalObject);
}
bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
return getStaticFunctionSlot<JSArray>(exec, ExecState::arrayTable(exec), this, propertyName, slot);
}
bool ArrayPrototype::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
{
return getStaticFunctionDescriptor<JSArray>(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);
}
static unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
{
JSValue value = exec->argument(argument);
if (value.isUndefined())
return undefinedValue;
double indexDouble = value.toInteger(exec);
if (indexDouble < 0) {
indexDouble += length;
return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
}
return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
{
JSValue thisValue = exec->hostThisValue();
bool isRealArray = isJSArray(&exec->globalData(), thisValue);
if (!isRealArray && !thisValue.inherits(&JSArray::s_info))
return throwVMTypeError(exec);
JSArray* thisObj = asArray(thisValue);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
StringRecursionChecker checker(exec, thisObj);
if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
return earlyReturnValue;
unsigned totalSize = length ? length - 1 : 0;
#if OS(SYMBIAN)
// Symbian has very limited stack size available.
// This function could be called recursively and allocating 1K on stack here cause
// stack overflow on Symbian devices.
Vector<RefPtr<StringImpl> > strBuffer(length);
#else
Vector<RefPtr<StringImpl>, 256> strBuffer(length);
#endif
for (unsigned k = 0; k < length; k++) {
JSValue element;
if (isRealArray && thisObj->canGetIndex(k))
element = thisObj->getIndex(k);
else
element = thisObj->get(exec, k);
if (element.isUndefinedOrNull())
continue;
UString str = element.toString(exec);
strBuffer[k] = str.impl();
totalSize += str.length();
if (!strBuffer.data()) {
throwOutOfMemoryError(exec);
}
if (exec->hadException())
break;
}
if (!totalSize)
return JSValue::encode(jsEmptyString(exec));
Vector<UChar> buffer;
buffer.reserveCapacity(totalSize);
if (!buffer.data())
return JSValue::encode(throwOutOfMemoryError(exec));
for (unsigned i = 0; i < length; i++) {
if (i)
buffer.append(',');
if (RefPtr<StringImpl> rep = strBuffer[i])
buffer.append(rep->characters(), rep->length());
}
ASSERT(buffer.size() == totalSize);
return JSValue::encode(jsString(exec, UString::adopt(buffer)));
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
{
JSValue thisValue = exec->hostThisValue();
if (!thisValue.inherits(&JSArray::s_info))
return throwVMTypeError(exec);
JSObject* thisObj = asArray(thisValue);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
StringRecursionChecker checker(exec, thisObj);
if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
return earlyReturnValue;
JSStringBuilder strBuffer;
for (unsigned k = 0; k < length; k++) {
if (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 = getCallData(conversionFunction, callData);
if (callType != CallTypeNone)
str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toString(exec);
else
str = element.toString(exec);
strBuffer.append(str);
}
}
return JSValue::encode(strBuffer.build(exec));
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
StringRecursionChecker checker(exec, thisObj);
if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
return earlyReturnValue;
JSStringBuilder strBuffer;
UString separator;
if (!exec->argument(0).isUndefined())
separator = exec->argument(0).toString(exec);
unsigned k = 0;
if (isJSArray(&exec->globalData(), thisObj)) {
JSArray* array = asArray(thisObj);
if (length) {
if (!array->canGetIndex(k))
goto skipFirstLoop;
JSValue element = array->getIndex(k);
if (!element.isUndefinedOrNull())
strBuffer.append(element.toString(exec));
k++;
}
if (separator.isNull()) {
for (; k < length; k++) {
if (!array->canGetIndex(k))
break;
strBuffer.append(',');
JSValue element = array->getIndex(k);
if (!element.isUndefinedOrNull())
strBuffer.append(element.toString(exec));
}
} else {
for (; k < length; k++) {
if (!array->canGetIndex(k))
break;
strBuffer.append(separator);
JSValue element = array->getIndex(k);
if (!element.isUndefinedOrNull())
strBuffer.append(element.toString(exec));
}
}
}
skipFirstLoop:
for (; k < length; k++) {
if (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));
}
return JSValue::encode(strBuffer.build(exec));
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
{
JSValue thisValue = exec->hostThisValue();
JSArray* arr = constructEmptyArray(exec);
unsigned n = 0;
JSValue curArg = thisValue.toThisObject(exec);
size_t i = 0;
size_t argCount = exec->argumentCount();
while (1) {
if (curArg.inherits(&JSArray::s_info)) {
unsigned length = curArg.get(exec, exec->propertyNames().length).toUInt32(exec);
JSObject* curObject = curArg.toObject(exec);
for (unsigned k = 0; k < length; ++k) {
if (JSValue v = getProperty(exec, curObject, k))
arr->put(exec, n, v);
n++;
}
} else {
arr->put(exec, n, curArg);
n++;
}
if (i == argCount)
break;
curArg = (exec->argument(i));
++i;
}
arr->setLength(n);
return JSValue::encode(arr);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
{
JSValue thisValue = exec->hostThisValue();
if (isJSArray(&exec->globalData(), thisValue))
return JSValue::encode(asArray(thisValue)->pop());
JSObject* thisObj = thisValue.toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSValue result;
if (length == 0) {
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length));
result = jsUndefined();
} else {
result = thisObj->get(exec, length - 1);
thisObj->deleteProperty(exec, length - 1);
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1));
}
return JSValue::encode(result);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec)
{
JSValue thisValue = exec->hostThisValue();
if (isJSArray(&exec->globalData(), thisValue) && exec->argumentCount() == 1) {
JSArray* array = asArray(thisValue);
array->push(exec, exec->argument(0));
return JSValue::encode(jsNumber(array->length()));
}
JSObject* thisObj = thisValue.toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
for (unsigned n = 0; n < exec->argumentCount(); n++)
thisObj->put(exec, length + n, exec->argument(n));
length += exec->argumentCount();
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length));
return JSValue::encode(jsNumber(length));
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
unsigned middle = length / 2;
for (unsigned k = 0; k < middle; k++) {
unsigned lk1 = length - k - 1;
JSValue obj2 = getProperty(exec, thisObj, lk1);
JSValue obj = getProperty(exec, thisObj, k);
if (obj2)
thisObj->put(exec, k, obj2);
else
thisObj->deleteProperty(exec, k);
if (obj)
thisObj->put(exec, lk1, obj);
else
thisObj->deleteProperty(exec, lk1);
}
return JSValue::encode(thisObj);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
JSValue result;
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
if (length == 0) {
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length));
result = jsUndefined();
} else {
result = thisObj->get(exec, 0);
if (isJSArray(&exec->globalData(), thisObj))
((JSArray *)thisObj)->shiftCount(exec, 1);
else {
for (unsigned k = 1; k < length; k++) {
if (JSValue obj = getProperty(exec, thisObj, k))
thisObj->put(exec, k - 1, obj);
else
thisObj->deleteProperty(exec, k - 1);
}
thisObj->deleteProperty(exec, length - 1);
}
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1));
}
return JSValue::encode(result);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
{
// http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
// We return a new array
JSArray* resObj = constructEmptyArray(exec);
JSValue result = resObj;
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
unsigned n = 0;
for (unsigned k = begin; k < end; k++, n++) {
if (JSValue v = getProperty(exec, thisObj, k))
resObj->put(exec, n, v);
}
resObj->setLength(n);
return JSValue::encode(result);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (!length || exec->hadException())
return JSValue::encode(thisObj);
JSValue function = exec->argument(0);
CallData callData;
CallType callType = getCallData(function, callData);
if (thisObj->classInfo() == &JSArray::s_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 JSValue::encode(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 < length - 1; ++i) {
JSValue iObj = thisObj->get(exec, i);
if (exec->hadException())
return JSValue::encode(jsUndefined());
unsigned themin = i;
JSValue minObj = iObj;
for (unsigned j = i + 1; j < length; ++j) {
JSValue jObj = thisObj->get(exec, j);
if (exec->hadException())
return JSValue::encode(jsUndefined());
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) < minObj.toString(exec)) ? -1 : 1;
if (compareResult < 0) {
themin = j;
minObj = jObj;
}
}
// Swap themin and i
if (themin > i) {
thisObj->put(exec, i, minObj);
thisObj->put(exec, themin, iObj);
}
}
return JSValue::encode(thisObj);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
{
// 15.4.4.12
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
if (!exec->argumentCount())
return JSValue::encode(constructEmptyArray(exec));
unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
unsigned deleteCount = length - begin;
if (exec->argumentCount() > 1) {
double deleteDouble = exec->argument(1).toInteger(exec);
if (deleteDouble < 0)
deleteCount = 0;
else if (deleteDouble > length - begin)
deleteCount = length - begin;
else
deleteCount = static_cast<unsigned>(deleteDouble);
}
JSArray* resObj = new (exec) JSArray(exec->globalData(), exec->lexicalGlobalObject()->arrayStructure(), deleteCount, CreateCompact);
JSValue result = resObj;
JSGlobalData& globalData = exec->globalData();
for (unsigned k = 0; k < deleteCount; k++)
resObj->uncheckedSetIndex(globalData, k, getProperty(exec, thisObj, k + begin));
resObj->setLength(deleteCount);
unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0);
if (additionalArgs != deleteCount) {
if (additionalArgs < deleteCount) {
if ((!begin) && (isJSArray(&exec->globalData(), thisObj)))
((JSArray *)thisObj)->shiftCount(exec, deleteCount - additionalArgs);
else {
for (unsigned k = begin; k < length - deleteCount; ++k) {
if (JSValue v = getProperty(exec, thisObj, k + deleteCount))
thisObj->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 {
if ((!begin) && (isJSArray(&exec->globalData(), thisObj)))
((JSArray *)thisObj)->unshiftCount(exec, additionalArgs - deleteCount);
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 < additionalArgs; ++k)
thisObj->put(exec, k + begin, exec->argument(k + 2));
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs));
return JSValue::encode(result);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
{
// 15.4.4.13
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
unsigned nrArgs = exec->argumentCount();
if ((nrArgs) && (length)) {
if (isJSArray(&exec->globalData(), thisObj))
((JSArray *)thisObj)->unshiftCount(exec, nrArgs);
else {
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 < nrArgs; ++k)
thisObj->put(exec, k, exec->argument(k));
JSValue result = jsNumber(length + nrArgs);
putProperty(exec, thisObj, exec->propertyNames().length, result);
return JSValue::encode(result);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSValue function = exec->argument(0);
CallData callData;
CallType callType = getCallData(function, callData);
if (callType == CallTypeNone)
return throwVMTypeError(exec);
JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec);
JSArray* resultArray = constructEmptyArray(exec);
unsigned filterIndex = 0;
unsigned k = 0;
if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
JSFunction* f = asFunction(function);
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3);
for (; k < length && !exec->hadException(); ++k) {
if (!array->canGetIndex(k))
break;
JSValue v = array->getIndex(k);
cachedCall.setThis(applyThis);
cachedCall.setArgument(0, v);
cachedCall.setArgument(1, jsNumber(k));
cachedCall.setArgument(2, thisObj);
JSValue result = cachedCall.call();
if (result.toBoolean(exec))
resultArray->put(exec, filterIndex++, v);
}
if (k == length)
return JSValue::encode(resultArray);
}
for (; k < length && !exec->hadException(); ++k) {
PropertySlot slot(thisObj);
if (!thisObj->getPropertySlot(exec, k, slot))
continue;
JSValue v = slot.getValue(exec, k);
if (exec->hadException())
return JSValue::encode(jsUndefined());
MarkedArgumentBuffer eachArguments;
eachArguments.append(v);
eachArguments.append(jsNumber(k));
eachArguments.append(thisObj);
JSValue result = call(exec, function, callType, callData, applyThis, eachArguments);
if (result.toBoolean(exec))
resultArray->put(exec, filterIndex++, v);
}
return JSValue::encode(resultArray);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSValue function = exec->argument(0);
CallData callData;
CallType callType = getCallData(function, callData);
if (callType == CallTypeNone)
return throwVMTypeError(exec);
JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(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);
for (; k < length && !exec->hadException(); ++k) {
if (UNLIKELY(!array->canGetIndex(k)))
break;
cachedCall.setThis(applyThis);
cachedCall.setArgument(0, array->getIndex(k));
cachedCall.setArgument(1, jsNumber(k));
cachedCall.setArgument(2, thisObj);
resultArray->JSArray::put(exec, k, cachedCall.call());
}
}
for (; k < length && !exec->hadException(); ++k) {
PropertySlot slot(thisObj);
if (!thisObj->getPropertySlot(exec, k, slot))
continue;
JSValue v = slot.getValue(exec, k);
if (exec->hadException())
return JSValue::encode(jsUndefined());
MarkedArgumentBuffer eachArguments;
eachArguments.append(v);
eachArguments.append(jsNumber(k));
eachArguments.append(thisObj);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSValue result = call(exec, function, callType, callData, applyThis, eachArguments);
resultArray->put(exec, k, result);
}
return JSValue::encode(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
EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSValue function = exec->argument(0);
CallData callData;
CallType callType = getCallData(function, callData);
if (callType == CallTypeNone)
return throwVMTypeError(exec);
JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec);
JSValue result = jsBoolean(true);
unsigned k = 0;
if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
JSFunction* f = asFunction(function);
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3);
for (; k < length && !exec->hadException(); ++k) {
if (UNLIKELY(!array->canGetIndex(k)))
break;
cachedCall.setThis(applyThis);
cachedCall.setArgument(0, array->getIndex(k));
cachedCall.setArgument(1, jsNumber(k));
cachedCall.setArgument(2, thisObj);
JSValue result = cachedCall.call();
if (!result.toBoolean(cachedCall.newCallFrame(exec)))
return JSValue::encode(jsBoolean(false));
}
}
for (; k < length && !exec->hadException(); ++k) {
PropertySlot slot(thisObj);
if (!thisObj->getPropertySlot(exec, k, slot))
continue;
MarkedArgumentBuffer eachArguments;
eachArguments.append(slot.getValue(exec, k));
eachArguments.append(jsNumber(k));
eachArguments.append(thisObj);
if (exec->hadException())
return JSValue::encode(jsUndefined());
bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec);
if (!predicateResult) {
result = jsBoolean(false);
break;
}
}
return JSValue::encode(result);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSValue function = exec->argument(0);
CallData callData;
CallType callType = getCallData(function, callData);
if (callType == CallTypeNone)
return throwVMTypeError(exec);
JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec);
unsigned k = 0;
if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
JSFunction* f = asFunction(function);
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3);
for (; k < length && !exec->hadException(); ++k) {
if (UNLIKELY(!array->canGetIndex(k)))
break;
cachedCall.setThis(applyThis);
cachedCall.setArgument(0, array->getIndex(k));
cachedCall.setArgument(1, jsNumber(k));
cachedCall.setArgument(2, thisObj);
cachedCall.call();
}
}
for (; k < length && !exec->hadException(); ++k) {
PropertySlot slot(thisObj);
if (!thisObj->getPropertySlot(exec, k, slot))
continue;
MarkedArgumentBuffer eachArguments;
eachArguments.append(slot.getValue(exec, k));
eachArguments.append(jsNumber(k));
eachArguments.append(thisObj);
if (exec->hadException())
return JSValue::encode(jsUndefined());
call(exec, function, callType, callData, applyThis, eachArguments);
}
return JSValue::encode(jsUndefined());
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSValue function = exec->argument(0);
CallData callData;
CallType callType = getCallData(function, callData);
if (callType == CallTypeNone)
return throwVMTypeError(exec);
JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec);
JSValue result = jsBoolean(false);
unsigned k = 0;
if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
JSFunction* f = asFunction(function);
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3);
for (; k < length && !exec->hadException(); ++k) {
if (UNLIKELY(!array->canGetIndex(k)))
break;
cachedCall.setThis(applyThis);
cachedCall.setArgument(0, array->getIndex(k));
cachedCall.setArgument(1, jsNumber(k));
cachedCall.setArgument(2, thisObj);
JSValue result = cachedCall.call();
if (result.toBoolean(cachedCall.newCallFrame(exec)))
return JSValue::encode(jsBoolean(true));
}
}
for (; k < length && !exec->hadException(); ++k) {
PropertySlot slot(thisObj);
if (!thisObj->getPropertySlot(exec, k, slot))
continue;
MarkedArgumentBuffer eachArguments;
eachArguments.append(slot.getValue(exec, k));
eachArguments.append(jsNumber(k));
eachArguments.append(thisObj);
if (exec->hadException())
return JSValue::encode(jsUndefined());
bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec);
if (predicateResult) {
result = jsBoolean(true);
break;
}
}
return JSValue::encode(result);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSValue function = exec->argument(0);
CallData callData;
CallType callType = getCallData(function, callData);
if (callType == CallTypeNone)
return throwVMTypeError(exec);
unsigned i = 0;
JSValue rv;
if (!length && exec->argumentCount() == 1)
return throwVMTypeError(exec);
JSArray* array = 0;
if (isJSArray(&exec->globalData(), thisObj))
array = asArray(thisObj);
if (exec->argumentCount() >= 2)
rv = exec->argument(1);
else if (array && array->canGetIndex(0)){
rv = array->getIndex(0);
i = 1;
} else {
for (i = 0; i < length; i++) {
rv = getProperty(exec, thisObj, i);
if (rv)
break;
}
if (!rv)
return throwVMTypeError(exec);
i++;
}
if (callType == CallTypeJS && array) {
CachedCall cachedCall(exec, asFunction(function), 4);
for (; i < length && !exec->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(i));
cachedCall.setArgument(3, array);
rv = cachedCall.call();
}
if (i == length) // only return if we reached the end of the array
return JSValue::encode(rv);
}
for (; i < length && !exec->hadException(); ++i) {
JSValue prop = getProperty(exec, thisObj, i);
if (exec->hadException())
return JSValue::encode(jsUndefined());
if (!prop)
continue;
MarkedArgumentBuffer eachArguments;
eachArguments.append(rv);
eachArguments.append(prop);
eachArguments.append(jsNumber(i));
eachArguments.append(thisObj);
rv = call(exec, function, callType, callData, jsNull(), eachArguments);
}
return JSValue::encode(rv);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSValue function = exec->argument(0);
CallData callData;
CallType callType = getCallData(function, callData);
if (callType == CallTypeNone)
return throwVMTypeError(exec);
unsigned i = 0;
JSValue rv;
if (!length && exec->argumentCount() == 1)
return throwVMTypeError(exec);
JSArray* array = 0;
if (isJSArray(&exec->globalData(), thisObj))
array = asArray(thisObj);
if (exec->argumentCount() >= 2)
rv = exec->argument(1);
else if (array && array->canGetIndex(length - 1)){
rv = array->getIndex(length - 1);
i = 1;
} else {
for (i = 0; i < length; i++) {
rv = getProperty(exec, thisObj, length - i - 1);
if (rv)
break;
}
if (!rv)
return throwVMTypeError(exec);
i++;
}
if (callType == CallTypeJS && array) {
CachedCall cachedCall(exec, asFunction(function), 4);
for (; i < length && !exec->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(idx));
cachedCall.setArgument(3, array);
rv = cachedCall.call();
}
if (i == length) // only return if we reached the end of the array
return JSValue::encode(rv);
}
for (; i < length && !exec->hadException(); ++i) {
unsigned idx = length - i - 1;
JSValue prop = getProperty(exec, thisObj, idx);
if (exec->hadException())
return JSValue::encode(jsUndefined());
if (!prop)
continue;
MarkedArgumentBuffer eachArguments;
eachArguments.append(rv);
eachArguments.append(prop);
eachArguments.append(jsNumber(idx));
eachArguments.append(thisObj);
rv = call(exec, function, callType, callData, jsNull(), eachArguments);
}
return JSValue::encode(rv);
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
{
// 15.4.4.14
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
JSValue searchElement = exec->argument(0);
for (; index < length; ++index) {
JSValue e = getProperty(exec, thisObj, index);
if (!e)
continue;
if (JSValue::strictEqual(exec, searchElement, e))
return JSValue::encode(jsNumber(index));
}
return JSValue::encode(jsNumber(-1));
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
{
// 15.4.4.15
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
if (!length)
return JSValue::encode(jsNumber(-1));
unsigned index = length - 1;
JSValue fromValue = exec->argument(1);
if (!fromValue.isUndefined()) {
double fromDouble = fromValue.toInteger(exec);
if (fromDouble < 0) {
fromDouble += length;
if (fromDouble < 0)
return JSValue::encode(jsNumber(-1));
}
if (fromDouble < length)
index = static_cast<unsigned>(fromDouble);
}
JSValue searchElement = exec->argument(0);
do {
ASSERT(index < length);
JSValue e = getProperty(exec, thisObj, index);
if (!e)
continue;
if (JSValue::strictEqual(exec, searchElement, e))
return JSValue::encode(jsNumber(index));
} while (index--);
return JSValue::encode(jsNumber(-1));
}
} // namespace JSC