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