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