winex11.drv: Update a comment.
[wine.git] / dlls / jscript / array.c
blob7941031e491dc67cb7fa1cd7bc1352beaa6847a2
1 /*
2 * Copyright 2008 Jacek Caban for CodeWeavers
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.
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.
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
20 #include <math.h>
21 #include <assert.h>
23 #include "jscript.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
29 typedef struct {
30 jsdisp_t dispex;
32 DWORD length;
33 } ArrayInstance;
35 static inline ArrayInstance *array_from_jsdisp(jsdisp_t *jsdisp)
37 return CONTAINING_RECORD(jsdisp, ArrayInstance, dispex);
40 static inline ArrayInstance *array_from_vdisp(vdisp_t *vdisp)
42 return array_from_jsdisp(vdisp->u.jsdisp);
45 static inline ArrayInstance *array_this(vdisp_t *jsthis)
47 return is_vclass(jsthis, JSCLASS_ARRAY) ? array_from_vdisp(jsthis) : NULL;
50 unsigned array_get_length(jsdisp_t *array)
52 assert(is_class(array, JSCLASS_ARRAY));
53 return array_from_jsdisp(array)->length;
56 static HRESULT get_length(script_ctx_t *ctx, vdisp_t *vdisp, jsdisp_t **jsthis, DWORD *ret)
58 ArrayInstance *array;
59 jsval_t val;
60 HRESULT hres;
62 array = array_this(vdisp);
63 if(array) {
64 *jsthis = &array->dispex;
65 *ret = array->length;
66 return S_OK;
69 if(!is_jsdisp(vdisp))
70 return JS_E_JSCRIPT_EXPECTED;
72 hres = jsdisp_propget_name(vdisp->u.jsdisp, L"length", &val);
73 if(FAILED(hres))
74 return hres;
76 hres = to_uint32(ctx, val, ret);
77 jsval_release(val);
78 if(FAILED(hres))
79 return hres;
81 *jsthis = vdisp->u.jsdisp;
82 return S_OK;
85 static HRESULT set_length(jsdisp_t *obj, DWORD length)
87 if(is_class(obj, JSCLASS_ARRAY)) {
88 array_from_jsdisp(obj)->length = length;
89 return S_OK;
92 return jsdisp_propput_name(obj, L"length", jsval_number(length));
95 static WCHAR *idx_to_str(DWORD idx, WCHAR *ptr)
97 if(!idx) {
98 *ptr = '0';
99 return ptr;
102 while(idx) {
103 *ptr-- = '0' + (idx%10);
104 idx /= 10;
107 return ptr+1;
110 static HRESULT Array_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
112 TRACE("%p\n", jsthis);
114 *r = jsval_number(array_from_jsdisp(jsthis)->length);
115 return S_OK;
118 static HRESULT Array_set_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
120 ArrayInstance *This = array_from_jsdisp(jsthis);
121 DOUBLE len = -1;
122 DWORD i;
123 HRESULT hres;
125 TRACE("%p %d\n", This, This->length);
127 hres = to_number(ctx, value, &len);
128 if(FAILED(hres))
129 return hres;
131 len = floor(len);
132 if(len!=(DWORD)len)
133 return JS_E_INVALID_LENGTH;
135 for(i=len; i < This->length; i++) {
136 hres = jsdisp_delete_idx(&This->dispex, i);
137 if(FAILED(hres))
138 return hres;
141 This->length = len;
142 return S_OK;
145 static HRESULT concat_array(jsdisp_t *array, ArrayInstance *obj, DWORD *len)
147 jsval_t val;
148 DWORD i;
149 HRESULT hres;
151 for(i=0; i < obj->length; i++) {
152 hres = jsdisp_get_idx(&obj->dispex, i, &val);
153 if(hres == DISP_E_UNKNOWNNAME)
154 continue;
155 if(FAILED(hres))
156 return hres;
158 hres = jsdisp_propput_idx(array, *len+i, val);
159 jsval_release(val);
160 if(FAILED(hres))
161 return hres;
164 *len += obj->length;
165 return S_OK;
168 static HRESULT concat_obj(jsdisp_t *array, IDispatch *obj, DWORD *len)
170 jsdisp_t *jsobj;
171 HRESULT hres;
173 jsobj = iface_to_jsdisp(obj);
174 if(jsobj) {
175 if(is_class(jsobj, JSCLASS_ARRAY)) {
176 hres = concat_array(array, array_from_jsdisp(jsobj), len);
177 jsdisp_release(jsobj);
178 return hres;
180 jsdisp_release(jsobj);
183 return jsdisp_propput_idx(array, (*len)++, jsval_disp(obj));
186 static HRESULT Array_concat(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
187 jsval_t *r)
189 jsdisp_t *ret;
190 DWORD len = 0;
191 HRESULT hres;
193 TRACE("\n");
195 hres = create_array(ctx, 0, &ret);
196 if(FAILED(hres))
197 return hres;
199 hres = concat_obj(ret, jsthis->u.disp, &len);
200 if(SUCCEEDED(hres)) {
201 DWORD i;
203 for(i=0; i < argc; i++) {
204 if(is_object_instance(argv[i]))
205 hres = concat_obj(ret, get_object(argv[i]), &len);
206 else
207 hres = jsdisp_propput_idx(ret, len++, argv[i]);
208 if(FAILED(hres))
209 break;
213 if(FAILED(hres))
214 return hres;
216 if(r)
217 *r = jsval_obj(ret);
218 else
219 jsdisp_release(ret);
220 return S_OK;
223 static HRESULT array_join(script_ctx_t *ctx, jsdisp_t *array, DWORD length, const WCHAR *sep,
224 unsigned seplen, jsval_t *r)
226 jsstr_t **str_tab, *ret = NULL;
227 jsval_t val;
228 DWORD i;
229 HRESULT hres = E_FAIL;
231 if(!length) {
232 if(r)
233 *r = jsval_string(jsstr_empty());
234 return S_OK;
237 str_tab = heap_alloc_zero(length * sizeof(*str_tab));
238 if(!str_tab)
239 return E_OUTOFMEMORY;
241 for(i=0; i < length; i++) {
242 hres = jsdisp_get_idx(array, i, &val);
243 if(hres == DISP_E_UNKNOWNNAME) {
244 hres = S_OK;
245 continue;
246 } else if(FAILED(hres))
247 break;
249 if(!is_undefined(val) && !is_null(val)) {
250 hres = to_string(ctx, val, str_tab+i);
251 jsval_release(val);
252 if(FAILED(hres))
253 break;
257 if(SUCCEEDED(hres)) {
258 DWORD len = 0;
260 if(str_tab[0])
261 len = jsstr_length(str_tab[0]);
262 for(i=1; i < length; i++) {
263 len += seplen;
264 if(str_tab[i])
265 len += jsstr_length(str_tab[i]);
266 if(len > JSSTR_MAX_LENGTH) {
267 hres = E_OUTOFMEMORY;
268 break;
272 if(SUCCEEDED(hres)) {
273 WCHAR *ptr = NULL;
275 ret = jsstr_alloc_buf(len, &ptr);
276 if(ret) {
277 if(str_tab[0])
278 ptr += jsstr_flush(str_tab[0], ptr);
280 for(i=1; i < length; i++) {
281 if(seplen) {
282 memcpy(ptr, sep, seplen*sizeof(WCHAR));
283 ptr += seplen;
286 if(str_tab[i])
287 ptr += jsstr_flush(str_tab[i], ptr);
289 }else {
290 hres = E_OUTOFMEMORY;
295 for(i=0; i < length; i++) {
296 if(str_tab[i])
297 jsstr_release(str_tab[i]);
299 heap_free(str_tab);
300 if(FAILED(hres))
301 return hres;
303 TRACE("= %s\n", debugstr_jsstr(ret));
305 if(r)
306 *r = jsval_string(ret);
307 else
308 jsstr_release(ret);
309 return S_OK;
312 /* ECMA-262 3rd Edition 15.4.4.5 */
313 static HRESULT Array_join(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
314 jsval_t *r)
316 jsdisp_t *jsthis;
317 DWORD length;
318 HRESULT hres;
320 TRACE("\n");
322 hres = get_length(ctx, vthis, &jsthis, &length);
323 if(FAILED(hres))
324 return hres;
326 if(argc) {
327 const WCHAR *sep;
328 jsstr_t *sep_str;
330 hres = to_flat_string(ctx, argv[0], &sep_str, &sep);
331 if(FAILED(hres))
332 return hres;
334 hres = array_join(ctx, jsthis, length, sep, jsstr_length(sep_str), r);
336 jsstr_release(sep_str);
337 }else {
338 hres = array_join(ctx, jsthis, length, L",", 1, r);
341 return hres;
344 static HRESULT Array_pop(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
345 jsval_t *r)
347 jsdisp_t *jsthis;
348 jsval_t val;
349 DWORD length;
350 HRESULT hres;
352 TRACE("\n");
354 hres = get_length(ctx, vthis, &jsthis, &length);
355 if(FAILED(hres))
356 return hres;
358 if(!length) {
359 hres = set_length(jsthis, 0);
360 if(FAILED(hres))
361 return hres;
363 if(r)
364 *r = jsval_undefined();
365 return S_OK;
368 length--;
369 hres = jsdisp_get_idx(jsthis, length, &val);
370 if(SUCCEEDED(hres))
371 hres = jsdisp_delete_idx(jsthis, length);
372 else if(hres == DISP_E_UNKNOWNNAME) {
373 val = jsval_undefined();
374 hres = S_OK;
375 }else
376 return hres;
378 if(SUCCEEDED(hres))
379 hres = set_length(jsthis, length);
381 if(FAILED(hres)) {
382 jsval_release(val);
383 return hres;
386 if(r)
387 *r = val;
388 else
389 jsval_release(val);
390 return hres;
393 /* ECMA-262 3rd Edition 15.4.4.7 */
394 static HRESULT Array_push(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
395 jsval_t *r)
397 jsdisp_t *jsthis;
398 DWORD length = 0;
399 unsigned i;
400 HRESULT hres;
402 TRACE("\n");
404 hres = get_length(ctx, vthis, &jsthis, &length);
405 if(FAILED(hres))
406 return hres;
408 for(i=0; i < argc; i++) {
409 hres = jsdisp_propput_idx(jsthis, length+i, argv[i]);
410 if(FAILED(hres))
411 return hres;
414 hres = set_length(jsthis, length+argc);
415 if(FAILED(hres))
416 return hres;
418 if(r)
419 *r = jsval_number(length+argc);
420 return S_OK;
423 static HRESULT Array_reverse(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
424 jsval_t *r)
426 jsdisp_t *jsthis;
427 DWORD length, k, l;
428 jsval_t v1, v2;
429 HRESULT hres1, hres2;
431 TRACE("\n");
433 hres1 = get_length(ctx, vthis, &jsthis, &length);
434 if(FAILED(hres1))
435 return hres1;
437 for(k=0; k<length/2; k++) {
438 l = length-k-1;
440 hres1 = jsdisp_get_idx(jsthis, k, &v1);
441 if(FAILED(hres1) && hres1!=DISP_E_UNKNOWNNAME)
442 return hres1;
444 hres2 = jsdisp_get_idx(jsthis, l, &v2);
445 if(FAILED(hres2) && hres2!=DISP_E_UNKNOWNNAME) {
446 jsval_release(v1);
447 return hres2;
450 if(hres1 == DISP_E_UNKNOWNNAME)
451 hres1 = jsdisp_delete_idx(jsthis, l);
452 else
453 hres1 = jsdisp_propput_idx(jsthis, l, v1);
455 if(FAILED(hres1)) {
456 jsval_release(v1);
457 jsval_release(v2);
458 return hres1;
461 if(hres2 == DISP_E_UNKNOWNNAME)
462 hres2 = jsdisp_delete_idx(jsthis, k);
463 else
464 hres2 = jsdisp_propput_idx(jsthis, k, v2);
466 if(FAILED(hres2)) {
467 jsval_release(v2);
468 return hres2;
472 if(r)
473 *r = jsval_obj(jsdisp_addref(jsthis));
474 return S_OK;
477 /* ECMA-262 3rd Edition 15.4.4.9 */
478 static HRESULT Array_shift(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
479 jsval_t *r)
481 jsdisp_t *jsthis;
482 DWORD length = 0, i;
483 jsval_t v, ret;
484 HRESULT hres;
486 TRACE("\n");
488 hres = get_length(ctx, vthis, &jsthis, &length);
489 if(FAILED(hres))
490 return hres;
492 if(!length) {
493 hres = set_length(jsthis, 0);
494 if(FAILED(hres))
495 return hres;
497 if(r)
498 *r = jsval_undefined();
499 return S_OK;
502 hres = jsdisp_get_idx(jsthis, 0, &ret);
503 if(hres == DISP_E_UNKNOWNNAME) {
504 ret = jsval_undefined();
505 hres = S_OK;
508 for(i=1; SUCCEEDED(hres) && i<length; i++) {
509 hres = jsdisp_get_idx(jsthis, i, &v);
510 if(hres == DISP_E_UNKNOWNNAME)
511 hres = jsdisp_delete_idx(jsthis, i-1);
512 else if(SUCCEEDED(hres))
513 hres = jsdisp_propput_idx(jsthis, i-1, v);
516 if(SUCCEEDED(hres)) {
517 hres = jsdisp_delete_idx(jsthis, length-1);
518 if(SUCCEEDED(hres))
519 hres = set_length(jsthis, length-1);
522 if(FAILED(hres))
523 return hres;
525 if(r)
526 *r = ret;
527 else
528 jsval_release(ret);
529 return hres;
532 /* ECMA-262 3rd Edition 15.4.4.10 */
533 static HRESULT Array_slice(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
535 jsdisp_t *arr, *jsthis;
536 DOUBLE range;
537 DWORD length, start, end, idx;
538 HRESULT hres;
540 TRACE("\n");
542 hres = get_length(ctx, vthis, &jsthis, &length);
543 if(FAILED(hres))
544 return hres;
546 if(argc) {
547 hres = to_number(ctx, argv[0], &range);
548 if(FAILED(hres))
549 return hres;
551 range = floor(range);
552 if(-range>length || isnan(range)) start = 0;
553 else if(range < 0) start = range+length;
554 else if(range <= length) start = range;
555 else start = length;
557 else start = 0;
559 if(argc > 1) {
560 hres = to_number(ctx, argv[1], &range);
561 if(FAILED(hres))
562 return hres;
564 range = floor(range);
565 if(-range>length) end = 0;
566 else if(range < 0) end = range+length;
567 else if(range <= length) end = range;
568 else end = length;
570 else end = length;
572 hres = create_array(ctx, (end>start)?end-start:0, &arr);
573 if(FAILED(hres))
574 return hres;
576 for(idx=start; idx<end; idx++) {
577 jsval_t v;
579 hres = jsdisp_get_idx(jsthis, idx, &v);
580 if(hres == DISP_E_UNKNOWNNAME)
581 continue;
583 if(SUCCEEDED(hres)) {
584 hres = jsdisp_propput_idx(arr, idx-start, v);
585 jsval_release(v);
588 if(FAILED(hres)) {
589 jsdisp_release(arr);
590 return hres;
594 if(r)
595 *r = jsval_obj(arr);
596 else
597 jsdisp_release(arr);
599 return S_OK;
602 static HRESULT sort_cmp(script_ctx_t *ctx, jsdisp_t *cmp_func, jsval_t v1, jsval_t v2, INT *cmp)
604 HRESULT hres;
606 if(cmp_func) {
607 jsval_t args[2] = {v1, v2};
608 jsval_t res;
609 double n;
611 hres = jsdisp_call_value(cmp_func, NULL, DISPATCH_METHOD, 2, args, &res);
612 if(FAILED(hres))
613 return hres;
615 hres = to_number(ctx, res, &n);
616 jsval_release(res);
617 if(FAILED(hres))
618 return hres;
620 if(n == 0)
621 *cmp = 0;
622 *cmp = n > 0.0 ? 1 : -1;
623 }else if(is_undefined(v1)) {
624 *cmp = is_undefined(v2) ? 0 : 1;
625 }else if(is_undefined(v2)) {
626 *cmp = -1;
627 }else if(is_number(v1) && is_number(v2)) {
628 double d = get_number(v1)-get_number(v2);
629 if(d > 0.0)
630 *cmp = 1;
631 else
632 *cmp = d < -0.0 ? -1 : 0;
633 }else {
634 jsstr_t *x, *y;
636 hres = to_string(ctx, v1, &x);
637 if(FAILED(hres))
638 return hres;
640 hres = to_string(ctx, v2, &y);
641 if(SUCCEEDED(hres)) {
642 *cmp = jsstr_cmp(x, y);
643 jsstr_release(y);
645 jsstr_release(x);
646 if(FAILED(hres))
647 return hres;
650 return S_OK;
653 /* ECMA-262 3rd Edition 15.4.4.11 */
654 static HRESULT Array_sort(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
655 jsval_t *r)
657 jsdisp_t *jsthis, *cmp_func = NULL;
658 jsval_t *vtab, **sorttab = NULL;
659 DWORD length;
660 DWORD i;
661 HRESULT hres = S_OK;
663 TRACE("\n");
665 hres = get_length(ctx, vthis, &jsthis, &length);
666 if(FAILED(hres))
667 return hres;
669 if(argc >= 1) {
670 if(is_object_instance(argv[0])) {
671 if(argc > 1 && ctx->version < SCRIPTLANGUAGEVERSION_ES5) {
672 WARN("invalid arg_cnt %d\n", argc);
673 return JS_E_JSCRIPT_EXPECTED;
675 cmp_func = iface_to_jsdisp(get_object(argv[0]));
676 if(!cmp_func || !is_class(cmp_func, JSCLASS_FUNCTION)) {
677 WARN("cmp_func is not a function\n");
678 if(cmp_func)
679 jsdisp_release(cmp_func);
680 return JS_E_JSCRIPT_EXPECTED;
682 }else if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5 ? !is_undefined(argv[0]) : !is_null(argv[0])) {
683 WARN("invalid arg %s\n", debugstr_jsval(argv[0]));
684 return JS_E_JSCRIPT_EXPECTED;
688 if(!length) {
689 if(cmp_func)
690 jsdisp_release(cmp_func);
691 if(r)
692 *r = jsval_obj(jsdisp_addref(jsthis));
693 return S_OK;
696 vtab = heap_alloc_zero(length * sizeof(*vtab));
697 if(vtab) {
698 for(i=0; i<length; i++) {
699 hres = jsdisp_get_idx(jsthis, i, vtab+i);
700 if(hres == DISP_E_UNKNOWNNAME) {
701 vtab[i] = jsval_undefined();
702 hres = S_OK;
703 } else if(FAILED(hres)) {
704 WARN("Could not get elem %d: %08x\n", i, hres);
705 break;
708 }else {
709 hres = E_OUTOFMEMORY;
712 if(SUCCEEDED(hres)) {
713 sorttab = heap_alloc(length*2*sizeof(*sorttab));
714 if(!sorttab)
715 hres = E_OUTOFMEMORY;
718 /* merge-sort */
719 if(SUCCEEDED(hres)) {
720 jsval_t *tmpv, **tmpbuf;
721 INT cmp;
723 tmpbuf = sorttab + length;
724 for(i=0; i < length; i++)
725 sorttab[i] = vtab+i;
727 for(i=0; i < length/2; i++) {
728 hres = sort_cmp(ctx, cmp_func, *sorttab[2*i+1], *sorttab[2*i], &cmp);
729 if(FAILED(hres))
730 break;
732 if(cmp < 0) {
733 tmpv = sorttab[2*i];
734 sorttab[2*i] = sorttab[2*i+1];
735 sorttab[2*i+1] = tmpv;
739 if(SUCCEEDED(hres)) {
740 DWORD k, a, b, bend;
742 for(k=2; k < length; k *= 2) {
743 for(i=0; i+k < length; i += 2*k) {
744 a = b = 0;
745 if(i+2*k <= length)
746 bend = k;
747 else
748 bend = length - (i+k);
750 memcpy(tmpbuf, sorttab+i, k*sizeof(jsval_t*));
752 while(a < k && b < bend) {
753 hres = sort_cmp(ctx, cmp_func, *tmpbuf[a], *sorttab[i+k+b], &cmp);
754 if(FAILED(hres))
755 break;
757 if(cmp < 0) {
758 sorttab[i+a+b] = tmpbuf[a];
759 a++;
760 }else {
761 sorttab[i+a+b] = sorttab[i+k+b];
762 b++;
766 if(FAILED(hres))
767 break;
769 if(a < k)
770 memcpy(sorttab+i+a+b, tmpbuf+a, (k-a)*sizeof(jsval_t*));
773 if(FAILED(hres))
774 break;
778 for(i=0; SUCCEEDED(hres) && i < length; i++)
779 hres = jsdisp_propput_idx(jsthis, i, *sorttab[i]);
782 if(vtab) {
783 for(i=0; i < length; i++)
784 jsval_release(vtab[i]);
785 heap_free(vtab);
787 heap_free(sorttab);
788 if(cmp_func)
789 jsdisp_release(cmp_func);
791 if(FAILED(hres))
792 return hres;
794 if(r)
795 *r = jsval_obj(jsdisp_addref(jsthis));
796 return S_OK;
799 /* ECMA-262 3rd Edition 15.4.4.12 */
800 static HRESULT Array_splice(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
801 jsval_t *r)
803 DWORD length, start=0, delete_cnt=0, i, add_args = 0;
804 jsdisp_t *ret_array = NULL, *jsthis;
805 jsval_t val;
806 double d;
807 int n;
808 HRESULT hres = S_OK;
810 TRACE("\n");
812 hres = get_length(ctx, vthis, &jsthis, &length);
813 if(FAILED(hres))
814 return hres;
816 if(argc) {
817 hres = to_integer(ctx, argv[0], &d);
818 if(FAILED(hres))
819 return hres;
821 if(is_int32(d)) {
822 if((n = d) >= 0)
823 start = min(n, length);
824 else
825 start = -n > length ? 0 : length + n;
826 }else {
827 start = d < 0.0 ? 0 : length;
831 if(argc >= 2) {
832 hres = to_integer(ctx, argv[1], &d);
833 if(FAILED(hres))
834 return hres;
836 if(is_int32(d)) {
837 if((n = d) > 0)
838 delete_cnt = min(n, length-start);
839 }else if(d > 0.0) {
840 delete_cnt = length-start;
843 add_args = argc-2;
846 if(r) {
847 hres = create_array(ctx, 0, &ret_array);
848 if(FAILED(hres))
849 return hres;
851 for(i=0; SUCCEEDED(hres) && i < delete_cnt; i++) {
852 hres = jsdisp_get_idx(jsthis, start+i, &val);
853 if(hres == DISP_E_UNKNOWNNAME) {
854 hres = S_OK;
855 }else if(SUCCEEDED(hres)) {
856 hres = jsdisp_propput_idx(ret_array, i, val);
857 jsval_release(val);
861 if(SUCCEEDED(hres))
862 hres = jsdisp_propput_name(ret_array, L"length", jsval_number(delete_cnt));
865 if(add_args < delete_cnt) {
866 for(i = start; SUCCEEDED(hres) && i < length-delete_cnt; i++) {
867 hres = jsdisp_get_idx(jsthis, i+delete_cnt, &val);
868 if(hres == DISP_E_UNKNOWNNAME) {
869 hres = jsdisp_delete_idx(jsthis, i+add_args);
870 }else if(SUCCEEDED(hres)) {
871 hres = jsdisp_propput_idx(jsthis, i+add_args, val);
872 jsval_release(val);
876 for(i=length; SUCCEEDED(hres) && i != length-delete_cnt+add_args; i--)
877 hres = jsdisp_delete_idx(jsthis, i-1);
878 }else if(add_args > delete_cnt) {
879 for(i=length-delete_cnt; SUCCEEDED(hres) && i != start; i--) {
880 hres = jsdisp_get_idx(jsthis, i+delete_cnt-1, &val);
881 if(hres == DISP_E_UNKNOWNNAME) {
882 hres = jsdisp_delete_idx(jsthis, i+add_args-1);
883 }else if(SUCCEEDED(hres)) {
884 hres = jsdisp_propput_idx(jsthis, i+add_args-1, val);
885 jsval_release(val);
890 for(i=0; SUCCEEDED(hres) && i < add_args; i++)
891 hres = jsdisp_propput_idx(jsthis, start+i, argv[i+2]);
893 if(SUCCEEDED(hres))
894 hres = jsdisp_propput_name(jsthis, L"length", jsval_number(length-delete_cnt+add_args));
896 if(FAILED(hres)) {
897 if(ret_array)
898 jsdisp_release(ret_array);
899 return hres;
902 if(r)
903 *r = jsval_obj(ret_array);
904 return S_OK;
907 /* ECMA-262 3rd Edition 15.4.4.2 */
908 static HRESULT Array_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
909 jsval_t *r)
911 ArrayInstance *array;
913 TRACE("\n");
915 array = array_this(jsthis);
916 if(!array)
917 return JS_E_ARRAY_EXPECTED;
919 return array_join(ctx, &array->dispex, array->length, L",", 1, r);
922 static HRESULT Array_toLocaleString(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
923 jsval_t *r)
925 FIXME("\n");
926 return E_NOTIMPL;
929 static HRESULT Array_forEach(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
930 jsval_t *r)
932 IDispatch *context_obj = NULL, *callback;
933 jsval_t value, args[3], res;
934 jsdisp_t *jsthis;
935 unsigned length, i;
936 HRESULT hres;
938 TRACE("\n");
940 hres = get_length(ctx, vthis, &jsthis, &length);
941 if(FAILED(hres))
942 return hres;
944 /* Fixme check IsCallable */
945 if(!argc || !is_object_instance(argv[0]) || !get_object(argv[0])) {
946 FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined()));
947 return E_INVALIDARG;
949 callback = get_object(argv[0]);
951 if(argc > 1 && !is_undefined(argv[1])) {
952 if(!is_object_instance(argv[1]) || !get_object(argv[1])) {
953 FIXME("Unsupported context this %s\n", debugstr_jsval(argv[1]));
954 return E_NOTIMPL;
956 context_obj = get_object(argv[1]);
959 for(i = 0; i < length; i++) {
960 hres = jsdisp_get_idx(jsthis, i, &value);
961 if(hres == DISP_E_UNKNOWNNAME)
962 continue;
963 if(FAILED(hres))
964 return hres;
966 args[0] = value;
967 args[1] = jsval_number(i);
968 args[2] = jsval_obj(jsthis);
969 hres = disp_call_value(ctx, callback, context_obj, DISPATCH_METHOD, ARRAY_SIZE(args), args, &res);
970 jsval_release(value);
971 if(FAILED(hres))
972 return hres;
973 jsval_release(res);
976 if(r) *r = jsval_undefined();
977 return S_OK;
980 static HRESULT Array_indexOf(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
981 jsval_t *r)
983 jsdisp_t *jsthis;
984 unsigned length, i, from = 0;
985 jsval_t search, value;
986 BOOL eq;
987 HRESULT hres;
989 TRACE("\n");
991 hres = get_length(ctx, vthis, &jsthis, &length);
992 if(FAILED(hres))
993 return hres;
994 if(!length) {
995 if(r) *r = jsval_number(-1);
996 return S_OK;
999 search = argc ? argv[0] : jsval_undefined();
1001 if(argc > 1) {
1002 double from_arg;
1004 hres = to_integer(ctx, argv[1], &from_arg);
1005 if(FAILED(hres))
1006 return hres;
1008 if(from_arg >= 0)
1009 from = min(from_arg, length);
1010 else
1011 from = max(from_arg + length, 0);
1014 for(i = from; i < length; i++) {
1015 hres = jsdisp_get_idx(jsthis, i, &value);
1016 if(hres == DISP_E_UNKNOWNNAME)
1017 continue;
1018 if(FAILED(hres))
1019 return hres;
1021 hres = jsval_strict_equal(value, search, &eq);
1022 jsval_release(value);
1023 if(FAILED(hres))
1024 return hres;
1025 if(eq) {
1026 if(r) *r = jsval_number(i);
1027 return S_OK;
1031 if(r) *r = jsval_number(-1);
1032 return S_OK;
1035 static HRESULT Array_map(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1037 IDispatch *context_this = NULL, *callback;
1038 jsval_t callback_args[3], mapped_value;
1039 jsdisp_t *jsthis, *array;
1040 DWORD length, k;
1041 HRESULT hres;
1043 TRACE("\n");
1045 hres = get_length(ctx, vthis, &jsthis, &length);
1046 if(FAILED(hres)) {
1047 FIXME("Could not get length\n");
1048 return hres;
1051 /* FIXME: check IsCallable */
1052 if(!argc || !is_object_instance(argv[0]) || !get_object(argv[0])) {
1053 FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined()));
1054 return E_INVALIDARG;
1056 callback = get_object(argv[0]);
1058 if(argc > 1) {
1059 if(is_object_instance(argv[1]) && get_object(argv[1])) {
1060 context_this = get_object(argv[1]);
1061 }else if(!is_undefined(argv[1])) {
1062 FIXME("Unsupported context this %s\n", debugstr_jsval(argv[1]));
1063 return E_NOTIMPL;
1067 hres = create_array(ctx, length, &array);
1068 if(FAILED(hres))
1069 return hres;
1071 for(k = 0; k < length; k++) {
1072 hres = jsdisp_get_idx(jsthis, k, &callback_args[0]);
1073 if(hres == DISP_E_UNKNOWNNAME)
1074 continue;
1075 if(FAILED(hres))
1076 break;
1078 callback_args[1] = jsval_number(k);
1079 callback_args[2] = jsval_obj(jsthis);
1080 hres = disp_call_value(ctx, callback, context_this, DISPATCH_METHOD, 3, callback_args, &mapped_value);
1081 jsval_release(callback_args[0]);
1082 if(FAILED(hres))
1083 break;
1085 hres = jsdisp_propput_idx(array, k, mapped_value);
1086 if(FAILED(hres))
1087 break;
1090 if(SUCCEEDED(hres) && r)
1091 *r = jsval_obj(array);
1092 else
1093 jsdisp_release(array);
1094 return hres;
1097 static HRESULT Array_reduce(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1099 IDispatch *context_this = NULL, *callback;
1100 jsval_t callback_args[4], acc, new_acc;
1101 BOOL have_value = FALSE;
1102 jsdisp_t *jsthis;
1103 DWORD length, k;
1104 HRESULT hres;
1106 TRACE("\n");
1108 hres = get_length(ctx, vthis, &jsthis, &length);
1109 if(FAILED(hres)) {
1110 FIXME("Could not get length\n");
1111 return hres;
1114 /* Fixme check IsCallable */
1115 if(!argc || !is_object_instance(argv[0]) || !get_object(argv[0])) {
1116 FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined()));
1117 return E_INVALIDARG;
1119 callback = get_object(argv[0]);
1121 if(argc > 1) {
1122 have_value = TRUE;
1123 hres = jsval_copy(argv[1], &acc);
1124 if(FAILED(hres))
1125 return hres;
1128 for(k = 0; k < length; k++) {
1129 hres = jsdisp_get_idx(jsthis, k, &callback_args[1]);
1130 if(hres == DISP_E_UNKNOWNNAME)
1131 continue;
1132 if(FAILED(hres))
1133 break;
1135 if(!have_value) {
1136 have_value = TRUE;
1137 acc = callback_args[1];
1138 continue;
1141 callback_args[0] = acc;
1142 callback_args[2] = jsval_number(k);
1143 callback_args[3] = jsval_obj(jsthis);
1144 hres = disp_call_value(ctx, callback, context_this, DISPATCH_METHOD, ARRAY_SIZE(callback_args), callback_args, &new_acc);
1145 jsval_release(callback_args[1]);
1146 if(FAILED(hres))
1147 break;
1149 jsval_release(acc);
1150 acc = new_acc;
1153 if(SUCCEEDED(hres) && !have_value) {
1154 WARN("No array element\n");
1155 hres = JS_E_INVALID_ACTION;
1158 if(SUCCEEDED(hres) && r)
1159 *r = acc;
1160 else if(have_value)
1161 jsval_release(acc);
1162 return hres;
1165 /* ECMA-262 3rd Edition 15.4.4.13 */
1166 static HRESULT Array_unshift(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
1167 jsval_t *r)
1169 jsdisp_t *jsthis;
1170 WCHAR buf[14], *buf_end, *str;
1171 DWORD i, length;
1172 jsval_t val;
1173 DISPID id;
1174 HRESULT hres;
1176 TRACE("\n");
1178 hres = get_length(ctx, vthis, &jsthis, &length);
1179 if(FAILED(hres))
1180 return hres;
1182 if(argc) {
1183 buf_end = buf + ARRAY_SIZE(buf)-1;
1184 *buf_end-- = 0;
1185 i = length;
1187 while(i--) {
1188 str = idx_to_str(i, buf_end);
1190 hres = jsdisp_get_id(jsthis, str, 0, &id);
1191 if(SUCCEEDED(hres)) {
1192 hres = jsdisp_propget(jsthis, id, &val);
1193 if(FAILED(hres))
1194 return hres;
1196 hres = jsdisp_propput_idx(jsthis, i+argc, val);
1197 jsval_release(val);
1198 }else if(hres == DISP_E_UNKNOWNNAME) {
1199 hres = IDispatchEx_DeleteMemberByDispID(vthis->u.dispex, id);
1203 if(FAILED(hres))
1204 return hres;
1207 for(i=0; i<argc; i++) {
1208 hres = jsdisp_propput_idx(jsthis, i, argv[i]);
1209 if(FAILED(hres))
1210 return hres;
1213 if(argc) {
1214 length += argc;
1215 hres = set_length(jsthis, length);
1216 if(FAILED(hres))
1217 return hres;
1220 if(r)
1221 *r = ctx->version < 2 ? jsval_undefined() : jsval_number(length);
1222 return S_OK;
1225 static HRESULT Array_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
1227 ArrayInstance *array = array_from_jsdisp(jsthis);
1229 TRACE("\n");
1231 return array_join(ctx, &array->dispex, array->length, L",", 1, r);
1234 static void Array_destructor(jsdisp_t *dispex)
1236 heap_free(dispex);
1239 static void Array_on_put(jsdisp_t *dispex, const WCHAR *name)
1241 ArrayInstance *array = array_from_jsdisp(dispex);
1242 const WCHAR *ptr = name;
1243 DWORD id = 0;
1245 if(!is_digit(*ptr))
1246 return;
1248 while(*ptr && is_digit(*ptr)) {
1249 id = id*10 + (*ptr-'0');
1250 ptr++;
1253 if(*ptr)
1254 return;
1256 if(id >= array->length)
1257 array->length = id+1;
1260 static const builtin_prop_t Array_props[] = {
1261 {L"concat", Array_concat, PROPF_METHOD|1},
1262 {L"forEach", Array_forEach, PROPF_METHOD|PROPF_ES5|1},
1263 {L"indexOf", Array_indexOf, PROPF_METHOD|PROPF_ES5|1},
1264 {L"join", Array_join, PROPF_METHOD|1},
1265 {L"length", NULL,0, Array_get_length, Array_set_length},
1266 {L"map", Array_map, PROPF_METHOD|PROPF_ES5|1},
1267 {L"pop", Array_pop, PROPF_METHOD},
1268 {L"push", Array_push, PROPF_METHOD|1},
1269 {L"reduce", Array_reduce, PROPF_METHOD|PROPF_ES5|1},
1270 {L"reverse", Array_reverse, PROPF_METHOD},
1271 {L"shift", Array_shift, PROPF_METHOD},
1272 {L"slice", Array_slice, PROPF_METHOD|2},
1273 {L"sort", Array_sort, PROPF_METHOD|1},
1274 {L"splice", Array_splice, PROPF_METHOD|2},
1275 {L"toLocaleString", Array_toLocaleString, PROPF_METHOD},
1276 {L"toString", Array_toString, PROPF_METHOD},
1277 {L"unshift", Array_unshift, PROPF_METHOD|1},
1280 static const builtin_info_t Array_info = {
1281 JSCLASS_ARRAY,
1282 {NULL, NULL,0, Array_get_value},
1283 ARRAY_SIZE(Array_props),
1284 Array_props,
1285 Array_destructor,
1286 Array_on_put
1289 static const builtin_prop_t ArrayInst_props[] = {
1290 {L"length", NULL,0, Array_get_length, Array_set_length}
1293 static const builtin_info_t ArrayInst_info = {
1294 JSCLASS_ARRAY,
1295 {NULL, NULL,0, Array_get_value},
1296 ARRAY_SIZE(ArrayInst_props),
1297 ArrayInst_props,
1298 Array_destructor,
1299 Array_on_put
1302 /* ECMA-262 5.1 Edition 15.4.3.2 */
1303 static HRESULT ArrayConstr_isArray(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1305 jsdisp_t *obj;
1307 TRACE("\n");
1309 if(!argc || !is_object_instance(argv[0])) {
1310 if(r) *r = jsval_bool(FALSE);
1311 return S_OK;
1314 obj = iface_to_jsdisp(get_object(argv[0]));
1315 if(r) *r = jsval_bool(obj && is_class(obj, JSCLASS_ARRAY));
1316 if(obj) jsdisp_release(obj);
1317 return S_OK;
1320 static HRESULT ArrayConstr_value(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
1321 jsval_t *r)
1323 jsdisp_t *obj;
1324 DWORD i;
1325 HRESULT hres;
1327 TRACE("\n");
1329 switch(flags) {
1330 case DISPATCH_METHOD:
1331 case DISPATCH_CONSTRUCT: {
1332 if(argc == 1 && is_number(argv[0])) {
1333 double n = get_number(argv[0]);
1335 if(n < 0 || !is_int32(n))
1336 return JS_E_INVALID_LENGTH;
1338 hres = create_array(ctx, n, &obj);
1339 if(FAILED(hres))
1340 return hres;
1342 *r = jsval_obj(obj);
1343 return S_OK;
1346 hres = create_array(ctx, argc, &obj);
1347 if(FAILED(hres))
1348 return hres;
1350 for(i=0; i < argc; i++) {
1351 hres = jsdisp_propput_idx(obj, i, argv[i]);
1352 if(FAILED(hres))
1353 break;
1355 if(FAILED(hres)) {
1356 jsdisp_release(obj);
1357 return hres;
1360 *r = jsval_obj(obj);
1361 break;
1363 default:
1364 FIXME("unimplemented flags: %x\n", flags);
1365 return E_NOTIMPL;
1368 return S_OK;
1371 static HRESULT alloc_array(script_ctx_t *ctx, jsdisp_t *object_prototype, ArrayInstance **ret)
1373 ArrayInstance *array;
1374 HRESULT hres;
1376 array = heap_alloc_zero(sizeof(ArrayInstance));
1377 if(!array)
1378 return E_OUTOFMEMORY;
1380 if(object_prototype)
1381 hres = init_dispex(&array->dispex, ctx, &Array_info, object_prototype);
1382 else
1383 hres = init_dispex_from_constr(&array->dispex, ctx, &ArrayInst_info, ctx->array_constr);
1385 if(FAILED(hres)) {
1386 heap_free(array);
1387 return hres;
1390 *ret = array;
1391 return S_OK;
1394 static const builtin_prop_t ArrayConstr_props[] = {
1395 {L"isArray", ArrayConstr_isArray, PROPF_ES5|PROPF_METHOD|1}
1398 static const builtin_info_t ArrayConstr_info = {
1399 JSCLASS_FUNCTION,
1400 DEFAULT_FUNCTION_VALUE,
1401 ARRAY_SIZE(ArrayConstr_props),
1402 ArrayConstr_props,
1403 NULL,
1404 NULL
1407 HRESULT create_array_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1409 ArrayInstance *array;
1410 HRESULT hres;
1412 hres = alloc_array(ctx, object_prototype, &array);
1413 if(FAILED(hres))
1414 return hres;
1416 hres = create_builtin_constructor(ctx, ArrayConstr_value, L"Array", &ArrayConstr_info, PROPF_CONSTR|1, &array->dispex, ret);
1418 jsdisp_release(&array->dispex);
1419 return hres;
1422 HRESULT create_array(script_ctx_t *ctx, DWORD length, jsdisp_t **ret)
1424 ArrayInstance *array;
1425 HRESULT hres;
1427 hres = alloc_array(ctx, NULL, &array);
1428 if(FAILED(hres))
1429 return hres;
1431 array->length = length;
1433 *ret = &array->dispex;
1434 return S_OK;