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