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