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