vbscript: Added assign statement tests.
[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 void vbstack_to_dp(exec_ctx_t *ctx, unsigned arg_cnt, DISPPARAMS *dp)
148 {
149     dp->cArgs = arg_cnt;
150     dp->rgdispidNamedArgs = NULL;
151     dp->cNamedArgs = 0;
152
153     if(arg_cnt) {
154         VARIANT tmp;
155         unsigned i;
156
157         assert(ctx->top >= arg_cnt);
158
159         for(i=1; i*2 <= arg_cnt; i++) {
160             tmp = ctx->stack[ctx->top-i];
161             ctx->stack[ctx->top-i] = ctx->stack[ctx->top-arg_cnt+i-1];
162             ctx->stack[ctx->top-arg_cnt+i-1] = tmp;
163         }
164
165         dp->rgvarg = ctx->stack + ctx->top-arg_cnt;
166     }else {
167         dp->rgvarg = NULL;
168     }
169 }
170
171 static HRESULT do_icall(exec_ctx_t *ctx, VARIANT *res)
172 {
173     BSTR identifier = ctx->instr->arg1.bstr;
174     const unsigned arg_cnt = ctx->instr->arg2.uint;
175     ref_t ref = {0};
176     DISPPARAMS dp;
177     HRESULT hres;
178
179     hres = lookup_identifier(ctx, identifier, &ref);
180     if(FAILED(hres))
181         return hres;
182
183     vbstack_to_dp(ctx, arg_cnt, &dp);
184
185     switch(ref.type) {
186     case REF_DISP:
187         hres = disp_call(ctx->script, ref.u.d.disp, ref.u.d.id, &dp, res);
188         if(FAILED(hres))
189             return hres;
190         break;
191     default:
192         FIXME("%s not found\n", debugstr_w(identifier));
193         return DISP_E_UNKNOWNNAME;
194     }
195
196     stack_popn(ctx, arg_cnt);
197     return S_OK;
198 }
199
200 static HRESULT interp_icall(exec_ctx_t *ctx)
201 {
202     VARIANT v;
203     HRESULT hres;
204
205     TRACE("\n");
206
207     hres = do_icall(ctx, &v);
208     if(FAILED(hres))
209         return hres;
210
211     return stack_push(ctx, &v);
212 }
213
214 static HRESULT interp_icallv(exec_ctx_t *ctx)
215 {
216     TRACE("\n");
217     return do_icall(ctx, NULL);
218 }
219
220 static HRESULT assign_ident(exec_ctx_t *ctx, BSTR name, VARIANT *val, BOOL own_val)
221 {
222     ref_t ref;
223     HRESULT hres;
224
225     hres = lookup_identifier(ctx, name, &ref);
226     if(FAILED(hres))
227         return hres;
228
229     switch(ref.type) {
230     case REF_DISP:
231         hres = disp_propput(ctx->script, ref.u.d.disp, ref.u.d.id, val);
232         if(own_val)
233             VariantClear(val);
234         break;
235     case REF_NONE:
236         FIXME("%s not found\n", debugstr_w(name));
237         if(own_val)
238             VariantClear(val);
239         return DISP_E_UNKNOWNNAME;
240     }
241
242     return hres;
243 }
244
245 static HRESULT interp_assign_ident(exec_ctx_t *ctx)
246 {
247     const BSTR arg = ctx->instr->arg1.bstr;
248     variant_val_t v;
249     HRESULT hres;
250
251     TRACE("%s\n", debugstr_w(arg));
252
253     hres = stack_pop_val(ctx, &v);
254     if(FAILED(hres))
255         return hres;
256
257     return assign_ident(ctx, arg, v.v, v.owned);
258 }
259
260 static HRESULT interp_ret(exec_ctx_t *ctx)
261 {
262     TRACE("\n");
263
264     ctx->instr = NULL;
265     return S_OK;
266 }
267
268 static HRESULT interp_bool(exec_ctx_t *ctx)
269 {
270     const VARIANT_BOOL arg = ctx->instr->arg1.lng;
271     VARIANT v;
272
273     TRACE("%s\n", arg ? "true" : "false");
274
275     V_VT(&v) = VT_BOOL;
276     V_BOOL(&v) = arg;
277     return stack_push(ctx, &v);
278 }
279
280 static HRESULT interp_string(exec_ctx_t *ctx)
281 {
282     VARIANT v;
283
284     TRACE("\n");
285
286     V_VT(&v) = VT_BSTR;
287     V_BSTR(&v) = SysAllocString(ctx->instr->arg1.str);
288     if(!V_BSTR(&v))
289         return E_OUTOFMEMORY;
290
291     return stack_push(ctx, &v);
292 }
293
294 static HRESULT interp_long(exec_ctx_t *ctx)
295 {
296     const LONG arg = ctx->instr->arg1.lng;
297     VARIANT v;
298
299     TRACE("%d\n", arg);
300
301     V_VT(&v) = VT_I4;
302     V_I4(&v) = arg;
303     return stack_push(ctx, &v);
304 }
305
306 static HRESULT interp_short(exec_ctx_t *ctx)
307 {
308     const LONG arg = ctx->instr->arg1.lng;
309     VARIANT v;
310
311     TRACE("%d\n", arg);
312
313     V_VT(&v) = VT_I2;
314     V_I2(&v) = arg;
315     return stack_push(ctx, &v);
316 }
317
318 static HRESULT interp_double(exec_ctx_t *ctx)
319 {
320     const DOUBLE *arg = ctx->instr->arg1.dbl;
321     VARIANT v;
322
323     TRACE("%lf\n", *arg);
324
325     V_VT(&v) = VT_R8;
326     V_R8(&v) = *arg;
327     return stack_push(ctx, &v);
328 }
329
330 static HRESULT interp_empty(exec_ctx_t *ctx)
331 {
332     VARIANT v;
333
334     TRACE("\n");
335
336     V_VT(&v) = VT_EMPTY;
337     return stack_push(ctx, &v);
338 }
339
340 static HRESULT interp_null(exec_ctx_t *ctx)
341 {
342     VARIANT v;
343
344     TRACE("\n");
345
346     V_VT(&v) = VT_NULL;
347     return stack_push(ctx, &v);
348 }
349
350 static HRESULT interp_not(exec_ctx_t *ctx)
351 {
352     variant_val_t val;
353     VARIANT v;
354     HRESULT hres;
355
356     TRACE("\n");
357
358     hres = stack_pop_val(ctx, &val);
359     if(FAILED(hres))
360         return hres;
361
362     hres = VarNot(val.v, &v);
363     release_val(&val);
364     if(FAILED(hres))
365         return hres;
366
367     return stack_push(ctx, &v);
368 }
369
370 static HRESULT cmp_oper(exec_ctx_t *ctx)
371 {
372     variant_val_t l, r;
373     HRESULT hres;
374
375     hres = stack_pop_val(ctx, &r);
376     if(FAILED(hres))
377         return hres;
378
379     hres = stack_pop_val(ctx, &l);
380     if(SUCCEEDED(hres)) {
381         if(V_VT(l.v) == VT_NULL || V_VT(r.v) == VT_NULL) {
382             FIXME("comparing nulls is not implemented\n");
383             hres = E_NOTIMPL;
384         }else {
385             hres = VarCmp(l.v, r.v, ctx->script->lcid, 0);
386         }
387     }
388
389     release_val(&r);
390     release_val(&l);
391     return hres;
392 }
393
394 static HRESULT interp_equal(exec_ctx_t *ctx)
395 {
396     VARIANT v;
397     HRESULT hres;
398
399     TRACE("\n");
400
401     hres = cmp_oper(ctx);
402     if(FAILED(hres))
403         return hres;
404
405     V_VT(&v) = VT_BOOL;
406     V_BOOL(&v) = hres == VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE;
407     return stack_push(ctx, &v);
408 }
409
410 static HRESULT interp_nequal(exec_ctx_t *ctx)
411 {
412     VARIANT v;
413     HRESULT hres;
414
415     TRACE("\n");
416
417     hres = cmp_oper(ctx);
418     if(FAILED(hres))
419         return hres;
420
421     V_VT(&v) = VT_BOOL;
422     V_BOOL(&v) = hres != VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE;
423     return stack_push(ctx, &v);
424 }
425
426 static HRESULT interp_concat(exec_ctx_t *ctx)
427 {
428     variant_val_t r, l;
429     VARIANT v;
430     HRESULT hres;
431
432     TRACE("\n");
433
434     hres = stack_pop_val(ctx, &r);
435     if(FAILED(hres))
436         return hres;
437
438     hres = stack_pop_val(ctx, &l);
439     if(SUCCEEDED(hres)) {
440         hres = VarCat(l.v, r.v, &v);
441         release_val(&l);
442     }
443     release_val(&r);
444     if(FAILED(hres))
445         return hres;
446
447     return stack_push(ctx, &v);
448 }
449
450 static HRESULT interp_add(exec_ctx_t *ctx)
451 {
452     variant_val_t r, l;
453     VARIANT v;
454     HRESULT hres;
455
456     TRACE("\n");
457
458     hres = stack_pop_val(ctx, &r);
459     if(FAILED(hres))
460         return hres;
461
462     hres = stack_pop_val(ctx, &l);
463     if(SUCCEEDED(hres)) {
464         hres = VarAdd(l.v, r.v, &v);
465         release_val(&l);
466     }
467     release_val(&r);
468     if(FAILED(hres))
469         return hres;
470
471     return stack_push(ctx, &v);
472 }
473
474 static HRESULT interp_sub(exec_ctx_t *ctx)
475 {
476     variant_val_t r, l;
477     VARIANT v;
478     HRESULT hres;
479
480     TRACE("\n");
481
482     hres = stack_pop_val(ctx, &r);
483     if(FAILED(hres))
484         return hres;
485
486     hres = stack_pop_val(ctx, &l);
487     if(SUCCEEDED(hres)) {
488         hres = VarSub(l.v, r.v, &v);
489         release_val(&l);
490     }
491     release_val(&r);
492     if(FAILED(hres))
493         return hres;
494
495     return stack_push(ctx, &v);
496 }
497
498 static HRESULT interp_neg(exec_ctx_t *ctx)
499 {
500     variant_val_t val;
501     VARIANT v;
502     HRESULT hres;
503
504     hres = stack_pop_val(ctx, &val);
505     if(FAILED(hres))
506         return hres;
507
508     hres = VarNeg(val.v, &v);
509     release_val(&val);
510     if(FAILED(hres))
511         return hres;
512
513     return stack_push(ctx, &v);
514 }
515
516 static const instr_func_t op_funcs[] = {
517 #define X(x,n,a,b) interp_ ## x,
518 OP_LIST
519 #undef X
520 };
521
522 static const unsigned op_move[] = {
523 #define X(x,n,a,b) n,
524 OP_LIST
525 #undef X
526 };
527
528 HRESULT exec_script(script_ctx_t *ctx, function_t *func)
529 {
530     exec_ctx_t exec;
531     vbsop_t op;
532     HRESULT hres = S_OK;
533
534     exec.stack_size = 16;
535     exec.top = 0;
536     exec.stack = heap_alloc(exec.stack_size * sizeof(VARIANT));
537     if(!exec.stack)
538         return E_OUTOFMEMORY;
539
540     exec.code = func->code_ctx;
541     exec.instr = exec.code->instrs + func->code_off;
542     exec.script = ctx;
543     exec.func = func;
544
545     while(exec.instr) {
546         op = exec.instr->op;
547         hres = op_funcs[op](&exec);
548         if(FAILED(hres)) {
549             FIXME("Failed %08x\n", hres);
550             stack_popn(&exec, exec.top);
551             break;
552         }
553
554         exec.instr += op_move[op];
555     }
556
557     assert(!exec.top);
558     heap_free(exec.stack);
559
560     return hres;
561 }