array.c 45.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * Copyright 2008 Jacek Caban for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

19

20
#include <math.h>
21
#include <assert.h>
22

23 24 25 26 27 28 29
#include "jscript.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(jscript);

typedef struct {
30
    jsdisp_t dispex;
31 32

    DWORD length;
33 34
} ArrayInstance;

35 36 37 38 39
static inline ArrayInstance *array_from_jsdisp(jsdisp_t *jsdisp)
{
    return CONTAINING_RECORD(jsdisp, ArrayInstance, dispex);
}

40
static inline ArrayInstance *array_this(jsval_t vthis)
41
{
42 43
    jsdisp_t *jsdisp = is_object_instance(vthis) ? to_jsdisp(get_object(vthis)) : NULL;
    return (jsdisp && is_class(jsdisp, JSCLASS_ARRAY)) ? array_from_jsdisp(jsdisp) : NULL;
44 45
}

46 47 48 49 50 51
unsigned array_get_length(jsdisp_t *array)
{
    assert(is_class(array, JSCLASS_ARRAY));
    return array_from_jsdisp(array)->length;
}

52
static HRESULT get_length(script_ctx_t *ctx, jsval_t vthis, jsdisp_t **jsthis, UINT32 *ret)
53
{
54 55
    jsdisp_t *jsdisp;
    IDispatch *disp;
56
    jsval_t val;
57 58
    HRESULT hres;

59 60 61
    hres = to_object(ctx, vthis, &disp);
    if(FAILED(hres))
        return hres;
62

63 64 65
    jsdisp = iface_to_jsdisp(disp);
    IDispatch_Release(disp);
    if(!jsdisp)
66
        return JS_E_JSCRIPT_EXPECTED;
67
    *jsthis = jsdisp;
68

69 70 71 72
    if(is_class(jsdisp, JSCLASS_ARRAY)) {
        *ret = array_from_jsdisp(jsdisp)->length;
        return S_OK;
    }
73

74 75 76 77 78 79 80
    hres = jsdisp_propget_name(jsdisp, L"length", &val);
    if(SUCCEEDED(hres)) {
        hres = to_uint32(ctx, val, ret);
        jsval_release(val);
        if(SUCCEEDED(hres))
            return hres;
    }
81

82 83
    jsdisp_release(jsdisp);
    return hres;
84 85
}

86
static HRESULT set_length(jsdisp_t *obj, DWORD length)
87
{
88
    if(is_class(obj, JSCLASS_ARRAY)) {
89
        array_from_jsdisp(obj)->length = length;
90 91 92
        return S_OK;
    }

93
    return jsdisp_propput_name(obj, L"length", jsval_number(length));
94 95
}

96
WCHAR *idx_to_str(DWORD idx, WCHAR *ptr)
97 98 99 100 101 102 103 104 105 106 107 108 109 110
{
    if(!idx) {
        *ptr = '0';
        return ptr;
    }

    while(idx) {
        *ptr-- = '0' + (idx%10);
        idx /= 10;
    }

    return ptr+1;
}

111
static HRESULT Array_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
112
{
113
    TRACE("%p\n", jsthis);
114

115
    *r = jsval_number(array_from_jsdisp(jsthis)->length);
116 117
    return S_OK;
}
118

119
static HRESULT Array_set_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
120
{
121
    ArrayInstance *This = array_from_jsdisp(jsthis);
122 123 124
    DOUBLE len = -1;
    DWORD i;
    HRESULT hres;
125

126
    TRACE("%p %ld\n", This, This->length);
127

128 129 130
    hres = to_number(ctx, value, &len);
    if(FAILED(hres))
        return hres;
131

132 133
    len = floor(len);
    if(len!=(DWORD)len)
134
        return JS_E_INVALID_LENGTH;
135 136 137 138 139

    for(i=len; i < This->length; i++) {
        hres = jsdisp_delete_idx(&This->dispex, i);
        if(FAILED(hres))
            return hres;
140 141
    }

142
    This->length = len;
143
    return S_OK;
144 145
}

146
static HRESULT concat_array(jsdisp_t *array, ArrayInstance *obj, DWORD *len)
147
{
148
    jsval_t val;
149 150 151 152
    DWORD i;
    HRESULT hres;

    for(i=0; i < obj->length; i++) {
153
        hres = jsdisp_get_idx(&obj->dispex, i, &val);
154 155 156 157 158
        if(hres == DISP_E_UNKNOWNNAME)
            continue;
        if(FAILED(hres))
            return hres;

159
        hres = jsdisp_propput_idx(array, *len+i, val);
160
        jsval_release(val);
161 162 163 164 165 166 167 168
        if(FAILED(hres))
            return hres;
    }

    *len += obj->length;
    return S_OK;
}

169
static HRESULT concat_obj(jsdisp_t *array, IDispatch *obj, DWORD *len)
170
{
171
    jsdisp_t *jsobj;
172 173
    HRESULT hres;

174
    jsobj = iface_to_jsdisp(obj);
175 176
    if(jsobj) {
        if(is_class(jsobj, JSCLASS_ARRAY)) {
177
            hres = concat_array(array, array_from_jsdisp(jsobj), len);
178 179 180 181 182 183
            jsdisp_release(jsobj);
            return hres;
        }
        jsdisp_release(jsobj);
    }

184
    return jsdisp_propput_idx(array, (*len)++, jsval_disp(obj));
185 186
}

187
static HRESULT Array_concat(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
188
        jsval_t *r)
189
{
190
    IDispatch *jsthis;
191
    jsdisp_t *ret;
192 193 194 195 196
    DWORD len = 0;
    HRESULT hres;

    TRACE("\n");

197
    hres = to_object(ctx, vthis, &jsthis);
198 199 200
    if(FAILED(hres))
        return hres;

201 202 203 204 205
    hres = create_array(ctx, 0, &ret);
    if(FAILED(hres))
        goto done;

    hres = concat_obj(ret, jsthis, &len);
206 207 208
    if(SUCCEEDED(hres)) {
        DWORD i;

209
        for(i=0; i < argc; i++) {
210
            if(is_object_instance(argv[i]))
211
                hres = concat_obj(ret, get_object(argv[i]), &len);
212
            else
213
                hres = jsdisp_propput_idx(ret, len++, argv[i]);
214 215 216 217 218 219
            if(FAILED(hres))
                break;
        }
    }

    if(FAILED(hres))
220
        goto done;
221

222 223
    if(r)
        *r = jsval_obj(ret);
224
    else
225
        jsdisp_release(ret);
226 227
done:
    IDispatch_Release(jsthis);
228
    return S_OK;
229 230
}

231
static HRESULT array_join(script_ctx_t *ctx, jsdisp_t *array, DWORD length, const WCHAR *sep,
232
        unsigned seplen, HRESULT (*to_string)(script_ctx_t*,jsval_t,jsstr_t**), jsval_t *r)
