more verbose context ops
[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 #include "php_psi_posix.h"
60
61 PHP_MINIT_FUNCTION(psi_context)
62 {
63 unsigned flags = 0;
64 struct psi_context_ops *ops = NULL;
65
66 #ifdef HAVE_LIBJIT
67 if (!strcasecmp(PSI_G(engine), "jit")) {
68 ops = psi_libjit_ops();
69 } else
70 #endif
71 #ifdef HAVE_LIBFFI
72 ops = psi_libffi_ops();
73 #endif
74
75 if (!ops) {
76 php_error(E_WARNING, "No PSI engine found");
77 return FAILURE;
78 }
79
80 PSI_G(ops) = ops;
81 if (ops->load && !ops->load()) {
82 return FAILURE;
83 }
84
85 if (psi_check_env("PSI_DEBUG")) {
86 flags |= PSI_DEBUG;
87 }
88 if (psi_check_env("PSI_SILENT")) {
89 flags |= PSI_SILENT;
90 }
91
92 PSI_G(context) = psi_context_init(NULL, PSI_G(ops), psi_error_wrapper, flags);
93 psi_context_build(PSI_G(context), PSI_G(directory));
94
95 return SUCCESS;
96 }
97
98 PHP_MSHUTDOWN_FUNCTION(psi_context)
99 {
100 if (psi_check_env("PSI_DUMP")) {
101 struct psi_dump dump = {{.hn = stdout}, (psi_dump_cb) fprintf};
102
103 psi_context_dump(&dump, PSI_G(context));
104 }
105
106 psi_context_free(&PSI_G(context));
107
108 if (PSI_G(ops)->free) {
109 PSI_G(ops)->free();
110 }
111
112 return SUCCESS;
113 }
114
115 struct psi_context *psi_context_init(struct psi_context *C, struct psi_context_ops *ops, psi_error_cb error, unsigned flags)
116 {
117 if (!C) {
118 C = pemalloc(sizeof(*C), 1);
119 }
120 memset(C, 0, sizeof(*C));
121
122 psi_data_ctor(PSI_DATA(C), error, flags);
123 C->ops = ops;
124
125 if (ops->init && !ops->init(C)) {
126 return NULL;
127 }
128
129 return C;
130 }
131
132 static int psi_select_dirent(const struct dirent *entry)
133 {
134 #ifndef FNM_CASEFOLD
135 # define FNM_CASEFOLD 0
136 #endif
137 return 0 == fnmatch("*.psi", entry->d_name, FNM_CASEFOLD);
138 }
139
140 static bool psi_context_add(struct psi_context *C, struct psi_parser *P)
141 {
142 bool valid;
143 struct psi_data *D;
144 struct psi_validate_scope scope = {0};
145
146 C->data = safe_perealloc(C->data, (C->count + 1), sizeof(*C->data), 0, 1);
147 D = psi_data_exchange(&C->data[C->count++], PSI_DATA(P));
148
149 psi_validate_scope_ctor(&scope);
150 scope.cpp = P->preproc;
151 valid = psi_validate(&scope, PSI_DATA(C), D);
152 psi_validate_scope_dtor(&scope);
153
154 return valid;
155 }
156
157 void psi_context_build(struct psi_context *C, const char *paths)
158 {
159 int i, n;
160 char *sep = NULL, *cpy = strdup(paths), *ptr = cpy;
161 struct dirent **entries;
162
163 do {
164 sep = strchr(ptr, ':');
165
166 if (sep) {
167 *sep = 0;
168 }
169
170 entries = NULL;
171 n = php_scandir(ptr, &entries, psi_select_dirent, alphasort);
172
173 if (n > 0) {
174 for (i = 0; i < n; ++i) {
175 char psi[MAXPATHLEN];
176 struct psi_parser P;
177 struct psi_parser_input *I;
178
179 if (MAXPATHLEN <= slprintf(psi, MAXPATHLEN, "%s/%s", ptr, entries[i]->d_name)) {
180 C->error(PSI_DATA(C), NULL, PSI_WARNING, "Path to PSI file too long: %s/%s",
181 ptr, entries[i]->d_name);
182 }
183 if (!psi_parser_init(&P, C->error, C->flags)) {
184 C->error(PSI_DATA(C), NULL, PSI_WARNING, "Failed to init PSI parser (%s): %s",
185 psi, strerror(errno));
186 continue;
187 }
188 if (!(I = psi_parser_open_file(&P, psi, true))) {
189 C->error(PSI_DATA(C), NULL, PSI_WARNING, "Failed to open PSI file (%s): %s",
190 psi, strerror(errno));
191 continue;
192 }
193 psi_parser_parse(&P, I);
194 psi_context_add(C, &P);
195 psi_parser_dtor(&P);
196 psi_parser_input_free(&I);
197 }
198 }
199
200 if (entries) {
201 for (i = 0; i < n; ++i) {
202 free(entries[i]);
203 }
204 free(entries);
205 }
206
207 ptr = sep + 1;
208 } while (sep);
209
210 free(cpy);
211
212 psi_context_compile(C);
213 }
214
215 #include <ctype.h>
216 static inline bool prefix_match(zend_string *a, zend_string *b)
217 {
218 size_t i;
219
220 for (i = 0; i < a->len && i < b->len; ++i) {
221 if (tolower(a->val[i]) != tolower(b->val[i])) {
222 return false;
223 }
224 if (i && a->val[i] == '_') {
225 break;
226 }
227 }
228
229 return true;
230 }
231
232 static inline void psi_context_consts_init(struct psi_context *C)
233 {
234 zend_constant zc;
235
236 ZEND_CONSTANT_SET_FLAGS(&zc, CONST_CS|CONST_PERSISTENT, EG(current_module)->module_number);
237
238 if (C->consts) {
239 size_t i = 0;
240 struct psi_const *c;
241
242 while (psi_plist_get(C->consts, i++, &c)) {
243
244 if (zend_get_constant(c->name)) {
245 continue;
246 }
247
248 zc.name = zend_string_copy(c->name);
249 psi_impl_def_val_get_zval(c->val, c->type ? c->type->type : PSI_T_MIXED, &zc.value);
250
251 zend_register_constant(&zc);
252 }
253 }
254 if (C->enums) {
255 size_t i = 0;
256 struct psi_decl_enum *e;
257
258 while (psi_plist_get(C->enums, i++, &e)) {
259 size_t j = 0;
260 struct psi_decl_enum_item *item;
261
262 while (psi_plist_get(e->items, j++, &item)) {
263 zend_string *name;
264
265 if (psi_decl_type_is_anon(e->name, "enum")
266 || prefix_match(e->name, item->name)) {
267 name = strpprintf(0, "psi\\%s", item->name->val);
268 } else {
269
270 name = strpprintf(0, "psi\\%s\\%s", e->name->val, item->name->val);
271 }
272
273 if (zend_get_constant(name)) {
274 zend_string_release(name);
275 continue;
276 }
277
278 zc.name = zend_string_dup(name, 1);
279 ZVAL_LONG(&zc.value, psi_num_exp_get_long(item->num, NULL, NULL));
280 zend_register_constant(&zc);
281 zend_string_release(name);
282 }
283 }
284 }
285 }
286
287 static inline void psi_context_extvars_init(struct psi_context *C)
288 {
289 if (C->vars) {
290 size_t v = 0;
291 struct psi_decl_extvar *evar;
292
293 while (psi_plist_get(C->vars, v++, &evar)) {
294 C->ops->extvar_init(C, evar);
295 }
296 }
297 }
298
299 static inline void psi_context_callback_init(struct psi_context *C,
300 struct psi_let_exp *let_exp, struct psi_impl *impl)
301 {
302 struct psi_let_func *fn = let_exp->data.func;
303
304 switch (let_exp->kind) {
305 case PSI_LET_CALLBACK:
306 C->ops->cb_init(C, let_exp, impl);
307 /* override fn */
308 fn = let_exp->data.callback->func;
309 /* no break */
310 case PSI_LET_FUNC:
311 if (fn->inner) {
312 size_t i = 0;
313 struct psi_let_exp *inner_let;
314
315 while (psi_plist_get(fn->inner, i++, &inner_let)) {
316 psi_context_callback_init(C, inner_let, impl);
317 }
318 }
319 break;
320 default:
321 break;
322 }
323 }
324
325 static inline void psi_context_callback_dtor(struct psi_context *C,
326 struct psi_let_exp *let_exp, struct psi_impl *impl)
327 {
328 struct psi_let_func *fn = let_exp->data.func;
329
330 switch (let_exp->kind) {
331 case PSI_LET_CALLBACK:
332 C->ops->cb_dtor(C, let_exp, impl);
333 /* override func */
334 fn = let_exp->data.callback->func;
335 /* no break */
336 case PSI_LET_FUNC:
337 if (fn->inner) {
338 size_t i = 0;
339 struct psi_let_exp *cb;
340
341 while (psi_plist_get(fn->inner, i++, &cb)) {
342 psi_context_callback_dtor(C, cb, impl);
343 }
344 }
345 break;
346 default:
347 break;
348 }
349 }
350
351 static inline void psi_context_impls_init(struct psi_context *C)
352 {
353 size_t nf = 0;
354 zend_function_entry *zfe = NULL;
355
356 if (C->impls) {
357 size_t i = 0;
358 struct psi_impl *impl;
359
360 zfe = pecalloc(psi_plist_count(C->impls) + 1, sizeof(*zfe), 1);
361
362 while (psi_plist_get(C->impls, i++, &impl)) {
363 zend_function_entry *zf = &zfe[nf];
364 struct psi_let_stmt *let;
365 size_t l = 0;
366
367 if (!impl->decl) {
368 continue;
369 }
370 if (!C->ops->decl_init(C, impl->decl)) {
371 continue;
372 }
373 if (!C->ops->impl_init(C, impl, &zf->handler)) {
374 continue;
375 }
376 while (psi_plist_get(impl->stmts.let, l++, &let)) {
377 psi_context_callback_init(C, let->exp, impl);
378 }
379
380 zf->fname = impl->func->name->val + (impl->func->name->val[0] == '\\');
381 zf->num_args = psi_plist_count(impl->func->args);
382 zf->arg_info = psi_internal_arginfo(impl);
383 ++nf;
384 }
385 }
386
387 C->closures = zfe;
388 }
389
390 static inline void psi_context_decls_init(struct psi_context *C)
391 {
392 if (C->decls) {
393 size_t d = 0;
394 struct psi_decl *decl;
395
396 while (psi_plist_get(C->decls, d++, &decl)) {
397 if (!decl->info) {
398 C->ops->decl_init(C, decl);
399 }
400 }
401 }
402 }
403
404 struct psi_struct_type_data {
405 struct psi_plist *els;
406 size_t offset;
407 size_t max_align;
408 };
409
410 static inline void psi_struct_type_pad(struct psi_context *C,
411 struct psi_struct_type_data *data, size_t padding)
412 {
413 void *ele = C->ops->typeof_decl(C, PSI_T_INT8);
414
415 while (padding--) {
416 void *pad = C->ops->copyof_type(C, ele);
417 data->els = psi_plist_add(data->els, &pad);
418 }
419 }
420
421 static inline void psi_struct_type_element(struct psi_context *C,
422 struct psi_struct_type_data *data, struct psi_decl_arg *darg)
423 {
424 void *type, *copy;
425 size_t i;
426 struct psi_layout type_layout;
427
428 if (darg->layout->pos) {
429 assert(data->offset <= darg->layout->pos);
430 psi_struct_type_pad(C, data, darg->layout->pos - data->offset);
431 data->offset = darg->layout->pos;
432 }
433
434 type = psi_context_decl_arg_full_type(C, darg);
435 C->ops->layoutof_type(C, type, &type_layout);
436
437 if (type_layout.pos > data->max_align) {
438 data->max_align = type_layout.pos;
439 }
440
441 assert(type_layout.len <= darg->layout->len);
442
443 for (i = 0; i < (darg->var->array_size ?: 1); ++i) {
444 copy = C->ops->copyof_type(C, type);
445 data->els = psi_plist_add(data->els, &copy);
446 }
447 assert(darg->layout->len == type_layout.len * (darg->var->array_size ?: 1));
448 data->offset += darg->layout->len;
449 }
450
451 static inline void psi_context_decl_struct_type_elements(struct psi_context *C,
452 struct psi_decl_struct *strct, struct psi_plist **els)
453 {
454 size_t i = 0;
455 struct psi_decl_arg *darg, *prev = NULL;
456 struct psi_struct_type_data data = {0};
457
458 data.els = *els;
459 while (psi_plist_get(strct->args, i++, &darg)) {
460 if (prev && prev->layout->pos == darg->layout->pos) {
461 /* skip bit fields */
462 continue;
463 }
464 psi_struct_type_element(C, &data, darg);
465 }
466
467 data.offset = (data.offset + data.max_align - 1) & ~(data.max_align - 1);
468 assert(data.offset <= strct->size);
469 psi_struct_type_pad(C, &data, strct->size - data.offset);
470
471 *els = data.els;
472 }
473
474 static inline void *psi_context_decl_arg_type(struct psi_context *C,
475 struct psi_decl_arg *darg)
476 {
477 struct psi_decl_type *real = psi_decl_type_get_real(darg->type);
478
479 if (real != darg->type && darg->type->real.def->var->pointer_level) {
480 return C->ops->typeof_decl(C, PSI_T_POINTER);
481 }
482
483 if (real->type == PSI_T_UNION) {
484 struct psi_decl_arg *arg;
485 psi_plist_get(real->real.unn->args, 0, &arg);
486 return psi_context_decl_arg_full_type(C, arg);
487 }
488
489 if (real->type == PSI_T_STRUCT) {
490 C->ops->composite_init(C, darg);
491 return darg->engine.type;
492 }
493
494 return C->ops->typeof_decl(C, real->type);
495 }
496
497 void *psi_context_decl_arg_call_type(struct psi_context *C,
498 struct psi_decl_arg *darg)
499 {
500 if (darg->var->pointer_level) {
501 return C->ops->typeof_decl(C, PSI_T_POINTER);
502 }
503
504 return psi_context_decl_arg_type(C, darg);
505 }
506
507 void *psi_context_decl_arg_full_type(struct psi_context *C,
508 struct psi_decl_arg *darg)
509 {
510 if (darg->var->array_size) {
511 C->ops->composite_init(C, darg);
512 return darg->engine.type;
513 }
514 if (darg->var->pointer_level) {
515 return C->ops->typeof_decl(C, PSI_T_POINTER);
516 }
517
518 return psi_context_decl_arg_type(C, darg);
519 }
520
521 void **psi_context_composite_type_elements(struct psi_context *C,
522 struct psi_decl_arg *darg, struct psi_plist **eles)
523 {
524 struct psi_decl_type *dtype;
525 struct psi_decl_arg *tmp;
526 void *type, *copy;
527
528 dtype = psi_decl_type_get_real(darg->type);
529
530 switch (dtype->type) {
531 case PSI_T_STRUCT:
532 psi_context_decl_struct_type_elements(C, dtype->real.strct, eles);
533 break;
534 case PSI_T_UNION:
535 if (psi_plist_bottom(dtype->real.unn->args, &tmp)) {
536 type = psi_context_decl_arg_full_type(C, tmp);
537 copy = C->ops->copyof_type(C, type);
538 *eles = psi_plist_add(*eles, &copy);
539 }
540 break;
541 default:
542 type = psi_context_decl_arg_type(C, darg);
543 for (size_t i = 0; i < darg->var->array_size; ++i) {
544 copy = C->ops->copyof_type(C, type);
545 *eles = psi_plist_add(*eles, &copy);
546 }
547 }
548
549 return psi_plist_eles(*eles);
550 }
551
552 /*
553 void psi_context_decl_func_array_elements(struct psi_context *C,
554 struct psi_decl *fn, struct psi_plist **els)
555 {
556 void *type;
557 size_t i;
558
559 if (fn->func->var->pointer_level > 1) {
560 type = C->ops->typeof_decl(C, PSI_T_POINTER);
561 } else {
562 type = psi_context_decl_type(C, fn->func->type);
563 }
564
565 for (i = 0; i < fn->func->var->array_size; ++i) {
566 void *copy = C->ops->copyof_type(C, type);
567 *els = psi_plist_add(*els, &copy);
568 }
569 }
570
571 void *psi_context_decl_func_type(struct psi_context *C, struct psi_decl *fn)
572 {
573 struct psi_decl_arg *darg = fn->func;
574
575 if (darg->engine.type) {
576 return darg->engine.type;
577 }
578
579 if (darg->var->pointer_level) {
580 if (!darg->var->array_size) {
581 return C->ops->typeof_decl(C, PSI_T_POINTER);
582 } else {
583 C->ops->composite_init(C, darg);
584 return darg->engine.type;
585 }
586 }
587
588 return psi_context_decl_type(C, darg->type);
589 }
590 */
591
592 void psi_context_compile(struct psi_context *C)
593 {
594 psi_context_consts_init(C);
595 psi_context_extvars_init(C);
596 psi_context_impls_init(C);
597 psi_context_decls_init(C);
598
599 /* zend_register_functions depends on EG(current_module) pointing into module */
600 EG(current_module) = zend_hash_str_find_ptr(&module_registry, "psi", sizeof("psi") - 1);
601 if (SUCCESS != zend_register_functions(NULL, C->closures, NULL, MODULE_PERSISTENT)) {
602 C->error(PSI_DATA(C), NULL, PSI_WARNING, "Failed to register functions!");
603 }
604 EG(current_module) = NULL;
605 }
606
607 ZEND_RESULT_CODE psi_context_call(struct psi_context *C, zend_execute_data *execute_data, zval *return_value, struct psi_impl *impl)
608 {
609 struct psi_call_frame *frame;
610
611 frame = psi_call_frame_init(C, impl->decl, impl);
612
613 if (SUCCESS != psi_call_frame_parse_args(frame, execute_data)) {
614 psi_call_frame_free(frame);
615
616 return FAILURE;
617 }
618
619 psi_call_frame_enter(frame);
620
621 if (SUCCESS != psi_call_frame_do_let(frame)) {
622 psi_call_frame_do_return(frame, return_value);
623 psi_call_frame_free(frame);
624
625 return FAILURE;
626 }
627
628 if (SUCCESS != psi_call_frame_do_assert(frame, PSI_ASSERT_PRE)) {
629 psi_call_frame_do_return(frame, return_value);
630 psi_call_frame_free(frame);
631
632 return FAILURE;
633 }
634
635 if (psi_call_frame_num_var_args(frame)) {
636 C->ops->call_va(frame);
637 } else {
638 C->ops->call(frame);
639 }
640
641 if (SUCCESS != psi_call_frame_do_assert(frame, PSI_ASSERT_POST)) {
642 psi_call_frame_do_return(frame, return_value);
643 psi_call_frame_free(frame);
644
645 return FAILURE;
646 }
647
648 psi_call_frame_do_return(frame, return_value);
649 psi_call_frame_do_set(frame);
650 psi_call_frame_do_free(frame);
651 psi_call_frame_free(frame);
652
653 return SUCCESS;
654 }
655
656
657 void psi_context_dtor(struct psi_context *C)
658 {
659 size_t i;
660 zend_function_entry *zfe;
661
662 if (C->decls) {
663 size_t i = 0;
664 struct psi_decl *decl;
665
666 while (psi_plist_get(C->decls, i++, &decl)) {
667 size_t j = 0;
668 struct psi_decl_arg *darg;
669
670 while (psi_plist_get(decl->args, j++, &darg)) {
671 C->ops->composite_dtor(C, darg);
672 }
673 C->ops->composite_dtor(C, decl->func);
674 C->ops->decl_dtor(C, decl);
675 }
676
677 }
678 if (C->vars) {
679 size_t i = 0;
680 struct psi_decl_extvar *evar;
681
682 while (psi_plist_get(C->vars, i++, &evar)) {
683 C->ops->composite_dtor(C, evar->getter->func);
684 C->ops->composite_dtor(C, evar->arg);
685 C->ops->extvar_dtor(C, evar);
686 }
687 }
688 if (C->impls) {
689 size_t i = 0;
690 struct psi_impl *impl;
691
692 while (psi_plist_get(C->impls, i++, &impl)) {
693 struct psi_let_stmt *let;
694 size_t j = 0;
695
696 while (psi_plist_get(impl->stmts.let, j++, &let)) {
697 psi_context_callback_dtor(C, let->exp, impl);
698 }
699
700 C->ops->impl_dtor(C, impl);
701 }
702 }
703
704 if (C->ops->dtor) {
705 C->ops->dtor(C);
706 }
707
708 psi_data_dtor(PSI_DATA(C));
709
710 if (C->data) {
711 for (i = 0; i < C->count; ++i) {
712 psi_data_dtor(&C->data[i]);
713 }
714 free(C->data);
715 }
716
717 if (C->closures) {
718 for (zfe = C->closures; zfe->fname; ++zfe) {
719 pefree((void *) zfe->arg_info, 1);
720 }
721 pefree(C->closures, 1);
722 }
723 }
724
725 void psi_context_free(struct psi_context **C)
726 {
727 if (*C) {
728 psi_context_dtor(*C);
729 free(*C);
730 *C = NULL;
731 }
732 }
733
734 void psi_context_dump(struct psi_dump *dump, struct psi_context *C)
735 {
736 PSI_DUMP(dump, "// psi.engine=%s\n// %lu files\n",
737 C->ops->name, C->count);
738
739 psi_data_dump(dump, PSI_DATA(C));
740
741 #if 0
742 if (C->flags & PSI_DEBUG) {
743 size_t i;
744
745 for (i = 0; i < C->count; ++i) {
746 psi_data_dump(dump, &C->data[i]);
747 }
748 }
749 #endif
750 }