jscript: Added implementation of Array constructor called as a function.
[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     FIXME("\n");
425     return E_NOTIMPL;
426 }
427
428 static HRESULT sort_cmp(script_ctx_t *ctx, DispatchEx *cmp_func, VARIANT *v1, VARIANT *v2, jsexcept_t *ei,
429         IServiceProvider *caller, INT *cmp)
430 {
431     HRESULT hres;
432
433     if(cmp_func) {
434         VARIANTARG args[2];
435         DISPPARAMS dp = {args, NULL, 2, 0};
436         VARIANT tmp;
437         VARIANT res;
438
439         args[0] = *v2;
440         args[1] = *v1;
441
442         hres = jsdisp_call_value(cmp_func, ctx->lcid, DISPATCH_METHOD, &dp, &res, ei, caller);
443         if(FAILED(hres))
444             return hres;
445
446         hres = to_number(ctx, &res, ei, &tmp);
447         VariantClear(&res);
448         if(FAILED(hres))
449             return hres;
450
451         if(V_VT(&tmp) == VT_I4)
452             *cmp = V_I4(&tmp);
453         else
454             *cmp = V_R8(&tmp) > 0.0 ? 1 : -1;
455     }else if(is_num_vt(V_VT(v1))) {
456         if(is_num_vt(V_VT(v2))) {
457             DOUBLE d = num_val(v1)-num_val(v2);
458             if(d > 0.0)
459                 *cmp = 1;
460             else if(d < -0.0)
461                 *cmp = -1;
462             else
463                 *cmp = 0;
464         }else {
465             *cmp = -1;
466         }
467     }else if(is_num_vt(V_VT(v2))) {
468         *cmp = 1;
469     }else if(V_VT(v1) == VT_BSTR) {
470         if(V_VT(v2) == VT_BSTR)
471             *cmp = strcmpW(V_BSTR(v1), V_BSTR(v2));
472         else
473             *cmp = -1;
474     }else if(V_VT(v2) == VT_BSTR) {
475         *cmp = 1;
476     }else {
477         *cmp = 0;
478     }
479
480     return S_OK;
481 }
482
483 /* ECMA-262 3rd Edition    15.4.4.11 */
484 static HRESULT Array_sort(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
485         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
486 {
487     DispatchEx *cmp_func = NULL;
488     VARIANT *vtab, **sorttab = NULL;
489     DWORD length;
490     DWORD i;
491     HRESULT hres = S_OK;
492
493     TRACE("\n");
494
495     if(is_class(dispex, JSCLASS_ARRAY)) {
496         length = ((ArrayInstance*)dispex)->length;
497     }else {
498         FIXME("unsupported this not array\n");
499         return E_NOTIMPL;
500     }
501
502     if(arg_cnt(dp) > 1) {
503         WARN("invalid arg_cnt %d\n", arg_cnt(dp));
504         return E_FAIL;
505     }
506
507     if(arg_cnt(dp) == 1) {
508         VARIANT *arg = get_arg(dp, 0);
509
510         if(V_VT(arg) != VT_DISPATCH) {
511             WARN("arg is not dispatch\n");
512             return E_FAIL;
513         }
514
515
516         cmp_func = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
517         if(!cmp_func || !is_class(cmp_func, JSCLASS_FUNCTION)) {
518             WARN("cmp_func is not a function\n");
519             if(cmp_func)
520                 jsdisp_release(cmp_func);
521             return E_FAIL;
522         }
523     }
524
525     if(!length) {
526         if(cmp_func)
527             jsdisp_release(cmp_func);
528         if(retv) {
529             V_VT(retv) = VT_DISPATCH;
530             V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(dispex);
531             IDispatchEx_AddRef(_IDispatchEx_(dispex));
532         }
533         return S_OK;
534     }
535
536     vtab = heap_alloc_zero(length * sizeof(VARIANT));
537     if(vtab) {
538         for(i=0; i<length; i++) {
539             hres = jsdisp_propget_idx(dispex, i, lcid, vtab+i, ei, caller);
540             if(FAILED(hres) && hres != DISP_E_UNKNOWNNAME) {
541                 WARN("Could not get elem %d: %08x\n", i, hres);
542                 break;
543             }
544         }
545     }else {
546         hres = E_OUTOFMEMORY;
547     }
548
549     if(SUCCEEDED(hres)) {
550         sorttab = heap_alloc(length*2*sizeof(VARIANT*));
551         if(!sorttab)
552             hres = E_OUTOFMEMORY;
553     }
554
555     /* merge-sort */
556     if(SUCCEEDED(hres)) {
557         VARIANT *tmpv, **tmpbuf;
558         INT cmp;
559
560         tmpbuf = sorttab + length;
561         for(i=0; i < length; i++)
562             sorttab[i] = vtab+i;
563
564         for(i=0; i < length/2; i++) {
565             hres = sort_cmp(dispex->ctx, cmp_func, sorttab[2*i+1], sorttab[2*i], ei, caller, &cmp);
566             if(FAILED(hres))
567                 break;
568
569             if(cmp < 0) {
570                 tmpv = sorttab[2*i];
571                 sorttab[2*i] = sorttab[2*i+1];
572                 sorttab[2*i+1] = tmpv;
573             }
574         }
575
576         if(SUCCEEDED(hres)) {
577             DWORD k, a, b, bend;
578
579             for(k=2; k < length; k *= 2) {
580                 for(i=0; i+k < length; i += 2*k) {
581                     a = b = 0;
582                     if(i+2*k <= length)
583                         bend = k;
584                     else
585                         bend = length - (i+k);
586
587                     memcpy(tmpbuf, sorttab+i, k*sizeof(VARIANT*));
588
589                     while(a < k && b < bend) {
590                         hres = sort_cmp(dispex->ctx, cmp_func, tmpbuf[a], sorttab[i+k+b], ei, caller, &cmp);
591                         if(FAILED(hres))
592                             break;
593
594                         if(cmp < 0) {
595                             sorttab[i+a+b] = tmpbuf[a];
596                             a++;
597                         }else {
598                             sorttab[i+a+b] = sorttab[i+k+b];
599                             b++;
600                         }
601                     }
602
603                     if(FAILED(hres))
604                         break;
605
606                     if(a < k)
607                         memcpy(sorttab+i+a+b, tmpbuf+a, (k-a)*sizeof(VARIANT*));
608                 }
609
610                 if(FAILED(hres))
611                     break;
612             }
613         }
614
615         for(i=0; SUCCEEDED(hres) && i < length; i++)
616             hres = jsdisp_propput_idx(dispex, i, lcid, sorttab[i], ei, caller);
617     }
618
619     if(vtab) {
620         for(i=0; i < length; i++)
621             VariantClear(vtab+i);
622         heap_free(vtab);
623     }
624     heap_free(sorttab);
625     if(cmp_func)
626         jsdisp_release(cmp_func);
627
628     if(FAILED(hres))
629         return hres;
630
631     if(retv) {
632         V_VT(retv) = VT_DISPATCH;
633         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(dispex);
634         IDispatch_AddRef(_IDispatchEx_(dispex));
635     }
636
637     return S_OK;
638 }
639
640 static HRESULT Array_splice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
641         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
642 {
643     FIXME("\n");
644     return E_NOTIMPL;
645 }
646
647 /* ECMA-262 3rd Edition    15.4.4.2 */
648 static HRESULT Array_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
649         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
650 {
651     TRACE("\n");
652
653     if(!is_class(dispex, JSCLASS_ARRAY)) {
654         WARN("not Array object\n");
655         return E_FAIL;
656     }
657
658     return array_join(dispex, lcid, ((ArrayInstance*)dispex)->length, default_separatorW, retv, ei, sp);
659 }
660
661 static HRESULT Array_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
662         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
663 {
664     FIXME("\n");
665     return E_NOTIMPL;
666 }
667
668 static HRESULT Array_unshift(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
669         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
670 {
671     FIXME("\n");
672     return E_NOTIMPL;
673 }
674
675 static HRESULT Array_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
676         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
677 {
678     FIXME("\n");
679     return E_NOTIMPL;
680 }
681
682 static HRESULT Array_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
683         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
684 {
685     FIXME("\n");
686     return E_NOTIMPL;
687 }
688
689 static HRESULT Array_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
690         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
691 {
692     FIXME("\n");
693     return E_NOTIMPL;
694 }
695
696 static HRESULT Array_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
697         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
698 {
699     TRACE("\n");
700
701     switch(flags) {
702     case INVOKE_PROPERTYGET:
703         return array_join(dispex, lcid, ((ArrayInstance*)dispex)->length, default_separatorW, retv, ei, sp);
704     default:
705         FIXME("unimplemented flags %x\n", flags);
706         return E_NOTIMPL;
707     }
708
709     return S_OK;
710 }
711
712 static void Array_destructor(DispatchEx *dispex)
713 {
714     heap_free(dispex);
715 }
716
717 static void Array_on_put(DispatchEx *dispex, const WCHAR *name)
718 {
719     ArrayInstance *array = (ArrayInstance*)dispex;
720     const WCHAR *ptr = name;
721     DWORD id = 0;
722
723     if(!isdigitW(*ptr))
724         return;
725
726     while(*ptr && isdigitW(*ptr)) {
727         id = id*10 + (*ptr-'0');
728         ptr++;
729     }
730
731     if(*ptr)
732         return;
733
734     if(id >= array->length)
735         array->length = id+1;
736 }
737
738 static const builtin_prop_t Array_props[] = {
739     {concatW,                Array_concat,               PROPF_METHOD},
740     {hasOwnPropertyW,        Array_hasOwnProperty,       PROPF_METHOD},
741     {isPrototypeOfW,         Array_isPrototypeOf,        PROPF_METHOD},
742     {joinW,                  Array_join,                 PROPF_METHOD},
743     {lengthW,                Array_length,               0},
744     {popW,                   Array_pop,                  PROPF_METHOD},
745     {propertyIsEnumerableW,  Array_propertyIsEnumerable, PROPF_METHOD},
746     {pushW,                  Array_push,                 PROPF_METHOD},
747     {reverseW,               Array_reverse,              PROPF_METHOD},
748     {shiftW,                 Array_shift,                PROPF_METHOD},
749     {sliceW,                 Array_slice,                PROPF_METHOD},
750     {sortW,                  Array_sort,                 PROPF_METHOD},
751     {spliceW,                Array_splice,               PROPF_METHOD},
752     {toLocaleStringW,        Array_toLocaleString,       PROPF_METHOD},
753     {toStringW,              Array_toString,             PROPF_METHOD},
754     {unshiftW,               Array_unshift,              PROPF_METHOD},
755 };
756
757 static const builtin_info_t Array_info = {
758     JSCLASS_ARRAY,
759     {NULL, Array_value, 0},
760     sizeof(Array_props)/sizeof(*Array_props),
761     Array_props,
762     Array_destructor,
763     Array_on_put
764 };
765
766 static HRESULT ArrayConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
767         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
768 {
769     DispatchEx *obj;
770     VARIANT *arg_var;
771     DWORD i;
772     HRESULT hres;
773
774     TRACE("\n");
775
776     switch(flags) {
777     case DISPATCH_METHOD:
778     case DISPATCH_CONSTRUCT: {
779         if(arg_cnt(dp) == 1 && V_VT((arg_var = get_arg(dp, 0))) == VT_I4) {
780             if(V_I4(arg_var) < 0) {
781                 FIXME("throw RangeError\n");
782                 return E_FAIL;
783             }
784
785             hres = create_array(dispex->ctx, V_I4(arg_var), &obj);
786             if(FAILED(hres))
787                 return hres;
788
789             V_VT(retv) = VT_DISPATCH;
790             V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj);
791             return S_OK;
792         }
793
794         hres = create_array(dispex->ctx, arg_cnt(dp), &obj);
795         if(FAILED(hres))
796             return hres;
797
798         for(i=0; i < arg_cnt(dp); i++) {
799             hres = jsdisp_propput_idx(obj, i, lcid, get_arg(dp, i), ei, caller);
800             if(FAILED(hres))
801                 break;
802         }
803         if(FAILED(hres)) {
804             jsdisp_release(obj);
805             return hres;
806         }
807
808         V_VT(retv) = VT_DISPATCH;
809         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj);
810         break;
811     }
812     default:
813         FIXME("unimplemented flags: %x\n", flags);
814         return E_NOTIMPL;
815     }
816
817     return S_OK;
818 }
819
820 static HRESULT alloc_array(script_ctx_t *ctx, BOOL use_constr, ArrayInstance **ret)
821 {
822     ArrayInstance *array;
823     HRESULT hres;
824
825     array = heap_alloc_zero(sizeof(ArrayInstance));
826     if(!array)
827         return E_OUTOFMEMORY;
828
829     if(use_constr)
830         hres = init_dispex_from_constr(&array->dispex, ctx, &Array_info, ctx->array_constr);
831     else
832         hres = init_dispex_from_constr(&array->dispex, ctx, &Array_info, ctx->object_constr);
833
834     if(FAILED(hres)) {
835         heap_free(array);
836         return hres;
837     }
838
839     *ret = array;
840     return S_OK;
841 }
842
843 HRESULT create_array_constr(script_ctx_t *ctx, DispatchEx **ret)
844 {
845     ArrayInstance *array;
846     HRESULT hres;
847
848     hres = alloc_array(ctx, FALSE, &array);
849     if(FAILED(hres))
850         return hres;
851
852     hres = create_builtin_function(ctx, ArrayConstr_value, NULL, PROPF_CONSTR, &array->dispex, ret);
853
854     IDispatchEx_Release(_IDispatchEx_(&array->dispex));
855     return hres;
856 }
857
858 HRESULT create_array(script_ctx_t *ctx, DWORD length, DispatchEx **ret)
859 {
860     ArrayInstance *array;
861     HRESULT hres;
862
863     hres = alloc_array(ctx, TRUE, &array);
864     if(FAILED(hres))
865         return hres;
866
867     array->length = length;
868
869     *ret = &array->dispex;
870     return S_OK;
871 }