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