jscript: Update French translation.
[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, lcid, &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, lcid, &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, lcid, &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, lcid, &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 static HRESULT Array_shift(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
454         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
455 {
456     FIXME("\n");
457     return E_NOTIMPL;
458 }
459
460 static HRESULT Array_slice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
461         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
462 {
463     DispatchEx *arr;
464     VARIANT v;
465     DOUBLE range;
466     DWORD length, start, end, idx;
467     HRESULT hres;
468
469     TRACE("\n");
470
471     if(is_class(dispex, JSCLASS_ARRAY)) {
472         length = ((ArrayInstance*)dispex)->length;
473     }else {
474         hres = get_jsdisp_length(dispex, lcid, ei, &length);
475         if(FAILED(hres))
476             return hres;
477     }
478
479     if(arg_cnt(dp)) {
480         hres = to_number(dispex->ctx, get_arg(dp, 0), ei, &v);
481         if(FAILED(hres))
482             return hres;
483
484         if(V_VT(&v) == VT_I4)
485             range = V_I4(&v);
486         else
487             range = floor(V_R8(&v));
488
489         if(-range>length || isnan(range)) start = 0;
490         else if(range < 0) start = range+length;
491         else if(range <= length) start = range;
492         else start = length;
493     }
494     else start = 0;
495
496     if(arg_cnt(dp)>1) {
497         hres = to_number(dispex->ctx, get_arg(dp, 1), ei, &v);
498         if(FAILED(hres))
499             return hres;
500
501         if(V_VT(&v) == VT_I4)
502             range = V_I4(&v);
503         else
504             range = floor(V_R8(&v));
505
506         if(-range>length) end = 0;
507         else if(range < 0) end = range+length;
508         else if(range <= length) end = range;
509         else end = length;
510     }
511     else end = length;
512
513     hres = create_array(dispex->ctx, (end>start)?end-start:0, &arr);
514     if(FAILED(hres))
515         return hres;
516
517     for(idx=start; idx<end; idx++) {
518         hres = jsdisp_propget_idx(dispex, idx, lcid, &v, ei, sp);
519         if(hres == DISP_E_UNKNOWNNAME)
520             continue;
521
522         if(SUCCEEDED(hres))
523             hres = jsdisp_propput_idx(arr, idx-start, lcid, &v, ei, sp);
524
525         if(FAILED(hres)) {
526             jsdisp_release(arr);
527             return hres;
528         }
529     }
530
531     if(retv) {
532         V_VT(retv) = VT_DISPATCH;
533         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(arr);
534     }
535     else
536         jsdisp_release(arr);
537
538     return S_OK;
539 }
540
541 static HRESULT sort_cmp(script_ctx_t *ctx, DispatchEx *cmp_func, VARIANT *v1, VARIANT *v2, jsexcept_t *ei,
542         IServiceProvider *caller, INT *cmp)
543 {
544     HRESULT hres;
545
546     if(cmp_func) {
547         VARIANTARG args[2];
548         DISPPARAMS dp = {args, NULL, 2, 0};
549         VARIANT tmp;
550         VARIANT res;
551
552         args[0] = *v2;
553         args[1] = *v1;
554
555         hres = jsdisp_call_value(cmp_func, ctx->lcid, DISPATCH_METHOD, &dp, &res, ei, caller);
556         if(FAILED(hres))
557             return hres;
558
559         hres = to_number(ctx, &res, ei, &tmp);
560         VariantClear(&res);
561         if(FAILED(hres))
562             return hres;
563
564         if(V_VT(&tmp) == VT_I4)
565             *cmp = V_I4(&tmp);
566         else
567             *cmp = V_R8(&tmp) > 0.0 ? 1 : -1;
568     }else if(is_num_vt(V_VT(v1))) {
569         if(is_num_vt(V_VT(v2))) {
570             DOUBLE d = num_val(v1)-num_val(v2);
571             if(d > 0.0)
572                 *cmp = 1;
573             else if(d < -0.0)
574                 *cmp = -1;
575             else
576                 *cmp = 0;
577         }else {
578             *cmp = -1;
579         }
580     }else if(is_num_vt(V_VT(v2))) {
581         *cmp = 1;
582     }else if(V_VT(v1) == VT_BSTR) {
583         if(V_VT(v2) == VT_BSTR)
584             *cmp = strcmpW(V_BSTR(v1), V_BSTR(v2));
585         else
586             *cmp = -1;
587     }else if(V_VT(v2) == VT_BSTR) {
588         *cmp = 1;
589     }else {
590         *cmp = 0;
591     }
592
593     return S_OK;
594 }
595
596 /* ECMA-262 3rd Edition    15.4.4.11 */
597 static HRESULT Array_sort(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
598         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
599 {
600     DispatchEx *cmp_func = NULL;
601     VARIANT *vtab, **sorttab = NULL;
602     DWORD length;
603     DWORD i;
604     HRESULT hres = S_OK;
605
606     TRACE("\n");
607
608     if(is_class(dispex, JSCLASS_ARRAY)) {
609         length = ((ArrayInstance*)dispex)->length;
610     }else {
611         FIXME("unsupported this not array\n");
612         return E_NOTIMPL;
613     }
614
615     if(arg_cnt(dp) > 1) {
616         WARN("invalid arg_cnt %d\n", arg_cnt(dp));
617         return E_FAIL;
618     }
619
620     if(arg_cnt(dp) == 1) {
621         VARIANT *arg = get_arg(dp, 0);
622
623         if(V_VT(arg) != VT_DISPATCH) {
624             WARN("arg is not dispatch\n");
625             return E_FAIL;
626         }
627
628
629         cmp_func = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
630         if(!cmp_func || !is_class(cmp_func, JSCLASS_FUNCTION)) {
631             WARN("cmp_func is not a function\n");
632             if(cmp_func)
633                 jsdisp_release(cmp_func);
634             return E_FAIL;
635         }
636     }
637
638     if(!length) {
639         if(cmp_func)
640             jsdisp_release(cmp_func);
641         if(retv) {
642             V_VT(retv) = VT_DISPATCH;
643             V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(dispex);
644             IDispatchEx_AddRef(_IDispatchEx_(dispex));
645         }
646         return S_OK;
647     }
648
649     vtab = heap_alloc_zero(length * sizeof(VARIANT));
650     if(vtab) {
651         for(i=0; i<length; i++) {
652             hres = jsdisp_propget_idx(dispex, i, lcid, vtab+i, ei, caller);
653             if(FAILED(hres) && hres != DISP_E_UNKNOWNNAME) {
654                 WARN("Could not get elem %d: %08x\n", i, hres);
655                 break;
656             }
657         }
658     }else {
659         hres = E_OUTOFMEMORY;
660     }
661
662     if(SUCCEEDED(hres)) {
663         sorttab = heap_alloc(length*2*sizeof(VARIANT*));
664         if(!sorttab)
665             hres = E_OUTOFMEMORY;
666     }
667
668     /* merge-sort */
669     if(SUCCEEDED(hres)) {
670         VARIANT *tmpv, **tmpbuf;
671         INT cmp;
672
673         tmpbuf = sorttab + length;
674         for(i=0; i < length; i++)
675             sorttab[i] = vtab+i;
676
677         for(i=0; i < length/2; i++) {
678             hres = sort_cmp(dispex->ctx, cmp_func, sorttab[2*i+1], sorttab[2*i], ei, caller, &cmp);
679             if(FAILED(hres))
680                 break;
681
682             if(cmp < 0) {
683                 tmpv = sorttab[2*i];
684                 sorttab[2*i] = sorttab[2*i+1];
685                 sorttab[2*i+1] = tmpv;
686             }
687         }
688
689         if(SUCCEEDED(hres)) {
690             DWORD k, a, b, bend;
691
692             for(k=2; k < length; k *= 2) {
693                 for(i=0; i+k < length; i += 2*k) {
694                     a = b = 0;
695                     if(i+2*k <= length)
696                         bend = k;
697                     else
698                         bend = length - (i+k);
699
700                     memcpy(tmpbuf, sorttab+i, k*sizeof(VARIANT*));
701
702                     while(a < k && b < bend) {
703                         hres = sort_cmp(dispex->ctx, cmp_func, tmpbuf[a], sorttab[i+k+b], ei, caller, &cmp);
704                         if(FAILED(hres))
705                             break;
706
707                         if(cmp < 0) {
708                             sorttab[i+a+b] = tmpbuf[a];
709                             a++;
710                         }else {
711                             sorttab[i+a+b] = sorttab[i+k+b];
712                             b++;
713                         }
714                     }
715
716                     if(FAILED(hres))
717                         break;
718
719                     if(a < k)
720                         memcpy(sorttab+i+a+b, tmpbuf+a, (k-a)*sizeof(VARIANT*));
721                 }
722
723                 if(FAILED(hres))
724                     break;
725             }
726         }
727
728         for(i=0; SUCCEEDED(hres) && i < length; i++)
729             hres = jsdisp_propput_idx(dispex, i, lcid, sorttab[i], ei, caller);
730     }
731
732     if(vtab) {
733         for(i=0; i < length; i++)
734             VariantClear(vtab+i);
735         heap_free(vtab);
736     }
737     heap_free(sorttab);
738     if(cmp_func)
739         jsdisp_release(cmp_func);
740
741     if(FAILED(hres))
742         return hres;
743
744     if(retv) {
745         V_VT(retv) = VT_DISPATCH;
746         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(dispex);
747         IDispatch_AddRef(_IDispatchEx_(dispex));
748     }
749
750     return S_OK;
751 }
752
753 static HRESULT Array_splice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
754         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
755 {
756     FIXME("\n");
757     return E_NOTIMPL;
758 }
759
760 /* ECMA-262 3rd Edition    15.4.4.2 */
761 static HRESULT Array_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
762         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
763 {
764     TRACE("\n");
765
766     if(!is_class(dispex, JSCLASS_ARRAY)) {
767         WARN("not Array object\n");
768         return E_FAIL;
769     }
770
771     return array_join(dispex, lcid, ((ArrayInstance*)dispex)->length, default_separatorW, retv, ei, sp);
772 }
773
774 static HRESULT Array_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
775         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
776 {
777     FIXME("\n");
778     return E_NOTIMPL;
779 }
780
781 static HRESULT Array_unshift(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
782         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
783 {
784     WCHAR buf[14], *buf_end, *str;
785     DWORD argc, i, length;
786     VARIANT var;
787     DISPID id;
788     HRESULT hres;
789
790     TRACE("\n");
791
792     if(is_class(dispex, JSCLASS_ARRAY)) {
793         length = ((ArrayInstance*)dispex)->length;
794     }else {
795         hres = get_jsdisp_length(dispex, lcid, ei, &length);
796         if(FAILED(hres))
797             return hres;
798     }
799
800     argc = arg_cnt(dp);
801     if(!argc) {
802         if(retv)
803             V_VT(retv) = VT_EMPTY;
804         return S_OK;
805     }
806
807     buf_end = buf + sizeof(buf)/sizeof(WCHAR)-1;
808     *buf_end-- = 0;
809     i = length;
810
811     while(i--) {
812         str = idx_to_str(i, buf_end);
813
814         hres = jsdisp_get_id(dispex, str, 0, &id);
815         if(SUCCEEDED(hres)) {
816             hres = jsdisp_propget(dispex, id, lcid, &var, ei, caller);
817             if(FAILED(hres))
818                 return hres;
819
820             hres = jsdisp_propput_idx(dispex, i+argc, lcid, &var, ei, caller);
821             VariantClear(&var);
822         }else if(hres == DISP_E_UNKNOWNNAME) {
823             hres = IDispatchEx_DeleteMemberByDispID(_IDispatchEx_(dispex), id);
824         }
825
826         if(FAILED(hres))
827             return hres;
828     }
829
830     for(i=0; i<argc; i++) {
831         hres = jsdisp_propput_idx(dispex, i, lcid, get_arg(dp,i), ei, caller);
832         if(FAILED(hres))
833             return hres;
834     }
835
836     if(!is_class(dispex, JSCLASS_ARRAY)) {
837         hres = set_jsdisp_length(dispex, lcid, ei, length+argc);
838         if(FAILED(hres))
839             return hres;
840     }
841
842     if(retv)
843         V_VT(retv) = VT_EMPTY;
844     return S_OK;
845 }
846
847 static HRESULT Array_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
848         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
849 {
850     TRACE("\n");
851
852     switch(flags) {
853     case INVOKE_FUNC:
854         return throw_type_error(dispex->ctx, ei, IDS_NOT_FUNC, NULL);
855     case INVOKE_PROPERTYGET:
856         return array_join(dispex, lcid, ((ArrayInstance*)dispex)->length, default_separatorW, retv, ei, sp);
857     default:
858         FIXME("unimplemented flags %x\n", flags);
859         return E_NOTIMPL;
860     }
861
862     return S_OK;
863 }
864
865 static void Array_destructor(DispatchEx *dispex)
866 {
867     heap_free(dispex);
868 }
869
870 static void Array_on_put(DispatchEx *dispex, const WCHAR *name)
871 {
872     ArrayInstance *array = (ArrayInstance*)dispex;
873     const WCHAR *ptr = name;
874     DWORD id = 0;
875
876     if(!isdigitW(*ptr))
877         return;
878
879     while(*ptr && isdigitW(*ptr)) {
880         id = id*10 + (*ptr-'0');
881         ptr++;
882     }
883
884     if(*ptr)
885         return;
886
887     if(id >= array->length)
888         array->length = id+1;
889 }
890
891 static const builtin_prop_t Array_props[] = {
892     {concatW,                Array_concat,               PROPF_METHOD|1},
893     {joinW,                  Array_join,                 PROPF_METHOD|1},
894     {lengthW,                Array_length,               0},
895     {popW,                   Array_pop,                  PROPF_METHOD},
896     {pushW,                  Array_push,                 PROPF_METHOD|1},
897     {reverseW,               Array_reverse,              PROPF_METHOD},
898     {shiftW,                 Array_shift,                PROPF_METHOD},
899     {sliceW,                 Array_slice,                PROPF_METHOD|2},
900     {sortW,                  Array_sort,                 PROPF_METHOD|1},
901     {spliceW,                Array_splice,               PROPF_METHOD|2},
902     {toLocaleStringW,        Array_toLocaleString,       PROPF_METHOD},
903     {toStringW,              Array_toString,             PROPF_METHOD},
904     {unshiftW,               Array_unshift,              PROPF_METHOD|1},
905 };
906
907 static const builtin_info_t Array_info = {
908     JSCLASS_ARRAY,
909     {NULL, Array_value, 0},
910     sizeof(Array_props)/sizeof(*Array_props),
911     Array_props,
912     Array_destructor,
913     Array_on_put
914 };
915
916 static HRESULT ArrayConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
917         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
918 {
919     DispatchEx *obj;
920     VARIANT *arg_var;
921     DWORD i;
922     HRESULT hres;
923
924     TRACE("\n");
925
926     switch(flags) {
927     case DISPATCH_METHOD:
928     case DISPATCH_CONSTRUCT: {
929         if(arg_cnt(dp) == 1 && V_VT((arg_var = get_arg(dp, 0))) == VT_I4) {
930             if(V_I4(arg_var) < 0)
931                 return throw_range_error(dispex->ctx, ei, IDS_INVALID_LENGTH, NULL);
932
933             hres = create_array(dispex->ctx, V_I4(arg_var), &obj);
934             if(FAILED(hres))
935                 return hres;
936
937             V_VT(retv) = VT_DISPATCH;
938             V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj);
939             return S_OK;
940         }
941
942         hres = create_array(dispex->ctx, arg_cnt(dp), &obj);
943         if(FAILED(hres))
944             return hres;
945
946         for(i=0; i < arg_cnt(dp); i++) {
947             hres = jsdisp_propput_idx(obj, i, lcid, get_arg(dp, i), ei, caller);
948             if(FAILED(hres))
949                 break;
950         }
951         if(FAILED(hres)) {
952             jsdisp_release(obj);
953             return hres;
954         }
955
956         V_VT(retv) = VT_DISPATCH;
957         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj);
958         break;
959     }
960     default:
961         FIXME("unimplemented flags: %x\n", flags);
962         return E_NOTIMPL;
963     }
964
965     return S_OK;
966 }
967
968 static HRESULT alloc_array(script_ctx_t *ctx, DispatchEx *object_prototype, ArrayInstance **ret)
969 {
970     ArrayInstance *array;
971     HRESULT hres;
972
973     array = heap_alloc_zero(sizeof(ArrayInstance));
974     if(!array)
975         return E_OUTOFMEMORY;
976
977     if(object_prototype)
978         hres = init_dispex(&array->dispex, ctx, &Array_info, object_prototype);
979     else
980         hres = init_dispex_from_constr(&array->dispex, ctx, &Array_info, ctx->array_constr);
981
982     if(FAILED(hres)) {
983         heap_free(array);
984         return hres;
985     }
986
987     *ret = array;
988     return S_OK;
989 }
990
991 HRESULT create_array_constr(script_ctx_t *ctx, DispatchEx *object_prototype, DispatchEx **ret)
992 {
993     ArrayInstance *array;
994     HRESULT hres;
995
996     hres = alloc_array(ctx, object_prototype, &array);
997     if(FAILED(hres))
998         return hres;
999
1000     hres = create_builtin_function(ctx, ArrayConstr_value, NULL, PROPF_CONSTR, &array->dispex, ret);
1001
1002     jsdisp_release(&array->dispex);
1003     return hres;
1004 }
1005
1006 HRESULT create_array(script_ctx_t *ctx, DWORD length, DispatchEx **ret)
1007 {
1008     ArrayInstance *array;
1009     HRESULT hres;
1010
1011     hres = alloc_array(ctx, NULL, &array);
1012     if(FAILED(hres))
1013         return hres;
1014
1015     array->length = length;
1016
1017     *ret = &array->dispex;
1018     return S_OK;
1019 }