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