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