1 /* 2 * Copyright (C) 1999-2000 Harri Porten (porten (at) kde.org) 3 * Copyright (C) 2003, 2007, 2008, 2009 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 "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 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 119 ArrayPrototype::ArrayPrototype(NonNullPassRefPtr<Structure> structure) 120 : JSArray(structure) 121 { 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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