vbscipt: Added object member assign parser/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_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_assign_member(exec_ctx_t *ctx)
261 {
262     FIXME("\n");
263     return E_NOTIMPL;
264 }
265
266 static HRESULT interp_ret(exec_ctx_t *ctx)
267 {
268     TRACE("\n");
269
270     ctx->instr = NULL;
271     return S_OK;
272 }
273
274 static HRESULT interp_bool(exec_ctx_t *ctx)
275 {
276     const VARIANT_BOOL arg = ctx->instr->arg1.lng;
277     VARIANT v;
278
279     TRACE("%s\n", arg ? "true" : "false");
280
281     V_VT(&v) = VT_BOOL;
282     V_BOOL(&v) = arg;
283     return stack_push(ctx, &v);
284 }
285
286 static HRESULT interp_string(exec_ctx_t *ctx)
287 {
288     VARIANT v;
289
290     TRACE("\n");
291
292     V_VT(&v) = VT_BSTR;
293     V_BSTR(&v) = SysAllocString(ctx->instr->arg1.str);
294     if(!V_BSTR(&v))
295         return E_OUTOFMEMORY;
296
297     return stack_push(ctx, &v);
298 }
299
300 static HRESULT interp_long(exec_ctx_t *ctx)
301 {
302     const LONG arg = ctx->instr->arg1.lng;
303     VARIANT v;
304
305     TRACE("%d\n", arg);
306
307     V_VT(&v) = VT_I4;
308     V_I4(&v) = arg;
309     return stack_push(ctx, &v);
310 }
311
312 static HRESULT interp_short(exec_ctx_t *ctx)
313 {
314     const LONG arg = ctx->instr->arg1.lng;
315     VARIANT v;
316
317     TRACE("%d\n", arg);
318
319     V_VT(&v) = VT_I2;
320     V_I2(&v) = arg;
321     return stack_push(ctx, &v);
322 }
323
324 static HRESULT interp_double(exec_ctx_t *ctx)
325 {
326     const DOUBLE *arg = ctx->instr->arg1.dbl;
327     VARIANT v;
328
329     TRACE("%lf\n", *arg);
330
331     V_VT(&v) = VT_R8;
332     V_R8(&v) = *arg;
333     return stack_push(ctx, &v);
334 }
335
336 static HRESULT interp_empty(exec_ctx_t *ctx)
337 {
338     VARIANT v;
339
340     TRACE("\n");
341
342     V_VT(&v) = VT_EMPTY;
343     return stack_push(ctx, &v);
344 }
345
346 static HRESULT interp_null(exec_ctx_t *ctx)
347 {
348     VARIANT v;
349
350     TRACE("\n");
351
352     V_VT(&v) = VT_NULL;
353     return stack_push(ctx, &v);
354 }
355
356 static HRESULT interp_not(exec_ctx_t *ctx)
357 {
358     variant_val_t val;
359     VARIANT v;
360     HRESULT hres;
361
362     TRACE("\n");
363
364     hres = stack_pop_val(ctx, &val);
365     if(FAILED(hres))
366         return hres;
367
368     hres = VarNot(val.v, &v);
369     release_val(&val);
370     if(FAILED(hres))
371         return hres;
372
373     return stack_push(ctx, &v);
374 }
375
376 static HRESULT cmp_oper(exec_ctx_t *ctx)
377 {
378     variant_val_t l, r;
379     HRESULT hres;
380
381     hres = stack_pop_val(ctx, &r);
382     if(FAILED(hres))
383         return hres;
384
385     hres = stack_pop_val(ctx, &l);
386     if(SUCCEEDED(hres)) {
387         if(V_VT(l.v) == VT_NULL || V_VT(r.v) == VT_NULL) {
388             FIXME("comparing nulls is not implemented\n");
389             hres = E_NOTIMPL;
390         }else {
391             hres = VarCmp(l.v, r.v, ctx->script->lcid, 0);
392         }
393     }
394
395     release_val(&r);
396     release_val(&l);
397     return hres;
398 }
399
400 static HRESULT interp_equal(exec_ctx_t *ctx)
401 {
402     VARIANT v;
403     HRESULT hres;
404
405     TRACE("\n");
406
407     hres = cmp_oper(ctx);
408     if(FAILED(hres))
409         return hres;
410
411     V_VT(&v) = VT_BOOL;
412     V_BOOL(&v) = hres == VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE;
413     return stack_push(ctx, &v);
414 }
415
416 static HRESULT interp_nequal(exec_ctx_t *ctx)
417 {
418     VARIANT v;
419     HRESULT hres;
420
421     TRACE("\n");
422
423     hres = cmp_oper(ctx);
424     if(FAILED(hres))
425         return hres;
426
427     V_VT(&v) = VT_BOOL;
428     V_BOOL(&v) = hres != VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE;
429     return stack_push(ctx, &v);
430 }
431
432 static HRESULT interp_concat(exec_ctx_t *ctx)
433 {
434     variant_val_t r, l;
435     VARIANT v;
436     HRESULT hres;
437
438     TRACE("\n");
439
440     hres = stack_pop_val(ctx, &r);
441     if(FAILED(hres))
442         return hres;
443
444     hres = stack_pop_val(ctx, &l);
445     if(SUCCEEDED(hres)) {
446         hres = VarCat(l.v, r.v, &v);
447         release_val(&l);
448     }
449     release_val(&r);
450     if(FAILED(hres))
451         return hres;
452
453     return stack_push(ctx, &v);
454 }
455
456 static HRESULT interp_add(exec_ctx_t *ctx)
457 {
458     variant_val_t r, l;
459     VARIANT v;
460     HRESULT hres;
461
462     TRACE("\n");
463
464     hres = stack_pop_val(ctx, &r);
465     if(FAILED(hres))
466         return hres;
467
468     hres = stack_pop_val(ctx, &l);
469     if(SUCCEEDED(hres)) {
470         hres = VarAdd(l.v, r.v, &v);
471         release_val(&l);
472     }
473     release_val(&r);
474     if(FAILED(hres))
475         return hres;
476
477     return stack_push(ctx, &v);
478 }
479
480 static HRESULT interp_sub(exec_ctx_t *ctx)
481 {
482     variant_val_t r, l;
483     VARIANT v;
484     HRESULT hres;
485
486     TRACE("\n");
487
488     hres = stack_pop_val(ctx, &r);
489     if(FAILED(hres))
490         return hres;
491
492     hres = stack_pop_val(ctx, &l);
493     if(SUCCEEDED(hres)) {
494         hres = VarSub(l.v, r.v, &v);
495         release_val(&l);
496     }
497     release_val(&r);
498     if(FAILED(hres))
499         return hres;
500
501     return stack_push(ctx, &v);
502 }
503
504 static HRESULT interp_neg(exec_ctx_t *ctx)
505 {
506     variant_val_t val;
507     VARIANT v;
508     HRESULT hres;
509
510     hres = stack_pop_val(ctx, &val);
511     if(FAILED(hres))
512         return hres;
513
514     hres = VarNeg(val.v, &v);
515     release_val(&val);
516     if(FAILED(hres))
517         return hres;
518
519     return stack_push(ctx, &v);
520 }
521
522 static const instr_func_t op_funcs[] = {
523 #define X(x,n,a,b) interp_ ## x,
524 OP_LIST
525 #undef X
526 };
527
528 static const unsigned op_move[] = {
529 #define X(x,n,a,b) n,
530 OP_LIST
531 #undef X
532 };
533
534 HRESULT exec_script(script_ctx_t *ctx, function_t *func)
535 {
536     exec_ctx_t exec;
537     vbsop_t op;
538     HRESULT hres = S_OK;
539
540     exec.stack_size = 16;
541     exec.top = 0;
542     exec.stack = heap_alloc(exec.stack_size * sizeof(VARIANT));
543     if(!exec.stack)
544         return E_OUTOFMEMORY;
545
546     exec.code = func->code_ctx;
547     exec.instr = exec.code->instrs + func->code_off;
548     exec.script = ctx;
549     exec.func = func;
550
551     while(exec.instr) {
552         op = exec.instr->op;
553         hres = op_funcs[op](&exec);
554         if(FAILED(hres)) {
555             FIXME("Failed %08x\n", hres);
556             stack_popn(&exec, exec.top);
557             break;
558         }
559
560         exec.instr += op_move[op];
561     }
562
563     assert(!exec.top);
564     heap_free(exec.stack);
565
566     return hres;
567 }