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