vbscript: Added parser/compiler support for |option explicit|.
[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 static HRESULT lookup_identifier(exec_ctx_t *ctx, BSTR name, ref_t *ref)
57 {
58     named_item_t *item;
59     DISPID id;
60     HRESULT hres;
61
62     LIST_FOR_EACH_ENTRY(item, &ctx->script->named_items, named_item_t, entry) {
63         if(item->flags & SCRIPTITEM_GLOBALMEMBERS) {
64             hres = disp_get_id(item->disp, name, &id);
65             if(SUCCEEDED(hres)) {
66                 ref->type = REF_DISP;
67                 ref->u.d.disp = item->disp;
68                 ref->u.d.id = id;
69                 return S_OK;
70             }
71         }
72     }
73
74     if(!ctx->func->code_ctx->option_explicit)
75         FIXME("create an attempt to set\n");
76
77     ref->type = REF_NONE;
78     return S_OK;
79 }
80
81 static inline VARIANT *stack_pop(exec_ctx_t *ctx)
82 {
83     assert(ctx->top);
84     return ctx->stack + --ctx->top;
85 }
86
87 static HRESULT stack_push(exec_ctx_t *ctx, VARIANT *v)
88 {
89     if(ctx->stack_size == ctx->top) {
90         VARIANT *new_stack;
91
92         new_stack = heap_realloc(ctx->stack, ctx->stack_size*2);
93         if(!new_stack) {
94             VariantClear(v);
95             return E_OUTOFMEMORY;
96         }
97
98         ctx->stack = new_stack;
99         ctx->stack_size *= 2;
100     }
101
102     ctx->stack[ctx->top++] = *v;
103     return S_OK;
104 }
105
106 static void stack_popn(exec_ctx_t *ctx, unsigned n)
107 {
108     while(n--)
109         VariantClear(stack_pop(ctx));
110 }
111
112 static void vbstack_to_dp(exec_ctx_t *ctx, unsigned arg_cnt, DISPPARAMS *dp)
113 {
114     dp->cArgs = arg_cnt;
115     dp->rgdispidNamedArgs = NULL;
116     dp->cNamedArgs = 0;
117
118     if(arg_cnt) {
119         VARIANT tmp;
120         unsigned i;
121
122         assert(ctx->top >= arg_cnt);
123
124         for(i=1; i*2 <= arg_cnt; i++) {
125             tmp = ctx->stack[ctx->top-i];
126             ctx->stack[ctx->top-i] = ctx->stack[ctx->top-arg_cnt+i-1];
127             ctx->stack[ctx->top-arg_cnt+i-1] = tmp;
128         }
129
130         dp->rgvarg = ctx->stack + ctx->top-arg_cnt;
131     }else {
132         dp->rgvarg = NULL;
133     }
134 }
135
136 static HRESULT interp_icallv(exec_ctx_t *ctx)
137 {
138     BSTR identifier = ctx->instr->arg1.bstr;
139     const unsigned arg_cnt = ctx->instr->arg2.uint;
140     ref_t ref = {0};
141     DISPPARAMS dp;
142     HRESULT hres;
143
144     TRACE("\n");
145
146     hres = lookup_identifier(ctx, identifier, &ref);
147     if(FAILED(hres))
148         return hres;
149
150     vbstack_to_dp(ctx, arg_cnt, &dp);
151
152     switch(ref.type) {
153     case REF_DISP:
154         hres = disp_call(ctx->script, ref.u.d.disp, ref.u.d.id, &dp, NULL);
155         if(FAILED(hres))
156             return hres;
157         break;
158     default:
159         FIXME("%s not found\n", debugstr_w(identifier));
160         return DISP_E_UNKNOWNNAME;
161     }
162
163     stack_popn(ctx, arg_cnt);
164     return S_OK;
165 }
166
167 static HRESULT interp_ret(exec_ctx_t *ctx)
168 {
169     TRACE("\n");
170
171     ctx->instr = NULL;
172     return S_OK;
173 }
174
175 static HRESULT interp_bool(exec_ctx_t *ctx)
176 {
177     const VARIANT_BOOL arg = ctx->instr->arg1.lng;
178     VARIANT v;
179
180     TRACE("%s\n", arg ? "true" : "false");
181
182     V_VT(&v) = VT_BOOL;
183     V_BOOL(&v) = arg;
184     return stack_push(ctx, &v);
185 }
186
187 static HRESULT interp_string(exec_ctx_t *ctx)
188 {
189     VARIANT v;
190
191     TRACE("\n");
192
193     V_VT(&v) = VT_BSTR;
194     V_BSTR(&v) = SysAllocString(ctx->instr->arg1.str);
195     if(!V_BSTR(&v))
196         return E_OUTOFMEMORY;
197
198     return stack_push(ctx, &v);
199 }
200
201 static const instr_func_t op_funcs[] = {
202 #define X(x,n,a,b) interp_ ## x,
203 OP_LIST
204 #undef X
205 };
206
207 static const unsigned op_move[] = {
208 #define X(x,n,a,b) n,
209 OP_LIST
210 #undef X
211 };
212
213 HRESULT exec_script(script_ctx_t *ctx, function_t *func)
214 {
215     exec_ctx_t exec;
216     vbsop_t op;
217     HRESULT hres = S_OK;
218
219     exec.stack_size = 16;
220     exec.top = 0;
221     exec.stack = heap_alloc(exec.stack_size * sizeof(VARIANT));
222     if(!exec.stack)
223         return E_OUTOFMEMORY;
224
225     exec.code = func->code_ctx;
226     exec.instr = exec.code->instrs + func->code_off;
227     exec.script = ctx;
228     exec.func = func;
229
230     while(exec.instr) {
231         op = exec.instr->op;
232         hres = op_funcs[op](&exec);
233         if(FAILED(hres)) {
234             FIXME("Failed %08x\n", hres);
235             stack_popn(&exec, exec.top);
236             break;
237         }
238
239         exec.instr += op_move[op];
240     }
241
242     assert(!exec.top);
243     heap_free(exec.stack);
244
245     return hres;
246 }