vbscript: Added interp_assign_member implementation.
[wine] / dlls / vbscript / interp.c
1 /*
2  * Copyright 2011 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 "vbscript.h"
22
23 #include "wine/debug.h"
24
25 WINE_DEFAULT_DEBUG_CHANNEL(vbscript);
26
27
28 typedef struct {
29     vbscode_t *code;
30     instr_t *instr;
31     script_ctx_t *script;
32     function_t *func;
33
34     unsigned stack_size;
35     unsigned top;
36     VARIANT *stack;
37 } exec_ctx_t;
38
39 typedef HRESULT (*instr_func_t)(exec_ctx_t*);
40
41 typedef enum {
42     REF_NONE,
43     REF_DISP
44 } ref_type_t;
45
46 typedef struct {
47     ref_type_t type;
48     union {
49         struct {
50             IDispatch *disp;
51             DISPID id;
52         } d;
53     } u;
54 } ref_t;
55
56 typedef struct {
57     VARIANT *v;
58     VARIANT store;
59     BOOL owned;
60 } variant_val_t;
61
62 static HRESULT lookup_identifier(exec_ctx_t *ctx, BSTR name, ref_t *ref)
63 {
64     named_item_t *item;
65     DISPID id;
66     HRESULT hres;
67
68     LIST_FOR_EACH_ENTRY(item, &ctx->script->named_items, named_item_t, entry) {
69         if(item->flags & SCRIPTITEM_GLOBALMEMBERS) {
70             hres = disp_get_id(item->disp, name, &id);
71             if(SUCCEEDED(hres)) {
72                 ref->type = REF_DISP;
73                 ref->u.d.disp = item->disp;
74                 ref->u.d.id = id;
75                 return S_OK;
76             }
77         }
78     }
79
80     if(!ctx->func->code_ctx->option_explicit)
81         FIXME("create an attempt to set\n");
82
83     ref->type = REF_NONE;
84     return S_OK;
85 }
86
87 static inline VARIANT *stack_pop(exec_ctx_t *ctx)
88 {
89     assert(ctx->top);
90     return ctx->stack + --ctx->top;
91 }
92
93 static HRESULT stack_push(exec_ctx_t *ctx, VARIANT *v)
94 {
95     if(ctx->stack_size == ctx->top) {
96         VARIANT *new_stack;
97
98         new_stack = heap_realloc(ctx->stack, ctx->stack_size*2);
99         if(!new_stack) {
100             VariantClear(v);
101             return E_OUTOFMEMORY;
102         }
103
104         ctx->stack = new_stack;
105         ctx->stack_size *= 2;
106     }
107
108     ctx->stack[ctx->top++] = *v;
109     return S_OK;
110 }
111
112 static void stack_popn(exec_ctx_t *ctx, unsigned n)
113 {
114     while(n--)
115         VariantClear(stack_pop(ctx));
116 }
117
118 static HRESULT stack_pop_val(exec_ctx_t *ctx, variant_val_t *v)
119 {
120     VARIANT *var;
121
122     var = stack_pop(ctx);
123
124     if(V_VT(var) == (VT_BYREF|VT_VARIANT)) {
125         v->owned = FALSE;
126         var = V_VARIANTREF(var);
127     }else {
128         v->owned = TRUE;
129     }
130
131     if(V_VT(var) == VT_DISPATCH) {
132         FIXME("got dispatch - get its default value\n");
133         return E_NOTIMPL;
134     }else {
135         v->v = var;
136     }
137
138     return S_OK;
139 }
140
141 static inline void release_val(variant_val_t *v)
142 {
143     if(v->owned)
144         VariantClear(v->v);
145 }
146
147 static HRESULT stack_pop_disp(exec_ctx_t *ctx, IDispatch **ret)
148 {
149     VARIANT *v = stack_pop(ctx);
150
151     if(V_VT(v) == VT_DISPATCH) {
152         *ret = V_DISPATCH(v);
153         return S_OK;
154     }
155
156     if(V_VT(v) != (VT_VARIANT|VT_BYREF)) {
157         FIXME("not supported type: %s\n", debugstr_variant(v));
158         VariantClear(v);
159         return E_FAIL;
160     }
161
162     v = V_BYREF(v);
163     if(V_VT(v) != VT_DISPATCH) {
164         FIXME("not disp %s\n", debugstr_variant(v));
165         return E_FAIL;
166     }
167
168     if(V_DISPATCH(v))
169         IDispatch_AddRef(V_DISPATCH(v));
170     *ret = V_DISPATCH(v);
171     return S_OK;
172 }
173
174 static void vbstack_to_dp(exec_ctx_t *ctx, unsigned arg_cnt, DISPPARAMS *dp)
175 {
176     dp->cArgs = arg_cnt;
177     dp->rgdispidNamedArgs = NULL;
178     dp->cNamedArgs = 0;
179
180     if(arg_cnt) {
181         VARIANT tmp;
182         unsigned i;
183
184         assert(ctx->top >= arg_cnt);
185
186         for(i=1; i*2 <= arg_cnt; i++) {
187             tmp = ctx->stack[ctx->top-i];
188             ctx->stack[ctx->top-i] = ctx->stack[ctx->top-arg_cnt+i-1];
189             ctx->stack[ctx->top-arg_cnt+i-1] = tmp;
190         }
191
192         dp->rgvarg = ctx->stack + ctx->top-arg_cnt;
193     }else {
194         dp->rgvarg = NULL;
195     }
196 }
197
198 static HRESULT do_icall(exec_ctx_t *ctx, VARIANT *res)
199 {
200     BSTR identifier = ctx->instr->arg1.bstr;
201     const unsigned arg_cnt = ctx->instr->arg2.uint;
202     ref_t ref = {0};
203     DISPPARAMS dp;
204     HRESULT hres;
205
206     hres = lookup_identifier(ctx, identifier, &ref);
207     if(FAILED(hres))
208         return hres;
209
210     vbstack_to_dp(ctx, arg_cnt, &dp);
211
212     switch(ref.type) {
213     case REF_DISP:
214         hres = disp_call(ctx->script, ref.u.d.disp, ref.u.d.id, &dp, res);
215         if(FAILED(hres))
216             return hres;
217         break;
218     default:
219         FIXME("%s not found\n", debugstr_w(identifier));
220         return DISP_E_UNKNOWNNAME;
221     }
222
223     stack_popn(ctx, arg_cnt);
224     return S_OK;
225 }
226
227 static HRESULT interp_icall(exec_ctx_t *ctx)
228 {
229     VARIANT v;
230     HRESULT hres;
231
232     TRACE("\n");
233
234     hres = do_icall(ctx, &v);
235     if(FAILED(hres))
236         return hres;
237
238     return stack_push(ctx, &v);
239 }
240
241 static HRESULT interp_icallv(exec_ctx_t *ctx)
242 {
243     TRACE("\n");
244     return do_icall(ctx, NULL);
245 }
246
247 static HRESULT assign_ident(exec_ctx_t *ctx, BSTR name, VARIANT *val, BOOL own_val)
248 {
249     ref_t ref;
250     HRESULT hres;
251
252     hres = lookup_identifier(ctx, name, &ref);
253     if(FAILED(hres))
254         return hres;
255
256     switch(ref.type) {
257     case REF_DISP:
258         hres = disp_propput(ctx->script, ref.u.d.disp, ref.u.d.id, val);
259         if(own_val)
260             VariantClear(val);
261         break;
262     case REF_NONE:
263         FIXME("%s not found\n", debugstr_w(name));
264         if(own_val)
265             VariantClear(val);
266         return DISP_E_UNKNOWNNAME;
267     }
268
269     return hres;
270 }
271
272 static HRESULT interp_assign_ident(exec_ctx_t *ctx)
273 {
274     const BSTR arg = ctx->instr->arg1.bstr;
275     variant_val_t v;
276     HRESULT hres;
277
278     TRACE("%s\n", debugstr_w(arg));
279
280     hres = stack_pop_val(ctx, &v);
281     if(FAILED(hres))
282         return hres;
283
284     return assign_ident(ctx, arg, v.v, v.owned);
285 }
286
287 static HRESULT interp_assign_member(exec_ctx_t *ctx)
288 {
289     BSTR identifier = ctx->instr->arg1.bstr;
290     variant_val_t val;
291     IDispatch *obj;
292     DISPID id;
293     HRESULT hres;
294
295     TRACE("%s\n", debugstr_w(identifier));
296
297     hres = stack_pop_disp(ctx, &obj);
298     if(FAILED(hres))
299         return hres;
300
301     if(!obj) {
302         FIXME("NULL obj\n");
303         return E_FAIL;
304     }
305
306     hres = stack_pop_val(ctx, &val);
307     if(FAILED(hres)) {
308         IDispatch_Release(obj);
309         return hres;
310     }
311
312     hres = disp_get_id(obj, identifier, &id);
313     if(SUCCEEDED(hres))
314         hres = disp_propput(ctx->script, obj, id, val.v);
315
316     release_val(&val);
317     IDispatch_Release(obj);
318     return hres;
319 }
320
321 static HRESULT interp_ret(exec_ctx_t *ctx)
322 {
323     TRACE("\n");
324
325     ctx->instr = NULL;
326     return S_OK;
327 }
328
329 static HRESULT interp_bool(exec_ctx_t *ctx)
330 {
331     const VARIANT_BOOL arg = ctx->instr->arg1.lng;
332     VARIANT v;
333
334     TRACE("%s\n", arg ? "true" : "false");
335
336     V_VT(&v) = VT_BOOL;
337     V_BOOL(&v) = arg;
338     return stack_push(ctx, &v);
339 }
340
341 static HRESULT interp_string(exec_ctx_t *ctx)
342 {
343     VARIANT v;
344
345     TRACE("\n");
346
347     V_VT(&v) = VT_BSTR;
348     V_BSTR(&v) = SysAllocString(ctx->instr->arg1.str);
349     if(!V_BSTR(&v))
350         return E_OUTOFMEMORY;
351
352     return stack_push(ctx, &v);
353 }
354
355 static HRESULT interp_long(exec_ctx_t *ctx)
356 {
357     const LONG arg = ctx->instr->arg1.lng;
358     VARIANT v;
359
360     TRACE("%d\n", arg);
361
362     V_VT(&v) = VT_I4;
363     V_I4(&v) = arg;
364     return stack_push(ctx, &v);
365 }
366
367 static HRESULT interp_short(exec_ctx_t *ctx)
368 {
369     const LONG arg = ctx->instr->arg1.lng;
370     VARIANT v;
371
372     TRACE("%d\n", arg);
373
374     V_VT(&v) = VT_I2;
375     V_I2(&v) = arg;
376     return stack_push(ctx, &v);
377 }
378
379 static HRESULT interp_double(exec_ctx_t *ctx)
380 {
381     const DOUBLE *arg = ctx->instr->arg1.dbl;
382     VARIANT v;
383
384     TRACE("%lf\n", *arg);
385
386     V_VT(&v) = VT_R8;
387     V_R8(&v) = *arg;
388     return stack_push(ctx, &v);
389 }
390
391 static HRESULT interp_empty(exec_ctx_t *ctx)
392 {
393     VARIANT v;
394
395     TRACE("\n");
396
397     V_VT(&v) = VT_EMPTY;
398     return stack_push(ctx, &v);
399 }
400
401 static HRESULT interp_null(exec_ctx_t *ctx)
402 {
403     VARIANT v;
404
405     TRACE("\n");
406
407     V_VT(&v) = VT_NULL;
408     return stack_push(ctx, &v);
409 }
410
411 static HRESULT interp_not(exec_ctx_t *ctx)
412 {
413     variant_val_t val;
414     VARIANT v;
415     HRESULT hres;
416
417     TRACE("\n");
418
419     hres = stack_pop_val(ctx, &val);
420     if(FAILED(hres))
421         return hres;
422
423     hres = VarNot(val.v, &v);
424     release_val(&val);
425     if(FAILED(hres))
426         return hres;
427
428     return stack_push(ctx, &v);
429 }
430
431 static HRESULT cmp_oper(exec_ctx_t *ctx)
432 {
433     variant_val_t l, r;
434     HRESULT hres;
435
436     hres = stack_pop_val(ctx, &r);
437     if(FAILED(hres))
438         return hres;
439
440     hres = stack_pop_val(ctx, &l);
441     if(SUCCEEDED(hres)) {
442         if(V_VT(l.v) == VT_NULL || V_VT(r.v) == VT_NULL) {
443             FIXME("comparing nulls is not implemented\n");
444             hres = E_NOTIMPL;
445         }else {
446             hres = VarCmp(l.v, r.v, ctx->script->lcid, 0);
447         }
448     }
449
450     release_val(&r);
451     release_val(&l);
452     return hres;
453 }
454
455 static HRESULT interp_equal(exec_ctx_t *ctx)
456 {
457     VARIANT v;
458     HRESULT hres;
459
460     TRACE("\n");
461
462     hres = cmp_oper(ctx);
463     if(FAILED(hres))
464         return hres;
465
466     V_VT(&v) = VT_BOOL;
467     V_BOOL(&v) = hres == VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE;
468     return stack_push(ctx, &v);
469 }
470
471 static HRESULT interp_nequal(exec_ctx_t *ctx)
472 {
473     VARIANT v;
474     HRESULT hres;
475
476     TRACE("\n");
477
478     hres = cmp_oper(ctx);
479     if(FAILED(hres))
480         return hres;
481
482     V_VT(&v) = VT_BOOL;
483     V_BOOL(&v) = hres != VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE;
484     return stack_push(ctx, &v);
485 }
486
487 static HRESULT interp_concat(exec_ctx_t *ctx)
488 {
489     variant_val_t r, l;
490     VARIANT v;
491     HRESULT hres;
492
493     TRACE("\n");
494
495     hres = stack_pop_val(ctx, &r);
496     if(FAILED(hres))
497         return hres;
498
499     hres = stack_pop_val(ctx, &l);
500     if(SUCCEEDED(hres)) {
501         hres = VarCat(l.v, r.v, &v);
502         release_val(&l);
503     }
504     release_val(&r);
505     if(FAILED(hres))
506         return hres;
507
508     return stack_push(ctx, &v);
509 }
510
511 static HRESULT interp_add(exec_ctx_t *ctx)
512 {
513     variant_val_t r, l;
514     VARIANT v;
515     HRESULT hres;
516
517     TRACE("\n");
518
519     hres = stack_pop_val(ctx, &r);
520     if(FAILED(hres))
521         return hres;
522
523     hres = stack_pop_val(ctx, &l);
524     if(SUCCEEDED(hres)) {
525         hres = VarAdd(l.v, r.v, &v);
526         release_val(&l);
527     }
528     release_val(&r);
529     if(FAILED(hres))
530         return hres;
531
532     return stack_push(ctx, &v);
533 }
534
535 static HRESULT interp_sub(exec_ctx_t *ctx)
536 {
537     variant_val_t r, l;
538     VARIANT v;
539     HRESULT hres;
540
541     TRACE("\n");
542
543     hres = stack_pop_val(ctx, &r);
544     if(FAILED(hres))
545         return hres;
546
547     hres = stack_pop_val(ctx, &l);
548     if(SUCCEEDED(hres)) {
549         hres = VarSub(l.v, r.v, &v);
550         release_val(&l);
551     }
552     release_val(&r);
553     if(FAILED(hres))
554         return hres;
555
556     return stack_push(ctx, &v);
557 }
558
559 static HRESULT interp_neg(exec_ctx_t *ctx)
560 {
561     variant_val_t val;
562     VARIANT v;
563     HRESULT hres;
564
565     hres = stack_pop_val(ctx, &val);
566     if(FAILED(hres))
567         return hres;
568
569     hres = VarNeg(val.v, &v);
570     release_val(&val);
571     if(FAILED(hres))
572         return hres;
573
574     return stack_push(ctx, &v);
575 }
576
577 static const instr_func_t op_funcs[] = {
578 #define X(x,n,a,b) interp_ ## x,
579 OP_LIST
580 #undef X
581 };
582
583 static const unsigned op_move[] = {
584 #define X(x,n,a,b) n,
585 OP_LIST
586 #undef X
587 };
588
589 HRESULT exec_script(script_ctx_t *ctx, function_t *func)
590 {
591     exec_ctx_t exec;
592     vbsop_t op;
593     HRESULT hres = S_OK;
594
595     exec.stack_size = 16;
596     exec.top = 0;
597     exec.stack = heap_alloc(exec.stack_size * sizeof(VARIANT));
598     if(!exec.stack)
599         return E_OUTOFMEMORY;
600
601     exec.code = func->code_ctx;
602     exec.instr = exec.code->instrs + func->code_off;
603     exec.script = ctx;
604     exec.func = func;
605
606     while(exec.instr) {
607         op = exec.instr->op;
608         hres = op_funcs[op](&exec);
609         if(FAILED(hres)) {
610             FIXME("Failed %08x\n", hres);
611             stack_popn(&exec, exec.top);
612             break;
613         }
614
615         exec.instr += op_move[op];
616     }
617
618     assert(!exec.top);
619     heap_free(exec.stack);
620
621     return hres;
622 }