233
{
234
    jsstr_t **str_tab, *ret = NULL;
235
    jsval_t val;
236 237 238 239
    DWORD i;
    HRESULT hres = E_FAIL;

    if(!length) {
240 241
        if(r)
            *r = jsval_string(jsstr_empty());
242 243 244
        return S_OK;
    }

245
    str_tab = heap_alloc_zero(length * sizeof(*str_tab));
246 247 248 249
    if(!str_tab)
        return E_OUTOFMEMORY;

    for(i=0; i < length; i++) {
250
        hres = jsdisp_get_idx(array, i, &val);
251 252 253 254
        if(hres == DISP_E_UNKNOWNNAME) {
            hres = S_OK;
            continue;
        } else if(FAILED(hres))
255 256
            break;

257
        if(!is_undefined(val) && !is_null(val)) {
258
            hres = to_string(ctx, val, str_tab+i);
259 260 261 262
            jsval_release(val);
            if(FAILED(hres))
                break;
        }
263 264 265
    }

    if(SUCCEEDED(hres)) {
266
        DWORD len = 0;
267 268

        if(str_tab[0])
269 270 271 272 273 274 275 276 277 278
            len = jsstr_length(str_tab[0]);
        for(i=1; i < length; i++) {
            len += seplen;
            if(str_tab[i])
                len += jsstr_length(str_tab[i]);
            if(len > JSSTR_MAX_LENGTH) {
                hres = E_OUTOFMEMORY;
                break;
            }
        }
279

280 281
        if(SUCCEEDED(hres)) {
            WCHAR *ptr = NULL;
282

283 284
            ret = jsstr_alloc_buf(len, &ptr);
            if(ret) {
285 286
                if(str_tab[0])
                    ptr += jsstr_flush(str_tab[0], ptr);
287

288 289 290 291 292
                for(i=1; i < length; i++) {
                    if(seplen) {
                        memcpy(ptr, sep, seplen*sizeof(WCHAR));
                        ptr += seplen;
                    }
293

294 295 296 297 298
                    if(str_tab[i])
                        ptr += jsstr_flush(str_tab[i], ptr);
                }
            }else {
                hres = E_OUTOFMEMORY;
299 300 301 302
            }
        }
    }

303 304 305 306
    for(i=0; i < length; i++) {
        if(str_tab[i])
            jsstr_release(str_tab[i]);
    }
307 308 309 310
    heap_free(str_tab);
    if(FAILED(hres))
        return hres;

311
    TRACE("= %s\n", debugstr_jsstr(ret));
312

313
    if(r)
314
        *r = jsval_string(ret);
315 316
    else
        jsstr_release(ret);
317 318 319 320
    return S_OK;
}

/* ECMA-262 3rd Edition    15.4.4.5 */
321
static HRESULT Array_join(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
322
        jsval_t *r)
323
{
324
    jsdisp_t *jsthis;
325
    UINT32 length;
326 327 328 329
    HRESULT hres;

    TRACE("\n");

330
    hres = get_length(ctx, vthis, &jsthis, &length);
331 332
    if(FAILED(hres))
        return hres;
333

334
    if(argc) {
335 336
        const WCHAR *sep;
        jsstr_t *sep_str;
337

338
        hres = to_flat_string(ctx, argv[0], &sep_str, &sep);
339
        if(FAILED(hres))
340
            goto done;
341

342
        hres = array_join(ctx, jsthis, length, sep, jsstr_length(sep_str), to_string, r);
343

344
        jsstr_release(sep_str);
345
    }else {
346
        hres = array_join(ctx, jsthis, length, L",", 1, to_string, r);
347 348
    }

349 350
done:
    jsdisp_release(jsthis);
351
    return hres;
352 353
}

354
static HRESULT Array_pop(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
355
        jsval_t *r)
356
{
357
    jsdisp_t *jsthis;
358
    jsval_t val;
359
    UINT32 length;
360 361 362 363
    HRESULT hres;

    TRACE("\n");

364
    hres = get_length(ctx, vthis, &jsthis, &length);
365 366
    if(FAILED(hres))
        return hres;
367 368

    if(!length) {
369
        hres = set_length(jsthis, 0);
370
        if(FAILED(hres))
371
            goto done;
372

373 374
        if(r)
            *r = jsval_undefined();
375
        goto done;
376 377
    }

378
    length--;
379
    hres = jsdisp_get_idx(jsthis, length, &val);
380
    if(SUCCEEDED(hres))
381
        hres = jsdisp_delete_idx(jsthis, length);
382
    else if(hres == DISP_E_UNKNOWNNAME) {
383
        val = jsval_undefined();
384 385
        hres = S_OK;
    }else
386
        goto done;
387

388
    if(SUCCEEDED(hres))
389
        hres = set_length(jsthis, length);
390 391

    if(FAILED(hres)) {
392
        jsval_release(val);
393
        goto done;
394 395
    }

396
    if(r)
397 398 399
        *r = val;
    else
        jsval_release(val);
400 401
done:
    jsdisp_release(jsthis);
402
    return hres;
403 404
}

405
/* ECMA-262 3rd Edition    15.4.4.7 */
406
static HRESULT Array_push(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
407
        jsval_t *r)
408
{
409
    jsdisp_t *jsthis;
410
    UINT32 length = 0;
411
    unsigned i;
412 413 414 415
    HRESULT hres;

    TRACE("\n");

416
    hres = get_length(ctx, vthis, &jsthis, &length);
417 418
    if(FAILED(hres))
        return hres;
419

420
    for(i=0; i < argc; i++) {
421
        hres = jsdisp_propput_idx(jsthis, length+i, argv[i]);
422
        if(FAILED(hres))
423
            goto done;
424 425
    }

426
    hres = set_length(jsthis, length+argc);
427
    if(FAILED(hres))
428
        goto done;
429

430 431
    if(r)
        *r = jsval_number(length+argc);
432 433 434
done:
    jsdisp_release(jsthis);
    return hres;
435 436
}

437
static HRESULT Array_reverse(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
438
        jsval_t *r)
439
{
440
    jsdisp_t *jsthis;
441
    UINT32 length, k, l;
442
    jsval_t v1, v2;
443 444 445 446
    HRESULT hres1, hres2;

    TRACE("\n");

447
    hres1 = get_length(ctx, vthis, &jsthis, &length);
448 449 450 451 452 453
    if(FAILED(hres1))
        return hres1;

    for(k=0; k<length/2; k++) {
        l = length-k-1;

454
        hres1 = jsdisp_get_idx(jsthis, k, &v1);
455
        if(FAILED(hres1) && hres1!=DISP_E_UNKNOWNNAME)
456
            goto done;
457

458
        hres2 = jsdisp_get_idx(jsthis, l, &v2);
459
        if(FAILED(hres2) && hres2!=DISP_E_UNKNOWNNAME) {
460
            jsval_release(v1);
461 462
            hres1 = hres2;
            goto done;
463
        }
464 465

        if(hres1 == DISP_E_UNKNOWNNAME)
466
            hres1 = jsdisp_delete_idx(jsthis, l);
467
        else
468
            hres1 = jsdisp_propput_idx(jsthis, l, v1);
469 470

        if(FAILED(hres1)) {
471 472
            jsval_release(v1);
            jsval_release(v2);
473
            goto done;
474
        }
475 476

        if(hres2 == DISP_E_UNKNOWNNAME)
477
            hres2 = jsdisp_delete_idx(jsthis, k);
478
        else
479
            hres2 = jsdisp_propput_idx(jsthis, k, v2);
480 481

        if(FAILED(hres2)) {
482
            jsval_release(v2);
483 484
            hres1 = hres2;
            goto done;
485
        }
486 487
    }

488 489
    if(r)
        *r = jsval_obj(jsdisp_addref(jsthis));
490 491 492
done:
    jsdisp_release(jsthis);
    return hres1;
493 494
}

