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