• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2003, 2007, 2008, 2009 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 "CachedCall.h"
29 #include "Interpreter.h"
30 #include "JIT.h"
31 #include "ObjectPrototype.h"
32 #include "Lookup.h"
33 #include "Operations.h"
34 #include <algorithm>
35 #include <wtf/Assertions.h>
36 #include <wtf/HashSet.h>
37 
38 namespace JSC {
39 
40 ASSERT_CLASS_FITS_IN_CELL(ArrayPrototype);
41 
42 static JSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&);
43 static JSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*, JSObject*, JSValue, const ArgList&);
44 static JSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*, JSObject*, JSValue, const ArgList&);
45 static JSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*, JSObject*, JSValue, const ArgList&);
46 static JSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*, JSObject*, JSValue, const ArgList&);
47 static JSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*, JSObject*, JSValue, const ArgList&);
48 static JSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*, JSObject*, JSValue, const ArgList&);
49 static JSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*, JSObject*, JSValue, const ArgList&);
50 static JSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*, JSObject*, JSValue, const ArgList&);
51 static JSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState*, JSObject*, JSValue, const ArgList&);
52 static JSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*, JSObject*, JSValue, const ArgList&);
53 static JSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*, JSObject*, JSValue, const ArgList&);
54 static JSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState*, JSObject*, JSValue, const ArgList&);
55 static JSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState*, JSObject*, JSValue, const ArgList&);
56 static JSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState*, JSObject*, JSValue, const ArgList&);
57 static JSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*, JSObject*, JSValue, const ArgList&);
58 static JSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState*, JSObject*, JSValue, const ArgList&);
59 static JSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState*, JSObject*, JSValue, const ArgList&);
60 static JSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState*, JSObject*, JSValue, const ArgList&);
61 static JSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState*, JSObject*, JSValue, const ArgList&);
62 static JSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*, JSObject*, JSValue, const ArgList&);
63 
64 }
65 
66 #include "ArrayPrototype.lut.h"
67 
68 namespace JSC {
69 
isNumericCompareFunction(CallType callType,const CallData & callData)70 static inline bool isNumericCompareFunction(CallType callType, const CallData& callData)
71 {
72     if (callType != CallTypeJS)
73         return false;
74 
75 #if ENABLE(JIT)
76     // If the JIT is enabled then we need to preserve the invariant that every
77     // function with a CodeBlock also has JIT code.
78     callData.js.functionBody->jitCode(callData.js.scopeChain);
79     CodeBlock& codeBlock = callData.js.functionBody->generatedBytecode();
80 #else
81     CodeBlock& codeBlock = callData.js.functionBody->bytecode(callData.js.scopeChain);
82 #endif
83 
84     return codeBlock.isNumericCompareFunction();
85 }
86 
87 // ------------------------------ ArrayPrototype ----------------------------
88 
89 const ClassInfo ArrayPrototype::info = {"Array", &JSArray::info, 0, ExecState::arrayTable};
90 
91 /* Source for ArrayPrototype.lut.h
92 @begin arrayTable 16
93   toString       arrayProtoFuncToString       DontEnum|Function 0
94   toLocaleString arrayProtoFuncToLocaleString DontEnum|Function 0
95   concat         arrayProtoFuncConcat         DontEnum|Function 1
96   join           arrayProtoFuncJoin           DontEnum|Function 1
97   pop            arrayProtoFuncPop            DontEnum|Function 0
98   push           arrayProtoFuncPush           DontEnum|Function 1
99   reverse        arrayProtoFuncReverse        DontEnum|Function 0
100   shift          arrayProtoFuncShift          DontEnum|Function 0
101   slice          arrayProtoFuncSlice          DontEnum|Function 2
102   sort           arrayProtoFuncSort           DontEnum|Function 1
103   splice         arrayProtoFuncSplice         DontEnum|Function 2
104   unshift        arrayProtoFuncUnShift        DontEnum|Function 1
105   every          arrayProtoFuncEvery          DontEnum|Function 1
106   forEach        arrayProtoFuncForEach        DontEnum|Function 1
107   some           arrayProtoFuncSome           DontEnum|Function 1
108   indexOf        arrayProtoFuncIndexOf        DontEnum|Function 1
109   lastIndexOf    arrayProtoFuncLastIndexOf    DontEnum|Function 1
110   filter         arrayProtoFuncFilter         DontEnum|Function 1
111   reduce         arrayProtoFuncReduce         DontEnum|Function 1
112   reduceRight    arrayProtoFuncReduceRight    DontEnum|Function 1
113   map            arrayProtoFuncMap            DontEnum|Function 1
114 @end
115 */
116 
117 // ECMA 15.4.4
ArrayPrototype(PassRefPtr<Structure> structure)118 ArrayPrototype::ArrayPrototype(PassRefPtr<Structure> structure)
119     : JSArray(structure)
120 {
121 }
122 
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)123 bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
124 {
125     return getStaticFunctionSlot<JSArray>(exec, ExecState::arrayTable(exec), this, propertyName, slot);
126 }
127 
128 // ------------------------------ Array Functions ----------------------------
129 
130 // Helper function
getProperty(ExecState * exec,JSObject * obj,unsigned index)131 static JSValue getProperty(ExecState* exec, JSObject* obj, unsigned index)
132 {
133     PropertySlot slot(obj);
134     if (!obj->getPropertySlot(exec, index, slot))
135         return JSValue();
136     return slot.getValue(exec, index);
137 }
138 
putProperty(ExecState * exec,JSObject * obj,const Identifier & propertyName,JSValue value)139 static void putProperty(ExecState* exec, JSObject* obj, const Identifier& propertyName, JSValue value)
140 {
141     PutPropertySlot slot;
142     obj->put(exec, propertyName, value, slot);
143 }
144 
arrayProtoFuncToString(ExecState * exec,JSObject *,JSValue thisValue,const ArgList &)145 JSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
146 {
147     if (!thisValue.isObject(&JSArray::info))
148         return throwError(exec, TypeError);
149     JSObject* thisObj = asArray(thisValue);
150 
151     HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
152     if (arrayVisitedElements.size() >= MaxSecondaryThreadReentryDepth) {
153         if (!isMainThread() || arrayVisitedElements.size() >= MaxMainThreadReentryDepth)
154             return throwError(exec, RangeError, "Maximum call stack size exceeded.");
155     }
156 
157     bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
158     if (alreadyVisited)
159         return jsEmptyString(exec); // return an empty string, avoiding infinite recursion.
160 
161     Vector<UChar, 256> strBuffer;
162     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
163     for (unsigned k = 0; k < length; k++) {
164         if (k >= 1)
165             strBuffer.append(',');
166         if (!strBuffer.data()) {
167             JSObject* error = Error::create(exec, GeneralError, "Out of memory");
168             exec->setException(error);
169             break;
170         }
171 
172         JSValue element = thisObj->get(exec, k);
173         if (element.isUndefinedOrNull())
174             continue;
175 
176         UString str = element.toString(exec);
177         strBuffer.append(str.data(), str.size());
178 
179         if (!strBuffer.data()) {
180             JSObject* error = Error::create(exec, GeneralError, "Out of memory");
181             exec->setException(error);
182         }
183 
184         if (exec->hadException())
185             break;
186     }
187     arrayVisitedElements.remove(thisObj);
188     return jsString(exec, UString(strBuffer.data(), strBuffer.data() ? strBuffer.size() : 0));
189 }
190 
arrayProtoFuncToLocaleString(ExecState * exec,JSObject *,JSValue thisValue,const ArgList &)191 JSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
192 {
193     if (!thisValue.isObject(&JSArray::info))
194         return throwError(exec, TypeError);
195     JSObject* thisObj = asArray(thisValue);
196 
197     HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
198     if (arrayVisitedElements.size() >= MaxSecondaryThreadReentryDepth) {
199         if (!isMainThread() || arrayVisitedElements.size() >= MaxMainThreadReentryDepth)
200             return throwError(exec, RangeError, "Maximum call stack size exceeded.");
201     }
202 
203     bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
204     if (alreadyVisited)
205         return jsEmptyString(exec); // return an empty string, avoding infinite recursion.
206 
207     Vector<UChar, 256> strBuffer;
208     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
209     for (unsigned k = 0; k < length; k++) {
210         if (k >= 1)
211             strBuffer.append(',');
212         if (!strBuffer.data()) {
213             JSObject* error = Error::create(exec, GeneralError, "Out of memory");
214             exec->setException(error);
215             break;
216         }
217 
218         JSValue element = thisObj->get(exec, k);
219         if (element.isUndefinedOrNull())
220             continue;
221 
222         JSObject* o = element.toObject(exec);
223         JSValue conversionFunction = o->get(exec, exec->propertyNames().toLocaleString);
224         UString str;
225         CallData callData;
226         CallType callType = conversionFunction.getCallData(callData);
227         if (callType != CallTypeNone)
228             str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toString(exec);
229         else
230             str = element.toString(exec);
231         strBuffer.append(str.data(), str.size());
232 
233         if (!strBuffer.data()) {
234             JSObject* error = Error::create(exec, GeneralError, "Out of memory");
235             exec->setException(error);
236         }
237 
238         if (exec->hadException())
239             break;
240     }
241     arrayVisitedElements.remove(thisObj);
242     return jsString(exec, UString(strBuffer.data(), strBuffer.data() ? strBuffer.size() : 0));
243 }
244 
arrayProtoFuncJoin(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)245 JSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
246 {
247     JSObject* thisObj = thisValue.toThisObject(exec);
248 
249     HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
250     if (arrayVisitedElements.size() >= MaxSecondaryThreadReentryDepth) {
251         if (!isMainThread() || arrayVisitedElements.size() >= MaxMainThreadReentryDepth)
252             return throwError(exec, RangeError, "Maximum call stack size exceeded.");
253     }
254 
255     bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
256     if (alreadyVisited)
257         return jsEmptyString(exec); // return an empty string, avoding infinite recursion.
258 
259     Vector<UChar, 256> strBuffer;
260 
261     UChar comma = ',';
262     UString separator = args.at(0).isUndefined() ? UString(&comma, 1) : args.at(0).toString(exec);
263 
264     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
265     for (unsigned k = 0; k < length; k++) {
266         if (k >= 1)
267             strBuffer.append(separator.data(), separator.size());
268         if (!strBuffer.data()) {
269             JSObject* error = Error::create(exec, GeneralError, "Out of memory");
270             exec->setException(error);
271             break;
272         }
273 
274         JSValue element = thisObj->get(exec, k);
275         if (element.isUndefinedOrNull())
276             continue;
277 
278         UString str = element.toString(exec);
279         strBuffer.append(str.data(), str.size());
280 
281         if (!strBuffer.data()) {
282             JSObject* error = Error::create(exec, GeneralError, "Out of memory");
283             exec->setException(error);
284         }
285 
286         if (exec->hadException())
287             break;
288     }
289     arrayVisitedElements.remove(thisObj);
290     return jsString(exec, UString(strBuffer.data(), strBuffer.data() ? strBuffer.size() : 0));
291 }
292 
arrayProtoFuncConcat(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)293 JSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
294 {
295     JSArray* arr = constructEmptyArray(exec);
296     int n = 0;
297     JSValue curArg = thisValue.toThisObject(exec);
298     ArgList::const_iterator it = args.begin();
299     ArgList::const_iterator end = args.end();
300     while (1) {
301         if (curArg.isObject(&JSArray::info)) {
302             unsigned length = curArg.get(exec, exec->propertyNames().length).toUInt32(exec);
303             JSObject* curObject = curArg.toObject(exec);
304             for (unsigned k = 0; k < length; ++k) {
305                 if (JSValue v = getProperty(exec, curObject, k))
306                     arr->put(exec, n, v);
307                 n++;
308             }
309         } else {
310             arr->put(exec, n, curArg);
311             n++;
312         }
313         if (it == end)
314             break;
315         curArg = (*it);
316         ++it;
317     }
318     arr->setLength(n);
319     return arr;
320 }
321 
arrayProtoFuncPop(ExecState * exec,JSObject *,JSValue thisValue,const ArgList &)322 JSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
323 {
324     if (isJSArray(&exec->globalData(), thisValue))
325         return asArray(thisValue)->pop();
326 
327     JSObject* thisObj = thisValue.toThisObject(exec);
328     JSValue result;
329     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
330     if (length == 0) {
331         putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length));
332         result = jsUndefined();
333     } else {
334         result = thisObj->get(exec, length - 1);
335         thisObj->deleteProperty(exec, length - 1);
336         putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - 1));
337     }
338     return result;
339 }
340 
arrayProtoFuncPush(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)341 JSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
342 {
343     if (isJSArray(&exec->globalData(), thisValue) && args.size() == 1) {
344         JSArray* array = asArray(thisValue);
345         array->push(exec, *args.begin());
346         return jsNumber(exec, array->length());
347     }
348 
349     JSObject* thisObj = thisValue.toThisObject(exec);
350     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
351     for (unsigned n = 0; n < args.size(); n++)
352         thisObj->put(exec, length + n, args.at(n));
353     length += args.size();
354     putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length));
355     return jsNumber(exec, length);
356 }
357 
arrayProtoFuncReverse(ExecState * exec,JSObject *,JSValue thisValue,const ArgList &)358 JSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
359 {
360     JSObject* thisObj = thisValue.toThisObject(exec);
361     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
362     unsigned middle = length / 2;
363 
364     for (unsigned k = 0; k < middle; k++) {
365         unsigned lk1 = length - k - 1;
366         JSValue obj2 = getProperty(exec, thisObj, lk1);
367         JSValue obj = getProperty(exec, thisObj, k);
368 
369         if (obj2)
370             thisObj->put(exec, k, obj2);
371         else
372             thisObj->deleteProperty(exec, k);
373 
374         if (obj)
375             thisObj->put(exec, lk1, obj);
376         else
377             thisObj->deleteProperty(exec, lk1);
378     }
379     return thisObj;
380 }
381 
arrayProtoFuncShift(ExecState * exec,JSObject *,JSValue thisValue,const ArgList &)382 JSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
383 {
384     JSObject* thisObj = thisValue.toThisObject(exec);
385     JSValue result;
386 
387     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
388     if (length == 0) {
389         putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length));
390         result = jsUndefined();
391     } else {
392         result = thisObj->get(exec, 0);
393         for (unsigned k = 1; k < length; k++) {
394             if (JSValue obj = getProperty(exec, thisObj, k))
395                 thisObj->put(exec, k - 1, obj);
396             else
397                 thisObj->deleteProperty(exec, k - 1);
398         }
399         thisObj->deleteProperty(exec, length - 1);
400         putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - 1));
401     }
402     return result;
403 }
404 
arrayProtoFuncSlice(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)405 JSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
406 {
407     // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
408 
409     JSObject* thisObj = thisValue.toThisObject(exec);
410 
411     // We return a new array
412     JSArray* resObj = constructEmptyArray(exec);
413     JSValue result = resObj;
414     double begin = args.at(0).toInteger(exec);
415     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
416     if (begin >= 0) {
417         if (begin > length)
418             begin = length;
419     } else {
420         begin += length;
421         if (begin < 0)
422             begin = 0;
423     }
424     double end;
425     if (args.at(1).isUndefined())
426         end = length;
427     else {
428         end = args.at(1).toInteger(exec);
429         if (end < 0) {
430             end += length;
431             if (end < 0)
432                 end = 0;
433         } else {
434             if (end > length)
435                 end = length;
436         }
437     }
438 
439     int n = 0;
440     int b = static_cast<int>(begin);
441     int e = static_cast<int>(end);
442     for (int k = b; k < e; k++, n++) {
443         if (JSValue v = getProperty(exec, thisObj, k))
444             resObj->put(exec, n, v);
445     }
446     resObj->setLength(n);
447     return result;
448 }
449 
arrayProtoFuncSort(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)450 JSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
451 {
452     JSObject* thisObj = thisValue.toThisObject(exec);
453 
454     JSValue function = args.at(0);
455     CallData callData;
456     CallType callType = function.getCallData(callData);
457 
458     if (thisObj->classInfo() == &JSArray::info) {
459         if (isNumericCompareFunction(callType, callData))
460             asArray(thisObj)->sortNumeric(exec, function, callType, callData);
461         else if (callType != CallTypeNone)
462             asArray(thisObj)->sort(exec, function, callType, callData);
463         else
464             asArray(thisObj)->sort(exec);
465         return thisObj;
466     }
467 
468     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
469 
470     if (!length)
471         return thisObj;
472 
473     // "Min" sort. Not the fastest, but definitely less code than heapsort
474     // or quicksort, and much less swapping than bubblesort/insertionsort.
475     for (unsigned i = 0; i < length - 1; ++i) {
476         JSValue iObj = thisObj->get(exec, i);
477         unsigned themin = i;
478         JSValue minObj = iObj;
479         for (unsigned j = i + 1; j < length; ++j) {
480             JSValue jObj = thisObj->get(exec, j);
481             double compareResult;
482             if (jObj.isUndefined())
483                 compareResult = 1; // don't check minObj because there's no need to differentiate == (0) from > (1)
484             else if (minObj.isUndefined())
485                 compareResult = -1;
486             else if (callType != CallTypeNone) {
487                 MarkedArgumentBuffer l;
488                 l.append(jObj);
489                 l.append(minObj);
490                 compareResult = call(exec, function, callType, callData, exec->globalThisValue(), l).toNumber(exec);
491             } else
492                 compareResult = (jObj.toString(exec) < minObj.toString(exec)) ? -1 : 1;
493 
494             if (compareResult < 0) {
495                 themin = j;
496                 minObj = jObj;
497             }
498         }
499         // Swap themin and i
500         if (themin > i) {
501             thisObj->put(exec, i, minObj);
502             thisObj->put(exec, themin, iObj);
503         }
504     }
505     return thisObj;
506 }
507 
arrayProtoFuncSplice(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)508 JSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
509 {
510     JSObject* thisObj = thisValue.toThisObject(exec);
511 
512     // 15.4.4.12
513     JSArray* resObj = constructEmptyArray(exec);
514     JSValue result = resObj;
515     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
516     if (!args.size())
517         return jsUndefined();
518     int begin = args.at(0).toUInt32(exec);
519     if (begin < 0)
520         begin = std::max<int>(begin + length, 0);
521     else
522         begin = std::min<int>(begin, length);
523 
524     unsigned deleteCount;
525     if (args.size() > 1)
526         deleteCount = std::min<int>(std::max<int>(args.at(1).toUInt32(exec), 0), length - begin);
527     else
528         deleteCount = length - begin;
529 
530     for (unsigned k = 0; k < deleteCount; k++) {
531         if (JSValue v = getProperty(exec, thisObj, k + begin))
532             resObj->put(exec, k, v);
533     }
534     resObj->setLength(deleteCount);
535 
536     unsigned additionalArgs = std::max<int>(args.size() - 2, 0);
537     if (additionalArgs != deleteCount) {
538         if (additionalArgs < deleteCount) {
539             for (unsigned k = begin; k < length - deleteCount; ++k) {
540                 if (JSValue v = getProperty(exec, thisObj, k + deleteCount))
541                     thisObj->put(exec, k + additionalArgs, v);
542                 else
543                     thisObj->deleteProperty(exec, k + additionalArgs);
544             }
545             for (unsigned k = length; k > length - deleteCount + additionalArgs; --k)
546                 thisObj->deleteProperty(exec, k - 1);
547         } else {
548             for (unsigned k = length - deleteCount; (int)k > begin; --k) {
549                 if (JSValue obj = getProperty(exec, thisObj, k + deleteCount - 1))
550                     thisObj->put(exec, k + additionalArgs - 1, obj);
551                 else
552                     thisObj->deleteProperty(exec, k + additionalArgs - 1);
553             }
554         }
555     }
556     for (unsigned k = 0; k < additionalArgs; ++k)
557         thisObj->put(exec, k + begin, args.at(k + 2));
558 
559     putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - deleteCount + additionalArgs));
560     return result;
561 }
562 
arrayProtoFuncUnShift(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)563 JSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
564 {
565     JSObject* thisObj = thisValue.toThisObject(exec);
566 
567     // 15.4.4.13
568     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
569     unsigned nrArgs = args.size();
570     if (nrArgs) {
571         for (unsigned k = length; k > 0; --k) {
572             if (JSValue v = getProperty(exec, thisObj, k - 1))
573                 thisObj->put(exec, k + nrArgs - 1, v);
574             else
575                 thisObj->deleteProperty(exec, k + nrArgs - 1);
576         }
577     }
578     for (unsigned k = 0; k < nrArgs; ++k)
579         thisObj->put(exec, k, args.at(k));
580     JSValue result = jsNumber(exec, length + nrArgs);
581     putProperty(exec, thisObj, exec->propertyNames().length, result);
582     return result;
583 }
584 
arrayProtoFuncFilter(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)585 JSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
586 {
587     JSObject* thisObj = thisValue.toThisObject(exec);
588 
589     JSValue function = args.at(0);
590     CallData callData;
591     CallType callType = function.getCallData(callData);
592     if (callType == CallTypeNone)
593         return throwError(exec, TypeError);
594 
595     JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec);
596     JSArray* resultArray = constructEmptyArray(exec);
597 
598     unsigned filterIndex = 0;
599     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
600     unsigned k = 0;
601     if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
602         JSFunction* f = asFunction(function);
603         JSArray* array = asArray(thisObj);
604         CachedCall cachedCall(exec, f, 3, exec->exceptionSlot());
605         for (; k < length && !exec->hadException(); ++k) {
606             if (!array->canGetIndex(k))
607                 break;
608             JSValue v = array->getIndex(k);
609             cachedCall.setThis(applyThis);
610             cachedCall.setArgument(0, v);
611             cachedCall.setArgument(1, jsNumber(exec, k));
612             cachedCall.setArgument(2, thisObj);
613 
614             JSValue result = cachedCall.call();
615             if (result.toBoolean(exec))
616                 resultArray->put(exec, filterIndex++, v);
617         }
618         if (k == length)
619             return resultArray;
620     }
621     for (; k < length && !exec->hadException(); ++k) {
622         PropertySlot slot(thisObj);
623 
624         if (!thisObj->getPropertySlot(exec, k, slot))
625             continue;
626 
627         JSValue v = slot.getValue(exec, k);
628 
629         MarkedArgumentBuffer eachArguments;
630 
631         eachArguments.append(v);
632         eachArguments.append(jsNumber(exec, k));
633         eachArguments.append(thisObj);
634 
635         JSValue result = call(exec, function, callType, callData, applyThis, eachArguments);
636 
637         if (result.toBoolean(exec))
638             resultArray->put(exec, filterIndex++, v);
639     }
640     return resultArray;
641 }
642 
arrayProtoFuncMap(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)643 JSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
644 {
645     JSObject* thisObj = thisValue.toThisObject(exec);
646 
647     JSValue function = args.at(0);
648     CallData callData;
649     CallType callType = function.getCallData(callData);
650     if (callType == CallTypeNone)
651         return throwError(exec, TypeError);
652 
653     JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec);
654 
655     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
656 
657     JSArray* resultArray = constructEmptyArray(exec, length);
658     unsigned k = 0;
659     if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
660         JSFunction* f = asFunction(function);
661         JSArray* array = asArray(thisObj);
662         CachedCall cachedCall(exec, f, 3, exec->exceptionSlot());
663         for (; k < length && !exec->hadException(); ++k) {
664             if (UNLIKELY(!array->canGetIndex(k)))
665                 break;
666 
667             cachedCall.setThis(applyThis);
668             cachedCall.setArgument(0, array->getIndex(k));
669             cachedCall.setArgument(1, jsNumber(exec, k));
670             cachedCall.setArgument(2, thisObj);
671 
672             resultArray->JSArray::put(exec, k, cachedCall.call());
673         }
674     }
675     for (; k < length && !exec->hadException(); ++k) {
676         PropertySlot slot(thisObj);
677         if (!thisObj->getPropertySlot(exec, k, slot))
678             continue;
679 
680         JSValue v = slot.getValue(exec, k);
681 
682         MarkedArgumentBuffer eachArguments;
683 
684         eachArguments.append(v);
685         eachArguments.append(jsNumber(exec, k));
686         eachArguments.append(thisObj);
687 
688         JSValue result = call(exec, function, callType, callData, applyThis, eachArguments);
689         resultArray->put(exec, k, result);
690     }
691 
692     return resultArray;
693 }
694 
695 // Documentation for these three is available at:
696 // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every
697 // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach
698 // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some
699 
arrayProtoFuncEvery(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)700 JSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
701 {
702     JSObject* thisObj = thisValue.toThisObject(exec);
703 
704     JSValue function = args.at(0);
705     CallData callData;
706     CallType callType = function.getCallData(callData);
707     if (callType == CallTypeNone)
708         return throwError(exec, TypeError);
709 
710     JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec);
711 
712     JSValue result = jsBoolean(true);
713 
714     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
715     unsigned k = 0;
716     if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
717         JSFunction* f = asFunction(function);
718         JSArray* array = asArray(thisObj);
719         CachedCall cachedCall(exec, f, 3, exec->exceptionSlot());
720         for (; k < length && !exec->hadException(); ++k) {
721             if (UNLIKELY(!array->canGetIndex(k)))
722                 break;
723 
724             cachedCall.setThis(applyThis);
725             cachedCall.setArgument(0, array->getIndex(k));
726             cachedCall.setArgument(1, jsNumber(exec, k));
727             cachedCall.setArgument(2, thisObj);
728 
729             if (!cachedCall.call().toBoolean(exec))
730                 return jsBoolean(false);
731         }
732     }
733     for (; k < length && !exec->hadException(); ++k) {
734         PropertySlot slot(thisObj);
735 
736         if (!thisObj->getPropertySlot(exec, k, slot))
737             continue;
738 
739         MarkedArgumentBuffer eachArguments;
740 
741         eachArguments.append(slot.getValue(exec, k));
742         eachArguments.append(jsNumber(exec, k));
743         eachArguments.append(thisObj);
744 
745         bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec);
746 
747         if (!predicateResult) {
748             result = jsBoolean(false);
749             break;
750         }
751     }
752 
753     return result;
754 }
755 
arrayProtoFuncForEach(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)756 JSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
757 {
758     JSObject* thisObj = thisValue.toThisObject(exec);
759 
760     JSValue function = args.at(0);
761     CallData callData;
762     CallType callType = function.getCallData(callData);
763     if (callType == CallTypeNone)
764         return throwError(exec, TypeError);
765 
766     JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec);
767 
768     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
769     unsigned k = 0;
770     if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
771         JSFunction* f = asFunction(function);
772         JSArray* array = asArray(thisObj);
773         CachedCall cachedCall(exec, f, 3, exec->exceptionSlot());
774         for (; k < length && !exec->hadException(); ++k) {
775             if (UNLIKELY(!array->canGetIndex(k)))
776                 break;
777 
778             cachedCall.setThis(applyThis);
779             cachedCall.setArgument(0, array->getIndex(k));
780             cachedCall.setArgument(1, jsNumber(exec, k));
781             cachedCall.setArgument(2, thisObj);
782 
783             cachedCall.call();
784         }
785     }
786     for (; k < length && !exec->hadException(); ++k) {
787         PropertySlot slot(thisObj);
788         if (!thisObj->getPropertySlot(exec, k, slot))
789             continue;
790 
791         MarkedArgumentBuffer eachArguments;
792         eachArguments.append(slot.getValue(exec, k));
793         eachArguments.append(jsNumber(exec, k));
794         eachArguments.append(thisObj);
795 
796         call(exec, function, callType, callData, applyThis, eachArguments);
797     }
798     return jsUndefined();
799 }
800 
arrayProtoFuncSome(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)801 JSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
802 {
803     JSObject* thisObj = thisValue.toThisObject(exec);
804 
805     JSValue function = args.at(0);
806     CallData callData;
807     CallType callType = function.getCallData(callData);
808     if (callType == CallTypeNone)
809         return throwError(exec, TypeError);
810 
811     JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec);
812 
813     JSValue result = jsBoolean(false);
814 
815     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
816     unsigned k = 0;
817     if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) {
818         JSFunction* f = asFunction(function);
819         JSArray* array = asArray(thisObj);
820         CachedCall cachedCall(exec, f, 3, exec->exceptionSlot());
821         for (; k < length && !exec->hadException(); ++k) {
822             if (UNLIKELY(!array->canGetIndex(k)))
823                 break;
824 
825             cachedCall.setThis(applyThis);
826             cachedCall.setArgument(0, array->getIndex(k));
827             cachedCall.setArgument(1, jsNumber(exec, k));
828             cachedCall.setArgument(2, thisObj);
829 
830             if (cachedCall.call().toBoolean(exec))
831                 return jsBoolean(true);
832         }
833     }
834     for (; k < length && !exec->hadException(); ++k) {
835         PropertySlot slot(thisObj);
836         if (!thisObj->getPropertySlot(exec, k, slot))
837             continue;
838 
839         MarkedArgumentBuffer eachArguments;
840         eachArguments.append(slot.getValue(exec, k));
841         eachArguments.append(jsNumber(exec, k));
842         eachArguments.append(thisObj);
843 
844         bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec);
845 
846         if (predicateResult) {
847             result = jsBoolean(true);
848             break;
849         }
850     }
851     return result;
852 }
853 
arrayProtoFuncReduce(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)854 JSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
855 {
856     JSObject* thisObj = thisValue.toThisObject(exec);
857 
858     JSValue function = args.at(0);
859     CallData callData;
860     CallType callType = function.getCallData(callData);
861     if (callType == CallTypeNone)
862         return throwError(exec, TypeError);
863 
864     unsigned i = 0;
865     JSValue rv;
866     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
867     if (!length && args.size() == 1)
868         return throwError(exec, TypeError);
869     JSArray* array = 0;
870     if (isJSArray(&exec->globalData(), thisObj))
871         array = asArray(thisObj);
872 
873     if (args.size() >= 2)
874         rv = args.at(1);
875     else if (array && array->canGetIndex(0)){
876         rv = array->getIndex(0);
877         i = 1;
878     } else {
879         for (i = 0; i < length; i++) {
880             rv = getProperty(exec, thisObj, i);
881             if (rv)
882                 break;
883         }
884         if (!rv)
885             return throwError(exec, TypeError);
886         i++;
887     }
888 
889     if (callType == CallTypeJS && array) {
890         CachedCall cachedCall(exec, asFunction(function), 4, exec->exceptionSlot());
891         for (; i < length && !exec->hadException(); ++i) {
892             cachedCall.setThis(jsNull());
893             cachedCall.setArgument(0, rv);
894             JSValue v;
895             if (LIKELY(array->canGetIndex(i)))
896                 v = array->getIndex(i);
897             else
898                 break; // length has been made unsafe while we enumerate fallback to slow path
899             cachedCall.setArgument(1, v);
900             cachedCall.setArgument(2, jsNumber(exec, i));
901             cachedCall.setArgument(3, array);
902             rv = cachedCall.call();
903         }
904         if (i == length) // only return if we reached the end of the array
905             return rv;
906     }
907 
908     for (; i < length && !exec->hadException(); ++i) {
909         JSValue prop = getProperty(exec, thisObj, i);
910         if (!prop)
911             continue;
912 
913         MarkedArgumentBuffer eachArguments;
914         eachArguments.append(rv);
915         eachArguments.append(prop);
916         eachArguments.append(jsNumber(exec, i));
917         eachArguments.append(thisObj);
918 
919         rv = call(exec, function, callType, callData, jsNull(), eachArguments);
920     }
921     return rv;
922 }
923 
arrayProtoFuncReduceRight(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)924 JSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
925 {
926     JSObject* thisObj = thisValue.toThisObject(exec);
927 
928     JSValue function = args.at(0);
929     CallData callData;
930     CallType callType = function.getCallData(callData);
931     if (callType == CallTypeNone)
932         return throwError(exec, TypeError);
933 
934     unsigned i = 0;
935     JSValue rv;
936     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
937     if (!length && args.size() == 1)
938         return throwError(exec, TypeError);
939     JSArray* array = 0;
940     if (isJSArray(&exec->globalData(), thisObj))
941         array = asArray(thisObj);
942 
943     if (args.size() >= 2)
944         rv = args.at(1);
945     else if (array && array->canGetIndex(length - 1)){
946         rv = array->getIndex(length - 1);
947         i = 1;
948     } else {
949         for (i = 0; i < length; i++) {
950             rv = getProperty(exec, thisObj, length - i - 1);
951             if (rv)
952                 break;
953         }
954         if (!rv)
955             return throwError(exec, TypeError);
956         i++;
957     }
958 
959     if (callType == CallTypeJS && array) {
960         CachedCall cachedCall(exec, asFunction(function), 4, exec->exceptionSlot());
961         for (; i < length && !exec->hadException(); ++i) {
962             unsigned idx = length - i - 1;
963             cachedCall.setThis(jsNull());
964             cachedCall.setArgument(0, rv);
965             if (UNLIKELY(!array->canGetIndex(idx)))
966                 break; // length has been made unsafe while we enumerate fallback to slow path
967             cachedCall.setArgument(1, array->getIndex(idx));
968             cachedCall.setArgument(2, jsNumber(exec, idx));
969             cachedCall.setArgument(3, array);
970             rv = cachedCall.call();
971         }
972         if (i == length) // only return if we reached the end of the array
973             return rv;
974     }
975 
976     for (; i < length && !exec->hadException(); ++i) {
977         unsigned idx = length - i - 1;
978         JSValue prop = getProperty(exec, thisObj, idx);
979         if (!prop)
980             continue;
981 
982         MarkedArgumentBuffer eachArguments;
983         eachArguments.append(rv);
984         eachArguments.append(prop);
985         eachArguments.append(jsNumber(exec, idx));
986         eachArguments.append(thisObj);
987 
988         rv = call(exec, function, callType, callData, jsNull(), eachArguments);
989     }
990     return rv;
991 }
992 
arrayProtoFuncIndexOf(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)993 JSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
994 {
995     // JavaScript 1.5 Extension by Mozilla
996     // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf
997 
998     JSObject* thisObj = thisValue.toThisObject(exec);
999 
1000     unsigned index = 0;
1001     double d = args.at(1).toInteger(exec);
1002     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
1003     if (d < 0)
1004         d += length;
1005     if (d > 0) {
1006         if (d > length)
1007             index = length;
1008         else
1009             index = static_cast<unsigned>(d);
1010     }
1011 
1012     JSValue searchElement = args.at(0);
1013     for (; index < length; ++index) {
1014         JSValue e = getProperty(exec, thisObj, index);
1015         if (!e)
1016             continue;
1017         if (JSValue::strictEqual(searchElement, e))
1018             return jsNumber(exec, index);
1019     }
1020 
1021     return jsNumber(exec, -1);
1022 }
1023 
arrayProtoFuncLastIndexOf(ExecState * exec,JSObject *,JSValue thisValue,const ArgList & args)1024 JSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
1025 {
1026     // JavaScript 1.6 Extension by Mozilla
1027     // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf
1028 
1029     JSObject* thisObj = thisValue.toThisObject(exec);
1030 
1031     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
1032     int index = length - 1;
1033     double d = args.at(1).toIntegerPreserveNaN(exec);
1034 
1035     if (d < 0) {
1036         d += length;
1037         if (d < 0)
1038             return jsNumber(exec, -1);
1039     }
1040     if (d < length)
1041         index = static_cast<int>(d);
1042 
1043     JSValue searchElement = args.at(0);
1044     for (; index >= 0; --index) {
1045         JSValue e = getProperty(exec, thisObj, index);
1046         if (!e)
1047             continue;
1048         if (JSValue::strictEqual(searchElement, e))
1049             return jsNumber(exec, index);
1050     }
1051 
1052     return jsNumber(exec, -1);
1053 }
1054 
1055 } // namespace JSC
1056