495
/* ECMA-262 3rd Edition    15.4.4.9 */
496
static HRESULT Array_shift(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
497
        jsval_t *r)
498
{
499
    jsdisp_t *jsthis;
500
    UINT32 length = 0, i;
501
    jsval_t v, ret;
502 503 504 505
    HRESULT hres;

    TRACE("\n");

506
    hres = get_length(ctx, vthis, &jsthis, &length);
507 508 509 510
    if(FAILED(hres))
        return hres;

    if(!length) {
511
        hres = set_length(jsthis, 0);
512
        if(FAILED(hres))
513
            goto done;
514

515 516
        if(r)
            *r = jsval_undefined();
517
        goto done;
518 519
    }

520
    hres = jsdisp_get_idx(jsthis, 0, &ret);
521
    if(hres == DISP_E_UNKNOWNNAME) {
522
        ret = jsval_undefined();
523 524 525 526
        hres = S_OK;
    }

    for(i=1; SUCCEEDED(hres) && i<length; i++) {
527
        hres = jsdisp_get_idx(jsthis, i, &v);
528
        if(hres == DISP_E_UNKNOWNNAME)
529
            hres = jsdisp_delete_idx(jsthis, i-1);
530
        else if(SUCCEEDED(hres))
531
            hres = jsdisp_propput_idx(jsthis, i-1, v);
532 533 534
    }

    if(SUCCEEDED(hres)) {
535
        hres = jsdisp_delete_idx(jsthis, length-1);
536
        if(SUCCEEDED(hres))
537
            hres = set_length(jsthis, length-1);
538 539
    }

540
    if(FAILED(hres))
541
        goto done;
542 543 544 545 546

    if(r)
        *r = ret;
    else
        jsval_release(ret);
547 548
done:
    jsdisp_release(jsthis);
549
    return hres;
550 551
}

552
/* ECMA-262 3rd Edition    15.4.4.10 */
553
static HRESULT Array_slice(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
554
{
555
    jsdisp_t *arr, *jsthis;
556
    DOUBLE range;
557
    UINT32 length, start, end, idx;
558 559 560 561
    HRESULT hres;

    TRACE("\n");

562
    hres = get_length(ctx, vthis, &jsthis, &length);
563 564
    if(FAILED(hres))
        return hres;
565

566
    if(argc) {
567
        hres = to_number(ctx, argv[0], &range);
568
        if(FAILED(hres))
569
            goto done;
570

571
        range = floor(range);
572 573 574 575 576 577 578
        if(-range>length || isnan(range)) start = 0;
        else if(range < 0) start = range+length;
        else if(range <= length) start = range;
        else start = length;
    }
    else start = 0;

579
    if(argc > 1) {
580
        hres = to_number(ctx, argv[1], &range);
581
        if(FAILED(hres))
582
            goto done;
583

584
        range = floor(range);
585 586 587 588 589 590 591
        if(-range>length) end = 0;
        else if(range < 0) end = range+length;
        else if(range <= length) end = range;
        else end = length;
    }
    else end = length;

592
    hres = create_array(ctx, (end>start)?end-start:0, &arr);
593
    if(FAILED(hres))
594
        goto done;
595 596

    for(idx=start; idx<end; idx++) {
597
        jsval_t v;
598

599
        hres = jsdisp_get_idx(jsthis, idx, &v);
600 601 602
        if(hres == DISP_E_UNKNOWNNAME)
            continue;

603
        if(SUCCEEDED(hres)) {
604
            hres = jsdisp_propput_idx(arr, idx-start, v);
605
            jsval_release(v);
606
        }
607 608 609

        if(FAILED(hres)) {
            jsdisp_release(arr);
610
            goto done;
611 612 613
        }
    }

614 615
    if(r)
        *r = jsval_obj(arr);
616 617
    else
        jsdisp_release(arr);
618
    hres = S_OK;
619

620 621 622
done:
    jsdisp_release(jsthis);
    return hres;
623 624
}

625
static HRESULT sort_cmp(script_ctx_t *ctx, jsdisp_t *cmp_func, jsval_t v1, jsval_t v2, INT *cmp)
626 627 628 629
{
    HRESULT hres;

    if(cmp_func) {
630
        jsval_t args[2] = {v1, v2};
631
        jsval_t res;
632
        double n;
633

634
        hres = jsdisp_call_value(cmp_func, NULL, DISPATCH_METHOD, 2, args, &res);
635 636 637
        if(FAILED(hres))
            return hres;

638
        hres = to_number(ctx, res, &n);
639
        jsval_release(res);
640 641 642
        if(FAILED(hres))
            return hres;

643 644 645
        if(n == 0)
            *cmp = 0;
        *cmp = n > 0.0 ? 1 : -1;
646 647 648
    }else if(is_undefined(v1)) {
        *cmp = is_undefined(v2) ? 0 : 1;
    }else if(is_undefined(v2)) {
649
        *cmp = -1;
650 651
    }else if(is_number(v1) && is_number(v2)) {
        double d = get_number(v1)-get_number(v2);
652 653
        if(d > 0.0)
            *cmp = 1;
654
        else
655
            *cmp = d < -0.0 ? -1 : 0;
656
    }else {
657
        jsstr_t *x, *y;
658

659
        hres = to_string(ctx, v1, &x);
660 661 662
        if(FAILED(hres))
            return hres;

663
        hres = to_string(ctx, v2, &y);
664
        if(SUCCEEDED(hres)) {
665 666
            *cmp = jsstr_cmp(x, y);
            jsstr_release(y);
667
        }
668
        jsstr_release(x);
669 670
        if(FAILED(hres))
            return hres;
671 672 673 674 675 676
    }

    return S_OK;
}

/* ECMA-262 3rd Edition    15.4.4.11 */
677
static HRESULT Array_sort(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
678
        jsval_t *r)
679
{
680
    jsdisp_t *jsthis, *cmp_func = NULL;
681
    jsval_t *vtab, **sorttab = NULL;
682
    UINT32 length;
683 684 685 686 687
    DWORD i;
    HRESULT hres = S_OK;

    TRACE("\n");

688
    hres = get_length(ctx, vthis, &jsthis, &length);
689 690
    if(FAILED(hres))
        return hres;
691

692 693 694 695
    if(argc >= 1) {
        if(is_object_instance(argv[0])) {
            if(argc > 1 && ctx->version < SCRIPTLANGUAGEVERSION_ES5) {
                WARN("invalid arg_cnt %d\n", argc);
696 697
                hres = JS_E_JSCRIPT_EXPECTED;
                goto done;
698 699 700 701 702 703
            }
            cmp_func = iface_to_jsdisp(get_object(argv[0]));
            if(!cmp_func || !is_class(cmp_func, JSCLASS_FUNCTION)) {
                WARN("cmp_func is not a function\n");
                if(cmp_func)
                    jsdisp_release(cmp_func);
704 705
                hres = JS_E_JSCRIPT_EXPECTED;
                goto done;
706
            }
707 708
        }else if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5 ? !is_undefined(argv[0]) :
                 (!is_null(argv[0]) || is_null_disp(argv[0]))) {
709
            WARN("invalid arg %s\n", debugstr_jsval(argv[0]));
710 711
            hres = JS_E_JSCRIPT_EXPECTED;
            goto done;
712 713 714 715 716 717
        }
    }

    if(!length) {
        if(cmp_func)
            jsdisp_release(cmp_func);
718 719
        if(r)
            *r = jsval_obj(jsdisp_addref(jsthis));
720
        goto done;
721 722
    }

723
    vtab = heap_alloc_zero(length * sizeof(*vtab));
724 725
    if(vtab) {
        for(i=0; i<length; i++) {
726
            hres = jsdisp_get_idx(jsthis, i, vtab+i);
727
            if(hres == DISP_E_UNKNOWNNAME) {
728
                vtab[i] = jsval_undefined();
729 730
                hres = S_OK;
            } else if(FAILED(hres)) {
731
                WARN("Could not get elem %ld: %08lx\n", i, hres);
732 733 734 735 736 737 738 739
                break;
            }
        }
    }else {
        hres = E_OUTOFMEMORY;
    }

    if(SUCCEEDED(hres)) {
740
        sorttab = heap_alloc(length*2*sizeof(*sorttab));
741 742 743 744 745 746
        if(!sorttab)
            hres = E_OUTOFMEMORY;
    }

    /* merge-sort */
    if(SUCCEEDED(hres)) {
747
        jsval_t *tmpv, **tmpbuf;
748 749 750 751 752 753 754
        INT cmp;

        tmpbuf = sorttab + length;
        for(i=0; i < length; i++)
            sorttab[i] = vtab+i;

        for(i=0; i < length/2; i++) {
755
            hres = sort_cmp(ctx, cmp_func, *sorttab[2*i+1], *sorttab[2*i], &cmp);
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
            if(FAILED(hres))
                break;

            if(cmp < 0) {
                tmpv = sorttab[2*i];
                sorttab[2*i] = sorttab[2*i+1];
                sorttab[2*i+1] = tmpv;
            }
        }

        if(SUCCEEDED(hres)) {
            DWORD k, a, b, bend;

            for(k=2; k < length; k *= 2) {
                for(i=0; i+k < length; i += 2*k) {
                    a = b = 0;
                    if(i+2*k <= length)
                        bend = k;
                    else
                        bend = length - (i+k);

777
                    memcpy(tmpbuf, sorttab+i, k*sizeof(jsval_t*));
778 779

                    while(a < k && b < bend) {
780
                        hres = sort_cmp(ctx, cmp_func, *tmpbuf[a], *sorttab[i+k+b], &cmp);
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
                        if(FAILED(hres))
                            break;

                        if(cmp < 0) {
                            sorttab[i+a+b] = tmpbuf[a];
                            a++;
                        }else {
                            sorttab[i+a+b] = sorttab[i+k+b];
                            b++;
                        }
                    }

                    if(FAILED(hres))
                        break;

                    if(a < k)
797
                        memcpy(sorttab+i+a+b, tmpbuf+a, (k-a)*sizeof(jsval_t*));
798 799 800 801 802 803 804 805
                }

                if(FAILED(hres))
                    break;
            }
        }

        for(i=0; SUCCEEDED(hres) && i < length; i++)
806
            hres = jsdisp_propput_idx(jsthis, i, *sorttab[i]);
807 808 809 810
    }

    if(vtab) {
        for(i=0; i < length; i++)
811
            jsval_release(vtab[i]);
812 813 814 815 816 817 818
        heap_free(vtab);
    }
    heap_free(sorttab);
    if(cmp_func)
        jsdisp_release(cmp_func);

    if(FAILED(hres))
819
        goto done;
820

821 822
    if(r)
        *r = jsval_obj(jsdisp_addref(jsthis));
823 824 825
done:
    jsdisp_release(jsthis);
    return hres;
826 827
}

