vbscript: Added parser/compiler support for |option explicit|.
[wine] / dlls / vbscript / compile.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 #include "parse.h"
23 #include "parser.tab.h"
24
25 #include "wine/debug.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(vbscript);
28
29 typedef struct {
30     parser_ctx_t parser;
31
32     unsigned instr_cnt;
33     unsigned instr_size;
34     vbscode_t *code;
35 } compile_ctx_t;
36
37 static HRESULT compile_expression(compile_ctx_t*,expression_t*);
38
39 static inline instr_t *instr_ptr(compile_ctx_t *ctx, unsigned id)
40 {
41     assert(id < ctx->instr_cnt);
42     return ctx->code->instrs + id;
43 }
44
45 static unsigned push_instr(compile_ctx_t *ctx, vbsop_t op)
46 {
47     assert(ctx->instr_size && ctx->instr_size >= ctx->instr_cnt);
48
49     if(ctx->instr_size == ctx->instr_cnt) {
50         instr_t *new_instr;
51
52         new_instr = heap_realloc(ctx->code->instrs, ctx->instr_size*2*sizeof(instr_t));
53         if(!new_instr)
54             return -1;
55
56         ctx->code->instrs = new_instr;
57         ctx->instr_size *= 2;
58     }
59
60     ctx->code->instrs[ctx->instr_cnt].op = op;
61     return ctx->instr_cnt++;
62 }
63
64 static HRESULT push_instr_int(compile_ctx_t *ctx, vbsop_t op, LONG arg)
65 {
66     unsigned ret;
67
68     ret = push_instr(ctx, op);
69     if(ret == -1)
70         return E_OUTOFMEMORY;
71
72     instr_ptr(ctx, ret)->arg1.lng = arg;
73     return S_OK;
74 }
75
76 static HRESULT push_instr_str(compile_ctx_t *ctx, vbsop_t op, const WCHAR *arg)
77 {
78     unsigned ret;
79
80     ret = push_instr(ctx, op);
81     if(ret == -1)
82         return E_OUTOFMEMORY;
83
84     instr_ptr(ctx, ret)->arg1.str = arg;
85     return S_OK;
86 }
87
88 static BSTR alloc_bstr_arg(compile_ctx_t *ctx, const WCHAR *str)
89 {
90     if(!ctx->code->bstr_pool_size) {
91         ctx->code->bstr_pool = heap_alloc(8 * sizeof(BSTR));
92         if(!ctx->code->bstr_pool)
93             return NULL;
94         ctx->code->bstr_pool_size = 8;
95     }else if(ctx->code->bstr_pool_size == ctx->code->bstr_cnt) {
96        BSTR *new_pool;
97
98         new_pool = heap_realloc(ctx->code->bstr_pool, ctx->code->bstr_pool_size*2*sizeof(BSTR));
99         if(!new_pool)
100             return NULL;
101
102         ctx->code->bstr_pool = new_pool;
103         ctx->code->bstr_pool_size *= 2;
104     }
105
106     ctx->code->bstr_pool[ctx->code->bstr_cnt] = SysAllocString(str);
107     if(!ctx->code->bstr_pool[ctx->code->bstr_cnt])
108         return NULL;
109
110     return ctx->code->bstr_pool[ctx->code->bstr_cnt++];
111 }
112
113 static HRESULT push_instr_bstr_uint(compile_ctx_t *ctx, vbsop_t op, const WCHAR *arg1, unsigned arg2)
114 {
115     unsigned instr;
116     BSTR bstr;
117
118     bstr = alloc_bstr_arg(ctx, arg1);
119     if(!bstr)
120         return E_OUTOFMEMORY;
121
122     instr = push_instr(ctx, op);
123     if(instr == -1)
124         return E_OUTOFMEMORY;
125
126     instr_ptr(ctx, instr)->arg1.bstr = bstr;
127     instr_ptr(ctx, instr)->arg2.uint = arg2;
128     return S_OK;
129 }
130
131 static HRESULT compile_args(compile_ctx_t *ctx, expression_t *args, unsigned *ret)
132 {
133     unsigned arg_cnt = 0;
134     HRESULT hres;
135
136     while(args) {
137         hres = compile_expression(ctx, args);
138         if(FAILED(hres))
139             return hres;
140
141         arg_cnt++;
142         args = args->next;
143     }
144
145     *ret = arg_cnt;
146     return S_OK;
147 }
148
149 static HRESULT compile_member_expression(compile_ctx_t *ctx, member_expression_t *expr)
150 {
151     unsigned arg_cnt = 0;
152     HRESULT hres;
153
154     hres = compile_args(ctx, expr->args, &arg_cnt);
155     if(FAILED(hres))
156         return hres;
157
158     if(expr->obj_expr) {
159         FIXME("obj_expr not implemented\n");
160         hres = E_NOTIMPL;
161     }else {
162         hres = push_instr_bstr_uint(ctx, OP_icallv, expr->identifier, arg_cnt);
163     }
164
165     return hres;
166 }
167
168 static HRESULT compile_expression(compile_ctx_t *ctx, expression_t *expr)
169 {
170     switch(expr->type) {
171     case EXPR_BOOL:
172         return push_instr_int(ctx, OP_bool, ((bool_expression_t*)expr)->value);
173     case EXPR_STRING:
174         return push_instr_str(ctx, OP_string, ((string_expression_t*)expr)->value);
175     default:
176         FIXME("Unimplemented expression type %d\n", expr->type);
177         return E_NOTIMPL;
178     }
179
180     return S_OK;
181 }
182
183 static HRESULT compile_statement(compile_ctx_t *ctx, statement_t *stat)
184 {
185     HRESULT hres;
186
187     while(stat) {
188         switch(stat->type) {
189         case STAT_CALL:
190             hres = compile_member_expression(ctx, ((call_statement_t*)stat)->expr);
191             break;
192         default:
193             FIXME("Unimplemented statement type %d\n", stat->type);
194             hres = E_NOTIMPL;
195         }
196
197         if(FAILED(hres))
198             return hres;
199         stat = stat->next;
200     }
201
202     return S_OK;
203 }
204
205 static HRESULT compile_func(compile_ctx_t *ctx, statement_t *stat, function_t *func)
206 {
207     HRESULT hres;
208
209     func->code_off = ctx->instr_cnt;
210
211     hres = compile_statement(ctx, stat);
212     if(FAILED(hres))
213         return hres;
214
215     if(push_instr(ctx, OP_ret) == -1)
216         return E_OUTOFMEMORY;
217
218     return S_OK;
219 }
220
221 void release_vbscode(vbscode_t *code)
222 {
223     unsigned i;
224
225     list_remove(&code->entry);
226
227     for(i=0; i < code->bstr_cnt; i++)
228         SysFreeString(code->bstr_pool[i]);
229
230     heap_free(code->bstr_pool);
231     heap_free(code->source);
232     heap_free(code->instrs);
233     heap_free(code);
234 }
235
236 static vbscode_t *alloc_vbscode(compile_ctx_t *ctx, const WCHAR *source)
237 {
238     vbscode_t *ret;
239
240     ret = heap_alloc(sizeof(*ret));
241     if(!ret)
242         return NULL;
243
244     ret->source = heap_strdupW(source);
245     if(!ret->source) {
246         heap_free(ret);
247         return NULL;
248     }
249
250     ret->instrs = heap_alloc(32*sizeof(instr_t));
251     if(!ret->instrs) {
252         release_vbscode(ret);
253         return NULL;
254     }
255
256     ctx->instr_cnt = 0;
257     ctx->instr_size = 32;
258
259     ret->option_explicit = ctx->parser.option_explicit;
260
261     ret->bstr_pool = NULL;
262     ret->bstr_pool_size = 0;
263     ret->bstr_cnt = 0;
264
265     ret->global_code.code_ctx = ret;
266
267     list_init(&ret->entry);
268     return ret;
269 }
270
271 HRESULT compile_script(script_ctx_t *script, const WCHAR *src, vbscode_t **ret)
272 {
273     compile_ctx_t ctx;
274     HRESULT hres;
275
276     hres = parse_script(&ctx.parser, src);
277     if(FAILED(hres))
278         return hres;
279
280     ctx.code = alloc_vbscode(&ctx, src);
281     if(!ctx.code)
282         return E_OUTOFMEMORY;
283
284     hres = compile_func(&ctx, ctx.parser.stats, &ctx.code->global_code);
285     if(FAILED(hres)) {
286         release_vbscode(ctx.code);
287         return hres;
288     }
289
290     list_add_tail(&script->code_list, &ctx.code->entry);
291     *ret = ctx.code;
292     return S_OK;
293 }