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