828
/* ECMA-262 3rd Edition    15.4.4.12 */
829
static HRESULT Array_splice(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
830
        jsval_t *r)
831
{
832
    UINT32 length, start=0, delete_cnt=0, i, add_args = 0;
833
    jsdisp_t *ret_array = NULL, *jsthis;
834
    jsval_t val;
835 836
    double d;
    int n;
837 838 839 840
    HRESULT hres = S_OK;

    TRACE("\n");

841
    hres = get_length(ctx, vthis, &jsthis, &length);
842 843
    if(FAILED(hres))
        return hres;
844

845
    if(argc) {
846
        hres = to_integer(ctx, argv[0], &d);
847
        if(FAILED(hres))
848
            goto done;
849

850 851 852
        if(is_int32(d)) {
            if((n = d) >= 0)
                start = min(n, length);
853
            else
854
                start = -n > length ? 0 : length + n;
855
        }else {
856
            start = d < 0.0 ? 0 : length;
857 858 859 860
        }
    }

    if(argc >= 2) {
861
        hres = to_integer(ctx, argv[1], &d);
862
        if(FAILED(hres))
863
            goto done;
864

865 866 867 868
        if(is_int32(d)) {
            if((n = d) > 0)
                delete_cnt = min(n, length-start);
        }else if(d > 0.0) {
869 870 871 872 873 874
            delete_cnt = length-start;
        }

        add_args = argc-2;
    }

875
    if(r) {
876
        hres = create_array(ctx, 0, &ret_array);
877
        if(FAILED(hres))
878
            goto done;
879 880

        for(i=0; SUCCEEDED(hres) && i < delete_cnt; i++) {
881
            hres = jsdisp_get_idx(jsthis, start+i, &val);
882
            if(hres == DISP_E_UNKNOWNNAME) {
883
                hres = S_OK;
884
            }else if(SUCCEEDED(hres)) {
885
                hres = jsdisp_propput_idx(ret_array, i, val);
886 887
                jsval_release(val);
            }
888 889
        }

890
        if(SUCCEEDED(hres))
891
            hres = jsdisp_propput_name(ret_array, L"length", jsval_number(delete_cnt));
892 893 894 895
    }

    if(add_args < delete_cnt) {
        for(i = start; SUCCEEDED(hres) && i < length-delete_cnt; i++) {
896
            hres = jsdisp_get_idx(jsthis, i+delete_cnt, &val);
897
            if(hres == DISP_E_UNKNOWNNAME) {
898
                hres = jsdisp_delete_idx(jsthis, i+add_args);
899
            }else if(SUCCEEDED(hres)) {
900
                hres = jsdisp_propput_idx(jsthis, i+add_args, val);
901 902
                jsval_release(val);
            }
903 904 905
        }

        for(i=length; SUCCEEDED(hres) && i != length-delete_cnt+add_args; i--)
906
            hres = jsdisp_delete_idx(jsthis, i-1);
907 908
    }else if(add_args > delete_cnt) {
        for(i=length-delete_cnt; SUCCEEDED(hres) && i != start; i--) {
909
            hres = jsdisp_get_idx(jsthis, i+delete_cnt-1, &val);
910
            if(hres == DISP_E_UNKNOWNNAME) {
911
                hres = jsdisp_delete_idx(jsthis, i+add_args-1);
912
            }else if(SUCCEEDED(hres)) {
913
                hres = jsdisp_propput_idx(jsthis, i+add_args-1, val);
914 915
                jsval_release(val);
            }
916 917 918 919
        }
    }

    for(i=0; SUCCEEDED(hres) && i < add_args; i++)
920
        hres = jsdisp_propput_idx(jsthis, start+i, argv[i+2]);
921

922
    if(SUCCEEDED(hres))
923
        hres = jsdisp_propput_name(jsthis, L"length", jsval_number(length-delete_cnt+add_args));
924 925 926 927

    if(FAILED(hres)) {
        if(ret_array)
            jsdisp_release(ret_array);
928
        goto done;
929 930
    }

931 932
    if(r)
        *r = jsval_obj(ret_array);
933 934 935
done:
    jsdisp_release(jsthis);
    return hres;
936 937
}

