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