Home | History | Annotate | Download | only in runtime
      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