xref: /reactos/dll/win32/jscript/array.c (revision 48cc7814)
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 "config.h"
20 #include "wine/port.h"
21 
22 #include <math.h>
23 #include <assert.h>
24 
25 #include "jscript.h"
26 
27 #include "wine/debug.h"
28 
29 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
30 
31 typedef struct {
32     jsdisp_t dispex;
33 
34     DWORD length;
35 } ArrayInstance;
36 
37 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
38 static const WCHAR concatW[] = {'c','o','n','c','a','t',0};
39 static const WCHAR joinW[] = {'j','o','i','n',0};
40 static const WCHAR popW[] = {'p','o','p',0};
41 static const WCHAR pushW[] = {'p','u','s','h',0};
42 static const WCHAR reverseW[] = {'r','e','v','e','r','s','e',0};
43 static const WCHAR shiftW[] = {'s','h','i','f','t',0};
44 static const WCHAR sliceW[] = {'s','l','i','c','e',0};
45 static const WCHAR sortW[] = {'s','o','r','t',0};
46 static const WCHAR spliceW[] = {'s','p','l','i','c','e',0};
47 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
48 static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0};
49 static const WCHAR unshiftW[] = {'u','n','s','h','i','f','t',0};
50 static const WCHAR indexOfW[] = {'i','n','d','e','x','O','f',0};
51 
52 static const WCHAR default_separatorW[] = {',',0};
53 
54 static inline ArrayInstance *array_from_jsdisp(jsdisp_t *jsdisp)
55 {
56     return CONTAINING_RECORD(jsdisp, ArrayInstance, dispex);
57 }
58 
59 static inline ArrayInstance *array_from_vdisp(vdisp_t *vdisp)
60 {
61     return array_from_jsdisp(vdisp->u.jsdisp);
62 }
63 
64 static inline ArrayInstance *array_this(vdisp_t *jsthis)
65 {
66     return is_vclass(jsthis, JSCLASS_ARRAY) ? array_from_vdisp(jsthis) : NULL;
67 }
68 
69 unsigned array_get_length(jsdisp_t *array)
70 {
71     assert(is_class(array, JSCLASS_ARRAY));
72     return array_from_jsdisp(array)->length;
73 }
74 
75 static HRESULT get_length(script_ctx_t *ctx, vdisp_t *vdisp, jsdisp_t **jsthis, DWORD *ret)
76 {
77     ArrayInstance *array;
78     jsval_t val;
79     HRESULT hres;
80 
81     array = array_this(vdisp);
82     if(array) {
83         *jsthis = &array->dispex;
84         *ret = array->length;
85         return S_OK;
86     }
87 
88     if(!is_jsdisp(vdisp))
89         return throw_type_error(ctx, JS_E_JSCRIPT_EXPECTED, NULL);
90 
91     hres = jsdisp_propget_name(vdisp->u.jsdisp, lengthW, &val);
92     if(FAILED(hres))
93         return hres;
94 
95     hres = to_uint32(ctx, val, ret);
96     jsval_release(val);
97     if(FAILED(hres))
98         return hres;
99 
100     *jsthis = vdisp->u.jsdisp;
101     return S_OK;
102 }
103 
104 static HRESULT set_length(jsdisp_t *obj, DWORD length)
105 {
106     if(is_class(obj, JSCLASS_ARRAY)) {
107         array_from_jsdisp(obj)->length = length;
108         return S_OK;
109     }
110 
111     return jsdisp_propput_name(obj, lengthW, jsval_number(length));
112 }
113 
114 static WCHAR *idx_to_str(DWORD idx, WCHAR *ptr)
115 {
116     if(!idx) {
117         *ptr = '0';
118         return ptr;
119     }
120 
121     while(idx) {
122         *ptr-- = '0' + (idx%10);
123         idx /= 10;
124     }
125 
126     return ptr+1;
127 }
128 
129 static HRESULT Array_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
130 {
131     TRACE("%p\n", jsthis);
132 
133     *r = jsval_number(array_from_jsdisp(jsthis)->length);
134     return S_OK;
135 }
136 
137 static HRESULT Array_set_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
138 {
139     ArrayInstance *This = array_from_jsdisp(jsthis);
140     DOUBLE len = -1;
141     DWORD i;
142     HRESULT hres;
143 
144     TRACE("%p %d\n", This, This->length);
145 
146     hres = to_number(ctx, value, &len);
147     if(FAILED(hres))
148         return hres;
149 
150     len = floor(len);
151     if(len!=(DWORD)len)
152         return throw_range_error(ctx, JS_E_INVALID_LENGTH, NULL);
153 
154     for(i=len; i < This->length; i++) {
155         hres = jsdisp_delete_idx(&This->dispex, i);
156         if(FAILED(hres))
157             return hres;
158     }
159 
160     This->length = len;
161     return S_OK;
162 }
163 
164 static HRESULT concat_array(jsdisp_t *array, ArrayInstance *obj, DWORD *len)
165 {
166     jsval_t val;
167     DWORD i;
168     HRESULT hres;
169 
170     for(i=0; i < obj->length; i++) {
171         hres = jsdisp_get_idx(&obj->dispex, i, &val);
172         if(hres == DISP_E_UNKNOWNNAME)
173             continue;
174         if(FAILED(hres))
175             return hres;
176 
177         hres = jsdisp_propput_idx(array, *len+i, val);
178         jsval_release(val);
179         if(FAILED(hres))
180             return hres;
181     }
182 
183     *len += obj->length;
184     return S_OK;
185 }
186 
187 static HRESULT concat_obj(jsdisp_t *array, IDispatch *obj, DWORD *len)
188 {
189     jsdisp_t *jsobj;
190     HRESULT hres;
191 
192     jsobj = iface_to_jsdisp(obj);
193     if(jsobj) {
194         if(is_class(jsobj, JSCLASS_ARRAY)) {
195             hres = concat_array(array, array_from_jsdisp(jsobj), len);
196             jsdisp_release(jsobj);
197             return hres;
198         }
199         jsdisp_release(jsobj);
200     }
201 
202     return jsdisp_propput_idx(array, (*len)++, jsval_disp(obj));
203 }
204 
205 static HRESULT Array_concat(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
206         jsval_t *r)
207 {
208     jsdisp_t *ret;
209     DWORD len = 0;
210     HRESULT hres;
211 
212     TRACE("\n");
213 
214     hres = create_array(ctx, 0, &ret);
215     if(FAILED(hres))
216         return hres;
217 
218     hres = concat_obj(ret, jsthis->u.disp, &len);
219     if(SUCCEEDED(hres)) {
220         DWORD i;
221 
222         for(i=0; i < argc; i++) {
223             if(is_object_instance(argv[i]))
224                 hres = concat_obj(ret, get_object(argv[i]), &len);
225             else
226                 hres = jsdisp_propput_idx(ret, len++, argv[i]);
227             if(FAILED(hres))
228                 break;
229         }
230     }
231 
232     if(FAILED(hres))
233         return hres;
234 
235     if(r)
236         *r = jsval_obj(ret);
237     else
238         jsdisp_release(ret);
239     return S_OK;
240 }
241 
242 static HRESULT array_join(script_ctx_t *ctx, jsdisp_t *array, DWORD length, const WCHAR *sep,
243         unsigned seplen, jsval_t *r)
244 {
245     jsstr_t **str_tab, *ret = NULL;
246     jsval_t val;
247     DWORD i;
248     HRESULT hres = E_FAIL;
249 
250     if(!length) {
251         if(r)
252             *r = jsval_string(jsstr_empty());
253         return S_OK;
254     }
255 
256     str_tab = heap_alloc_zero(length * sizeof(*str_tab));
257     if(!str_tab)
258         return E_OUTOFMEMORY;
259 
260     for(i=0; i < length; i++) {
261         hres = jsdisp_get_idx(array, i, &val);
262         if(hres == DISP_E_UNKNOWNNAME) {
263             hres = S_OK;
264             continue;
265         } else if(FAILED(hres))
266             break;
267 
268         if(!is_undefined(val) && !is_null(val)) {
269             hres = to_string(ctx, val, str_tab+i);
270             jsval_release(val);
271             if(FAILED(hres))
272                 break;
273         }
274     }
275 
276     if(SUCCEEDED(hres)) {
277         DWORD len = 0;
278 
279         if(str_tab[0])
280             len = jsstr_length(str_tab[0]);
281         for(i=1; i < length; i++) {
282             len += seplen;
283             if(str_tab[i])
284                 len += jsstr_length(str_tab[i]);
285             if(len > JSSTR_MAX_LENGTH) {
286                 hres = E_OUTOFMEMORY;
287                 break;
288             }
289         }
290 
291         if(SUCCEEDED(hres)) {
292             WCHAR *ptr = NULL;
293 
294             ret = jsstr_alloc_buf(len, &ptr);
295             if(ret) {
296                 if(str_tab[0])
297                     ptr += jsstr_flush(str_tab[0], ptr);
298 
299                 for(i=1; i < length; i++) {
300                     if(seplen) {
301                         memcpy(ptr, sep, seplen*sizeof(WCHAR));
302                         ptr += seplen;
303                     }
304 
305                     if(str_tab[i])
306                         ptr += jsstr_flush(str_tab[i], ptr);
307                 }
308             }else {
309                 hres = E_OUTOFMEMORY;
310             }
311         }
312     }
313 
314     for(i=0; i < length; i++) {
315         if(str_tab[i])
316             jsstr_release(str_tab[i]);
317     }
318     heap_free(str_tab);
319     if(FAILED(hres))
320         return hres;
321 
322     TRACE("= %s\n", debugstr_jsstr(ret));
323 
324     if(r)
325         *r = jsval_string(ret);
326     else
327         jsstr_release(ret);
328     return S_OK;
329 }
330 
331 /* ECMA-262 3rd Edition    15.4.4.5 */
332 static HRESULT Array_join(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
333         jsval_t *r)
334 {
335     jsdisp_t *jsthis;
336     DWORD length;
337     HRESULT hres;
338 
339     TRACE("\n");
340 
341     hres = get_length(ctx, vthis, &jsthis, &length);
342     if(FAILED(hres))
343         return hres;
344 
345     if(argc) {
346         const WCHAR *sep;
347         jsstr_t *sep_str;
348 
349         hres = to_flat_string(ctx, argv[0], &sep_str, &sep);
350         if(FAILED(hres))
351             return hres;
352 
353         hres = array_join(ctx, jsthis, length, sep, jsstr_length(sep_str), r);
354 
355         jsstr_release(sep_str);
356     }else {
357         hres = array_join(ctx, jsthis, length, default_separatorW, strlenW(default_separatorW), r);
358     }
359 
360     return hres;
361 }
362 
363 static HRESULT Array_pop(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
364         jsval_t *r)
365 {
366     jsdisp_t *jsthis;
367     jsval_t val;
368     DWORD length;
369     HRESULT hres;
370 
371     TRACE("\n");
372 
373     hres = get_length(ctx, vthis, &jsthis, &length);
374     if(FAILED(hres))
375         return hres;
376 
377     if(!length) {
378         hres = set_length(jsthis, 0);
379         if(FAILED(hres))
380             return hres;
381 
382         if(r)
383             *r = jsval_undefined();
384         return S_OK;
385     }
386 
387     length--;
388     hres = jsdisp_get_idx(jsthis, length, &val);
389     if(SUCCEEDED(hres))
390         hres = jsdisp_delete_idx(jsthis, length);
391     else if(hres == DISP_E_UNKNOWNNAME) {
392         val = jsval_undefined();
393         hres = S_OK;
394     }else
395         return hres;
396 
397     if(SUCCEEDED(hres))
398         hres = set_length(jsthis, length);
399 
400     if(FAILED(hres)) {
401         jsval_release(val);
402         return hres;
403     }
404 
405     if(r)
406         *r = val;
407     else
408         jsval_release(val);
409     return hres;
410 }
411 
412 /* ECMA-262 3rd Edition    15.4.4.7 */
413 static HRESULT Array_push(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
414         jsval_t *r)
415 {
416     jsdisp_t *jsthis;
417     DWORD length = 0;
418     unsigned i;
419     HRESULT hres;
420 
421     TRACE("\n");
422 
423     hres = get_length(ctx, vthis, &jsthis, &length);
424     if(FAILED(hres))
425         return hres;
426 
427     for(i=0; i < argc; i++) {
428         hres = jsdisp_propput_idx(jsthis, length+i, argv[i]);
429         if(FAILED(hres))
430             return hres;
431     }
432 
433     hres = set_length(jsthis, length+argc);
434     if(FAILED(hres))
435         return hres;
436 
437     if(r)
438         *r = jsval_number(length+argc);
439     return S_OK;
440 }
441 
442 static HRESULT Array_reverse(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
443         jsval_t *r)
444 {
445     jsdisp_t *jsthis;
446     DWORD length, k, l;
447     jsval_t v1, v2;
448     HRESULT hres1, hres2;
449 
450     TRACE("\n");
451 
452     hres1 = get_length(ctx, vthis, &jsthis, &length);
453     if(FAILED(hres1))
454         return hres1;
455 
456     for(k=0; k<length/2; k++) {
457         l = length-k-1;
458 
459         hres1 = jsdisp_get_idx(jsthis, k, &v1);
460         if(FAILED(hres1) && hres1!=DISP_E_UNKNOWNNAME)
461             return hres1;
462 
463         hres2 = jsdisp_get_idx(jsthis, l, &v2);
464         if(FAILED(hres2) && hres2!=DISP_E_UNKNOWNNAME) {
465             jsval_release(v1);
466             return hres2;
467         }
468 
469         if(hres1 == DISP_E_UNKNOWNNAME)
470             hres1 = jsdisp_delete_idx(jsthis, l);
471         else
472             hres1 = jsdisp_propput_idx(jsthis, l, v1);
473 
474         if(FAILED(hres1)) {
475             jsval_release(v1);
476             jsval_release(v2);
477             return hres1;
478         }
479 
480         if(hres2 == DISP_E_UNKNOWNNAME)
481             hres2 = jsdisp_delete_idx(jsthis, k);
482         else
483             hres2 = jsdisp_propput_idx(jsthis, k, v2);
484 
485         if(FAILED(hres2)) {
486             jsval_release(v2);
487             return hres2;
488         }
489     }
490 
491     if(r)
492         *r = jsval_obj(jsdisp_addref(jsthis));
493     return S_OK;
494 }
495 
496 /* ECMA-262 3rd Edition    15.4.4.9 */
497 static HRESULT Array_shift(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
498         jsval_t *r)
499 {
500     jsdisp_t *jsthis;
501     DWORD length = 0, i;
502     jsval_t v, ret;
503     HRESULT hres;
504 
505     TRACE("\n");
506 
507     hres = get_length(ctx, vthis, &jsthis, &length);
508     if(FAILED(hres))
509         return hres;
510 
511     if(!length) {
512         hres = set_length(jsthis, 0);
513         if(FAILED(hres))
514             return hres;
515 
516         if(r)
517             *r = jsval_undefined();
518         return S_OK;
519     }
520 
521     hres = jsdisp_get_idx(jsthis, 0, &ret);
522     if(hres == DISP_E_UNKNOWNNAME) {
523         ret = jsval_undefined();
524         hres = S_OK;
525     }
526 
527     for(i=1; SUCCEEDED(hres) && i<length; i++) {
528         hres = jsdisp_get_idx(jsthis, i, &v);
529         if(hres == DISP_E_UNKNOWNNAME)
530             hres = jsdisp_delete_idx(jsthis, i-1);
531         else if(SUCCEEDED(hres))
532             hres = jsdisp_propput_idx(jsthis, i-1, v);
533     }
534 
535     if(SUCCEEDED(hres)) {
536         hres = jsdisp_delete_idx(jsthis, length-1);
537         if(SUCCEEDED(hres))
538             hres = set_length(jsthis, length-1);
539     }
540 
541     if(FAILED(hres))
542         return hres;
543 
544     if(r)
545         *r = ret;
546     else
547         jsval_release(ret);
548     return hres;
549 }
550 
551 /* ECMA-262 3rd Edition    15.4.4.10 */
552 static HRESULT Array_slice(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
553 {
554     jsdisp_t *arr, *jsthis;
555     DOUBLE range;
556     DWORD length, start, end, idx;
557     HRESULT hres;
558 
559     TRACE("\n");
560 
561     hres = get_length(ctx, vthis, &jsthis, &length);
562     if(FAILED(hres))
563         return hres;
564 
565     if(argc) {
566         hres = to_number(ctx, argv[0], &range);
567         if(FAILED(hres))
568             return hres;
569 
570         range = floor(range);
571         if(-range>length || isnan(range)) start = 0;
572         else if(range < 0) start = range+length;
573         else if(range <= length) start = range;
574         else start = length;
575     }
576     else start = 0;
577 
578     if(argc > 1) {
579         hres = to_number(ctx, argv[1], &range);
580         if(FAILED(hres))
581             return hres;
582 
583         range = floor(range);
584         if(-range>length) end = 0;
585         else if(range < 0) end = range+length;
586         else if(range <= length) end = range;
587         else end = length;
588     }
589     else end = length;
590 
591     hres = create_array(ctx, (end>start)?end-start:0, &arr);
592     if(FAILED(hres))
593         return hres;
594 
595     for(idx=start; idx<end; idx++) {
596         jsval_t v;
597 
598         hres = jsdisp_get_idx(jsthis, idx, &v);
599         if(hres == DISP_E_UNKNOWNNAME)
600             continue;
601 
602         if(SUCCEEDED(hres)) {
603             hres = jsdisp_propput_idx(arr, idx-start, v);
604             jsval_release(v);
605         }
606 
607         if(FAILED(hres)) {
608             jsdisp_release(arr);
609             return hres;
610         }
611     }
612 
613     if(r)
614         *r = jsval_obj(arr);
615     else
616         jsdisp_release(arr);
617 
618     return S_OK;
619 }
620 
621 static HRESULT sort_cmp(script_ctx_t *ctx, jsdisp_t *cmp_func, jsval_t v1, jsval_t v2, INT *cmp)
622 {
623     HRESULT hres;
624 
625     if(cmp_func) {
626         jsval_t args[2];
627         jsval_t res;
628         double n;
629 
630         args[0] = v1;
631         args[1] = v2;
632 
633         hres = jsdisp_call_value(cmp_func, NULL, DISPATCH_METHOD, 2, args, &res);
634         if(FAILED(hres))
635             return hres;
636 
637         hres = to_number(ctx, res, &n);
638         jsval_release(res);
639         if(FAILED(hres))
640             return hres;
641 
642         if(n == 0)
643             *cmp = 0;
644         *cmp = n > 0.0 ? 1 : -1;
645     }else if(is_undefined(v1)) {
646         *cmp = is_undefined(v2) ? 0 : 1;
647     }else if(is_undefined(v2)) {
648         *cmp = -1;
649     }else if(is_number(v1) && is_number(v2)) {
650         double d = get_number(v1)-get_number(v2);
651         if(d > 0.0)
652             *cmp = 1;
653         else
654             *cmp = d < -0.0 ? -1 : 0;
655     }else {
656         jsstr_t *x, *y;
657 
658         hres = to_string(ctx, v1, &x);
659         if(FAILED(hres))
660             return hres;
661 
662         hres = to_string(ctx, v2, &y);
663         if(SUCCEEDED(hres)) {
664             *cmp = jsstr_cmp(x, y);
665             jsstr_release(y);
666         }
667         jsstr_release(x);
668         if(FAILED(hres))
669             return hres;
670     }
671 
672     return S_OK;
673 }
674 
675 /* ECMA-262 3rd Edition    15.4.4.11 */
676 static HRESULT Array_sort(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
677         jsval_t *r)
678 {
679     jsdisp_t *jsthis, *cmp_func = NULL;
680     jsval_t *vtab, **sorttab = NULL;
681     DWORD length;
682     DWORD i;
683     HRESULT hres = S_OK;
684 
685     TRACE("\n");
686 
687     hres = get_length(ctx, vthis, &jsthis, &length);
688     if(FAILED(hres))
689         return hres;
690 
691     if(argc > 1) {
692         WARN("invalid arg_cnt %d\n", argc);
693         return E_FAIL;
694     }
695 
696     if(argc == 1) {
697         if(!is_object_instance(argv[0])) {
698             WARN("arg is not dispatch\n");
699             return E_FAIL;
700         }
701 
702         cmp_func = iface_to_jsdisp(get_object(argv[0]));
703         if(!cmp_func || !is_class(cmp_func, JSCLASS_FUNCTION)) {
704             WARN("cmp_func is not a function\n");
705             if(cmp_func)
706                 jsdisp_release(cmp_func);
707             return E_FAIL;
708         }
709     }
710 
711     if(!length) {
712         if(cmp_func)
713             jsdisp_release(cmp_func);
714         if(r)
715             *r = jsval_obj(jsdisp_addref(jsthis));
716         return S_OK;
717     }
718 
719     vtab = heap_alloc_zero(length * sizeof(*vtab));
720     if(vtab) {
721         for(i=0; i<length; i++) {
722             hres = jsdisp_get_idx(jsthis, i, vtab+i);
723             if(hres == DISP_E_UNKNOWNNAME) {
724                 vtab[i] = jsval_undefined();
725                 hres = S_OK;
726             } else if(FAILED(hres)) {
727                 WARN("Could not get elem %d: %08x\n", i, hres);
728                 break;
729             }
730         }
731     }else {
732         hres = E_OUTOFMEMORY;
733     }
734 
735     if(SUCCEEDED(hres)) {
736         sorttab = heap_alloc(length*2*sizeof(*sorttab));
737         if(!sorttab)
738             hres = E_OUTOFMEMORY;
739     }
740 
741     /* merge-sort */
742     if(SUCCEEDED(hres)) {
743         jsval_t *tmpv, **tmpbuf;
744         INT cmp;
745 
746         tmpbuf = sorttab + length;
747         for(i=0; i < length; i++)
748             sorttab[i] = vtab+i;
749 
750         for(i=0; i < length/2; i++) {
751             hres = sort_cmp(ctx, cmp_func, *sorttab[2*i+1], *sorttab[2*i], &cmp);
752             if(FAILED(hres))
753                 break;
754 
755             if(cmp < 0) {
756                 tmpv = sorttab[2*i];
757                 sorttab[2*i] = sorttab[2*i+1];
758                 sorttab[2*i+1] = tmpv;
759             }
760         }
761 
762         if(SUCCEEDED(hres)) {
763             DWORD k, a, b, bend;
764 
765             for(k=2; k < length; k *= 2) {
766                 for(i=0; i+k < length; i += 2*k) {
767                     a = b = 0;
768                     if(i+2*k <= length)
769                         bend = k;
770                     else
771                         bend = length - (i+k);
772 
773                     memcpy(tmpbuf, sorttab+i, k*sizeof(jsval_t*));
774 
775                     while(a < k && b < bend) {
776                         hres = sort_cmp(ctx, cmp_func, *tmpbuf[a], *sorttab[i+k+b], &cmp);
777                         if(FAILED(hres))
778                             break;
779 
780                         if(cmp < 0) {
781                             sorttab[i+a+b] = tmpbuf[a];
782                             a++;
783                         }else {
784                             sorttab[i+a+b] = sorttab[i+k+b];
785                             b++;
786                         }
787                     }
788 
789                     if(FAILED(hres))
790                         break;
791 
792                     if(a < k)
793                         memcpy(sorttab+i+a+b, tmpbuf+a, (k-a)*sizeof(jsval_t*));
794                 }
795 
796                 if(FAILED(hres))
797                     break;
798             }
799         }
800 
801         for(i=0; SUCCEEDED(hres) && i < length; i++)
802             hres = jsdisp_propput_idx(jsthis, i, *sorttab[i]);
803     }
804 
805     if(vtab) {
806         for(i=0; i < length; i++)
807             jsval_release(vtab[i]);
808         heap_free(vtab);
809     }
810     heap_free(sorttab);
811     if(cmp_func)
812         jsdisp_release(cmp_func);
813 
814     if(FAILED(hres))
815         return hres;
816 
817     if(r)
818         *r = jsval_obj(jsdisp_addref(jsthis));
819     return S_OK;
820 }
821 
822 /* ECMA-262 3rd Edition    15.4.4.12 */
823 static HRESULT Array_splice(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
824         jsval_t *r)
825 {
826     DWORD length, start=0, delete_cnt=0, i, add_args = 0;
827     jsdisp_t *ret_array = NULL, *jsthis;
828     jsval_t val;
829     double d;
830     int n;
831     HRESULT hres = S_OK;
832 
833     TRACE("\n");
834 
835     hres = get_length(ctx, vthis, &jsthis, &length);
836     if(FAILED(hres))
837         return hres;
838 
839     if(argc) {
840         hres = to_integer(ctx, argv[0], &d);
841         if(FAILED(hres))
842             return hres;
843 
844         if(is_int32(d)) {
845             if((n = d) >= 0)
846                 start = min(n, length);
847             else
848                 start = -n > length ? 0 : length + n;
849         }else {
850             start = d < 0.0 ? 0 : length;
851         }
852     }
853 
854     if(argc >= 2) {
855         hres = to_integer(ctx, argv[1], &d);
856         if(FAILED(hres))
857             return hres;
858 
859         if(is_int32(d)) {
860             if((n = d) > 0)
861                 delete_cnt = min(n, length-start);
862         }else if(d > 0.0) {
863             delete_cnt = length-start;
864         }
865 
866         add_args = argc-2;
867     }
868 
869     if(r) {
870         hres = create_array(ctx, 0, &ret_array);
871         if(FAILED(hres))
872             return hres;
873 
874         for(i=0; SUCCEEDED(hres) && i < delete_cnt; i++) {
875             hres = jsdisp_get_idx(jsthis, start+i, &val);
876             if(hres == DISP_E_UNKNOWNNAME) {
877                 hres = S_OK;
878             }else if(SUCCEEDED(hres)) {
879                 hres = jsdisp_propput_idx(ret_array, i, val);
880                 jsval_release(val);
881             }
882         }
883 
884         if(SUCCEEDED(hres))
885             hres = jsdisp_propput_name(ret_array, lengthW, jsval_number(delete_cnt));
886     }
887 
888     if(add_args < delete_cnt) {
889         for(i = start; SUCCEEDED(hres) && i < length-delete_cnt; i++) {
890             hres = jsdisp_get_idx(jsthis, i+delete_cnt, &val);
891             if(hres == DISP_E_UNKNOWNNAME) {
892                 hres = jsdisp_delete_idx(jsthis, i+add_args);
893             }else if(SUCCEEDED(hres)) {
894                 hres = jsdisp_propput_idx(jsthis, i+add_args, val);
895                 jsval_release(val);
896             }
897         }
898 
899         for(i=length; SUCCEEDED(hres) && i != length-delete_cnt+add_args; i--)
900             hres = jsdisp_delete_idx(jsthis, i-1);
901     }else if(add_args > delete_cnt) {
902         for(i=length-delete_cnt; SUCCEEDED(hres) && i != start; i--) {
903             hres = jsdisp_get_idx(jsthis, i+delete_cnt-1, &val);
904             if(hres == DISP_E_UNKNOWNNAME) {
905                 hres = jsdisp_delete_idx(jsthis, i+add_args-1);
906             }else if(SUCCEEDED(hres)) {
907                 hres = jsdisp_propput_idx(jsthis, i+add_args-1, val);
908                 jsval_release(val);
909             }
910         }
911     }
912 
913     for(i=0; SUCCEEDED(hres) && i < add_args; i++)
914         hres = jsdisp_propput_idx(jsthis, start+i, argv[i+2]);
915 
916     if(SUCCEEDED(hres))
917         hres = jsdisp_propput_name(jsthis, lengthW, jsval_number(length-delete_cnt+add_args));
918 
919     if(FAILED(hres)) {
920         if(ret_array)
921             jsdisp_release(ret_array);
922         return hres;
923     }
924 
925     if(r)
926         *r = jsval_obj(ret_array);
927     return S_OK;
928 }
929 
930 /* ECMA-262 3rd Edition    15.4.4.2 */
931 static HRESULT Array_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
932         jsval_t *r)
933 {
934     ArrayInstance *array;
935 
936     TRACE("\n");
937 
938     array = array_this(jsthis);
939     if(!array)
940         return throw_type_error(ctx, JS_E_ARRAY_EXPECTED, NULL);
941 
942     return array_join(ctx, &array->dispex, array->length, default_separatorW,
943                       strlenW(default_separatorW), r);
944 }
945 
946 static HRESULT Array_toLocaleString(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
947         jsval_t *r)
948 {
949     FIXME("\n");
950     return E_NOTIMPL;
951 }
952 
953 static HRESULT Array_indexOf(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
954         jsval_t *r)
955 {
956     jsdisp_t *jsthis;
957     unsigned length, i, from = 0;
958     jsval_t search, value;
959     BOOL eq;
960     HRESULT hres;
961 
962     TRACE("\n");
963 
964     hres = get_length(ctx, vthis, &jsthis, &length);
965     if(FAILED(hres))
966         return hres;
967     if(!length) {
968         if(r) *r = jsval_number(-1);
969         return S_OK;
970     }
971 
972     search = argc ? argv[0] : jsval_undefined();
973 
974     if(argc > 1) {
975         double from_arg;
976 
977         hres = to_integer(ctx, argv[1], &from_arg);
978         if(FAILED(hres))
979             return hres;
980 
981         if(from_arg >= 0)
982             from = min(from_arg, length);
983         else
984             from = max(from_arg + length, 0);
985     }
986 
987     for(i = from; i < length; i++) {
988         hres = jsdisp_get_idx(jsthis, i, &value);
989         if(hres == DISP_E_UNKNOWNNAME)
990             continue;
991         if(FAILED(hres))
992             return hres;
993 
994         hres = jsval_strict_equal(value, search, &eq);
995         jsval_release(value);
996         if(FAILED(hres))
997             return hres;
998         if(eq) {
999             if(r) *r = jsval_number(i);
1000             return S_OK;
1001         }
1002     }
1003 
1004     if(r) *r = jsval_number(-1);
1005     return S_OK;
1006 }
1007 
1008 /* ECMA-262 3rd Edition    15.4.4.13 */
1009 static HRESULT Array_unshift(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
1010         jsval_t *r)
1011 {
1012     jsdisp_t *jsthis;
1013     WCHAR buf[14], *buf_end, *str;
1014     DWORD i, length;
1015     jsval_t val;
1016     DISPID id;
1017     HRESULT hres;
1018 
1019     TRACE("\n");
1020 
1021     hres = get_length(ctx, vthis, &jsthis, &length);
1022     if(FAILED(hres))
1023         return hres;
1024 
1025     if(argc) {
1026         buf_end = buf + sizeof(buf)/sizeof(WCHAR)-1;
1027         *buf_end-- = 0;
1028         i = length;
1029 
1030         while(i--) {
1031             str = idx_to_str(i, buf_end);
1032 
1033             hres = jsdisp_get_id(jsthis, str, 0, &id);
1034             if(SUCCEEDED(hres)) {
1035                 hres = jsdisp_propget(jsthis, id, &val);
1036                 if(FAILED(hres))
1037                     return hres;
1038 
1039                 hres = jsdisp_propput_idx(jsthis, i+argc, val);
1040                 jsval_release(val);
1041             }else if(hres == DISP_E_UNKNOWNNAME) {
1042                 hres = IDispatchEx_DeleteMemberByDispID(vthis->u.dispex, id);
1043             }
1044         }
1045 
1046         if(FAILED(hres))
1047             return hres;
1048     }
1049 
1050     for(i=0; i<argc; i++) {
1051         hres = jsdisp_propput_idx(jsthis, i, argv[i]);
1052         if(FAILED(hres))
1053             return hres;
1054     }
1055 
1056     if(argc) {
1057         length += argc;
1058         hres = set_length(jsthis, length);
1059         if(FAILED(hres))
1060             return hres;
1061     }
1062 
1063     if(r)
1064         *r = ctx->version < 2 ? jsval_undefined() : jsval_number(length);
1065     return S_OK;
1066 }
1067 
1068 static HRESULT Array_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
1069 {
1070     ArrayInstance *array = array_from_jsdisp(jsthis);
1071 
1072     TRACE("\n");
1073 
1074     return array_join(ctx, &array->dispex, array->length, default_separatorW,
1075                       strlenW(default_separatorW), r);
1076 }
1077 
1078 static void Array_destructor(jsdisp_t *dispex)
1079 {
1080     heap_free(dispex);
1081 }
1082 
1083 static void Array_on_put(jsdisp_t *dispex, const WCHAR *name)
1084 {
1085     ArrayInstance *array = array_from_jsdisp(dispex);
1086     const WCHAR *ptr = name;
1087     DWORD id = 0;
1088 
1089     if(!isdigitW(*ptr))
1090         return;
1091 
1092     while(*ptr && isdigitW(*ptr)) {
1093         id = id*10 + (*ptr-'0');
1094         ptr++;
1095     }
1096 
1097     if(*ptr)
1098         return;
1099 
1100     if(id >= array->length)
1101         array->length = id+1;
1102 }
1103 
1104 static const builtin_prop_t Array_props[] = {
1105     {concatW,                Array_concat,               PROPF_METHOD|1},
1106     {indexOfW,               Array_indexOf,              PROPF_ES5|PROPF_METHOD|1},
1107     {joinW,                  Array_join,                 PROPF_METHOD|1},
1108     {lengthW,                NULL,0,                     Array_get_length, Array_set_length},
1109     {popW,                   Array_pop,                  PROPF_METHOD},
1110     {pushW,                  Array_push,                 PROPF_METHOD|1},
1111     {reverseW,               Array_reverse,              PROPF_METHOD},
1112     {shiftW,                 Array_shift,                PROPF_METHOD},
1113     {sliceW,                 Array_slice,                PROPF_METHOD|2},
1114     {sortW,                  Array_sort,                 PROPF_METHOD|1},
1115     {spliceW,                Array_splice,               PROPF_METHOD|2},
1116     {toLocaleStringW,        Array_toLocaleString,       PROPF_METHOD},
1117     {toStringW,              Array_toString,             PROPF_METHOD},
1118     {unshiftW,               Array_unshift,              PROPF_METHOD|1},
1119 };
1120 
1121 static const builtin_info_t Array_info = {
1122     JSCLASS_ARRAY,
1123     {NULL, NULL,0, Array_get_value},
1124     sizeof(Array_props)/sizeof(*Array_props),
1125     Array_props,
1126     Array_destructor,
1127     Array_on_put
1128 };
1129 
1130 static const builtin_prop_t ArrayInst_props[] = {
1131     {lengthW,                NULL,0,                     Array_get_length, Array_set_length}
1132 };
1133 
1134 static const builtin_info_t ArrayInst_info = {
1135     JSCLASS_ARRAY,
1136     {NULL, NULL,0, Array_get_value},
1137     sizeof(ArrayInst_props)/sizeof(*ArrayInst_props),
1138     ArrayInst_props,
1139     Array_destructor,
1140     Array_on_put
1141 };
1142 
1143 /* ECMA-262 5.1 Edition    15.4.3.2 */
1144 static HRESULT ArrayConstr_isArray(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1145 {
1146     jsdisp_t *obj;
1147 
1148     TRACE("\n");
1149 
1150     if(!argc || !is_object_instance(argv[0])) {
1151         if(r) *r = jsval_bool(FALSE);
1152         return S_OK;
1153     }
1154 
1155     obj = iface_to_jsdisp(get_object(argv[0]));
1156     if(r) *r = jsval_bool(obj && is_class(obj, JSCLASS_ARRAY));
1157     if(obj) jsdisp_release(obj);
1158     return S_OK;
1159 }
1160 
1161 static HRESULT ArrayConstr_value(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
1162         jsval_t *r)
1163 {
1164     jsdisp_t *obj;
1165     DWORD i;
1166     HRESULT hres;
1167 
1168     TRACE("\n");
1169 
1170     switch(flags) {
1171     case DISPATCH_METHOD:
1172     case DISPATCH_CONSTRUCT: {
1173         if(argc == 1 && is_number(argv[0])) {
1174             double n = get_number(argv[0]);
1175 
1176             if(n < 0 || !is_int32(n))
1177                 return throw_range_error(ctx, JS_E_INVALID_LENGTH, NULL);
1178 
1179             hres = create_array(ctx, n, &obj);
1180             if(FAILED(hres))
1181                 return hres;
1182 
1183             *r = jsval_obj(obj);
1184             return S_OK;
1185         }
1186 
1187         hres = create_array(ctx, argc, &obj);
1188         if(FAILED(hres))
1189             return hres;
1190 
1191         for(i=0; i < argc; i++) {
1192             hres = jsdisp_propput_idx(obj, i, argv[i]);
1193             if(FAILED(hres))
1194                 break;
1195         }
1196         if(FAILED(hres)) {
1197             jsdisp_release(obj);
1198             return hres;
1199         }
1200 
1201         *r = jsval_obj(obj);
1202         break;
1203     }
1204     default:
1205         FIXME("unimplemented flags: %x\n", flags);
1206         return E_NOTIMPL;
1207     }
1208 
1209     return S_OK;
1210 }
1211 
1212 static HRESULT alloc_array(script_ctx_t *ctx, jsdisp_t *object_prototype, ArrayInstance **ret)
1213 {
1214     ArrayInstance *array;
1215     HRESULT hres;
1216 
1217     array = heap_alloc_zero(sizeof(ArrayInstance));
1218     if(!array)
1219         return E_OUTOFMEMORY;
1220 
1221     if(object_prototype)
1222         hres = init_dispex(&array->dispex, ctx, &Array_info, object_prototype);
1223     else
1224         hres = init_dispex_from_constr(&array->dispex, ctx, &ArrayInst_info, ctx->array_constr);
1225 
1226     if(FAILED(hres)) {
1227         heap_free(array);
1228         return hres;
1229     }
1230 
1231     *ret = array;
1232     return S_OK;
1233 }
1234 
1235 static const WCHAR isArrayW[] = {'i','s','A','r','r','a','y',0};
1236 
1237 static const builtin_prop_t ArrayConstr_props[] = {
1238     {isArrayW,    ArrayConstr_isArray,    PROPF_ES5|PROPF_METHOD|1}
1239 };
1240 
1241 static const builtin_info_t ArrayConstr_info = {
1242     JSCLASS_FUNCTION,
1243     DEFAULT_FUNCTION_VALUE,
1244     sizeof(ArrayConstr_props)/sizeof(*ArrayConstr_props),
1245     ArrayConstr_props,
1246     NULL,
1247     NULL
1248 };
1249 
1250 HRESULT create_array_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1251 {
1252     ArrayInstance *array;
1253     HRESULT hres;
1254 
1255     static const WCHAR ArrayW[] = {'A','r','r','a','y',0};
1256 
1257     hres = alloc_array(ctx, object_prototype, &array);
1258     if(FAILED(hres))
1259         return hres;
1260 
1261     hres = create_builtin_constructor(ctx, ArrayConstr_value, ArrayW, &ArrayConstr_info, PROPF_CONSTR|1, &array->dispex, ret);
1262 
1263     jsdisp_release(&array->dispex);
1264     return hres;
1265 }
1266 
1267 HRESULT create_array(script_ctx_t *ctx, DWORD length, jsdisp_t **ret)
1268 {
1269     ArrayInstance *array;
1270     HRESULT hres;
1271 
1272     hres = alloc_array(ctx, NULL, &array);
1273     if(FAILED(hres))
1274         return hres;
1275 
1276     array->length = length;
1277 
1278     *ret = &array->dispex;
1279     return S_OK;
1280 }
1281