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 #include "engine.h" 27 28 #include "wine/debug.h" 29 30 WINE_DEFAULT_DEBUG_CHANNEL(jscript); 31 WINE_DECLARE_DEBUG_CHANNEL(heap); 32 33 const char *debugstr_jsval(const jsval_t v) 34 { 35 switch(jsval_type(v)) { 36 case JSV_UNDEFINED: 37 return "undefined"; 38 case JSV_NULL: 39 return "null"; 40 case JSV_OBJECT: 41 return wine_dbg_sprintf("obj(%p)", get_object(v)); 42 case JSV_STRING: 43 return wine_dbg_sprintf("str(%s)", debugstr_jsstr(get_string(v))); 44 case JSV_NUMBER: 45 return wine_dbg_sprintf("%lf", get_number(v)); 46 case JSV_BOOL: 47 return get_bool(v) ? "true" : "false"; 48 case JSV_VARIANT: 49 return debugstr_variant(get_variant(v)); 50 } 51 52 assert(0); 53 return NULL; 54 } 55 56 BOOL is_finite(double n) 57 { 58 return !isnan(n) && !isinf(n); 59 } 60 61 #define MIN_BLOCK_SIZE 128 62 #define ARENA_FREE_FILLER 0xaa 63 64 static inline DWORD block_size(DWORD block) 65 { 66 return MIN_BLOCK_SIZE << block; 67 } 68 69 void heap_pool_init(heap_pool_t *heap) 70 { 71 memset(heap, 0, sizeof(*heap)); 72 list_init(&heap->custom_blocks); 73 } 74 75 void *heap_pool_alloc(heap_pool_t *heap, DWORD size) 76 { 77 struct list *list; 78 void *tmp; 79 80 if(!heap->block_cnt) { 81 if(!heap->blocks) { 82 heap->blocks = heap_alloc(sizeof(void*)); 83 if(!heap->blocks) 84 return NULL; 85 } 86 87 tmp = heap_alloc(block_size(0)); 88 if(!tmp) 89 return NULL; 90 91 heap->blocks[0] = tmp; 92 heap->block_cnt = 1; 93 } 94 95 if(heap->offset + size <= block_size(heap->last_block)) { 96 tmp = ((BYTE*)heap->blocks[heap->last_block])+heap->offset; 97 heap->offset += size; 98 return tmp; 99 } 100 101 if(size <= block_size(heap->last_block+1)) { 102 if(heap->last_block+1 == heap->block_cnt) { 103 tmp = heap_realloc(heap->blocks, (heap->block_cnt+1)*sizeof(void*)); 104 if(!tmp) 105 return NULL; 106 107 heap->blocks = tmp; 108 heap->blocks[heap->block_cnt] = heap_alloc(block_size(heap->block_cnt)); 109 if(!heap->blocks[heap->block_cnt]) 110 return NULL; 111 112 heap->block_cnt++; 113 } 114 115 heap->last_block++; 116 heap->offset = size; 117 return heap->blocks[heap->last_block]; 118 } 119 120 list = heap_alloc(size + sizeof(struct list)); 121 if(!list) 122 return NULL; 123 124 list_add_head(&heap->custom_blocks, list); 125 return list+1; 126 } 127 128 void *heap_pool_grow(heap_pool_t *heap, void *mem, DWORD size, DWORD inc) 129 { 130 void *ret; 131 132 if(mem == (BYTE*)heap->blocks[heap->last_block] + heap->offset-size 133 && heap->offset+inc < block_size(heap->last_block)) { 134 heap->offset += inc; 135 return mem; 136 } 137 138 ret = heap_pool_alloc(heap, size+inc); 139 if(ret) /* FIXME: avoid copying for custom blocks */ 140 memcpy(ret, mem, size); 141 return ret; 142 } 143 144 void heap_pool_clear(heap_pool_t *heap) 145 { 146 struct list *tmp; 147 148 if(!heap) 149 return; 150 151 while((tmp = list_head(&heap->custom_blocks))) { 152 list_remove(tmp); 153 heap_free(tmp); 154 } 155 156 if(WARN_ON(heap)) { 157 DWORD i; 158 159 for(i=0; i < heap->block_cnt; i++) 160 memset(heap->blocks[i], ARENA_FREE_FILLER, block_size(i)); 161 } 162 163 heap->last_block = heap->offset = 0; 164 heap->mark = FALSE; 165 } 166 167 void heap_pool_free(heap_pool_t *heap) 168 { 169 DWORD i; 170 171 heap_pool_clear(heap); 172 173 for(i=0; i < heap->block_cnt; i++) 174 heap_free(heap->blocks[i]); 175 heap_free(heap->blocks); 176 177 heap_pool_init(heap); 178 } 179 180 heap_pool_t *heap_pool_mark(heap_pool_t *heap) 181 { 182 if(heap->mark) 183 return NULL; 184 185 heap->mark = TRUE; 186 return heap; 187 } 188 189 void jsval_release(jsval_t val) 190 { 191 switch(jsval_type(val)) { 192 case JSV_OBJECT: 193 if(get_object(val)) 194 IDispatch_Release(get_object(val)); 195 break; 196 case JSV_STRING: 197 jsstr_release(get_string(val)); 198 break; 199 case JSV_VARIANT: 200 VariantClear(get_variant(val)); 201 heap_free(get_variant(val)); 202 break; 203 default: 204 break; 205 } 206 } 207 208 static HRESULT jsval_variant(jsval_t *val, VARIANT *var) 209 { 210 VARIANT *v; 211 HRESULT hres; 212 213 __JSVAL_TYPE(*val) = JSV_VARIANT; 214 __JSVAL_VAR(*val) = v = heap_alloc(sizeof(VARIANT)); 215 if(!v) { 216 *val = jsval_undefined(); 217 return E_OUTOFMEMORY; 218 } 219 220 V_VT(v) = VT_EMPTY; 221 hres = VariantCopy(v, var); 222 if(FAILED(hres)) { 223 *val = jsval_undefined(); 224 heap_free(v); 225 } 226 return hres; 227 } 228 229 HRESULT jsval_copy(jsval_t v, jsval_t *r) 230 { 231 switch(jsval_type(v)) { 232 case JSV_UNDEFINED: 233 case JSV_NULL: 234 case JSV_NUMBER: 235 case JSV_BOOL: 236 *r = v; 237 return S_OK; 238 case JSV_OBJECT: 239 if(get_object(v)) 240 IDispatch_AddRef(get_object(v)); 241 *r = v; 242 return S_OK; 243 case JSV_STRING: { 244 jsstr_addref(get_string(v)); 245 *r = v; 246 return S_OK; 247 } 248 case JSV_VARIANT: 249 return jsval_variant(r, get_variant(v)); 250 } 251 252 assert(0); 253 return E_FAIL; 254 } 255 256 HRESULT variant_to_jsval(VARIANT *var, jsval_t *r) 257 { 258 if(V_VT(var) == (VT_VARIANT|VT_BYREF)) 259 var = V_VARIANTREF(var); 260 261 switch(V_VT(var)) { 262 case VT_EMPTY: 263 *r = jsval_undefined(); 264 return S_OK; 265 case VT_NULL: 266 *r = jsval_null(); 267 return S_OK; 268 case VT_BOOL: 269 *r = jsval_bool(V_BOOL(var)); 270 return S_OK; 271 case VT_I4: 272 *r = jsval_number(V_I4(var)); 273 return S_OK; 274 case VT_R8: 275 *r = jsval_number(V_R8(var)); 276 return S_OK; 277 case VT_BSTR: { 278 jsstr_t *str; 279 280 if(V_BSTR(var)) { 281 str = jsstr_alloc_len(V_BSTR(var), SysStringLen(V_BSTR(var))); 282 if(!str) 283 return E_OUTOFMEMORY; 284 }else { 285 str = jsstr_null_bstr(); 286 } 287 288 *r = jsval_string(str); 289 return S_OK; 290 } 291 case VT_DISPATCH: { 292 if(V_DISPATCH(var)) 293 IDispatch_AddRef(V_DISPATCH(var)); 294 *r = jsval_disp(V_DISPATCH(var)); 295 return S_OK; 296 } 297 case VT_I2: 298 *r = jsval_number(V_I2(var)); 299 return S_OK; 300 case VT_UI2: 301 *r = jsval_number(V_UI2(var)); 302 return S_OK; 303 case VT_INT: 304 *r = jsval_number(V_INT(var)); 305 return S_OK; 306 case VT_UI4: 307 *r = jsval_number(V_UI4(var)); 308 return S_OK; 309 case VT_UI8: 310 /* 311 * Native doesn't support VT_UI8 here, but it's needed for IE9+ APIs 312 * (native IE9 doesn't use jscript.dll for JavaScript). 313 */ 314 *r = jsval_number(V_UI8(var)); 315 return S_OK; 316 case VT_R4: 317 *r = jsval_number(V_R4(var)); 318 return S_OK; 319 case VT_UNKNOWN: 320 if(V_UNKNOWN(var)) { 321 IDispatch *disp; 322 HRESULT hres; 323 324 hres = IUnknown_QueryInterface(V_UNKNOWN(var), &IID_IDispatch, (void**)&disp); 325 if(SUCCEEDED(hres)) { 326 *r = jsval_disp(disp); 327 return S_OK; 328 } 329 }else { 330 *r = jsval_disp(NULL); 331 return S_OK; 332 } 333 /* fall through */ 334 default: 335 return jsval_variant(r, var); 336 } 337 } 338 339 HRESULT jsval_to_variant(jsval_t val, VARIANT *retv) 340 { 341 switch(jsval_type(val)) { 342 case JSV_UNDEFINED: 343 V_VT(retv) = VT_EMPTY; 344 return S_OK; 345 case JSV_NULL: 346 V_VT(retv) = VT_NULL; 347 return S_OK; 348 case JSV_OBJECT: 349 V_VT(retv) = VT_DISPATCH; 350 if(get_object(val)) 351 IDispatch_AddRef(get_object(val)); 352 V_DISPATCH(retv) = get_object(val); 353 return S_OK; 354 case JSV_STRING: { 355 jsstr_t *str = get_string(val); 356 357 V_VT(retv) = VT_BSTR; 358 if(is_null_bstr(str)) { 359 V_BSTR(retv) = NULL; 360 }else { 361 V_BSTR(retv) = SysAllocStringLen(NULL, jsstr_length(str)); 362 if(V_BSTR(retv)) 363 jsstr_flush(str, V_BSTR(retv)); 364 else 365 return E_OUTOFMEMORY; 366 } 367 return S_OK; 368 } 369 case JSV_NUMBER: { 370 double n = get_number(val); 371 372 if(is_int32(n)) { 373 V_VT(retv) = VT_I4; 374 V_I4(retv) = n; 375 }else { 376 V_VT(retv) = VT_R8; 377 V_R8(retv) = n; 378 } 379 380 return S_OK; 381 } 382 case JSV_BOOL: 383 V_VT(retv) = VT_BOOL; 384 V_BOOL(retv) = get_bool(val) ? VARIANT_TRUE : VARIANT_FALSE; 385 return S_OK; 386 case JSV_VARIANT: 387 V_VT(retv) = VT_EMPTY; 388 return VariantCopy(retv, get_variant(val)); 389 } 390 391 assert(0); 392 return E_FAIL; 393 } 394 395 /* ECMA-262 3rd Edition 9.1 */ 396 HRESULT to_primitive(script_ctx_t *ctx, jsval_t val, jsval_t *ret, hint_t hint) 397 { 398 if(is_object_instance(val)) { 399 jsdisp_t *jsdisp; 400 jsval_t prim; 401 DISPID id; 402 HRESULT hres; 403 404 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0}; 405 static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0}; 406 407 if(!get_object(val)) { 408 *ret = jsval_null(); 409 return S_OK; 410 } 411 412 jsdisp = iface_to_jsdisp(get_object(val)); 413 if(!jsdisp) 414 return disp_propget(ctx, get_object(val), DISPID_VALUE, ret); 415 416 if(hint == NO_HINT) 417 hint = is_class(jsdisp, JSCLASS_DATE) ? HINT_STRING : HINT_NUMBER; 418 419 /* Native implementation doesn't throw TypeErrors, returns strange values */ 420 421 hres = jsdisp_get_id(jsdisp, hint == HINT_STRING ? toStringW : valueOfW, 0, &id); 422 if(SUCCEEDED(hres)) { 423 hres = jsdisp_call(jsdisp, id, DISPATCH_METHOD, 0, NULL, &prim); 424 if(FAILED(hres)) { 425 WARN("call error - forwarding exception\n"); 426 jsdisp_release(jsdisp); 427 return hres; 428 }else if(!is_object_instance(prim)) { 429 jsdisp_release(jsdisp); 430 *ret = prim; 431 return S_OK; 432 }else { 433 IDispatch_Release(get_object(prim)); 434 } 435 } 436 437 hres = jsdisp_get_id(jsdisp, hint == HINT_STRING ? valueOfW : toStringW, 0, &id); 438 if(SUCCEEDED(hres)) { 439 hres = jsdisp_call(jsdisp, id, DISPATCH_METHOD, 0, NULL, &prim); 440 if(FAILED(hres)) { 441 WARN("call error - forwarding exception\n"); 442 jsdisp_release(jsdisp); 443 return hres; 444 }else if(!is_object_instance(prim)) { 445 jsdisp_release(jsdisp); 446 *ret = prim; 447 return S_OK; 448 }else { 449 IDispatch_Release(get_object(prim)); 450 } 451 } 452 453 jsdisp_release(jsdisp); 454 455 WARN("failed\n"); 456 return throw_type_error(ctx, JS_E_TO_PRIMITIVE, NULL); 457 } 458 459 return jsval_copy(val, ret); 460 461 } 462 463 /* ECMA-262 3rd Edition 9.2 */ 464 HRESULT to_boolean(jsval_t val, BOOL *ret) 465 { 466 switch(jsval_type(val)) { 467 case JSV_UNDEFINED: 468 case JSV_NULL: 469 *ret = FALSE; 470 return S_OK; 471 case JSV_OBJECT: 472 *ret = get_object(val) != NULL; 473 return S_OK; 474 case JSV_STRING: 475 *ret = jsstr_length(get_string(val)) != 0; 476 return S_OK; 477 case JSV_NUMBER: 478 *ret = !isnan(get_number(val)) && get_number(val); 479 return S_OK; 480 case JSV_BOOL: 481 *ret = get_bool(val); 482 return S_OK; 483 case JSV_VARIANT: 484 FIXME("unimplemented for variant %s\n", debugstr_variant(get_variant(val))); 485 return E_NOTIMPL; 486 } 487 488 assert(0); 489 return E_FAIL; 490 } 491 492 static int hex_to_int(WCHAR c) 493 { 494 if('0' <= c && c <= '9') 495 return c-'0'; 496 497 if('a' <= c && c <= 'f') 498 return c-'a'+10; 499 500 if('A' <= c && c <= 'F') 501 return c-'A'+10; 502 503 return -1; 504 } 505 506 /* ECMA-262 3rd Edition 9.3.1 */ 507 static HRESULT str_to_number(jsstr_t *str, double *ret) 508 { 509 const WCHAR *ptr; 510 BOOL neg = FALSE; 511 DOUBLE d = 0.0; 512 513 static const WCHAR infinityW[] = {'I','n','f','i','n','i','t','y'}; 514 515 ptr = jsstr_flatten(str); 516 if(!ptr) 517 return E_OUTOFMEMORY; 518 519 while(isspaceW(*ptr)) 520 ptr++; 521 522 if(*ptr == '-') { 523 neg = TRUE; 524 ptr++; 525 }else if(*ptr == '+') { 526 ptr++; 527 } 528 529 if(!strncmpW(ptr, infinityW, sizeof(infinityW)/sizeof(WCHAR))) { 530 ptr += sizeof(infinityW)/sizeof(WCHAR); 531 while(*ptr && isspaceW(*ptr)) 532 ptr++; 533 534 if(*ptr) 535 *ret = NAN; 536 else 537 *ret = neg ? -INFINITY : INFINITY; 538 return S_OK; 539 } 540 541 if(*ptr == '0' && ptr[1] == 'x') { 542 DWORD l = 0; 543 544 ptr += 2; 545 while((l = hex_to_int(*ptr)) != -1) { 546 d = d*16 + l; 547 ptr++; 548 } 549 550 *ret = d; 551 return S_OK; 552 } 553 554 while(isdigitW(*ptr)) 555 d = d*10 + (*ptr++ - '0'); 556 557 if(*ptr == 'e' || *ptr == 'E') { 558 BOOL eneg = FALSE; 559 LONG l = 0; 560 561 ptr++; 562 if(*ptr == '-') { 563 ptr++; 564 eneg = TRUE; 565 }else if(*ptr == '+') { 566 ptr++; 567 } 568 569 while(isdigitW(*ptr)) 570 l = l*10 + (*ptr++ - '0'); 571 if(eneg) 572 l = -l; 573 574 d *= pow(10, l); 575 }else if(*ptr == '.') { 576 DOUBLE dec = 0.1; 577 578 ptr++; 579 while(isdigitW(*ptr)) { 580 d += dec * (*ptr++ - '0'); 581 dec *= 0.1; 582 } 583 } 584 585 while(isspaceW(*ptr)) 586 ptr++; 587 588 if(*ptr) { 589 *ret = NAN; 590 return S_OK; 591 } 592 593 if(neg) 594 d = -d; 595 596 *ret = d; 597 return S_OK; 598 } 599 600 /* ECMA-262 3rd Edition 9.3 */ 601 HRESULT to_number(script_ctx_t *ctx, jsval_t val, double *ret) 602 { 603 switch(jsval_type(val)) { 604 case JSV_UNDEFINED: 605 *ret = NAN; 606 return S_OK; 607 case JSV_NULL: 608 *ret = 0; 609 return S_OK; 610 case JSV_NUMBER: 611 *ret = get_number(val); 612 return S_OK; 613 case JSV_STRING: 614 return str_to_number(get_string(val), ret); 615 case JSV_OBJECT: { 616 jsval_t prim; 617 HRESULT hres; 618 619 hres = to_primitive(ctx, val, &prim, HINT_NUMBER); 620 if(FAILED(hres)) 621 return hres; 622 623 hres = to_number(ctx, prim, ret); 624 jsval_release(prim); 625 return hres; 626 } 627 case JSV_BOOL: 628 *ret = get_bool(val) ? 1 : 0; 629 return S_OK; 630 case JSV_VARIANT: 631 FIXME("unimplemented for variant %s\n", debugstr_variant(get_variant(val))); 632 return E_NOTIMPL; 633 }; 634 635 assert(0); 636 return E_FAIL; 637 } 638 639 /* ECMA-262 3rd Edition 9.4 */ 640 HRESULT to_integer(script_ctx_t *ctx, jsval_t v, double *ret) 641 { 642 double n; 643 HRESULT hres; 644 645 hres = to_number(ctx, v, &n); 646 if(FAILED(hres)) 647 return hres; 648 649 if(isnan(n)) 650 *ret = 0; 651 else 652 *ret = n >= 0.0 ? floor(n) : -floor(-n); 653 return S_OK; 654 } 655 656 /* ECMA-262 3rd Edition 9.5 */ 657 HRESULT to_int32(script_ctx_t *ctx, jsval_t v, INT *ret) 658 { 659 double n; 660 HRESULT hres; 661 662 const double p32 = (double)0xffffffff + 1; 663 664 hres = to_number(ctx, v, &n); 665 if(FAILED(hres)) 666 return hres; 667 668 if(is_finite(n)) 669 n = n > 0 ? fmod(n, p32) : -fmod(-n, p32); 670 else 671 n = 0; 672 673 *ret = (UINT32)n; 674 return S_OK; 675 } 676 677 /* ECMA-262 3rd Edition 9.6 */ 678 HRESULT to_uint32(script_ctx_t *ctx, jsval_t val, UINT32 *ret) 679 { 680 INT32 n; 681 HRESULT hres; 682 683 hres = to_int32(ctx, val, &n); 684 if(SUCCEEDED(hres)) 685 *ret = n; 686 return hres; 687 } 688 689 static jsstr_t *int_to_string(int i) 690 { 691 WCHAR buf[12], *p; 692 BOOL neg = FALSE; 693 694 if(!i) { 695 static const WCHAR zeroW[] = {'0',0}; 696 return jsstr_alloc(zeroW); 697 } 698 699 if(i < 0) { 700 neg = TRUE; 701 i = -i; 702 } 703 704 p = buf + sizeof(buf)/sizeof(*buf)-1; 705 *p-- = 0; 706 while(i) { 707 *p-- = i%10 + '0'; 708 i /= 10; 709 } 710 711 if(neg) 712 *p = '-'; 713 else 714 p++; 715 716 return jsstr_alloc(p); 717 } 718 719 HRESULT double_to_string(double n, jsstr_t **str) 720 { 721 const WCHAR InfinityW[] = {'-','I','n','f','i','n','i','t','y',0}; 722 723 if(isnan(n)) { 724 *str = jsstr_nan(); 725 }else if(isinf(n)) { 726 *str = jsstr_alloc(n<0 ? InfinityW : InfinityW+1); 727 }else if(is_int32(n)) { 728 *str = int_to_string(n); 729 }else { 730 VARIANT strv, v; 731 HRESULT hres; 732 733 /* FIXME: Don't use VariantChangeTypeEx */ 734 V_VT(&v) = VT_R8; 735 V_R8(&v) = n; 736 V_VT(&strv) = VT_EMPTY; 737 hres = VariantChangeTypeEx(&strv, &v, MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT), 0, VT_BSTR); 738 if(FAILED(hres)) 739 return hres; 740 741 *str = jsstr_alloc(V_BSTR(&strv)); 742 SysFreeString(V_BSTR(&strv)); 743 } 744 745 return *str ? S_OK : E_OUTOFMEMORY; 746 } 747 748 /* ECMA-262 3rd Edition 9.8 */ 749 HRESULT to_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str) 750 { 751 const WCHAR nullW[] = {'n','u','l','l',0}; 752 const WCHAR trueW[] = {'t','r','u','e',0}; 753 const WCHAR falseW[] = {'f','a','l','s','e',0}; 754 755 switch(jsval_type(val)) { 756 case JSV_UNDEFINED: 757 *str = jsstr_undefined(); 758 return S_OK; 759 case JSV_NULL: 760 *str = jsstr_alloc(nullW); 761 break; 762 case JSV_NUMBER: 763 return double_to_string(get_number(val), str); 764 case JSV_STRING: 765 *str = jsstr_addref(get_string(val)); 766 break; 767 case JSV_OBJECT: { 768 jsval_t prim; 769 HRESULT hres; 770 771 hres = to_primitive(ctx, val, &prim, HINT_STRING); 772 if(FAILED(hres)) 773 return hres; 774 775 hres = to_string(ctx, prim, str); 776 jsval_release(prim); 777 return hres; 778 } 779 case JSV_BOOL: 780 *str = jsstr_alloc(get_bool(val) ? trueW : falseW); 781 break; 782 default: 783 FIXME("unsupported %s\n", debugstr_jsval(val)); 784 return E_NOTIMPL; 785 } 786 787 return *str ? S_OK : E_OUTOFMEMORY; 788 } 789 790 HRESULT to_flat_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str, const WCHAR **ret_str) 791 { 792 HRESULT hres; 793 794 hres = to_string(ctx, val, str); 795 if(FAILED(hres)) 796 return hres; 797 798 *ret_str = jsstr_flatten(*str); 799 if(!*ret_str) { 800 jsstr_release(*str); 801 return E_OUTOFMEMORY; 802 } 803 804 return S_OK; 805 } 806 807 /* ECMA-262 3rd Edition 9.9 */ 808 HRESULT to_object(script_ctx_t *ctx, jsval_t val, IDispatch **disp) 809 { 810 jsdisp_t *dispex; 811 HRESULT hres; 812 813 switch(jsval_type(val)) { 814 case JSV_STRING: 815 hres = create_string(ctx, get_string(val), &dispex); 816 if(FAILED(hres)) 817 return hres; 818 819 *disp = to_disp(dispex); 820 break; 821 case JSV_NUMBER: 822 hres = create_number(ctx, get_number(val), &dispex); 823 if(FAILED(hres)) 824 return hres; 825 826 *disp = to_disp(dispex); 827 break; 828 case JSV_OBJECT: 829 if(get_object(val)) { 830 *disp = get_object(val); 831 IDispatch_AddRef(*disp); 832 }else { 833 jsdisp_t *obj; 834 835 hres = create_object(ctx, NULL, &obj); 836 if(FAILED(hres)) 837 return hres; 838 839 *disp = to_disp(obj); 840 } 841 break; 842 case JSV_BOOL: 843 hres = create_bool(ctx, get_bool(val), &dispex); 844 if(FAILED(hres)) 845 return hres; 846 847 *disp = to_disp(dispex); 848 break; 849 case JSV_UNDEFINED: 850 case JSV_NULL: 851 WARN("object expected\n"); 852 return throw_type_error(ctx, JS_E_OBJECT_EXPECTED, NULL); 853 case JSV_VARIANT: 854 switch(V_VT(get_variant(val))) { 855 case VT_ARRAY|VT_VARIANT: 856 hres = create_vbarray(ctx, V_ARRAY(get_variant(val)), &dispex); 857 if(FAILED(hres)) 858 return hres; 859 860 *disp = to_disp(dispex); 861 break; 862 863 default: 864 FIXME("Unsupported %s\n", debugstr_variant(get_variant(val))); 865 return E_NOTIMPL; 866 } 867 break; 868 } 869 870 return S_OK; 871 } 872 873 HRESULT variant_change_type(script_ctx_t *ctx, VARIANT *dst, VARIANT *src, VARTYPE vt) 874 { 875 jsval_t val; 876 HRESULT hres; 877 878 clear_ei(ctx); 879 hres = variant_to_jsval(src, &val); 880 if(FAILED(hres)) 881 return hres; 882 883 switch(vt) { 884 case VT_I2: 885 case VT_I4: { 886 INT i; 887 888 hres = to_int32(ctx, val, &i); 889 if(SUCCEEDED(hres)) { 890 if(vt == VT_I4) 891 V_I4(dst) = i; 892 else 893 V_I2(dst) = i; 894 } 895 break; 896 } 897 case VT_R8: { 898 double n; 899 hres = to_number(ctx, val, &n); 900 if(SUCCEEDED(hres)) 901 V_R8(dst) = n; 902 break; 903 } 904 case VT_R4: { 905 double n; 906 907 hres = to_number(ctx, val, &n); 908 if(SUCCEEDED(hres)) 909 V_R4(dst) = n; 910 break; 911 } 912 case VT_BOOL: { 913 BOOL b; 914 915 hres = to_boolean(val, &b); 916 if(SUCCEEDED(hres)) 917 V_BOOL(dst) = b ? VARIANT_TRUE : VARIANT_FALSE; 918 break; 919 } 920 case VT_BSTR: { 921 jsstr_t *str; 922 923 hres = to_string(ctx, val, &str); 924 if(FAILED(hres)) 925 break; 926 927 if(is_null_bstr(str)) { 928 V_BSTR(dst) = NULL; 929 break; 930 } 931 932 V_BSTR(dst) = SysAllocStringLen(NULL, jsstr_length(str)); 933 if(V_BSTR(dst)) 934 jsstr_flush(str, V_BSTR(dst)); 935 else 936 hres = E_OUTOFMEMORY; 937 break; 938 } 939 case VT_EMPTY: 940 hres = V_VT(src) == VT_EMPTY ? S_OK : E_NOTIMPL; 941 break; 942 case VT_NULL: 943 hres = V_VT(src) == VT_NULL ? S_OK : E_NOTIMPL; 944 break; 945 default: 946 FIXME("vt %d not implemented\n", vt); 947 hres = E_NOTIMPL; 948 } 949 950 jsval_release(val); 951 if(FAILED(hres)) 952 return hres; 953 954 V_VT(dst) = vt; 955 return S_OK; 956 } 957 958 static inline JSCaller *impl_from_IServiceProvider(IServiceProvider *iface) 959 { 960 return CONTAINING_RECORD(iface, JSCaller, IServiceProvider_iface); 961 } 962 963 static HRESULT WINAPI JSCaller_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv) 964 { 965 JSCaller *This = impl_from_IServiceProvider(iface); 966 967 if(IsEqualGUID(&IID_IUnknown, riid)) { 968 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); 969 *ppv = &This->IServiceProvider_iface; 970 }else if(IsEqualGUID(&IID_IServiceProvider, riid)) { 971 TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv); 972 *ppv = &This->IServiceProvider_iface; 973 }else { 974 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); 975 *ppv = NULL; 976 return E_NOINTERFACE; 977 } 978 979 IUnknown_AddRef((IUnknown*)*ppv); 980 return S_OK; 981 } 982 983 static ULONG WINAPI JSCaller_AddRef(IServiceProvider *iface) 984 { 985 JSCaller *This = impl_from_IServiceProvider(iface); 986 LONG ref = InterlockedIncrement(&This->ref); 987 988 TRACE("(%p) ref=%d\n", This, ref); 989 990 return ref; 991 } 992 993 static ULONG WINAPI JSCaller_Release(IServiceProvider *iface) 994 { 995 JSCaller *This = impl_from_IServiceProvider(iface); 996 LONG ref = InterlockedIncrement(&This->ref); 997 998 TRACE("(%p) ref=%d\n", This, ref); 999 1000 if(!ref) { 1001 assert(!This->ctx); 1002 heap_free(This); 1003 } 1004 1005 return ref; 1006 } 1007 1008 static HRESULT WINAPI JSCaller_QueryService(IServiceProvider *iface, REFGUID guidService, 1009 REFIID riid, void **ppv) 1010 { 1011 JSCaller *This = impl_from_IServiceProvider(iface); 1012 1013 if(IsEqualGUID(guidService, &SID_VariantConversion) && This->ctx && This->ctx->active_script) { 1014 TRACE("(%p)->(SID_VariantConversion)\n", This); 1015 return IActiveScript_QueryInterface(This->ctx->active_script, riid, ppv); 1016 } 1017 1018 FIXME("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv); 1019 1020 *ppv = NULL; 1021 return E_NOINTERFACE; 1022 } 1023 1024 static const IServiceProviderVtbl ServiceProviderVtbl = { 1025 JSCaller_QueryInterface, 1026 JSCaller_AddRef, 1027 JSCaller_Release, 1028 JSCaller_QueryService 1029 }; 1030 1031 HRESULT create_jscaller(script_ctx_t *ctx) 1032 { 1033 JSCaller *ret; 1034 1035 ret = heap_alloc(sizeof(*ret)); 1036 if(!ret) 1037 return E_OUTOFMEMORY; 1038 1039 ret->IServiceProvider_iface.lpVtbl = &ServiceProviderVtbl; 1040 ret->ref = 1; 1041 ret->ctx = ctx; 1042 1043 ctx->jscaller = ret; 1044 return S_OK; 1045 } 1046