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