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