vbscript: Added dim statement compiler implementation.
[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
36     dim_decl_t *dim_decls;
37     dynamic_var_t *global_vars;
38 } compile_ctx_t;
39
40 static HRESULT compile_expression(compile_ctx_t*,expression_t*);
41
42 static inline void *compiler_alloc(vbscode_t *vbscode, size_t size)
43 {
44     return vbsheap_alloc(&vbscode->heap, size);
45 }
46
47 static WCHAR *compiler_alloc_string(vbscode_t *vbscode, const WCHAR *str)
48 {
49     size_t size;
50     WCHAR *ret;
51
52     size = (strlenW(str)+1)*sizeof(WCHAR);
53     ret = compiler_alloc(vbscode, size);
54     if(ret)
55         memcpy(ret, str, size);
56     return ret;
57 }
58
59 static inline instr_t *instr_ptr(compile_ctx_t *ctx, unsigned id)
60 {
61     assert(id < ctx->instr_cnt);
62     return ctx->code->instrs + id;
63 }
64
65 static unsigned push_instr(compile_ctx_t *ctx, vbsop_t op)
66 {
67     assert(ctx->instr_size && ctx->instr_size >= ctx->instr_cnt);
68
69     if(ctx->instr_size == ctx->instr_cnt) {
70         instr_t *new_instr;
71
72         new_instr = heap_realloc(ctx->code->instrs, ctx->instr_size*2*sizeof(instr_t));
73         if(!new_instr)
74             return -1;
75
76         ctx->code->instrs = new_instr;
77         ctx->instr_size *= 2;
78     }
79
80     ctx->code->instrs[ctx->instr_cnt].op = op;
81     return ctx->instr_cnt++;
82 }
83
84 static HRESULT push_instr_int(compile_ctx_t *ctx, vbsop_t op, LONG arg)
85 {
86     unsigned ret;
87
88     ret = push_instr(ctx, op);
89     if(ret == -1)
90         return E_OUTOFMEMORY;
91
92     instr_ptr(ctx, ret)->arg1.lng = arg;
93     return S_OK;
94 }
95
96 static HRESULT push_instr_str(compile_ctx_t *ctx, vbsop_t op, const WCHAR *arg)
97 {
98     unsigned instr;
99     WCHAR *str;
100
101     str = compiler_alloc_string(ctx->code, arg);
102     if(!str)
103         return E_OUTOFMEMORY;
104
105     instr = push_instr(ctx, op);
106     if(instr == -1)
107         return E_OUTOFMEMORY;
108
109     instr_ptr(ctx, instr)->arg1.str = str;
110     return S_OK;
111 }
112
113 static HRESULT push_instr_double(compile_ctx_t *ctx, vbsop_t op, double arg)
114 {
115     unsigned instr;
116     double *d;
117
118     d = compiler_alloc(ctx->code, sizeof(double));
119     if(!d)
120         return E_OUTOFMEMORY;
121
122     instr = push_instr(ctx, op);
123     if(instr == -1)
124         return E_OUTOFMEMORY;
125
126     *d = arg;
127     instr_ptr(ctx, instr)->arg1.dbl = d;
128     return S_OK;
129 }
130
131 static BSTR alloc_bstr_arg(compile_ctx_t *ctx, const WCHAR *str)
132 {
133     if(!ctx->code->bstr_pool_size) {
134         ctx->code->bstr_pool = heap_alloc(8 * sizeof(BSTR));
135         if(!ctx->code->bstr_pool)
136             return NULL;
137         ctx->code->bstr_pool_size = 8;
138     }else if(ctx->code->bstr_pool_size == ctx->code->bstr_cnt) {
139        BSTR *new_pool;
140
141         new_pool = heap_realloc(ctx->code->bstr_pool, ctx->code->bstr_pool_size*2*sizeof(BSTR));
142         if(!new_pool)
143             return NULL;
144
145         ctx->code->bstr_pool = new_pool;
146         ctx->code->bstr_pool_size *= 2;
147     }
148
149     ctx->code->bstr_pool[ctx->code->bstr_cnt] = SysAllocString(str);
150     if(!ctx->code->bstr_pool[ctx->code->bstr_cnt])
151         return NULL;
152
153     return ctx->code->bstr_pool[ctx->code->bstr_cnt++];
154 }
155
156 static HRESULT push_instr_bstr(compile_ctx_t *ctx, vbsop_t op, const WCHAR *arg)
157 {
158     unsigned instr;
159     BSTR bstr;
160
161     bstr = alloc_bstr_arg(ctx, arg);
162     if(!bstr)
163         return E_OUTOFMEMORY;
164
165     instr = push_instr(ctx, op);
166     if(instr == -1)
167         return E_OUTOFMEMORY;
168
169     instr_ptr(ctx, instr)->arg1.bstr = bstr;
170     return S_OK;
171 }
172
173 static HRESULT push_instr_bstr_uint(compile_ctx_t *ctx, vbsop_t op, const WCHAR *arg1, unsigned arg2)
174 {
175     unsigned instr;
176     BSTR bstr;
177
178     bstr = alloc_bstr_arg(ctx, arg1);
179     if(!bstr)
180         return E_OUTOFMEMORY;
181
182     instr = push_instr(ctx, op);
183     if(instr == -1)
184         return E_OUTOFMEMORY;
185
186     instr_ptr(ctx, instr)->arg1.bstr = bstr;
187     instr_ptr(ctx, instr)->arg2.uint = arg2;
188     return S_OK;
189 }
190
191 static HRESULT compile_args(compile_ctx_t *ctx, expression_t *args, unsigned *ret)
192 {
193     unsigned arg_cnt = 0;
194     HRESULT hres;
195
196     while(args) {
197         hres = compile_expression(ctx, args);
198         if(FAILED(hres))
199             return hres;
200
201         arg_cnt++;
202         args = args->next;
203     }
204
205     *ret = arg_cnt;
206     return S_OK;
207 }
208
209 static HRESULT compile_member_expression(compile_ctx_t *ctx, member_expression_t *expr, BOOL ret_val)
210 {
211     unsigned arg_cnt = 0;
212     HRESULT hres;
213
214     hres = compile_args(ctx, expr->args, &arg_cnt);
215     if(FAILED(hres))
216         return hres;
217
218     if(expr->obj_expr) {
219         FIXME("obj_expr not implemented\n");
220         hres = E_NOTIMPL;
221     }else {
222         hres = push_instr_bstr_uint(ctx, ret_val ? OP_icall : OP_icallv, expr->identifier, arg_cnt);
223     }
224
225     return hres;
226 }
227
228 static HRESULT compile_unary_expression(compile_ctx_t *ctx, unary_expression_t *expr, vbsop_t op)
229 {
230     HRESULT hres;
231
232     hres = compile_expression(ctx, expr->subexpr);
233     if(FAILED(hres))
234         return hres;
235
236     return push_instr(ctx, op) == -1 ? E_OUTOFMEMORY : S_OK;
237 }
238
239 static HRESULT compile_binary_expression(compile_ctx_t *ctx, binary_expression_t *expr, vbsop_t op)
240 {
241     HRESULT hres;
242
243     hres = compile_expression(ctx, expr->left);
244     if(FAILED(hres))
245         return hres;
246
247     hres = compile_expression(ctx, expr->right);
248     if(FAILED(hres))
249         return hres;
250
251     return push_instr(ctx, op) == -1 ? E_OUTOFMEMORY : S_OK;
252 }
253
254 static HRESULT compile_expression(compile_ctx_t *ctx, expression_t *expr)
255 {
256     switch(expr->type) {
257     case EXPR_ADD:
258         return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_add);
259     case EXPR_BOOL:
260         return push_instr_int(ctx, OP_bool, ((bool_expression_t*)expr)->value);
261     case EXPR_CONCAT:
262         return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_concat);
263     case EXPR_DOUBLE:
264         return push_instr_double(ctx, OP_double, ((double_expression_t*)expr)->value);
265     case EXPR_EMPTY:
266         return push_instr(ctx, OP_empty) != -1 ? S_OK : E_OUTOFMEMORY;
267     case EXPR_EQUAL:
268         return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_equal);
269     case EXPR_MEMBER:
270         return compile_member_expression(ctx, (member_expression_t*)expr, TRUE);
271     case EXPR_NEG:
272         return compile_unary_expression(ctx, (unary_expression_t*)expr, OP_neg);
273     case EXPR_NEQUAL:
274         return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_nequal);
275     case EXPR_NOT:
276         return compile_unary_expression(ctx, (unary_expression_t*)expr, OP_not);
277     case EXPR_NULL:
278         return push_instr(ctx, OP_null) != -1 ? S_OK : E_OUTOFMEMORY;
279     case EXPR_STRING:
280         return push_instr_str(ctx, OP_string, ((string_expression_t*)expr)->value);
281     case EXPR_SUB:
282         return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_sub);
283     case EXPR_USHORT:
284         return push_instr_int(ctx, OP_short, ((int_expression_t*)expr)->value);
285     case EXPR_ULONG:
286         return push_instr_int(ctx, OP_long, ((int_expression_t*)expr)->value);
287     default:
288         FIXME("Unimplemented expression type %d\n", expr->type);
289         return E_NOTIMPL;
290     }
291
292     return S_OK;
293 }
294
295 static HRESULT compile_assign_statement(compile_ctx_t *ctx, assign_statement_t *stat)
296 {
297     HRESULT hres;
298
299     hres = compile_expression(ctx, stat->value_expr);
300     if(FAILED(hres))
301         return hres;
302
303     if(stat->member_expr->args) {
304         FIXME("arguments support not implemented\n");
305         return E_NOTIMPL;
306     }
307
308     if(stat->member_expr->obj_expr) {
309         hres = compile_expression(ctx, stat->member_expr->obj_expr);
310         if(FAILED(hres))
311             return hres;
312
313         hres = push_instr_bstr(ctx, OP_assign_member, stat->member_expr->identifier);
314     }else {
315         hres = push_instr_bstr(ctx, OP_assign_ident, stat->member_expr->identifier);
316     }
317
318     return hres;
319 }
320
321 static BOOL lookup_dim_decls(compile_ctx_t *ctx, const WCHAR *name)
322 {
323     dim_decl_t *dim_decl;
324
325     for(dim_decl = ctx->dim_decls; dim_decl; dim_decl = dim_decl->next) {
326         if(!strcmpiW(dim_decl->name, name))
327             return TRUE;
328     }
329
330     return FALSE;
331 }
332
333 static HRESULT compile_dim_statement(compile_ctx_t *ctx, dim_statement_t *stat)
334 {
335     dim_decl_t *dim_decl = stat->dim_decls;
336
337     while(1) {
338         if(lookup_dim_decls(ctx, dim_decl->name)) {
339             FIXME("dim %s name redefined\n", debugstr_w(dim_decl->name));
340             return E_FAIL;
341         }
342
343         if(!dim_decl->next)
344             break;
345         dim_decl = dim_decl->next;
346     }
347
348     dim_decl->next = ctx->dim_decls;
349     ctx->dim_decls = stat->dim_decls;
350     return S_OK;
351 }
352
353 static HRESULT compile_statement(compile_ctx_t *ctx, statement_t *stat)
354 {
355     HRESULT hres;
356
357     while(stat) {
358         switch(stat->type) {
359         case STAT_ASSIGN:
360             hres = compile_assign_statement(ctx, (assign_statement_t*)stat);
361             break;
362         case STAT_CALL:
363             hres = compile_member_expression(ctx, ((call_statement_t*)stat)->expr, FALSE);
364             break;
365         case STAT_DIM:
366             hres = compile_dim_statement(ctx, (dim_statement_t*)stat);
367             break;
368         default:
369             FIXME("Unimplemented statement type %d\n", stat->type);
370             hres = E_NOTIMPL;
371         }
372
373         if(FAILED(hres))
374             return hres;
375         stat = stat->next;
376     }
377
378     return S_OK;
379 }
380
381 static HRESULT compile_func(compile_ctx_t *ctx, statement_t *stat, function_t *func)
382 {
383     HRESULT hres;
384
385     func->code_off = ctx->instr_cnt;
386
387     hres = compile_statement(ctx, stat);
388     if(FAILED(hres))
389         return hres;
390
391     if(push_instr(ctx, OP_ret) == -1)
392         return E_OUTOFMEMORY;
393
394     if(ctx->dim_decls) {
395         dim_decl_t *dim_decl;
396         dynamic_var_t *new_var;
397
398         for(dim_decl = ctx->dim_decls; dim_decl; dim_decl = dim_decl->next) {
399             new_var = compiler_alloc(ctx->code, sizeof(*new_var));
400             if(!new_var)
401                 return E_OUTOFMEMORY;
402
403             new_var->name = compiler_alloc_string(ctx->code, dim_decl->name);
404             if(!new_var->name)
405                 return E_OUTOFMEMORY;
406
407             V_VT(&new_var->v) = VT_EMPTY;
408
409             new_var->next = ctx->global_vars;
410             ctx->global_vars = new_var;
411         }
412     }
413
414     return S_OK;
415 }
416
417 static BOOL lookup_script_identifier(script_ctx_t *script, const WCHAR *identifier)
418 {
419     dynamic_var_t *var;
420
421     for(var = script->global_vars; var; var = var->next) {
422         if(!strcmpiW(var->name, identifier))
423             return TRUE;
424     }
425
426     return FALSE;
427 }
428
429 static HRESULT check_script_collisions(compile_ctx_t *ctx, script_ctx_t *script)
430 {
431     dynamic_var_t *var;
432
433     for(var = ctx->global_vars; var; var = var->next) {
434         if(lookup_script_identifier(script, var->name)) {
435             FIXME("%s: redefined\n", debugstr_w(var->name));
436             return E_FAIL;
437         }
438     }
439
440     return S_OK;
441 }
442
443 void release_vbscode(vbscode_t *code)
444 {
445     unsigned i;
446
447     list_remove(&code->entry);
448
449     for(i=0; i < code->bstr_cnt; i++)
450         SysFreeString(code->bstr_pool[i]);
451
452     vbsheap_free(&code->heap);
453
454     heap_free(code->bstr_pool);
455     heap_free(code->source);
456     heap_free(code->instrs);
457     heap_free(code);
458 }
459
460 static vbscode_t *alloc_vbscode(compile_ctx_t *ctx, const WCHAR *source)
461 {
462     vbscode_t *ret;
463
464     ret = heap_alloc(sizeof(*ret));
465     if(!ret)
466         return NULL;
467
468     ret->source = heap_strdupW(source);
469     if(!ret->source) {
470         heap_free(ret);
471         return NULL;
472     }
473
474     ret->instrs = heap_alloc(32*sizeof(instr_t));
475     if(!ret->instrs) {
476         release_vbscode(ret);
477         return NULL;
478     }
479
480     ctx->instr_cnt = 0;
481     ctx->instr_size = 32;
482     vbsheap_init(&ret->heap);
483
484     ret->option_explicit = ctx->parser.option_explicit;
485
486     ret->bstr_pool = NULL;
487     ret->bstr_pool_size = 0;
488     ret->bstr_cnt = 0;
489
490     ret->global_code.code_ctx = ret;
491
492     list_init(&ret->entry);
493     return ret;
494 }
495
496 HRESULT compile_script(script_ctx_t *script, const WCHAR *src, vbscode_t **ret)
497 {
498     compile_ctx_t ctx;
499     HRESULT hres;
500
501     hres = parse_script(&ctx.parser, src);
502     if(FAILED(hres))
503         return hres;
504
505     ctx.code = alloc_vbscode(&ctx, src);
506     if(!ctx.code)
507         return E_OUTOFMEMORY;
508
509     ctx.global_vars = NULL;
510     ctx.dim_decls = NULL;
511
512     hres = compile_func(&ctx, ctx.parser.stats, &ctx.code->global_code);
513     if(FAILED(hres)) {
514         release_vbscode(ctx.code);
515         return hres;
516     }
517
518     hres = check_script_collisions(&ctx, script);
519     if(FAILED(hres)) {
520         release_vbscode(ctx.code);
521         return hres;
522     }
523
524     if(ctx.global_vars) {
525         dynamic_var_t *var;
526
527         for(var = ctx.global_vars; var->next; var = var->next);
528
529         var->next = script->global_vars;
530         script->global_vars = ctx.global_vars;
531     }
532
533     parser_release(&ctx.parser);
534
535     list_add_tail(&script->code_list, &ctx.code->entry);
536     *ret = ctx.code;
537     return S_OK;
538 }