Patch from Harri, reviewed by me.
[WebKit-https.git] / JavaScriptCore / kjs / array_object.cpp
1 // -*- c-basic-offset: 2 -*-
2 /*
3  *  This file is part of the KDE libraries
4  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
5  *  Copyright (C) 2003 Apple Computer, Inc.
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #include "value.h"
24 #include "object.h"
25 #include "types.h"
26 #include "interpreter.h"
27 #include "operations.h"
28 #include "array_object.h"
29 #include "internal.h"
30 #include "error_object.h"
31
32 #include "array_object.lut.h"
33
34 #include <stdio.h>
35 #include <assert.h>
36
37 using namespace KJS;
38
39 // ------------------------------ ArrayInstanceImp -----------------------------
40
41 const unsigned sparseArrayCutoff = 10000;
42
43 const ClassInfo ArrayInstanceImp::info = {"Array", 0, 0, 0};
44
45 ArrayInstanceImp::ArrayInstanceImp(ObjectImp *proto, unsigned initialLength)
46   : ObjectImp(proto)
47   , length(initialLength)
48   , storageLength(initialLength)
49   , capacity(storageLength)
50   , storage(capacity ? (ValueImp **)calloc(capacity, sizeof(ValueImp *)) : 0)
51 {
52 }
53
54 ArrayInstanceImp::ArrayInstanceImp(ObjectImp *proto, const List &list)
55   : ObjectImp(proto)
56   , length(list.size())
57   , storageLength(length)
58   , capacity(storageLength)
59   , storage(capacity ? (ValueImp **)malloc(sizeof(ValueImp *) * capacity) : 0)
60 {
61   ListIterator it = list.begin();
62   unsigned l = length;
63   for (unsigned i = 0; i < l; ++i) {
64     storage[i] = (it++).imp();
65   }
66 }
67
68 ArrayInstanceImp::~ArrayInstanceImp()
69 {
70   free(storage);
71 }
72
73 Value ArrayInstanceImp::get(ExecState *exec, const Identifier &propertyName) const
74 {
75   if (propertyName == lengthPropertyName)
76     return Number(length);
77
78   bool ok;
79   unsigned index = propertyName.toArrayIndex(&ok);
80   if (ok) {
81     if (index >= length)
82       return Undefined();
83     if (index < storageLength) {
84       ValueImp *v = storage[index];
85       return v ? Value(v) : Undefined();
86     }
87   }
88
89   return ObjectImp::get(exec, propertyName);
90 }
91
92 Value ArrayInstanceImp::get(ExecState *exec, unsigned index) const
93 {
94   if (index >= length)
95     return Undefined();
96   if (index < storageLength) {
97     ValueImp *v = storage[index];
98     return v ? Value(v) : Undefined();
99   }
100   
101   return ObjectImp::get(exec, Identifier::from(index));
102 }
103
104 // Special implementation of [[Put]] - see ECMA 15.4.5.1
105 void ArrayInstanceImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
106 {
107   if (propertyName == lengthPropertyName) {
108     setLength(value.toUInt32(exec), exec);
109     return;
110   }
111   
112   bool ok;
113   unsigned index = propertyName.toArrayIndex(&ok);
114   if (ok) {
115     put(exec, index, value, attr);
116     return;
117   }
118   
119   ObjectImp::put(exec, propertyName, value, attr);
120 }
121
122 void ArrayInstanceImp::put(ExecState *exec, unsigned index, const Value &value, int attr)
123 {
124   if (index < sparseArrayCutoff && index >= storageLength) {
125     resizeStorage(index + 1);
126   }
127
128   if (index >= length) {
129     length = index + 1;
130   }
131
132   if (index < storageLength) {
133     storage[index] = value.imp();
134     return;
135   }
136   
137   assert(index >= sparseArrayCutoff);
138   ObjectImp::put(exec, Identifier::from(index), value, attr);
139 }
140
141 bool ArrayInstanceImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
142 {
143   if (propertyName == lengthPropertyName)
144     return true;
145   
146   bool ok;
147   unsigned index = propertyName.toArrayIndex(&ok);
148   if (ok) {
149     if (index >= length)
150       return false;
151     if (index < storageLength) {
152       ValueImp *v = storage[index];
153       return v && v != UndefinedImp::staticUndefined;
154     }
155   }
156   
157   return ObjectImp::hasProperty(exec, propertyName);
158 }
159
160 bool ArrayInstanceImp::hasProperty(ExecState *exec, unsigned index) const
161 {
162   if (index >= length)
163     return false;
164   if (index < storageLength) {
165     ValueImp *v = storage[index];
166     return v && v != UndefinedImp::staticUndefined;
167   }
168   
169   return ObjectImp::hasProperty(exec, Identifier::from(index));
170 }
171
172 bool ArrayInstanceImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
173 {
174   if (propertyName == lengthPropertyName)
175     return false;
176   
177   bool ok;
178   uint32_t index = propertyName.toUInt32(&ok);
179   if (ok) {
180     if (index >= length)
181       return true;
182     if (index < storageLength) {
183       storage[index] = 0;
184       return true;
185     }
186   }
187   
188   return ObjectImp::deleteProperty(exec, propertyName);
189 }
190
191 bool ArrayInstanceImp::deleteProperty(ExecState *exec, unsigned index)
192 {
193   if (index >= length)
194     return true;
195   if (index < storageLength) {
196     storage[index] = 0;
197     return true;
198   }
199   
200   return ObjectImp::deleteProperty(exec, Identifier::from(index));
201 }
202
203 ReferenceList ArrayInstanceImp::propList(ExecState *exec, bool recursive)
204 {
205   ReferenceList properties = ObjectImp::propList(exec,recursive);
206
207   // avoid fetching this every time through the loop
208   ValueImp *undefined = UndefinedImp::staticUndefined;
209
210   for (unsigned i = 0; i < storageLength; ++i) {
211     ValueImp *imp = storage[i];
212     if (imp && imp != undefined) {
213       properties.append(Reference(this, i));
214     }
215   }
216   return properties;
217 }
218
219 void ArrayInstanceImp::resizeStorage(unsigned newLength)
220 {
221     if (newLength < storageLength) {
222       memset(storage + newLength, 0, sizeof(ValueImp *) * (storageLength - newLength));
223     }
224     if (newLength > capacity) {
225       unsigned newCapacity;
226       if (newLength > sparseArrayCutoff) {
227         newCapacity = newLength;
228       } else {
229         newCapacity = (newLength * 3 + 1) / 2;
230         if (newCapacity > sparseArrayCutoff) {
231           newCapacity = sparseArrayCutoff;
232         }
233       }
234       storage = (ValueImp **)realloc(storage, newCapacity * sizeof (ValueImp *));
235       memset(storage + capacity, 0, sizeof(ValueImp *) * (newCapacity - capacity));
236       capacity = newCapacity;
237     }
238     storageLength = newLength;
239 }
240
241 void ArrayInstanceImp::setLength(unsigned newLength, ExecState *exec)
242 {
243   if (newLength <= storageLength) {
244     resizeStorage(newLength);
245   }
246
247   if (newLength < length) {
248     ReferenceList sparseProperties;
249     
250     _prop.addSparseArrayPropertiesToReferenceList(sparseProperties, Object(this));
251     
252     ReferenceListIterator it = sparseProperties.begin();
253     while (it != sparseProperties.end()) {
254       Reference ref = it++;
255       bool ok;
256       unsigned index = ref.getPropertyName(exec).toArrayIndex(&ok);
257       if (ok && index > newLength) {
258         ref.deleteValue(exec);
259       }
260     }
261   }
262   
263   length = newLength;
264 }
265
266 void ArrayInstanceImp::mark()
267 {
268   ObjectImp::mark();
269   unsigned l = storageLength;
270   for (unsigned i = 0; i < l; ++i) {
271     ValueImp *imp = storage[i];
272     if (imp && !imp->marked())
273       imp->mark();
274   }
275 }
276
277 static ExecState *execForCompareByStringForQSort;
278
279 static int compareByStringForQSort(const void *a, const void *b)
280 {
281     ExecState *exec = execForCompareByStringForQSort;
282     ValueImp *va = *(ValueImp **)a;
283     ValueImp *vb = *(ValueImp **)b;
284     if (va->dispatchType() == UndefinedType) {
285         return vb->dispatchType() == UndefinedType ? 0 : 1;
286     }
287     if (vb->dispatchType() == UndefinedType) {
288         return -1;
289     }
290     return compare(va->dispatchToString(exec), vb->dispatchToString(exec));
291 }
292
293 void ArrayInstanceImp::sort(ExecState *exec)
294 {
295     int lengthNotIncludingUndefined = pushUndefinedObjectsToEnd(exec);
296     
297     execForCompareByStringForQSort = exec;
298     qsort(storage, lengthNotIncludingUndefined, sizeof(ValueImp *), compareByStringForQSort);
299     execForCompareByStringForQSort = 0;
300 }
301
302 struct CompareWithCompareFunctionArguments {
303     CompareWithCompareFunctionArguments(ExecState *e, ObjectImp *cf)
304         : exec(e)
305         , compareFunction(cf)
306         , globalObject(e->interpreter()->globalObject())
307     {
308         arguments.append(Undefined());
309         arguments.append(Undefined());
310     }
311
312     ExecState *exec;
313     ObjectImp *compareFunction;
314     List arguments;
315     Object globalObject;
316 };
317
318 static CompareWithCompareFunctionArguments *compareWithCompareFunctionArguments;
319
320 static int compareWithCompareFunctionForQSort(const void *a, const void *b)
321 {
322     CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments;
323
324     ValueImp *va = *(ValueImp **)a;
325     ValueImp *vb = *(ValueImp **)b;
326     if (va->dispatchType() == UndefinedType) {
327         return vb->dispatchType() == UndefinedType ? 0 : 1;
328     }
329     if (vb->dispatchType() == UndefinedType) {
330         return -1;
331     }
332
333     args->arguments.clear();
334     args->arguments.append(va);
335     args->arguments.append(vb);
336     double compareResult = args->compareFunction->call
337         (args->exec, args->globalObject, args->arguments).toNumber(args->exec);
338     return compareResult < 0 ? -1 : compareResult > 0 ? 1 : 0;
339 }
340
341 void ArrayInstanceImp::sort(ExecState *exec, Object &compareFunction)
342 {
343     int lengthNotIncludingUndefined = pushUndefinedObjectsToEnd(exec);
344     
345     CompareWithCompareFunctionArguments args(exec, compareFunction.imp());
346     compareWithCompareFunctionArguments = &args;
347     qsort(storage, lengthNotIncludingUndefined, sizeof(ValueImp *), compareWithCompareFunctionForQSort);
348     compareWithCompareFunctionArguments = 0;
349 }
350
351 unsigned ArrayInstanceImp::pushUndefinedObjectsToEnd(ExecState *exec)
352 {
353     ValueImp *undefined = UndefinedImp::staticUndefined;
354
355     unsigned o = 0;
356     
357     for (unsigned i = 0; i != storageLength; ++i) {
358         ValueImp *v = storage[i];
359         if (v && v != undefined) {
360             if (o != i)
361                 storage[o] = v;
362             o++;
363         }
364     }
365     
366     ReferenceList sparseProperties;
367     _prop.addSparseArrayPropertiesToReferenceList(sparseProperties, Object(this));
368     unsigned newLength = o + sparseProperties.length();
369
370     if (newLength > storageLength) {
371       resizeStorage(newLength);
372     } 
373
374     ReferenceListIterator it = sparseProperties.begin();
375     while (it != sparseProperties.end()) {
376       Reference ref = it++;
377       storage[o] = ref.getValue(exec).imp();
378       ObjectImp::deleteProperty(exec, ref.getPropertyName(exec));
379       o++;
380     }
381     
382     if (newLength != storageLength)
383         memset(storage + o, 0, sizeof(ValueImp *) * (storageLength - o));
384     
385     return o;
386 }
387
388 // ------------------------------ ArrayPrototypeImp ----------------------------
389
390 const ClassInfo ArrayPrototypeImp::info = {"Array", &ArrayInstanceImp::info, &arrayTable, 0};
391
392 /* Source for array_object.lut.h
393 @begin arrayTable 13
394   toString       ArrayProtoFuncImp::ToString       DontEnum|Function 0
395   toLocaleString ArrayProtoFuncImp::ToLocaleString DontEnum|Function 0
396   concat         ArrayProtoFuncImp::Concat         DontEnum|Function 1
397   join           ArrayProtoFuncImp::Join           DontEnum|Function 1
398   pop            ArrayProtoFuncImp::Pop            DontEnum|Function 0
399   push           ArrayProtoFuncImp::Push           DontEnum|Function 1
400   reverse        ArrayProtoFuncImp::Reverse        DontEnum|Function 0
401   shift          ArrayProtoFuncImp::Shift          DontEnum|Function 0
402   slice          ArrayProtoFuncImp::Slice          DontEnum|Function 2
403   sort           ArrayProtoFuncImp::Sort           DontEnum|Function 1
404   splice         ArrayProtoFuncImp::Splice         DontEnum|Function 2
405   unshift        ArrayProtoFuncImp::UnShift        DontEnum|Function 1
406 @end
407 */
408
409 // ECMA 15.4.4
410 ArrayPrototypeImp::ArrayPrototypeImp(ExecState *exec,
411                                      ObjectPrototypeImp *objProto)
412   : ArrayInstanceImp(objProto, 0)
413 {
414   Value protect(this);
415   setInternalValue(Null());
416 }
417
418 Value ArrayPrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
419 {
420   //fprintf( stderr, "ArrayPrototypeImp::get(%s)\n", propertyName.ascii() );
421   return lookupGetFunction<ArrayProtoFuncImp, ArrayInstanceImp>( exec, propertyName, &arrayTable, this );
422 }
423
424 // ------------------------------ ArrayProtoFuncImp ----------------------------
425
426 ArrayProtoFuncImp::ArrayProtoFuncImp(ExecState *exec, int i, int len)
427   : InternalFunctionImp(
428     static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
429     ), id(i)
430 {
431   Value protect(this);
432   put(exec,lengthPropertyName,Number(len),DontDelete|ReadOnly|DontEnum);
433 }
434
435 bool ArrayProtoFuncImp::implementsCall() const
436 {
437   return true;
438 }
439
440 // ECMA 15.4.4
441 Value ArrayProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
442 {
443   unsigned int length = thisObj.get(exec,lengthPropertyName).toUInt32(exec);
444
445   Value result;
446   switch (id) {
447   case ToLocaleString:
448     // TODO  - see 15.4.4.3
449     // fall through
450   case ToString:
451
452     if (!thisObj.inherits(&ArrayInstanceImp::info)) {
453       Object err = Error::create(exec,TypeError);
454       exec->setException(err);
455       return err;
456     }
457
458     // fall through
459
460   case Join: {
461     UString separator = ",";
462     UString str = "";
463
464     if (args.size() > 0)
465       separator = args[0].toString(exec);
466     for (unsigned int k = 0; k < length; k++) {
467       if (k >= 1)
468         str += separator;
469       Value element = thisObj.get(exec,k);
470       if (element.type() != UndefinedType && element.type() != NullType)
471         str += element.toString(exec);
472     }
473     result = String(str);
474     break;
475   }
476   case Concat: {
477     Object arr = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty()));
478     int n = 0;
479     Value curArg = thisObj;
480     Object curObj = Object::dynamicCast(thisObj);
481     ListIterator it = args.begin();
482     for (;;) {
483       if (curArg.type() == ObjectType &&
484           curObj.inherits(&ArrayInstanceImp::info)) {
485         unsigned int k = 0;
486         // Older versions tried to optimize out getting the length of thisObj
487         // by checking for n != 0, but that doesn't work if thisObj is an empty array.
488         length = curObj.get(exec,lengthPropertyName).toUInt32(exec);
489         while (k < length) {
490           if (curObj.hasProperty(exec,k))
491             arr.put(exec, n, curObj.get(exec, k));
492           n++;
493           k++;
494         }
495       } else {
496         arr.put(exec, n, curArg);
497         n++;
498       }
499       if (it == args.end())
500         break;
501       curArg = *it;
502       curObj = Object::dynamicCast(it++); // may be 0
503     }
504     arr.put(exec,lengthPropertyName, Number(n), DontEnum | DontDelete);
505
506     result = arr;
507     break;
508   }
509   case Pop:{
510     if (length == 0) {
511       thisObj.put(exec, lengthPropertyName, Number(length), DontEnum | DontDelete);
512       result = Undefined();
513     } else {
514       result = thisObj.get(exec, length - 1);
515       thisObj.put(exec, lengthPropertyName, Number(length - 1), DontEnum | DontDelete);
516     }
517     break;
518   }
519   case Push: {
520     for (int n = 0; n < args.size(); n++)
521       thisObj.put(exec, length + n, args[n]);
522     length += args.size();
523     thisObj.put(exec,lengthPropertyName, Number(length), DontEnum | DontDelete);
524     result = Number(length);
525     break;
526   }
527   case Reverse: {
528
529     unsigned int middle = length / 2;
530
531     for (unsigned int k = 0; k < middle; k++) {
532       unsigned lk1 = length - k - 1;
533       Value obj = thisObj.get(exec,k);
534       Value obj2 = thisObj.get(exec,lk1);
535       if (thisObj.hasProperty(exec,lk1)) {
536         if (thisObj.hasProperty(exec,k)) {
537           thisObj.put(exec, k, obj2);
538           thisObj.put(exec, lk1, obj);
539         } else {
540           thisObj.put(exec, k, obj2);
541           thisObj.deleteProperty(exec, lk1);
542         }
543       } else {
544         if (thisObj.hasProperty(exec, k)) {
545           thisObj.deleteProperty(exec, k);
546           thisObj.put(exec, lk1, obj);
547         } else {
548           // why delete something that's not there ? Strange.
549           thisObj.deleteProperty(exec, k);
550           thisObj.deleteProperty(exec, lk1);
551         }
552       }
553     }
554     result = thisObj;
555     break;
556   }
557   case Shift: {
558     if (length == 0) {
559       thisObj.put(exec, lengthPropertyName, Number(length), DontEnum | DontDelete);
560       result = Undefined();
561     } else {
562       result = thisObj.get(exec, 0);
563       for(unsigned int k = 1; k < length; k++) {
564         if (thisObj.hasProperty(exec, k)) {
565           Value obj = thisObj.get(exec, k);
566           thisObj.put(exec, k-1, obj);
567         } else
568           thisObj.deleteProperty(exec, k-1);
569       }
570       thisObj.deleteProperty(exec, length - 1);
571       thisObj.put(exec, lengthPropertyName, Number(length - 1), DontEnum | DontDelete);
572     }
573     break;
574   }
575   case Slice: {
576     // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
577
578     // We return a new array
579     Object resObj = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty()));
580     result = resObj;
581     int begin = args[0].toInteger(exec);
582     if ( begin < 0 )
583       begin = maxInt( begin + length, 0 );
584     else
585       begin = minInt( begin, length );
586     int end = length;
587     if (args[1].type() != UndefinedType)
588     {
589       end = args[1].toInteger(exec);
590       if ( end < 0 )
591         end = maxInt( end + length, 0 );
592       else
593         end = minInt( end, length );
594     }
595
596     //printf( "Slicing from %d to %d \n", begin, end );
597     int n = 0;
598     for(int k = begin; k < end; k++, n++) {
599       if (thisObj.hasProperty(exec, k)) {
600         Value obj = thisObj.get(exec, k);
601         resObj.put(exec, n, obj);
602       }
603     }
604     resObj.put(exec, lengthPropertyName, Number(n), DontEnum | DontDelete);
605     break;
606   }
607   case Sort:{
608 #if 0
609     printf("KJS Array::Sort length=%d\n", length);
610     for ( unsigned int i = 0 ; i<length ; ++i )
611       printf("KJS Array::Sort: %d: %s\n", i, thisObj.get(exec, i).toString(exec).ascii() );
612 #endif
613     Object sortFunction;
614     bool useSortFunction = (args[0].type() != UndefinedType);
615     if (useSortFunction)
616       {
617         sortFunction = args[0].toObject(exec);
618         if (!sortFunction.implementsCall())
619           useSortFunction = false;
620       }
621     
622     if (thisObj.imp()->classInfo() == &ArrayInstanceImp::info) {
623       if (useSortFunction)
624         ((ArrayInstanceImp *)thisObj.imp())->sort(exec, sortFunction);
625       else
626         ((ArrayInstanceImp *)thisObj.imp())->sort(exec);
627       result = thisObj;
628       break;
629     }
630
631     if (length == 0) {
632       thisObj.put(exec, lengthPropertyName, Number(0), DontEnum | DontDelete);
633       result = thisObj;
634       break;
635     }
636
637     // "Min" sort. Not the fastest, but definitely less code than heapsort
638     // or quicksort, and much less swapping than bubblesort/insertionsort.
639     for ( unsigned int i = 0 ; i<length-1 ; ++i )
640       {
641         Value iObj = thisObj.get(exec,i);
642         unsigned int themin = i;
643         Value minObj = iObj;
644         for ( unsigned int j = i+1 ; j<length ; ++j )
645           {
646             Value jObj = thisObj.get(exec,j);
647             double cmp;
648             if (jObj.type() == UndefinedType) {
649               cmp = 1; // don't check minObj because there's no need to differentiate == (0) from > (1)
650             } else if (minObj.type() == UndefinedType) {
651               cmp = -1;
652             } else if (useSortFunction) {
653                 List l;
654                 l.append(jObj);
655                 l.append(minObj);
656                 cmp = sortFunction.call(exec, exec->interpreter()->globalObject(), l).toNumber(exec);
657             } else {
658               cmp = (jObj.toString(exec) < minObj.toString(exec)) ? -1 : 1;
659             }
660             if ( cmp < 0 )
661               {
662                 themin = j;
663                 minObj = jObj;
664               }
665           }
666         // Swap themin and i
667         if ( themin > i )
668           {
669             //printf("KJS Array::Sort: swapping %d and %d\n", i, themin );
670             thisObj.put( exec, i, minObj );
671             thisObj.put( exec, themin, iObj );
672           }
673       }
674 #if 0
675     printf("KJS Array::Sort -- Resulting array:\n");
676     for ( unsigned int i = 0 ; i<length ; ++i )
677       printf("KJS Array::Sort: %d: %s\n", i, thisObj.get(exec, i).toString(exec).ascii() );
678 #endif
679     result = thisObj;
680     break;
681   }
682   case Splice: {
683     // 15.4.4.12 - oh boy this is huge
684     Object resObj = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty()));
685     result = resObj;
686     int begin = args[0].toUInt32(exec);
687     if ( begin < 0 )
688       begin = maxInt( begin + length, 0 );
689     else
690       begin = minInt( begin, length );
691     unsigned int deleteCount = minInt( maxInt( args[1].toUInt32(exec), 0 ), length - begin );
692
693     //printf( "Splicing from %d, deleteCount=%d \n", begin, deleteCount );
694     for(unsigned int k = 0; k < deleteCount; k++) {
695       if (thisObj.hasProperty(exec,k+begin)) {
696         Value obj = thisObj.get(exec, k+begin);
697         resObj.put(exec, k, obj);
698       }
699     }
700     resObj.put(exec, lengthPropertyName, Number(deleteCount), DontEnum | DontDelete);
701
702     unsigned int additionalArgs = maxInt( args.size() - 2, 0 );
703     if ( additionalArgs != deleteCount )
704     {
705       if ( additionalArgs < deleteCount )
706       {
707         for ( unsigned int k = begin; k < length - deleteCount; ++k )
708         {
709           if (thisObj.hasProperty(exec,k+deleteCount)) {
710             Value obj = thisObj.get(exec, k+deleteCount);
711             thisObj.put(exec, k+additionalArgs, obj);
712           }
713           else
714             thisObj.deleteProperty(exec, k+additionalArgs);
715         }
716         for ( unsigned int k = length ; k > length - deleteCount + additionalArgs; --k )
717           thisObj.deleteProperty(exec, k-1);
718       }
719       else
720       {
721         for ( unsigned int k = length - deleteCount; (int)k > begin; --k )
722         {
723           if (thisObj.hasProperty(exec,k+deleteCount-1)) {
724             Value obj = thisObj.get(exec, k+deleteCount-1);
725             thisObj.put(exec, k+additionalArgs-1, obj);
726           }
727           else
728             thisObj.deleteProperty(exec, k+additionalArgs-1);
729         }
730       }
731     }
732     for ( unsigned int k = 0; k < additionalArgs; ++k )
733     {
734       thisObj.put(exec, k+begin, args[k+2]);
735     }
736     thisObj.put(exec, lengthPropertyName, Number(length - deleteCount + additionalArgs), DontEnum | DontDelete);
737     break;
738   }
739   case UnShift: { // 15.4.4.13
740     unsigned int nrArgs = args.size();
741     for ( unsigned int k = length; k > 0; --k )
742     {
743       if (thisObj.hasProperty(exec,k-1)) {
744         Value obj = thisObj.get(exec, k-1);
745         thisObj.put(exec, k+nrArgs-1, obj);
746       } else {
747         thisObj.deleteProperty(exec, k+nrArgs-1);
748       }
749     }
750     for ( unsigned int k = 0; k < nrArgs; ++k )
751       thisObj.put(exec, k, args[k]);
752     result = Number(length + nrArgs);
753     thisObj.put(exec, lengthPropertyName, result, DontEnum | DontDelete);
754     break;
755   }
756   default:
757     assert(0);
758     break;
759   }
760   return result;
761 }
762
763 // ------------------------------ ArrayObjectImp -------------------------------
764
765 ArrayObjectImp::ArrayObjectImp(ExecState *exec,
766                                FunctionPrototypeImp *funcProto,
767                                ArrayPrototypeImp *arrayProto)
768   : InternalFunctionImp(funcProto)
769 {
770   Value protect(this);
771   // ECMA 15.4.3.1 Array.prototype
772   put(exec,prototypePropertyName, Object(arrayProto), DontEnum|DontDelete|ReadOnly);
773
774   // no. of arguments for constructor
775   put(exec,lengthPropertyName, Number(1), ReadOnly|DontDelete|DontEnum);
776 }
777
778 bool ArrayObjectImp::implementsConstruct() const
779 {
780   return true;
781 }
782
783 // ECMA 15.4.2
784 Object ArrayObjectImp::construct(ExecState *exec, const List &args)
785 {
786   // a single numeric argument denotes the array size (!)
787   if (args.size() == 1 && args[0].type() == NumberType)
788     return Object(new ArrayInstanceImp(exec->interpreter()->builtinArrayPrototype().imp(), args[0].toUInt32(exec)));
789
790   // otherwise the array is constructed with the arguments in it
791   return Object(new ArrayInstanceImp(exec->interpreter()->builtinArrayPrototype().imp(), args));
792 }
793
794 bool ArrayObjectImp::implementsCall() const
795 {
796   return true;
797 }
798
799 // ECMA 15.6.1
800 Value ArrayObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
801 {
802   // equivalent to 'new Array(....)'
803   return construct(exec,args);
804 }