jscript: Use num_set_int where possible.
[wine] / dlls / jscript / array.c
1 /*
2  * Copyright 2008 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20 #include "wine/port.h"
21
22 #include <math.h>
23
24 #include "jscript.h"
25
26 #include "wine/debug.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
29
30 typedef struct {
31     jsdisp_t dispex;
32
33     DWORD length;
34 } ArrayInstance;
35
36 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
37 static const WCHAR concatW[] = {'c','o','n','c','a','t',0};
38 static const WCHAR joinW[] = {'j','o','i','n',0};
39 static const WCHAR popW[] = {'p','o','p',0};
40 static const WCHAR pushW[] = {'p','u','s','h',0};
41 static const WCHAR reverseW[] = {'r','e','v','e','r','s','e',0};
42 static const WCHAR shiftW[] = {'s','h','i','f','t',0};
43 static const WCHAR sliceW[] = {'s','l','i','c','e',0};
44 static const WCHAR sortW[] = {'s','o','r','t',0};
45 static const WCHAR spliceW[] = {'s','p','l','i','c','e',0};
46 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
47 static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0};
48 static const WCHAR unshiftW[] = {'u','n','s','h','i','f','t',0};
49
50 static const WCHAR default_separatorW[] = {',',0};
51
52 static inline ArrayInstance *array_from_vdisp(vdisp_t *vdisp)
53 {
54     return (ArrayInstance*)vdisp->u.jsdisp;
55 }
56
57 static inline ArrayInstance *array_this(vdisp_t *jsthis)
58 {
59     return is_vclass(jsthis, JSCLASS_ARRAY) ? array_from_vdisp(jsthis) : NULL;
60 }
61
62 static HRESULT get_length(script_ctx_t *ctx, vdisp_t *vdisp, jsexcept_t *ei, jsdisp_t **jsthis, DWORD *ret)
63 {
64     ArrayInstance *array;
65     VARIANT var;
66     HRESULT hres;
67
68     array = array_this(vdisp);
69     if(array) {
70         *jsthis = &array->dispex;
71         *ret = array->length;
72         return S_OK;
73     }
74
75     if(!is_jsdisp(vdisp))
76         return throw_type_error(ctx, ei, JS_E_JSCRIPT_EXPECTED, NULL);
77
78     hres = jsdisp_propget_name(vdisp->u.jsdisp, lengthW, &var, ei);
79     if(FAILED(hres))
80         return hres;
81
82     hres = to_uint32(ctx, &var, ei, ret);
83     VariantClear(&var);
84     if(FAILED(hres))
85         return hres;
86
87     *jsthis = vdisp->u.jsdisp;
88     return S_OK;
89 }
90
91 static HRESULT set_length(jsdisp_t *obj, jsexcept_t *ei, DWORD length)
92 {
93     VARIANT var;
94
95     if(is_class(obj, JSCLASS_ARRAY)) {
96         ((ArrayInstance*)obj)->length = length;
97         return S_OK;
98     }
99
100     num_set_int(&var, length);
101     return jsdisp_propput_name(obj, lengthW, &var, ei);
102 }
103
104 static WCHAR *idx_to_str(DWORD idx, WCHAR *ptr)
105 {
106     if(!idx) {
107         *ptr = '0';
108         return ptr;
109     }
110
111     while(idx) {
112         *ptr-- = '0' + (idx%10);
113         idx /= 10;
114     }
115
116     return ptr+1;
117 }
118
119 static HRESULT Array_length(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
120         VARIANT *retv, jsexcept_t *ei)
121 {
122     ArrayInstance *This = array_from_vdisp(jsthis);
123
124     TRACE("%p %d\n", This, This->length);
125
126     switch(flags) {
127     case DISPATCH_PROPERTYGET:
128         num_set_int(retv, This->length);
129         break;
130     case DISPATCH_PROPERTYPUT: {
131         DOUBLE len = -1;
132         DWORD i;
133         HRESULT hres;
134
135         hres = to_number(ctx, get_arg(dp, 0), ei, &len);
136         if(FAILED(hres))
137             return hres;
138
139         len = floor(len);
140         if(len!=(DWORD)len)
141             return throw_range_error(ctx, ei, JS_E_INVALID_LENGTH, NULL);
142
143         for(i=len; i<This->length; i++) {
144             hres = jsdisp_delete_idx(&This->dispex, i);
145             if(FAILED(hres))
146                 return hres;
147         }
148
149         This->length = len;
150         break;
151     }
152     default:
153         FIXME("unimplemented flags %x\n", flags);
154         return E_NOTIMPL;
155     }
156
157     return S_OK;
158 }
159
160 static HRESULT concat_array(jsdisp_t *array, ArrayInstance *obj, DWORD *len, jsexcept_t *ei)
161 {
162     VARIANT var;
163     DWORD i;
164     HRESULT hres;
165
166     for(i=0; i < obj->length; i++) {
167         hres = jsdisp_get_idx(&obj->dispex, i, &var, ei);
168         if(hres == DISP_E_UNKNOWNNAME)
169             continue;
170         if(FAILED(hres))
171             return hres;
172
173         hres = jsdisp_propput_idx(array, *len+i, &var, ei);
174         VariantClear(&var);
175         if(FAILED(hres))
176             return hres;
177     }
178
179     *len += obj->length;
180     return S_OK;
181 }
182
183 static HRESULT concat_obj(jsdisp_t *array, IDispatch *obj, DWORD *len, jsexcept_t *ei)
184 {
185     jsdisp_t *jsobj;
186     VARIANT var;
187     HRESULT hres;
188
189     jsobj = iface_to_jsdisp((IUnknown*)obj);
190     if(jsobj) {
191         if(is_class(jsobj, JSCLASS_ARRAY)) {
192             hres = concat_array(array, (ArrayInstance*)jsobj, len, ei);
193             jsdisp_release(jsobj);
194             return hres;
195         }
196         jsdisp_release(jsobj);
197     }
198
199     V_VT(&var) = VT_DISPATCH;
200     V_DISPATCH(&var) = obj;
201     return jsdisp_propput_idx(array, (*len)++, &var, ei);
202 }
203
204 static HRESULT Array_concat(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
205         VARIANT *retv, jsexcept_t *ei)
206 {
207     jsdisp_t *ret;
208     DWORD len = 0;
209     HRESULT hres;
210
211     TRACE("\n");
212
213     hres = create_array(ctx, 0, &ret);
214     if(FAILED(hres))
215         return hres;
216
217     hres = concat_obj(ret, jsthis->u.disp, &len, ei);
218     if(SUCCEEDED(hres)) {
219         VARIANT *arg;
220         DWORD i;
221
222         for(i=0; i < arg_cnt(dp); i++) {
223             arg = get_arg(dp, i);
224             if(V_VT(arg) == VT_DISPATCH)
225                 hres = concat_obj(ret, V_DISPATCH(arg), &len, ei);
226             else
227                 hres = jsdisp_propput_idx(ret, len++, arg, ei);
228             if(FAILED(hres))
229                 break;
230         }
231     }
232
233     if(FAILED(hres))
234         return hres;
235
236     if(retv)
237         var_set_jsdisp(retv, ret);
238     else
239         jsdisp_release(ret);
240     return S_OK;
241 }
242
243 static HRESULT array_join(script_ctx_t *ctx, jsdisp_t *array, DWORD length, const WCHAR *sep, VARIANT *retv, jsexcept_t *ei)
244 {
245     BSTR *str_tab, ret = NULL;
246     VARIANT var;
247     DWORD i;
248     HRESULT hres = E_FAIL;
249
250     if(!length) {
251         if(retv) {
252             V_VT(retv) = VT_BSTR;
253             V_BSTR(retv) = SysAllocStringLen(NULL, 0);
254             if(!V_BSTR(retv))
255                 return E_OUTOFMEMORY;
256         }
257         return S_OK;
258     }
259
260     str_tab = heap_alloc_zero(length * sizeof(BSTR));
261     if(!str_tab)
262         return E_OUTOFMEMORY;
263
264     for(i=0; i < length; i++) {
265         hres = jsdisp_get_idx(array, i, &var, ei);
266         if(hres == DISP_E_UNKNOWNNAME) {
267             hres = S_OK;
268             continue;
269         } else if(FAILED(hres))
270             break;
271
272         if(V_VT(&var) != VT_EMPTY && V_VT(&var) != VT_NULL)
273             hres = to_string(ctx, &var, ei, str_tab+i);
274         VariantClear(&var);
275         if(FAILED(hres))
276             break;
277     }
278
279     if(SUCCEEDED(hres)) {
280         DWORD seplen = 0, len = 0;
281         WCHAR *ptr;
282
283         seplen = strlenW(sep);
284
285         if(str_tab[0])
286             len = SysStringLen(str_tab[0]);
287         for(i=1; i < length; i++)
288             len += seplen + SysStringLen(str_tab[i]);
289
290         ret = SysAllocStringLen(NULL, len);
291         if(ret) {
292             DWORD tmplen = 0;
293
294             if(str_tab[0]) {
295                 tmplen = SysStringLen(str_tab[0]);
296                 memcpy(ret, str_tab[0], tmplen*sizeof(WCHAR));
297             }
298
299             ptr = ret + tmplen;
300             for(i=1; i < length; i++) {
301                 if(seplen) {
302                     memcpy(ptr, sep, seplen*sizeof(WCHAR));
303                     ptr += seplen;
304                 }
305
306                 if(str_tab[i]) {
307                     tmplen = SysStringLen(str_tab[i]);
308                     memcpy(ptr, str_tab[i], tmplen*sizeof(WCHAR));
309                     ptr += tmplen;
310                 }
311             }
312             *ptr=0;
313         }else {
314             hres = E_OUTOFMEMORY;
315         }
316     }
317
318     for(i=0; i < length; i++)
319         SysFreeString(str_tab[i]);
320     heap_free(str_tab);
321     if(FAILED(hres))
322         return hres;
323
324     TRACE("= %s\n", debugstr_w(ret));
325
326     if(retv) {
327         if(!ret) {
328             ret = SysAllocStringLen(NULL, 0);
329             if(!ret)
330                 return E_OUTOFMEMORY;
331         }
332
333         V_VT(retv) = VT_BSTR;
334         V_BSTR(retv) = ret;
335     }else {
336         SysFreeString(ret);
337     }
338
339     return S_OK;
340 }
341
342 /* ECMA-262 3rd Edition    15.4.4.5 */
343 static HRESULT Array_join(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, DISPPARAMS *dp,
344         VARIANT *retv, jsexcept_t *ei)
345 {
346     jsdisp_t *jsthis;
347     DWORD length;
348     HRESULT hres;
349
350     TRACE("\n");
351
352     hres = get_length(ctx, vthis, ei, &jsthis, &length);
353     if(FAILED(hres))
354         return hres;
355
356     if(arg_cnt(dp)) {
357         BSTR sep;
358
359         hres = to_string(ctx, get_arg(dp,0), ei, &sep);
360         if(FAILED(hres))
361             return hres;
362
363         hres = array_join(ctx, jsthis, length, sep, retv, ei);
364
365         SysFreeString(sep);
366     }else {
367         hres = array_join(ctx, jsthis, length, default_separatorW, retv, ei);
368     }
369
370     return hres;
371 }
372
373 static HRESULT Array_pop(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, DISPPARAMS *dp,
374         VARIANT *retv, jsexcept_t *ei)
375 {
376     jsdisp_t *jsthis;
377     VARIANT val;
378     DWORD length;
379     HRESULT hres;
380
381     TRACE("\n");
382
383     hres = get_length(ctx, vthis, ei, &jsthis, &length);
384     if(FAILED(hres))
385         return hres;
386
387     if(!length) {
388         hres = set_length(jsthis, ei, 0);
389         if(FAILED(hres))
390             return hres;
391
392         if(retv)
393             V_VT(retv) = VT_EMPTY;
394         return S_OK;
395     }
396
397     length--;
398     hres = jsdisp_get_idx(jsthis, length, &val, ei);
399     if(SUCCEEDED(hres)) {
400         hres = jsdisp_delete_idx(jsthis, length);
401     } else if(hres == DISP_E_UNKNOWNNAME) {
402         V_VT(&val) = VT_EMPTY;
403         hres = S_OK;
404     } else
405         return hres;
406
407     if(SUCCEEDED(hres))
408         hres = set_length(jsthis, ei, length);
409
410     if(FAILED(hres)) {
411         VariantClear(&val);
412         return hres;
413     }
414
415     if(retv)
416         *retv = val;
417     else
418         VariantClear(&val);
419
420     return S_OK;
421 }
422
423 /* ECMA-262 3rd Edition    15.4.4.7 */
424 static HRESULT Array_push(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, DISPPARAMS *dp,
425         VARIANT *retv, jsexcept_t *ei)
426 {
427     jsdisp_t *jsthis;
428     DWORD length = 0;
429     int i, n;
430     HRESULT hres;
431
432     TRACE("\n");
433
434     hres = get_length(ctx, vthis, ei, &jsthis, &length);
435     if(FAILED(hres))
436         return hres;
437
438     n = arg_cnt(dp);
439     for(i=0; i < n; i++) {
440         hres = jsdisp_propput_idx(jsthis, length+i, get_arg(dp, i), ei);
441         if(FAILED(hres))
442             return hres;
443     }
444
445     hres = set_length(jsthis, ei, length+n);
446     if(FAILED(hres))
447         return hres;
448
449     if(retv)
450         num_set_int(retv, length+n);
451     return S_OK;
452 }
453
454 static HRESULT Array_reverse(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, DISPPARAMS *dp,
455         VARIANT *retv, jsexcept_t *ei)
456 {
457     jsdisp_t *jsthis;
458     DWORD length, k, l;
459     VARIANT v1, v2;
460     HRESULT hres1, hres2;
461
462     TRACE("\n");
463
464     hres1 = get_length(ctx, vthis, ei, &jsthis, &length);
465     if(FAILED(hres1))
466         return hres1;
467
468     for(k=0; k<length/2; k++) {
469         l = length-k-1;
470
471         hres1 = jsdisp_get_idx(jsthis, k, &v1, ei);
472         if(FAILED(hres1) && hres1!=DISP_E_UNKNOWNNAME)
473             return hres1;
474
475         hres2 = jsdisp_get_idx(jsthis, l, &v2, ei);
476         if(FAILED(hres2) && hres2!=DISP_E_UNKNOWNNAME) {
477             VariantClear(&v1);
478             return hres2;
479         }
480
481         if(hres1 == DISP_E_UNKNOWNNAME)
482             hres1 = jsdisp_delete_idx(jsthis, l);
483         else
484             hres1 = jsdisp_propput_idx(jsthis, l, &v1, ei);
485
486         if(FAILED(hres1)) {
487             VariantClear(&v1);
488             VariantClear(&v2);
489             return hres1;
490         }
491
492         if(hres2 == DISP_E_UNKNOWNNAME)
493             hres2 = jsdisp_delete_idx(jsthis, k);
494         else
495             hres2 = jsdisp_propput_idx(jsthis, k, &v2, ei);
496
497         if(FAILED(hres2)) {
498             VariantClear(&v2);
499             return hres2;
500         }
501     }
502
503     if(retv) {
504         jsdisp_addref(jsthis);
505         var_set_jsdisp(retv, jsthis);
506     }
507
508     return S_OK;
509 }
510
511 /* ECMA-262 3rd Edition    15.4.4.9 */
512 static HRESULT Array_shift(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, DISPPARAMS *dp,
513         VARIANT *retv, jsexcept_t *ei)
514 {
515     jsdisp_t *jsthis;
516     DWORD length = 0, i;
517     VARIANT v, ret;
518     HRESULT hres;
519
520     TRACE("\n");
521
522     hres = get_length(ctx, vthis, ei, &jsthis, &length);
523     if(FAILED(hres))
524         return hres;
525
526     if(!length) {
527         hres = set_length(jsthis, ei, 0);
528         if(FAILED(hres))
529             return hres;
530     }
531
532     if(!length) {
533         if(retv)
534             V_VT(retv) = VT_EMPTY;
535         return S_OK;
536     }
537
538     hres = jsdisp_get_idx(jsthis, 0, &ret, ei);
539     if(hres == DISP_E_UNKNOWNNAME) {
540         V_VT(&ret) = VT_EMPTY;
541         hres = S_OK;
542     }
543
544     for(i=1; SUCCEEDED(hres) && i<length; i++) {
545         hres = jsdisp_get_idx(jsthis, i, &v, ei);
546         if(hres == DISP_E_UNKNOWNNAME)
547             hres = jsdisp_delete_idx(jsthis, i-1);
548         else if(SUCCEEDED(hres))
549             hres = jsdisp_propput_idx(jsthis, i-1, &v, ei);
550     }
551
552     if(SUCCEEDED(hres)) {
553         hres = jsdisp_delete_idx(jsthis, length-1);
554         if(SUCCEEDED(hres))
555             hres = set_length(jsthis, ei, length-1);
556     }
557
558     if(SUCCEEDED(hres) && retv)
559         *retv = ret;
560     else
561         VariantClear(&ret);
562     return hres;
563 }
564
565 /* ECMA-262 3rd Edition    15.4.4.10 */
566 static HRESULT Array_slice(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, DISPPARAMS *dp,
567         VARIANT *retv, jsexcept_t *ei)
568 {
569     jsdisp_t *arr, *jsthis;
570     DOUBLE range;
571     DWORD length, start, end, idx;
572     HRESULT hres;
573
574     TRACE("\n");
575
576     hres = get_length(ctx, vthis, ei, &jsthis, &length);
577     if(FAILED(hres))
578         return hres;
579
580     if(arg_cnt(dp)) {
581         hres = to_number(ctx, get_arg(dp, 0), ei, &range);
582         if(FAILED(hres))
583             return hres;
584
585         range = floor(range);
586         if(-range>length || isnan(range)) start = 0;
587         else if(range < 0) start = range+length;
588         else if(range <= length) start = range;
589         else start = length;
590     }
591     else start = 0;
592
593     if(arg_cnt(dp)>1) {
594         hres = to_number(ctx, get_arg(dp, 1), ei, &range);
595         if(FAILED(hres))
596             return hres;
597
598         range = floor(range);
599         if(-range>length) end = 0;
600         else if(range < 0) end = range+length;
601         else if(range <= length) end = range;
602         else end = length;
603     }
604     else end = length;
605
606     hres = create_array(ctx, (end>start)?end-start:0, &arr);
607     if(FAILED(hres))
608         return hres;
609
610     for(idx=start; idx<end; idx++) {
611         VARIANT v;
612
613         hres = jsdisp_get_idx(jsthis, idx, &v, ei);
614         if(hres == DISP_E_UNKNOWNNAME)
615             continue;
616
617         if(SUCCEEDED(hres)) {
618             hres = jsdisp_propput_idx(arr, idx-start, &v, ei);
619             VariantClear(&v);
620         }
621
622         if(FAILED(hres)) {
623             jsdisp_release(arr);
624             return hres;
625         }
626     }
627
628     if(retv)
629         var_set_jsdisp(retv, arr);
630     else
631         jsdisp_release(arr);
632
633     return S_OK;
634 }
635
636 static HRESULT sort_cmp(script_ctx_t *ctx, jsdisp_t *cmp_func, VARIANT *v1, VARIANT *v2, jsexcept_t *ei, INT *cmp)
637 {
638     HRESULT hres;
639
640     if(cmp_func) {
641         VARIANTARG args[2];
642         DISPPARAMS dp = {args, NULL, 2, 0};
643         double n;
644         VARIANT res;
645
646         args[0] = *v2;
647         args[1] = *v1;
648
649         hres = jsdisp_call_value(cmp_func, DISPATCH_METHOD, &dp, &res, ei);
650         if(FAILED(hres))
651             return hres;
652
653         hres = to_number(ctx, &res, ei, &n);
654         VariantClear(&res);
655         if(FAILED(hres))
656             return hres;
657
658         if(n == 0)
659             *cmp = 0;
660         *cmp = n > 0.0 ? 1 : -1;
661     }else if(V_VT(v1) == VT_EMPTY) {
662         *cmp = V_VT(v2) == VT_EMPTY ? 0 : 1;
663     }else if(V_VT(v2) == VT_EMPTY) {
664         *cmp = -1;
665     }else if(is_num_vt(V_VT(v1)) && is_num_vt(V_VT(v2))) {
666         DOUBLE d = num_val(v1)-num_val(v2);
667         if(d > 0.0)
668             *cmp = 1;
669         else
670             *cmp = d < -0.0 ? -1 : 0;
671     }else {
672         BSTR x, y;
673
674         hres = to_string(ctx, v1, ei, &x);
675         if(FAILED(hres))
676             return hres;
677
678         hres = to_string(ctx, v2, ei, &y);
679         if(SUCCEEDED(hres)) {
680             *cmp = strcmpW(x, y);
681             SysFreeString(y);
682         }
683         SysFreeString(x);
684         if(FAILED(hres))
685             return hres;
686     }
687
688     return S_OK;
689 }
690
691 /* ECMA-262 3rd Edition    15.4.4.11 */
692 static HRESULT Array_sort(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, DISPPARAMS *dp,
693         VARIANT *retv, jsexcept_t *ei)
694 {
695     jsdisp_t *jsthis, *cmp_func = NULL;
696     VARIANT *vtab, **sorttab = NULL;
697     DWORD length;
698     DWORD i;
699     HRESULT hres = S_OK;
700
701     TRACE("\n");
702
703     hres = get_length(ctx, vthis, ei, &jsthis, &length);
704     if(FAILED(hres))
705         return hres;
706
707     if(arg_cnt(dp) > 1) {
708         WARN("invalid arg_cnt %d\n", arg_cnt(dp));
709         return E_FAIL;
710     }
711
712     if(arg_cnt(dp) == 1) {
713         VARIANT *arg = get_arg(dp, 0);
714
715         if(V_VT(arg) != VT_DISPATCH) {
716             WARN("arg is not dispatch\n");
717             return E_FAIL;
718         }
719
720
721         cmp_func = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
722         if(!cmp_func || !is_class(cmp_func, JSCLASS_FUNCTION)) {
723             WARN("cmp_func is not a function\n");
724             if(cmp_func)
725                 jsdisp_release(cmp_func);
726             return E_FAIL;
727         }
728     }
729
730     if(!length) {
731         if(cmp_func)
732             jsdisp_release(cmp_func);
733         if(retv) {
734             jsdisp_addref(jsthis);
735             var_set_jsdisp(retv, jsthis);
736         }
737         return S_OK;
738     }
739
740     vtab = heap_alloc_zero(length * sizeof(VARIANT));
741     if(vtab) {
742         for(i=0; i<length; i++) {
743             hres = jsdisp_get_idx(jsthis, i, vtab+i, ei);
744             if(hres == DISP_E_UNKNOWNNAME) {
745                 V_VT(vtab+i) = VT_EMPTY;
746                 hres = S_OK;
747             } else if(FAILED(hres)) {
748                 WARN("Could not get elem %d: %08x\n", i, hres);
749                 break;
750             }
751         }
752     }else {
753         hres = E_OUTOFMEMORY;
754     }
755
756     if(SUCCEEDED(hres)) {
757         sorttab = heap_alloc(length*2*sizeof(VARIANT*));
758         if(!sorttab)
759             hres = E_OUTOFMEMORY;
760     }
761
762     /* merge-sort */
763     if(SUCCEEDED(hres)) {
764         VARIANT *tmpv, **tmpbuf;
765         INT cmp;
766
767         tmpbuf = sorttab + length;
768         for(i=0; i < length; i++)
769             sorttab[i] = vtab+i;
770
771         for(i=0; i < length/2; i++) {
772             hres = sort_cmp(ctx, cmp_func, sorttab[2*i+1], sorttab[2*i], ei, &cmp);
773             if(FAILED(hres))
774                 break;
775
776             if(cmp < 0) {
777                 tmpv = sorttab[2*i];
778                 sorttab[2*i] = sorttab[2*i+1];
779                 sorttab[2*i+1] = tmpv;
780             }
781         }
782
783         if(SUCCEEDED(hres)) {
784             DWORD k, a, b, bend;
785
786             for(k=2; k < length; k *= 2) {
787                 for(i=0; i+k < length; i += 2*k) {
788                     a = b = 0;
789                     if(i+2*k <= length)
790                         bend = k;
791                     else
792                         bend = length - (i+k);
793
794                     memcpy(tmpbuf, sorttab+i, k*sizeof(VARIANT*));
795
796                     while(a < k && b < bend) {
797                         hres = sort_cmp(ctx, cmp_func, tmpbuf[a], sorttab[i+k+b], ei, &cmp);
798                         if(FAILED(hres))
799                             break;
800
801                         if(cmp < 0) {
802                             sorttab[i+a+b] = tmpbuf[a];
803                             a++;
804                         }else {
805                             sorttab[i+a+b] = sorttab[i+k+b];
806                             b++;
807                         }
808                     }
809
810                     if(FAILED(hres))
811                         break;
812
813                     if(a < k)
814                         memcpy(sorttab+i+a+b, tmpbuf+a, (k-a)*sizeof(VARIANT*));
815                 }
816
817                 if(FAILED(hres))
818                     break;
819             }
820         }
821
822         for(i=0; SUCCEEDED(hres) && i < length; i++)
823             hres = jsdisp_propput_idx(jsthis, i, sorttab[i], ei);
824     }
825
826     if(vtab) {
827         for(i=0; i < length; i++)
828             VariantClear(vtab+i);
829         heap_free(vtab);
830     }
831     heap_free(sorttab);
832     if(cmp_func)
833         jsdisp_release(cmp_func);
834
835     if(FAILED(hres))
836         return hres;
837
838     if(retv) {
839         jsdisp_addref(jsthis);
840         var_set_jsdisp(retv, jsthis);
841     }
842
843     return S_OK;
844 }
845
846 /* ECMA-262 3rd Edition    15.4.4.12 */
847 static HRESULT Array_splice(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, DISPPARAMS *dp,
848         VARIANT *retv, jsexcept_t *ei)
849 {
850     DWORD length, start=0, delete_cnt=0, argc, i, add_args = 0;
851     jsdisp_t *ret_array = NULL, *jsthis;
852     VARIANT v;
853     double d;
854     int n;
855     HRESULT hres = S_OK;
856
857     TRACE("\n");
858
859     hres = get_length(ctx, vthis, ei, &jsthis, &length);
860     if(FAILED(hres))
861         return hres;
862
863     argc = arg_cnt(dp);
864     if(argc >= 1) {
865         hres = to_integer(ctx, get_arg(dp,0), ei, &d);
866         if(FAILED(hres))
867             return hres;
868
869         if(is_int32(d)) {
870             if((n = d) >= 0)
871                 start = min(n, length);
872             else
873                 start = -n > length ? 0 : length + n;
874         }else {
875             start = d < 0.0 ? 0 : length;
876         }
877     }
878
879     if(argc >= 2) {
880         hres = to_integer(ctx, get_arg(dp,1), ei, &d);
881         if(FAILED(hres))
882             return hres;
883
884         if(is_int32(d)) {
885             if((n = d) > 0)
886                 delete_cnt = min(n, length-start);
887         }else if(d > 0.0) {
888             delete_cnt = length-start;
889         }
890
891         add_args = argc-2;
892     }
893
894     if(retv) {
895         hres = create_array(ctx, 0, &ret_array);
896         if(FAILED(hres))
897             return hres;
898
899         for(i=0; SUCCEEDED(hres) && i < delete_cnt; i++) {
900             hres = jsdisp_get_idx(jsthis, start+i, &v, ei);
901             if(hres == DISP_E_UNKNOWNNAME)
902                 hres = S_OK;
903             else if(SUCCEEDED(hres))
904                 hres = jsdisp_propput_idx(ret_array, i, &v, ei);
905         }
906
907         if(SUCCEEDED(hres)) {
908             num_set_int(&v, delete_cnt);
909             hres = jsdisp_propput_name(ret_array, lengthW, &v, ei);
910         }
911     }
912
913     if(add_args < delete_cnt) {
914         for(i = start; SUCCEEDED(hres) && i < length-delete_cnt; i++) {
915             hres = jsdisp_get_idx(jsthis, i+delete_cnt, &v, ei);
916             if(hres == DISP_E_UNKNOWNNAME)
917                 hres = jsdisp_delete_idx(jsthis, i+add_args);
918             else if(SUCCEEDED(hres))
919                 hres = jsdisp_propput_idx(jsthis, i+add_args, &v, ei);
920         }
921
922         for(i=length; SUCCEEDED(hres) && i != length-delete_cnt+add_args; i--)
923             hres = jsdisp_delete_idx(jsthis, i-1);
924     }else if(add_args > delete_cnt) {
925         for(i=length-delete_cnt; SUCCEEDED(hres) && i != start; i--) {
926             hres = jsdisp_get_idx(jsthis, i+delete_cnt-1, &v, ei);
927             if(hres == DISP_E_UNKNOWNNAME)
928                 hres = jsdisp_delete_idx(jsthis, i+add_args-1);
929             else if(SUCCEEDED(hres))
930                 hres = jsdisp_propput_idx(jsthis, i+add_args-1, &v, ei);
931         }
932     }
933
934     for(i=0; SUCCEEDED(hres) && i < add_args; i++)
935         hres = jsdisp_propput_idx(jsthis, start+i, get_arg(dp,i+2), ei);
936
937     if(SUCCEEDED(hres)) {
938         num_set_int(&v, length-delete_cnt+add_args);
939         hres = jsdisp_propput_name(jsthis, lengthW, &v, ei);
940     }
941
942     if(FAILED(hres)) {
943         if(ret_array)
944             jsdisp_release(ret_array);
945         return hres;
946     }
947
948     if(retv)
949         var_set_jsdisp(retv, ret_array);
950     return S_OK;
951 }
952
953 /* ECMA-262 3rd Edition    15.4.4.2 */
954 static HRESULT Array_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
955         VARIANT *retv, jsexcept_t *ei)
956 {
957     ArrayInstance *array;
958
959     TRACE("\n");
960
961     array = array_this(jsthis);
962     if(!array)
963         return throw_type_error(ctx, ei, JS_E_ARRAY_EXPECTED, NULL);
964
965     return array_join(ctx, &array->dispex, array->length, default_separatorW, retv, ei);
966 }
967
968 static HRESULT Array_toLocaleString(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, DISPPARAMS *dp,
969         VARIANT *retv, jsexcept_t *ei)
970 {
971     FIXME("\n");
972     return E_NOTIMPL;
973 }
974
975 /* ECMA-262 3rd Edition    15.4.4.13 */
976 static HRESULT Array_unshift(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, DISPPARAMS *dp,
977         VARIANT *retv, jsexcept_t *ei)
978 {
979     jsdisp_t *jsthis;
980     WCHAR buf[14], *buf_end, *str;
981     DWORD argc, i, length;
982     VARIANT var;
983     DISPID id;
984     HRESULT hres;
985
986     TRACE("\n");
987
988     hres = get_length(ctx, vthis, ei, &jsthis, &length);
989     if(FAILED(hres))
990         return hres;
991
992     argc = arg_cnt(dp);
993     if(argc) {
994         buf_end = buf + sizeof(buf)/sizeof(WCHAR)-1;
995         *buf_end-- = 0;
996         i = length;
997
998         while(i--) {
999             str = idx_to_str(i, buf_end);
1000
1001             hres = jsdisp_get_id(jsthis, str, 0, &id);
1002             if(SUCCEEDED(hres)) {
1003                 hres = jsdisp_propget(jsthis, id, &var, ei);
1004                 if(FAILED(hres))
1005                     return hres;
1006
1007                 hres = jsdisp_propput_idx(jsthis, i+argc, &var, ei);
1008                 VariantClear(&var);
1009             }else if(hres == DISP_E_UNKNOWNNAME) {
1010                 hres = IDispatchEx_DeleteMemberByDispID(vthis->u.dispex, id);
1011             }
1012         }
1013
1014         if(FAILED(hres))
1015             return hres;
1016     }
1017
1018     for(i=0; i<argc; i++) {
1019         hres = jsdisp_propput_idx(jsthis, i, get_arg(dp,i), ei);
1020         if(FAILED(hres))
1021             return hres;
1022     }
1023
1024     if(argc) {
1025         length += argc;
1026         hres = set_length(jsthis, ei, length);
1027         if(FAILED(hres))
1028             return hres;
1029     }
1030
1031     if(retv) {
1032         if(ctx->version < 2)
1033             V_VT(retv) = VT_EMPTY;
1034         else
1035             num_set_int(retv, length);
1036     }
1037     return S_OK;
1038 }
1039
1040 static HRESULT Array_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1041         VARIANT *retv, jsexcept_t *ei)
1042 {
1043     TRACE("\n");
1044
1045     switch(flags) {
1046     case INVOKE_FUNC:
1047         return throw_type_error(ctx, ei, JS_E_FUNCTION_EXPECTED, NULL);
1048     case INVOKE_PROPERTYGET:
1049         return array_join(ctx, jsthis->u.jsdisp, array_from_vdisp(jsthis)->length, default_separatorW, retv, ei);
1050     default:
1051         FIXME("unimplemented flags %x\n", flags);
1052         return E_NOTIMPL;
1053     }
1054
1055     return S_OK;
1056 }
1057
1058 static void Array_destructor(jsdisp_t *dispex)
1059 {
1060     heap_free(dispex);
1061 }
1062
1063 static void Array_on_put(jsdisp_t *dispex, const WCHAR *name)
1064 {
1065     ArrayInstance *array = (ArrayInstance*)dispex;
1066     const WCHAR *ptr = name;
1067     DWORD id = 0;
1068
1069     if(!isdigitW(*ptr))
1070         return;
1071
1072     while(*ptr && isdigitW(*ptr)) {
1073         id = id*10 + (*ptr-'0');
1074         ptr++;
1075     }
1076
1077     if(*ptr)
1078         return;
1079
1080     if(id >= array->length)
1081         array->length = id+1;
1082 }
1083
1084 static const builtin_prop_t Array_props[] = {
1085     {concatW,                Array_concat,               PROPF_METHOD|1},
1086     {joinW,                  Array_join,                 PROPF_METHOD|1},
1087     {lengthW,                Array_length,               0},
1088     {popW,                   Array_pop,                  PROPF_METHOD},
1089     {pushW,                  Array_push,                 PROPF_METHOD|1},
1090     {reverseW,               Array_reverse,              PROPF_METHOD},
1091     {shiftW,                 Array_shift,                PROPF_METHOD},
1092     {sliceW,                 Array_slice,                PROPF_METHOD|2},
1093     {sortW,                  Array_sort,                 PROPF_METHOD|1},
1094     {spliceW,                Array_splice,               PROPF_METHOD|2},
1095     {toLocaleStringW,        Array_toLocaleString,       PROPF_METHOD},
1096     {toStringW,              Array_toString,             PROPF_METHOD},
1097     {unshiftW,               Array_unshift,              PROPF_METHOD|1},
1098 };
1099
1100 static const builtin_info_t Array_info = {
1101     JSCLASS_ARRAY,
1102     {NULL, Array_value, 0},
1103     sizeof(Array_props)/sizeof(*Array_props),
1104     Array_props,
1105     Array_destructor,
1106     Array_on_put
1107 };
1108
1109 static HRESULT ArrayConstr_value(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, DISPPARAMS *dp,
1110         VARIANT *retv, jsexcept_t *ei)
1111 {
1112     jsdisp_t *obj;
1113     VARIANT *arg_var;
1114     DWORD i;
1115     HRESULT hres;
1116
1117     TRACE("\n");
1118
1119     switch(flags) {
1120     case DISPATCH_METHOD:
1121     case DISPATCH_CONSTRUCT: {
1122         if(arg_cnt(dp) == 1 && V_VT((arg_var = get_arg(dp, 0))) == VT_I4) {
1123             if(V_I4(arg_var) < 0)
1124                 return throw_range_error(ctx, ei, JS_E_INVALID_LENGTH, NULL);
1125
1126             hres = create_array(ctx, V_I4(arg_var), &obj);
1127             if(FAILED(hres))
1128                 return hres;
1129
1130             var_set_jsdisp(retv, obj);
1131             return S_OK;
1132         }
1133
1134         hres = create_array(ctx, arg_cnt(dp), &obj);
1135         if(FAILED(hres))
1136             return hres;
1137
1138         for(i=0; i < arg_cnt(dp); i++) {
1139             hres = jsdisp_propput_idx(obj, i, get_arg(dp, i), ei);
1140             if(FAILED(hres))
1141                 break;
1142         }
1143         if(FAILED(hres)) {
1144             jsdisp_release(obj);
1145             return hres;
1146         }
1147
1148         var_set_jsdisp(retv, obj);
1149         break;
1150     }
1151     default:
1152         FIXME("unimplemented flags: %x\n", flags);
1153         return E_NOTIMPL;
1154     }
1155
1156     return S_OK;
1157 }
1158
1159 static HRESULT alloc_array(script_ctx_t *ctx, jsdisp_t *object_prototype, ArrayInstance **ret)
1160 {
1161     ArrayInstance *array;
1162     HRESULT hres;
1163
1164     array = heap_alloc_zero(sizeof(ArrayInstance));
1165     if(!array)
1166         return E_OUTOFMEMORY;
1167
1168     if(object_prototype)
1169         hres = init_dispex(&array->dispex, ctx, &Array_info, object_prototype);
1170     else
1171         hres = init_dispex_from_constr(&array->dispex, ctx, &Array_info, ctx->array_constr);
1172
1173     if(FAILED(hres)) {
1174         heap_free(array);
1175         return hres;
1176     }
1177
1178     *ret = array;
1179     return S_OK;
1180 }
1181
1182 HRESULT create_array_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1183 {
1184     ArrayInstance *array;
1185     HRESULT hres;
1186
1187     static const WCHAR ArrayW[] = {'A','r','r','a','y',0};
1188
1189     hres = alloc_array(ctx, object_prototype, &array);
1190     if(FAILED(hres))
1191         return hres;
1192
1193     hres = create_builtin_function(ctx, ArrayConstr_value, ArrayW, NULL, PROPF_CONSTR|1, &array->dispex, ret);
1194
1195     jsdisp_release(&array->dispex);
1196     return hres;
1197 }
1198
1199 HRESULT create_array(script_ctx_t *ctx, DWORD length, jsdisp_t **ret)
1200 {
1201     ArrayInstance *array;
1202     HRESULT hres;
1203
1204     hres = alloc_array(ctx, NULL, &array);
1205     if(FAILED(hres))
1206         return hres;
1207
1208     array->length = length;
1209
1210     *ret = &array->dispex;
1211     return S_OK;
1212 }