vbscript: Added compiler support for equality expression.
[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 interp_icallv(exec_ctx_t *ctx)
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     TRACE("\n");
180
181     hres = lookup_identifier(ctx, identifier, &ref);
182     if(FAILED(hres))
183         return hres;
184
185     vbstack_to_dp(ctx, arg_cnt, &dp);
186
187     switch(ref.type) {
188     case REF_DISP:
189         hres = disp_call(ctx->script, ref.u.d.disp, ref.u.d.id, &dp, NULL);
190         if(FAILED(hres))
191             return hres;
192         break;
193     default:
194         FIXME("%s not found\n", debugstr_w(identifier));
195         return DISP_E_UNKNOWNNAME;
196     }
197
198     stack_popn(ctx, arg_cnt);
199     return S_OK;
200 }
201
202 static HRESULT interp_ret(exec_ctx_t *ctx)
203 {
204     TRACE("\n");
205
206     ctx->instr = NULL;
207     return S_OK;
208 }
209
210 static HRESULT interp_bool(exec_ctx_t *ctx)
211 {
212     const VARIANT_BOOL arg = ctx->instr->arg1.lng;
213     VARIANT v;
214
215     TRACE("%s\n", arg ? "true" : "false");
216
217     V_VT(&v) = VT_BOOL;
218     V_BOOL(&v) = arg;
219     return stack_push(ctx, &v);
220 }
221
222 static HRESULT interp_string(exec_ctx_t *ctx)
223 {
224     VARIANT v;
225
226     TRACE("\n");
227
228     V_VT(&v) = VT_BSTR;
229     V_BSTR(&v) = SysAllocString(ctx->instr->arg1.str);
230     if(!V_BSTR(&v))
231         return E_OUTOFMEMORY;
232
233     return stack_push(ctx, &v);
234 }
235
236 static HRESULT interp_not(exec_ctx_t *ctx)
237 {
238     variant_val_t val;
239     VARIANT v;
240     HRESULT hres;
241
242     TRACE("\n");
243
244     hres = stack_pop_val(ctx, &val);
245     if(FAILED(hres))
246         return hres;
247
248     hres = VarNot(val.v, &v);
249     release_val(&val);
250     if(FAILED(hres))
251         return hres;
252
253     return stack_push(ctx, &v);
254 }
255
256 static HRESULT interp_equal(exec_ctx_t *ctx)
257 {
258     FIXME("\n");
259     return E_NOTIMPL;
260 }
261
262 static const instr_func_t op_funcs[] = {
263 #define X(x,n,a,b) interp_ ## x,
264 OP_LIST
265 #undef X
266 };
267
268 static const unsigned op_move[] = {
269 #define X(x,n,a,b) n,
270 OP_LIST
271 #undef X
272 };
273
274 HRESULT exec_script(script_ctx_t *ctx, function_t *func)
275 {
276     exec_ctx_t exec;
277     vbsop_t op;
278     HRESULT hres = S_OK;
279
280     exec.stack_size = 16;
281     exec.top = 0;
282     exec.stack = heap_alloc(exec.stack_size * sizeof(VARIANT));
283     if(!exec.stack)
284         return E_OUTOFMEMORY;
285
286     exec.code = func->code_ctx;
287     exec.instr = exec.code->instrs + func->code_off;
288     exec.script = ctx;
289     exec.func = func;
290
291     while(exec.instr) {
292         op = exec.instr->op;
293         hres = op_funcs[op](&exec);
294         if(FAILED(hres)) {
295             FIXME("Failed %08x\n", hres);
296             stack_popn(&exec, exec.top);
297             break;
298         }
299
300         exec.instr += op_move[op];
301     }
302
303     assert(!exec.top);
304     heap_free(exec.stack);
305
306     return hres;
307 }