2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007, 2008, 2009 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"
36 #include <wtf/Assertions.h>
37 #include <wtf/HashSet.h>
41 ASSERT_CLASS_FITS_IN_CELL(ArrayPrototype);
43 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);
44 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*);
45 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*);
46 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*);
47 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*);
48 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*);
49 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*);
50 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*);
51 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*);
52 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState*);
53 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*);
54 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*);
55 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState*);
56 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState*);
57 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState*);
58 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*);
59 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState*);
60 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState*);
61 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState*);
62 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState*);
63 static EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*);
67 #include "ArrayPrototype.lut.h"
71 static inline bool isNumericCompareFunction(ExecState* exec, CallType callType, const CallData& callData)
73 if (callType != CallTypeJS)
76 FunctionExecutable* executable = callData.js.functionExecutable;
78 JSObject* error = executable->compileForCall(exec, callData.js.scopeChain);
82 return executable->generatedBytecodeForCall().isNumericCompareFunction();
85 // ------------------------------ ArrayPrototype ----------------------------
87 const ClassInfo ArrayPrototype::info = {"Array", &JSArray::info, 0, ExecState::arrayTable};
89 /* Source for ArrayPrototype.lut.h
91 toString arrayProtoFuncToString DontEnum|Function 0
92 toLocaleString arrayProtoFuncToLocaleString DontEnum|Function 0
93 concat arrayProtoFuncConcat DontEnum|Function 1
94 join arrayProtoFuncJoin DontEnum|Function 1
95 pop arrayProtoFuncPop DontEnum|Function 0
96 push arrayProtoFuncPush DontEnum|Function 1
97 reverse arrayProtoFuncReverse DontEnum|Function 0
98 shift arrayProtoFuncShift DontEnum|Function 0
99 slice arrayProtoFuncSlice DontEnum|Function 2
100 sort arrayProtoFuncSort DontEnum|Function 1
101 splice arrayProtoFuncSplice DontEnum|Function 2
102 unshift arrayProtoFuncUnShift DontEnum|Function 1
103 every arrayProtoFuncEvery DontEnum|Function 1
104 forEach arrayProtoFuncForEach DontEnum|Function 1
105 some arrayProtoFuncSome DontEnum|Function 1
106 indexOf arrayProtoFuncIndexOf DontEnum|Function 1
107 lastIndexOf arrayProtoFuncLastIndexOf DontEnum|Function 1
108 filter arrayProtoFuncFilter DontEnum|Function 1
109 reduce arrayProtoFuncReduce DontEnum|Function 1
110 reduceRight arrayProtoFuncReduceRight DontEnum|Function 1
111 map arrayProtoFuncMap DontEnum|Function 1
116 ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure)
119 putAnonymousValue(0, globalObject);
122 bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
124 return getStaticFunctionSlot<JSArray>(exec, ExecState::arrayTable(exec), this, propertyName, slot);
127 bool ArrayPrototype::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
129 return getStaticFunctionDescriptor<JSArray>(exec, ExecState::arrayTable(exec), this, propertyName, descriptor);
132 // ------------------------------ Array Functions ----------------------------
135 static JSValue getProperty(ExecState* exec, JSObject* obj, unsigned index)
137 PropertySlot slot(obj);
138 if (!obj->getPropertySlot(exec, index, slot))
140 return slot.getValue(exec, index);
143 static void putProperty(ExecState* exec, JSObject* obj, const Identifier& propertyName, JSValue value)
145 PutPropertySlot slot;
146 obj->put(exec, propertyName, value, slot);
149 static unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
151 JSValue value = exec->argument(argument);
152 if (value.isUndefined())
153 return undefinedValue;
155 double indexDouble = value.toInteger(exec);
156 if (indexDouble < 0) {
157 indexDouble += length;
158 return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
160 return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
163 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
165 JSValue thisValue = exec->hostThisValue();
166 bool isRealArray = isJSArray(&exec->globalData(), thisValue);
167 if (!isRealArray && !thisValue.inherits(&JSArray::info))
168 return throwVMTypeError(exec);
169 JSArray* thisObj = asArray(thisValue);
171 HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
172 if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) {
173 if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth)
174 return throwVMError(exec, createStackOverflowError(exec));
177 bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
179 return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoiding infinite recursion.
181 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
182 unsigned totalSize = length ? length - 1 : 0;
184 // Symbian has very limited stack size available.
185 // This function could be called recursively and allocating 1K on stack here cause
186 // stack overflow on Symbian devices.
187 Vector<RefPtr<StringImpl> > strBuffer(length);
189 Vector<RefPtr<StringImpl>, 256> strBuffer(length);
191 for (unsigned k = 0; k < length; k++) {
193 if (isRealArray && thisObj->canGetIndex(k))
194 element = thisObj->getIndex(k);
196 element = thisObj->get(exec, k);
198 if (element.isUndefinedOrNull())
201 UString str = element.toString(exec);
202 strBuffer[k] = str.impl();
203 totalSize += str.length();
205 if (!strBuffer.data()) {
206 throwOutOfMemoryError(exec);
209 if (exec->hadException())
212 arrayVisitedElements.remove(thisObj);
214 return JSValue::encode(jsEmptyString(exec));
215 Vector<UChar> buffer;
216 buffer.reserveCapacity(totalSize);
218 return JSValue::encode(throwOutOfMemoryError(exec));
220 for (unsigned i = 0; i < length; i++) {
223 if (RefPtr<StringImpl> rep = strBuffer[i])
224 buffer.append(rep->characters(), rep->length());
226 ASSERT(buffer.size() == totalSize);
227 return JSValue::encode(jsString(exec, UString::adopt(buffer)));
230 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
232 JSValue thisValue = exec->hostThisValue();
233 if (!thisValue.inherits(&JSArray::info))
234 return throwVMTypeError(exec);
235 JSObject* thisObj = asArray(thisValue);
237 HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
238 if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) {
239 if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth)
240 return throwVMError(exec, createStackOverflowError(exec));
243 bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
245 return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoding infinite recursion.
247 JSStringBuilder strBuffer;
248 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
249 for (unsigned k = 0; k < length; k++) {
251 strBuffer.append(',');
253 JSValue element = thisObj->get(exec, k);
254 if (!element.isUndefinedOrNull()) {
255 JSObject* o = element.toObject(exec);
256 JSValue conversionFunction = o->get(exec, exec->propertyNames().toLocaleString);
259 CallType callType = getCallData(conversionFunction, callData);
260 if (callType != CallTypeNone)
261 str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toString(exec);
263 str = element.toString(exec);
264 strBuffer.append(str);
267 arrayVisitedElements.remove(thisObj);
268 return JSValue::encode(strBuffer.build(exec));
271 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
273 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
275 HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
276 if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) {
277 if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth)
278 return throwVMError(exec, createStackOverflowError(exec));
281 bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
283 return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoding infinite recursion.
285 JSStringBuilder strBuffer;
288 if (!exec->argument(0).isUndefined())
289 separator = exec->argument(0).toString(exec);
291 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
293 if (isJSArray(&exec->globalData(), thisObj)) {
294 JSArray* array = asArray(thisObj);
297 if (!array->canGetIndex(k))
299 JSValue element = array->getIndex(k);
300 if (!element.isUndefinedOrNull())
301 strBuffer.append(element.toString(exec));
305 if (separator.isNull()) {
306 for (; k < length; k++) {
307 if (!array->canGetIndex(k))
309 strBuffer.append(',');
310 JSValue element = array->getIndex(k);
311 if (!element.isUndefinedOrNull())
312 strBuffer.append(element.toString(exec));
315 for (; k < length; k++) {
316 if (!array->canGetIndex(k))
318 strBuffer.append(separator);
319 JSValue element = array->getIndex(k);
320 if (!element.isUndefinedOrNull())
321 strBuffer.append(element.toString(exec));
326 for (; k < length; k++) {
328 if (separator.isNull())
329 strBuffer.append(',');
331 strBuffer.append(separator);
334 JSValue element = thisObj->get(exec, k);
335 if (!element.isUndefinedOrNull())
336 strBuffer.append(element.toString(exec));
338 arrayVisitedElements.remove(thisObj);
339 return JSValue::encode(strBuffer.build(exec));
342 EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
344 JSValue thisValue = exec->hostThisValue();
345 JSArray* arr = constructEmptyArray(exec);
347 JSValue curArg = thisValue.toThisObject(exec);
349 size_t argCount = exec->argumentCount();
351 if (curArg.inherits(&JSArray::info)) {
352 unsigned length = curArg.get(exec, exec->propertyNames().length).toUInt32(exec);
353 JSObject* curObject = curArg.toObject(exec);
354 for (unsigned k = 0; k < length; ++k) {
355 if (JSValue v = getProperty(exec, curObject, k))
356 arr->put(exec, n, v);
360 arr->put(exec, n, curArg);
365 curArg = (exec->argument(i));
369 return JSValue::encode(arr);
372 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
374 JSValue thisValue = exec->hostThisValue();
375 if (isJSArray(&exec->globalData(), thisValue))
376 return JSValue::encode(asArray(thisValue)->pop());
378 JSObject* thisObj = thisValue.toThisObject(exec);
380 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
382 putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length));
383 result = jsUndefined();
385 result = thisObj->get(exec, length - 1);
386 thisObj->deleteProperty(exec, length - 1);
387 putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1));
389 return JSValue::encode(result);
392 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec)
394 JSValue thisValue = exec->hostThisValue();
395 if (isJSArray(&exec->globalData(), thisValue) && exec->argumentCount() == 1) {
396 JSArray* array = asArray(thisValue);
397 array->push(exec, exec->argument(0));
398 return JSValue::encode(jsNumber(array->length()));
401 JSObject* thisObj = thisValue.toThisObject(exec);
402 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
403 for (unsigned n = 0; n < exec->argumentCount(); n++)
404 thisObj->put(exec, length + n, exec->argument(n));
405 length += exec->argumentCount();
406 putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length));
407 return JSValue::encode(jsNumber(length));
410 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
412 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
413 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
414 unsigned middle = length / 2;
416 for (unsigned k = 0; k < middle; k++) {
417 unsigned lk1 = length - k - 1;
418 JSValue obj2 = getProperty(exec, thisObj, lk1);
419 JSValue obj = getProperty(exec, thisObj, k);
422 thisObj->put(exec, k, obj2);
424 thisObj->deleteProperty(exec, k);
427 thisObj->put(exec, lk1, obj);
429 thisObj->deleteProperty(exec, lk1);
431 return JSValue::encode(thisObj);
434 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
436 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
439 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
441 putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length));
442 result = jsUndefined();
444 result = thisObj->get(exec, 0);
445 if (isJSArray(&exec->globalData(), thisObj))
446 ((JSArray *)thisObj)->shiftCount(exec, 1);
448 for (unsigned k = 1; k < length; k++) {
449 if (JSValue obj = getProperty(exec, thisObj, k))
450 thisObj->put(exec, k - 1, obj);
452 thisObj->deleteProperty(exec, k - 1);
454 thisObj->deleteProperty(exec, length - 1);
456 putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1));
458 return JSValue::encode(result);
461 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
463 // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
464 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
466 // We return a new array
467 JSArray* resObj = constructEmptyArray(exec);
468 JSValue result = resObj;
470 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
471 unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
472 unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
475 for (unsigned k = begin; k < end; k++, n++) {
476 if (JSValue v = getProperty(exec, thisObj, k))
477 resObj->put(exec, n, v);
479 resObj->setLength(n);
480 return JSValue::encode(result);
483 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec)
485 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
487 JSValue function = exec->argument(0);
489 CallType callType = getCallData(function, callData);
491 if (thisObj->classInfo() == &JSArray::info) {
492 if (isNumericCompareFunction(exec, callType, callData))
493 asArray(thisObj)->sortNumeric(exec, function, callType, callData);
494 else if (callType != CallTypeNone)
495 asArray(thisObj)->sort(exec, function, callType, callData);
497 asArray(thisObj)->sort(exec);
498 return JSValue::encode(thisObj);
501 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
504 return JSValue::encode(thisObj);
506 // "Min" sort. Not the fastest, but definitely less code than heapsort
507 // or quicksort, and much less swapping than bubblesort/insertionsort.
508 for (unsigned i = 0; i < length - 1; ++i) {
509 JSValue iObj = thisObj->get(exec, i);
511 JSValue minObj = iObj;
512 for (unsigned j = i + 1; j < length; ++j) {
513 JSValue jObj = thisObj->get(exec, j);
514 double compareResult;
515 if (jObj.isUndefined())
516 compareResult = 1; // don't check minObj because there's no need to differentiate == (0) from > (1)
517 else if (minObj.isUndefined())
519 else if (callType != CallTypeNone) {
520 MarkedArgumentBuffer l;
523 compareResult = call(exec, function, callType, callData, exec->globalThisValue(), l).toNumber(exec);
525 compareResult = (jObj.toString(exec) < minObj.toString(exec)) ? -1 : 1;
527 if (compareResult < 0) {
534 thisObj->put(exec, i, minObj);
535 thisObj->put(exec, themin, iObj);
538 return JSValue::encode(thisObj);
541 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
543 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
547 if (!exec->argumentCount())
548 return JSValue::encode(constructEmptyArray(exec));
550 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
551 unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
553 unsigned deleteCount = length - begin;
554 if (exec->argumentCount() > 1) {
555 double deleteDouble = exec->argument(1).toInteger(exec);
556 if (deleteDouble < 0)
558 else if (deleteDouble > length - begin)
559 deleteCount = length - begin;
561 deleteCount = static_cast<unsigned>(deleteDouble);
564 JSArray* resObj = new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), deleteCount, CreateCompact);
565 JSValue result = resObj;
567 for (unsigned k = 0; k < deleteCount; k++)
568 resObj->uncheckedSetIndex(k, getProperty(exec, thisObj, k + begin));
570 resObj->setLength(deleteCount);
572 unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0);
573 if (additionalArgs != deleteCount) {
574 if (additionalArgs < deleteCount) {
575 if ((!begin) && (isJSArray(&exec->globalData(), thisObj)))
576 ((JSArray *)thisObj)->shiftCount(exec, deleteCount - additionalArgs);
578 for (unsigned k = begin; k < length - deleteCount; ++k) {
579 if (JSValue v = getProperty(exec, thisObj, k + deleteCount))
580 thisObj->put(exec, k + additionalArgs, v);
582 thisObj->deleteProperty(exec, k + additionalArgs);
584 for (unsigned k = length; k > length - deleteCount + additionalArgs; --k)
585 thisObj->deleteProperty(exec, k - 1);
588 if ((!begin) && (isJSArray(&exec->globalData(), thisObj)))
589 ((JSArray *)thisObj)->unshiftCount(exec, additionalArgs - deleteCount);
591 for (unsigned k = length - deleteCount; k > begin; --k) {
592 if (JSValue obj = getProperty(exec, thisObj, k + deleteCount - 1))
593 thisObj->put(exec, k + additionalArgs - 1, obj);
595 thisObj->deleteProperty(exec, k + additionalArgs - 1);
600 for (unsigned k = 0; k < additionalArgs; ++k)
601 thisObj->put(exec, k + begin, exec->argument(k + 2));
603 putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs));
604 return JSValue::encode(result);
607 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
609 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
612 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
613 unsigned nrArgs = exec->argumentCount();
614 if ((nrArgs) && (length)) {
615 if (isJSArray(&exec->globalData(), thisObj))
616 ((JSArray *)thisObj)->unshiftCount(exec, nrArgs);
618 for (unsigned k = length; k > 0; --k) {
619 if (JSValue v = getProperty(exec, thisObj, k - 1))
620 thisObj->put(exec, k + nrArgs - 1, v);
622 thisObj->deleteProperty(exec, k + nrArgs - 1);
626 for (unsigned k = 0; k < nrArgs; ++k)
627 thisObj->put(exec, k, exec->argument(k));
628 JSValue result = jsNumber(length + nrArgs);
629 putProperty(exec, thisObj, exec->propertyNames().length, result);
630 return JSValue::encode(result);
633 EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec)
635 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
637 JSValue function = exec->argument(0);
639 CallType callType = getCallData(function, callData);
640 if (callType == CallTypeNone)
641 return throwVMTypeError(exec);
643 JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec);
644 JSArray* resultArray = constructEmptyArray(exec);
646 unsigned filterIndex = 0;
647 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
649 if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
650 JSFunction* f = asFunction(function);
651 JSArray* array = asArray(thisObj);
652 CachedCall cachedCall(exec, f, 3);
653 for (; k < length && !exec->hadException(); ++k) {
654 if (!array->canGetIndex(k))
656 JSValue v = array->getIndex(k);
657 cachedCall.setThis(applyThis);
658 cachedCall.setArgument(0, v);
659 cachedCall.setArgument(1, jsNumber(k));
660 cachedCall.setArgument(2, thisObj);
662 JSValue result = cachedCall.call();
663 if (result.toBoolean(exec))
664 resultArray->put(exec, filterIndex++, v);
667 return JSValue::encode(resultArray);
669 for (; k < length && !exec->hadException(); ++k) {
670 PropertySlot slot(thisObj);
672 if (!thisObj->getPropertySlot(exec, k, slot))
675 JSValue v = slot.getValue(exec, k);
677 MarkedArgumentBuffer eachArguments;
679 eachArguments.append(v);
680 eachArguments.append(jsNumber(k));
681 eachArguments.append(thisObj);
683 JSValue result = call(exec, function, callType, callData, applyThis, eachArguments);
685 if (result.toBoolean(exec))
686 resultArray->put(exec, filterIndex++, v);
688 return JSValue::encode(resultArray);
691 EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec)
693 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
695 JSValue function = exec->argument(0);
697 CallType callType = getCallData(function, callData);
698 if (callType == CallTypeNone)
699 return throwVMTypeError(exec);
701 JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec);
703 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
705 JSArray* resultArray = constructEmptyArray(exec, length);
707 if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
708 JSFunction* f = asFunction(function);
709 JSArray* array = asArray(thisObj);
710 CachedCall cachedCall(exec, f, 3);
711 for (; k < length && !exec->hadException(); ++k) {
712 if (UNLIKELY(!array->canGetIndex(k)))
715 cachedCall.setThis(applyThis);
716 cachedCall.setArgument(0, array->getIndex(k));
717 cachedCall.setArgument(1, jsNumber(k));
718 cachedCall.setArgument(2, thisObj);
720 resultArray->JSArray::put(exec, k, cachedCall.call());
723 for (; k < length && !exec->hadException(); ++k) {
724 PropertySlot slot(thisObj);
725 if (!thisObj->getPropertySlot(exec, k, slot))
728 JSValue v = slot.getValue(exec, k);
730 MarkedArgumentBuffer eachArguments;
732 eachArguments.append(v);
733 eachArguments.append(jsNumber(k));
734 eachArguments.append(thisObj);
736 JSValue result = call(exec, function, callType, callData, applyThis, eachArguments);
737 resultArray->put(exec, k, result);
740 return JSValue::encode(resultArray);
743 // Documentation for these three is available at:
744 // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every
745 // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach
746 // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some
748 EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec)
750 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
752 JSValue function = exec->argument(0);
754 CallType callType = getCallData(function, callData);
755 if (callType == CallTypeNone)
756 return throwVMTypeError(exec);
758 JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec);
760 JSValue result = jsBoolean(true);
762 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
764 if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
765 JSFunction* f = asFunction(function);
766 JSArray* array = asArray(thisObj);
767 CachedCall cachedCall(exec, f, 3);
768 for (; k < length && !exec->hadException(); ++k) {
769 if (UNLIKELY(!array->canGetIndex(k)))
772 cachedCall.setThis(applyThis);
773 cachedCall.setArgument(0, array->getIndex(k));
774 cachedCall.setArgument(1, jsNumber(k));
775 cachedCall.setArgument(2, thisObj);
776 JSValue result = cachedCall.call();
777 if (!result.toBoolean(cachedCall.newCallFrame(exec)))
778 return JSValue::encode(jsBoolean(false));
781 for (; k < length && !exec->hadException(); ++k) {
782 PropertySlot slot(thisObj);
784 if (!thisObj->getPropertySlot(exec, k, slot))
787 MarkedArgumentBuffer eachArguments;
789 eachArguments.append(slot.getValue(exec, k));
790 eachArguments.append(jsNumber(k));
791 eachArguments.append(thisObj);
793 bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec);
795 if (!predicateResult) {
796 result = jsBoolean(false);
801 return JSValue::encode(result);
804 EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec)
806 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
808 JSValue function = exec->argument(0);
810 CallType callType = getCallData(function, callData);
811 if (callType == CallTypeNone)
812 return throwVMTypeError(exec);
814 JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec);
816 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
818 if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
819 JSFunction* f = asFunction(function);
820 JSArray* array = asArray(thisObj);
821 CachedCall cachedCall(exec, f, 3);
822 for (; k < length && !exec->hadException(); ++k) {
823 if (UNLIKELY(!array->canGetIndex(k)))
826 cachedCall.setThis(applyThis);
827 cachedCall.setArgument(0, array->getIndex(k));
828 cachedCall.setArgument(1, jsNumber(k));
829 cachedCall.setArgument(2, thisObj);
834 for (; k < length && !exec->hadException(); ++k) {
835 PropertySlot slot(thisObj);
836 if (!thisObj->getPropertySlot(exec, k, slot))
839 MarkedArgumentBuffer eachArguments;
840 eachArguments.append(slot.getValue(exec, k));
841 eachArguments.append(jsNumber(k));
842 eachArguments.append(thisObj);
844 call(exec, function, callType, callData, applyThis, eachArguments);
846 return JSValue::encode(jsUndefined());
849 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec)
851 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
853 JSValue function = exec->argument(0);
855 CallType callType = getCallData(function, callData);
856 if (callType == CallTypeNone)
857 return throwVMTypeError(exec);
859 JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec);
861 JSValue result = jsBoolean(false);
863 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
865 if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
866 JSFunction* f = asFunction(function);
867 JSArray* array = asArray(thisObj);
868 CachedCall cachedCall(exec, f, 3);
869 for (; k < length && !exec->hadException(); ++k) {
870 if (UNLIKELY(!array->canGetIndex(k)))
873 cachedCall.setThis(applyThis);
874 cachedCall.setArgument(0, array->getIndex(k));
875 cachedCall.setArgument(1, jsNumber(k));
876 cachedCall.setArgument(2, thisObj);
877 JSValue result = cachedCall.call();
878 if (result.toBoolean(cachedCall.newCallFrame(exec)))
879 return JSValue::encode(jsBoolean(true));
882 for (; k < length && !exec->hadException(); ++k) {
883 PropertySlot slot(thisObj);
884 if (!thisObj->getPropertySlot(exec, k, slot))
887 MarkedArgumentBuffer eachArguments;
888 eachArguments.append(slot.getValue(exec, k));
889 eachArguments.append(jsNumber(k));
890 eachArguments.append(thisObj);
892 bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec);
894 if (predicateResult) {
895 result = jsBoolean(true);
899 return JSValue::encode(result);
902 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec)
904 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
906 JSValue function = exec->argument(0);
908 CallType callType = getCallData(function, callData);
909 if (callType == CallTypeNone)
910 return throwVMTypeError(exec);
914 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
915 if (!length && exec->argumentCount() == 1)
916 return throwVMTypeError(exec);
918 if (isJSArray(&exec->globalData(), thisObj))
919 array = asArray(thisObj);
921 if (exec->argumentCount() >= 2)
922 rv = exec->argument(1);
923 else if (array && array->canGetIndex(0)){
924 rv = array->getIndex(0);
927 for (i = 0; i < length; i++) {
928 rv = getProperty(exec, thisObj, i);
933 return throwVMTypeError(exec);
937 if (callType == CallTypeJS && array) {
938 CachedCall cachedCall(exec, asFunction(function), 4);
939 for (; i < length && !exec->hadException(); ++i) {
940 cachedCall.setThis(jsNull());
941 cachedCall.setArgument(0, rv);
943 if (LIKELY(array->canGetIndex(i)))
944 v = array->getIndex(i);
946 break; // length has been made unsafe while we enumerate fallback to slow path
947 cachedCall.setArgument(1, v);
948 cachedCall.setArgument(2, jsNumber(i));
949 cachedCall.setArgument(3, array);
950 rv = cachedCall.call();
952 if (i == length) // only return if we reached the end of the array
953 return JSValue::encode(rv);
956 for (; i < length && !exec->hadException(); ++i) {
957 JSValue prop = getProperty(exec, thisObj, i);
961 MarkedArgumentBuffer eachArguments;
962 eachArguments.append(rv);
963 eachArguments.append(prop);
964 eachArguments.append(jsNumber(i));
965 eachArguments.append(thisObj);
967 rv = call(exec, function, callType, callData, jsNull(), eachArguments);
969 return JSValue::encode(rv);
972 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec)
974 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
976 JSValue function = exec->argument(0);
978 CallType callType = getCallData(function, callData);
979 if (callType == CallTypeNone)
980 return throwVMTypeError(exec);
984 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
985 if (!length && exec->argumentCount() == 1)
986 return throwVMTypeError(exec);
988 if (isJSArray(&exec->globalData(), thisObj))
989 array = asArray(thisObj);
991 if (exec->argumentCount() >= 2)
992 rv = exec->argument(1);
993 else if (array && array->canGetIndex(length - 1)){
994 rv = array->getIndex(length - 1);
997 for (i = 0; i < length; i++) {
998 rv = getProperty(exec, thisObj, length - i - 1);
1003 return throwVMTypeError(exec);
1007 if (callType == CallTypeJS && array) {
1008 CachedCall cachedCall(exec, asFunction(function), 4);
1009 for (; i < length && !exec->hadException(); ++i) {
1010 unsigned idx = length - i - 1;
1011 cachedCall.setThis(jsNull());
1012 cachedCall.setArgument(0, rv);
1013 if (UNLIKELY(!array->canGetIndex(idx)))
1014 break; // length has been made unsafe while we enumerate fallback to slow path
1015 cachedCall.setArgument(1, array->getIndex(idx));
1016 cachedCall.setArgument(2, jsNumber(idx));
1017 cachedCall.setArgument(3, array);
1018 rv = cachedCall.call();
1020 if (i == length) // only return if we reached the end of the array
1021 return JSValue::encode(rv);
1024 for (; i < length && !exec->hadException(); ++i) {
1025 unsigned idx = length - i - 1;
1026 JSValue prop = getProperty(exec, thisObj, idx);
1030 MarkedArgumentBuffer eachArguments;
1031 eachArguments.append(rv);
1032 eachArguments.append(prop);
1033 eachArguments.append(jsNumber(idx));
1034 eachArguments.append(thisObj);
1036 rv = call(exec, function, callType, callData, jsNull(), eachArguments);
1038 return JSValue::encode(rv);
1041 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
1043 // JavaScript 1.5 Extension by Mozilla
1044 // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf
1045 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
1047 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
1048 unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
1050 JSValue searchElement = exec->argument(0);
1051 for (; index < length; ++index) {
1052 JSValue e = getProperty(exec, thisObj, index);
1055 if (JSValue::strictEqual(exec, searchElement, e))
1056 return JSValue::encode(jsNumber(index));
1059 return JSValue::encode(jsNumber(-1));
1062 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
1064 // JavaScript 1.6 Extension by Mozilla
1065 // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf
1066 JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
1068 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
1070 return JSValue::encode(jsNumber(-1));
1072 unsigned index = length - 1;
1073 JSValue fromValue = exec->argument(1);
1074 if (!fromValue.isUndefined()) {
1075 double fromDouble = fromValue.toInteger(exec);
1076 if (fromDouble < 0) {
1077 fromDouble += length;
1079 return JSValue::encode(jsNumber(-1));
1081 if (fromDouble < length)
1082 index = static_cast<unsigned>(fromDouble);
1085 JSValue searchElement = exec->argument(0);
1087 ASSERT(index < length);
1088 JSValue e = getProperty(exec, thisObj, index);
1091 if (JSValue::strictEqual(exec, searchElement, e))
1092 return JSValue::encode(jsNumber(index));
1095 return JSValue::encode(jsNumber(-1));