travis: update
[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 size_t i;
695
696 dtype = psi_decl_type_get_real(darg->type);
697
698 switch (dtype->type) {
699 case PSI_T_STRUCT:
700 psi_context_decl_struct_type_elements(C, dtype->real.strct, eles);
701 break;
702 case PSI_T_UNION:
703 if (psi_plist_bottom(dtype->real.unn->args, &tmp)) {
704 type = psi_context_decl_arg_full_type(C, tmp);
705 copy = C->ops->copyof_type(C, type);
706 *eles = psi_plist_add(*eles, &copy);
707 }
708 break;
709 default:
710 type = psi_context_decl_arg_type(C, darg);
711 for (i = 0; i < darg->var->array_size; ++i) {
712 copy = C->ops->copyof_type(C, type);
713 *eles = psi_plist_add(*eles, &copy);
714 }
715 }
716
717 return psi_plist_eles(*eles);
718 }
719
720 void psi_context_compile(struct psi_context *C)
721 {
722 psi_context_consts_init(C);
723 psi_context_extvars_init(C);
724 psi_context_impls_init(C);
725 psi_context_decls_init(C);
726
727 /* zend_register_functions depends on EG(current_module) pointing into module */
728 EG(current_module) = zend_hash_str_find_ptr(&module_registry, "psi", sizeof("psi") - 1);
729 if (SUCCESS != zend_register_functions(NULL, C->closures, NULL, MODULE_PERSISTENT)) {
730 C->error(PSI_DATA(C), NULL, PSI_WARNING, "Failed to register functions!");
731 }
732 EG(current_module) = NULL;
733 }
734
735 bool psi_context_call(struct psi_context *C, zend_execute_data *execute_data, zval *return_value, struct psi_impl *impl)
736 {
737 struct psi_call_frame *frame;
738
739 frame = psi_call_frame_init(C, impl->decl, impl);
740
741 if (!psi_call_frame_parse_args(frame, execute_data)) {
742 psi_call_frame_free(frame);
743
744 return false;
745 }
746
747 psi_call_frame_enter(frame);
748
749 if (!psi_call_frame_do_let(frame)) {
750 psi_call_frame_do_return(frame, return_value);
751 psi_call_frame_free(frame);
752
753 return false;
754 }
755
756 if (!psi_call_frame_do_assert(frame, PSI_ASSERT_PRE)) {
757 psi_call_frame_do_return(frame, return_value);
758 psi_call_frame_free(frame);
759
760 return false;
761 }
762
763 if (psi_call_frame_num_var_args(frame)) {
764 C->ops->call_va(frame);
765 } else {
766 C->ops->call(frame);
767 }
768
769 if (!psi_call_frame_do_assert(frame, PSI_ASSERT_POST)) {
770 psi_call_frame_do_return(frame, return_value);
771 psi_call_frame_free(frame);
772
773 return false;
774 }
775
776 psi_call_frame_do_return(frame, return_value);
777 psi_call_frame_do_set(frame);
778 psi_call_frame_do_free(frame);
779 psi_call_frame_free(frame);
780
781 return true;
782 }
783
784
785 void psi_context_dtor(struct psi_context *C)
786 {
787 size_t i;
788 zend_function_entry *zfe;
789
790 if (C->decls) {
791 size_t i = 0;
792 struct psi_decl *decl;
793
794 while (psi_plist_get(C->decls, i++, &decl)) {
795 size_t j = 0;
796 struct psi_decl_arg *darg;
797
798 while (psi_plist_get(decl->args, j++, &darg)) {
799 C->ops->composite_dtor(C, darg);
800 }
801 C->ops->composite_dtor(C, decl->func);
802 C->ops->decl_dtor(C, decl);
803 }
804
805 }
806 if (C->vars) {
807 size_t i = 0;
808 struct psi_decl_extvar *evar;
809
810 while (psi_plist_get(C->vars, i++, &evar)) {
811 C->ops->composite_dtor(C, evar->getter->func);
812 C->ops->composite_dtor(C, evar->arg);
813 C->ops->extvar_dtor(C, evar);
814 }
815 }
816 if (C->impls) {
817 size_t i = 0;
818 struct psi_impl *impl;
819
820 while (psi_plist_get(C->impls, i++, &impl)) {
821 struct psi_let_stmt *let;
822 size_t j = 0;
823
824 while (psi_plist_get(impl->stmts.let, j++, &let)) {
825 psi_context_callback_dtor(C, let->exp, impl);
826 }
827
828 C->ops->impl_dtor(C, impl);
829 }
830 }
831
832 if (C->ops->dtor) {
833 C->ops->dtor(C);
834 }
835
836 psi_data_dtor(PSI_DATA(C));
837
838 if (C->data) {
839 for (i = 0; i < C->count; ++i) {
840 psi_data_dtor(&C->data[i]);
841 }
842 free(C->data);
843 }
844
845 if (C->closures) {
846 for (zfe = C->closures; zfe->fname; ++zfe) {
847 pefree((void *) zfe->arg_info, 1);
848 }
849 pefree(C->closures, 1);
850 }
851 }
852
853 void psi_context_free(struct psi_context **C)
854 {
855 if (*C) {
856 psi_context_dtor(*C);
857 free(*C);
858 *C = NULL;
859 }
860 }
861
862 void psi_context_dump(struct psi_dump *dump, struct psi_context *C)
863 {
864 PSI_DUMP(dump, "// psi.engine=%s\n// %lu files\n",
865 C->ops->name, C->count);
866
867 psi_data_dump(dump, PSI_DATA(C));
868
869 #if 0
870 if (C->flags & PSI_DEBUG) {
871 size_t i;
872
873 for (i = 0; i < C->count; ++i) {
874 psi_data_dump(dump, &C->data[i]);
875 }
876 }
877 #endif
878 }