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