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