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