938
/* ECMA-262 3rd Edition    15.4.4.2 */
939
static HRESULT Array_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
940
        jsval_t *r)
941
{
942 943
    ArrayInstance *array;

944 945
    TRACE("\n");

946
    array = array_this(vthis);
947
    if(!array)
948
        return JS_E_ARRAY_EXPECTED;
949

950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
    return array_join(ctx, &array->dispex, array->length, L",", 1, to_string, r);
}

static HRESULT to_locale_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str)
{
    jsdisp_t *jsdisp;
    IDispatch *obj;
    HRESULT hres;

    switch(jsval_type(val)) {
    case JSV_OBJECT:
        hres = disp_call_name(ctx, get_object(val), L"toLocaleString", DISPATCH_METHOD, 0, NULL, &val);
        if(FAILED(hres)) {
            if(hres == JS_E_INVALID_PROPERTY && ctx->version >= SCRIPTLANGUAGEVERSION_ES5)
                hres = JS_E_FUNCTION_EXPECTED;
            return hres;
        }
        break;
    case JSV_NUMBER:
        if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5)
            return localize_number(ctx, get_number(val), FALSE, str);
        /* fall through */
    default:
        if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5)
            break;

        hres = to_object(ctx, val, &obj);
        if(FAILED(hres))
            return hres;

        jsdisp = as_jsdisp(obj);
        hres = jsdisp_call_name(jsdisp, L"toLocaleString", DISPATCH_METHOD, 0, NULL, &val);
        jsdisp_release(jsdisp);
        if(FAILED(hres))
            return hres;
        break;
    }

    return to_string(ctx, val, str);
989 990
}

991
static HRESULT Array_toLocaleString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
992
        jsval_t *r)
993
{
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
    jsdisp_t *jsthis;
    UINT32 length;
    HRESULT hres;
    WCHAR buf[5];
    int len;

    TRACE("\n");

    if(ctx->version < SCRIPTLANGUAGEVERSION_ES5) {
        ArrayInstance *array = array_this(vthis);
        if(!array)
            return JS_E_ARRAY_EXPECTED;
        jsthis = jsdisp_addref(&array->dispex);
        length = array->length;
    }else {
        hres = get_length(ctx, vthis, &jsthis, &length);
        if(FAILED(hres))
            return hres;
    }

    if(!(len = GetLocaleInfoW(ctx->lcid, LOCALE_SLIST, buf, ARRAY_SIZE(buf) - 1))) {
        buf[len++] = ',';
        len++;
    }
    buf[len - 1] = ' ';
    buf[len] = '\0';

    hres = array_join(ctx, jsthis, length, buf, len, to_locale_string, r);
    jsdisp_release(jsthis);
    return hres;
1024 1025
}

1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
static HRESULT Array_every(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
        jsval_t *r)
{
    IDispatch *context_obj = NULL, *callback;
    jsval_t value, args[3], res;
    BOOL boolval, ret = TRUE;
    unsigned length, i;
    jsdisp_t *jsthis;
    HRESULT hres;

    TRACE("\n");

    hres = get_length(ctx, vthis, &jsthis, &length);
    if(FAILED(hres))
        return hres;

    /* FIXME: check IsCallable */
    if(!argc || !is_object_instance(argv[0])) {
        FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined()));
        hres = E_INVALIDARG;
        goto done;
    }
    callback = get_object(argv[0]);

    if(argc > 1 && !is_undefined(argv[1])) {
        if(!is_object_instance(argv[1])) {
            FIXME("Unsupported context this %s\n", debugstr_jsval(argv[1]));
            hres = E_NOTIMPL;
            goto done;
        }
        context_obj = get_object(argv[1]);
    }

    for(i = 0; i < length; i++) {
        hres = jsdisp_get_idx(jsthis, i, &value);
        if(FAILED(hres)) {
            if(hres == DISP_E_UNKNOWNNAME)
                continue;
            goto done;
        }
        args[0] = value;
        args[1] = jsval_number(i);
        args[2] = jsval_obj(jsthis);
        hres = disp_call_value(ctx, callback, context_obj, DISPATCH_METHOD, ARRAY_SIZE(args), args, &res);
        jsval_release(value);
        if(FAILED(hres))
            goto done;

        hres = to_boolean(res, &boolval);
        jsval_release(res);
        if(FAILED(hres))
            goto done;
        if(!boolval) {
            ret = FALSE;
            break;
        }
    }

    if(r)
        *r = jsval_bool(ret);
    hres = S_OK;
done:
    jsdisp_release(jsthis);
    return hres;
}

1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
static HRESULT Array_filter(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
        jsval_t *r)
{
    IDispatch *context_obj = NULL, *callback;
    jsval_t value, args[3], res;
    unsigned length, i, j = 0;
    jsdisp_t *jsthis, *arr;
    HRESULT hres;
    BOOL boolval;

    TRACE("\n");

    hres = get_length(ctx, vthis, &jsthis, &length);
    if(FAILED(hres))
        return hres;

    /* FIXME: check IsCallable */
    if(!argc || !is_object_instance(argv[0])) {
        FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined()));
        hres = E_INVALIDARG;
        goto done;
    }
    callback = get_object(argv[0]);

    if(argc > 1 && !is_undefined(argv[1])) {
        if(!is_object_instance(argv[1])) {
            FIXME("Unsupported context this %s\n", debugstr_jsval(argv[1]));
            hres = E_NOTIMPL;
            goto done;
        }
        context_obj = get_object(argv[1]);
    }

    hres = create_array(ctx, 0, &arr);
    if(FAILED(hres))
        goto done;

    for(i = 0; i < length; i++) {
        hres = jsdisp_get_idx(jsthis, i, &value);
        if(FAILED(hres)) {
            if(hres == DISP_E_UNKNOWNNAME) {
                hres = S_OK;
                continue;
            }
            break;
        }
        args[0] = value;
        args[1] = jsval_number(i);
        args[2] = jsval_obj(jsthis);
        hres = disp_call_value(ctx, callback, context_obj, DISPATCH_METHOD, ARRAY_SIZE(args), args, &res);
        if(SUCCEEDED(hres)) {
            hres = to_boolean(res, &boolval);
            jsval_release(res);
            if(SUCCEEDED(hres) && boolval)
                hres = jsdisp_propput_idx(arr, j++, value);
        }
        jsval_release(value);
        if(FAILED(hres))
            break;
    }

    if(FAILED(hres)) {
        jsdisp_release(arr);
        goto done;
    }
    set_length(arr, j);

    if(r)
        *r = jsval_obj(arr);
