vbscript: Added interp_sub 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 interp_ret(exec_ctx_t *ctx)
221 {
222     TRACE("\n");
223
224     ctx->instr = NULL;
225     return S_OK;
226 }
227
228 static HRESULT interp_bool(exec_ctx_t *ctx)
229 {
230     const VARIANT_BOOL arg = ctx->instr->arg1.lng;
231     VARIANT v;
232
233     TRACE("%s\n", arg ? "true" : "false");
234
235     V_VT(&v) = VT_BOOL;
236     V_BOOL(&v) = arg;
237     return stack_push(ctx, &v);
238 }
239
240 static HRESULT interp_string(exec_ctx_t *ctx)
241 {
242     VARIANT v;
243
244     TRACE("\n");
245
246     V_VT(&v) = VT_BSTR;
247     V_BSTR(&v) = SysAllocString(ctx->instr->arg1.str);
248     if(!V_BSTR(&v))
249         return E_OUTOFMEMORY;
250
251     return stack_push(ctx, &v);
252 }
253
254 static HRESULT interp_long(exec_ctx_t *ctx)
255 {
256     const LONG arg = ctx->instr->arg1.lng;
257     VARIANT v;
258
259     TRACE("%d\n", arg);
260
261     V_VT(&v) = VT_I4;
262     V_I4(&v) = arg;
263     return stack_push(ctx, &v);
264 }
265
266 static HRESULT interp_short(exec_ctx_t *ctx)
267 {
268     const LONG arg = ctx->instr->arg1.lng;
269     VARIANT v;
270
271     TRACE("%d\n", arg);
272
273     V_VT(&v) = VT_I2;
274     V_I2(&v) = arg;
275     return stack_push(ctx, &v);
276 }
277
278 static HRESULT interp_double(exec_ctx_t *ctx)
279 {
280     const DOUBLE *arg = ctx->instr->arg1.dbl;
281     VARIANT v;
282
283     TRACE("%lf\n", *arg);
284
285     V_VT(&v) = VT_R8;
286     V_R8(&v) = *arg;
287     return stack_push(ctx, &v);
288 }
289
290 static HRESULT interp_empty(exec_ctx_t *ctx)
291 {
292     VARIANT v;
293
294     TRACE("\n");
295
296     V_VT(&v) = VT_EMPTY;
297     return stack_push(ctx, &v);
298 }
299
300 static HRESULT interp_null(exec_ctx_t *ctx)
301 {
302     VARIANT v;
303
304     TRACE("\n");
305
306     V_VT(&v) = VT_NULL;
307     return stack_push(ctx, &v);
308 }
309
310 static HRESULT interp_not(exec_ctx_t *ctx)
311 {
312     variant_val_t val;
313     VARIANT v;
314     HRESULT hres;
315
316     TRACE("\n");
317
318     hres = stack_pop_val(ctx, &val);
319     if(FAILED(hres))
320         return hres;
321
322     hres = VarNot(val.v, &v);
323     release_val(&val);
324     if(FAILED(hres))
325         return hres;
326
327     return stack_push(ctx, &v);
328 }
329
330 static HRESULT cmp_oper(exec_ctx_t *ctx)
331 {
332     variant_val_t l, r;
333     HRESULT hres;
334
335     hres = stack_pop_val(ctx, &r);
336     if(FAILED(hres))
337         return hres;
338
339     hres = stack_pop_val(ctx, &l);
340     if(SUCCEEDED(hres)) {
341         if(V_VT(l.v) == VT_NULL || V_VT(r.v) == VT_NULL) {
342             FIXME("comparing nulls is not implemented\n");
343             hres = E_NOTIMPL;
344         }else {
345             hres = VarCmp(l.v, r.v, ctx->script->lcid, 0);
346         }
347     }
348
349     release_val(&r);
350     release_val(&l);
351     return hres;
352 }
353
354 static HRESULT interp_equal(exec_ctx_t *ctx)
355 {
356     VARIANT v;
357     HRESULT hres;
358
359     TRACE("\n");
360
361     hres = cmp_oper(ctx);
362     if(FAILED(hres))
363         return hres;
364
365     V_VT(&v) = VT_BOOL;
366     V_BOOL(&v) = hres == VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE;
367     return stack_push(ctx, &v);
368 }
369
370 static HRESULT interp_concat(exec_ctx_t *ctx)
371 {
372     variant_val_t r, l;
373     VARIANT v;
374     HRESULT hres;
375
376     TRACE("\n");
377
378     hres = stack_pop_val(ctx, &r);
379     if(FAILED(hres))
380         return hres;
381
382     hres = stack_pop_val(ctx, &l);
383     if(SUCCEEDED(hres)) {
384         hres = VarCat(l.v, r.v, &v);
385         release_val(&l);
386     }
387     release_val(&r);
388     if(FAILED(hres))
389         return hres;
390
391     return stack_push(ctx, &v);
392 }
393
394 static HRESULT interp_add(exec_ctx_t *ctx)
395 {
396     variant_val_t r, l;
397     VARIANT v;
398     HRESULT hres;
399
400     TRACE("\n");
401
402     hres = stack_pop_val(ctx, &r);
403     if(FAILED(hres))
404         return hres;
405
406     hres = stack_pop_val(ctx, &l);
407     if(SUCCEEDED(hres)) {
408         hres = VarAdd(l.v, r.v, &v);
409         release_val(&l);
410     }
411     release_val(&r);
412     if(FAILED(hres))
413         return hres;
414
415     return stack_push(ctx, &v);
416 }
417
418 static HRESULT interp_sub(exec_ctx_t *ctx)
419 {
420     variant_val_t r, l;
421     VARIANT v;
422     HRESULT hres;
423
424     TRACE("\n");
425
426     hres = stack_pop_val(ctx, &r);
427     if(FAILED(hres))
428         return hres;
429
430     hres = stack_pop_val(ctx, &l);
431     if(SUCCEEDED(hres)) {
432         hres = VarSub(l.v, r.v, &v);
433         release_val(&l);
434     }
435     release_val(&r);
436     if(FAILED(hres))
437         return hres;
438
439     return stack_push(ctx, &v);
440 }
441
442 static HRESULT interp_neg(exec_ctx_t *ctx)
443 {
444     variant_val_t val;
445     VARIANT v;
446     HRESULT hres;
447
448     hres = stack_pop_val(ctx, &val);
449     if(FAILED(hres))
450         return hres;
451
452     hres = VarNeg(val.v, &v);
453     release_val(&val);
454     if(FAILED(hres))
455         return hres;
456
457     return stack_push(ctx, &v);
458 }
459
460 static const instr_func_t op_funcs[] = {
461 #define X(x,n,a,b) interp_ ## x,
462 OP_LIST
463 #undef X
464 };
465
466 static const unsigned op_move[] = {
467 #define X(x,n,a,b) n,
468 OP_LIST
469 #undef X
470 };
471
472 HRESULT exec_script(script_ctx_t *ctx, function_t *func)
473 {
474     exec_ctx_t exec;
475     vbsop_t op;
476     HRESULT hres = S_OK;
477
478     exec.stack_size = 16;
479     exec.top = 0;
480     exec.stack = heap_alloc(exec.stack_size * sizeof(VARIANT));
481     if(!exec.stack)
482         return E_OUTOFMEMORY;
483
484     exec.code = func->code_ctx;
485     exec.instr = exec.code->instrs + func->code_off;
486     exec.script = ctx;
487     exec.func = func;
488
489     while(exec.instr) {
490         op = exec.instr->op;
491         hres = op_funcs[op](&exec);
492         if(FAILED(hres)) {
493             FIXME("Failed %08x\n", hres);
494             stack_popn(&exec, exec.top);
495             break;
496         }
497
498         exec.instr += op_move[op];
499     }
500
501     assert(!exec.top);
502     heap_free(exec.stack);
503
504     return hres;
505 }