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