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
isNumericCompareFunction(CallType callType,const CallData & callData)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
ArrayPrototype(PassRefPtr<Structure> structure)103 ArrayPrototype::ArrayPrototype(PassRefPtr<Structure> structure)
104 : JSArray(structure)
105 {
106 }
107
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)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
getProperty(ExecState * exec,JSObject * obj,unsigned index)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
putProperty(ExecState * exec,JSObject * obj,const Identifier & propertyName,JSValuePtr value)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
arrayProtoFuncToString(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)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
arrayProtoFuncToLocaleString(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)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
arrayProtoFuncJoin(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncConcat(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncPop(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)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
arrayProtoFuncPush(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncReverse(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)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
arrayProtoFuncShift(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)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
arrayProtoFuncSlice(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncSort(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncSplice(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncUnShift(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncFilter(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncMap(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncEvery(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncForEach(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncSome(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncIndexOf(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
arrayProtoFuncLastIndexOf(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)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
806