vbscript: Added simple call test.
[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 inline instr_t *instr_ptr(compile_ctx_t *ctx, unsigned id)
38 {
39     assert(id < ctx->instr_cnt);
40     return ctx->code->instrs + id;
41 }
42
43 static unsigned push_instr(compile_ctx_t *ctx, vbsop_t op)
44 {
45     assert(ctx->instr_size && ctx->instr_size >= ctx->instr_cnt);
46
47     if(ctx->instr_size == ctx->instr_cnt) {
48         instr_t *new_instr;
49
50         new_instr = heap_realloc(ctx->code->instrs, ctx->instr_size*2*sizeof(instr_t));
51         if(!new_instr)
52             return -1;
53
54         ctx->code->instrs = new_instr;
55         ctx->instr_size *= 2;
56     }
57
58     ctx->code->instrs[ctx->instr_cnt].op = op;
59     return ctx->instr_cnt++;
60 }
61
62 static BSTR alloc_bstr_arg(compile_ctx_t *ctx, const WCHAR *str)
63 {
64     if(!ctx->code->bstr_pool_size) {
65         ctx->code->bstr_pool = heap_alloc(8 * sizeof(BSTR));
66         if(!ctx->code->bstr_pool)
67             return NULL;
68         ctx->code->bstr_pool_size = 8;
69     }else if(ctx->code->bstr_pool_size == ctx->code->bstr_cnt) {
70        BSTR *new_pool;
71
72         new_pool = heap_realloc(ctx->code->bstr_pool, ctx->code->bstr_pool_size*2*sizeof(BSTR));
73         if(!new_pool)
74             return NULL;
75
76         ctx->code->bstr_pool = new_pool;
77         ctx->code->bstr_pool_size *= 2;
78     }
79
80     ctx->code->bstr_pool[ctx->code->bstr_cnt] = SysAllocString(str);
81     if(!ctx->code->bstr_pool[ctx->code->bstr_cnt])
82         return NULL;
83
84     return ctx->code->bstr_pool[ctx->code->bstr_cnt++];
85 }
86
87 static HRESULT push_instr_bstr(compile_ctx_t *ctx, vbsop_t op, const WCHAR *arg)
88 {
89     unsigned instr;
90     BSTR bstr;
91
92     bstr = alloc_bstr_arg(ctx, arg);
93     if(!bstr)
94         return E_OUTOFMEMORY;
95
96     instr = push_instr(ctx, op);
97     if(instr == -1)
98         return E_OUTOFMEMORY;
99
100     instr_ptr(ctx, instr)->arg1.bstr = bstr;
101     return S_OK;
102 }
103
104 static HRESULT compile_member_expression(compile_ctx_t *ctx, member_expression_t *expr)
105 {
106     HRESULT hres;
107
108     if(expr->args) {
109         FIXME("arguments not implemented\n");
110         return E_NOTIMPL;
111     }
112
113     if(expr->obj_expr) {
114         FIXME("obj_expr not implemented\n");
115         hres = E_NOTIMPL;
116     }else {
117         hres = push_instr_bstr(ctx, OP_icallv, expr->identifier);
118     }
119
120     return hres;
121 }
122
123 static HRESULT compile_statement(compile_ctx_t *ctx, statement_t *stat)
124 {
125     HRESULT hres;
126
127     while(stat) {
128         switch(stat->type) {
129         case STAT_CALL:
130             hres = compile_member_expression(ctx, ((call_statement_t*)stat)->expr);
131             break;
132         default:
133             FIXME("Unimplemented statement type %d\n", stat->type);
134             hres = E_NOTIMPL;
135         }
136
137         if(FAILED(hres))
138             return hres;
139         stat = stat->next;
140     }
141
142     return S_OK;
143 }
144
145 static HRESULT compile_func(compile_ctx_t *ctx, statement_t *stat, function_t *func)
146 {
147     HRESULT hres;
148
149     func->code_off = ctx->instr_cnt;
150
151     hres = compile_statement(ctx, stat);
152     if(FAILED(hres))
153         return hres;
154
155     if(push_instr(ctx, OP_ret) == -1)
156         return E_OUTOFMEMORY;
157
158     return S_OK;
159 }
160
161 void release_vbscode(vbscode_t *code)
162 {
163     unsigned i;
164
165     list_remove(&code->entry);
166
167     for(i=0; i < code->bstr_cnt; i++)
168         SysFreeString(code->bstr_pool[i]);
169
170     heap_free(code->bstr_pool);
171     heap_free(code->source);
172     heap_free(code->instrs);
173     heap_free(code);
174 }
175
176 static vbscode_t *alloc_vbscode(compile_ctx_t *ctx, const WCHAR *source)
177 {
178     vbscode_t *ret;
179
180     ret = heap_alloc(sizeof(*ret));
181     if(!ret)
182         return NULL;
183
184     ret->source = heap_strdupW(source);
185     if(!ret->source) {
186         heap_free(ret);
187         return NULL;
188     }
189
190     ret->instrs = heap_alloc(32*sizeof(instr_t));
191     if(!ret->instrs) {
192         release_vbscode(ret);
193         return NULL;
194     }
195
196     ctx->instr_cnt = 0;
197     ctx->instr_size = 32;
198
199     ret->bstr_pool = NULL;
200     ret->bstr_pool_size = 0;
201     ret->bstr_cnt = 0;
202
203     ret->global_code.code_ctx = ret;
204
205     list_init(&ret->entry);
206     return ret;
207 }
208
209 HRESULT compile_script(script_ctx_t *script, const WCHAR *src, vbscode_t **ret)
210 {
211     compile_ctx_t ctx;
212     HRESULT hres;
213
214     hres = parse_script(&ctx.parser, src);
215     if(FAILED(hres))
216         return hres;
217
218     ctx.code = alloc_vbscode(&ctx, src);
219     if(!ctx.code)
220         return E_OUTOFMEMORY;
221
222     hres = compile_func(&ctx, ctx.parser.stats, &ctx.code->global_code);
223     if(FAILED(hres)) {
224         release_vbscode(ctx.code);
225         return hres;
226     }
227
228     list_add_tail(&script->code_list, &ctx.code->entry);
229     *ret = ctx.code;
230     return S_OK;
231 }