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