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