009a87c91214824eb7d47878893fc383d56da725
[WebKit-https.git] / JavaScriptCore / runtime / ArrayPrototype.cpp
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved.
4  *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
5  *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
6  *
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.
11  *
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.
16  *
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
20  *  USA
21  *
22  */
23
24 #include "config.h"
25 #include "ArrayPrototype.h"
26
27 #include "Interpreter.h"
28 #include "ObjectPrototype.h"
29 #include "Lookup.h"
30 #include "Operations.h"
31 #include <algorithm>
32 #include <wtf/Assertions.h>
33 #include <wtf/HashSet.h>
34
35 namespace JSC {
36
37 ASSERT_CLASS_FITS_IN_CELL(ArrayPrototype);
38
39 static JSValuePtr arrayProtoFuncToString(ExecState*, JSObject*, JSValuePtr, const ArgList&);
40 static JSValuePtr arrayProtoFuncToLocaleString(ExecState*, JSObject*, JSValuePtr, const ArgList&);
41 static JSValuePtr arrayProtoFuncConcat(ExecState*, JSObject*, JSValuePtr, const ArgList&);
42 static JSValuePtr arrayProtoFuncJoin(ExecState*, JSObject*, JSValuePtr, const ArgList&);
43 static JSValuePtr arrayProtoFuncPop(ExecState*, JSObject*, JSValuePtr, const ArgList&);
44 static JSValuePtr arrayProtoFuncPush(ExecState*, JSObject*, JSValuePtr, const ArgList&);
45 static JSValuePtr arrayProtoFuncReverse(ExecState*, JSObject*, JSValuePtr, const ArgList&);
46 static JSValuePtr arrayProtoFuncShift(ExecState*, JSObject*, JSValuePtr, const ArgList&);
47 static JSValuePtr arrayProtoFuncSlice(ExecState*, JSObject*, JSValuePtr, const ArgList&);
48 static JSValuePtr arrayProtoFuncSort(ExecState*, JSObject*, JSValuePtr, const ArgList&);
49 static JSValuePtr arrayProtoFuncSplice(ExecState*, JSObject*, JSValuePtr, const ArgList&);
50 static JSValuePtr arrayProtoFuncUnShift(ExecState*, JSObject*, JSValuePtr, const ArgList&);
51 static JSValuePtr arrayProtoFuncEvery(ExecState*, JSObject*, JSValuePtr, const ArgList&);
52 static JSValuePtr arrayProtoFuncForEach(ExecState*, JSObject*, JSValuePtr, const ArgList&);
53 static JSValuePtr arrayProtoFuncSome(ExecState*, JSObject*, JSValuePtr, const ArgList&);
54 static JSValuePtr arrayProtoFuncIndexOf(ExecState*, JSObject*, JSValuePtr, const ArgList&);
55 static JSValuePtr arrayProtoFuncFilter(ExecState*, JSObject*, JSValuePtr, const ArgList&);
56 static JSValuePtr arrayProtoFuncMap(ExecState*, JSObject*, JSValuePtr, const ArgList&);
57 static JSValuePtr arrayProtoFuncLastIndexOf(ExecState*, JSObject*, JSValuePtr, const ArgList&);
58
59 }
60
61 #include "ArrayPrototype.lut.h"
62
63 namespace JSC {
64
65 // ------------------------------ ArrayPrototype ----------------------------
66
67 const ClassInfo ArrayPrototype::info = {"Array", &JSArray::info, 0, ExecState::arrayTable};
68
69 /* Source for ArrayPrototype.lut.h
70 @begin arrayTable 16
71   toString       arrayProtoFuncToString       DontEnum|Function 0
72   toLocaleString arrayProtoFuncToLocaleString DontEnum|Function 0
73   concat         arrayProtoFuncConcat         DontEnum|Function 1
74   join           arrayProtoFuncJoin           DontEnum|Function 1
75   pop            arrayProtoFuncPop            DontEnum|Function 0
76   push           arrayProtoFuncPush           DontEnum|Function 1
77   reverse        arrayProtoFuncReverse        DontEnum|Function 0
78   shift          arrayProtoFuncShift          DontEnum|Function 0
79   slice          arrayProtoFuncSlice          DontEnum|Function 2
80   sort           arrayProtoFuncSort           DontEnum|Function 1
81   splice         arrayProtoFuncSplice         DontEnum|Function 2
82   unshift        arrayProtoFuncUnShift        DontEnum|Function 1
83   every          arrayProtoFuncEvery          DontEnum|Function 1
84   forEach        arrayProtoFuncForEach        DontEnum|Function 1
85   some           arrayProtoFuncSome           DontEnum|Function 1
86   indexOf        arrayProtoFuncIndexOf        DontEnum|Function 1
87   lastIndexOf    arrayProtoFuncLastIndexOf    DontEnum|Function 1
88   filter         arrayProtoFuncFilter         DontEnum|Function 1
89   map            arrayProtoFuncMap            DontEnum|Function 1
90 @end
91 */
92
93 // ECMA 15.4.4
94 ArrayPrototype::ArrayPrototype(PassRefPtr<Structure> structure)
95     : JSArray(structure)
96 {
97 }
98
99 bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
100 {
101     return getStaticFunctionSlot<JSArray>(exec, ExecState::arrayTable(exec), this, propertyName, slot);
102 }
103
104 // ------------------------------ Array Functions ----------------------------
105
106 // Helper function
107 static JSValuePtr getProperty(ExecState* exec, JSObject* obj, unsigned index)
108 {
109     PropertySlot slot(obj);
110     if (!obj->getPropertySlot(exec, index, slot))
111         return noValue();
112     return slot.getValue(exec, index);
113 }
114
115 static void putProperty(ExecState* exec, JSObject* obj, const Identifier& propertyName, JSValuePtr value)
116 {
117     PutPropertySlot slot;
118     obj->put(exec, propertyName, value, slot);
119 }
120
121 JSValuePtr arrayProtoFuncToString(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
122 {
123     if (!thisValue->isObject(&JSArray::info))
124         return throwError(exec, TypeError);
125     JSObject* thisObj = asArray(thisValue);
126
127     HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
128     if (arrayVisitedElements.size() > MaxReentryDepth)
129         return throwError(exec, RangeError, "Maximum call stack size exceeded.");
130
131     bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
132     if (alreadyVisited)
133         return jsEmptyString(exec); // return an empty string, avoiding infinite recursion.
134
135     Vector<UChar, 256> strBuffer;
136     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
137     for (unsigned k = 0; k < length; k++) {
138         if (k >= 1)
139             strBuffer.append(',');
140         if (!strBuffer.data()) {
141             JSObject* error = Error::create(exec, GeneralError, "Out of memory");
142             exec->setException(error);
143             break;
144         }
145
146         JSValuePtr element = thisObj->get(exec, k);
147         if (element->isUndefinedOrNull())
148             continue;
149
150         UString str = element->toString(exec);
151         strBuffer.append(str.data(), str.size());
152
153         if (!strBuffer.data()) {
154             JSObject* error = Error::create(exec, GeneralError, "Out of memory");
155             exec->setException(error);
156         }
157
158         if (exec->hadException())
159             break;
160     }
161     arrayVisitedElements.remove(thisObj);
162     return jsString(exec, UString(strBuffer.data(), strBuffer.data() ? strBuffer.size() : 0));
163 }
164
165 JSValuePtr arrayProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
166 {
167     if (!thisValue->isObject(&JSArray::info))
168         return throwError(exec, TypeError);
169     JSObject* thisObj = asArray(thisValue);
170
171     HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
172     if (arrayVisitedElements.size() > MaxReentryDepth)
173         return throwError(exec, RangeError, "Maximum call stack size exceeded.");
174
175     bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
176     if (alreadyVisited)
177         return jsEmptyString(exec); // return an empty string, avoding infinite recursion.
178
179     Vector<UChar, 256> strBuffer;
180     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
181     for (unsigned k = 0; k < length; k++) {
182         if (k >= 1)
183             strBuffer.append(',');
184         if (!strBuffer.data()) {
185             JSObject* error = Error::create(exec, GeneralError, "Out of memory");
186             exec->setException(error);
187             break;
188         }
189
190         JSValuePtr element = thisObj->get(exec, k);
191         if (element->isUndefinedOrNull())
192             continue;
193
194         JSObject* o = element->toObject(exec);
195         JSValuePtr conversionFunction = o->get(exec, exec->propertyNames().toLocaleString);
196         UString str;
197         CallData callData;
198         CallType callType = conversionFunction->getCallData(callData);
199         if (callType != CallTypeNone)
200             str = call(exec, conversionFunction, callType, callData, element, exec->emptyList())->toString(exec);
201         else
202             str = element->toString(exec);
203         strBuffer.append(str.data(), str.size());
204
205         if (!strBuffer.data()) {
206             JSObject* error = Error::create(exec, GeneralError, "Out of memory");
207             exec->setException(error);
208         }
209
210         if (exec->hadException())
211             break;
212     }
213     arrayVisitedElements.remove(thisObj);
214     return jsString(exec, UString(strBuffer.data(), strBuffer.data() ? strBuffer.size() : 0));
215 }
216
217 JSValuePtr arrayProtoFuncJoin(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
218 {
219     JSObject* thisObj = thisValue->toThisObject(exec);
220
221     HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
222     if (arrayVisitedElements.size() > MaxReentryDepth)
223         return throwError(exec, RangeError, "Maximum call stack size exceeded.");
224
225     bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
226     if (alreadyVisited)
227         return jsEmptyString(exec); // return an empty string, avoding infinite recursion.
228
229     Vector<UChar, 256> strBuffer;
230
231     UChar comma = ',';
232     UString separator = args.at(exec, 0)->isUndefined() ? UString(&comma, 1) : args.at(exec, 0)->toString(exec);
233
234     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
235     for (unsigned k = 0; k < length; k++) {
236         if (k >= 1)
237             strBuffer.append(separator.data(), separator.size());
238         if (!strBuffer.data()) {
239             JSObject* error = Error::create(exec, GeneralError, "Out of memory");
240             exec->setException(error);
241             break;
242         }
243
244         JSValuePtr element = thisObj->get(exec, k);
245         if (element->isUndefinedOrNull())
246             continue;
247
248         UString str = element->toString(exec);
249         strBuffer.append(str.data(), str.size());
250
251         if (!strBuffer.data()) {
252             JSObject* error = Error::create(exec, GeneralError, "Out of memory");
253             exec->setException(error);
254         }
255
256         if (exec->hadException())
257             break;
258     }
259     arrayVisitedElements.remove(thisObj);
260     return jsString(exec, UString(strBuffer.data(), strBuffer.data() ? strBuffer.size() : 0));
261 }
262
263 JSValuePtr arrayProtoFuncConcat(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
264 {
265     JSArray* arr = constructEmptyArray(exec);
266     int n = 0;
267     JSValuePtr curArg = thisValue->toThisObject(exec);
268     ArgList::const_iterator it = args.begin();
269     ArgList::const_iterator end = args.end();
270     while (1) {
271         if (curArg->isObject(&JSArray::info)) {
272             JSArray* curArray = asArray(curArg);
273             unsigned length = curArray->length();
274             for (unsigned k = 0; k < length; ++k) {
275                 if (JSValuePtr v = getProperty(exec, curArray, k))
276                     arr->put(exec, n, v);
277                 n++;
278             }
279         } else {
280             arr->put(exec, n, curArg);
281             n++;
282         }
283         if (it == end)
284             break;
285         curArg = (*it).jsValue(exec);
286         ++it;
287     }
288     arr->setLength(n);
289     return arr;
290 }
291
292 JSValuePtr arrayProtoFuncPop(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
293 {
294     if (exec->interpreter()->isJSArray(thisValue))
295         return asArray(thisValue)->pop();
296
297     JSObject* thisObj = thisValue->toThisObject(exec);
298     JSValuePtr result;
299     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
300     if (length == 0) {
301         putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length));
302         result = jsUndefined();
303     } else {
304         result = thisObj->get(exec, length - 1);
305         thisObj->deleteProperty(exec, length - 1);
306         putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - 1));
307     }
308     return result;
309 }
310
311 JSValuePtr arrayProtoFuncPush(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
312 {
313     if (exec->interpreter()->isJSArray(thisValue) && args.size() == 1) {
314         JSArray* array = asArray(thisValue);
315         array->push(exec, args.begin()->jsValue(exec));
316         return jsNumber(exec, array->length());
317     }
318
319     JSObject* thisObj = thisValue->toThisObject(exec);
320     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
321     for (unsigned n = 0; n < args.size(); n++)
322         thisObj->put(exec, length + n, args.at(exec, n));
323     length += args.size();
324     putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length));
325     return jsNumber(exec, length);
326 }
327
328 JSValuePtr arrayProtoFuncReverse(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
329 {
330     JSObject* thisObj = thisValue->toThisObject(exec);
331     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
332     unsigned middle = length / 2;
333
334     for (unsigned k = 0; k < middle; k++) {
335         unsigned lk1 = length - k - 1;
336         JSValuePtr obj2 = getProperty(exec, thisObj, lk1);
337         JSValuePtr obj = getProperty(exec, thisObj, k);
338
339         if (obj2)
340             thisObj->put(exec, k, obj2);
341         else
342             thisObj->deleteProperty(exec, k);
343
344         if (obj)
345             thisObj->put(exec, lk1, obj);
346         else
347             thisObj->deleteProperty(exec, lk1);
348     }
349     return thisObj;
350 }
351
352 JSValuePtr arrayProtoFuncShift(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
353 {
354     JSObject* thisObj = thisValue->toThisObject(exec);
355     JSValuePtr result;
356
357     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
358     if (length == 0) {
359         putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length));
360         result = jsUndefined();
361     } else {
362         result = thisObj->get(exec, 0);
363         for (unsigned k = 1; k < length; k++) {
364             if (JSValuePtr obj = getProperty(exec, thisObj, k))
365                 thisObj->put(exec, k - 1, obj);
366             else
367                 thisObj->deleteProperty(exec, k - 1);
368         }
369         thisObj->deleteProperty(exec, length - 1);
370         putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - 1));
371     }
372     return result;
373 }
374
375 JSValuePtr arrayProtoFuncSlice(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
376 {
377     // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
378
379     JSObject* thisObj = thisValue->toThisObject(exec);
380
381     // We return a new array
382     JSArray* resObj = constructEmptyArray(exec);
383     JSValuePtr result = resObj;
384     double begin = args.at(exec, 0)->toInteger(exec);
385     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
386     if (begin >= 0) {
387         if (begin > length)
388             begin = length;
389     } else {
390         begin += length;
391         if (begin < 0)
392             begin = 0;
393     }
394     double end;
395     if (args.at(exec, 1)->isUndefined())
396         end = length;
397     else {
398         end = args.at(exec, 1)->toInteger(exec);
399         if (end < 0) {
400             end += length;
401             if (end < 0)
402                 end = 0;
403         } else {
404             if (end > length)
405                 end = length;
406         }
407     }
408
409     int n = 0;
410     int b = static_cast<int>(begin);
411     int e = static_cast<int>(end);
412     for (int k = b; k < e; k++, n++) {
413         if (JSValuePtr v = getProperty(exec, thisObj, k))
414             resObj->put(exec, n, v);
415     }
416     resObj->setLength(n);
417     return result;
418 }
419
420 JSValuePtr arrayProtoFuncSort(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
421 {
422     JSObject* thisObj = thisValue->toThisObject(exec);
423
424     JSValuePtr function = args.at(exec, 0);
425     CallData callData;
426     CallType callType = function->getCallData(callData);
427
428     if (thisObj->classInfo() == &JSArray::info) {
429         if (callType != CallTypeNone)
430             asArray(thisObj)->sort(exec, function, callType, callData);
431         else
432             asArray(thisObj)->sort(exec);
433         return thisObj;
434     }
435
436     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
437
438     if (!length)
439         return thisObj;
440
441     // "Min" sort. Not the fastest, but definitely less code than heapsort
442     // or quicksort, and much less swapping than bubblesort/insertionsort.
443     for (unsigned i = 0; i < length - 1; ++i) {
444         JSValuePtr iObj = thisObj->get(exec, i);
445         unsigned themin = i;
446         JSValuePtr minObj = iObj;
447         for (unsigned j = i + 1; j < length; ++j) {
448             JSValuePtr jObj = thisObj->get(exec, j);
449             double compareResult;
450             if (jObj->isUndefined())
451                 compareResult = 1; // don't check minObj because there's no need to differentiate == (0) from > (1)
452             else if (minObj->isUndefined())
453                 compareResult = -1;
454             else if (callType != CallTypeNone) {
455                 ArgList l;
456                 l.append(jObj);
457                 l.append(minObj);
458                 compareResult = call(exec, function, callType, callData, exec->globalThisValue(), l)->toNumber(exec);
459             } else
460                 compareResult = (jObj->toString(exec) < minObj->toString(exec)) ? -1 : 1;
461
462             if (compareResult < 0) {
463                 themin = j;
464                 minObj = jObj;
465             }
466         }
467         // Swap themin and i
468         if (themin > i) {
469             thisObj->put(exec, i, minObj);
470             thisObj->put(exec, themin, iObj);
471         }
472     }
473     return thisObj;
474 }
475
476 JSValuePtr arrayProtoFuncSplice(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
477 {
478     JSObject* thisObj = thisValue->toThisObject(exec);
479
480     // 15.4.4.12
481     JSArray* resObj = constructEmptyArray(exec);
482     JSValuePtr result = resObj;
483     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
484     if (!args.size())
485         return jsUndefined();
486     int begin = args.at(exec, 0)->toUInt32(exec);
487     if (begin < 0)
488         begin = std::max<int>(begin + length, 0);
489     else
490         begin = std::min<int>(begin, length);
491
492     unsigned deleteCount;
493     if (args.size() > 1)
494         deleteCount = std::min<int>(std::max<int>(args.at(exec, 1)->toUInt32(exec), 0), length - begin);
495     else
496         deleteCount = length - begin;
497
498     for (unsigned k = 0; k < deleteCount; k++) {
499         if (JSValuePtr v = getProperty(exec, thisObj, k + begin))
500             resObj->put(exec, k, v);
501     }
502     resObj->setLength(deleteCount);
503
504     unsigned additionalArgs = std::max<int>(args.size() - 2, 0);
505     if (additionalArgs != deleteCount) {
506         if (additionalArgs < deleteCount) {
507             for (unsigned k = begin; k < length - deleteCount; ++k) {
508                 if (JSValuePtr v = getProperty(exec, thisObj, k + deleteCount))
509                     thisObj->put(exec, k + additionalArgs, v);
510                 else
511                     thisObj->deleteProperty(exec, k + additionalArgs);
512             }
513             for (unsigned k = length; k > length - deleteCount + additionalArgs; --k)
514                 thisObj->deleteProperty(exec, k - 1);
515         } else {
516             for (unsigned k = length - deleteCount; (int)k > begin; --k) {
517                 if (JSValuePtr obj = getProperty(exec, thisObj, k + deleteCount - 1))
518                     thisObj->put(exec, k + additionalArgs - 1, obj);
519                 else
520                     thisObj->deleteProperty(exec, k + additionalArgs - 1);
521             }
522         }
523     }
524     for (unsigned k = 0; k < additionalArgs; ++k)
525         thisObj->put(exec, k + begin, args.at(exec, k + 2));
526
527     putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - deleteCount + additionalArgs));
528     return result;
529 }
530
531 JSValuePtr arrayProtoFuncUnShift(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
532 {
533     JSObject* thisObj = thisValue->toThisObject(exec);
534
535     // 15.4.4.13
536     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
537     unsigned nrArgs = args.size();
538     if (nrArgs) {
539         for (unsigned k = length; k > 0; --k) {
540             if (JSValuePtr v = getProperty(exec, thisObj, k - 1))
541                 thisObj->put(exec, k + nrArgs - 1, v);
542             else
543                 thisObj->deleteProperty(exec, k + nrArgs - 1);
544         }
545     }
546     for (unsigned k = 0; k < nrArgs; ++k)
547         thisObj->put(exec, k, args.at(exec, k));
548     JSValuePtr result = jsNumber(exec, length + nrArgs);
549     putProperty(exec, thisObj, exec->propertyNames().length, result);
550     return result;
551 }
552
553 JSValuePtr arrayProtoFuncFilter(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
554 {
555     JSObject* thisObj = thisValue->toThisObject(exec);
556
557     JSValuePtr function = args.at(exec, 0);
558     CallData callData;
559     CallType callType = function->getCallData(callData);
560     if (callType == CallTypeNone)
561         return throwError(exec, TypeError);
562
563     JSObject* applyThis = args.at(exec, 1)->isUndefinedOrNull() ? exec->globalThisValue() : args.at(exec, 1)->toObject(exec);
564     JSArray* resultArray = constructEmptyArray(exec);
565
566     unsigned filterIndex = 0;
567     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
568     for (unsigned k = 0; k < length && !exec->hadException(); ++k) {
569         PropertySlot slot(thisObj);
570
571         if (!thisObj->getPropertySlot(exec, k, slot))
572             continue;
573
574         JSValuePtr v = slot.getValue(exec, k);
575
576         ArgList eachArguments;
577
578         eachArguments.append(v);
579         eachArguments.append(jsNumber(exec, k));
580         eachArguments.append(thisObj);
581
582         JSValuePtr result = call(exec, function, callType, callData, applyThis, eachArguments);
583
584         if (result->toBoolean(exec))
585             resultArray->put(exec, filterIndex++, v);
586     }
587     return resultArray;
588 }
589
590 JSValuePtr arrayProtoFuncMap(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
591 {
592     JSObject* thisObj = thisValue->toThisObject(exec);
593
594     JSValuePtr function = args.at(exec, 0);
595     CallData callData;
596     CallType callType = function->getCallData(callData);
597     if (callType == CallTypeNone)
598         return throwError(exec, TypeError);
599
600     JSObject* applyThis = args.at(exec, 1)->isUndefinedOrNull() ? exec->globalThisValue() : args.at(exec, 1)->toObject(exec);
601
602     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
603
604     JSArray* resultArray = constructEmptyArray(exec, length);
605
606     for (unsigned k = 0; k < length && !exec->hadException(); ++k) {
607         PropertySlot slot(thisObj);
608         if (!thisObj->getPropertySlot(exec, k, slot))
609             continue;
610
611         JSValuePtr v = slot.getValue(exec, k);
612
613         ArgList eachArguments;
614
615         eachArguments.append(v);
616         eachArguments.append(jsNumber(exec, k));
617         eachArguments.append(thisObj);
618
619         JSValuePtr result = call(exec, function, callType, callData, applyThis, eachArguments);
620         resultArray->put(exec, k, result);
621     }
622
623     return resultArray;
624 }
625
626 // Documentation for these three is available at:
627 // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every
628 // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach
629 // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some
630
631 JSValuePtr arrayProtoFuncEvery(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
632 {
633     JSObject* thisObj = thisValue->toThisObject(exec);
634
635     JSValuePtr function = args.at(exec, 0);
636     CallData callData;
637     CallType callType = function->getCallData(callData);
638     if (callType == CallTypeNone)
639         return throwError(exec, TypeError);
640
641     JSObject* applyThis = args.at(exec, 1)->isUndefinedOrNull() ? exec->globalThisValue() : args.at(exec, 1)->toObject(exec);
642
643     JSValuePtr result = jsBoolean(true);
644
645     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
646     for (unsigned k = 0; k < length && !exec->hadException(); ++k) {
647         PropertySlot slot(thisObj);
648
649         if (!thisObj->getPropertySlot(exec, k, slot))
650             continue;
651
652         ArgList eachArguments;
653
654         eachArguments.append(slot.getValue(exec, k));
655         eachArguments.append(jsNumber(exec, k));
656         eachArguments.append(thisObj);
657
658         bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments)->toBoolean(exec);
659
660         if (!predicateResult) {
661             result = jsBoolean(false);
662             break;
663         }
664     }
665
666     return result;
667 }
668
669 JSValuePtr arrayProtoFuncForEach(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
670 {
671     JSObject* thisObj = thisValue->toThisObject(exec);
672
673     JSValuePtr function = args.at(exec, 0);
674     CallData callData;
675     CallType callType = function->getCallData(callData);
676     if (callType == CallTypeNone)
677         return throwError(exec, TypeError);
678
679     JSObject* applyThis = args.at(exec, 1)->isUndefinedOrNull() ? exec->globalThisValue() : args.at(exec, 1)->toObject(exec);
680
681     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
682     for (unsigned k = 0; k < length && !exec->hadException(); ++k) {
683         PropertySlot slot(thisObj);
684         if (!thisObj->getPropertySlot(exec, k, slot))
685             continue;
686
687         ArgList eachArguments;
688         eachArguments.append(slot.getValue(exec, k));
689         eachArguments.append(jsNumber(exec, k));
690         eachArguments.append(thisObj);
691
692         call(exec, function, callType, callData, applyThis, eachArguments);
693     }
694     return jsUndefined();
695 }
696
697 JSValuePtr arrayProtoFuncSome(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
698 {
699     JSObject* thisObj = thisValue->toThisObject(exec);
700
701     JSValuePtr function = args.at(exec, 0);
702     CallData callData;
703     CallType callType = function->getCallData(callData);
704     if (callType == CallTypeNone)
705         return throwError(exec, TypeError);
706
707     JSObject* applyThis = args.at(exec, 1)->isUndefinedOrNull() ? exec->globalThisValue() : args.at(exec, 1)->toObject(exec);
708
709     JSValuePtr result = jsBoolean(false);
710
711     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
712     for (unsigned k = 0; k < length && !exec->hadException(); ++k) {
713         PropertySlot slot(thisObj);
714         if (!thisObj->getPropertySlot(exec, k, slot))
715             continue;
716
717         ArgList eachArguments;
718         eachArguments.append(slot.getValue(exec, k));
719         eachArguments.append(jsNumber(exec, k));
720         eachArguments.append(thisObj);
721
722         bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments)->toBoolean(exec);
723
724         if (predicateResult) {
725             result = jsBoolean(true);
726             break;
727         }
728     }
729     return result;
730 }
731
732 JSValuePtr arrayProtoFuncIndexOf(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
733 {
734     // JavaScript 1.5 Extension by Mozilla
735     // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf
736
737     JSObject* thisObj = thisValue->toThisObject(exec);
738
739     unsigned index = 0;
740     double d = args.at(exec, 1)->toInteger(exec);
741     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
742     if (d < 0)
743         d += length;
744     if (d > 0) {
745         if (d > length)
746             index = length;
747         else
748             index = static_cast<unsigned>(d);
749     }
750
751     JSValuePtr searchElement = args.at(exec, 0);
752     for (; index < length; ++index) {
753         JSValuePtr e = getProperty(exec, thisObj, index);
754         if (!e)
755             continue;
756         if (JSValuePtr::strictEqual(searchElement, e))
757             return jsNumber(exec, index);
758     }
759
760     return jsNumber(exec, -1);
761 }
762
763 JSValuePtr arrayProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
764 {
765     // JavaScript 1.6 Extension by Mozilla
766     // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf
767
768     JSObject* thisObj = thisValue->toThisObject(exec);
769
770     unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
771     int index = length - 1;
772     double d = args.at(exec, 1)->toIntegerPreserveNaN(exec);
773
774     if (d < 0) {
775         d += length;
776         if (d < 0)
777             return jsNumber(exec, -1);
778     }
779     if (d < length)
780         index = static_cast<int>(d);
781
782     JSValuePtr searchElement = args.at(exec, 0);
783     for (; index >= 0; --index) {
784         JSValuePtr e = getProperty(exec, thisObj, index);
785         if (!e)
786             continue;
787         if (JSValuePtr::strictEqual(searchElement, e))
788             return jsNumber(exec, index);
789     }
790
791     return jsNumber(exec, -1);
792 }
793
794 } // namespace JSC