jscript: Added Array_slice implementation.
[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 static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0};
47 static const WCHAR propertyIsEnumerableW[] =
48     {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0};
49 static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0};
50
51 static const WCHAR default_separatorW[] = {',',0};
52
53 static HRESULT Array_length(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
54         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
55 {
56     ArrayInstance *This = (ArrayInstance*)dispex;
57
58     TRACE("%p %d\n", This, This->length);
59
60     switch(flags) {
61     case DISPATCH_PROPERTYGET:
62         V_VT(retv) = VT_I4;
63         V_I4(retv) = This->length;
64         break;
65     case DISPATCH_PROPERTYPUT: {
66         VARIANT num;
67         DOUBLE len = -1;
68         DWORD i;
69         HRESULT hres;
70
71         hres = to_number(dispex->ctx, get_arg(dp, 0), ei, &num);
72         if(V_VT(&num) == VT_I4)
73             len = V_I4(&num);
74         else
75             len = floor(V_R8(&num));
76
77         if(len!=(DWORD)len) {
78             FIXME("Throw RangeError\n");
79             return E_FAIL;
80         }
81
82         for(i=len; i<This->length; i++) {
83             hres = jsdisp_delete_idx(dispex, i);
84             if(FAILED(hres))
85                 return hres;
86         }
87
88         This->length = len;
89         break;
90     }
91     default:
92         FIXME("unimplemented flags %x\n", flags);
93         return E_NOTIMPL;
94     }
95
96     return S_OK;
97 }
98
99 static HRESULT concat_array(DispatchEx *array, ArrayInstance *obj, DWORD *len, LCID lcid,
100         jsexcept_t *ei, IServiceProvider *caller)
101 {
102     VARIANT var;
103     DWORD i;
104     HRESULT hres;
105
106     for(i=0; i < obj->length; i++) {
107         hres = jsdisp_propget_idx(&obj->dispex, i, lcid, &var, ei, caller);
108         if(hres == DISP_E_UNKNOWNNAME)
109             continue;
110         if(FAILED(hres))
111             return hres;
112
113         hres = jsdisp_propput_idx(array, *len+i, lcid, &var, ei, caller);
114         VariantClear(&var);
115         if(FAILED(hres))
116             return hres;
117     }
118
119     *len += obj->length;
120     return S_OK;
121 }
122
123 static HRESULT concat_obj(DispatchEx *array, IDispatch *obj, DWORD *len, LCID lcid, jsexcept_t *ei, IServiceProvider *caller)
124 {
125     DispatchEx *jsobj;
126     VARIANT var;
127     HRESULT hres;
128
129     jsobj = iface_to_jsdisp((IUnknown*)obj);
130     if(jsobj) {
131         if(is_class(jsobj, JSCLASS_ARRAY)) {
132             hres = concat_array(array, (ArrayInstance*)jsobj, len, lcid, ei, caller);
133             jsdisp_release(jsobj);
134             return hres;
135         }
136         jsdisp_release(jsobj);
137     }
138
139     V_VT(&var) = VT_DISPATCH;
140     V_DISPATCH(&var) = obj;
141     return jsdisp_propput_idx(array, (*len)++, lcid, &var, ei, caller);
142 }
143
144 static HRESULT Array_concat(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
145         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
146 {
147     DispatchEx *ret;
148     DWORD len = 0;
149     HRESULT hres;
150
151     TRACE("\n");
152
153     hres = create_array(dispex->ctx, 0, &ret);
154     if(FAILED(hres))
155         return hres;
156
157     hres = concat_obj(ret, (IDispatch*)_IDispatchEx_(dispex), &len, lcid, ei, caller);
158     if(SUCCEEDED(hres)) {
159         VARIANT *arg;
160         DWORD i;
161
162         for(i=0; i < arg_cnt(dp); i++) {
163             arg = get_arg(dp, i);
164             if(V_VT(arg) == VT_DISPATCH)
165                 hres = concat_obj(ret, V_DISPATCH(arg), &len, lcid, ei, caller);
166             else
167                 hres = jsdisp_propput_idx(ret, len++, lcid, arg, ei, caller);
168             if(FAILED(hres))
169                 break;
170         }
171     }
172
173     if(FAILED(hres))
174         return hres;
175
176     if(retv) {
177         V_VT(retv) = VT_DISPATCH;
178         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(ret);
179     }else {
180         jsdisp_release(ret);
181     }
182     return S_OK;
183 }
184
185 static HRESULT array_join(DispatchEx *array, LCID lcid, DWORD length, const WCHAR *sep, VARIANT *retv,
186         jsexcept_t *ei, IServiceProvider *caller)
187 {
188     BSTR *str_tab, ret = NULL;
189     VARIANT var;
190     DWORD i;
191     HRESULT hres = E_FAIL;
192
193     if(!length) {
194         if(retv) {
195             V_VT(retv) = VT_BSTR;
196             V_BSTR(retv) = SysAllocStringLen(NULL, 0);
197             if(!V_BSTR(retv))
198                 return E_OUTOFMEMORY;
199         }
200         return S_OK;
201     }
202
203     str_tab = heap_alloc_zero(length * sizeof(BSTR));
204     if(!str_tab)
205         return E_OUTOFMEMORY;
206
207     for(i=0; i < length; i++) {
208         hres = jsdisp_propget_idx(array, i, lcid, &var, ei, caller);
209         if(FAILED(hres))
210             break;
211
212         if(V_VT(&var) != VT_EMPTY && V_VT(&var) != VT_NULL)
213             hres = to_string(array->ctx, &var, ei, str_tab+i);
214         VariantClear(&var);
215         if(FAILED(hres))
216             break;
217     }
218
219     if(SUCCEEDED(hres)) {
220         DWORD seplen = 0, len = 0;
221         WCHAR *ptr;
222
223         seplen = strlenW(sep);
224
225         if(str_tab[0])
226             len = SysStringLen(str_tab[0]);
227         for(i=1; i < length; i++)
228             len += seplen + SysStringLen(str_tab[i]);
229
230         ret = SysAllocStringLen(NULL, len);
231         if(ret) {
232             DWORD tmplen = 0;
233
234             if(str_tab[0]) {
235                 tmplen = SysStringLen(str_tab[0]);
236                 memcpy(ret, str_tab[0], tmplen*sizeof(WCHAR));
237             }
238
239             ptr = ret + tmplen;
240             for(i=1; i < length; i++) {
241                 if(seplen) {
242                     memcpy(ptr, sep, seplen*sizeof(WCHAR));
243                     ptr += seplen;
244                 }
245
246                 if(str_tab[i]) {
247                     tmplen = SysStringLen(str_tab[i]);
248                     memcpy(ptr, str_tab[i], tmplen*sizeof(WCHAR));
249                     ptr += tmplen;
250                 }
251             }
252             *ptr=0;
253         }else {
254             hres = E_OUTOFMEMORY;
255         }
256     }
257
258     for(i=0; i < length; i++)
259         SysFreeString(str_tab[i]);
260     heap_free(str_tab);
261     if(FAILED(hres))
262         return hres;
263
264     TRACE("= %s\n", debugstr_w(ret));
265
266     if(retv) {
267         if(!ret) {
268             ret = SysAllocStringLen(NULL, 0);
269             if(!ret)
270                 return E_OUTOFMEMORY;
271         }
272
273         V_VT(retv) = VT_BSTR;
274         V_BSTR(retv) = ret;
275     }else {
276         SysFreeString(ret);
277     }
278
279     return S_OK;
280 }
281
282 /* ECMA-262 3rd Edition    15.4.4.5 */
283 static HRESULT Array_join(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
284         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
285 {
286     DWORD length;
287     HRESULT hres;
288
289     TRACE("\n");
290
291     if(is_class(dispex, JSCLASS_ARRAY)) {
292         length = ((ArrayInstance*)dispex)->length;
293     }else {
294         FIXME("dispid is not Array\n");
295         return E_NOTIMPL;
296     }
297
298     if(arg_cnt(dp)) {
299         BSTR sep;
300
301         hres = to_string(dispex->ctx, dp->rgvarg + dp->cArgs-1, ei, &sep);
302         if(FAILED(hres))
303             return hres;
304
305         hres = array_join(dispex, lcid, length, sep, retv, ei, caller);
306
307         SysFreeString(sep);
308     }else {
309         hres = array_join(dispex, lcid, length, default_separatorW, retv, ei, caller);
310     }
311
312     return hres;
313 }
314
315 static HRESULT Array_pop(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
316         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
317 {
318     VARIANT val;
319     DWORD length;
320     WCHAR buf[14];
321     DISPID id;
322     HRESULT hres;
323
324     static const WCHAR formatW[] = {'%','d',0};
325
326     TRACE("\n");
327
328     if(is_class(dispex, JSCLASS_ARRAY)) {
329         ArrayInstance *array = (ArrayInstance*)dispex;
330         length = array->length;
331     }else {
332         FIXME("not Array this\n");
333         return E_NOTIMPL;
334     }
335
336     if(!length) {
337         if(retv)
338             V_VT(retv) = VT_EMPTY;
339         return S_OK;
340     }
341
342     sprintfW(buf, formatW, --length);
343     hres = jsdisp_get_id(dispex, buf, 0, &id);
344     if(SUCCEEDED(hres)) {
345         hres = jsdisp_propget(dispex, id, lcid, &val, ei, caller);
346         if(FAILED(hres))
347             return hres;
348
349         hres = IDispatchEx_DeleteMemberByDispID(_IDispatchEx_(dispex), id);
350     }else if(hres == DISP_E_UNKNOWNNAME) {
351         V_VT(&val) = VT_EMPTY;
352         hres = S_OK;
353     }else {
354         return hres;
355     }
356
357     if(SUCCEEDED(hres)) {
358         if(is_class(dispex, JSCLASS_ARRAY)) {
359             ArrayInstance *array = (ArrayInstance*)dispex;
360             array->length = length;
361         }
362     }
363
364     if(FAILED(hres)) {
365         VariantClear(&val);
366         return hres;
367     }
368
369     if(retv)
370         *retv = val;
371     else
372         VariantClear(&val);
373     return S_OK;
374 }
375
376 /* ECMA-262 3rd Edition    15.4.4.7 */
377 static HRESULT Array_push(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
378         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
379 {
380     DWORD length = 0;
381     int i, n;
382     HRESULT hres;
383
384     TRACE("\n");
385
386     if(dispex->builtin_info->class == JSCLASS_ARRAY) {
387         length = ((ArrayInstance*)dispex)->length;
388     }else {
389         FIXME("not Array this\n");
390         return E_NOTIMPL;
391     }
392
393     n = dp->cArgs - dp->cNamedArgs;
394     for(i=0; i < n; i++) {
395         hres = jsdisp_propput_idx(dispex, length+i, lcid, get_arg(dp, i), ei, sp);
396         if(FAILED(hres))
397             return hres;
398     }
399
400     if(retv) {
401         V_VT(retv) = VT_I4;
402         V_I4(retv) = length+n;
403     }
404     return S_OK;
405 }
406
407 static HRESULT Array_reverse(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
408         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
409 {
410     FIXME("\n");
411     return E_NOTIMPL;
412 }
413
414 static HRESULT Array_shift(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
415         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
416 {
417     FIXME("\n");
418     return E_NOTIMPL;
419 }
420
421 static HRESULT Array_slice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
422         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
423 {
424     DispatchEx *arr;
425     VARIANT v;
426     DOUBLE range;
427     DWORD length, start, end, idx;
428     HRESULT hres;
429
430     TRACE("\n");
431
432     if(is_class(dispex, JSCLASS_ARRAY)) {
433         length = ((ArrayInstance*)dispex)->length;
434     }else {
435         FIXME("not Array this\n");
436         return E_NOTIMPL;
437     }
438
439     if(arg_cnt(dp)) {
440         hres = to_number(dispex->ctx, get_arg(dp, 0), ei, &v);
441         if(FAILED(hres))
442             return hres;
443
444         if(V_VT(&v) == VT_I4)
445             range = V_I4(&v);
446         else
447             range = floor(V_R8(&v));
448
449         if(-range>length || isnan(range)) start = 0;
450         else if(range < 0) start = range+length;
451         else if(range <= length) start = range;
452         else start = length;
453     }
454     else start = 0;
455
456     if(arg_cnt(dp)>1) {
457         hres = to_number(dispex->ctx, get_arg(dp, 1), ei, &v);
458         if(FAILED(hres))
459             return hres;
460
461         if(V_VT(&v) == VT_I4)
462             range = V_I4(&v);
463         else
464             range = floor(V_R8(&v));
465
466         if(-range>length) end = 0;
467         else if(range < 0) end = range+length;
468         else if(range <= length) end = range;
469         else end = length;
470     }
471     else end = length;
472
473     hres = create_array(dispex->ctx, (end>start)?end-start:0, &arr);
474     if(FAILED(hres))
475         return hres;
476
477     for(idx=start; idx<end; idx++) {
478         hres = jsdisp_propget_idx(dispex, idx, lcid, &v, ei, sp);
479         if(hres == DISP_E_UNKNOWNNAME)
480             continue;
481
482         if(SUCCEEDED(hres))
483             hres = jsdisp_propput_idx(arr, idx-start, lcid, &v, ei, sp);
484
485         if(FAILED(hres)) {
486             jsdisp_release(arr);
487             return hres;
488         }
489     }
490
491     if(retv) {
492         V_VT(retv) = VT_DISPATCH;
493         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(arr);
494     }
495     else
496         jsdisp_release(arr);
497
498     return S_OK;
499 }
500
501 static HRESULT sort_cmp(script_ctx_t *ctx, DispatchEx *cmp_func, VARIANT *v1, VARIANT *v2, jsexcept_t *ei,
502         IServiceProvider *caller, INT *cmp)
503 {
504     HRESULT hres;
505
506     if(cmp_func) {
507         VARIANTARG args[2];
508         DISPPARAMS dp = {args, NULL, 2, 0};
509         VARIANT tmp;
510         VARIANT res;
511
512         args[0] = *v2;
513         args[1] = *v1;
514
515         hres = jsdisp_call_value(cmp_func, ctx->lcid, DISPATCH_METHOD, &dp, &res, ei, caller);
516         if(FAILED(hres))
517             return hres;
518
519         hres = to_number(ctx, &res, ei, &tmp);
520         VariantClear(&res);
521         if(FAILED(hres))
522             return hres;
523
524         if(V_VT(&tmp) == VT_I4)
525             *cmp = V_I4(&tmp);
526         else
527             *cmp = V_R8(&tmp) > 0.0 ? 1 : -1;
528     }else if(is_num_vt(V_VT(v1))) {
529         if(is_num_vt(V_VT(v2))) {
530             DOUBLE d = num_val(v1)-num_val(v2);
531             if(d > 0.0)
532                 *cmp = 1;
533             else if(d < -0.0)
534                 *cmp = -1;
535             else
536                 *cmp = 0;
537         }else {
538             *cmp = -1;
539         }
540     }else if(is_num_vt(V_VT(v2))) {
541         *cmp = 1;
542     }else if(V_VT(v1) == VT_BSTR) {
543         if(V_VT(v2) == VT_BSTR)
544             *cmp = strcmpW(V_BSTR(v1), V_BSTR(v2));
545         else
546             *cmp = -1;
547     }else if(V_VT(v2) == VT_BSTR) {
548         *cmp = 1;
549     }else {
550         *cmp = 0;
551     }
552
553     return S_OK;
554 }
555
556 /* ECMA-262 3rd Edition    15.4.4.11 */
557 static HRESULT Array_sort(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
558         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
559 {
560     DispatchEx *cmp_func = NULL;
561     VARIANT *vtab, **sorttab = NULL;
562     DWORD length;
563     DWORD i;
564     HRESULT hres = S_OK;
565
566     TRACE("\n");
567
568     if(is_class(dispex, JSCLASS_ARRAY)) {
569         length = ((ArrayInstance*)dispex)->length;
570     }else {
571         FIXME("unsupported this not array\n");
572         return E_NOTIMPL;
573     }
574
575     if(arg_cnt(dp) > 1) {
576         WARN("invalid arg_cnt %d\n", arg_cnt(dp));
577         return E_FAIL;
578     }
579
580     if(arg_cnt(dp) == 1) {
581         VARIANT *arg = get_arg(dp, 0);
582
583         if(V_VT(arg) != VT_DISPATCH) {
584             WARN("arg is not dispatch\n");
585             return E_FAIL;
586         }
587
588
589         cmp_func = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
590         if(!cmp_func || !is_class(cmp_func, JSCLASS_FUNCTION)) {
591             WARN("cmp_func is not a function\n");
592             if(cmp_func)
593                 jsdisp_release(cmp_func);
594             return E_FAIL;
595         }
596     }
597
598     if(!length) {
599         if(cmp_func)
600             jsdisp_release(cmp_func);
601         if(retv) {
602             V_VT(retv) = VT_DISPATCH;
603             V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(dispex);
604             IDispatchEx_AddRef(_IDispatchEx_(dispex));
605         }
606         return S_OK;
607     }
608
609     vtab = heap_alloc_zero(length * sizeof(VARIANT));
610     if(vtab) {
611         for(i=0; i<length; i++) {
612             hres = jsdisp_propget_idx(dispex, i, lcid, vtab+i, ei, caller);
613             if(FAILED(hres) && hres != DISP_E_UNKNOWNNAME) {
614                 WARN("Could not get elem %d: %08x\n", i, hres);
615                 break;
616             }
617         }
618     }else {
619         hres = E_OUTOFMEMORY;
620     }
621
622     if(SUCCEEDED(hres)) {
623         sorttab = heap_alloc(length*2*sizeof(VARIANT*));
624         if(!sorttab)
625             hres = E_OUTOFMEMORY;
626     }
627
628     /* merge-sort */
629     if(SUCCEEDED(hres)) {
630         VARIANT *tmpv, **tmpbuf;
631         INT cmp;
632
633         tmpbuf = sorttab + length;
634         for(i=0; i < length; i++)
635             sorttab[i] = vtab+i;
636
637         for(i=0; i < length/2; i++) {
638             hres = sort_cmp(dispex->ctx, cmp_func, sorttab[2*i+1], sorttab[2*i], ei, caller, &cmp);
639             if(FAILED(hres))
640                 break;
641
642             if(cmp < 0) {
643                 tmpv = sorttab[2*i];
644                 sorttab[2*i] = sorttab[2*i+1];
645                 sorttab[2*i+1] = tmpv;
646             }
647         }
648
649         if(SUCCEEDED(hres)) {
650             DWORD k, a, b, bend;
651
652             for(k=2; k < length; k *= 2) {
653                 for(i=0; i+k < length; i += 2*k) {
654                     a = b = 0;
655                     if(i+2*k <= length)
656                         bend = k;
657                     else
658                         bend = length - (i+k);
659
660                     memcpy(tmpbuf, sorttab+i, k*sizeof(VARIANT*));
661
662                     while(a < k && b < bend) {
663                         hres = sort_cmp(dispex->ctx, cmp_func, tmpbuf[a], sorttab[i+k+b], ei, caller, &cmp);
664                         if(FAILED(hres))
665                             break;
666
667                         if(cmp < 0) {
668                             sorttab[i+a+b] = tmpbuf[a];
669                             a++;
670                         }else {
671                             sorttab[i+a+b] = sorttab[i+k+b];
672                             b++;
673                         }
674                     }
675
676                     if(FAILED(hres))
677                         break;
678
679                     if(a < k)
680                         memcpy(sorttab+i+a+b, tmpbuf+a, (k-a)*sizeof(VARIANT*));
681                 }
682
683                 if(FAILED(hres))
684                     break;
685             }
686         }
687
688         for(i=0; SUCCEEDED(hres) && i < length; i++)
689             hres = jsdisp_propput_idx(dispex, i, lcid, sorttab[i], ei, caller);
690     }
691
692     if(vtab) {
693         for(i=0; i < length; i++)
694             VariantClear(vtab+i);
695         heap_free(vtab);
696     }
697     heap_free(sorttab);
698     if(cmp_func)
699         jsdisp_release(cmp_func);
700
701     if(FAILED(hres))
702         return hres;
703
704     if(retv) {
705         V_VT(retv) = VT_DISPATCH;
706         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(dispex);
707         IDispatch_AddRef(_IDispatchEx_(dispex));
708     }
709
710     return S_OK;
711 }
712
713 static HRESULT Array_splice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
714         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
715 {
716     FIXME("\n");
717     return E_NOTIMPL;
718 }
719
720 /* ECMA-262 3rd Edition    15.4.4.2 */
721 static HRESULT Array_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
722         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
723 {
724     TRACE("\n");
725
726     if(!is_class(dispex, JSCLASS_ARRAY)) {
727         WARN("not Array object\n");
728         return E_FAIL;
729     }
730
731     return array_join(dispex, lcid, ((ArrayInstance*)dispex)->length, default_separatorW, retv, ei, sp);
732 }
733
734 static HRESULT Array_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
735         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
736 {
737     FIXME("\n");
738     return E_NOTIMPL;
739 }
740
741 static HRESULT Array_unshift(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
742         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
743 {
744     FIXME("\n");
745     return E_NOTIMPL;
746 }
747
748 static HRESULT Array_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
749         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
750 {
751     FIXME("\n");
752     return E_NOTIMPL;
753 }
754
755 static HRESULT Array_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
756         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
757 {
758     FIXME("\n");
759     return E_NOTIMPL;
760 }
761
762 static HRESULT Array_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
763         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
764 {
765     FIXME("\n");
766     return E_NOTIMPL;
767 }
768
769 static HRESULT Array_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
770         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
771 {
772     TRACE("\n");
773
774     switch(flags) {
775     case INVOKE_PROPERTYGET:
776         return array_join(dispex, lcid, ((ArrayInstance*)dispex)->length, default_separatorW, retv, ei, sp);
777     default:
778         FIXME("unimplemented flags %x\n", flags);
779         return E_NOTIMPL;
780     }
781
782     return S_OK;
783 }
784
785 static void Array_destructor(DispatchEx *dispex)
786 {
787     heap_free(dispex);
788 }
789
790 static void Array_on_put(DispatchEx *dispex, const WCHAR *name)
791 {
792     ArrayInstance *array = (ArrayInstance*)dispex;
793     const WCHAR *ptr = name;
794     DWORD id = 0;
795
796     if(!isdigitW(*ptr))
797         return;
798
799     while(*ptr && isdigitW(*ptr)) {
800         id = id*10 + (*ptr-'0');
801         ptr++;
802     }
803
804     if(*ptr)
805         return;
806
807     if(id >= array->length)
808         array->length = id+1;
809 }
810
811 static const builtin_prop_t Array_props[] = {
812     {concatW,                Array_concat,               PROPF_METHOD},
813     {hasOwnPropertyW,        Array_hasOwnProperty,       PROPF_METHOD},
814     {isPrototypeOfW,         Array_isPrototypeOf,        PROPF_METHOD},
815     {joinW,                  Array_join,                 PROPF_METHOD},
816     {lengthW,                Array_length,               0},
817     {popW,                   Array_pop,                  PROPF_METHOD},
818     {propertyIsEnumerableW,  Array_propertyIsEnumerable, PROPF_METHOD},
819     {pushW,                  Array_push,                 PROPF_METHOD},
820     {reverseW,               Array_reverse,              PROPF_METHOD},
821     {shiftW,                 Array_shift,                PROPF_METHOD},
822     {sliceW,                 Array_slice,                PROPF_METHOD},
823     {sortW,                  Array_sort,                 PROPF_METHOD},
824     {spliceW,                Array_splice,               PROPF_METHOD},
825     {toLocaleStringW,        Array_toLocaleString,       PROPF_METHOD},
826     {toStringW,              Array_toString,             PROPF_METHOD},
827     {unshiftW,               Array_unshift,              PROPF_METHOD},
828 };
829
830 static const builtin_info_t Array_info = {
831     JSCLASS_ARRAY,
832     {NULL, Array_value, 0},
833     sizeof(Array_props)/sizeof(*Array_props),
834     Array_props,
835     Array_destructor,
836     Array_on_put
837 };
838
839 static HRESULT ArrayConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
840         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
841 {
842     DispatchEx *obj;
843     VARIANT *arg_var;
844     DWORD i;
845     HRESULT hres;
846
847     TRACE("\n");
848
849     switch(flags) {
850     case DISPATCH_METHOD:
851     case DISPATCH_CONSTRUCT: {
852         if(arg_cnt(dp) == 1 && V_VT((arg_var = get_arg(dp, 0))) == VT_I4) {
853             if(V_I4(arg_var) < 0) {
854                 FIXME("throw RangeError\n");
855                 return E_FAIL;
856             }
857
858             hres = create_array(dispex->ctx, V_I4(arg_var), &obj);
859             if(FAILED(hres))
860                 return hres;
861
862             V_VT(retv) = VT_DISPATCH;
863             V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj);
864             return S_OK;
865         }
866
867         hres = create_array(dispex->ctx, arg_cnt(dp), &obj);
868         if(FAILED(hres))
869             return hres;
870
871         for(i=0; i < arg_cnt(dp); i++) {
872             hres = jsdisp_propput_idx(obj, i, lcid, get_arg(dp, i), ei, caller);
873             if(FAILED(hres))
874                 break;
875         }
876         if(FAILED(hres)) {
877             jsdisp_release(obj);
878             return hres;
879         }
880
881         V_VT(retv) = VT_DISPATCH;
882         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj);
883         break;
884     }
885     default:
886         FIXME("unimplemented flags: %x\n", flags);
887         return E_NOTIMPL;
888     }
889
890     return S_OK;
891 }
892
893 static HRESULT alloc_array(script_ctx_t *ctx, BOOL use_constr, ArrayInstance **ret)
894 {
895     ArrayInstance *array;
896     HRESULT hres;
897
898     array = heap_alloc_zero(sizeof(ArrayInstance));
899     if(!array)
900         return E_OUTOFMEMORY;
901
902     if(use_constr)
903         hres = init_dispex_from_constr(&array->dispex, ctx, &Array_info, ctx->array_constr);
904     else
905         hres = init_dispex_from_constr(&array->dispex, ctx, &Array_info, ctx->object_constr);
906
907     if(FAILED(hres)) {
908         heap_free(array);
909         return hres;
910     }
911
912     *ret = array;
913     return S_OK;
914 }
915
916 HRESULT create_array_constr(script_ctx_t *ctx, DispatchEx **ret)
917 {
918     ArrayInstance *array;
919     HRESULT hres;
920
921     hres = alloc_array(ctx, FALSE, &array);
922     if(FAILED(hres))
923         return hres;
924
925     hres = create_builtin_function(ctx, ArrayConstr_value, NULL, PROPF_CONSTR, &array->dispex, ret);
926
927     IDispatchEx_Release(_IDispatchEx_(&array->dispex));
928     return hres;
929 }
930
931 HRESULT create_array(script_ctx_t *ctx, DWORD length, DispatchEx **ret)
932 {
933     ArrayInstance *array;
934     HRESULT hres;
935
936     hres = alloc_array(ctx, TRUE, &array);
937     if(FAILED(hres))
938         return hres;
939
940     array->length = length;
941
942     *ret = &array->dispex;
943     return S_OK;
944 }