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