done:
    jsdisp_release(jsthis);
    return hres;
}

1166
static HRESULT Array_forEach(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1167 1168
        jsval_t *r)
{
1169
    IDispatch *context_obj = NULL, *callback;
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
    jsval_t value, args[3], res;
    jsdisp_t *jsthis;
    unsigned length, i;
    HRESULT hres;

    TRACE("\n");

    hres = get_length(ctx, vthis, &jsthis, &length);
    if(FAILED(hres))
        return hres;

1181
    /* Fixme check IsCallable */
1182
    if(!argc || !is_object_instance(argv[0])) {
1183
        FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined()));
1184 1185
        hres = E_INVALIDARG;
        goto done;
1186
    }
1187
    callback = get_object(argv[0]);
1188 1189

    if(argc > 1 && !is_undefined(argv[1])) {
1190
        if(!is_object_instance(argv[1])) {
1191
            FIXME("Unsupported context this %s\n", debugstr_jsval(argv[1]));
1192 1193
            hres = E_NOTIMPL;
            goto done;
1194 1195
        }
        context_obj = get_object(argv[1]);
1196 1197
    }

1198 1199 1200 1201 1202
    for(i = 0; i < length; i++) {
        hres = jsdisp_get_idx(jsthis, i, &value);
        if(hres == DISP_E_UNKNOWNNAME)
            continue;
        if(FAILED(hres))
1203
            goto done;
1204 1205 1206 1207

        args[0] = value;
        args[1] = jsval_number(i);
        args[2] = jsval_obj(jsthis);
1208
        hres = disp_call_value(ctx, callback, context_obj, DISPATCH_METHOD, ARRAY_SIZE(args), args, &res);
1209 1210
        jsval_release(value);
        if(FAILED(hres))
1211
            goto done;
1212 1213 1214 1215
        jsval_release(res);
    }

    if(r) *r = jsval_undefined();
1216 1217 1218 1219
    hres = S_OK;
done:
    jsdisp_release(jsthis);
    return hres;
1220 1221
}

1222
static HRESULT Array_indexOf(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237
        jsval_t *r)
{
    jsdisp_t *jsthis;
    unsigned length, i, from = 0;
    jsval_t search, value;
    BOOL eq;
    HRESULT hres;

    TRACE("\n");

    hres = get_length(ctx, vthis, &jsthis, &length);
    if(FAILED(hres))
        return hres;
    if(!length) {
        if(r) *r = jsval_number(-1);
1238
        goto done;
1239 1240 1241 1242 1243 1244 1245 1246 1247
    }

    search = argc ? argv[0] : jsval_undefined();

    if(argc > 1) {
        double from_arg;

        hres = to_integer(ctx, argv[1], &from_arg);
        if(FAILED(hres))
1248
            goto done;
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260

        if(from_arg >= 0)
            from = min(from_arg, length);
        else
            from = max(from_arg + length, 0);
    }

    for(i = from; i < length; i++) {
        hres = jsdisp_get_idx(jsthis, i, &value);
        if(hres == DISP_E_UNKNOWNNAME)
            continue;
        if(FAILED(hres))
1261
            goto done;
1262 1263 1264 1265

        hres = jsval_strict_equal(value, search, &eq);
        jsval_release(value);
        if(FAILED(hres))
1266
            goto done;
1267 1268
        if(eq) {
            if(r) *r = jsval_number(i);
1269
            goto done;
1270 1271 1272 1273
        }
    }

    if(r) *r = jsval_number(-1);
1274 1275 1276 1277
    hres = S_OK;
done:
    jsdisp_release(jsthis);
    return hres;
1278 1279
}

1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341
static HRESULT Array_lastIndexOf(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
        jsval_t *r)
{
    jsval_t search, value;
    unsigned i, length;
    jsdisp_t *jsthis;
    HRESULT hres;
    BOOL eq;

    TRACE("\n");

    hres = get_length(ctx, vthis, &jsthis, &length);
    if(FAILED(hres))
        return hres;
    if(!length)
        goto notfound;

    search = argc ? argv[0] : jsval_undefined();

    i = length - 1;
    if(argc > 1) {
        double from_arg;

        hres = to_integer(ctx, argv[1], &from_arg);
        if(FAILED(hres))
            goto done;

        if(from_arg >= 0.0)
            i = min(from_arg, i);
        else {
            from_arg += length;
            if(from_arg < 0.0)
                goto notfound;
            i = from_arg;
        }
    }

    do {
        hres = jsdisp_get_idx(jsthis, i, &value);
        if(hres == DISP_E_UNKNOWNNAME)
            continue;
        if(FAILED(hres))
            goto done;

        hres = jsval_strict_equal(value, search, &eq);
        jsval_release(value);
        if(FAILED(hres))
            goto done;
        if(eq) {
            if(r) *r = jsval_number(i);
            goto done;
        }
    } while(i--);

notfound:
    if(r) *r = jsval_number(-1);
    hres = S_OK;
done:
    jsdisp_release(jsthis);
    return hres;
}

1342
static HRESULT Array_map(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1343 1344 1345 1346
{
    IDispatch *context_this = NULL, *callback;
    jsval_t callback_args[3], mapped_value;
    jsdisp_t *jsthis, *array;
1347
    UINT32 length, k;
1348 1349 1350 1351 1352 1353 1354 1355 1356 1357
    HRESULT hres;

    TRACE("\n");

    hres = get_length(ctx, vthis, &jsthis, &length);
    if(FAILED(hres)) {
        FIXME("Could not get length\n");
        return hres;
    }

1358
    /* FIXME: check IsCallable */
1359
    if(!argc || !is_object_instance(argv[0])) {
1360
        FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined()));
1361 1362
        hres = E_INVALIDARG;
        goto done;
1363 1364 1365 1366
    }
    callback = get_object(argv[0]);

    if(argc > 1) {
1367
        if(is_object_instance(argv[1])) {
1368 1369
            context_this = get_object(argv[1]);
        }else if(!is_undefined(argv[1])) {
1370
            FIXME("Unsupported context this %s\n", debugstr_jsval(argv[1]));
1371 1372
            hres = E_NOTIMPL;
            goto done;
1373 1374 1375 1376 1377
        }
    }

    hres = create_array(ctx, length, &array);
    if(FAILED(hres))
1378
        goto done;
1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402

    for(k = 0; k < length; k++) {
        hres = jsdisp_get_idx(jsthis, k, &callback_args[0]);
        if(hres == DISP_E_UNKNOWNNAME)
            continue;
        if(FAILED(hres))
            break;

        callback_args[1] = jsval_number(k);
        callback_args[2] = jsval_obj(jsthis);
        hres = disp_call_value(ctx, callback, context_this, DISPATCH_METHOD, 3, callback_args, &mapped_value);
        jsval_release(callback_args[0]);
        if(FAILED(hres))
            break;

        hres = jsdisp_propput_idx(array, k, mapped_value);
        if(FAILED(hres))
            break;
    }

    if(SUCCEEDED(hres) && r)
        *r = jsval_obj(array);
    else
        jsdisp_release(array);
