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