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 core.stdc.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(CallContext* cc)
46     {
47         super(cc, 1, cc.tc.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(cc);
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(cc);
71                 if(cast(double)len != v.number)
72                 {
73                     ErrInfo errinfo;
74 
75                     ret.putVundefined();
76                     return RangeError(&errinfo, cc, 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(cc, 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(cc, 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, cc, 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, cc, ERR_TLS_NOT_TRANSFERRABLE);
158     }
159 
160     v = othis.Get(TEXT_length);
161     len = v ? v.toUint32(cc) : 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(cc);
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(cc);
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(cc);
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(cc, n, v, 0);
231                 n++;
232             }
233         }
234         else
235         {
236             A.Put(cc, n, v, 0);
237             n++;
238         }
239         if(a == arglist.length)
240             break;
241         v = &arglist[a];
242     }
243 
244     A.Put(cc, 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, cc, ret, arglist);
254     return null;
255 }
256 
257 void array_join(Dobject othis, CallContext* cc, 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(cc) : 0;
269     if(arglist.length == 0 || arglist[0].isUndefined())
270         separator = TEXT_comma;
271     else
272         separator = arglist[0].toString(cc);
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(cc);
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(cc) : 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(cc);
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(cc);
329     if(u == 0)
330     {
331         othis.Put(cc, 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(cc, 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(cc);
360     for(a = 0; a < arglist.length; a++)
361     {
362         othis.Put(cc, u + a, &arglist[a], 0);
363     }
364     othis.Put(cc, 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(cc) : 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(cc, a, vb, 0);
396         else
397             othis.Delete(a);
398 
399         if(va)
400             othis.Put(cc, 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(cc);
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(cc, 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(cc, 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(cc);
469 
470 version(SliceSpliceExtension){
471     d_number start;
472     d_number end;
473     switch(arglist.length)
474     {
475     case 0:
476         start = vundefined.toNumber(cc);
477         end = len;
478         break;
479 
480     case 1:
481         start = arglist[0].toNumber(cc);
482         end = len;
483         break;
484 
485     default:
486         start = arglist[0].toNumber(cc);
487 		if(arglist[1].isUndefined())
488 			end = len;
489 		else{
490 			end = arglist[1].toNumber(cc);
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(cc);
579     for(n = 0; k < r8; k++)
580     {
581         v = othis.Get(k);
582         if(v)
583         {
584             A.Put(cc, n, v, 0);
585         }
586         n++;
587     }
588 
589     A.Put(cc, TEXT_length, n, DontEnum);
590     Value.copy(ret, &A.value);
591     return null;
592 }
593 
594 /* ===================== Darray_prototype_sort ================= */
595 
596 
597 void *Darray_prototype_sort(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
598 {
599     // ECMA v3 15.4.4.11
600     Value* v;
601     d_uint32 len;
602     uint u;
603 
604     //writef("Array.prototype.sort()\n");
605     v = othis.Get(TEXT_length);
606     len = v ? v.toUint32(cc) : 0;
607 
608     // This is not optimal, as isArrayIndex is done at least twice
609     // for every array member. Additionally, the qsort() by index
610     // can be avoided if we can deduce it is not a sparse array.
611 
612     Property *p;
613     Value[] pvalues;
614     d_uint32[] pindices;
615     d_uint32 parraydim;
616     d_uint32 nprops;
617 
618     // First, size & alloc our temp array
619     if(len < 100)
620     {   // Probably not too sparse an array
621         parraydim = len;
622     }
623     else
624     {
625         parraydim = 0;
626         foreach(ref Property p; *othis.proptable)
627         {
628             if(p.attributes == 0)       // don't count special properties
629                 parraydim++;
630         }
631         if(parraydim > len)             // could theoretically happen
632             parraydim = len;
633     }
634 
635     Value[] p1 = null;
636     Value* v1;
637     version(Win32)      // eh and alloca() not working under linux
638     {
639         if(parraydim < 128)
640             v1 = cast(Value*)alloca(parraydim * Value.sizeof);
641     }
642     if(v1)
643         pvalues = v1[0 .. parraydim];
644     else
645     {
646         p1 = new Value[parraydim];
647         pvalues = p1;
648     }
649 
650     d_uint32[] p2 = null;
651     d_uint32* p3;
652     version(Win32)
653     {
654         if(parraydim < 128)
655             p3 = cast(d_uint32*)alloca(parraydim * d_uint32.sizeof);
656     }
657     if(p3)
658         pindices = p3[0 .. parraydim];
659     else
660     {
661         p2 = new d_uint32[parraydim];
662         pindices = p2;
663     }
664 
665     // Now fill it with all the Property's that are array indices
666     nprops = 0;
667     foreach(Value key, ref Property p; *othis.proptable)
668     {
669         d_uint32 index;
670 
671         if(p.attributes == 0 && key.isArrayIndex(cc, index))
672         {
673             pindices[nprops] = index;
674             Value.copy(&pvalues[nprops], &p.value);
675             nprops++;
676         }
677     }
678 
679     synchronized
680     {
681         Dobject comparefn;
682 
683         if(arglist.length)
684         {
685             if(!arglist[0].isPrimitive())
686                 comparefn = arglist[0].object;
687         }
688 
689 
690         bool compare_value(ref Value vx, ref Value vy)
691         {
692             d_string sx;
693             d_string sy;
694             int cmp;
695 
696             //writef("compare_value()\n");
697             if(vx.isUndefined())
698             {
699                 cmp = (vy.isUndefined()) ? 0 : 1;
700             }
701             else if(vy.isUndefined())
702                 cmp = -1;
703             else
704             {
705                 if(comparefn)
706                 {
707                     Value[2] arglist;
708                     Value ret;
709                     Value* v;
710                     d_number n;
711 
712                     Value.copy(&arglist[0], &vx);
713                     Value.copy(&arglist[1], &vy);
714                     ret.putVundefined();
715                     comparefn.Call(cc, comparefn, &ret, arglist);
716                     n = ret.toNumber(cc);
717                     if(n < 0)
718                         cmp = -1;
719                     else if(n > 0)
720                         cmp = 1;
721                     else
722                         cmp = 0;
723                 }
724                 else
725                 {
726                     sx = vx.toString(cc);
727                     sy = vy.toString(cc);
728                     cmp = std..string.cmp(sx, sy);
729                     if(cmp < 0)
730                         cmp = -1;
731                     else if(cmp > 0)
732                         cmp = 1;
733                 }
734             }
735             return cmp < 0;
736         }
737 
738         // Sort pvalues[]
739         import std.algorithm.sorting : sort;
740         pvalues[0 .. nprops].sort!compare_value();
741     }
742 
743     // Stuff the sorted value's back into the array
744     for(u = 0; u < nprops; u++)
745     {
746         d_uint32 index;
747 
748         othis.Put(cc, u, &pvalues[u], 0);
749         index = pindices[u];
750         if(index >= nprops)
751         {
752             othis.Delete(index);
753         }
754     }
755 
756     destroy(p1);
757     destroy(p2);
758 
759     ret.putVobject(othis);
760     return null;
761 }
762 
763 /* ===================== Darray_prototype_splice ================= */
764 
765 void *Darray_prototype_splice(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
766 {
767     // ECMA v3 15.4.4.12
768     d_uint32 len;
769     d_uint32 k;
770     
771     Value* v;
772     Darray A;
773     d_uint32 a;
774     d_uint32 delcnt;
775     d_uint32 inscnt;
776     d_uint32 startidx;
777     
778     v = othis.Get(TEXT_length);
779     if(!v)
780         v = &vundefined;
781     len = v.toUint32(cc);
782     
783 version(SliceSpliceExtension){
784     d_number start;
785     d_number deleteCount;
786     
787     switch(arglist.length)
788     {
789     case 0:
790         start = vundefined.toNumber(cc);
791         deleteCount = 0;
792         break;
793 
794     case 1:
795         start = arglist[0].toNumber(cc);
796         deleteCount = vundefined.toNumber(cc);
797         break;
798 
799     default:
800         start = arglist[0].toNumber(cc);
801         deleteCount = arglist[1].toNumber(cc);
802 		//checked later
803         break;
804     }
805     if(start == d_number.infinity)
806         startidx = len;
807     else if(start == -d_number.infinity)
808         startidx = 0;
809     else{
810         if(start < 0)
811         {
812             startidx = cast(uint)(len + start);
813             if(cast(d_int32)startidx < 0)
814                 startidx = 0;
815         }
816         else
817             startidx = cast(uint)start;
818     }
819 	startidx = startidx > len ? len : startidx; 
820     if(deleteCount == d_number.infinity)
821         delcnt = len;
822     else if(deleteCount == -d_number.infinity)
823         delcnt = 0;
824     else
825         delcnt = (cast(uint)deleteCount > 0) ? cast(uint) deleteCount : 0;
826     if(delcnt > len - startidx)
827         delcnt = len - startidx;
828 }else{    
829     long start;
830     d_int32 deleteCount;
831     switch(arglist.length)
832     {
833     case 0:
834         start = vundefined.toInt32();
835         deleteCount = 0;
836         break;
837 
838     case 1:
839         start = arglist[0].toInt32();
840         deleteCount = vundefined.toInt32();
841         break;
842 
843     default:
844         start = arglist[0].toInt32();
845         deleteCount = arglist[1].toInt32();
846 		//checked later
847         break;
848     }
849     startidx = cast(uint)start;
850 	startidx = startidx > len ? len : startidx; 
851     delcnt = (deleteCount > 0) ? deleteCount : 0;
852     if(delcnt > len - startidx)
853         delcnt = len - startidx;
854 }
855 	
856     A = new Darray(cc);
857 
858     // If deleteCount is not specified, ECMA implies it should
859     // be 0, while "JavaScript The Definitive Guide" says it should
860     // be delete to end of array. Jscript doesn't implement splice().
861     // We'll do it the Guide way.
862     if(arglist.length < 2)
863         delcnt = len - startidx;
864 
865     //writef("Darray.splice(startidx = %d, delcnt = %d)\n", startidx, delcnt);
866     for(k = 0; k != delcnt; k++)
867     {
868         v = othis.Get(startidx + k);
869         if(v)
870             A.Put(cc, k, v, 0);
871     }
872 
873     A.Put(cc, TEXT_length, delcnt, DontEnum);
874     inscnt = (arglist.length > 2) ? cast(uint)arglist.length - 2 : 0;
875     if(inscnt != delcnt)
876     {
877         if(inscnt <= delcnt)
878         {
879             for(k = startidx; k != (len - delcnt); k++)
880             {
881                 v = othis.Get(k + delcnt);
882                 if(v)
883                     othis.Put(cc, k + inscnt, v, 0);
884                 else
885                     othis.Delete(k + inscnt);
886             }
887 
888             for(k = len; k != (len - delcnt + inscnt); k--)
889                 othis.Delete(k - 1);
890         }
891         else
892         {
893             for(k = len - delcnt; k != startidx; k--)
894             {
895                 v = othis.Get(k + delcnt - 1);
896                 if(v)
897                     othis.Put(cc, k + inscnt - 1, v, 0);
898                 else
899                     othis.Delete(k + inscnt - 1);
900             }
901         }
902     }
903     k = startidx;
904     for(a = 2; a < arglist.length; a++)
905     {
906         v = &arglist[a];
907         othis.Put(cc, k, v, 0);
908         k++;
909     }
910 
911     othis.Put(cc, TEXT_length, len - delcnt + inscnt,  DontEnum);
912     Value.copy(ret, &A.value);
913     return null;
914 }
915 
916 /* ===================== Darray_prototype_unshift ================= */
917 
918 void *Darray_prototype_unshift(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
919 {
920     // ECMA v3 15.4.4.13
921     Value* v;
922     d_uint32 len;
923     d_uint32 k;
924 
925     v = othis.Get(TEXT_length);
926     if(!v)
927         v = &vundefined;
928     len = v.toUint32(cc);
929 
930     for(k = len; k>0; k--)
931     {
932         v = othis.Get(k - 1);
933         if(v)
934             othis.Put(cc, cast(uint)(k + arglist.length - 1), v, 0);
935         else
936             othis.Delete(cast(uint)(k + arglist.length - 1));
937     }
938 
939     for(k = 0; k < arglist.length; k++)
940     {
941         othis.Put(cc, k, &arglist[k], 0);
942     }
943     othis.Put(cc, TEXT_length, len + arglist.length,  DontEnum);
944     ret.putVnumber(len + arglist.length);
945     return null;
946 }
947 
948 /* =========================== Darray_prototype =================== */
949 
950 class DarrayPrototype : Darray
951 {
952     this(CallContext* cc)
953     {
954         super(cc, cc.tc.Dobject_prototype);
955         Dobject f = cc.tc.Dfunction_prototype;
956 
957         Put(cc, TEXT_constructor, cc.tc.Darray_constructor, DontEnum);
958 
959         static enum NativeFunctionData[] nfd =
960         [
961             { TEXT_toString, &Darray_prototype_toString, 0 },
962             { TEXT_toLocaleString, &Darray_prototype_toLocaleString, 0 },
963             { TEXT_toSource, &Darray_prototype_toSource, 0 },
964             { TEXT_concat, &Darray_prototype_concat, 1 },
965             { TEXT_join, &Darray_prototype_join, 1 },
966             { TEXT_pop, &Darray_prototype_pop, 0 },
967             { TEXT_push, &Darray_prototype_push, 1 },
968             { TEXT_reverse, &Darray_prototype_reverse, 0 },
969             { TEXT_shift, &Darray_prototype_shift, 0, },
970             { TEXT_slice, &Darray_prototype_slice, 2 },
971             { TEXT_sort, &Darray_prototype_sort, 1 },
972             { TEXT_splice, &Darray_prototype_splice, 2 },
973             { TEXT_unshift, &Darray_prototype_unshift, 1 },
974         ];
975 
976         DnativeFunction.initialize(this, cc, nfd, DontEnum);
977     }
978 }
979 
980 
981 /* =========================== Darray =================== */
982 
983 class Darray : Dobject
984 {
985     Value length;               // length property
986     d_uint32 ulength;
987 
988     this(CallContext* cc)
989     {
990         this(cc, getPrototype(cc));
991     }
992 
993     this(CallContext* cc, Dobject prototype)
994     {
995         super(cc, prototype);
996         length.putVnumber(0);
997         ulength = 0;
998         classname = TEXT_Array;
999     }
1000 
1001     override  Value* Put(CallContext* cc, Identifier* key, Value* value, uint attributes)
1002     {
1003         Value* result = proptable.put(&key.value, key.value.hash, value, attributes);
1004         if(!result)
1005             Put(cc, key.value..string, value, attributes);
1006         return null;
1007     }
1008 
1009     override Value* Put(CallContext* cc, d_string name, Value* v, uint attributes)
1010     {
1011         d_uint32 i;
1012         uint c;
1013         Value* result;
1014 
1015         // ECMA 15.4.5.1
1016         result = proptable.put(name, v, attributes);
1017         if(!result)
1018         {
1019             if(name == TEXT_length)
1020             {
1021                 i = v.toUint32(cc);
1022                 if(i != v.toInteger(cc))
1023                 {
1024                     ErrInfo errinfo;
1025 
1026                     return Dobject.RangeError(&errinfo, cc, ERR_LENGTH_INT);
1027                 }
1028                 if(i < ulength)
1029                 {
1030                     // delete all properties with keys >= i
1031                     d_uint32[] todelete;
1032 
1033                     foreach(Value key, ref Property p; *proptable)
1034                     {
1035                         d_uint32 j;
1036 
1037                         j = key.toUint32(cc);
1038                         if(j >= i)
1039                             todelete ~= j;
1040                     }
1041                     foreach(d_uint32 j; todelete)
1042                     {
1043                         proptable.del(j);
1044                     }
1045                 }
1046                 ulength = i;
1047                 length.number = i;
1048                 proptable.put(name, v, attributes | DontEnum);
1049             }
1050 
1051             // if (name is an array index i)
1052 
1053             i = 0;
1054             for(size_t j = 0; j < name.length; j++)
1055             {
1056                 ulong k;
1057 
1058                 c = name[j];
1059                 if(c == '0' && i == 0 && name.length > 1)
1060                     goto Lret;
1061                 if(c >= '0' && c <= '9')
1062                 {
1063                     k = i * cast(ulong)10 + c - '0';
1064                     i = cast(d_uint32)k;
1065                     if(i != k)
1066                         goto Lret;              // overflow
1067                 }
1068                 else
1069                     goto Lret;
1070             }
1071             if(i >= ulength)
1072             {
1073                 if(i == 0xFFFFFFFF)
1074                     goto Lret;
1075                 ulength = i + 1;
1076                 length.number = ulength;
1077             }
1078         }
1079         Lret:
1080         return null;
1081     }
1082 
1083     override Value* Put(CallContext* cc, d_string name, Dobject o, uint attributes)
1084     {
1085         return Put(cc, name, &o.value, attributes);
1086     }
1087 
1088     override Value* Put(CallContext* cc, d_string PropertyName, d_number n, uint attributes)
1089     {
1090         Value v;
1091 
1092         v.putVnumber(n);
1093         return Put(cc, PropertyName, &v, attributes);
1094     }
1095 
1096     override Value* Put(CallContext* cc, d_string PropertyName, d_string string, uint attributes)
1097     {
1098         Value v;
1099 
1100         v.putVstring(string);
1101         return Put(cc, PropertyName, &v, attributes);
1102     }
1103 
1104     override Value* Put(CallContext* cc, d_uint32 index, Value* vindex, Value* value, uint attributes)
1105     {
1106         if(index >= ulength)
1107             ulength = index + 1;
1108 
1109         proptable.put(vindex, index ^ 0x55555555 /*Value.calcHash(index)*/, value, attributes);
1110         return null;
1111     }
1112 
1113     override Value* Put(CallContext* cc, d_uint32 index, Value* value, uint attributes)
1114     {
1115         if(index >= ulength)
1116         {
1117             ulength = index + 1;
1118             length.number = ulength;
1119         }
1120 
1121         proptable.put(index, value, attributes);
1122         return null;
1123     }
1124 
1125     final Value* Put(CallContext* cc, d_uint32 index, d_string string, uint attributes)
1126     {
1127         if(index >= ulength)
1128         {
1129             ulength = index + 1;
1130             length.number = ulength;
1131         }
1132 
1133         proptable.put(index, string, attributes);
1134         return null;
1135     }
1136 
1137     override Value* Get(Identifier* id)
1138     {
1139         //writef("Darray.Get(%p, '%s')\n", &proptable, PropertyName);
1140         if(id.value..string == TEXT_length)
1141         {
1142             length.number = ulength;
1143             return &length;
1144         }
1145         else
1146             return Dobject.Get(id);
1147     }
1148 
1149     override Value* Get(d_string PropertyName, uint hash)
1150     {
1151         //writef("Darray.Get(%p, '%s')\n", &proptable, PropertyName);
1152         if(PropertyName == TEXT_length)
1153         {
1154             length.number = ulength;
1155             return &length;
1156         }
1157         else
1158             return Dobject.Get(PropertyName, hash);
1159     }
1160 
1161     override Value* Get(d_uint32 index)
1162     {
1163         Value* v;
1164 
1165         //writef("Darray.Get(%p, %d)\n", &proptable, index);
1166         v = proptable.get(index);
1167         return v;
1168     }
1169 
1170     override Value* Get(d_uint32 index, Value* vindex)
1171     {
1172         Value* v;
1173 
1174         //writef("Darray.Get(%p, %d)\n", &proptable, index);
1175         v = proptable.get(vindex, index ^ 0x55555555 /*Value.calcHash(index)*/);
1176         return v;
1177     }
1178 
1179     override int Delete(d_string PropertyName)
1180     {
1181         // ECMA 8.6.2.5
1182         //writef("Darray.Delete('%ls')\n", d_string_ptr(PropertyName));
1183         if(PropertyName == TEXT_length)
1184             return 0;           // can't delete 'length' property
1185         else
1186             return proptable.del(PropertyName);
1187     }
1188 
1189     override int Delete(d_uint32 index)
1190     {
1191         // ECMA 8.6.2.5
1192         return proptable.del(index);
1193     }
1194 
1195 
1196     static Dfunction getConstructor(CallContext* cc)
1197     {
1198         return cc.tc.Darray_constructor;
1199     }
1200 
1201     static Dobject getPrototype(CallContext* cc)
1202     {
1203         return cc.tc.Darray_prototype;
1204     }
1205 
1206     static void initialize(CallContext* cc)
1207     {
1208         cc.tc.Darray_constructor = new DarrayConstructor(cc);
1209         cc.tc.Darray_prototype = new DarrayPrototype(cc);
1210 
1211         cc.tc.Darray_constructor.Put(cc, TEXT_prototype, cc.tc.Darray_prototype, DontEnum |  ReadOnly);
1212     }
1213 }
1214