2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007 Apple Inc. All rights reserved.
4 * Copyright (C) 2003 Peter Kelly (pmk@post.com)
5 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
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.
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.
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
25 #include "array_object.h"
26 #include "array_object.lut.h"
28 #include "error_object.h"
30 #include "operations.h"
32 #include <wtf/Assertions.h>
33 #include <wtf/HashSet.h>
37 // ------------------------------ ArrayPrototype ----------------------------
39 const ClassInfo ArrayPrototype::info = {"Array", &ArrayInstance::info, &arrayTable};
41 /* Source for array_object.lut.h
43 toString ArrayProtoFunc::ToString DontEnum|Function 0
44 toLocaleString ArrayProtoFunc::ToLocaleString DontEnum|Function 0
45 concat ArrayProtoFunc::Concat DontEnum|Function 1
46 join ArrayProtoFunc::Join DontEnum|Function 1
47 pop ArrayProtoFunc::Pop DontEnum|Function 0
48 push ArrayProtoFunc::Push DontEnum|Function 1
49 reverse ArrayProtoFunc::Reverse DontEnum|Function 0
50 shift ArrayProtoFunc::Shift DontEnum|Function 0
51 slice ArrayProtoFunc::Slice DontEnum|Function 2
52 sort ArrayProtoFunc::Sort DontEnum|Function 1
53 splice ArrayProtoFunc::Splice DontEnum|Function 2
54 unshift ArrayProtoFunc::UnShift DontEnum|Function 1
55 every ArrayProtoFunc::Every DontEnum|Function 1
56 forEach ArrayProtoFunc::ForEach DontEnum|Function 1
57 some ArrayProtoFunc::Some DontEnum|Function 1
58 indexOf ArrayProtoFunc::IndexOf DontEnum|Function 1
59 lastIndexOf ArrayProtoFunc::LastIndexOf DontEnum|Function 1
60 filter ArrayProtoFunc::Filter DontEnum|Function 1
61 map ArrayProtoFunc::Map DontEnum|Function 1
66 ArrayPrototype::ArrayPrototype(ExecState*, ObjectPrototype* objProto)
67 : ArrayInstance(objProto, 0)
71 bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
73 return getStaticFunctionSlot<ArrayProtoFunc, ArrayInstance>(exec, &arrayTable, this, propertyName, slot);
76 // ------------------------------ ArrayProtoFunc ----------------------------
78 ArrayProtoFunc::ArrayProtoFunc(ExecState* exec, int i, int len, const Identifier& name)
79 : InternalFunctionImp(static_cast<FunctionPrototype*>
80 (exec->lexicalInterpreter()->builtinFunctionPrototype()), name)
83 put(exec, exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum);
86 static JSValue *getProperty(ExecState *exec, JSObject *obj, unsigned index)
89 if (!obj->getPropertySlot(exec, index, slot))
91 return slot.getValue(exec, obj, index);
95 JSValue* ArrayProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
97 unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
99 JSValue *result = 0; // work around gcc 4.0 bug in uninitialized variable warning
105 if (!thisObj->inherits(&ArrayInstance::info))
106 return throwError(exec, TypeError);
110 static HashSet<JSObject*> visitedElems;
111 static const UString* empty = new UString("");
112 static const UString* comma = new UString(",");
113 bool alreadyVisited = !visitedElems.add(thisObj).second;
115 return jsString(*empty);
116 UString separator = *comma;
117 UString str = *empty;
119 if (id == Join && !args[0]->isUndefined())
120 separator = args[0]->toString(exec);
121 for (unsigned int k = 0; k < length; k++) {
125 JSObject *error = Error::create(exec, GeneralError, "Out of memory");
126 exec->setException(error);
130 JSValue* element = thisObj->get(exec, k);
131 if (element->isUndefinedOrNull())
134 bool fallback = false;
135 if (id == ToLocaleString) {
136 JSObject* o = element->toObject(exec);
137 JSValue* conversionFunction = o->get(exec, exec->propertyNames().toLocaleString);
138 if (conversionFunction->isObject() && static_cast<JSObject*>(conversionFunction)->implementsCall()) {
140 str += static_cast<JSObject*>(conversionFunction)->call(exec, o, args)->toString(exec);
142 // try toString() fallback
147 if (id == ToString || id == Join || fallback)
148 str += element->toString(exec);
151 JSObject *error = Error::create(exec, GeneralError, "Out of memory");
152 exec->setException(error);
155 if (exec->hadException())
158 visitedElems.remove(thisObj);
159 result = jsString(str);
163 JSObject *arr = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec,List::empty()));
165 JSValue *curArg = thisObj;
166 JSObject *curObj = static_cast<JSObject *>(thisObj);
167 List::const_iterator it = args.begin();
168 List::const_iterator end = args.end();
170 if (curArg->isObject() &&
171 curObj->inherits(&ArrayInstance::info)) {
173 // Older versions tried to optimize out getting the length of thisObj
174 // by checking for n != 0, but that doesn't work if thisObj is an empty array.
175 length = curObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
177 if (JSValue *v = getProperty(exec, curObj, k))
178 arr->put(exec, n, v);
183 arr->put(exec, n, curArg);
189 curObj = static_cast<JSObject*>(curArg); // may be 0
192 arr->put(exec, exec->propertyNames().length, jsNumber(n), DontEnum | DontDelete);
199 thisObj->put(exec, exec->propertyNames().length, jsNumber(length), DontEnum | DontDelete);
200 result = jsUndefined();
202 result = thisObj->get(exec, length - 1);
203 thisObj->put(exec, exec->propertyNames().length, jsNumber(length - 1), DontEnum | DontDelete);
208 for (unsigned int n = 0; n < args.size(); n++)
209 thisObj->put(exec, length + n, args[n]);
210 length += args.size();
211 thisObj->put(exec, exec->propertyNames().length, jsNumber(length), DontEnum | DontDelete);
212 result = jsNumber(length);
217 unsigned int middle = length / 2;
219 for (unsigned int k = 0; k < middle; k++) {
220 unsigned lk1 = length - k - 1;
221 JSValue *obj2 = getProperty(exec, thisObj, lk1);
222 JSValue *obj = getProperty(exec, thisObj, k);
225 thisObj->put(exec, k, obj2);
227 thisObj->deleteProperty(exec, k);
230 thisObj->put(exec, lk1, obj);
232 thisObj->deleteProperty(exec, lk1);
239 thisObj->put(exec, exec->propertyNames().length, jsNumber(length), DontEnum | DontDelete);
240 result = jsUndefined();
242 result = thisObj->get(exec, 0);
243 for(unsigned int k = 1; k < length; k++) {
244 if (JSValue *obj = getProperty(exec, thisObj, k))
245 thisObj->put(exec, k-1, obj);
247 thisObj->deleteProperty(exec, k-1);
249 thisObj->deleteProperty(exec, length - 1);
250 thisObj->put(exec, exec->propertyNames().length, jsNumber(length - 1), DontEnum | DontDelete);
255 // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
257 // We return a new array
258 JSObject *resObj = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec,List::empty()));
260 double begin = args[0]->toInteger(exec);
270 if (args[1]->isUndefined())
273 end = args[1]->toInteger(exec);
285 int b = static_cast<int>(begin);
286 int e = static_cast<int>(end);
287 for(int k = b; k < e; k++, n++) {
288 if (JSValue *v = getProperty(exec, thisObj, k))
289 resObj->put(exec, n, v);
291 resObj->put(exec, exec->propertyNames().length, jsNumber(n), DontEnum | DontDelete);
296 printf("KJS Array::Sort length=%d\n", length);
297 for ( unsigned int i = 0 ; i<length ; ++i )
298 printf("KJS Array::Sort: %d: %s\n", i, thisObj->get(exec, i)->toString(exec).ascii() );
300 JSObject *sortFunction = NULL;
301 if (!args[0]->isUndefined())
303 sortFunction = args[0]->toObject(exec);
304 if (!sortFunction->implementsCall())
308 if (thisObj->classInfo() == &ArrayInstance::info) {
310 ((ArrayInstance *)thisObj)->sort(exec, sortFunction);
312 ((ArrayInstance *)thisObj)->sort(exec);
318 thisObj->put(exec, exec->propertyNames().length, jsNumber(0), DontEnum | DontDelete);
323 // "Min" sort. Not the fastest, but definitely less code than heapsort
324 // or quicksort, and much less swapping than bubblesort/insertionsort.
325 for ( unsigned int i = 0 ; i<length-1 ; ++i )
327 JSValue *iObj = thisObj->get(exec,i);
328 unsigned int themin = i;
329 JSValue *minObj = iObj;
330 for ( unsigned int j = i+1 ; j<length ; ++j )
332 JSValue *jObj = thisObj->get(exec,j);
334 if (jObj->isUndefined()) {
335 cmp = 1; // don't check minObj because there's no need to differentiate == (0) from > (1)
336 } else if (minObj->isUndefined()) {
338 } else if (sortFunction) {
342 cmp = sortFunction->call(exec, exec->dynamicInterpreter()->globalObject(), l)->toNumber(exec);
344 cmp = (jObj->toString(exec) < minObj->toString(exec)) ? -1 : 1;
355 //printf("KJS Array::Sort: swapping %d and %d\n", i, themin );
356 thisObj->put( exec, i, minObj );
357 thisObj->put( exec, themin, iObj );
361 printf("KJS Array::Sort -- Resulting array:\n");
362 for ( unsigned int i = 0 ; i<length ; ++i )
363 printf("KJS Array::Sort: %d: %s\n", i, thisObj->get(exec, i)->toString(exec).ascii() );
369 // 15.4.4.12 - oh boy this is huge
370 JSObject *resObj = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec,List::empty()));
372 int begin = args[0]->toUInt32(exec);
374 begin = maxInt( begin + length, 0 );
376 begin = minInt( begin, length );
377 unsigned int deleteCount = minInt( maxInt( args[1]->toUInt32(exec), 0 ), length - begin );
379 //printf( "Splicing from %d, deleteCount=%d \n", begin, deleteCount );
380 for(unsigned int k = 0; k < deleteCount; k++) {
381 if (JSValue *v = getProperty(exec, thisObj, k+begin))
382 resObj->put(exec, k, v);
384 resObj->put(exec, exec->propertyNames().length, jsNumber(deleteCount), DontEnum | DontDelete);
386 unsigned int additionalArgs = maxInt( args.size() - 2, 0 );
387 if ( additionalArgs != deleteCount )
389 if ( additionalArgs < deleteCount )
391 for ( unsigned int k = begin; k < length - deleteCount; ++k )
393 if (JSValue *v = getProperty(exec, thisObj, k+deleteCount))
394 thisObj->put(exec, k+additionalArgs, v);
396 thisObj->deleteProperty(exec, k+additionalArgs);
398 for ( unsigned int k = length ; k > length - deleteCount + additionalArgs; --k )
399 thisObj->deleteProperty(exec, k-1);
403 for ( unsigned int k = length - deleteCount; (int)k > begin; --k )
405 if (JSValue *obj = getProperty(exec, thisObj, k + deleteCount - 1))
406 thisObj->put(exec, k + additionalArgs - 1, obj);
408 thisObj->deleteProperty(exec, k+additionalArgs-1);
412 for ( unsigned int k = 0; k < additionalArgs; ++k )
414 thisObj->put(exec, k+begin, args[k+2]);
416 thisObj->put(exec, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs), DontEnum | DontDelete);
419 case UnShift: { // 15.4.4.13
420 unsigned int nrArgs = args.size();
421 for ( unsigned int k = length; k > 0; --k )
423 if (JSValue *v = getProperty(exec, thisObj, k - 1))
424 thisObj->put(exec, k+nrArgs-1, v);
426 thisObj->deleteProperty(exec, k+nrArgs-1);
428 for ( unsigned int k = 0; k < nrArgs; ++k )
429 thisObj->put(exec, k, args[k]);
430 result = jsNumber(length + nrArgs);
431 thisObj->put(exec, exec->propertyNames().length, result, DontEnum | DontDelete);
436 JSObject *eachFunction = args[0]->toObject(exec);
438 if (!eachFunction->implementsCall())
439 return throwError(exec, TypeError);
441 JSObject *applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicInterpreter()->globalObject() : args[1]->toObject(exec);
442 JSObject *resultArray;
445 resultArray = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec, List::empty()));
448 args.append(jsNumber(length));
449 resultArray = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec, args));
452 unsigned filterIndex = 0;
453 for (unsigned k = 0; k < length && !exec->hadException(); ++k) {
456 if (!thisObj->getPropertySlot(exec, k, slot))
459 JSValue *v = slot.getValue(exec, thisObj, k);
463 eachArguments.append(v);
464 eachArguments.append(jsNumber(k));
465 eachArguments.append(thisObj);
467 JSValue *result = eachFunction->call(exec, applyThis, eachArguments);
470 resultArray->put(exec, k, result);
471 else if (result->toBoolean(exec))
472 resultArray->put(exec, filterIndex++, v);
480 //Documentation for these three is available at:
481 //http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every
482 //http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach
483 //http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some
485 JSObject *eachFunction = args[0]->toObject(exec);
487 if (!eachFunction->implementsCall())
488 return throwError(exec, TypeError);
490 JSObject *applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicInterpreter()->globalObject() : args[1]->toObject(exec);
492 if (id == Some || id == Every)
493 result = jsBoolean(id == Every);
495 result = jsUndefined();
497 for (unsigned k = 0; k < length && !exec->hadException(); ++k) {
500 if (!thisObj->getPropertySlot(exec, k, slot))
505 eachArguments.append(slot.getValue(exec, thisObj, k));
506 eachArguments.append(jsNumber(k));
507 eachArguments.append(thisObj);
509 bool predicateResult = eachFunction->call(exec, applyThis, eachArguments)->toBoolean(exec);
511 if (id == Every && !predicateResult) {
512 result = jsBoolean(false);
515 if (id == Some && predicateResult) {
516 result = jsBoolean(true);
524 // JavaScript 1.5 Extension by Mozilla
525 // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf
528 double d = args[1]->toInteger(exec);
535 index = static_cast<unsigned>(d);
538 JSValue* searchElement = args[0];
539 for (; index < length; ++index) {
540 JSValue* e = getProperty(exec, thisObj, index);
543 if (strictEqual(exec, searchElement, e))
544 return jsNumber(index);
550 // JavaScript 1.6 Extension by Mozilla
551 // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf
553 int index = length - 1;
554 double d = args[1]->toIntegerPreserveNaN(exec);
562 index = static_cast<int>(d);
564 JSValue* searchElement = args[0];
565 for (; index >= 0; --index) {
566 JSValue* e = getProperty(exec, thisObj, index);
569 if (strictEqual(exec, searchElement, e))
570 return jsNumber(index);
583 // ------------------------------ ArrayObjectImp -------------------------------
585 ArrayObjectImp::ArrayObjectImp(ExecState *exec,
586 FunctionPrototype *funcProto,
587 ArrayPrototype *arrayProto)
588 : InternalFunctionImp(funcProto)
590 // ECMA 15.4.3.1 Array.prototype
591 put(exec, exec->propertyNames().prototype, arrayProto, DontEnum|DontDelete|ReadOnly);
593 // no. of arguments for constructor
594 put(exec, exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum);
597 bool ArrayObjectImp::implementsConstruct() const
603 JSObject *ArrayObjectImp::construct(ExecState *exec, const List &args)
605 // a single numeric argument denotes the array size (!)
606 if (args.size() == 1 && args[0]->isNumber()) {
607 uint32_t n = args[0]->toUInt32(exec);
608 if (n != args[0]->toNumber(exec))
609 return throwError(exec, RangeError, "Array size is not a small enough positive integer.");
610 return new ArrayInstance(exec->lexicalInterpreter()->builtinArrayPrototype(), n);
613 // otherwise the array is constructed with the arguments in it
614 return new ArrayInstance(exec->lexicalInterpreter()->builtinArrayPrototype(), args);
618 JSValue *ArrayObjectImp::callAsFunction(ExecState *exec, JSObject *, const List &args)
620 // equivalent to 'new Array(....)'
621 return construct(exec,args);