[JSCRIPT] Sync with Wine 3.0. CORE-14225
[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 if(ctx != jsdisp->ctx)
1100 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1101 hres = jsdisp_call(jsdisp, id, flags, argc, argv, ret);
1102 jsdisp_release(jsdisp);
1103 return hres;
1104 }
1105
1106 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1107 if(ret && argc)
1108 flags |= DISPATCH_PROPERTYGET;
1109
1110 dp.cArgs = argc;
1111
1112 if(flags & DISPATCH_PROPERTYPUT) {
1113 static DISPID propput_dispid = DISPID_PROPERTYPUT;
1114
1115 dp.cNamedArgs = 1;
1116 dp.rgdispidNamedArgs = &propput_dispid;
1117 }else {
1118 dp.cNamedArgs = 0;
1119 dp.rgdispidNamedArgs = NULL;
1120 }
1121
1122 if(argc > 6) {
1123 dp.rgvarg = heap_alloc(argc*sizeof(VARIANT));
1124 if(!dp.rgvarg)
1125 return E_OUTOFMEMORY;
1126 }else {
1127 dp.rgvarg = buf;
1128 }
1129
1130 for(i=0; i<argc; i++) {
1131 hres = jsval_to_variant(argv[i], dp.rgvarg+argc-i-1);
1132 if(FAILED(hres)) {
1133 while(i--)
1134 VariantClear(dp.rgvarg+argc-i-1);
1135 if(dp.rgvarg != buf)
1136 heap_free(dp.rgvarg);
1137 return hres;
1138 }
1139 }
1140
1141 V_VT(&retv) = VT_EMPTY;
1142 clear_ei(ctx);
1143 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1144 if(SUCCEEDED(hres)) {
1145 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, &dp, ret ? &retv : NULL, &ctx->ei.ei,
1146 &ctx->jscaller->IServiceProvider_iface);
1147 IDispatchEx_Release(dispex);
1148 }else {
1149 UINT err = 0;
1150
1151 if(flags == DISPATCH_CONSTRUCT) {
1152 WARN("IDispatch cannot be constructor\n");
1153 return DISP_E_MEMBERNOTFOUND;
1154 }
1155
1156 TRACE("using IDispatch\n");
1157 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, &dp, ret ? &retv : NULL, &ctx->ei.ei, &err);
1158 }
1159
1160 for(i=0; i<argc; i++)
1161 VariantClear(dp.rgvarg+argc-i-1);
1162 if(dp.rgvarg != buf)
1163 heap_free(dp.rgvarg);
1164 if(FAILED(hres))
1165 return hres;
1166
1167 if(ret) {
1168 hres = variant_to_jsval(&retv, ret);
1169 VariantClear(&retv);
1170 }
1171
1172 return hres;
1173 }
1174
1175 HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1176 jsval_t *r)
1177 {
1178 jsdisp_t *jsdisp;
1179 IDispatchEx *dispex;
1180 VARIANT buf[6], retv;
1181 DISPPARAMS dp;
1182 unsigned i;
1183 HRESULT hres;
1184
1185 assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK)));
1186
1187 jsdisp = iface_to_jsdisp(disp);
1188 if(jsdisp) {
1189 if(ctx != jsdisp->ctx)
1190 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1191 hres = jsdisp_call_value(jsdisp, jsthis, flags, argc, argv, r);
1192 jsdisp_release(jsdisp);
1193 return hres;
1194 }
1195
1196 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1197 if(r && argc && flags == DISPATCH_METHOD)
1198 flags |= DISPATCH_PROPERTYGET;
1199
1200 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1201 if(FAILED(hres)) {
1202 TRACE("using IDispatch\n");
1203 dispex = NULL;
1204 jsthis = NULL;
1205 }
1206
1207 if(jsthis) {
1208 static DISPID this_id = DISPID_THIS;
1209
1210 dp.cArgs = argc+1;
1211 dp.cNamedArgs = 1;
1212 dp.rgdispidNamedArgs = &this_id;
1213 }else {
1214 dp.cArgs = argc;
1215 dp.cNamedArgs = 0;
1216 dp.rgdispidNamedArgs = NULL;
1217 }
1218
1219 if(dp.cArgs > sizeof(buf)/sizeof(*buf)) {
1220 dp.rgvarg = heap_alloc(dp.cArgs*sizeof(VARIANT));
1221 if(!dp.rgvarg) {
1222 if(dispex)
1223 IDispatchEx_Release(dispex);
1224 return E_OUTOFMEMORY;
1225 }
1226 }else {
1227 dp.rgvarg = buf;
1228 }
1229
1230 for(i=0; i<argc; i++) {
1231 hres = jsval_to_variant(argv[i], dp.rgvarg+dp.cArgs-i-1);
1232 if(FAILED(hres)) {
1233 while(i--)
1234 VariantClear(dp.rgvarg+dp.cArgs-i-1);
1235 if(dp.rgvarg != buf)
1236 heap_free(dp.rgvarg);
1237 if(dispex)
1238 IDispatchEx_Release(dispex);
1239 return hres;
1240 }
1241 }
1242 if(jsthis) {
1243 V_VT(dp.rgvarg) = VT_DISPATCH;
1244 V_DISPATCH(dp.rgvarg) = jsthis;
1245 }
1246
1247 V_VT(&retv) = VT_EMPTY;
1248 clear_ei(ctx);
1249 if(dispex) {
1250 hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, ctx->lcid, flags, &dp, r ? &retv : NULL, &ctx->ei.ei,
1251 &ctx->jscaller->IServiceProvider_iface);
1252 IDispatchEx_Release(dispex);
1253 }else {
1254 UINT err = 0;
1255
1256 if(flags == DISPATCH_CONSTRUCT) {
1257 WARN("IDispatch cannot be constructor\n");
1258 return DISP_E_MEMBERNOTFOUND;
1259 }
1260
1261 hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, ctx->lcid, flags, &dp, r ? &retv : NULL, &ctx->ei.ei, &err);
1262 }
1263
1264 for(i=0; i<argc; i++)
1265 VariantClear(dp.rgvarg+dp.cArgs-i-1);
1266 if(dp.rgvarg != buf)
1267 heap_free(dp.rgvarg);
1268 if(FAILED(hres))
1269 return hres;
1270
1271 if(!r)
1272 return S_OK;
1273
1274 hres = variant_to_jsval(&retv, r);
1275 VariantClear(&retv);
1276 return hres;
1277 }
1278
1279 HRESULT jsdisp_propput(jsdisp_t *obj, const WCHAR *name, DWORD flags, jsval_t val)
1280 {
1281 dispex_prop_t *prop;
1282 HRESULT hres;
1283
1284 hres = ensure_prop_name(obj, name, FALSE, flags, &prop);
1285 if(FAILED(hres))
1286 return hres;
1287
1288 return prop_put(obj, prop, val, NULL);
1289 }
1290
1291 HRESULT jsdisp_propput_name(jsdisp_t *obj, const WCHAR *name, jsval_t val)
1292 {
1293 return jsdisp_propput(obj, name, PROPF_ENUM, val);
1294 }
1295
1296 HRESULT jsdisp_propput_const(jsdisp_t *obj, const WCHAR *name, jsval_t val)
1297 {
1298 dispex_prop_t *prop;
1299 HRESULT hres;
1300
1301 hres = ensure_prop_name(obj, name, FALSE, PROPF_CONST, &prop);
1302 if(FAILED(hres))
1303 return hres;
1304
1305 return jsval_copy(val, &prop->u.val);
1306 }
1307
1308 HRESULT jsdisp_propput_dontenum(jsdisp_t *obj, const WCHAR *name, jsval_t val)
1309 {
1310 return jsdisp_propput(obj, name, 0, val);
1311 }
1312
1313 HRESULT jsdisp_propput_idx(jsdisp_t *obj, DWORD idx, jsval_t val)
1314 {
1315 WCHAR buf[12];
1316
1317 static const WCHAR formatW[] = {'%','d',0};
1318
1319 sprintfW(buf, formatW, idx);
1320 return jsdisp_propput_name(obj, buf, val);
1321 }
1322
1323 HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t val)
1324 {
1325 jsdisp_t *jsdisp;
1326 HRESULT hres;
1327
1328 jsdisp = iface_to_jsdisp(disp);
1329 if(jsdisp) {
1330 dispex_prop_t *prop;
1331
1332 prop = get_prop(jsdisp, id);
1333 if(prop)
1334 hres = prop_put(jsdisp, prop, val, NULL);
1335 else
1336 hres = DISP_E_MEMBERNOTFOUND;
1337
1338 jsdisp_release(jsdisp);
1339 }else {
1340 DISPID dispid = DISPID_PROPERTYPUT;
1341 DWORD flags = DISPATCH_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 if(V_VT(&var) == VT_DISPATCH)
1351 flags |= DISPATCH_PROPERTYPUTREF;
1352
1353 clear_ei(ctx);
1354 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1355 if(SUCCEEDED(hres)) {
1356 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, &dp, NULL, &ctx->ei.ei,
1357 &ctx->jscaller->IServiceProvider_iface);
1358 IDispatchEx_Release(dispex);
1359 }else {
1360 ULONG err = 0;
1361
1362 TRACE("using IDispatch\n");
1363 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, &dp, NULL, &ctx->ei.ei, &err);
1364 }
1365
1366 VariantClear(&var);
1367 }
1368
1369 return hres;
1370 }
1371
1372 HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val)
1373 {
1374 DISPPARAMS dp = {NULL, NULL, 0, 0};
1375 dispex_prop_t *prop;
1376 HRESULT hres;
1377
1378 hres = find_prop_name_prot(obj, string_hash(name), name, &prop);
1379 if(FAILED(hres))
1380 return hres;
1381
1382 if(!prop || prop->type==PROP_DELETED) {
1383 *val = jsval_undefined();
1384 return S_OK;
1385 }
1386
1387 return prop_get(obj, prop, &dp, val, NULL);
1388 }
1389
1390 HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r)
1391 {
1392 WCHAR name[12];
1393 DISPPARAMS dp = {NULL, NULL, 0, 0};
1394 dispex_prop_t *prop;
1395 HRESULT hres;
1396
1397 static const WCHAR formatW[] = {'%','d',0};
1398
1399 sprintfW(name, formatW, idx);
1400
1401 hres = find_prop_name_prot(obj, string_hash(name), name, &prop);
1402 if(FAILED(hres))
1403 return hres;
1404
1405 if(!prop || prop->type==PROP_DELETED) {
1406 *r = jsval_undefined();
1407 return DISP_E_UNKNOWNNAME;
1408 }
1409
1410 return prop_get(obj, prop, &dp, r, NULL);
1411 }
1412
1413 HRESULT jsdisp_propget(jsdisp_t *jsdisp, DISPID id, jsval_t *val)
1414 {
1415 DISPPARAMS dp = {NULL,NULL,0,0};
1416 dispex_prop_t *prop;
1417
1418 prop = get_prop(jsdisp, id);
1419 if(!prop)
1420 return DISP_E_MEMBERNOTFOUND;
1421
1422 return prop_get(jsdisp, prop, &dp, val, NULL);
1423 }
1424
1425 HRESULT disp_propget(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t *val)
1426 {
1427 DISPPARAMS dp = {NULL,NULL,0,0};
1428 IDispatchEx *dispex;
1429 jsdisp_t *jsdisp;
1430 VARIANT var;
1431 HRESULT hres;
1432
1433 jsdisp = iface_to_jsdisp(disp);
1434 if(jsdisp) {
1435 hres = jsdisp_propget(jsdisp, id, val);
1436 jsdisp_release(jsdisp);
1437 return hres;
1438 }
1439
1440 V_VT(&var) = VT_EMPTY;
1441 clear_ei(ctx);
1442 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1443 if(SUCCEEDED(hres)) {
1444 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, INVOKE_PROPERTYGET, &dp, &var, &ctx->ei.ei,
1445 &ctx->jscaller->IServiceProvider_iface);
1446 IDispatchEx_Release(dispex);
1447 }else {
1448 ULONG err = 0;
1449
1450 TRACE("using IDispatch\n");
1451 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, INVOKE_PROPERTYGET, &dp, &var, &ctx->ei.ei, &err);
1452 }
1453 if(FAILED(hres))
1454 return hres;
1455
1456 hres = variant_to_jsval(&var, val);
1457 VariantClear(&var);
1458 return hres;
1459 }
1460
1461 HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx)
1462 {
1463 static const WCHAR formatW[] = {'%','d',0};
1464 WCHAR buf[12];
1465 dispex_prop_t *prop;
1466 BOOL b;
1467 HRESULT hres;
1468
1469 sprintfW(buf, formatW, idx);
1470
1471 hres = find_prop_name(obj, string_hash(buf), buf, &prop);
1472 if(FAILED(hres) || !prop)
1473 return hres;
1474
1475 return delete_prop(prop, &b);
1476 }
1477
1478 HRESULT disp_delete(IDispatch *disp, DISPID id, BOOL *ret)
1479 {
1480 IDispatchEx *dispex;
1481 jsdisp_t *jsdisp;
1482 HRESULT hres;
1483
1484 jsdisp = iface_to_jsdisp(disp);
1485 if(jsdisp) {
1486 dispex_prop_t *prop;
1487
1488 prop = get_prop(jsdisp, id);
1489 if(prop)
1490 hres = delete_prop(prop, ret);
1491 else
1492 hres = DISP_E_MEMBERNOTFOUND;
1493
1494 jsdisp_release(jsdisp);
1495 return hres;
1496 }
1497
1498 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1499 if(FAILED(hres)) {
1500 *ret = FALSE;
1501 return S_OK;
1502 }
1503
1504 hres = IDispatchEx_DeleteMemberByDispID(dispex, id);
1505 IDispatchEx_Release(dispex);
1506 if(FAILED(hres))
1507 return hres;
1508
1509 *ret = hres == S_OK;
1510 return S_OK;
1511 }
1512
1513 HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL *ret)
1514 {
1515 IDispatchEx *dispex;
1516 jsdisp_t *jsdisp;
1517 BSTR bstr;
1518 HRESULT hres;
1519
1520 jsdisp = iface_to_jsdisp(disp);
1521 if(jsdisp) {
1522 dispex_prop_t *prop;
1523 const WCHAR *ptr;
1524
1525 ptr = jsstr_flatten(name);
1526 if(!ptr) {
1527 jsdisp_release(jsdisp);
1528 return E_OUTOFMEMORY;
1529 }
1530
1531 hres = find_prop_name(jsdisp, string_hash(ptr), ptr, &prop);
1532 if(prop) {
1533 hres = delete_prop(prop, ret);
1534 }else {
1535 *ret = TRUE;
1536 hres = S_OK;
1537 }
1538
1539 jsdisp_release(jsdisp);
1540 return hres;
1541 }
1542
1543 bstr = SysAllocStringLen(NULL, jsstr_length(name));
1544 if(!bstr)
1545 return E_OUTOFMEMORY;
1546 jsstr_flush(name, bstr);
1547
1548 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1549 if(SUCCEEDED(hres)) {
1550 hres = IDispatchEx_DeleteMemberByName(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive));
1551 if(SUCCEEDED(hres))
1552 *ret = hres == S_OK;
1553 IDispatchEx_Release(dispex);
1554 }else {
1555 DISPID id;
1556
1557 hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id);
1558 if(SUCCEEDED(hres)) {
1559 /* Property exists and we can't delete it from pure IDispatch interface, so return false. */
1560 *ret = FALSE;
1561 }else if(hres == DISP_E_UNKNOWNNAME) {
1562 /* Property doesn't exist, so nothing to delete */
1563 *ret = TRUE;
1564 hres = S_OK;
1565 }
1566 }
1567
1568 SysFreeString(bstr);
1569 return hres;
1570 }
1571
1572 HRESULT jsdisp_is_own_prop(jsdisp_t *obj, const WCHAR *name, BOOL *ret)
1573 {
1574 dispex_prop_t *prop;
1575 HRESULT hres;
1576
1577 hres = find_prop_name(obj, string_hash(name), name, &prop);
1578 if(FAILED(hres))
1579 return hres;
1580
1581 *ret = prop && (prop->type == PROP_JSVAL || prop->type == PROP_BUILTIN);
1582 return S_OK;
1583 }
1584
1585 HRESULT jsdisp_is_enumerable(jsdisp_t *obj, const WCHAR *name, BOOL *ret)
1586 {
1587 dispex_prop_t *prop;
1588 HRESULT hres;
1589
1590 hres = find_prop_name(obj, string_hash(name), name, &prop);
1591 if(FAILED(hres))
1592 return hres;
1593
1594 *ret = prop && (prop->flags & PROPF_ENUM) && prop->type != PROP_PROTREF;
1595 return S_OK;
1596 }