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