1403 1404
done:
    jsdisp_release(jsthis);
1405 1406 1407
    return hres;
}

1408
static HRESULT Array_reduce(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1409 1410 1411 1412 1413
{
    IDispatch *context_this = NULL, *callback;
    jsval_t callback_args[4], acc, new_acc;
    BOOL have_value = FALSE;
    jsdisp_t *jsthis;
1414
    UINT32 length, k;
1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425
    HRESULT hres;

    TRACE("\n");

    hres = get_length(ctx, vthis, &jsthis, &length);
    if(FAILED(hres)) {
        FIXME("Could not get length\n");
        return hres;
    }

    /* Fixme check IsCallable */
1426
    if(!argc || !is_object_instance(argv[0])) {
1427
        FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined()));
1428 1429
        hres = E_INVALIDARG;
        goto done;
1430 1431 1432 1433 1434 1435 1436
    }
    callback = get_object(argv[0]);

    if(argc > 1) {
        have_value = TRUE;
        hres = jsval_copy(argv[1], &acc);
        if(FAILED(hres))
1437
            goto done;
1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473
    }

    for(k = 0; k < length; k++) {
        hres = jsdisp_get_idx(jsthis, k, &callback_args[1]);
        if(hres == DISP_E_UNKNOWNNAME)
            continue;
        if(FAILED(hres))
            break;

        if(!have_value) {
            have_value = TRUE;
            acc = callback_args[1];
            continue;
        }

        callback_args[0] = acc;
        callback_args[2] = jsval_number(k);
        callback_args[3] = jsval_obj(jsthis);
        hres = disp_call_value(ctx, callback, context_this, DISPATCH_METHOD, ARRAY_SIZE(callback_args), callback_args, &new_acc);
        jsval_release(callback_args[1]);
        if(FAILED(hres))
            break;

        jsval_release(acc);
        acc = new_acc;
    }

    if(SUCCEEDED(hres) && !have_value) {
        WARN("No array element\n");
        hres = JS_E_INVALID_ACTION;
    }

    if(SUCCEEDED(hres) && r)
        *r = acc;
    else if(have_value)
        jsval_release(acc);
1474 1475
done:
    jsdisp_release(jsthis);
1476 1477 1478
    return hres;
}

1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544
static HRESULT Array_some(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
        jsval_t *r)
{
    IDispatch *context_obj = NULL, *callback;
    jsval_t value, args[3], res;
    BOOL boolval, ret = FALSE;
    unsigned length, i;
    jsdisp_t *jsthis;
    HRESULT hres;

    TRACE("\n");

    hres = get_length(ctx, vthis, &jsthis, &length);
    if(FAILED(hres))
        return hres;

    /* FIXME: check IsCallable */
    if(!argc || !is_object_instance(argv[0])) {
        FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined()));
        hres = E_INVALIDARG;
        goto done;
    }
    callback = get_object(argv[0]);

    if(argc > 1 && !is_undefined(argv[1])) {
        if(!is_object_instance(argv[1])) {
            FIXME("Unsupported context this %s\n", debugstr_jsval(argv[1]));
            hres = E_NOTIMPL;
            goto done;
        }
        context_obj = get_object(argv[1]);
    }

    for(i = 0; i < length; i++) {
        hres = jsdisp_get_idx(jsthis, i, &value);
        if(FAILED(hres)) {
            if(hres == DISP_E_UNKNOWNNAME)
                continue;
            goto done;
        }
        args[0] = value;
        args[1] = jsval_number(i);
        args[2] = jsval_obj(jsthis);
        hres = disp_call_value(ctx, callback, context_obj, DISPATCH_METHOD, ARRAY_SIZE(args), args, &res);
        jsval_release(value);
        if(FAILED(hres))
            goto done;

        hres = to_boolean(res, &boolval);
        jsval_release(res);
        if(FAILED(hres))
            goto done;
        if(boolval) {
            ret = TRUE;
            break;
        }
    }

    if(r)
        *r = jsval_bool(ret);
    hres = S_OK;
done:
    jsdisp_release(jsthis);
    return hres;
}

1545
/* ECMA-262 3rd Edition    15.4.4.13 */
1546
static HRESULT Array_unshift(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1547
        jsval_t *r)
1548
{
1549
    jsdisp_t *jsthis;
1550
    WCHAR buf[14], *buf_end, *str;
1551
    UINT32 i, length;
1552
    jsval_t val;
1553 1554 1555 1556 1557
    DISPID id;
    HRESULT hres;

    TRACE("\n");

1558
    hres = get_length(ctx, vthis, &jsthis, &length);
1559 1560
    if(FAILED(hres))
        return hres;
1561

1562
    if(argc) {
1563
        buf_end = buf + ARRAY_SIZE(buf)-1;
1564 1565
        *buf_end-- = 0;
        i = length;
1566

1567 1568
        while(i--) {
            str = idx_to_str(i, buf_end);
1569

1570 1571
            hres = jsdisp_get_id(jsthis, str, 0, &id);
            if(SUCCEEDED(hres)) {
1572
                hres = jsdisp_propget(jsthis, id, &val);
1573
                if(FAILED(hres))
1574
                    goto done;
1575

1576
                hres = jsdisp_propput_idx(jsthis, i+argc, val);
1577
                jsval_release(val);
1578
            }else if(hres == DISP_E_UNKNOWNNAME) {
1579
                hres = IDispatchEx_DeleteMemberByDispID(&jsthis->IDispatchEx_iface, id);
1580
            }
1581 1582 1583
        }

        if(FAILED(hres))
1584
            goto done;
1585 1586 1587
    }

    for(i=0; i<argc; i++) {
1588
        hres = jsdisp_propput_idx(jsthis, i, argv[i]);
1589
        if(FAILED(hres))
1590
            goto done;
1591 1592
    }

1593 1594
    if(argc) {
        length += argc;
1595
        hres = set_length(jsthis, length);
1596
        if(FAILED(hres))
1597
            goto done;
1598
    }
1599

1600 1601
    if(r)
        *r = ctx->version < 2 ? jsval_undefined() : jsval_number(length);
1602 1603 1604 1605
    hres = S_OK;
done:
    jsdisp_release(jsthis);
    return hres;
1606 1607
}

1608
static void Array_destructor(jsdisp_t *dispex)
1609 1610 1611 1612
{
    heap_free(dispex);
}

1613
static void Array_on_put(jsdisp_t *dispex, const WCHAR *name)
1614
{
1615
    ArrayInstance *array = array_from_jsdisp(dispex);
1616 1617 1618
    const WCHAR *ptr = name;
    DWORD id = 0;

1619
    if(!is_digit(*ptr))
1620 1621
        return;

1622
    while(*ptr && is_digit(*ptr)) {
1623 1624 1625 1626 1627 1628 1629 1630 1631
        id = id*10 + (*ptr-'0');
        ptr++;
    }

    if(*ptr)
        return;

    if(id >= array->length)
        array->length = id+1;
1632 1633 1634
}

