vbscript: Added compiler support for numeric literals.
[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     FIXME("\n");
257     return E_NOTIMPL;
258 }
259
260 static HRESULT interp_short(exec_ctx_t *ctx)
261 {
262     FIXME("\n");
263     return E_NOTIMPL;
264 }
265
266 static HRESULT interp_double(exec_ctx_t *ctx)
267 {
268     FIXME("\n");
269     return E_NOTIMPL;
270 }
271
272 static HRESULT interp_empty(exec_ctx_t *ctx)
273 {
274     VARIANT v;
275
276     TRACE("\n");
277
278     V_VT(&v) = VT_EMPTY;
279     return stack_push(ctx, &v);
280 }
281
282 static HRESULT interp_null(exec_ctx_t *ctx)
283 {
284     VARIANT v;
285
286     TRACE("\n");
287
288     V_VT(&v) = VT_NULL;
289     return stack_push(ctx, &v);
290 }
291
292 static HRESULT interp_not(exec_ctx_t *ctx)
293 {
294     variant_val_t val;
295     VARIANT v;
296     HRESULT hres;
297
298     TRACE("\n");
299
300     hres = stack_pop_val(ctx, &val);
301     if(FAILED(hres))
302         return hres;
303
304     hres = VarNot(val.v, &v);
305     release_val(&val);
306     if(FAILED(hres))
307         return hres;
308
309     return stack_push(ctx, &v);
310 }
311
312 static HRESULT cmp_oper(exec_ctx_t *ctx)
313 {
314     variant_val_t l, r;
315     HRESULT hres;
316
317     hres = stack_pop_val(ctx, &r);
318     if(FAILED(hres))
319         return hres;
320
321     hres = stack_pop_val(ctx, &l);
322     if(SUCCEEDED(hres)) {
323         if(V_VT(l.v) == VT_NULL || V_VT(r.v) == VT_NULL) {
324             FIXME("comparing nulls is not implemented\n");
325             hres = E_NOTIMPL;
326         }else {
327             hres = VarCmp(l.v, r.v, ctx->script->lcid, 0);
328         }
329     }
330
331     release_val(&r);
332     release_val(&l);
333     return hres;
334 }
335
336 static HRESULT interp_equal(exec_ctx_t *ctx)
337 {
338     VARIANT v;
339     HRESULT hres;
340
341     TRACE("\n");
342
343     hres = cmp_oper(ctx);
344     if(FAILED(hres))
345         return hres;
346
347     V_VT(&v) = VT_BOOL;
348     V_BOOL(&v) = hres == VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE;
349     return stack_push(ctx, &v);
350 }
351
352 static const instr_func_t op_funcs[] = {
353 #define X(x,n,a,b) interp_ ## x,
354 OP_LIST
355 #undef X
356 };
357
358 static const unsigned op_move[] = {
359 #define X(x,n,a,b) n,
360 OP_LIST
361 #undef X
362 };
363
364 HRESULT exec_script(script_ctx_t *ctx, function_t *func)
365 {
366     exec_ctx_t exec;
367     vbsop_t op;
368     HRESULT hres = S_OK;
369
370     exec.stack_size = 16;
371     exec.top = 0;
372     exec.stack = heap_alloc(exec.stack_size * sizeof(VARIANT));
373     if(!exec.stack)
374         return E_OUTOFMEMORY;
375
376     exec.code = func->code_ctx;
377     exec.instr = exec.code->instrs + func->code_off;
378     exec.script = ctx;
379     exec.func = func;
380
381     while(exec.instr) {
382         op = exec.instr->op;
383         hres = op_funcs[op](&exec);
384         if(FAILED(hres)) {
385             FIXME("Failed %08x\n", hres);
386             stack_popn(&exec, exec.top);
387             break;
388         }
389
390         exec.instr += op_move[op];
391     }
392
393     assert(!exec.top);
394     heap_free(exec.stack);
395
396     return hres;
397 }