File indexing completed on 2024-05-12 15:43:14

0001 /*
0002  *  This file is part of the KDE libraries
0003  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
0004  *  Copyright (C) 2003, 2007 Apple Inc. All rights reserved.
0005  *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
0006  *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
0007  *  Copyright (C) 2008 Janusz Lewandowski (lew21st@gmail.com)
0008  *
0009  *  This library is free software; you can redistribute it and/or
0010  *  modify it under the terms of the GNU Lesser General Public
0011  *  License as published by the Free Software Foundation; either
0012  *  version 2 of the License, or (at your option) any later version.
0013  *
0014  *  This library is distributed in the hope that it will be useful,
0015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017  *  Lesser General Public License for more details.
0018  *
0019  *  You should have received a copy of the GNU Lesser General Public
0020  *  License along with this library; if not, write to the Free Software
0021  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0022  *
0023  */
0024 
0025 #include "array_object.h"
0026 #include "array_object.lut.h"
0027 
0028 #include "error_object.h"
0029 #include "lookup.h"
0030 #include "operations.h"
0031 #include "PropertyNameArray.h"
0032 #include <wtf/HashSet.h>
0033 #include <stdio.h>
0034 
0035 // GCC cstring uses these automatically, but not all implementations do.
0036 using std::strlen;
0037 using std::strcpy;
0038 using std::strncpy;
0039 using std::memset;
0040 using std::memcpy;
0041 
0042 namespace KJS
0043 {
0044 
0045 /**
0046  * @internal
0047  *
0048  * Class to implement all methods that are properties of the
0049  * Object object
0050  */
0051 class ArrayObjectFuncImp : public InternalFunctionImp
0052 {
0053 public:
0054     ArrayObjectFuncImp(ExecState *, FunctionPrototype *, int i, int len, const Identifier &);
0055 
0056     JSValue *callAsFunction(ExecState *, JSObject *thisObj, const List &args) override;
0057 
0058     enum { IsArray };
0059 
0060 private:
0061     int id;
0062 };
0063 
0064 // ------------------------------ ArrayPrototype ----------------------------
0065 
0066 const ClassInfo ArrayPrototype::info = {"Array", &ArrayInstance::info, &arrayTable, nullptr};
0067 
0068 /* Source for array_object.lut.h
0069 @begin arrayTable 16
0070   toString       ArrayProtoFunc::ToString       DontEnum|Function 0
0071   toLocaleString ArrayProtoFunc::ToLocaleString DontEnum|Function 0
0072   concat         ArrayProtoFunc::Concat         DontEnum|Function 1
0073   join           ArrayProtoFunc::Join           DontEnum|Function 1
0074   pop            ArrayProtoFunc::Pop            DontEnum|Function 0
0075   push           ArrayProtoFunc::Push           DontEnum|Function 1
0076   reverse        ArrayProtoFunc::Reverse        DontEnum|Function 0
0077   shift          ArrayProtoFunc::Shift          DontEnum|Function 0
0078   slice          ArrayProtoFunc::Slice          DontEnum|Function 2
0079   sort           ArrayProtoFunc::Sort           DontEnum|Function 1
0080   splice         ArrayProtoFunc::Splice         DontEnum|Function 2
0081   unshift        ArrayProtoFunc::UnShift        DontEnum|Function 1
0082   every          ArrayProtoFunc::Every          DontEnum|Function 1
0083   forEach        ArrayProtoFunc::ForEach        DontEnum|Function 1
0084   some           ArrayProtoFunc::Some           DontEnum|Function 1
0085   indexOf        ArrayProtoFunc::IndexOf        DontEnum|Function 1
0086   lastIndexOf    ArrayProtoFunc::LastIndexOf    DontEnum|Function 1
0087   filter         ArrayProtoFunc::Filter         DontEnum|Function 1
0088   map            ArrayProtoFunc::Map            DontEnum|Function 1
0089   reduce         ArrayProtoFunc::Reduce         DontEnum|Function 1
0090   reduceRight    ArrayProtoFunc::ReduceRight    DontEnum|Function 1
0091 @end
0092 */
0093 
0094 // ECMA 15.4.4
0095 ArrayPrototype::ArrayPrototype(ExecState *, ObjectPrototype *objProto)
0096     : ArrayInstance(objProto, 0)
0097 {
0098 }
0099 
0100 bool ArrayPrototype::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
0101 {
0102     return getStaticFunctionSlot<ArrayProtoFunc, ArrayInstance>(exec, &arrayTable, this, propertyName, slot);
0103 }
0104 
0105 // ------------------------------ ArrayProtoFunc ----------------------------
0106 
0107 ArrayProtoFunc::ArrayProtoFunc(ExecState *exec, int i, int len, const Identifier &name)
0108     : InternalFunctionImp(static_cast<FunctionPrototype *>
0109                           (exec->lexicalInterpreter()->builtinFunctionPrototype()), name)
0110     , id(i)
0111 {
0112     put(exec, exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum);
0113 }
0114 
0115 static JSValue *getProperty(ExecState *exec, JSObject *obj, unsigned index)
0116 {
0117     PropertySlot slot;
0118     if (!obj->getPropertySlot(exec, index, slot)) {
0119         return nullptr;
0120     }
0121     return slot.getValue(exec, obj, index);
0122 }
0123 
0124 // ECMA 15.4.4
0125 JSValue *ArrayProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
0126 {
0127     unsigned length = JSValue::toUInt32(thisObj->get(exec, exec->propertyNames().length), exec);
0128 
0129     JSValue *result = nullptr; // work around gcc 4.0 bug in uninitialized variable warning
0130 
0131     switch (id) {
0132     case ToLocaleString:
0133     case ToString:
0134 
0135         if (!thisObj->inherits(&ArrayInstance::info)) {
0136             return throwError(exec, TypeError);
0137         }
0138 
0139     // fall through
0140     case Join: {
0141         static HashSet<JSObject *> visitedElems;
0142         static const UString *empty = new UString("");
0143         static const UString *comma = new UString(",");
0144         bool alreadyVisited = !visitedElems.add(thisObj).second;
0145         if (alreadyVisited) {
0146             return jsString(*empty);
0147         }
0148         UString separator = *comma;
0149         UString str = *empty;
0150 
0151         if (id == Join && !JSValue::isUndefined(args[0])) {
0152             separator = JSValue::toString(args[0], exec);
0153         }
0154         for (unsigned int k = 0; k < length; k++) {
0155             if (k >= 1) {
0156                 str += separator;
0157             }
0158             if (str.isNull()) {
0159                 JSObject *error = Error::create(exec, GeneralError, "Out of memory");
0160                 exec->setException(error);
0161                 break;
0162             }
0163 
0164             JSValue *element = thisObj->get(exec, k);
0165             if (JSValue::isUndefinedOrNull(element)) {
0166                 continue;
0167             }
0168 
0169             bool fallback = false;
0170             if (id == ToLocaleString) {
0171                 JSObject *o = JSValue::toObject(element, exec);
0172                 JSValue *conversionFunction = o->get(exec, exec->propertyNames().toLocaleString);
0173                 if (JSValue::isObject(conversionFunction) && static_cast<JSObject *>(conversionFunction)->implementsCall()) {
0174                     str += JSValue::toString(static_cast<JSObject *>(conversionFunction)->call(exec, o, List()), exec);
0175                 } else
0176                     // try toString() fallback
0177                 {
0178                     fallback = true;
0179                 }
0180             }
0181 
0182             if (id == ToString || id == Join || fallback) {
0183                 str += JSValue::toString(element, exec);
0184                 if (exec->hadException()) {
0185                     break;
0186                 }
0187 
0188                 if (str.isNull()) {
0189                     JSObject *error = Error::create(exec, GeneralError, "Out of memory");
0190                     exec->setException(error);
0191                 }
0192             }
0193 
0194             if (exec->hadException()) {
0195                 break;
0196             }
0197         }
0198         visitedElems.remove(thisObj);
0199         result = jsString(str);
0200         break;
0201     }
0202     case Concat: {
0203         JSObject *arr = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec, List::empty()));
0204         int n = 0;
0205         JSValue *curArg = thisObj;
0206         JSObject *curObj = static_cast<JSObject *>(thisObj);
0207         ListIterator it = args.begin();
0208         for (;;) {
0209             if (JSValue::isObject(curArg) &&
0210                     curObj->inherits(&ArrayInstance::info)) {
0211                 unsigned int k = 0;
0212                 // Older versions tried to optimize out getting the length of thisObj
0213                 // by checking for n != 0, but that doesn't work if thisObj is an empty array.
0214                 length = JSValue::toUInt32(curObj->get(exec, exec->propertyNames().length), exec);
0215                 while (k < length) {
0216                     if (JSValue *v = getProperty(exec, curObj, k)) {
0217                         arr->put(exec, n, v);
0218                     }
0219                     n++;
0220                     k++;
0221                 }
0222             } else {
0223                 arr->put(exec, n, curArg);
0224                 n++;
0225             }
0226             if (it == args.end()) {
0227                 break;
0228             }
0229             curArg = *it;
0230             curObj = reinterpret_cast<JSObject *>(it++); // may be 0
0231         }
0232         arr->put(exec, exec->propertyNames().length, jsNumber(n), DontEnum | DontDelete);
0233 
0234         result = arr;
0235         break;
0236     }
0237     case Pop: {
0238         if (length == 0) {
0239             thisObj->put(exec, exec->propertyNames().length, jsNumber(length), DontEnum | DontDelete);
0240             result = jsUndefined();
0241         } else {
0242             result = thisObj->get(exec, length - 1);
0243             thisObj->put(exec, exec->propertyNames().length, jsNumber(length - 1), DontEnum | DontDelete);
0244         }
0245         break;
0246     }
0247     case Push: {
0248         for (int n = 0; n < args.size(); n++) {
0249             thisObj->put(exec, length + n, args[n]);
0250         }
0251         length += args.size();
0252         thisObj->put(exec, exec->propertyNames().length, jsNumber(length), DontEnum | DontDelete);
0253         result = jsNumber(length);
0254         break;
0255     }
0256     case Reverse: {
0257 
0258         unsigned int middle = length / 2;
0259 
0260         for (unsigned int k = 0; k < middle; k++) {
0261             unsigned lk1 = length - k - 1;
0262             JSValue *obj2 = getProperty(exec, thisObj, lk1);
0263             JSValue *obj = getProperty(exec, thisObj, k);
0264 
0265             if (obj2) {
0266                 thisObj->put(exec, k, obj2);
0267             } else {
0268                 thisObj->deleteProperty(exec, k);
0269             }
0270 
0271             if (obj) {
0272                 thisObj->put(exec, lk1, obj);
0273             } else {
0274                 thisObj->deleteProperty(exec, lk1);
0275             }
0276         }
0277         result = thisObj;
0278         break;
0279     }
0280     case Shift: {
0281         if (length == 0) {
0282             thisObj->put(exec, exec->propertyNames().length, jsNumber(length), DontEnum | DontDelete);
0283             result = jsUndefined();
0284         } else {
0285             result = thisObj->get(exec, 0);
0286             for (unsigned int k = 1; k < length; k++) {
0287                 if (JSValue *obj = getProperty(exec, thisObj, k)) {
0288                     thisObj->put(exec, k - 1, obj);
0289                 } else {
0290                     thisObj->deleteProperty(exec, k - 1);
0291                 }
0292             }
0293             thisObj->deleteProperty(exec, length - 1);
0294             thisObj->put(exec, exec->propertyNames().length, jsNumber(length - 1), DontEnum | DontDelete);
0295         }
0296         break;
0297     }
0298     case Slice: {
0299         // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
0300 
0301         // We return a new array
0302         JSObject *resObj = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec, List::empty()));
0303         result = resObj;
0304         double begin = 0;
0305         if (!JSValue::isUndefined(args[0])) {
0306             begin = JSValue::toInteger(args[0], exec);
0307             if (begin >= 0) { // false for NaN
0308                 if (begin > length) {
0309                     begin = length;
0310                 }
0311             } else {
0312                 begin += length;
0313                 if (!(begin >= 0)) { // true for NaN
0314                     begin = 0;
0315                 }
0316             }
0317         }
0318         double end = length;
0319         if (!JSValue::isUndefined(args[1])) {
0320             end = JSValue::toInteger(args[1], exec);
0321             if (end < 0) { // false for NaN
0322                 end += length;
0323                 if (end < 0) {
0324                     end = 0;
0325                 }
0326             } else {
0327                 if (!(end <= length)) { // true for NaN
0328                     end = length;
0329                 }
0330             }
0331         }
0332 
0333         //printf( "Slicing from %d to %d \n", begin, end );
0334         int n = 0;
0335         int b = static_cast<int>(begin);
0336         int e = static_cast<int>(end);
0337         for (int k = b; k < e; k++, n++) {
0338             if (JSValue *v = getProperty(exec, thisObj, k)) {
0339                 resObj->put(exec, n, v);
0340             }
0341         }
0342         resObj->put(exec, exec->propertyNames().length, jsNumber(n), DontEnum | DontDelete);
0343         break;
0344     }
0345     case Sort: {
0346 #if 0
0347         printf("KJS Array::Sort length=%d\n", length);
0348         for (unsigned int i = 0; i < length; ++i) {
0349             printf("KJS Array::Sort: %d: %s\n", i, thisObj->get(exec, i)->toString(exec).ascii());
0350         }
0351 #endif
0352         JSObject *sortFunction = nullptr;
0353         if (!JSValue::isUndefined(args[0])) {
0354             sortFunction = JSValue::toObject(args[0], exec);
0355             if (!sortFunction->implementsCall()) {
0356                 sortFunction = nullptr;
0357             }
0358         }
0359 
0360         if (thisObj->classInfo() == &ArrayInstance::info) {
0361             if (sortFunction) {
0362                 ((ArrayInstance *)thisObj)->sort(exec, sortFunction);
0363             } else {
0364                 ((ArrayInstance *)thisObj)->sort(exec);
0365             }
0366             result = thisObj;
0367             break;
0368         }
0369 
0370         if (length == 0) {
0371             thisObj->put(exec, exec->propertyNames().length, jsNumber(0), DontEnum | DontDelete);
0372             result = thisObj;
0373             break;
0374         }
0375 
0376         // "Min" sort. Not the fastest, but definitely less code than heapsort
0377         // or quicksort, and much less swapping than bubblesort/insertionsort.
0378         for (unsigned int i = 0; i < length - 1; ++i) {
0379             JSValue *iObj = thisObj->get(exec, i);
0380             unsigned int themin = i;
0381             JSValue *minObj = iObj;
0382             for (unsigned int j = i + 1; j < length; ++j) {
0383                 JSValue *jObj = thisObj->get(exec, j);
0384                 double cmp;
0385                 if (JSValue::isUndefined(jObj)) {
0386                     cmp = 1; // don't check minObj because there's no need to differentiate == (0) from > (1)
0387                 } else if (JSValue::isUndefined(minObj)) {
0388                     cmp = -1;
0389                 } else if (sortFunction) {
0390                     List l;
0391                     l.append(jObj);
0392                     l.append(minObj);
0393                     cmp = JSValue::toNumber(sortFunction->call(exec, exec->dynamicInterpreter()->globalObject(), l), exec);
0394                 } else {
0395                     cmp = (JSValue::toString(jObj, exec) < JSValue::toString(minObj, exec)) ? -1 : 1;
0396                 }
0397                 if (cmp < 0) {
0398                     themin = j;
0399                     minObj = jObj;
0400                 }
0401             }
0402             // Swap themin and i
0403             if (themin > i) {
0404                 //printf("KJS Array::Sort: swapping %d and %d\n", i, themin );
0405                 thisObj->put(exec, i, minObj);
0406                 thisObj->put(exec, themin, iObj);
0407             }
0408         }
0409 #if 0
0410         printf("KJS Array::Sort -- Resulting array:\n");
0411         for (unsigned int i = 0; i < length; ++i) {
0412             printf("KJS Array::Sort: %d: %s\n", i, thisObj->get(exec, i)->toString(exec).ascii());
0413         }
0414 #endif
0415         result = thisObj;
0416         break;
0417     }
0418     case Splice: {
0419         // 15.4.4.12 - oh boy this is huge
0420         JSObject *resObj = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec, List::empty()));
0421         result = resObj;
0422         double start = JSValue::toInteger(args[0], exec);
0423         uint32_t begin = 0;
0424         if (start < 0) {
0425             begin = static_cast<uint32_t>(std::max<double>(start + length, 0));
0426         } else {
0427             begin = static_cast<uint32_t>(std::min<double>(start, length));
0428         }
0429         uint32_t deleteCount = static_cast<uint32_t>(std::min<double>(std::max<double>(JSValue::toInteger(args[1], exec), 0), length - begin));
0430 
0431         //printf( "Splicing from %d, deleteCount=%d \n", begin, deleteCount );
0432         for (unsigned int k = 0; k < deleteCount; k++) {
0433             if (JSValue *v = getProperty(exec, thisObj, k + begin)) {
0434                 resObj->put(exec, k, v);
0435             }
0436         }
0437         resObj->put(exec, exec->propertyNames().length, jsNumber(deleteCount), DontEnum | DontDelete);
0438 
0439         unsigned int additionalArgs = maxInt(args.size() - 2, 0);
0440         if (additionalArgs != deleteCount) {
0441             if (additionalArgs < deleteCount) {
0442                 for (unsigned int k = begin; k < length - deleteCount; ++k) {
0443                     if (JSValue *v = getProperty(exec, thisObj, k + deleteCount)) {
0444                         thisObj->put(exec, k + additionalArgs, v);
0445                     } else {
0446                         thisObj->deleteProperty(exec, k + additionalArgs);
0447                     }
0448                 }
0449                 for (unsigned int k = length; k > length - deleteCount + additionalArgs; --k) {
0450                     thisObj->deleteProperty(exec, k - 1);
0451                 }
0452             } else {
0453                 for (unsigned int k = length - deleteCount; k > begin; --k) {
0454                     if (JSValue *obj = getProperty(exec, thisObj, k + deleteCount - 1)) {
0455                         thisObj->put(exec, k + additionalArgs - 1, obj);
0456                     } else {
0457                         thisObj->deleteProperty(exec, k + additionalArgs - 1);
0458                     }
0459                 }
0460             }
0461         }
0462         for (unsigned int k = 0; k < additionalArgs; ++k) {
0463             thisObj->put(exec, k + begin, args[k + 2]);
0464         }
0465         thisObj->put(exec, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs), DontEnum | DontDelete);
0466         break;
0467     }
0468     case UnShift: { // 15.4.4.13
0469         unsigned int nrArgs = args.size();
0470         for (unsigned int k = length; k > 0; --k) {
0471             if (JSValue *v = getProperty(exec, thisObj, k - 1)) {
0472                 thisObj->put(exec, k + nrArgs - 1, v);
0473             } else {
0474                 thisObj->deleteProperty(exec, k + nrArgs - 1);
0475             }
0476         }
0477         for (unsigned int k = 0; k < nrArgs; ++k) {
0478             thisObj->put(exec, k, args[k]);
0479         }
0480         result = jsNumber(length + nrArgs);
0481         thisObj->put(exec, exec->propertyNames().length, result, DontEnum | DontDelete);
0482         break;
0483     }
0484     case Filter:
0485     case Map: {
0486         JSObject *eachFunction = JSValue::toObject(args[0], exec);
0487 
0488         if (!eachFunction->implementsCall()) {
0489             return throwError(exec, TypeError);
0490         }
0491 
0492         JSObject *applyThis = JSValue::isUndefinedOrNull(args[1]) ? exec->dynamicInterpreter()->globalObject() :  JSValue::toObject(args[1], exec);
0493         JSObject *resultArray;
0494 
0495         if (id == Filter) {
0496             resultArray = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec, List::empty()));
0497         } else {
0498             List args;
0499             args.append(jsNumber(length));
0500             resultArray = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec, args));
0501         }
0502 
0503         unsigned filterIndex = 0;
0504         for (unsigned k = 0; k < length && !exec->hadException(); ++k) {
0505             PropertySlot slot;
0506 
0507             if (!thisObj->getPropertySlot(exec, k, slot)) {
0508                 continue;
0509             }
0510 
0511             JSValue *v = slot.getValue(exec, thisObj, k);
0512 
0513             List eachArguments;
0514 
0515             eachArguments.append(v);
0516             eachArguments.append(jsNumber(k));
0517             eachArguments.append(thisObj);
0518 
0519             JSValue *result = eachFunction->call(exec, applyThis, eachArguments);
0520 
0521             if (id == Map) {
0522                 resultArray->put(exec, k, result);
0523             } else if (JSValue::toBoolean(result, exec)) {
0524                 resultArray->put(exec, filterIndex++, v);
0525             }
0526         }
0527 
0528         return resultArray;
0529     }
0530     case Every:
0531     case ForEach:
0532     case Some: {
0533         //Documentation for these three is available at:
0534         //http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every
0535         //http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach
0536         //http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some
0537 
0538         JSObject *eachFunction = JSValue::toObject(args[0], exec);
0539 
0540         if (!eachFunction->implementsCall()) {
0541             return throwError(exec, TypeError);
0542         }
0543 
0544         JSObject *applyThis = JSValue::isUndefinedOrNull(args[1]) ? exec->dynamicInterpreter()->globalObject() :  JSValue::toObject(args[1], exec);
0545 
0546         if (id == Some || id == Every) {
0547             result = jsBoolean(id == Every);
0548         } else {
0549             result = jsUndefined();
0550         }
0551 
0552         for (unsigned k = 0; k < length && !exec->hadException(); ++k) {
0553             PropertySlot slot;
0554 
0555             if (!thisObj->getPropertySlot(exec, k, slot)) {
0556                 continue;
0557             }
0558 
0559             List eachArguments;
0560 
0561             eachArguments.append(slot.getValue(exec, thisObj, k));
0562             eachArguments.append(jsNumber(k));
0563             eachArguments.append(thisObj);
0564 
0565             bool predicateResult = JSValue::toBoolean(eachFunction->call(exec, applyThis, eachArguments), exec);
0566 
0567             if (id == Every && !predicateResult) {
0568                 result = jsBoolean(false);
0569                 break;
0570             }
0571             if (id == Some && predicateResult) {
0572                 result = jsBoolean(true);
0573                 break;
0574             }
0575         }
0576         break;
0577     }
0578 
0579     case IndexOf: {
0580         // JavaScript 1.5 Extension by Mozilla
0581         // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf
0582 
0583         unsigned index = 0;
0584         double d = JSValue::toInteger(args[1], exec);
0585         if (d < 0) {
0586             d += length;
0587         }
0588         if (d > 0) {
0589             if (d > length) {
0590                 index = length;
0591             } else {
0592                 index = static_cast<unsigned>(d);
0593             }
0594         }
0595 
0596         JSValue *searchElement = args[0];
0597         for (; index < length; ++index) {
0598             JSValue *e = getProperty(exec, thisObj, index);
0599             if (!e) {
0600                 continue;
0601             }
0602             if (strictEqual(exec, searchElement, e)) {
0603                 return jsNumber(index);
0604             }
0605         }
0606 
0607         return jsNumber(-1);
0608     }
0609 
0610     case LastIndexOf: {
0611         // JavaScript 1.6 Extension by Mozilla
0612         // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf
0613 
0614         int index = length - 1;
0615         double d = JSValue::toIntegerPreserveNaN(args[1], exec);
0616 
0617         if (d < 0) {
0618             d += length;
0619             if (d < 0) {
0620                 return jsNumber(-1);
0621             }
0622         }
0623         if (d < length) {
0624             index = static_cast<int>(d);
0625         }
0626 
0627         JSValue *searchElement = args[0];
0628         for (; index >= 0; --index) {
0629             JSValue *e = getProperty(exec, thisObj, index);
0630             if (!e) {
0631                 continue;
0632             }
0633             if (strictEqual(exec, searchElement, e)) {
0634                 return jsNumber(index);
0635             }
0636         }
0637 
0638         return jsNumber(-1);
0639     }
0640 
0641     case Reduce:
0642     case ReduceRight: {
0643         // JavaScript 1.8 Extensions by Mozilla
0644         // Documentation:
0645         // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
0646         // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
0647 
0648         JSObject *callback = JSValue::toObject(args[0], exec);
0649 
0650         if (!callback->implementsCall()) {
0651             return throwError(exec, TypeError);
0652         }
0653 
0654         JSObject *applyThis = JSValue::isUndefinedOrNull(args[2]) ? exec->dynamicInterpreter()->globalObject() :  JSValue::toObject(args[2], exec);
0655 
0656         if (!length && args.size() < 2) {
0657             return throwError(exec, TypeError);
0658         }
0659 
0660         unsigned k = 0;
0661         unsigned last = length - 1;
0662 
0663         if (args.size() >= 2) {
0664             result = args[1];
0665         } else {
0666             for (; k < length && !exec->hadException(); ++k) {
0667                 PropertySlot slot;
0668 
0669                 if (!thisObj->getPropertySlot(exec, (id == Reduce) ? k : (last - k), slot)) {
0670                     continue;
0671                 }
0672 
0673                 result = slot.getValue(exec, thisObj, (id == Reduce) ? k++ : (last - k++));
0674                 break;
0675             }
0676         }
0677 
0678         for (; k < length && !exec->hadException(); ++k) {
0679             PropertySlot slot;
0680 
0681             if (!thisObj->getPropertySlot(exec, (id == Reduce) ? k : (last - k), slot)) {
0682                 continue;
0683             }
0684 
0685             JSValue *v = slot.getValue(exec, thisObj, (id == Reduce) ? k : (last - k));
0686 
0687             List eachArguments;
0688 
0689             eachArguments.append(result);
0690             eachArguments.append(v);
0691             eachArguments.append(jsNumber((id == Reduce) ? k : (last - k)));
0692             eachArguments.append(thisObj);
0693 
0694             result = callback->call(exec, applyThis, eachArguments);
0695         }
0696 
0697         break;
0698     }
0699 
0700     default:
0701         assert(0);
0702         result = nullptr;
0703         break;
0704     }
0705     return result;
0706 }
0707 
0708 // ------------------------------ ArrayObjectImp -------------------------------
0709 
0710 ArrayObjectImp::ArrayObjectImp(ExecState *exec,
0711                                FunctionPrototype *funcProto,
0712                                ArrayPrototype *arrayProto)
0713     : InternalFunctionImp(funcProto)
0714 {
0715     static const Identifier *isArrayName = new Identifier("isArray");
0716 
0717     // ECMA 15.4.3.1 Array.prototype
0718     put(exec, exec->propertyNames().prototype, arrayProto, DontEnum | DontDelete | ReadOnly);
0719 
0720     putDirectFunction(new ArrayObjectFuncImp(exec, funcProto, ArrayObjectFuncImp::IsArray, 1, *isArrayName), DontEnum);
0721 
0722     // no. of arguments for constructor
0723     put(exec, exec->propertyNames().length, jsNumber(1), ReadOnly | DontDelete | DontEnum);
0724 }
0725 
0726 bool ArrayObjectImp::implementsConstruct() const
0727 {
0728     return true;
0729 }
0730 
0731 // ECMA 15.4.2
0732 JSObject *ArrayObjectImp::construct(ExecState *exec, const List &args)
0733 {
0734     // a single numeric argument denotes the array size (!)
0735     if (args.size() == 1 && JSValue::isNumber(args[0])) {
0736         uint32_t n = JSValue::toUInt32(args[0], exec);
0737         if (n != JSValue::toNumber(args[0], exec)) {
0738             return throwError(exec, RangeError, "Array size is not a small enough positive integer.");
0739         }
0740         return new ArrayInstance(exec->lexicalInterpreter()->builtinArrayPrototype(), n);
0741     }
0742 
0743     // otherwise the array is constructed with the arguments in it
0744     return new ArrayInstance(exec->lexicalInterpreter()->builtinArrayPrototype(), args);
0745 }
0746 
0747 // ECMA 15.6.1
0748 JSValue *ArrayObjectImp::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List &args)
0749 {
0750     // equivalent to 'new Array(....)'
0751     return construct(exec, args);
0752 }
0753 
0754 // ------------------------------ ArrayObjectFuncImp ----------------------------
0755 
0756 ArrayObjectFuncImp::ArrayObjectFuncImp(ExecState *exec, FunctionPrototype *funcProto, int i, int len, const Identifier &name)
0757     : InternalFunctionImp(funcProto, name), id(i)
0758 {
0759     putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum);
0760 }
0761 
0762 JSValue *ArrayObjectFuncImp::callAsFunction(ExecState * /*exec*/, JSObject *, const List &args)
0763 {
0764     switch (id) {
0765     case IsArray: {
0766         JSObject *jso = JSValue::getObject(args[0]);
0767         if (!jso) {
0768             return jsBoolean(false);
0769         }
0770         return jsBoolean(jso->inherits(&ArrayInstance::info));
0771     }
0772     default:
0773         return jsUndefined();
0774     }
0775 }
0776 
0777 }
0778