pre-evaluate constant value on validation
[m6w6/ext-psi] / src / context.c
1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
25
26 #include "php_psi_stdinc.h"
27
28 #include "php.h"
29
30 #ifdef HAVE_DIRENT_H
31 # include <dirent.h>
32 # define NAMLEN(dirent) strlen ((dirent)->d_name)
33 #else
34 # define dirent direct
35 # define NAMLEN(dirent) ((dirent)->d_namlen)
36 # ifdef HAVE_SYS_NDIR_H
37 # include <sys/ndir.h>
38 # endif
39 # ifdef HAVE_SYS_DIR_H
40 # include <sys/dir.h>
41 # endif
42 # ifdef HAVE_NDIR_H
43 # include <ndir.h>
44 # endif
45 #endif
46
47 #include <fnmatch.h>
48
49 #include "php_scandir.h"
50 #include "php_psi.h"
51 #include "calc.h"
52 #include "call.h"
53 #include "libjit.h"
54 #include "libffi.h"
55
56 #include "token.h"
57 #include "parser.h"
58
59 #define PSI_PREDEF_TYPES
60 #define PSI_PREDEF_CONSTS
61 #define PSI_PREDEF_COMPOSITES
62 #define PSI_PREDEF_DECLS
63 #include "php_psi_posix.h"
64
65 struct psi_context *psi_context_init(struct psi_context *C, struct psi_context_ops *ops, psi_error_cb error, unsigned flags)
66 {
67 struct psi_data T;
68 struct psi_predef_type *predef_type;
69 struct psi_predef_const *predef_const;
70 struct psi_predef_composite *predef_composite;
71 struct psi_predef_decl *predef_decl;
72
73 if (!C) {
74 C = malloc(sizeof(*C));
75 }
76 memset(C, 0, sizeof(*C));
77
78 psi_data_ctor(PSI_DATA(C), error, flags);
79 C->ops = ops;
80
81 if (ops->init) {
82 ops->init(C);
83 }
84
85 assert(ops->call != NULL);
86 assert(ops->compile != NULL);
87
88 /* build up predefs in a temporary PSI_Data for validation */
89 memset(&T, 0, sizeof(T));
90 psi_data_ctor_with_dtors(&T, error, flags);
91
92 for (predef_type = &psi_predef_types[0]; predef_type->type_tag; ++predef_type) {
93 struct psi_decl_type *type = psi_decl_type_init(predef_type->type_tag, predef_type->type_name);
94 struct psi_decl_var *var = psi_decl_var_init(predef_type->alias, 0, 0); /* FIXME: indirection */
95 struct psi_decl_arg *def = psi_decl_arg_init(type, var);
96
97 T.types = psi_plist_add(T.types, &def);
98 }
99 for (predef_const = &psi_predef_consts[0]; predef_const->type_tag; ++predef_const) {
100 struct psi_impl_def_val *val = psi_impl_def_val_init(predef_const->val_type_tag, predef_const->val_text);
101 struct psi_const_type *type = psi_const_type_init(predef_const->type_tag, predef_const->type_name);
102 struct psi_const *constant = psi_const_init(type, predef_const->var_name, val);
103
104 T.consts = psi_plist_add(T.consts, &constant);
105 }
106 for (predef_composite = &psi_predef_composites[0]; predef_composite->type_tag; ++predef_composite) {
107 struct psi_predef_composite *member;
108 struct psi_decl_struct *dstruct;
109 struct psi_decl_union *dunion;
110 struct psi_plist *dargs = psi_plist_init((psi_plist_dtor) psi_decl_arg_free);
111
112 switch (predef_composite->type_tag) {
113 case PSI_T_STRUCT:
114 dstruct = psi_decl_struct_init(predef_composite->var_name, dargs);
115 dstruct->size = predef_composite->size;
116 dstruct->align = predef_composite->offset;
117 break;
118 case PSI_T_UNION:
119 dunion = psi_decl_union_init(predef_composite->var_name, dargs);
120 dunion->size = predef_composite->size;
121 dunion->align = predef_composite->offset;
122 break;
123 default:
124 assert(0);
125 }
126 for (member = &predef_composite[1]; member->type_tag; ++member) {
127 struct psi_decl_type *type;
128 struct psi_decl_var *dvar;
129 struct psi_decl_arg *darg;
130
131 type = psi_decl_type_init(member->type_tag, member->type_name);
132 dvar = psi_decl_var_init(member->var_name, member->pointer_level, member->array_size);
133 darg = psi_decl_arg_init(type, dvar);
134 darg->layout = psi_layout_init(member->offset, member->size);
135
136 switch (predef_composite->type_tag) {
137 case PSI_T_STRUCT:
138 dstruct->args = psi_plist_add(dstruct->args, &darg);
139 break;
140 case PSI_T_UNION:
141 dunion->args = psi_plist_add(dunion->args, &darg);
142 break;
143 default:
144 assert(0);
145 }
146 }
147 switch (predef_composite->type_tag) {
148 case PSI_T_STRUCT:
149 T.structs = psi_plist_add(T.structs, &dstruct);
150 break;
151 case PSI_T_UNION:
152 T.unions = psi_plist_add(T.unions, &dunion);
153 break;
154 default:
155 assert(0);
156 }
157
158 predef_composite = member;
159 }
160 for (predef_decl = &psi_predef_decls[0]; predef_decl->type_tag; ++predef_decl) {
161 struct psi_predef_decl *farg;
162 struct psi_decl_type *dtype, *ftype = psi_decl_type_init(predef_decl->type_tag, predef_decl->type_name);
163 struct psi_decl_var *fname = psi_decl_var_init(predef_decl->var_name, predef_decl->pointer_level, predef_decl->array_size);
164 struct psi_decl_arg *tdef, *func = psi_decl_arg_init(ftype, fname);
165 struct psi_plist *args = psi_plist_init((psi_plist_dtor) psi_decl_arg_free);
166 struct psi_decl *decl = psi_decl_init(psi_decl_abi_init("default"), func, args);
167
168 for (farg = &predef_decl[1]; farg->type_tag; ++farg) {
169 struct psi_decl_type *arg_type = psi_decl_type_init(farg->type_tag, farg->type_name);
170 struct psi_decl_var *arg_var = psi_decl_var_init(farg->var_name, farg->pointer_level, farg->array_size);
171 struct psi_decl_arg *darg = psi_decl_arg_init(arg_type, arg_var);
172 decl->args = psi_plist_add(decl->args, &darg);
173 }
174
175 switch (predef_decl->kind) {
176 case DECL_KIND_VARARG:
177 decl->varargs = 1;
178 /* no break */
179 case DECL_KIND_STD:
180 T.decls = psi_plist_add(T.decls, &decl);
181 break;
182 case DECL_KIND_FUNCTOR:
183 dtype = psi_decl_type_init(PSI_T_FUNCTION, fname->name);
184 dtype->real.func = decl;
185 tdef = psi_decl_arg_init(dtype, psi_decl_var_copy(fname));
186 T.types = psi_plist_add(T.types, &tdef);
187 break;
188 default:
189 assert(0);
190 }
191
192 predef_decl = farg;
193 }
194
195 psi_context_add_data(C, &T);
196
197 return C;
198 }
199
200 static int psi_select_dirent(const struct dirent *entry)
201 {
202 #ifndef FNM_CASEFOLD
203 # define FNM_CASEFOLD 0
204 #endif
205 return 0 == fnmatch("*.psi", entry->d_name, FNM_CASEFOLD);
206 }
207
208 void psi_context_build(struct psi_context *C, const char *paths)
209 {
210 int i, n;
211 char *sep = NULL, *cpy = strdup(paths), *ptr = cpy;
212 struct dirent **entries;
213
214 do {
215 sep = strchr(ptr, ':');
216
217 if (sep) {
218 *sep = 0;
219 }
220
221 entries = NULL;
222 n = php_scandir(ptr, &entries, psi_select_dirent, alphasort);
223
224 if (n > 0) {
225 for (i = 0; i < n; ++i) {
226 char psi[MAXPATHLEN];
227 struct psi_parser P;
228
229 if (MAXPATHLEN <= slprintf(psi, MAXPATHLEN, "%s/%s", ptr, entries[i]->d_name)) {
230 C->error(PSI_DATA(C), NULL, PSI_WARNING, "Path to PSI file too long: %s/%s",
231 ptr, entries[i]->d_name);
232 }
233 if (!psi_parser_init(&P, C->error, C->flags)) {
234 C->error(PSI_DATA(C), NULL, PSI_WARNING, "Failed to init PSI parser (%s): %s",
235 psi, strerror(errno));
236 continue;
237 }
238 if (!psi_parser_open_file(&P, psi)) {
239 C->error(PSI_DATA(C), NULL, PSI_WARNING, "Failed to open PSI file (%s): %s",
240 psi, strerror(errno));
241 continue;
242 }
243
244 while (0 < psi_parser_scan(&P)) {
245 psi_parser_parse(&P, psi_token_alloc(&P));
246 if (P.num == PSI_T_EOF) {
247 break;
248 }
249 }
250
251 psi_parser_parse(&P, NULL);
252 psi_context_add_data(C, PSI_DATA(&P));
253 psi_parser_dtor(&P);
254 }
255 }
256
257 if (entries) {
258 for (i = 0; i < n; ++i) {
259 free(entries[i]);
260 }
261 free(entries);
262 }
263
264 ptr = sep + 1;
265 } while (sep);
266
267
268 if (psi_context_compile(C) && SUCCESS != zend_register_functions(NULL, C->closures, NULL, MODULE_PERSISTENT)) {
269 C->error(PSI_DATA(C), NULL, PSI_WARNING, "Failed to register functions!");
270 }
271
272 free(cpy);
273 }
274
275 zend_function_entry *psi_context_compile(struct psi_context *C)
276 {
277 zend_constant zc;
278
279 zc.flags = CONST_PERSISTENT|CONST_CS;
280 zc.module_number = EG(current_module)->module_number;
281
282 if (C->consts) {
283 size_t i = 0;
284 struct psi_const *c;
285
286 while (psi_plist_get(C->consts, i++, &c)) {
287
288 if (zend_get_constant_str(c->name, strlen(c->name))) {
289 continue;
290 }
291
292 zc.name = zend_string_init(c->name + (c->name[0] == '\\'), strlen(c->name) - (c->name[0] == '\\'), 1);
293
294 switch (c->type->type) {
295 case PSI_T_BOOL:
296 ZVAL_BOOL(&zc.value, c->val->ival.zend.bval);
297 break;
298 case PSI_T_INT:
299 ZVAL_LONG(&zc.value, c->val->ival.zend.lval);
300 break;
301 case PSI_T_FLOAT:
302 ZVAL_DOUBLE(&zc.value, c->val->ival.dval);
303 break;
304 case PSI_T_STRING:
305 case PSI_T_QUOTED_STRING:
306 ZVAL_NEW_STR(&zc.value, zend_string_init(c->val->text, strlen(c->val->text), 1));
307 break;
308 default:
309 assert(0);
310 break;
311 }
312
313 zend_register_constant(&zc);
314 }
315 }
316
317 if (C->enums) {
318 size_t i = 0;
319 struct psi_decl_enum *e;
320
321 while (psi_plist_get(C->enums, i++, &e)) {
322 size_t j = 0;
323 struct psi_decl_enum_item *item;
324
325 while (psi_plist_get(e->items, j++, &item)) {
326 zend_string *name = strpprintf(0, "psi\\%s\\%s", e->name, item->name);
327
328 zc.name = zend_string_dup(name, 1);
329 ZVAL_LONG(&zc.value, psi_long_num_exp(item->num, NULL));
330 zend_register_constant(&zc);
331 zend_string_release(name);
332 }
333 }
334 }
335
336 return C->closures = C->ops->compile(C);
337 }
338
339
340 ZEND_RESULT_CODE psi_context_call(struct psi_context *C, zend_execute_data *execute_data, zval *return_value, struct psi_impl *impl)
341 {
342 struct psi_call_frame *frame;
343
344 frame = psi_call_frame_init(C, impl->decl, impl);
345
346 if (SUCCESS != psi_call_frame_parse_args(frame, execute_data)) {
347 psi_call_frame_free(frame);
348
349 return FAILURE;
350 }
351
352 psi_call_frame_enter(frame);
353
354 if (SUCCESS != psi_call_frame_do_let(frame)) {
355 psi_call_frame_do_return(frame, return_value);
356 psi_call_frame_free(frame);
357
358 return FAILURE;
359 }
360
361 psi_call_frame_do_call(frame);
362 psi_call_frame_do_return(frame, return_value);
363 psi_call_frame_do_set(frame);
364 psi_call_frame_do_free(frame);
365 psi_call_frame_free(frame);
366
367 return SUCCESS;
368 }
369
370
371 void psi_context_dtor(struct psi_context *C)
372 {
373 size_t i;
374 zend_function_entry *zfe;
375
376 if (C->ops->dtor) {
377 C->ops->dtor(C);
378 }
379
380 psi_data_dtor(PSI_DATA(C));
381
382 if (C->data) {
383 for (i = 0; i < C->count; ++i) {
384 psi_data_dtor(&C->data[i]);
385 }
386 free(C->data);
387 }
388
389 if (C->closures) {
390 for (zfe = C->closures; zfe->fname; ++zfe) {
391 free((void *) zfe->arg_info);
392 }
393 free(C->closures);
394 }
395 }
396
397 void psi_context_free(struct psi_context **C)
398 {
399 if (*C) {
400 psi_context_dtor(*C);
401 free(*C);
402 *C = NULL;
403 }
404 }
405
406 bool psi_context_add_data(struct psi_context *C, struct psi_data *P)
407 {
408 struct psi_data *D;
409
410 C->data = realloc(C->data, (C->count + 1) * sizeof(*C->data));
411 D = psi_data_exchange(&C->data[C->count++], P);
412
413 return psi_data_validate(PSI_DATA(C), D);
414 }
415
416 void psi_context_dump(struct psi_context *C, int fd)
417 {
418
419 dprintf(fd, "// psi.engine=%s\n",
420 (char *) C->ops->query(C, PSI_CONTEXT_QUERY_SELF, NULL));
421
422 psi_data_dump(fd, PSI_DATA(C));
423
424 // size_t i;
425 // dprintf(fd, "/* parsed\n");
426 // for (i = 0; i < C->count; ++i) {
427 // psi_data_dump(fd, &C->data[i]);
428 // }
429 // dprintf(fd, "*/\n");
430
431 }