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