1 /* 2 * Copyright (C) 1999-2000 Harri Porten (porten (at) kde.org) 3 * Copyright (C) 2003, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. 4 * Copyright (C) 2003 Peter Kelly (pmk (at) post.com) 5 * Copyright (C) 2006 Alexey Proskuryakov (ap (at) 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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