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