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 <assert.h> 20 21 #include "jscript.h" 22 23 #include "wine/debug.h" 24 25 WINE_DEFAULT_DEBUG_CHANNEL(jscript); 26 27 #define FDEX_VERSION_MASK 0xf0000000 28 #define GOLDEN_RATIO 0x9E3779B9U 29 30 typedef enum { 31 PROP_JSVAL, 32 PROP_BUILTIN, 33 PROP_PROTREF, 34 PROP_ACCESSOR, 35 PROP_DELETED, 36 PROP_IDX 37 } prop_type_t; 38 39 struct _dispex_prop_t { 40 WCHAR *name; 41 unsigned hash; 42 prop_type_t type; 43 DWORD flags; 44 45 union { 46 jsval_t val; 47 const builtin_prop_t *p; 48 DWORD ref; 49 unsigned idx; 50 struct { 51 jsdisp_t *getter; 52 jsdisp_t *setter; 53 } accessor; 54 } u; 55 56 int bucket_head; 57 int bucket_next; 58 }; 59 60 static inline DISPID prop_to_id(jsdisp_t *This, dispex_prop_t *prop) 61 { 62 return prop - This->props; 63 } 64 65 static inline dispex_prop_t *get_prop(jsdisp_t *This, DISPID id) 66 { 67 if(id < 0 || id >= This->prop_cnt || This->props[id].type == PROP_DELETED) 68 return NULL; 69 70 return This->props+id; 71 } 72 73 static DWORD get_flags(jsdisp_t *This, dispex_prop_t *prop) 74 { 75 if(prop->type == PROP_PROTREF) { 76 dispex_prop_t *parent = get_prop(This->prototype, prop->u.ref); 77 if(!parent) { 78 prop->type = PROP_DELETED; 79 return 0; 80 } 81 82 return get_flags(This->prototype, parent); 83 } 84 85 return prop->flags; 86 } 87 88 static const builtin_prop_t *find_builtin_prop(jsdisp_t *This, const WCHAR *name) 89 { 90 int min = 0, max, i, r; 91 92 max = This->builtin_info->props_cnt-1; 93 while(min <= max) { 94 i = (min+max)/2; 95 96 r = wcscmp(name, This->builtin_info->props[i].name); 97 if(!r) { 98 /* Skip prop if it's available only in higher compatibility mode. */ 99 unsigned version = (This->builtin_info->props[i].flags & PROPF_VERSION_MASK) 100 >> PROPF_VERSION_SHIFT; 101 if(version && version > This->ctx->version) 102 return NULL; 103 104 /* Skip prop if it's available only in HTML mode and we're not running in HTML mode. */ 105 if((This->builtin_info->props[i].flags & PROPF_HTML) && !This->ctx->html_mode) 106 return NULL; 107 108 return This->builtin_info->props + i; 109 } 110 111 if(r < 0) 112 max = i-1; 113 else 114 min = i+1; 115 } 116 117 return NULL; 118 } 119 120 static inline unsigned string_hash(const WCHAR *name) 121 { 122 unsigned h = 0; 123 for(; *name; name++) 124 h = (h>>(sizeof(unsigned)*8-4)) ^ (h<<4) ^ towlower(*name); 125 return h; 126 } 127 128 static inline unsigned get_props_idx(jsdisp_t *This, unsigned hash) 129 { 130 return (hash*GOLDEN_RATIO) & (This->buf_size-1); 131 } 132 133 static inline HRESULT resize_props(jsdisp_t *This) 134 { 135 dispex_prop_t *props; 136 int i, bucket; 137 138 if(This->buf_size != This->prop_cnt) 139 return S_FALSE; 140 141 props = heap_realloc(This->props, sizeof(dispex_prop_t)*This->buf_size*2); 142 if(!props) 143 return E_OUTOFMEMORY; 144 This->buf_size *= 2; 145 This->props = props; 146 147 for(i=0; i<This->buf_size; i++) { 148 This->props[i].bucket_head = 0; 149 This->props[i].bucket_next = 0; 150 } 151 152 for(i=1; i<This->prop_cnt; i++) { 153 props = This->props+i; 154 155 bucket = get_props_idx(This, props->hash); 156 props->bucket_next = This->props[bucket].bucket_head; 157 This->props[bucket].bucket_head = i; 158 } 159 160 return S_OK; 161 } 162 163 static inline dispex_prop_t* alloc_prop(jsdisp_t *This, const WCHAR *name, prop_type_t type, DWORD flags) 164 { 165 dispex_prop_t *prop; 166 unsigned bucket; 167 168 if(FAILED(resize_props(This))) 169 return NULL; 170 171 prop = &This->props[This->prop_cnt]; 172 prop->name = heap_strdupW(name); 173 if(!prop->name) 174 return NULL; 175 prop->type = type; 176 prop->flags = flags; 177 prop->hash = string_hash(name); 178 179 bucket = get_props_idx(This, prop->hash); 180 prop->bucket_next = This->props[bucket].bucket_head; 181 This->props[bucket].bucket_head = This->prop_cnt++; 182 return prop; 183 } 184 185 static dispex_prop_t *alloc_protref(jsdisp_t *This, const WCHAR *name, DWORD ref) 186 { 187 dispex_prop_t *ret; 188 189 ret = alloc_prop(This, name, PROP_PROTREF, 0); 190 if(!ret) 191 return NULL; 192 193 ret->u.ref = ref; 194 return ret; 195 } 196 197 static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, dispex_prop_t **ret) 198 { 199 const builtin_prop_t *builtin; 200 unsigned bucket, pos, prev = 0; 201 dispex_prop_t *prop; 202 203 bucket = get_props_idx(This, hash); 204 pos = This->props[bucket].bucket_head; 205 while(pos != 0) { 206 if(!wcscmp(name, This->props[pos].name)) { 207 if(prev != 0) { 208 This->props[prev].bucket_next = This->props[pos].bucket_next; 209 This->props[pos].bucket_next = This->props[bucket].bucket_head; 210 This->props[bucket].bucket_head = pos; 211 } 212 213 *ret = &This->props[pos]; 214 return S_OK; 215 } 216 217 prev = pos; 218 pos = This->props[pos].bucket_next; 219 } 220 221 builtin = find_builtin_prop(This, name); 222 if(builtin) { 223 unsigned flags = builtin->flags; 224 if(flags & PROPF_METHOD) 225 flags |= PROPF_WRITABLE | PROPF_CONFIGURABLE; 226 else if(builtin->setter) 227 flags |= PROPF_WRITABLE; 228 flags &= PROPF_ENUMERABLE | PROPF_WRITABLE | PROPF_CONFIGURABLE; 229 prop = alloc_prop(This, name, PROP_BUILTIN, flags); 230 if(!prop) 231 return E_OUTOFMEMORY; 232 233 prop->u.p = builtin; 234 *ret = prop; 235 return S_OK; 236 } 237 238 if(This->builtin_info->idx_length) { 239 const WCHAR *ptr; 240 unsigned idx = 0; 241 242 for(ptr = name; iswdigit(*ptr) && idx < 0x10000; ptr++) 243 idx = idx*10 + (*ptr-'0'); 244 if(!*ptr && idx < This->builtin_info->idx_length(This)) { 245 prop = alloc_prop(This, name, PROP_IDX, This->builtin_info->idx_put ? PROPF_WRITABLE : 0); 246 if(!prop) 247 return E_OUTOFMEMORY; 248 249 prop->u.idx = idx; 250 *ret = prop; 251 return S_OK; 252 } 253 } 254 255 *ret = NULL; 256 return S_OK; 257 } 258 259 static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *name, dispex_prop_t **ret) 260 { 261 dispex_prop_t *prop, *del=NULL; 262 HRESULT hres; 263 264 hres = find_prop_name(This, hash, name, &prop); 265 if(FAILED(hres)) 266 return hres; 267 if(prop && prop->type==PROP_DELETED) { 268 del = prop; 269 } else if(prop) { 270 *ret = prop; 271 return S_OK; 272 } 273 274 if(This->prototype) { 275 hres = find_prop_name_prot(This->prototype, hash, name, &prop); 276 if(FAILED(hres)) 277 return hres; 278 if(prop) { 279 if(del) { 280 del->type = PROP_PROTREF; 281 del->u.ref = prop - This->prototype->props; 282 prop = del; 283 }else { 284 prop = alloc_protref(This, prop->name, prop - This->prototype->props); 285 if(!prop) 286 return E_OUTOFMEMORY; 287 } 288 289 *ret = prop; 290 return S_OK; 291 } 292 } 293 294 *ret = del; 295 return S_OK; 296 } 297 298 static HRESULT ensure_prop_name(jsdisp_t *This, const WCHAR *name, DWORD create_flags, dispex_prop_t **ret) 299 { 300 dispex_prop_t *prop; 301 HRESULT hres; 302 303 hres = find_prop_name_prot(This, string_hash(name), name, &prop); 304 if(SUCCEEDED(hres) && (!prop || prop->type == PROP_DELETED)) { 305 TRACE("creating prop %s flags %x\n", debugstr_w(name), create_flags); 306 307 if(prop) { 308 prop->type = PROP_JSVAL; 309 prop->flags = create_flags; 310 prop->u.val = jsval_undefined(); 311 }else { 312 prop = alloc_prop(This, name, PROP_JSVAL, create_flags); 313 if(!prop) 314 return E_OUTOFMEMORY; 315 } 316 317 prop->u.val = jsval_undefined(); 318 } 319 320 *ret = prop; 321 return hres; 322 } 323 324 static IDispatch *get_this(DISPPARAMS *dp) 325 { 326 DWORD i; 327 328 for(i=0; i < dp->cNamedArgs; i++) { 329 if(dp->rgdispidNamedArgs[i] == DISPID_THIS) { 330 if(V_VT(dp->rgvarg+i) == VT_DISPATCH) 331 return V_DISPATCH(dp->rgvarg+i); 332 333 WARN("This is not VT_DISPATCH\n"); 334 return NULL; 335 } 336 } 337 338 TRACE("no this passed\n"); 339 return NULL; 340 } 341 342 static HRESULT convert_params(const DISPPARAMS *dp, jsval_t *buf, unsigned *argc, jsval_t **ret) 343 { 344 jsval_t *argv; 345 unsigned cnt; 346 unsigned i; 347 HRESULT hres; 348 349 cnt = dp->cArgs - dp->cNamedArgs; 350 351 if(cnt > 6) { 352 argv = heap_alloc(cnt * sizeof(*argv)); 353 if(!argv) 354 return E_OUTOFMEMORY; 355 }else { 356 argv = buf; 357 } 358 359 for(i = 0; i < cnt; i++) { 360 hres = variant_to_jsval(dp->rgvarg+dp->cArgs-i-1, argv+i); 361 if(FAILED(hres)) { 362 while(i--) 363 jsval_release(argv[i]); 364 if(argv != buf) 365 heap_free(argv); 366 return hres; 367 } 368 } 369 370 *argc = cnt; 371 *ret = argv; 372 return S_OK; 373 } 374 375 static HRESULT invoke_prop_func(jsdisp_t *This, IDispatch *jsthis, dispex_prop_t *prop, WORD flags, 376 unsigned argc, jsval_t *argv, jsval_t *r, IServiceProvider *caller) 377 { 378 HRESULT hres; 379 380 switch(prop->type) { 381 case PROP_BUILTIN: { 382 if(flags == DISPATCH_CONSTRUCT && (prop->flags & PROPF_METHOD)) { 383 WARN("%s is not a constructor\n", debugstr_w(prop->name)); 384 return E_INVALIDARG; 385 } 386 387 if(prop->name || This->builtin_info->class != JSCLASS_FUNCTION) { 388 vdisp_t vthis; 389 390 if(This->builtin_info->class != JSCLASS_FUNCTION && prop->u.p->invoke != JSGlobal_eval) 391 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK; 392 if(jsthis) 393 set_disp(&vthis, jsthis); 394 else 395 set_jsdisp(&vthis, This); 396 hres = prop->u.p->invoke(This->ctx, &vthis, flags, argc, argv, r); 397 vdisp_release(&vthis); 398 }else { 399 /* Function object calls are special case */ 400 hres = Function_invoke(This, jsthis, flags, argc, argv, r); 401 } 402 return hres; 403 } 404 case PROP_PROTREF: 405 return invoke_prop_func(This->prototype, jsthis, This->prototype->props+prop->u.ref, 406 flags, argc, argv, r, caller); 407 case PROP_JSVAL: { 408 if(!is_object_instance(prop->u.val)) { 409 FIXME("invoke %s\n", debugstr_jsval(prop->u.val)); 410 return E_FAIL; 411 } 412 413 TRACE("call %s %p\n", debugstr_w(prop->name), get_object(prop->u.val)); 414 415 return disp_call_value(This->ctx, get_object(prop->u.val), jsthis, flags, argc, argv, r); 416 } 417 case PROP_ACCESSOR: 418 FIXME("accessor\n"); 419 return E_NOTIMPL; 420 case PROP_IDX: 421 FIXME("Invoking PROP_IDX not yet supported\n"); 422 return E_NOTIMPL; 423 case PROP_DELETED: 424 assert(0); 425 } 426 427 assert(0); 428 return E_FAIL; 429 } 430 431 static HRESULT prop_get(jsdisp_t *This, dispex_prop_t *prop, jsval_t *r) 432 { 433 jsdisp_t *prop_obj = This; 434 HRESULT hres; 435 436 while(prop->type == PROP_PROTREF) { 437 prop_obj = prop_obj->prototype; 438 prop = prop_obj->props + prop->u.ref; 439 } 440 441 switch(prop->type) { 442 case PROP_BUILTIN: 443 if(prop->u.p->getter) { 444 hres = prop->u.p->getter(This->ctx, This, r); 445 }else { 446 jsdisp_t *obj; 447 448 assert(prop->u.p->invoke != NULL); 449 hres = create_builtin_function(This->ctx, prop->u.p->invoke, prop->u.p->name, NULL, 450 prop->u.p->flags, NULL, &obj); 451 if(FAILED(hres)) 452 break; 453 454 prop->type = PROP_JSVAL; 455 prop->u.val = jsval_obj(obj); 456 457 jsdisp_addref(obj); 458 *r = jsval_obj(obj); 459 } 460 break; 461 case PROP_JSVAL: 462 hres = jsval_copy(prop->u.val, r); 463 break; 464 case PROP_ACCESSOR: 465 if(prop->u.accessor.getter) { 466 hres = jsdisp_call_value(prop->u.accessor.getter, to_disp(This), 467 DISPATCH_METHOD, 0, NULL, r); 468 }else { 469 *r = jsval_undefined(); 470 hres = S_OK; 471 } 472 break; 473 case PROP_IDX: 474 hres = prop_obj->builtin_info->idx_get(prop_obj, prop->u.idx, r); 475 break; 476 default: 477 ERR("type %d\n", prop->type); 478 return E_FAIL; 479 } 480 481 if(FAILED(hres)) { 482 TRACE("fail %08x\n", hres); 483 return hres; 484 } 485 486 TRACE("%s ret %s\n", debugstr_w(prop->name), debugstr_jsval(*r)); 487 return hres; 488 } 489 490 static HRESULT prop_put(jsdisp_t *This, dispex_prop_t *prop, jsval_t val) 491 { 492 HRESULT hres; 493 494 if(prop->type == PROP_PROTREF) { 495 dispex_prop_t *prop_iter = prop; 496 jsdisp_t *prototype_iter = This; 497 498 do { 499 prototype_iter = prototype_iter->prototype; 500 prop_iter = prototype_iter->props + prop_iter->u.ref; 501 } while(prop_iter->type == PROP_PROTREF); 502 503 if(prop_iter->type == PROP_ACCESSOR) 504 prop = prop_iter; 505 } 506 507 switch(prop->type) { 508 case PROP_BUILTIN: 509 if(!prop->u.p->setter) { 510 TRACE("getter with no setter\n"); 511 return S_OK; 512 } 513 return prop->u.p->setter(This->ctx, This, val); 514 case PROP_PROTREF: 515 case PROP_DELETED: 516 prop->type = PROP_JSVAL; 517 prop->flags = PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE; 518 prop->u.val = jsval_undefined(); 519 break; 520 case PROP_JSVAL: 521 if(!(prop->flags & PROPF_WRITABLE)) 522 return S_OK; 523 524 jsval_release(prop->u.val); 525 break; 526 case PROP_ACCESSOR: 527 if(!prop->u.accessor.setter) { 528 TRACE("no setter\n"); 529 return S_OK; 530 } 531 return jsdisp_call_value(prop->u.accessor.setter, to_disp(This), DISPATCH_METHOD, 1, &val, NULL); 532 case PROP_IDX: 533 if(!This->builtin_info->idx_put) { 534 TRACE("no put_idx\n"); 535 return S_OK; 536 } 537 return This->builtin_info->idx_put(This, prop->u.idx, val); 538 default: 539 ERR("type %d\n", prop->type); 540 return E_FAIL; 541 } 542 543 TRACE("%s = %s\n", debugstr_w(prop->name), debugstr_jsval(val)); 544 545 hres = jsval_copy(val, &prop->u.val); 546 if(FAILED(hres)) 547 return hres; 548 549 if(This->builtin_info->on_put) 550 This->builtin_info->on_put(This, prop->name); 551 552 return S_OK; 553 } 554 555 HRESULT builtin_set_const(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value) 556 { 557 TRACE("%p %s\n", jsthis, debugstr_jsval(value)); 558 return S_OK; 559 } 560 561 static HRESULT fill_protrefs(jsdisp_t *This) 562 { 563 dispex_prop_t *iter, *prop; 564 HRESULT hres; 565 566 if(!This->prototype) 567 return S_OK; 568 569 fill_protrefs(This->prototype); 570 571 for(iter = This->prototype->props; iter < This->prototype->props+This->prototype->prop_cnt; iter++) { 572 if(!iter->name) 573 continue; 574 hres = find_prop_name(This, iter->hash, iter->name, &prop); 575 if(FAILED(hres)) 576 return hres; 577 if(!prop || prop->type==PROP_DELETED) { 578 if(prop) { 579 prop->type = PROP_PROTREF; 580 prop->flags = 0; 581 prop->u.ref = iter - This->prototype->props; 582 }else { 583 prop = alloc_protref(This, iter->name, iter - This->prototype->props); 584 if(!prop) 585 return E_OUTOFMEMORY; 586 } 587 } 588 } 589 590 return S_OK; 591 } 592 593 static inline jsdisp_t *impl_from_IDispatchEx(IDispatchEx *iface) 594 { 595 return CONTAINING_RECORD(iface, jsdisp_t, IDispatchEx_iface); 596 } 597 598 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv) 599 { 600 jsdisp_t *This = impl_from_IDispatchEx(iface); 601 602 if(IsEqualGUID(&IID_IUnknown, riid)) { 603 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); 604 *ppv = &This->IDispatchEx_iface; 605 }else if(IsEqualGUID(&IID_IDispatch, riid)) { 606 TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv); 607 *ppv = &This->IDispatchEx_iface; 608 }else if(IsEqualGUID(&IID_IDispatchEx, riid)) { 609 TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv); 610 *ppv = &This->IDispatchEx_iface; 611 }else { 612 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); 613 *ppv = NULL; 614 return E_NOINTERFACE; 615 } 616 617 jsdisp_addref(This); 618 return S_OK; 619 } 620 621 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface) 622 { 623 jsdisp_t *This = impl_from_IDispatchEx(iface); 624 jsdisp_addref(This); 625 return This->ref; 626 } 627 628 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface) 629 { 630 jsdisp_t *This = impl_from_IDispatchEx(iface); 631 ULONG ref = --This->ref; 632 TRACE("(%p) ref=%d\n", This, ref); 633 if(!ref) 634 jsdisp_free(This); 635 return ref; 636 } 637 638 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo) 639 { 640 jsdisp_t *This = impl_from_IDispatchEx(iface); 641 642 TRACE("(%p)->(%p)\n", This, pctinfo); 643 644 *pctinfo = 1; 645 return S_OK; 646 } 647 648 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid, 649 ITypeInfo **ppTInfo) 650 { 651 jsdisp_t *This = impl_from_IDispatchEx(iface); 652 FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); 653 return E_NOTIMPL; 654 } 655 656 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid, 657 LPOLESTR *rgszNames, UINT cNames, LCID lcid, 658 DISPID *rgDispId) 659 { 660 jsdisp_t *This = impl_from_IDispatchEx(iface); 661 UINT i; 662 HRESULT hres; 663 664 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, 665 lcid, rgDispId); 666 667 for(i=0; i < cNames; i++) { 668 hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[i], 0, rgDispId+i); 669 if(FAILED(hres)) 670 return hres; 671 } 672 673 return S_OK; 674 } 675 676 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember, 677 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, 678 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) 679 { 680 jsdisp_t *This = impl_from_IDispatchEx(iface); 681 682 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), 683 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); 684 685 return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags, 686 pDispParams, pVarResult, pExcepInfo, NULL); 687 } 688 689 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid) 690 { 691 jsdisp_t *This = impl_from_IDispatchEx(iface); 692 693 TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid); 694 695 if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) { 696 FIXME("Unsupported grfdex %x\n", grfdex); 697 return E_NOTIMPL; 698 } 699 700 return jsdisp_get_id(This, bstrName, grfdex, pid); 701 } 702 703 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, 704 VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) 705 { 706 jsdisp_t *This = impl_from_IDispatchEx(iface); 707 dispex_prop_t *prop; 708 HRESULT hres; 709 710 TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller); 711 712 if(pvarRes) 713 V_VT(pvarRes) = VT_EMPTY; 714 715 prop = get_prop(This, id); 716 if(!prop || prop->type == PROP_DELETED) { 717 TRACE("invalid id\n"); 718 return DISP_E_MEMBERNOTFOUND; 719 } 720 721 clear_ei(This->ctx); 722 723 switch(wFlags) { 724 case DISPATCH_METHOD|DISPATCH_PROPERTYGET: 725 wFlags = DISPATCH_METHOD; 726 /* fall through */ 727 case DISPATCH_METHOD: 728 case DISPATCH_CONSTRUCT: { 729 jsval_t *argv, buf[6], r; 730 unsigned argc; 731 732 hres = convert_params(pdp, buf, &argc, &argv); 733 if(FAILED(hres)) 734 return hres; 735 736 hres = invoke_prop_func(This, get_this(pdp), prop, wFlags, argc, argv, pvarRes ? &r : NULL, pspCaller); 737 if(argv != buf) 738 heap_free(argv); 739 if(SUCCEEDED(hres) && pvarRes) { 740 hres = jsval_to_variant(r, pvarRes); 741 jsval_release(r); 742 } 743 break; 744 } 745 case DISPATCH_PROPERTYGET: { 746 jsval_t r; 747 748 hres = prop_get(This, prop, &r); 749 if(SUCCEEDED(hres)) { 750 hres = jsval_to_variant(r, pvarRes); 751 jsval_release(r); 752 } 753 break; 754 } 755 case DISPATCH_PROPERTYPUT: { 756 jsval_t val; 757 DWORD i; 758 759 for(i=0; i < pdp->cNamedArgs; i++) { 760 if(pdp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT) 761 break; 762 } 763 764 if(i == pdp->cNamedArgs) { 765 TRACE("no value to set\n"); 766 return DISP_E_PARAMNOTOPTIONAL; 767 } 768 769 hres = variant_to_jsval(pdp->rgvarg+i, &val); 770 if(FAILED(hres)) 771 return hres; 772 773 hres = prop_put(This, prop, val); 774 jsval_release(val); 775 break; 776 } 777 default: 778 FIXME("Unimplemented flags %x\n", wFlags); 779 return E_INVALIDARG; 780 } 781 782 if(pei) 783 *pei = This->ctx->ei.ei; 784 return hres; 785 } 786 787 static HRESULT delete_prop(dispex_prop_t *prop, BOOL *ret) 788 { 789 if(!(prop->flags & PROPF_CONFIGURABLE)) { 790 *ret = FALSE; 791 return S_OK; 792 } 793 794 *ret = TRUE; /* FIXME: not exactly right */ 795 796 if(prop->type == PROP_JSVAL) { 797 jsval_release(prop->u.val); 798 prop->type = PROP_DELETED; 799 } 800 if(prop->type == PROP_ACCESSOR) 801 FIXME("not supported on accessor property\n"); 802 return S_OK; 803 } 804 805 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex) 806 { 807 jsdisp_t *This = impl_from_IDispatchEx(iface); 808 dispex_prop_t *prop; 809 BOOL b; 810 HRESULT hres; 811 812 TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex); 813 814 if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) 815 FIXME("Unsupported grfdex %x\n", grfdex); 816 817 hres = find_prop_name(This, string_hash(bstrName), bstrName, &prop); 818 if(FAILED(hres)) 819 return hres; 820 if(!prop) { 821 TRACE("not found\n"); 822 return S_OK; 823 } 824 825 return delete_prop(prop, &b); 826 } 827 828 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id) 829 { 830 jsdisp_t *This = impl_from_IDispatchEx(iface); 831 dispex_prop_t *prop; 832 BOOL b; 833 834 TRACE("(%p)->(%x)\n", This, id); 835 836 prop = get_prop(This, id); 837 if(!prop) { 838 WARN("invalid id\n"); 839 return DISP_E_MEMBERNOTFOUND; 840 } 841 842 return delete_prop(prop, &b); 843 } 844 845 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex) 846 { 847 jsdisp_t *This = impl_from_IDispatchEx(iface); 848 FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex); 849 return E_NOTIMPL; 850 } 851 852 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName) 853 { 854 jsdisp_t *This = impl_from_IDispatchEx(iface); 855 dispex_prop_t *prop; 856 857 TRACE("(%p)->(%x %p)\n", This, id, pbstrName); 858 859 prop = get_prop(This, id); 860 if(!prop || !prop->name || prop->type == PROP_DELETED) 861 return DISP_E_MEMBERNOTFOUND; 862 863 *pbstrName = SysAllocString(prop->name); 864 if(!*pbstrName) 865 return E_OUTOFMEMORY; 866 867 return S_OK; 868 } 869 870 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid) 871 { 872 jsdisp_t *This = impl_from_IDispatchEx(iface); 873 HRESULT hres; 874 875 TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid); 876 877 hres = jsdisp_next_prop(This, id, FALSE, pid); 878 if(hres == S_FALSE) 879 *pid = DISPID_STARTENUM; 880 return hres; 881 } 882 883 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk) 884 { 885 jsdisp_t *This = impl_from_IDispatchEx(iface); 886 FIXME("(%p)->(%p)\n", This, ppunk); 887 return E_NOTIMPL; 888 } 889 890 static IDispatchExVtbl DispatchExVtbl = { 891 DispatchEx_QueryInterface, 892 DispatchEx_AddRef, 893 DispatchEx_Release, 894 DispatchEx_GetTypeInfoCount, 895 DispatchEx_GetTypeInfo, 896 DispatchEx_GetIDsOfNames, 897 DispatchEx_Invoke, 898 DispatchEx_GetDispID, 899 DispatchEx_InvokeEx, 900 DispatchEx_DeleteMemberByName, 901 DispatchEx_DeleteMemberByDispID, 902 DispatchEx_GetMemberProperties, 903 DispatchEx_GetMemberName, 904 DispatchEx_GetNextDispID, 905 DispatchEx_GetNameSpaceParent 906 }; 907 908 jsdisp_t *as_jsdisp(IDispatch *disp) 909 { 910 assert(disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl); 911 return impl_from_IDispatchEx((IDispatchEx*)disp); 912 } 913 914 jsdisp_t *to_jsdisp(IDispatch *disp) 915 { 916 return disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl ? impl_from_IDispatchEx((IDispatchEx*)disp) : NULL; 917 } 918 919 HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype) 920 { 921 TRACE("%p (%p)\n", dispex, prototype); 922 923 dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl; 924 dispex->ref = 1; 925 dispex->builtin_info = builtin_info; 926 927 dispex->props = heap_alloc_zero(sizeof(dispex_prop_t)*(dispex->buf_size=4)); 928 if(!dispex->props) 929 return E_OUTOFMEMORY; 930 931 dispex->prototype = prototype; 932 if(prototype) 933 jsdisp_addref(prototype); 934 935 dispex->prop_cnt = 1; 936 if(builtin_info->value_prop.invoke || builtin_info->value_prop.getter) { 937 dispex->props[0].type = PROP_BUILTIN; 938 dispex->props[0].u.p = &builtin_info->value_prop; 939 }else { 940 dispex->props[0].type = PROP_DELETED; 941 } 942 943 script_addref(ctx); 944 dispex->ctx = ctx; 945 946 return S_OK; 947 } 948 949 static const builtin_info_t dispex_info = { 950 JSCLASS_NONE, 951 {NULL, NULL, 0}, 952 0, NULL, 953 NULL, 954 NULL 955 }; 956 957 HRESULT create_dispex(script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype, jsdisp_t **dispex) 958 { 959 jsdisp_t *ret; 960 HRESULT hres; 961 962 ret = heap_alloc_zero(sizeof(jsdisp_t)); 963 if(!ret) 964 return E_OUTOFMEMORY; 965 966 hres = init_dispex(ret, ctx, builtin_info ? builtin_info : &dispex_info, prototype); 967 if(FAILED(hres)) { 968 heap_free(ret); 969 return hres; 970 } 971 972 *dispex = ret; 973 return S_OK; 974 } 975 976 void jsdisp_free(jsdisp_t *obj) 977 { 978 dispex_prop_t *prop; 979 980 TRACE("(%p)\n", obj); 981 982 for(prop = obj->props; prop < obj->props+obj->prop_cnt; prop++) { 983 switch(prop->type) { 984 case PROP_JSVAL: 985 jsval_release(prop->u.val); 986 break; 987 case PROP_ACCESSOR: 988 if(prop->u.accessor.getter) 989 jsdisp_release(prop->u.accessor.getter); 990 if(prop->u.accessor.setter) 991 jsdisp_release(prop->u.accessor.setter); 992 break; 993 default: 994 break; 995 }; 996 heap_free(prop->name); 997 } 998 heap_free(obj->props); 999 script_release(obj->ctx); 1000 if(obj->prototype) 1001 jsdisp_release(obj->prototype); 1002 1003 if(obj->builtin_info->destructor) 1004 obj->builtin_info->destructor(obj); 1005 else 1006 heap_free(obj); 1007 } 1008 1009 #ifdef TRACE_REFCNT 1010 1011 jsdisp_t *jsdisp_addref(jsdisp_t *jsdisp) 1012 { 1013 ULONG ref = ++jsdisp->ref; 1014 TRACE("(%p) ref=%d\n", jsdisp, ref); 1015 return jsdisp; 1016 } 1017 1018 void jsdisp_release(jsdisp_t *jsdisp) 1019 { 1020 ULONG ref = --jsdisp->ref; 1021 1022 TRACE("(%p) ref=%d\n", jsdisp, ref); 1023 1024 if(!ref) 1025 jsdisp_free(jsdisp); 1026 } 1027 1028 #endif 1029 1030 HRESULT init_dispex_from_constr(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *constr) 1031 { 1032 jsdisp_t *prot = NULL; 1033 dispex_prop_t *prop; 1034 HRESULT hres; 1035 1036 static const WCHAR prototypeW[] = {'p','r','o','t','o','t','y','p','e',0}; 1037 1038 hres = find_prop_name_prot(constr, string_hash(prototypeW), prototypeW, &prop); 1039 if(SUCCEEDED(hres) && prop && prop->type!=PROP_DELETED) { 1040 jsval_t val; 1041 1042 hres = prop_get(constr, prop, &val); 1043 if(FAILED(hres)) { 1044 ERR("Could not get prototype\n"); 1045 return hres; 1046 } 1047 1048 if(is_object_instance(val)) 1049 prot = iface_to_jsdisp(get_object(val)); 1050 jsval_release(val); 1051 } 1052 1053 hres = init_dispex(dispex, ctx, builtin_info, prot); 1054 1055 if(prot) 1056 jsdisp_release(prot); 1057 return hres; 1058 } 1059 1060 jsdisp_t *iface_to_jsdisp(IDispatch *iface) 1061 { 1062 return iface->lpVtbl == (const IDispatchVtbl*)&DispatchExVtbl 1063 ? jsdisp_addref( impl_from_IDispatchEx((IDispatchEx*)iface)) 1064 : NULL; 1065 } 1066 1067 HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID *id) 1068 { 1069 dispex_prop_t *prop; 1070 HRESULT hres; 1071 1072 if(flags & fdexNameEnsure) 1073 hres = ensure_prop_name(jsdisp, name, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE, 1074 &prop); 1075 else 1076 hres = find_prop_name_prot(jsdisp, string_hash(name), name, &prop); 1077 if(FAILED(hres)) 1078 return hres; 1079 1080 if(prop && prop->type!=PROP_DELETED) { 1081 *id = prop_to_id(jsdisp, prop); 1082 return S_OK; 1083 } 1084 1085 TRACE("not found %s\n", debugstr_w(name)); 1086 return DISP_E_UNKNOWNNAME; 1087 } 1088 1089 HRESULT jsdisp_call_value(jsdisp_t *jsfunc, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) 1090 { 1091 HRESULT hres; 1092 1093 assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK))); 1094 1095 if(is_class(jsfunc, JSCLASS_FUNCTION)) { 1096 hres = Function_invoke(jsfunc, jsthis, flags, argc, argv, r); 1097 }else { 1098 vdisp_t vdisp; 1099 1100 if(!jsfunc->builtin_info->value_prop.invoke) { 1101 WARN("Not a function\n"); 1102 return throw_type_error(jsfunc->ctx, JS_E_FUNCTION_EXPECTED, NULL); 1103 } 1104 1105 set_disp(&vdisp, jsthis); 1106 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK; 1107 hres = jsfunc->builtin_info->value_prop.invoke(jsfunc->ctx, &vdisp, flags, argc, argv, r); 1108 vdisp_release(&vdisp); 1109 } 1110 return hres; 1111 } 1112 1113 HRESULT jsdisp_call(jsdisp_t *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) 1114 { 1115 dispex_prop_t *prop; 1116 1117 prop = get_prop(disp, id); 1118 if(!prop) 1119 return DISP_E_MEMBERNOTFOUND; 1120 1121 return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, NULL); 1122 } 1123 1124 HRESULT jsdisp_call_name(jsdisp_t *disp, const WCHAR *name, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) 1125 { 1126 dispex_prop_t *prop; 1127 HRESULT hres; 1128 1129 hres = find_prop_name_prot(disp, string_hash(name), name, &prop); 1130 if(FAILED(hres)) 1131 return hres; 1132 1133 return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, NULL); 1134 } 1135 1136 HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *ret) 1137 { 1138 IDispatchEx *dispex; 1139 jsdisp_t *jsdisp; 1140 VARIANT buf[6], retv; 1141 DISPPARAMS dp; 1142 unsigned i; 1143 HRESULT hres; 1144 1145 jsdisp = iface_to_jsdisp(disp); 1146 if(jsdisp) { 1147 if(flags & DISPATCH_PROPERTYPUT) { 1148 FIXME("disp_call(propput) on builtin object\n"); 1149 return E_FAIL; 1150 } 1151 1152 if(ctx != jsdisp->ctx) 1153 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK; 1154 hres = jsdisp_call(jsdisp, id, flags, argc, argv, ret); 1155 jsdisp_release(jsdisp); 1156 return hres; 1157 } 1158 1159 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK; 1160 if(ret && argc) 1161 flags |= DISPATCH_PROPERTYGET; 1162 1163 dp.cArgs = argc; 1164 1165 if(flags & DISPATCH_PROPERTYPUT) { 1166 static DISPID propput_dispid = DISPID_PROPERTYPUT; 1167 1168 dp.cNamedArgs = 1; 1169 dp.rgdispidNamedArgs = &propput_dispid; 1170 }else { 1171 dp.cNamedArgs = 0; 1172 dp.rgdispidNamedArgs = NULL; 1173 } 1174 1175 if(argc > 6) { 1176 dp.rgvarg = heap_alloc(argc*sizeof(VARIANT)); 1177 if(!dp.rgvarg) 1178 return E_OUTOFMEMORY; 1179 }else { 1180 dp.rgvarg = buf; 1181 } 1182 1183 for(i=0; i<argc; i++) { 1184 hres = jsval_to_variant(argv[i], dp.rgvarg+argc-i-1); 1185 if(FAILED(hres)) { 1186 while(i--) 1187 VariantClear(dp.rgvarg+argc-i-1); 1188 if(dp.rgvarg != buf) 1189 heap_free(dp.rgvarg); 1190 return hres; 1191 } 1192 } 1193 1194 V_VT(&retv) = VT_EMPTY; 1195 clear_ei(ctx); 1196 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); 1197 if(SUCCEEDED(hres)) { 1198 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, &dp, ret ? &retv : NULL, &ctx->ei.ei, 1199 &ctx->jscaller->IServiceProvider_iface); 1200 IDispatchEx_Release(dispex); 1201 }else { 1202 UINT err = 0; 1203 1204 if(flags == DISPATCH_CONSTRUCT) { 1205 WARN("IDispatch cannot be constructor\n"); 1206 return DISP_E_MEMBERNOTFOUND; 1207 } 1208 1209 TRACE("using IDispatch\n"); 1210 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, &dp, ret ? &retv : NULL, &ctx->ei.ei, &err); 1211 } 1212 1213 for(i=0; i<argc; i++) 1214 VariantClear(dp.rgvarg+argc-i-1); 1215 if(dp.rgvarg != buf) 1216 heap_free(dp.rgvarg); 1217 if(FAILED(hres)) 1218 return hres; 1219 1220 if(ret) { 1221 hres = variant_to_jsval(&retv, ret); 1222 VariantClear(&retv); 1223 } 1224 1225 return hres; 1226 } 1227 1228 HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1229 jsval_t *r) 1230 { 1231 jsdisp_t *jsdisp; 1232 IDispatchEx *dispex; 1233 VARIANT buf[6], retv; 1234 DISPPARAMS dp; 1235 unsigned i; 1236 HRESULT hres; 1237 1238 assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK))); 1239 1240 jsdisp = iface_to_jsdisp(disp); 1241 if(jsdisp) { 1242 if(ctx != jsdisp->ctx) 1243 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK; 1244 hres = jsdisp_call_value(jsdisp, jsthis, flags, argc, argv, r); 1245 jsdisp_release(jsdisp); 1246 return hres; 1247 } 1248 1249 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK; 1250 if(r && argc && flags == DISPATCH_METHOD) 1251 flags |= DISPATCH_PROPERTYGET; 1252 1253 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); 1254 if(FAILED(hres)) { 1255 TRACE("using IDispatch\n"); 1256 dispex = NULL; 1257 jsthis = NULL; 1258 } 1259 1260 if(jsthis) { 1261 static DISPID this_id = DISPID_THIS; 1262 1263 dp.cArgs = argc+1; 1264 dp.cNamedArgs = 1; 1265 dp.rgdispidNamedArgs = &this_id; 1266 }else { 1267 dp.cArgs = argc; 1268 dp.cNamedArgs = 0; 1269 dp.rgdispidNamedArgs = NULL; 1270 } 1271 1272 if(dp.cArgs > ARRAY_SIZE(buf)) { 1273 dp.rgvarg = heap_alloc(dp.cArgs*sizeof(VARIANT)); 1274 if(!dp.rgvarg) { 1275 if(dispex) 1276 IDispatchEx_Release(dispex); 1277 return E_OUTOFMEMORY; 1278 } 1279 }else { 1280 dp.rgvarg = buf; 1281 } 1282 1283 for(i=0; i<argc; i++) { 1284 hres = jsval_to_variant(argv[i], dp.rgvarg+dp.cArgs-i-1); 1285 if(FAILED(hres)) { 1286 while(i--) 1287 VariantClear(dp.rgvarg+dp.cArgs-i-1); 1288 if(dp.rgvarg != buf) 1289 heap_free(dp.rgvarg); 1290 if(dispex) 1291 IDispatchEx_Release(dispex); 1292 return hres; 1293 } 1294 } 1295 if(jsthis) { 1296 V_VT(dp.rgvarg) = VT_DISPATCH; 1297 V_DISPATCH(dp.rgvarg) = jsthis; 1298 } 1299 1300 V_VT(&retv) = VT_EMPTY; 1301 clear_ei(ctx); 1302 if(dispex) { 1303 hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, ctx->lcid, flags, &dp, r ? &retv : NULL, &ctx->ei.ei, 1304 &ctx->jscaller->IServiceProvider_iface); 1305 IDispatchEx_Release(dispex); 1306 }else { 1307 UINT err = 0; 1308 1309 if(flags == DISPATCH_CONSTRUCT) { 1310 WARN("IDispatch cannot be constructor\n"); 1311 return DISP_E_MEMBERNOTFOUND; 1312 } 1313 1314 hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, ctx->lcid, flags, &dp, r ? &retv : NULL, &ctx->ei.ei, &err); 1315 } 1316 1317 for(i=0; i<argc; i++) 1318 VariantClear(dp.rgvarg+dp.cArgs-i-1); 1319 if(dp.rgvarg != buf) 1320 heap_free(dp.rgvarg); 1321 if(FAILED(hres)) 1322 return hres; 1323 1324 if(!r) 1325 return S_OK; 1326 1327 hres = variant_to_jsval(&retv, r); 1328 VariantClear(&retv); 1329 return hres; 1330 } 1331 1332 HRESULT jsdisp_propput(jsdisp_t *obj, const WCHAR *name, DWORD flags, jsval_t val) 1333 { 1334 dispex_prop_t *prop; 1335 HRESULT hres; 1336 1337 hres = ensure_prop_name(obj, name, flags, &prop); 1338 if(FAILED(hres)) 1339 return hres; 1340 1341 return prop_put(obj, prop, val); 1342 } 1343 1344 HRESULT jsdisp_propput_name(jsdisp_t *obj, const WCHAR *name, jsval_t val) 1345 { 1346 return jsdisp_propput(obj, name, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE, val); 1347 } 1348 1349 HRESULT jsdisp_propput_idx(jsdisp_t *obj, DWORD idx, jsval_t val) 1350 { 1351 WCHAR buf[12]; 1352 1353 static const WCHAR formatW[] = {'%','d',0}; 1354 1355 swprintf(buf, formatW, idx); 1356 return jsdisp_propput_name(obj, buf, val); 1357 } 1358 1359 HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t val) 1360 { 1361 jsdisp_t *jsdisp; 1362 HRESULT hres; 1363 1364 jsdisp = iface_to_jsdisp(disp); 1365 if(jsdisp) { 1366 dispex_prop_t *prop; 1367 1368 prop = get_prop(jsdisp, id); 1369 if(prop) 1370 hres = prop_put(jsdisp, prop, val); 1371 else 1372 hres = DISP_E_MEMBERNOTFOUND; 1373 1374 jsdisp_release(jsdisp); 1375 }else { 1376 DISPID dispid = DISPID_PROPERTYPUT; 1377 DWORD flags = DISPATCH_PROPERTYPUT; 1378 VARIANT var; 1379 DISPPARAMS dp = {&var, &dispid, 1, 1}; 1380 IDispatchEx *dispex; 1381 1382 hres = jsval_to_variant(val, &var); 1383 if(FAILED(hres)) 1384 return hres; 1385 1386 if(V_VT(&var) == VT_DISPATCH) 1387 flags |= DISPATCH_PROPERTYPUTREF; 1388 1389 clear_ei(ctx); 1390 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); 1391 if(SUCCEEDED(hres)) { 1392 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, &dp, NULL, &ctx->ei.ei, 1393 &ctx->jscaller->IServiceProvider_iface); 1394 IDispatchEx_Release(dispex); 1395 }else { 1396 ULONG err = 0; 1397 1398 TRACE("using IDispatch\n"); 1399 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, &dp, NULL, &ctx->ei.ei, &err); 1400 } 1401 1402 VariantClear(&var); 1403 } 1404 1405 return hres; 1406 } 1407 1408 HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val) 1409 { 1410 dispex_prop_t *prop; 1411 HRESULT hres; 1412 1413 hres = find_prop_name_prot(obj, string_hash(name), name, &prop); 1414 if(FAILED(hres)) 1415 return hres; 1416 1417 if(!prop || prop->type==PROP_DELETED) { 1418 *val = jsval_undefined(); 1419 return S_OK; 1420 } 1421 1422 return prop_get(obj, prop, val); 1423 } 1424 1425 HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r) 1426 { 1427 WCHAR name[12]; 1428 dispex_prop_t *prop; 1429 HRESULT hres; 1430 1431 static const WCHAR formatW[] = {'%','d',0}; 1432 1433 swprintf(name, formatW, idx); 1434 1435 hres = find_prop_name_prot(obj, string_hash(name), name, &prop); 1436 if(FAILED(hres)) 1437 return hres; 1438 1439 if(!prop || prop->type==PROP_DELETED) { 1440 *r = jsval_undefined(); 1441 return DISP_E_UNKNOWNNAME; 1442 } 1443 1444 return prop_get(obj, prop, r); 1445 } 1446 1447 HRESULT jsdisp_propget(jsdisp_t *jsdisp, DISPID id, jsval_t *val) 1448 { 1449 dispex_prop_t *prop; 1450 1451 prop = get_prop(jsdisp, id); 1452 if(!prop) 1453 return DISP_E_MEMBERNOTFOUND; 1454 1455 return prop_get(jsdisp, prop, val); 1456 } 1457 1458 HRESULT disp_propget(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t *val) 1459 { 1460 DISPPARAMS dp = {NULL,NULL,0,0}; 1461 IDispatchEx *dispex; 1462 jsdisp_t *jsdisp; 1463 VARIANT var; 1464 HRESULT hres; 1465 1466 jsdisp = iface_to_jsdisp(disp); 1467 if(jsdisp) { 1468 hres = jsdisp_propget(jsdisp, id, val); 1469 jsdisp_release(jsdisp); 1470 return hres; 1471 } 1472 1473 V_VT(&var) = VT_EMPTY; 1474 clear_ei(ctx); 1475 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); 1476 if(SUCCEEDED(hres)) { 1477 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, INVOKE_PROPERTYGET, &dp, &var, &ctx->ei.ei, 1478 &ctx->jscaller->IServiceProvider_iface); 1479 IDispatchEx_Release(dispex); 1480 }else { 1481 ULONG err = 0; 1482 1483 TRACE("using IDispatch\n"); 1484 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, INVOKE_PROPERTYGET, &dp, &var, &ctx->ei.ei, &err); 1485 } 1486 if(FAILED(hres)) 1487 return hres; 1488 1489 hres = variant_to_jsval(&var, val); 1490 VariantClear(&var); 1491 return hres; 1492 } 1493 1494 HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx) 1495 { 1496 static const WCHAR formatW[] = {'%','d',0}; 1497 WCHAR buf[12]; 1498 dispex_prop_t *prop; 1499 BOOL b; 1500 HRESULT hres; 1501 1502 swprintf(buf, formatW, idx); 1503 1504 hres = find_prop_name(obj, string_hash(buf), buf, &prop); 1505 if(FAILED(hres) || !prop) 1506 return hres; 1507 1508 return delete_prop(prop, &b); 1509 } 1510 1511 HRESULT disp_delete(IDispatch *disp, DISPID id, BOOL *ret) 1512 { 1513 IDispatchEx *dispex; 1514 jsdisp_t *jsdisp; 1515 HRESULT hres; 1516 1517 jsdisp = iface_to_jsdisp(disp); 1518 if(jsdisp) { 1519 dispex_prop_t *prop; 1520 1521 prop = get_prop(jsdisp, id); 1522 if(prop) 1523 hres = delete_prop(prop, ret); 1524 else 1525 hres = DISP_E_MEMBERNOTFOUND; 1526 1527 jsdisp_release(jsdisp); 1528 return hres; 1529 } 1530 1531 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); 1532 if(FAILED(hres)) { 1533 *ret = FALSE; 1534 return S_OK; 1535 } 1536 1537 hres = IDispatchEx_DeleteMemberByDispID(dispex, id); 1538 IDispatchEx_Release(dispex); 1539 if(FAILED(hres)) 1540 return hres; 1541 1542 *ret = hres == S_OK; 1543 return S_OK; 1544 } 1545 1546 HRESULT jsdisp_next_prop(jsdisp_t *obj, DISPID id, BOOL own_only, DISPID *ret) 1547 { 1548 dispex_prop_t *iter; 1549 HRESULT hres; 1550 1551 if(id == DISPID_STARTENUM && !own_only) { 1552 hres = fill_protrefs(obj); 1553 if(FAILED(hres)) 1554 return hres; 1555 } 1556 1557 if(id + 1 < 0 || id+1 >= obj->prop_cnt) 1558 return S_FALSE; 1559 1560 for(iter = &obj->props[id + 1]; iter < obj->props + obj->prop_cnt; iter++) { 1561 if(!iter->name || iter->type == PROP_DELETED) 1562 continue; 1563 if(own_only && iter->type == PROP_PROTREF) 1564 continue; 1565 if(!(get_flags(obj, iter) & PROPF_ENUMERABLE)) 1566 continue; 1567 *ret = prop_to_id(obj, iter); 1568 return S_OK; 1569 } 1570 1571 return S_FALSE; 1572 } 1573 1574 HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL *ret) 1575 { 1576 IDispatchEx *dispex; 1577 jsdisp_t *jsdisp; 1578 BSTR bstr; 1579 HRESULT hres; 1580 1581 jsdisp = iface_to_jsdisp(disp); 1582 if(jsdisp) { 1583 dispex_prop_t *prop; 1584 const WCHAR *ptr; 1585 1586 ptr = jsstr_flatten(name); 1587 if(!ptr) { 1588 jsdisp_release(jsdisp); 1589 return E_OUTOFMEMORY; 1590 } 1591 1592 hres = find_prop_name(jsdisp, string_hash(ptr), ptr, &prop); 1593 if(prop) { 1594 hres = delete_prop(prop, ret); 1595 }else { 1596 *ret = TRUE; 1597 hres = S_OK; 1598 } 1599 1600 jsdisp_release(jsdisp); 1601 return hres; 1602 } 1603 1604 bstr = SysAllocStringLen(NULL, jsstr_length(name)); 1605 if(!bstr) 1606 return E_OUTOFMEMORY; 1607 jsstr_flush(name, bstr); 1608 1609 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); 1610 if(SUCCEEDED(hres)) { 1611 hres = IDispatchEx_DeleteMemberByName(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive)); 1612 if(SUCCEEDED(hres)) 1613 *ret = hres == S_OK; 1614 IDispatchEx_Release(dispex); 1615 }else { 1616 DISPID id; 1617 1618 hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id); 1619 if(SUCCEEDED(hres)) { 1620 /* Property exists and we can't delete it from pure IDispatch interface, so return false. */ 1621 *ret = FALSE; 1622 }else if(hres == DISP_E_UNKNOWNNAME) { 1623 /* Property doesn't exist, so nothing to delete */ 1624 *ret = TRUE; 1625 hres = S_OK; 1626 } 1627 } 1628 1629 SysFreeString(bstr); 1630 return hres; 1631 } 1632 1633 HRESULT jsdisp_get_own_property(jsdisp_t *obj, const WCHAR *name, BOOL flags_only, 1634 property_desc_t *desc) 1635 { 1636 dispex_prop_t *prop; 1637 HRESULT hres; 1638 1639 hres = find_prop_name(obj, string_hash(name), name, &prop); 1640 if(FAILED(hres)) 1641 return hres; 1642 1643 if(!prop) 1644 return DISP_E_UNKNOWNNAME; 1645 1646 memset(desc, 0, sizeof(*desc)); 1647 1648 switch(prop->type) { 1649 case PROP_BUILTIN: 1650 case PROP_JSVAL: 1651 desc->mask |= PROPF_WRITABLE; 1652 desc->explicit_value = TRUE; 1653 if(!flags_only) { 1654 hres = prop_get(obj, prop, &desc->value); 1655 if(FAILED(hres)) 1656 return hres; 1657 } 1658 break; 1659 case PROP_ACCESSOR: 1660 desc->explicit_getter = desc->explicit_setter = TRUE; 1661 if(!flags_only) { 1662 desc->getter = prop->u.accessor.getter 1663 ? jsdisp_addref(prop->u.accessor.getter) : NULL; 1664 desc->setter = prop->u.accessor.setter 1665 ? jsdisp_addref(prop->u.accessor.setter) : NULL; 1666 } 1667 break; 1668 default: 1669 return DISP_E_UNKNOWNNAME; 1670 } 1671 1672 desc->flags = prop->flags & (PROPF_ENUMERABLE | PROPF_WRITABLE | PROPF_CONFIGURABLE); 1673 desc->mask |= PROPF_ENUMERABLE | PROPF_CONFIGURABLE; 1674 return S_OK; 1675 } 1676 1677 HRESULT jsdisp_define_property(jsdisp_t *obj, const WCHAR *name, property_desc_t *desc) 1678 { 1679 dispex_prop_t *prop; 1680 HRESULT hres; 1681 1682 hres = find_prop_name(obj, string_hash(name), name, &prop); 1683 if(FAILED(hres)) 1684 return hres; 1685 1686 if(!prop && !(prop = alloc_prop(obj, name, PROP_DELETED, 0))) 1687 return E_OUTOFMEMORY; 1688 1689 if(prop->type == PROP_DELETED || prop->type == PROP_PROTREF) { 1690 prop->flags = desc->flags; 1691 if(desc->explicit_getter || desc->explicit_setter) { 1692 prop->type = PROP_ACCESSOR; 1693 prop->u.accessor.getter = desc->getter ? jsdisp_addref(desc->getter) : NULL; 1694 prop->u.accessor.setter = desc->setter ? jsdisp_addref(desc->setter) : NULL; 1695 TRACE("%s = accessor { get: %p set: %p }\n", debugstr_w(name), 1696 prop->u.accessor.getter, prop->u.accessor.setter); 1697 }else { 1698 prop->type = PROP_JSVAL; 1699 if(desc->explicit_value) { 1700 hres = jsval_copy(desc->value, &prop->u.val); 1701 if(FAILED(hres)) 1702 return hres; 1703 }else { 1704 prop->u.val = jsval_undefined(); 1705 } 1706 TRACE("%s = %s\n", debugstr_w(name), debugstr_jsval(prop->u.val)); 1707 } 1708 return S_OK; 1709 } 1710 1711 TRACE("existing prop %s prop flags %x desc flags %x desc mask %x\n", debugstr_w(name), 1712 prop->flags, desc->flags, desc->mask); 1713 1714 if(!(prop->flags & PROPF_CONFIGURABLE)) { 1715 if(((desc->mask & PROPF_CONFIGURABLE) && (desc->flags & PROPF_CONFIGURABLE)) 1716 || ((desc->mask & PROPF_ENUMERABLE) 1717 && ((desc->flags & PROPF_ENUMERABLE) != (prop->flags & PROPF_ENUMERABLE)))) 1718 return throw_type_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name); 1719 } 1720 1721 if(desc->explicit_value || (desc->mask & PROPF_WRITABLE)) { 1722 if(prop->type == PROP_ACCESSOR) { 1723 if(!(prop->flags & PROPF_CONFIGURABLE)) 1724 return throw_type_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name); 1725 if(prop->u.accessor.getter) 1726 jsdisp_release(prop->u.accessor.getter); 1727 if(prop->u.accessor.setter) 1728 jsdisp_release(prop->u.accessor.setter); 1729 1730 prop->type = PROP_JSVAL; 1731 hres = jsval_copy(desc->value, &prop->u.val); 1732 if(FAILED(hres)) { 1733 prop->u.val = jsval_undefined(); 1734 return hres; 1735 } 1736 }else { 1737 if(!(prop->flags & PROPF_CONFIGURABLE) && !(prop->flags & PROPF_WRITABLE)) { 1738 if((desc->mask & PROPF_WRITABLE) && (desc->flags & PROPF_WRITABLE)) 1739 return throw_type_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name); 1740 if(desc->explicit_value) { 1741 if(prop->type == PROP_JSVAL) { 1742 BOOL eq; 1743 hres = jsval_strict_equal(desc->value, prop->u.val, &eq); 1744 if(FAILED(hres)) 1745 return hres; 1746 if(!eq) 1747 return throw_type_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name); 1748 }else { 1749 FIXME("redefinition of property type %d\n", prop->type); 1750 } 1751 } 1752 } 1753 if(desc->explicit_value) { 1754 if(prop->type == PROP_JSVAL) 1755 jsval_release(prop->u.val); 1756 else 1757 prop->type = PROP_JSVAL; 1758 hres = jsval_copy(desc->value, &prop->u.val); 1759 if(FAILED(hres)) { 1760 prop->u.val = jsval_undefined(); 1761 return hres; 1762 } 1763 } 1764 } 1765 }else if(desc->explicit_getter || desc->explicit_setter) { 1766 if(prop->type != PROP_ACCESSOR) { 1767 if(!(prop->flags & PROPF_CONFIGURABLE)) 1768 return throw_type_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name); 1769 if(prop->type == PROP_JSVAL) 1770 jsval_release(prop->u.val); 1771 prop->type = PROP_ACCESSOR; 1772 prop->u.accessor.getter = prop->u.accessor.setter = NULL; 1773 }else if(!(prop->flags & PROPF_CONFIGURABLE)) { 1774 if((desc->explicit_getter && desc->getter != prop->u.accessor.getter) 1775 || (desc->explicit_setter && desc->setter != prop->u.accessor.setter)) 1776 return throw_type_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name); 1777 } 1778 1779 if(desc->explicit_getter) { 1780 if(prop->u.accessor.getter) { 1781 jsdisp_release(prop->u.accessor.getter); 1782 prop->u.accessor.getter = NULL; 1783 } 1784 if(desc->getter) 1785 prop->u.accessor.getter = jsdisp_addref(desc->getter); 1786 } 1787 if(desc->explicit_setter) { 1788 if(prop->u.accessor.setter) { 1789 jsdisp_release(prop->u.accessor.setter); 1790 prop->u.accessor.setter = NULL; 1791 } 1792 if(desc->setter) 1793 prop->u.accessor.setter = jsdisp_addref(desc->setter); 1794 } 1795 } 1796 1797 prop->flags = (prop->flags & ~desc->mask) | (desc->flags & desc->mask); 1798 return S_OK; 1799 } 1800 1801 HRESULT jsdisp_define_data_property(jsdisp_t *obj, const WCHAR *name, unsigned flags, jsval_t value) 1802 { 1803 property_desc_t prop_desc = { flags, flags, TRUE }; 1804 prop_desc.value = value; 1805 return jsdisp_define_property(obj, name, &prop_desc); 1806 } 1807