1 /* Digital Mars DMDScript source code.
2  * Copyright (c) 2000-2002 by Chromium Communications
3  * D version Copyright (c) 2004-2010 by Digital Mars
4  * Distributed under the Boost Software License, Version 1.0.
5  * (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  * written by Walter Bright
7  * http://www.digitalmars.com
8  *
9  * D2 port by Dmitry Olshansky 
10  *
11  * DMDScript is implemented in the D Programming Language,
12  * http://www.digitalmars.com/d/
13  *
14  * For a C++ implementation of DMDScript, including COM support, see
15  * http://www.digitalmars.com/dscript/cppscript.html
16  */
17 
18 
19 module dmdscript.darray;
20 
21 //Nonstandard treatment of Infinity as array length in slice/splice functions, supported by majority of browsers
22 //also treats negative starting index in splice wrapping it around just like in slice
23 version =  SliceSpliceExtension;
24 
25 import std..string;
26 import std.c.stdlib;
27 import std.math;
28 
29 import dmdscript.script;
30 import dmdscript.value;
31 import dmdscript.dobject;
32 import dmdscript.threadcontext;
33 import dmdscript.identifier;
34 import dmdscript.dfunction;
35 import dmdscript.text;
36 import dmdscript.property;
37 import dmdscript.errmsgs;
38 import dmdscript.dnative;
39 import dmdscript.program;
40 
41 /* ===================== Darray_constructor ==================== */
42 
43 class DarrayConstructor : Dfunction
44 {
45     this()
46     {
47         super(1, Dfunction_prototype);
48         name = "Array";
49     }
50 
51     override void* Construct(CallContext *cc, Value *ret, Value[] arglist)
52     {
53         // ECMA 15.4.2
54         Darray a;
55 
56         a = new Darray();
57         if(arglist.length == 0)
58         {
59             a.ulength = 0;
60             a.length.number = 0;
61         }
62         else if(arglist.length == 1)
63         {
64             Value* v = &arglist[0];
65 
66             if(v.isNumber())
67             {
68                 d_uint32 len;
69 
70                 len = v.toUint32();
71                 if(cast(double)len != v.number)
72                 {
73                     ErrInfo errinfo;
74 
75                     ret.putVundefined();
76                     return RangeError(&errinfo, ERR_ARRAY_LEN_OUT_OF_BOUNDS, v.number);
77                 }
78                 else
79                 {
80                     a.ulength = len;
81                     a.length.number = len;
82                     /+
83                        if (len > 16)
84                        {
85                         //writef("setting %p dimension to %d\n", &a.proptable, len);
86                         if (len > 10000)
87                             len = 10000;		// cap so we don't run out of memory
88                         a.proptable.roots.setDim(len);
89                         a.proptable.roots.zero();
90                        }
91                      +/
92                 }
93             }
94             else
95             {
96                 a.ulength = 1;
97                 a.length.number = 1;
98                 a.Put(cast(d_uint32)0, v, 0);
99             }
100         }
101         else
102         {
103             //if (arglist.length > 10) writef("Array constructor: arglist.length = %d\n", arglist.length);
104             /+
105                if (arglist.length > 16)
106                {
107                 a.proptable.roots.setDim(arglist.length);
108                 a.proptable.roots.zero();
109                }
110              +/
111             a.ulength = cast(uint)arglist.length;
112             a.length.number = arglist.length;
113             for(uint k = 0; k < arglist.length; k++)
114             {
115                 a.Put(k, &arglist[k], 0);
116             }
117         }
118         Value.copy(ret, &a.value);
119         //writef("Darray_constructor.Construct(): length = %g\n", a.length.number);
120         return null;
121     }
122 
123     override void* Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
124     {
125         // ECMA 15.4.1
126         return Construct(cc, ret, arglist);
127     }
128 }
129 
130 
131 /* ===================== Darray_prototype_toString ================= */
132 
133 void *Darray_prototype_toString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
134 {
135     //writef("Darray_prototype_toString()\n");
136     array_join(othis, ret, null);
137     return null;
138 }
139 
140 /* ===================== Darray_prototype_toLocaleString ================= */
141 
142 void *Darray_prototype_toLocaleString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
143 {
144     // ECMA v3 15.4.4.3
145     d_string separator;
146     d_string r;
147     d_uint32 len;
148     d_uint32 k;
149     Value* v;
150 
151     //writef("array_join(othis = %p)\n", othis);
152 
153     if(!othis.isClass(TEXT_Array))
154     {
155         ret.putVundefined();
156         ErrInfo errinfo;
157         return Dobject.RuntimeError(&errinfo, ERR_TLS_NOT_TRANSFERRABLE);
158     }
159 
160     v = othis.Get(TEXT_length);
161     len = v ? v.toUint32() : 0;
162 
163     Program prog = cc.prog;
164     if(!prog.slist)
165     {
166         // Determine what list separator is only once per thread
167         //prog.slist = list_separator(prog.lcid);
168         prog.slist = ",";
169     }
170     separator = prog.slist;
171 
172     for(k = 0; k != len; k++)
173     {
174         if(k)
175             r ~= separator;
176         v = othis.Get(k);
177         if(v && !v.isUndefinedOrNull())
178         {
179             Dobject ot;
180 
181             ot = v.toObject();
182             v = ot.Get(TEXT_toLocaleString);
183             if(v && !v.isPrimitive())   // if it's an Object
184             {
185                 void* a;
186                 Dobject o;
187                 Value rt;
188 
189                 o = v.object;
190                 rt.putVundefined();
191                 a = o.Call(cc, ot, &rt, null);
192                 if(a)                   // if exception was thrown
193                     return a;
194                 r ~= rt.toString();
195             }
196         }
197     }
198 
199     ret.putVstring(r);
200     return null;
201 }
202 
203 /* ===================== Darray_prototype_concat ================= */
204 
205 void *Darray_prototype_concat(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
206 {
207     // ECMA v3 15.4.4.4
208     Darray A;
209     Darray E;
210     Value* v;
211     d_uint32 k;
212     d_uint32 n;
213     d_uint32 a;
214 
215     A = new Darray();
216     n = 0;
217     v = &othis.value;
218     for(a = 0;; a++)
219     {
220         if(!v.isPrimitive() && v.object.isDarray())
221         {
222             d_uint32 len;
223 
224             E = cast(Darray)v.object;
225             len = E.ulength;
226             for(k = 0; k != len; k++)
227             {
228                 v = E.Get(k);
229                 if(v)
230                     A.Put(n, v, 0);
231                 n++;
232             }
233         }
234         else
235         {
236             A.Put(n, v, 0);
237             n++;
238         }
239         if(a == arglist.length)
240             break;
241         v = &arglist[a];
242     }
243 
244     A.Put(TEXT_length, n,  DontEnum);
245     Value.copy(ret, &A.value);
246     return null;
247 }
248 
249 /* ===================== Darray_prototype_join ================= */
250 
251 void *Darray_prototype_join(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
252 {
253     array_join(othis, ret, arglist);
254     return null;
255 }
256 
257 void array_join(Dobject othis, Value* ret, Value[] arglist)
258 {
259     // ECMA 15.4.4.3
260     d_string separator;
261     d_string r;
262     d_uint32 len;
263     d_uint32 k;
264     Value* v;
265 
266     //writef("array_join(othis = %p)\n", othis);
267     v = othis.Get(TEXT_length);
268     len = v ? v.toUint32() : 0;
269     if(arglist.length == 0 || arglist[0].isUndefined())
270         separator = TEXT_comma;
271     else
272         separator = arglist[0].toString();
273 
274     for(k = 0; k != len; k++)
275     {
276         if(k)
277             r ~= separator;
278         v = othis.Get(k);
279         if(v && !v.isUndefinedOrNull())
280             r ~= v.toString();
281     }
282 
283     ret.putVstring(r);
284 }
285 
286 /* ===================== Darray_prototype_toSource ================= */
287 
288 void *Darray_prototype_toSource(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
289 {
290     d_string separator;
291     d_string r;
292     d_uint32 len;
293     d_uint32 k;
294     Value* v;
295 
296     v = othis.Get(TEXT_length);
297     len = v ? v.toUint32() : 0;
298     separator = ",";
299 
300     r = "[".idup;
301     for(k = 0; k != len; k++)
302     {
303         if(k)
304             r ~= separator;
305         v = othis.Get(k);
306         if(v && !v.isUndefinedOrNull())
307             r ~= v.toSource();
308     }
309     r ~= "]";
310 
311     ret.putVstring(r);
312     return null;
313 }
314 
315 
316 /* ===================== Darray_prototype_pop ================= */
317 
318 void *Darray_prototype_pop(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
319 {
320     // ECMA v3 15.4.4.6
321     Value* v;
322     d_uint32 u;
323 
324     // If othis is a Darray, then we can optimize this significantly
325     v = othis.Get(TEXT_length);
326     if(!v)
327         v = &vundefined;
328     u = v.toUint32();
329     if(u == 0)
330     {
331         othis.Put(TEXT_length, 0.0,  DontEnum);
332         ret.putVundefined();
333     }
334     else
335     {
336         v = othis.Get(u - 1);
337         if(!v)
338             v = &vundefined;
339         Value.copy(ret, v);
340         othis.Delete(u - 1);
341         othis.Put(TEXT_length, u - 1,  DontEnum);
342     }
343     return null;
344 }
345 
346 /* ===================== Darray_prototype_push ================= */
347 
348 void *Darray_prototype_push(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
349 {
350     // ECMA v3 15.4.4.7
351     Value* v;
352     d_uint32 u;
353     d_uint32 a;
354 
355     // If othis is a Darray, then we can optimize this significantly
356     v = othis.Get(TEXT_length);
357     if(!v)
358         v = &vundefined;
359     u = v.toUint32();
360     for(a = 0; a < arglist.length; a++)
361     {
362         othis.Put(u + a, &arglist[a], 0);
363     }
364     othis.Put(TEXT_length, u + a,  DontEnum);
365     ret.putVnumber(u + a);
366     return null;
367 }
368 
369 /* ===================== Darray_prototype_reverse ================= */
370 
371 void *Darray_prototype_reverse(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
372 {
373     // ECMA 15.4.4.4
374     d_uint32 a;
375     d_uint32 b;
376     Value* va;
377     Value* vb;
378     Value* v;
379     d_uint32 pivot;
380     d_uint32 len;
381     Value tmp;
382 
383     v = othis.Get(TEXT_length);
384     len = v ? v.toUint32() : 0;
385     pivot = len / 2;
386     for(a = 0; a != pivot; a++)
387     {
388         b = len - a - 1;
389         //writef("a = %d, b = %d\n", a, b);
390         va = othis.Get(a);
391         if(va)
392             Value.copy(&tmp, va);
393         vb = othis.Get(b);
394         if(vb)
395             othis.Put(a, vb, 0);
396         else
397             othis.Delete(a);
398 
399         if(va)
400             othis.Put(b, &tmp, 0);
401         else
402             othis.Delete(b);
403     }
404     Value.copy(ret, &othis.value);
405     return null;
406 }
407 
408 /* ===================== Darray_prototype_shift ================= */
409 
410 void *Darray_prototype_shift(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
411 {
412     // ECMA v3 15.4.4.9
413     Value* v;
414     Value* result;
415     d_uint32 len;
416     d_uint32 k;
417 
418     // If othis is a Darray, then we can optimize this significantly
419     //writef("shift(othis = %p)\n", othis);
420     v = othis.Get(TEXT_length);
421     if(!v)
422         v = &vundefined;
423     len = v.toUint32();
424     
425     if(len)
426     {
427         result = othis.Get(0u);
428         Value.copy(ret, result ? result : &vundefined);
429         for(k = 1; k != len; k++)
430         {
431             v = othis.Get(k);
432             if(v)
433             {
434                 othis.Put(k - 1, v, 0);
435             }
436             else
437             {
438                 othis.Delete(k - 1);
439             }
440         }
441         othis.Delete(len - 1);
442         len--;
443     }
444     else
445         Value.copy(ret, &vundefined);
446 
447     othis.Put(TEXT_length, len, DontEnum);
448     return null;
449 }
450 
451 
452 /* ===================== Darray_prototype_slice ================= */
453 
454 void *Darray_prototype_slice(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
455 {
456     // ECMA v3 15.4.4.10
457     d_uint32 len;
458     d_uint32 n;
459     d_uint32 k;
460     d_uint32 r8;
461 
462     Value* v;
463     Darray A;
464 
465     v = othis.Get(TEXT_length);
466     if(!v)
467         v = &vundefined;
468     len = v.toUint32();
469 
470 version(SliceSpliceExtension){
471     d_number start;
472     d_number end;
473     switch(arglist.length)
474     {
475     case 0:
476         start = vundefined.toNumber();
477         end = len;
478         break;
479 
480     case 1:
481         start = arglist[0].toNumber();
482         end = len;
483         break;
484 
485     default:
486         start = arglist[0].toNumber();
487 		if(arglist[1].isUndefined())
488 			end = len;
489 		else{
490 			end = arglist[1].toNumber();
491 		}
492         break;
493     }
494     if(start < 0)
495     {
496         k = cast(uint)(len + start);
497         if(cast(d_int32)k < 0)
498             k = 0;
499     }
500     else if(start == d_number.infinity)
501         k = len;
502     else if(start == -d_number.infinity)
503         k = 0;
504     else
505     {
506         k = cast(uint)start;
507         if(len < k)
508             k = len;
509     }
510 
511     if(end < 0)
512     {
513         r8 = cast(uint)(len + end);
514         if(cast(d_int32)r8 < 0)
515             r8 = 0;
516     }
517     else if(end == d_number.infinity)
518             r8 = len;
519     else if(end == -d_number.infinity)
520             r8 = 0;
521     else
522     {
523         r8 = cast(uint)end;
524         if(len < end)
525             r8 = len;
526     }
527 }
528 else{//Canonical ECMA all kinds of infinity maped to 0
529     int start;
530     int end;
531     switch(arglist.length)
532     {
533     case 0:
534         start = vundefined.toInt32();
535         end = len;
536         break;
537 
538     case 1:
539         start = arglist[0].toInt32();
540         end = len;
541         break;
542 
543     default:
544         start = arglist[0].toInt32();
545 		if(arglist[1].isUndefined())
546 			end = len;
547 		else{
548 			end = arglist[1].toInt32();
549 		}
550         break;
551     }
552     if(start < 0)
553     {
554         k = cast(uint)(len + start);
555         if(cast(d_int32)k < 0)
556             k = 0;
557     }
558     else
559     {
560         k = cast(uint)start;
561         if(len < k)
562             k = len;
563     }
564 
565     if(end < 0)
566     {
567         r8 = cast(uint)(len + end);
568         if(cast(d_int32)r8 < 0)
569             r8 = 0;
570     }
571     else
572     {
573         r8 = cast(uint)end;
574         if(len < end)
575             r8 = len;
576     }
577 }
578     A = new Darray();
579     for(n = 0; k < r8; k++)
580     {
581         v = othis.Get(k);
582         if(v)
583         {
584             A.Put(n, v, 0);
585         }
586         n++;
587     }
588 
589     A.Put(TEXT_length, n, DontEnum);
590     Value.copy(ret, &A.value);
591     return null;
592 }
593 
594 /* ===================== Darray_prototype_sort ================= */
595 
596 static Dobject comparefn;
597 static CallContext *comparecc;
598 
599 extern (C) int compare_value(const void* x, const void* y)
600 {
601     Value* vx = cast(Value*)x;
602     Value* vy = cast(Value*)y;
603     d_string sx;
604     d_string sy;
605     int cmp;
606 
607     //writef("compare_value()\n");
608     if(vx.isUndefined())
609     {
610         cmp = (vy.isUndefined()) ? 0 : 1;
611     }
612     else if(vy.isUndefined())
613         cmp = -1;
614     else
615     {
616         if(comparefn)
617         {
618             Value arglist[2];
619             Value ret;
620             Value* v;
621             d_number n;
622 
623             Value.copy(&arglist[0], vx);
624             Value.copy(&arglist[1], vy);
625             ret.putVundefined();
626             comparefn.Call(comparecc, comparefn, &ret, arglist);
627             n = ret.toNumber();
628             if(n < 0)
629                 cmp = -1;
630             else if(n > 0)
631                 cmp = 1;
632             else
633                 cmp = 0;
634         }
635         else
636         {
637             sx = vx.toString();
638             sy = vy.toString();
639             cmp = std..string.cmp(sx, sy);
640             if(cmp < 0)
641                 cmp = -1;
642             else if(cmp > 0)
643                 cmp = 1;
644         }
645     }
646     return cmp;
647 }
648 
649 void *Darray_prototype_sort(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
650 {
651     // ECMA v3 15.4.4.11
652     Value* v;
653     d_uint32 len;
654     uint u;
655 
656     //writef("Array.prototype.sort()\n");
657     v = othis.Get(TEXT_length);
658     len = v ? v.toUint32() : 0;
659 
660     // This is not optimal, as isArrayIndex is done at least twice
661     // for every array member. Additionally, the qsort() by index
662     // can be avoided if we can deduce it is not a sparse array.
663 
664     Property *p;
665     Value[] pvalues;
666     d_uint32[] pindices;
667     d_uint32 parraydim;
668     d_uint32 nprops;
669 
670     // First, size & alloc our temp array
671     if(len < 100)
672     {   // Probably not too sparse an array
673         parraydim = len;
674     }
675     else
676     {
677         parraydim = 0;
678         foreach(ref Property p; *othis.proptable)
679         {
680             if(p.attributes == 0)       // don't count special properties
681                 parraydim++;
682         }
683         if(parraydim > len)             // could theoretically happen
684             parraydim = len;
685     }
686 
687     Value[] p1 = null;
688     Value* v1;
689     version(Win32)      // eh and alloca() not working under linux
690     {
691         if(parraydim < 128)
692             v1 = cast(Value*)alloca(parraydim * Value.sizeof);
693     }
694     if(v1)
695         pvalues = v1[0 .. parraydim];
696     else
697     {
698         p1 = new Value[parraydim];
699         pvalues = p1;
700     }
701 
702     d_uint32[] p2 = null;
703     d_uint32* p3;
704     version(Win32)
705     {
706         if(parraydim < 128)
707             p3 = cast(d_uint32*)alloca(parraydim * d_uint32.sizeof);
708     }
709     if(p3)
710         pindices = p3[0 .. parraydim];
711     else
712     {
713         p2 = new d_uint32[parraydim];
714         pindices = p2;
715     }
716 
717     // Now fill it with all the Property's that are array indices
718     nprops = 0;
719     foreach(Value key, ref Property p; *othis.proptable)
720     {
721         d_uint32 index;
722 
723         if(p.attributes == 0 && key.isArrayIndex(index))
724         {
725             pindices[nprops] = index;
726             Value.copy(&pvalues[nprops], &p.value);
727             nprops++;
728         }
729     }
730 
731     synchronized
732     {
733         comparefn = null;
734         comparecc = cc;
735         if(arglist.length)
736         {
737             if(!arglist[0].isPrimitive())
738                 comparefn = arglist[0].object;
739         }
740 
741         // Sort pvalues[]
742         std.c.stdlib.qsort(pvalues.ptr, nprops, Value.sizeof, &compare_value);
743 
744         comparefn = null;
745         comparecc = null;
746     }
747 
748     // Stuff the sorted value's back into the array
749     for(u = 0; u < nprops; u++)
750     {
751         d_uint32 index;
752 
753         othis.Put(u, &pvalues[u], 0);
754         index = pindices[u];
755         if(index >= nprops)
756         {
757             othis.Delete(index);
758         }
759     }
760 
761     delete p1;
762     delete p2;
763 
764     ret.putVobject(othis);
765     return null;
766 }
767 
768 /* ===================== Darray_prototype_splice ================= */
769 
770 void *Darray_prototype_splice(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
771 {
772     // ECMA v3 15.4.4.12
773     d_uint32 len;
774     d_uint32 k;
775     
776     Value* v;
777     Darray A;
778     d_uint32 a;
779     d_uint32 delcnt;
780     d_uint32 inscnt;
781     d_uint32 startidx;
782     
783     v = othis.Get(TEXT_length);
784     if(!v)
785         v = &vundefined;
786     len = v.toUint32();
787     
788 version(SliceSpliceExtension){
789     d_number start;
790     d_number deleteCount;
791     
792     switch(arglist.length)
793     {
794     case 0:
795         start = vundefined.toNumber();
796         deleteCount = 0;
797         break;
798 
799     case 1:
800         start = arglist[0].toNumber();
801         deleteCount = vundefined.toNumber();
802         break;
803 
804     default:
805         start = arglist[0].toNumber();
806         deleteCount = arglist[1].toNumber();
807 		//checked later
808         break;
809     }
810     if(start == d_number.infinity)
811         startidx = len;
812     else if(start == -d_number.infinity)
813         startidx = 0;
814     else{
815         if(start < 0)
816         {
817             startidx = cast(uint)(len + start);
818             if(cast(d_int32)startidx < 0)
819                 startidx = 0;
820         }
821         else
822             startidx = cast(uint)start;
823     }
824 	startidx = startidx > len ? len : startidx; 
825     if(deleteCount == d_number.infinity)
826         delcnt = len;
827     else if(deleteCount == -d_number.infinity)
828         delcnt = 0;
829     else
830         delcnt = (cast(uint)deleteCount > 0) ? cast(uint) deleteCount : 0;
831     if(delcnt > len - startidx)
832         delcnt = len - startidx;
833 }else{    
834     long start;
835     d_int32 deleteCount;
836     switch(arglist.length)
837     {
838     case 0:
839         start = vundefined.toInt32();
840         deleteCount = 0;
841         break;
842 
843     case 1:
844         start = arglist[0].toInt32();
845         deleteCount = vundefined.toInt32();
846         break;
847 
848     default:
849         start = arglist[0].toInt32();
850         deleteCount = arglist[1].toInt32();
851 		//checked later
852         break;
853     }
854     startidx = cast(uint)start;
855 	startidx = startidx > len ? len : startidx; 
856     delcnt = (deleteCount > 0) ? deleteCount : 0;
857     if(delcnt > len - startidx)
858         delcnt = len - startidx;
859 }
860 	
861     A = new Darray();
862 
863     // If deleteCount is not specified, ECMA implies it should
864     // be 0, while "JavaScript The Definitive Guide" says it should
865     // be delete to end of array. Jscript doesn't implement splice().
866     // We'll do it the Guide way.
867     if(arglist.length < 2)
868         delcnt = len - startidx;
869 
870     //writef("Darray.splice(startidx = %d, delcnt = %d)\n", startidx, delcnt);
871     for(k = 0; k != delcnt; k++)
872     {
873         v = othis.Get(startidx + k);
874         if(v)
875             A.Put(k, v, 0);
876     }
877 
878     A.Put(TEXT_length, delcnt, DontEnum);
879     inscnt = (arglist.length > 2) ? cast(uint)arglist.length - 2 : 0;
880     if(inscnt != delcnt)
881     {
882         if(inscnt <= delcnt)
883         {
884             for(k = startidx; k != (len - delcnt); k++)
885             {
886                 v = othis.Get(k + delcnt);
887                 if(v)
888                     othis.Put(k + inscnt, v, 0);
889                 else
890                     othis.Delete(k + inscnt);
891             }
892 
893             for(k = len; k != (len - delcnt + inscnt); k--)
894                 othis.Delete(k - 1);
895         }
896         else
897         {
898             for(k = len - delcnt; k != startidx; k--)
899             {
900                 v = othis.Get(k + delcnt - 1);
901                 if(v)
902                     othis.Put(k + inscnt - 1, v, 0);
903                 else
904                     othis.Delete(k + inscnt - 1);
905             }
906         }
907     }
908     k = startidx;
909     for(a = 2; a < arglist.length; a++)
910     {
911         v = &arglist[a];
912         othis.Put(k, v, 0);
913         k++;
914     }
915 
916     othis.Put(TEXT_length, len - delcnt + inscnt,  DontEnum);
917     Value.copy(ret, &A.value);
918     return null;
919 }
920 
921 /* ===================== Darray_prototype_unshift ================= */
922 
923 void *Darray_prototype_unshift(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
924 {
925     // ECMA v3 15.4.4.13
926     Value* v;
927     d_uint32 len;
928     d_uint32 k;
929 
930     v = othis.Get(TEXT_length);
931     if(!v)
932         v = &vundefined;
933     len = v.toUint32();
934 
935     for(k = len; k>0; k--)
936     {
937         v = othis.Get(k - 1);
938         if(v)
939             othis.Put(cast(uint)(k + arglist.length - 1), v, 0);
940         else
941             othis.Delete(cast(uint)(k + arglist.length - 1));
942     }
943 
944     for(k = 0; k < arglist.length; k++)
945     {
946         othis.Put(k, &arglist[k], 0);
947     }
948     othis.Put(TEXT_length, len + arglist.length,  DontEnum);
949     ret.putVnumber(len + arglist.length);
950     return null;
951 }
952 
953 /* =========================== Darray_prototype =================== */
954 
955 class DarrayPrototype : Darray
956 {
957     this()
958     {
959         super(Dobject_prototype);
960         Dobject f = Dfunction_prototype;
961 
962         Put(TEXT_constructor, Darray_constructor, DontEnum);
963 
964         static enum NativeFunctionData nfd[] =
965         [
966             { TEXT_toString, &Darray_prototype_toString, 0 },
967             { TEXT_toLocaleString, &Darray_prototype_toLocaleString, 0 },
968             { TEXT_toSource, &Darray_prototype_toSource, 0 },
969             { TEXT_concat, &Darray_prototype_concat, 1 },
970             { TEXT_join, &Darray_prototype_join, 1 },
971             { TEXT_pop, &Darray_prototype_pop, 0 },
972             { TEXT_push, &Darray_prototype_push, 1 },
973             { TEXT_reverse, &Darray_prototype_reverse, 0 },
974             { TEXT_shift, &Darray_prototype_shift, 0, },
975             { TEXT_slice, &Darray_prototype_slice, 2 },
976             { TEXT_sort, &Darray_prototype_sort, 1 },
977             { TEXT_splice, &Darray_prototype_splice, 2 },
978             { TEXT_unshift, &Darray_prototype_unshift, 1 },
979         ];
980 
981         DnativeFunction.initialize(this, nfd, DontEnum);
982     }
983 }
984 
985 
986 /* =========================== Darray =================== */
987 
988 class Darray : Dobject
989 {
990     Value length;               // length property
991     d_uint32 ulength;
992 
993     this()
994     {
995         this(getPrototype());
996     }
997 
998     this(Dobject prototype)
999     {
1000         super(prototype);
1001         length.putVnumber(0);
1002         ulength = 0;
1003         classname = TEXT_Array;
1004     }
1005 
1006     override  Value* Put(Identifier* key, Value* value, uint attributes)
1007     {
1008         Value* result = proptable.put(&key.value, key.value.hash, value, attributes);
1009         if(!result)
1010             Put(key.value..string, value, attributes);
1011         return null;
1012     }
1013 
1014     override Value* Put(d_string name, Value* v, uint attributes)
1015     {
1016         d_uint32 i;
1017         uint c;
1018         Value* result;
1019 
1020         // ECMA 15.4.5.1
1021         result = proptable.put(name, v, attributes);
1022         if(!result)
1023         {
1024             if(name == TEXT_length)
1025             {
1026                 i = v.toUint32();
1027                 if(i != v.toInteger())
1028                 {
1029                     ErrInfo errinfo;
1030 
1031                     return Dobject.RangeError(&errinfo, ERR_LENGTH_INT);
1032                 }
1033                 if(i < ulength)
1034                 {
1035                     // delete all properties with keys >= i
1036                     d_uint32[] todelete;
1037 
1038                     foreach(Value key, ref Property p; *proptable)
1039                     {
1040                         d_uint32 j;
1041 
1042                         j = key.toUint32();
1043                         if(j >= i)
1044                             todelete ~= j;
1045                     }
1046                     foreach(d_uint32 j; todelete)
1047                     {
1048                         proptable.del(j);
1049                     }
1050                 }
1051                 ulength = i;
1052                 length.number = i;
1053                 proptable.put(name, v, attributes | DontEnum);
1054             }
1055 
1056             // if (name is an array index i)
1057 
1058             i = 0;
1059             for(size_t j = 0; j < name.length; j++)
1060             {
1061                 ulong k;
1062 
1063                 c = name[j];
1064                 if(c == '0' && i == 0 && name.length > 1)
1065                     goto Lret;
1066                 if(c >= '0' && c <= '9')
1067                 {
1068                     k = i * cast(ulong)10 + c - '0';
1069                     i = cast(d_uint32)k;
1070                     if(i != k)
1071                         goto Lret;              // overflow
1072                 }
1073                 else
1074                     goto Lret;
1075             }
1076             if(i >= ulength)
1077             {
1078                 if(i == 0xFFFFFFFF)
1079                     goto Lret;
1080                 ulength = i + 1;
1081                 length.number = ulength;
1082             }
1083         }
1084         Lret:
1085         return null;
1086     }
1087 
1088     override Value* Put(d_string name, Dobject o, uint attributes)
1089     {
1090         return Put(name, &o.value, attributes);
1091     }
1092 
1093     override Value* Put(d_string PropertyName, d_number n, uint attributes)
1094     {
1095         Value v;
1096 
1097         v.putVnumber(n);
1098         return Put(PropertyName, &v, attributes);
1099     }
1100 
1101     override Value* Put(d_string PropertyName, d_string string, uint attributes)
1102     {
1103         Value v;
1104 
1105         v.putVstring(string);
1106         return Put(PropertyName, &v, attributes);
1107     }
1108 
1109     override Value* Put(d_uint32 index, Value* vindex, Value* value, uint attributes)
1110     {
1111         if(index >= ulength)
1112             ulength = index + 1;
1113 
1114         proptable.put(vindex, index ^ 0x55555555 /*Value.calcHash(index)*/, value, attributes);
1115         return null;
1116     }
1117 
1118     override Value* Put(d_uint32 index, Value* value, uint attributes)
1119     {
1120         if(index >= ulength)
1121         {
1122             ulength = index + 1;
1123             length.number = ulength;
1124         }
1125 
1126         proptable.put(index, value, attributes);
1127         return null;
1128     }
1129 
1130     final Value* Put(d_uint32 index, d_string string, uint attributes)
1131     {
1132         if(index >= ulength)
1133         {
1134             ulength = index + 1;
1135             length.number = ulength;
1136         }
1137 
1138         proptable.put(index, string, attributes);
1139         return null;
1140     }
1141 
1142     override Value* Get(Identifier* id)
1143     {
1144         //writef("Darray.Get(%p, '%s')\n", &proptable, PropertyName);
1145         if(id.value..string == TEXT_length)
1146         {
1147             length.number = ulength;
1148             return &length;
1149         }
1150         else
1151             return Dobject.Get(id);
1152     }
1153 
1154     override Value* Get(d_string PropertyName, uint hash)
1155     {
1156         //writef("Darray.Get(%p, '%s')\n", &proptable, PropertyName);
1157         if(PropertyName == TEXT_length)
1158         {
1159             length.number = ulength;
1160             return &length;
1161         }
1162         else
1163             return Dobject.Get(PropertyName, hash);
1164     }
1165 
1166     override Value* Get(d_uint32 index)
1167     {
1168         Value* v;
1169 
1170         //writef("Darray.Get(%p, %d)\n", &proptable, index);
1171         v = proptable.get(index);
1172         return v;
1173     }
1174 
1175     override Value* Get(d_uint32 index, Value* vindex)
1176     {
1177         Value* v;
1178 
1179         //writef("Darray.Get(%p, %d)\n", &proptable, index);
1180         v = proptable.get(vindex, index ^ 0x55555555 /*Value.calcHash(index)*/);
1181         return v;
1182     }
1183 
1184     override int Delete(d_string PropertyName)
1185     {
1186         // ECMA 8.6.2.5
1187         //writef("Darray.Delete('%ls')\n", d_string_ptr(PropertyName));
1188         if(PropertyName == TEXT_length)
1189             return 0;           // can't delete 'length' property
1190         else
1191             return proptable.del(PropertyName);
1192     }
1193 
1194     override int Delete(d_uint32 index)
1195     {
1196         // ECMA 8.6.2.5
1197         return proptable.del(index);
1198     }
1199 
1200 
1201     static Dfunction getConstructor()
1202     {
1203         return Darray_constructor;
1204     }
1205 
1206     static Dobject getPrototype()
1207     {
1208         return Darray_prototype;
1209     }
1210 
1211     static void initialize()
1212     {
1213         Darray_constructor = new DarrayConstructor();
1214         Darray_prototype = new DarrayPrototype();
1215 
1216         Darray_constructor.Put(TEXT_prototype, Darray_prototype, DontEnum |  ReadOnly);
1217     }
1218 }
1219