2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
4 * Copyright (C) 2003 Peter Kelly (pmk@post.com)
5 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
25 #include "ArrayPrototype.h"
27 #include "CachedCall.h"
28 #include "CodeBlock.h"
29 #include "Interpreter.h"
31 #include "JSStringBuilder.h"
33 #include "ObjectPrototype.h"
34 #include "Operations.h"
35 #include "StringRecursionChecker.h"
37 #include <wtf/Assertions.h>
38 #include <wtf/HashSet.h>
42 ASSERT_CLASS_FITS_IN_CELL(ArrayPrototype);
44 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);
45 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*);
46 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*);
47 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*);
48 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*);
49 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*);
50 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*);
51 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*);
52 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*);
53 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState*);
54 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*);
55 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*);
56 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState*);
57 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState*);
58 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState*);
59 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*);
60 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState*);
61 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState*);
62 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState*);
63 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState*);
64 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*);
68 #include "ArrayPrototype.lut.h"
72 static inline bool isNumericCompareFunction(ExecState* exec, CallType callType, const CallData& callData)
74 if (callType != CallTypeJS)
77 FunctionExecutable* executable = callData.js.functionExecutable;
79 JSObject* error = executable->compileForCall(exec, callData.js.scopeChain);
83 return executable->generatedBytecodeForCall().isNumericCompareFunction();
86 // ------------------------------ ArrayPrototype ----------------------------
88 const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, 0, ExecState::arrayPrototypeTable, CREATE_METHOD_TABLE(ArrayPrototype)};
90 /* Source for ArrayPrototype.lut.h
91 @begin arrayPrototypeTable 16
92 toString arrayProtoFuncToString DontEnum|Function 0
93 toLocaleString arrayProtoFuncToLocaleString DontEnum|Function 0
94 concat arrayProtoFuncConcat DontEnum|Function 1
95 join arrayProtoFuncJoin DontEnum|Function 1
96 pop arrayProtoFuncPop DontEnum|Function 0
97 push arrayProtoFuncPush DontEnum|Function 1
98 reverse arrayProtoFuncReverse DontEnum|Function 0
99 shift arrayProtoFuncShift DontEnum|Function 0
100 slice arrayProtoFuncSlice DontEnum|Function 2
101 sort arrayProtoFuncSort DontEnum|Function 1
102 splice arrayProtoFuncSplice DontEnum|Function 2
103 unshift arrayProtoFuncUnShift DontEnum|Function 1
104 every arrayProtoFuncEvery DontEnum|Function 1
105 forEach arrayProtoFuncForEach DontEnum|Function 1
106 some arrayProtoFuncSome DontEnum|Function 1
107 indexOf arrayProtoFuncIndexOf DontEnum|Function 1
108 lastIndexOf arrayProtoFuncLastIndexOf DontEnum|Function 1
109 filter arrayProtoFuncFilter DontEnum|Function 1
110 reduce arrayProtoFuncReduce DontEnum|Function 1
111 reduceRight arrayProtoFuncReduceRight DontEnum|Function 1
112 map arrayProtoFuncMap DontEnum|Function 1
117 ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure)
118 : JSArray(globalObject->globalData(), structure)
122 void ArrayPrototype::finishCreation(JSGlobalObject* globalObject)
124 Base::finishCreation(globalObject->globalData());
125 ASSERT(inherits(&s_info));
128 bool ArrayPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
130 return getStaticFunctionSlot<JSArray>(exec, ExecState::arrayPrototypeTable(exec), jsCast<ArrayPrototype*>(cell), propertyName, slot);
133 bool ArrayPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
135 return getStaticFunctionDescriptor<JSArray>(exec, ExecState::arrayPrototypeTable(exec), jsCast<ArrayPrototype*>(object), propertyName, descriptor);
138 // ------------------------------ Array Functions ----------------------------
141 static JSValue getProperty(ExecState* exec, JSObject* obj, unsigned index)
143 PropertySlot slot(obj);
144 if (!obj->getPropertySlot(exec, index, slot))
146 return slot.getValue(exec, index);
149 static void putProperty(ExecState* exec, JSObject* obj, const Identifier& propertyName, JSValue value)
151 PutPropertySlot slot;
152 obj->methodTable()->put(obj, exec, propertyName, value, slot);
155 static unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
157 JSValue value = exec->argument(argument);
158 if (value.isUndefined())
159 return undefinedValue;
161 double indexDouble = value.toInteger(exec);
162 if (indexDouble < 0) {
163 indexDouble += length;
164 return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
166 return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
169 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
171 JSValue thisValue = exec->hostThisValue();
173 bool isRealArray = isJSArray(&exec->globalData(), thisValue);
174 if (!isRealArray && !thisValue.inherits(&JSArray::s_info))
175 return throwVMTypeError(exec);
176 JSArray* thisObj = asArray(thisValue);
178 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
179 if (exec->hadException())
180 return JSValue::encode(jsUndefined());
182 StringRecursionChecker checker(exec, thisObj);
183 if (JSValue earlyReturnValue = checker.earlyReturnValue())
184 return JSValue::encode(earlyReturnValue);
186 unsigned totalSize = length ? length - 1 : 0;
187 Vector<RefPtr<StringImpl>, 256> strBuffer(length);
188 bool allStrings8Bit = true;
190 for (unsigned k = 0; k < length; k++) {
192 if (isRealArray && thisObj->canGetIndex(k))
193 element = thisObj->getIndex(k);
195 element = thisObj->get(exec, k);
197 if (element.isUndefinedOrNull())
200 UString str = element.toString(exec);
201 strBuffer[k] = str.impl();
202 totalSize += str.length();
203 allStrings8Bit = allStrings8Bit && str.is8Bit();
205 if (!strBuffer.data()) {
206 throwOutOfMemoryError(exec);
209 if (exec->hadException())
213 return JSValue::encode(jsEmptyString(exec));
215 if (allStrings8Bit) {
216 Vector<LChar> buffer;
217 buffer.reserveCapacity(totalSize);
219 return JSValue::encode(throwOutOfMemoryError(exec));
221 for (unsigned i = 0; i < length; i++) {
224 if (RefPtr<StringImpl> rep = strBuffer[i])
225 buffer.append(rep->characters8(), rep->length());
227 ASSERT(buffer.size() == totalSize);
228 return JSValue::encode(jsString(exec, UString::adopt(buffer)));
231 Vector<UChar> buffer;
232 buffer.reserveCapacity(totalSize);
234 return JSValue::encode(throwOutOfMemoryError(exec));
236 for (unsigned i = 0; i < length; i++) {
239 if (RefPtr<StringImpl> rep = strBuffer[i])
240 buffer.append(rep->characters(), rep->length());
242 ASSERT(buffer.size() == totalSize);
243 return JSValue::encode(jsString(exec, UString::adopt(buffer)));
246 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
248 JSValue thisValue = exec->hostThisValue();
250 if (!thisValue.inherits(&JSArray::s_info))
251 return throwVMTypeError(exec);
252 JSObject* thisObj = asArray(thisValue);
254 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
255 if (exec->hadException())
256 return JSValue::encode(jsUndefined());
258 StringRecursionChecker checker(exec, thisObj);
259 if (JSValue earlyReturnValue = checker.earlyReturnValue())
260 return JSValue::encode(earlyReturnValue);
262 JSStringBuilder strBuffer;
263 for (unsigned k = 0; k < length; k++) {
265 strBuffer.append(',');
267 JSValue element = thisObj->get(exec, k);
268 if (!element.isUndefinedOrNull()) {
269 JSObject* o = element.toObject(exec);
270 JSValue conversionFunction = o->get(exec, exec->propertyNames().toLocaleString);
273 CallType callType = getCallData(conversionFunction, callData);
274 if (callType != CallTypeNone)
275 str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toString(exec);
277 str = element.toString(exec);
278 strBuffer.append(str);
282 return JSValue::encode(strBuffer.build(exec));
285 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
287 JSObject* thisObj = exec->hostThisValue().toObject(exec);
288 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
289 if (exec->hadException())
290 return JSValue::encode(jsUndefined());
292 StringRecursionChecker checker(exec, thisObj);
293 if (JSValue earlyReturnValue = checker.earlyReturnValue())
294 return JSValue::encode(earlyReturnValue);
296 JSStringBuilder strBuffer;
299 if (!exec->argument(0).isUndefined())
300 separator = exec->argument(0).toString(exec);
303 if (isJSArray(&exec->globalData(), thisObj)) {
304 JSArray* array = asArray(thisObj);
307 if (!array->canGetIndex(k))
309 JSValue element = array->getIndex(k);
310 if (!element.isUndefinedOrNull())
311 strBuffer.append(element.toString(exec));
315 if (separator.isNull()) {
316 for (; k < length; k++) {
317 if (!array->canGetIndex(k))
319 strBuffer.append(',');
320 JSValue element = array->getIndex(k);
321 if (!element.isUndefinedOrNull())
322 strBuffer.append(element.toString(exec));
325 for (; k < length; k++) {
326 if (!array->canGetIndex(k))
328 strBuffer.append(separator);
329 JSValue element = array->getIndex(k);
330 if (!element.isUndefinedOrNull())
331 strBuffer.append(element.toString(exec));
336 for (; k < length; k++) {
338 if (separator.isNull())
339 strBuffer.append(',');
341 strBuffer.append(separator);
344 JSValue element = thisObj->get(exec, k);
345 if (!element.isUndefinedOrNull())
346 strBuffer.append(element.toString(exec));
349 return JSValue::encode(strBuffer.build(exec));
352 EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
354 JSValue thisValue = exec->hostThisValue();
355 JSArray* arr = constructEmptyArray(exec);
357 JSValue curArg = thisValue.toObject(exec);
358 if (exec->hadException())
359 return JSValue::encode(jsUndefined());
361 size_t argCount = exec->argumentCount();
363 if (curArg.inherits(&JSArray::s_info)) {
364 unsigned length = curArg.get(exec, exec->propertyNames().length).toUInt32(exec);
365 JSObject* curObject = curArg.toObject(exec);
366 for (unsigned k = 0; k < length; ++k) {
367 JSValue v = getProperty(exec, curObject, k);
368 if (exec->hadException())
369 return JSValue::encode(jsUndefined());
371 arr->methodTable()->putByIndex(arr, exec, n, v);
375 arr->methodTable()->putByIndex(arr, exec, n, curArg);
380 curArg = (exec->argument(i));
384 return JSValue::encode(arr);
387 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
389 JSValue thisValue = exec->hostThisValue();
391 if (isJSArray(&exec->globalData(), thisValue))
392 return JSValue::encode(asArray(thisValue)->pop());
394 JSObject* thisObj = thisValue.toObject(exec);
395 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
396 if (exec->hadException())
397 return JSValue::encode(jsUndefined());
401 putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length));
402 result = jsUndefined();
404 result = thisObj->get(exec, length - 1);
405 thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, length - 1);
406 putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1));
408 return JSValue::encode(result);
411 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec)
413 JSValue thisValue = exec->hostThisValue();
415 if (isJSArray(&exec->globalData(), thisValue) && exec->argumentCount() == 1) {
416 JSArray* array = asArray(thisValue);
417 array->push(exec, exec->argument(0));
418 return JSValue::encode(jsNumber(array->length()));
421 JSObject* thisObj = thisValue.toObject(exec);
422 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
423 if (exec->hadException())
424 return JSValue::encode(jsUndefined());
426 for (unsigned n = 0; n < exec->argumentCount(); n++) {
427 // Check for integer overflow; where safe we can do a fast put by index.
428 if (length + n >= length)
429 thisObj->methodTable()->putByIndex(thisObj, exec, length + n, exec->argument(n));
431 PutPropertySlot slot;
432 Identifier propertyName(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toString(exec));
433 thisObj->methodTable()->put(thisObj, exec, propertyName, exec->argument(n), slot);
436 JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount()));
437 putProperty(exec, thisObj, exec->propertyNames().length, newLength);
438 return JSValue::encode(newLength);
441 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
443 JSObject* thisObj = exec->hostThisValue().toObject(exec);
444 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
445 if (exec->hadException())
446 return JSValue::encode(jsUndefined());
448 unsigned middle = length / 2;
449 for (unsigned k = 0; k < middle; k++) {
450 unsigned lk1 = length - k - 1;
451 JSValue obj2 = getProperty(exec, thisObj, lk1);
452 if (exec->hadException())
453 return JSValue::encode(jsUndefined());
454 JSValue obj = getProperty(exec, thisObj, k);
455 if (exec->hadException())
456 return JSValue::encode(jsUndefined());
459 thisObj->methodTable()->putByIndex(thisObj, exec, k, obj2);
461 thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k);
464 thisObj->methodTable()->putByIndex(thisObj, exec, lk1, obj);
466 thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, lk1);
468 return JSValue::encode(thisObj);
471 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
473 JSObject* thisObj = exec->hostThisValue().toObject(exec);
474 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
475 if (exec->hadException())
476 return JSValue::encode(jsUndefined());
480 putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length));
481 result = jsUndefined();
483 result = thisObj->get(exec, 0);
484 if (isJSArray(&exec->globalData(), thisObj))
485 ((JSArray *)thisObj)->shiftCount(exec, 1);
487 for (unsigned k = 1; k < length; k++) {
488 JSValue obj = getProperty(exec, thisObj, k);
489 if (exec->hadException())
490 return JSValue::encode(jsUndefined());
492 thisObj->methodTable()->putByIndex(thisObj, exec, k - 1, obj);
494 thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k - 1);
496 thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, length - 1);
498 putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1));
500 return JSValue::encode(result);
503 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
505 // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
506 JSObject* thisObj = exec->hostThisValue().toObject(exec);
507 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
508 if (exec->hadException())
509 return JSValue::encode(jsUndefined());
511 // We return a new array
512 JSArray* resObj = constructEmptyArray(exec);
513 JSValue result = resObj;
515 unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
516 unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
519 for (unsigned k = begin; k < end; k++, n++) {
520 JSValue v = getProperty(exec, thisObj, k);
521 if (exec->hadException())
522 return JSValue::encode(jsUndefined());
524 resObj->methodTable()->putByIndex(resObj, exec, n, v);
526 resObj->setLength(n);
527 return JSValue::encode(result);
530 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec)
532 JSObject* thisObj = exec->hostThisValue().toObject(exec);
533 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
534 if (!length || exec->hadException())
535 return JSValue::encode(thisObj);
537 JSValue function = exec->argument(0);
539 CallType callType = getCallData(function, callData);
541 if (thisObj->classInfo() == &JSArray::s_info) {
542 if (isNumericCompareFunction(exec, callType, callData))
543 asArray(thisObj)->sortNumeric(exec, function, callType, callData);
544 else if (callType != CallTypeNone)
545 asArray(thisObj)->sort(exec, function, callType, callData);
547 asArray(thisObj)->sort(exec);
548 return JSValue::encode(thisObj);
551 // "Min" sort. Not the fastest, but definitely less code than heapsort
552 // or quicksort, and much less swapping than bubblesort/insertionsort.
553 for (unsigned i = 0; i < length - 1; ++i) {
554 JSValue iObj = thisObj->get(exec, i);
555 if (exec->hadException())
556 return JSValue::encode(jsUndefined());
558 JSValue minObj = iObj;
559 for (unsigned j = i + 1; j < length; ++j) {
560 JSValue jObj = thisObj->get(exec, j);
561 if (exec->hadException())
562 return JSValue::encode(jsUndefined());
563 double compareResult;
564 if (jObj.isUndefined())
565 compareResult = 1; // don't check minObj because there's no need to differentiate == (0) from > (1)
566 else if (minObj.isUndefined())
568 else if (callType != CallTypeNone) {
569 MarkedArgumentBuffer l;
572 compareResult = call(exec, function, callType, callData, jsUndefined(), l).toNumber(exec);
574 compareResult = (jObj.toString(exec) < minObj.toString(exec)) ? -1 : 1;
576 if (compareResult < 0) {
583 thisObj->methodTable()->putByIndex(thisObj, exec, i, minObj);
584 thisObj->methodTable()->putByIndex(thisObj, exec, themin, iObj);
587 return JSValue::encode(thisObj);
590 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
594 JSObject* thisObj = exec->hostThisValue().toObject(exec);
595 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
596 if (exec->hadException())
597 return JSValue::encode(jsUndefined());
599 if (!exec->argumentCount())
600 return JSValue::encode(constructEmptyArray(exec));
602 unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
604 unsigned deleteCount = length - begin;
605 if (exec->argumentCount() > 1) {
606 double deleteDouble = exec->argument(1).toInteger(exec);
607 if (deleteDouble < 0)
609 else if (deleteDouble > length - begin)
610 deleteCount = length - begin;
612 deleteCount = static_cast<unsigned>(deleteDouble);
615 JSArray* resObj = JSArray::create(exec->globalData(), exec->lexicalGlobalObject()->arrayStructure(), deleteCount, CreateCompact);
616 JSValue result = resObj;
617 JSGlobalData& globalData = exec->globalData();
618 for (unsigned k = 0; k < deleteCount; k++) {
619 JSValue v = getProperty(exec, thisObj, k + begin);
620 if (exec->hadException())
621 return JSValue::encode(jsUndefined());
622 resObj->uncheckedSetIndex(globalData, k, v);
625 resObj->setLength(deleteCount);
627 unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0);
628 if (additionalArgs != deleteCount) {
629 if (additionalArgs < deleteCount) {
630 if ((!begin) && (isJSArray(&exec->globalData(), thisObj)))
631 ((JSArray *)thisObj)->shiftCount(exec, deleteCount - additionalArgs);
633 for (unsigned k = begin; k < length - deleteCount; ++k) {
634 JSValue v = getProperty(exec, thisObj, k + deleteCount);
635 if (exec->hadException())
636 return JSValue::encode(jsUndefined());
638 thisObj->methodTable()->putByIndex(thisObj, exec, k + additionalArgs, v);
640 thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k + additionalArgs);
642 for (unsigned k = length; k > length - deleteCount + additionalArgs; --k)
643 thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k - 1);
646 if ((!begin) && (isJSArray(&exec->globalData(), thisObj)))
647 ((JSArray *)thisObj)->unshiftCount(exec, additionalArgs - deleteCount);
649 for (unsigned k = length - deleteCount; k > begin; --k) {
650 JSValue obj = getProperty(exec, thisObj, k + deleteCount - 1);
651 if (exec->hadException())
652 return JSValue::encode(jsUndefined());
654 thisObj->methodTable()->putByIndex(thisObj, exec, k + additionalArgs - 1, obj);
656 thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k + additionalArgs - 1);
661 for (unsigned k = 0; k < additionalArgs; ++k)
662 thisObj->methodTable()->putByIndex(thisObj, exec, k + begin, exec->argument(k + 2));
664 putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs));
665 return JSValue::encode(result);
668 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
672 JSObject* thisObj = exec->hostThisValue().toObject(exec);
673 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
674 if (exec->hadException())
675 return JSValue::encode(jsUndefined());
677 unsigned nrArgs = exec->argumentCount();
678 if ((nrArgs) && (length)) {
679 if (isJSArray(&exec->globalData(), thisObj))
680 ((JSArray *)thisObj)->unshiftCount(exec, nrArgs);
682 for (unsigned k = length; k > 0; --k) {
683 JSValue v = getProperty(exec, thisObj, k - 1);
684 if (exec->hadException())
685 return JSValue::encode(jsUndefined());
687 thisObj->methodTable()->putByIndex(thisObj, exec, k + nrArgs - 1, v);
689 thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k + nrArgs - 1);
693 for (unsigned k = 0; k < nrArgs; ++k)
694 thisObj->methodTable()->putByIndex(thisObj, exec, k, exec->argument(k));
695 JSValue result = jsNumber(length + nrArgs);
696 putProperty(exec, thisObj, exec->propertyNames().length, result);
697 return JSValue::encode(result);
700 EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec)
702 JSObject* thisObj = exec->hostThisValue().toObject(exec);
703 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
704 if (exec->hadException())
705 return JSValue::encode(jsUndefined());
707 JSValue function = exec->argument(0);
709 CallType callType = getCallData(function, callData);
710 if (callType == CallTypeNone)
711 return throwVMTypeError(exec);
713 JSValue applyThis = exec->argument(1);
714 JSArray* resultArray = constructEmptyArray(exec);
716 unsigned filterIndex = 0;
718 if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
719 JSFunction* f = asFunction(function);
720 JSArray* array = asArray(thisObj);
721 CachedCall cachedCall(exec, f, 3);
722 for (; k < length && !exec->hadException(); ++k) {
723 if (!array->canGetIndex(k))
725 JSValue v = array->getIndex(k);
726 cachedCall.setThis(applyThis);
727 cachedCall.setArgument(0, v);
728 cachedCall.setArgument(1, jsNumber(k));
729 cachedCall.setArgument(2, thisObj);
731 JSValue result = cachedCall.call();
732 if (result.toBoolean(exec))
733 resultArray->methodTable()->putByIndex(resultArray, exec, filterIndex++, v);
736 return JSValue::encode(resultArray);
738 for (; k < length && !exec->hadException(); ++k) {
739 PropertySlot slot(thisObj);
740 if (!thisObj->getPropertySlot(exec, k, slot))
742 JSValue v = slot.getValue(exec, k);
744 if (exec->hadException())
745 return JSValue::encode(jsUndefined());
747 MarkedArgumentBuffer eachArguments;
748 eachArguments.append(v);
749 eachArguments.append(jsNumber(k));
750 eachArguments.append(thisObj);
752 JSValue result = call(exec, function, callType, callData, applyThis, eachArguments);
753 if (result.toBoolean(exec))
754 resultArray->methodTable()->putByIndex(resultArray, exec, filterIndex++, v);
756 return JSValue::encode(resultArray);
759 EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec)
761 JSObject* thisObj = exec->hostThisValue().toObject(exec);
762 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
763 if (exec->hadException())
764 return JSValue::encode(jsUndefined());
766 JSValue function = exec->argument(0);
768 CallType callType = getCallData(function, callData);
769 if (callType == CallTypeNone)
770 return throwVMTypeError(exec);
772 JSValue applyThis = exec->argument(1);
774 JSArray* resultArray = constructEmptyArray(exec, length);
776 if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
777 JSFunction* f = asFunction(function);
778 JSArray* array = asArray(thisObj);
779 CachedCall cachedCall(exec, f, 3);
780 for (; k < length && !exec->hadException(); ++k) {
781 if (UNLIKELY(!array->canGetIndex(k)))
784 cachedCall.setThis(applyThis);
785 cachedCall.setArgument(0, array->getIndex(k));
786 cachedCall.setArgument(1, jsNumber(k));
787 cachedCall.setArgument(2, thisObj);
789 JSArray::putByIndex(resultArray, exec, k, cachedCall.call());
792 for (; k < length && !exec->hadException(); ++k) {
793 PropertySlot slot(thisObj);
794 if (!thisObj->getPropertySlot(exec, k, slot))
796 JSValue v = slot.getValue(exec, k);
798 if (exec->hadException())
799 return JSValue::encode(jsUndefined());
801 MarkedArgumentBuffer eachArguments;
802 eachArguments.append(v);
803 eachArguments.append(jsNumber(k));
804 eachArguments.append(thisObj);
806 if (exec->hadException())
807 return JSValue::encode(jsUndefined());
809 JSValue result = call(exec, function, callType, callData, applyThis, eachArguments);
810 resultArray->methodTable()->putByIndex(resultArray, exec, k, result);
813 return JSValue::encode(resultArray);
816 // Documentation for these three is available at:
817 // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every
818 // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach
819 // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some
821 EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec)
823 JSObject* thisObj = exec->hostThisValue().toObject(exec);
824 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
825 if (exec->hadException())
826 return JSValue::encode(jsUndefined());
828 JSValue function = exec->argument(0);
830 CallType callType = getCallData(function, callData);
831 if (callType == CallTypeNone)
832 return throwVMTypeError(exec);
834 JSValue applyThis = exec->argument(1);
836 JSValue result = jsBoolean(true);
839 if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
840 JSFunction* f = asFunction(function);
841 JSArray* array = asArray(thisObj);
842 CachedCall cachedCall(exec, f, 3);
843 for (; k < length && !exec->hadException(); ++k) {
844 if (UNLIKELY(!array->canGetIndex(k)))
847 cachedCall.setThis(applyThis);
848 cachedCall.setArgument(0, array->getIndex(k));
849 cachedCall.setArgument(1, jsNumber(k));
850 cachedCall.setArgument(2, thisObj);
851 JSValue result = cachedCall.call();
852 if (!result.toBoolean(cachedCall.newCallFrame(exec)))
853 return JSValue::encode(jsBoolean(false));
856 for (; k < length && !exec->hadException(); ++k) {
857 PropertySlot slot(thisObj);
858 if (!thisObj->getPropertySlot(exec, k, slot))
861 MarkedArgumentBuffer eachArguments;
862 eachArguments.append(slot.getValue(exec, k));
863 eachArguments.append(jsNumber(k));
864 eachArguments.append(thisObj);
866 if (exec->hadException())
867 return JSValue::encode(jsUndefined());
869 bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec);
870 if (!predicateResult) {
871 result = jsBoolean(false);
876 return JSValue::encode(result);
879 EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec)
881 JSObject* thisObj = exec->hostThisValue().toObject(exec);
882 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
883 if (exec->hadException())
884 return JSValue::encode(jsUndefined());
886 JSValue function = exec->argument(0);
888 CallType callType = getCallData(function, callData);
889 if (callType == CallTypeNone)
890 return throwVMTypeError(exec);
892 JSValue applyThis = exec->argument(1);
895 if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
896 JSFunction* f = asFunction(function);
897 JSArray* array = asArray(thisObj);
898 CachedCall cachedCall(exec, f, 3);
899 for (; k < length && !exec->hadException(); ++k) {
900 if (UNLIKELY(!array->canGetIndex(k)))
903 cachedCall.setThis(applyThis);
904 cachedCall.setArgument(0, array->getIndex(k));
905 cachedCall.setArgument(1, jsNumber(k));
906 cachedCall.setArgument(2, thisObj);
911 for (; k < length && !exec->hadException(); ++k) {
912 PropertySlot slot(thisObj);
913 if (!thisObj->getPropertySlot(exec, k, slot))
916 MarkedArgumentBuffer eachArguments;
917 eachArguments.append(slot.getValue(exec, k));
918 eachArguments.append(jsNumber(k));
919 eachArguments.append(thisObj);
921 if (exec->hadException())
922 return JSValue::encode(jsUndefined());
924 call(exec, function, callType, callData, applyThis, eachArguments);
926 return JSValue::encode(jsUndefined());
929 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec)
931 JSObject* thisObj = exec->hostThisValue().toObject(exec);
932 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
933 if (exec->hadException())
934 return JSValue::encode(jsUndefined());
936 JSValue function = exec->argument(0);
938 CallType callType = getCallData(function, callData);
939 if (callType == CallTypeNone)
940 return throwVMTypeError(exec);
942 JSValue applyThis = exec->argument(1);
944 JSValue result = jsBoolean(false);
947 if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
948 JSFunction* f = asFunction(function);
949 JSArray* array = asArray(thisObj);
950 CachedCall cachedCall(exec, f, 3);
951 for (; k < length && !exec->hadException(); ++k) {
952 if (UNLIKELY(!array->canGetIndex(k)))
955 cachedCall.setThis(applyThis);
956 cachedCall.setArgument(0, array->getIndex(k));
957 cachedCall.setArgument(1, jsNumber(k));
958 cachedCall.setArgument(2, thisObj);
959 JSValue result = cachedCall.call();
960 if (result.toBoolean(cachedCall.newCallFrame(exec)))
961 return JSValue::encode(jsBoolean(true));
964 for (; k < length && !exec->hadException(); ++k) {
965 PropertySlot slot(thisObj);
966 if (!thisObj->getPropertySlot(exec, k, slot))
969 MarkedArgumentBuffer eachArguments;
970 eachArguments.append(slot.getValue(exec, k));
971 eachArguments.append(jsNumber(k));
972 eachArguments.append(thisObj);
974 if (exec->hadException())
975 return JSValue::encode(jsUndefined());
977 bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec);
978 if (predicateResult) {
979 result = jsBoolean(true);
983 return JSValue::encode(result);
986 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec)
988 JSObject* thisObj = exec->hostThisValue().toObject(exec);
989 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
990 if (exec->hadException())
991 return JSValue::encode(jsUndefined());
993 JSValue function = exec->argument(0);
995 CallType callType = getCallData(function, callData);
996 if (callType == CallTypeNone)
997 return throwVMTypeError(exec);
1001 if (!length && exec->argumentCount() == 1)
1002 return throwVMTypeError(exec);
1005 if (isJSArray(&exec->globalData(), thisObj))
1006 array = asArray(thisObj);
1008 if (exec->argumentCount() >= 2)
1009 rv = exec->argument(1);
1010 else if (array && array->canGetIndex(0)){
1011 rv = array->getIndex(0);
1014 for (i = 0; i < length; i++) {
1015 rv = getProperty(exec, thisObj, i);
1016 if (exec->hadException())
1017 return JSValue::encode(jsUndefined());
1022 return throwVMTypeError(exec);
1026 if (callType == CallTypeJS && array) {
1027 CachedCall cachedCall(exec, asFunction(function), 4);
1028 for (; i < length && !exec->hadException(); ++i) {
1029 cachedCall.setThis(jsUndefined());
1030 cachedCall.setArgument(0, rv);
1032 if (LIKELY(array->canGetIndex(i)))
1033 v = array->getIndex(i);
1035 break; // length has been made unsafe while we enumerate fallback to slow path
1036 cachedCall.setArgument(1, v);
1037 cachedCall.setArgument(2, jsNumber(i));
1038 cachedCall.setArgument(3, array);
1039 rv = cachedCall.call();
1041 if (i == length) // only return if we reached the end of the array
1042 return JSValue::encode(rv);
1045 for (; i < length && !exec->hadException(); ++i) {
1046 JSValue prop = getProperty(exec, thisObj, i);
1047 if (exec->hadException())
1048 return JSValue::encode(jsUndefined());
1052 MarkedArgumentBuffer eachArguments;
1053 eachArguments.append(rv);
1054 eachArguments.append(prop);
1055 eachArguments.append(jsNumber(i));
1056 eachArguments.append(thisObj);
1058 rv = call(exec, function, callType, callData, jsUndefined(), eachArguments);
1060 return JSValue::encode(rv);
1063 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec)
1065 JSObject* thisObj = exec->hostThisValue().toObject(exec);
1066 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
1067 if (exec->hadException())
1068 return JSValue::encode(jsUndefined());
1070 JSValue function = exec->argument(0);
1072 CallType callType = getCallData(function, callData);
1073 if (callType == CallTypeNone)
1074 return throwVMTypeError(exec);
1078 if (!length && exec->argumentCount() == 1)
1079 return throwVMTypeError(exec);
1082 if (isJSArray(&exec->globalData(), thisObj))
1083 array = asArray(thisObj);
1085 if (exec->argumentCount() >= 2)
1086 rv = exec->argument(1);
1087 else if (array && array->canGetIndex(length - 1)){
1088 rv = array->getIndex(length - 1);
1091 for (i = 0; i < length; i++) {
1092 rv = getProperty(exec, thisObj, length - i - 1);
1093 if (exec->hadException())
1094 return JSValue::encode(jsUndefined());
1099 return throwVMTypeError(exec);
1103 if (callType == CallTypeJS && array) {
1104 CachedCall cachedCall(exec, asFunction(function), 4);
1105 for (; i < length && !exec->hadException(); ++i) {
1106 unsigned idx = length - i - 1;
1107 cachedCall.setThis(jsUndefined());
1108 cachedCall.setArgument(0, rv);
1109 if (UNLIKELY(!array->canGetIndex(idx)))
1110 break; // length has been made unsafe while we enumerate fallback to slow path
1111 cachedCall.setArgument(1, array->getIndex(idx));
1112 cachedCall.setArgument(2, jsNumber(idx));
1113 cachedCall.setArgument(3, array);
1114 rv = cachedCall.call();
1116 if (i == length) // only return if we reached the end of the array
1117 return JSValue::encode(rv);
1120 for (; i < length && !exec->hadException(); ++i) {
1121 unsigned idx = length - i - 1;
1122 JSValue prop = getProperty(exec, thisObj, idx);
1123 if (exec->hadException())
1124 return JSValue::encode(jsUndefined());
1128 MarkedArgumentBuffer eachArguments;
1129 eachArguments.append(rv);
1130 eachArguments.append(prop);
1131 eachArguments.append(jsNumber(idx));
1132 eachArguments.append(thisObj);
1134 rv = call(exec, function, callType, callData, jsUndefined(), eachArguments);
1136 return JSValue::encode(rv);
1139 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
1142 JSObject* thisObj = exec->hostThisValue().toObject(exec);
1143 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
1144 if (exec->hadException())
1145 return JSValue::encode(jsUndefined());
1147 unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
1148 JSValue searchElement = exec->argument(0);
1149 for (; index < length; ++index) {
1150 JSValue e = getProperty(exec, thisObj, index);
1151 if (exec->hadException())
1152 return JSValue::encode(jsUndefined());
1155 if (JSValue::strictEqual(exec, searchElement, e))
1156 return JSValue::encode(jsNumber(index));
1159 return JSValue::encode(jsNumber(-1));
1162 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
1165 JSObject* thisObj = exec->hostThisValue().toObject(exec);
1166 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
1168 return JSValue::encode(jsNumber(-1));
1170 unsigned index = length - 1;
1171 JSValue fromValue = exec->argument(1);
1172 if (!fromValue.isUndefined()) {
1173 double fromDouble = fromValue.toInteger(exec);
1174 if (fromDouble < 0) {
1175 fromDouble += length;
1177 return JSValue::encode(jsNumber(-1));
1179 if (fromDouble < length)
1180 index = static_cast<unsigned>(fromDouble);
1183 JSValue searchElement = exec->argument(0);
1185 ASSERT(index < length);
1186 JSValue e = getProperty(exec, thisObj, index);
1187 if (exec->hadException())
1188 return JSValue::encode(jsUndefined());
1191 if (JSValue::strictEqual(exec, searchElement, e))
1192 return JSValue::encode(jsNumber(index));
1195 return JSValue::encode(jsNumber(-1));