static const builtin_prop_t Array_props[] = {
1635
    {L"concat",                Array_concat,               PROPF_METHOD|1},
1636
    {L"every",                 Array_every,                PROPF_METHOD|PROPF_ES5|1},
1637
    {L"filter",                Array_filter,               PROPF_METHOD|PROPF_ES5|1},
1638 1639 1640
    {L"forEach",               Array_forEach,              PROPF_METHOD|PROPF_ES5|1},
    {L"indexOf",               Array_indexOf,              PROPF_METHOD|PROPF_ES5|1},
    {L"join",                  Array_join,                 PROPF_METHOD|1},
1641
    {L"lastIndexOf",           Array_lastIndexOf,          PROPF_METHOD|PROPF_ES5|1},
1642 1643 1644 1645
    {L"length",                NULL,0,                     Array_get_length, Array_set_length},
    {L"map",                   Array_map,                  PROPF_METHOD|PROPF_ES5|1},
    {L"pop",                   Array_pop,                  PROPF_METHOD},
    {L"push",                  Array_push,                 PROPF_METHOD|1},
1646
    {L"reduce",                Array_reduce,               PROPF_METHOD|PROPF_ES5|1},
1647 1648 1649
    {L"reverse",               Array_reverse,              PROPF_METHOD},
    {L"shift",                 Array_shift,                PROPF_METHOD},
    {L"slice",                 Array_slice,                PROPF_METHOD|2},
1650
    {L"some",                  Array_some,                 PROPF_METHOD|PROPF_ES5|1},
1651 1652 1653 1654 1655
    {L"sort",                  Array_sort,                 PROPF_METHOD|1},
    {L"splice",                Array_splice,               PROPF_METHOD|2},
    {L"toLocaleString",        Array_toLocaleString,       PROPF_METHOD},
    {L"toString",              Array_toString,             PROPF_METHOD},
    {L"unshift",               Array_unshift,              PROPF_METHOD|1},
1656 1657 1658 1659
};

static const builtin_info_t Array_info = {
    JSCLASS_ARRAY,
1660
    NULL,
1661
    ARRAY_SIZE(Array_props),
1662 1663 1664 1665 1666
    Array_props,
    Array_destructor,
    Array_on_put
};

1667
static const builtin_prop_t ArrayInst_props[] = {
1668
    {L"length",                NULL,0,                     Array_get_length, Array_set_length}
1669 1670 1671 1672
};

static const builtin_info_t ArrayInst_info = {
    JSCLASS_ARRAY,
1673
    NULL,
1674
    ARRAY_SIZE(ArrayInst_props),
1675 1676 1677 1678 1679
    ArrayInst_props,
    Array_destructor,
    Array_on_put
};

1680
/* ECMA-262 5.1 Edition    15.4.3.2 */
1681
static HRESULT ArrayConstr_isArray(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697
{
    jsdisp_t *obj;

    TRACE("\n");

    if(!argc || !is_object_instance(argv[0])) {
        if(r) *r = jsval_bool(FALSE);
        return S_OK;
    }

    obj = iface_to_jsdisp(get_object(argv[0]));
    if(r) *r = jsval_bool(obj && is_class(obj, JSCLASS_ARRAY));
    if(obj) jsdisp_release(obj);
    return S_OK;
}

1698
static HRESULT ArrayConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1699
        jsval_t *r)
1700
{
1701
    jsdisp_t *obj;
1702 1703 1704 1705 1706 1707
    DWORD i;
    HRESULT hres;

    TRACE("\n");

    switch(flags) {
1708
    case DISPATCH_METHOD:
1709
    case DISPATCH_CONSTRUCT: {
1710 1711 1712 1713
        if(argc == 1 && is_number(argv[0])) {
            double n = get_number(argv[0]);

            if(n < 0 || !is_int32(n))
1714
                return JS_E_INVALID_LENGTH;
1715 1716
            if(!r)
                return S_OK;
1717

1718
            hres = create_array(ctx, n, &obj);
1719 1720 1721
            if(FAILED(hres))
                return hres;

1722
            *r = jsval_obj(obj);
1723 1724 1725
            return S_OK;
        }

1726 1727
        if(!r)
            return S_OK;
1728
        hres = create_array(ctx, argc, &obj);
1729 1730 1731
        if(FAILED(hres))
            return hres;

1732
        for(i=0; i < argc; i++) {
1733
            hres = jsdisp_propput_idx(obj, i, argv[i]);
1734 1735 1736 1737 1738 1739 1740 1741
            if(FAILED(hres))
                break;
        }
        if(FAILED(hres)) {
            jsdisp_release(obj);
            return hres;
        }

1742
        *r = jsval_obj(obj);
1743 1744 1745 1746 1747 1748 1749 1750
        break;
    }
    default:
        FIXME("unimplemented flags: %x\n", flags);
        return E_NOTIMPL;
    }

    return S_OK;
1751 1752
}

1753
static HRESULT alloc_array(script_ctx_t *ctx, jsdisp_t *object_prototype, ArrayInstance **ret)
1754
{
1755
    ArrayInstance *array;
1756 1757
    HRESULT hres;

1758 1759 1760 1761
    array = heap_alloc_zero(sizeof(ArrayInstance));
    if(!array)
        return E_OUTOFMEMORY;

1762 1763
    if(object_prototype)
        hres = init_dispex(&array->dispex, ctx, &Array_info, object_prototype);
1764
    else
1765
        hres = init_dispex_from_constr(&array->dispex, ctx, &ArrayInst_info, ctx->array_constr);
1766 1767 1768 1769 1770 1771 1772 1773 1774 1775

    if(FAILED(hres)) {
        heap_free(array);
        return hres;
    }

    *ret = array;
    return S_OK;
}

1776
static const builtin_prop_t ArrayConstr_props[] = {
1777
    {L"isArray",    ArrayConstr_isArray,    PROPF_ES5|PROPF_METHOD|1}
1778 1779 1780 1781
};

static const builtin_info_t ArrayConstr_info = {
    JSCLASS_FUNCTION,
1782
    Function_value,
1783
    ARRAY_SIZE(ArrayConstr_props),
1784 1785 1786 1787 1788
    ArrayConstr_props,
    NULL,
    NULL
};

1789
HRESULT create_array_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1790 1791 1792 1793
{
    ArrayInstance *array;
    HRESULT hres;

1794
    hres = alloc_array(ctx, object_prototype, &array);
1795 1796 1797
    if(FAILED(hres))
        return hres;

1798
    hres = create_builtin_constructor(ctx, ArrayConstr_value, L"Array", &ArrayConstr_info, PROPF_CONSTR|1, &array->dispex, ret);
1799

Jacek Caban's avatar
Jacek Caban committed
1800
    jsdisp_release(&array->dispex);
1801 1802
    return hres;
}
1803

1804
HRESULT create_array(script_ctx_t *ctx, DWORD length, jsdisp_t **ret)
1805 1806 1807 1808
{
    ArrayInstance *array;
    HRESULT hres;

1809
    hres = alloc_array(ctx, NULL, &array);
1810 1811 1812 1813 1814 1815 1816 1817
    if(FAILED(hres))
        return hres;

    array->length = length;

    *ret = &array->dispex;
    return S_OK;
}