fix test
[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 char cwd[PATH_MAX];
310 C->error(PSI_DATA(C), NULL, PSI_WARNING,
311 "Failed to scan PSI directory '%s%s%s': %s",
312 *ptr == '/' ? "" : getcwd(cwd, PATH_MAX),
313 *ptr != '/' && *ptr != '.' ? "/" : "",
314 ptr, strerror(errno));
315 } else {
316 for (i = 0; i < n; ++i) {
317 worker = psi_context_build_worker_init(C, ptr, entries[i]->d_name);
318 PSI_DEBUG_PRINT(C, "PSI: init worker(%p) for %s/%s\n",
319 worker, ptr, entries[i]->d_name);
320 if (worker) {
321 workers = psi_plist_add(workers, &worker);
322 }
323 free(entries[i]);
324 }
325 free(entries);
326 }
327 ptr = sep + 1;
328 } while (sep);
329
330 free(cpy);
331
332 if (psi_plist_count(workers)) {
333 struct psi_plist *running = psi_plist_init(
334 (psi_plist_dtor) psi_context_build_worker_free);
335 long active = 0;
336 #ifdef _SC_NPROCESSORS_ONLN
337 long pool = sysconf(_SC_NPROCESSORS_ONLN);
338 #else
339 long pool = 4;
340 #endif
341
342 while (psi_plist_count(workers) && active < pool) {
343 if (psi_plist_pop(workers, &worker)) {
344 PSI_DEBUG_PRINT(C, "PSI: starting worker %p\n", worker);
345 if (psi_context_build_worker_exec(worker)) {
346 running = psi_plist_add(running, &worker);
347 ++active;
348 }
349 }
350 }
351 while (active) {
352 size_t i = 0;
353
354 while (psi_plist_get(running, i++, &worker)) {
355 if (psi_context_build_worker_done(worker)) {
356 PSI_DEBUG_PRINT(C, "PSI: collecting worker %p\n", worker);
357 psi_context_add(C, &worker->parser);
358
359 psi_plist_del(running, --i, NULL);
360 psi_context_build_worker_free(&worker);
361
362 if (psi_plist_pop(workers, &worker)) {
363 PSI_DEBUG_PRINT(C, "PSI: starting worker %p\n", worker);
364 if (psi_context_build_worker_exec(worker)) {
365 running = psi_plist_add(running, &worker);
366 }
367 } else {
368 --active;
369 }
370 }
371 }
372 }
373 }
374
375 psi_context_compile(C);
376 }
377
378 #include <ctype.h>
379 static inline bool prefix_match(zend_string *a, zend_string *b)
380 {
381 size_t i;
382
383 for (i = 0; i < a->len && i < b->len; ++i) {
384 if (tolower(a->val[i]) != tolower(b->val[i])) {
385 return false;
386 }
387 if (i && a->val[i] == '_') {
388 break;
389 }
390 }
391
392 return true;
393 }
394
395 static inline void psi_context_consts_init(struct psi_context *C)
396 {
397 zend_constant zc;
398
399 ZEND_CONSTANT_SET_FLAGS(&zc, CONST_CS|CONST_PERSISTENT, EG(current_module)->module_number);
400
401 if (C->consts) {
402 size_t i = 0;
403 struct psi_const *c;
404
405 while (psi_plist_get(C->consts, i++, &c)) {
406
407 if (zend_get_constant(c->name)) {
408 continue;
409 }
410
411 zc.name = zend_string_copy(c->name);
412 psi_impl_def_val_get_zval(c->val, c->type ? c->type->type : PSI_T_MIXED, &zc.value);
413
414 zend_register_constant(&zc);
415 }
416 }
417 if (C->enums) {
418 size_t i = 0;
419 struct psi_decl_enum *e;
420
421 while (psi_plist_get(C->enums, i++, &e)) {
422 size_t j = 0;
423 struct psi_decl_enum_item *item;
424
425 while (psi_plist_get(e->items, j++, &item)) {
426 zend_string *name;
427
428 if (psi_decl_type_is_anon(e->name, "enum")
429 || prefix_match(e->name, item->name)) {
430 name = strpprintf(0, "psi\\%s", item->name->val);
431 } else {
432
433 name = strpprintf(0, "psi\\%s\\%s", e->name->val, item->name->val);
434 }
435
436 if (zend_get_constant(name)) {
437 zend_string_release(name);
438 continue;
439 }
440
441 zc.name = zend_string_dup(name, 1);
442 ZVAL_LONG(&zc.value, psi_num_exp_get_long(item->num, NULL, NULL));
443 zend_register_constant(&zc);
444 zend_string_release(name);
445 }
446 }
447 }
448 }
449
450 static inline void psi_context_extvars_init(struct psi_context *C)
451 {
452 if (C->vars) {
453 size_t v = 0;
454 struct psi_decl_extvar *evar;
455
456 while (psi_plist_get(C->vars, v++, &evar)) {
457 C->ops->extvar_init(C, evar);
458 }
459 }
460 }
461
462 static inline void psi_context_callback_init(struct psi_context *C,
463 struct psi_let_exp *let_exp, struct psi_impl *impl)
464 {
465 struct psi_let_func *fn = let_exp->data.func;
466
467 switch (let_exp->kind) {
468 case PSI_LET_CALLBACK:
469 C->ops->cb_init(C, let_exp, impl);
470 /* override fn */
471 fn = let_exp->data.callback->func;
472 /* no break */
473 case PSI_LET_FUNC:
474 if (fn->inner) {
475 size_t i = 0;
476 struct psi_let_exp *inner_let;
477
478 while (psi_plist_get(fn->inner, i++, &inner_let)) {
479 psi_context_callback_init(C, inner_let, impl);
480 }
481 }
482 break;
483 default:
484 break;
485 }
486 }
487
488 static inline void psi_context_callback_dtor(struct psi_context *C,
489 struct psi_let_exp *let_exp, struct psi_impl *impl)
490 {
491 struct psi_let_func *fn = let_exp->data.func;
492
493 switch (let_exp->kind) {
494 case PSI_LET_CALLBACK:
495 C->ops->cb_dtor(C, let_exp, impl);
496 /* override func */
497 fn = let_exp->data.callback->func;
498 /* no break */
499 case PSI_LET_FUNC:
500 if (fn->inner) {
501 size_t i = 0;
502 struct psi_let_exp *cb;
503
504 while (psi_plist_get(fn->inner, i++, &cb)) {
505 psi_context_callback_dtor(C, cb, impl);
506 }
507 }
508 break;
509 default:
510 break;
511 }
512 }
513
514 static inline void psi_context_impls_init(struct psi_context *C)
515 {
516 size_t nf = 0;
517 zend_function_entry *zfe = NULL;
518
519 if (C->impls) {
520 size_t i = 0;
521 struct psi_impl *impl;
522
523 zfe = pecalloc(psi_plist_count(C->impls) + 1, sizeof(*zfe), 1);
524
525 while (psi_plist_get(C->impls, i++, &impl)) {
526 zend_function_entry *zf = &zfe[nf];
527 struct psi_let_stmt *let;
528 size_t l = 0;
529
530 if (!impl->decl) {
531 continue;
532 }
533 if (!C->ops->decl_init(C, impl->decl)) {
534 continue;
535 }
536 if (!C->ops->impl_init(C, impl, &zf->handler)) {
537 continue;
538 }
539 while (psi_plist_get(impl->stmts.let, l++, &let)) {
540 psi_context_callback_init(C, let->exp, impl);
541 }
542
543 zf->fname = impl->func->name->val + (impl->func->name->val[0] == '\\');
544 zf->num_args = psi_plist_count(impl->func->args);
545 zf->arg_info = psi_internal_arginfo(impl);
546 ++nf;
547 }
548 }
549
550 C->closures = zfe;
551 }
552
553 static inline void psi_context_decls_init(struct psi_context *C)
554 {
555 if (C->decls) {
556 size_t d = 0;
557 struct psi_decl *decl;
558
559 while (psi_plist_get(C->decls, d++, &decl)) {
560 if (!decl->info) {
561 C->ops->decl_init(C, decl);
562 }
563 }
564 }
565 }
566
567 struct psi_struct_type_data {
568 struct psi_plist *els;
569 size_t offset;
570 size_t max_align;
571 };
572
573 static inline void psi_struct_type_pad(struct psi_context *C,
574 struct psi_struct_type_data *data, size_t padding)
575 {
576 void *ele = C->ops->typeof_decl(C, PSI_T_INT8);
577
578 while (padding--) {
579 void *pad = C->ops->copyof_type(C, ele);
580 data->els = psi_plist_add(data->els, &pad);
581 }
582 }
583
584 static inline void psi_struct_type_element(struct psi_context *C,
585 struct psi_struct_type_data *data, struct psi_decl_arg *darg)
586 {
587 void *type, *copy;
588 size_t i;
589 struct psi_layout type_layout;
590
591 if (darg->layout->pos) {
592 assert(data->offset <= darg->layout->pos);
593 psi_struct_type_pad(C, data, darg->layout->pos - data->offset);
594 data->offset = darg->layout->pos;
595 }
596
597 type = psi_context_decl_arg_full_type(C, darg);
598 C->ops->layoutof_type(C, type, &type_layout);
599
600 if (type_layout.pos > data->max_align) {
601 data->max_align = type_layout.pos;
602 }
603
604 assert(type_layout.len <= darg->layout->len);
605
606 for (i = 0; i < (darg->var->array_size ?: 1); ++i) {
607 copy = C->ops->copyof_type(C, type);
608 data->els = psi_plist_add(data->els, &copy);
609 }
610 assert(darg->layout->len == type_layout.len * (darg->var->array_size ?: 1));
611 data->offset += darg->layout->len;
612 }
613
614 static inline void psi_context_decl_struct_type_elements(struct psi_context *C,
615 struct psi_decl_struct *strct, struct psi_plist **els)
616 {
617 size_t i = 0;
618 struct psi_decl_arg *darg, *prev = NULL;
619 struct psi_struct_type_data data = {0};
620
621 data.els = *els;
622 while (psi_plist_get(strct->args, i++, &darg)) {
623 if (prev && prev->layout->pos == darg->layout->pos) {
624 /* skip bit fields */
625 continue;
626 }
627 psi_struct_type_element(C, &data, darg);
628 }
629
630 data.offset = (data.offset + data.max_align - 1) & ~(data.max_align - 1);
631 assert(data.offset <= strct->size);
632 psi_struct_type_pad(C, &data, strct->size - data.offset);
633
634 *els = data.els;
635 }
636
637 static inline void *psi_context_decl_arg_type(struct psi_context *C,
638 struct psi_decl_arg *darg)
639 {
640 struct psi_decl_type *real = psi_decl_type_get_real(darg->type);
641
642 if (real != darg->type && darg->type->real.def->var->pointer_level) {
643 return C->ops->typeof_decl(C, PSI_T_POINTER);
644 }
645
646 if (real->type == PSI_T_UNION) {
647 struct psi_decl_arg *arg;
648 psi_plist_get(real->real.unn->args, 0, &arg);
649 return psi_context_decl_arg_full_type(C, arg);
650 }
651
652 if (real->type == PSI_T_STRUCT) {
653 C->ops->composite_init(C, darg);
654 return darg->engine.type;
655 }
656
657 return C->ops->typeof_decl(C, real->type);
658 }
659
660 void *psi_context_decl_arg_call_type(struct psi_context *C,
661 struct psi_decl_arg *darg)
662 {
663 if (darg->var->pointer_level) {
664 return C->ops->typeof_decl(C, PSI_T_POINTER);
665 }
666
667 return psi_context_decl_arg_type(C, darg);
668 }
669
670 void *psi_context_decl_arg_full_type(struct psi_context *C,
671 struct psi_decl_arg *darg)
672 {
673 if (darg->var->array_size) {
674 C->ops->composite_init(C, darg);
675 return darg->engine.type;
676 }
677 if (darg->var->pointer_level) {
678 return C->ops->typeof_decl(C, PSI_T_POINTER);
679 }
680
681 return psi_context_decl_arg_type(C, darg);
682 }
683
684 void **psi_context_composite_type_elements(struct psi_context *C,
685 struct psi_decl_arg *darg, struct psi_plist **eles)
686 {
687 struct psi_decl_type *dtype;
688 struct psi_decl_arg *tmp;
689 void *type, *copy;
690
691 dtype = psi_decl_type_get_real(darg->type);
692
693 switch (dtype->type) {
694 case PSI_T_STRUCT:
695 psi_context_decl_struct_type_elements(C, dtype->real.strct, eles);
696 break;
697 case PSI_T_UNION:
698 if (psi_plist_bottom(dtype->real.unn->args, &tmp)) {
699 type = psi_context_decl_arg_full_type(C, tmp);
700 copy = C->ops->copyof_type(C, type);
701 *eles = psi_plist_add(*eles, &copy);
702 }
703 break;
704 default:
705 type = psi_context_decl_arg_type(C, darg);
706 for (size_t i = 0; i < darg->var->array_size; ++i) {
707 copy = C->ops->copyof_type(C, type);
708 *eles = psi_plist_add(*eles, &copy);
709 }
710 }
711
712 return psi_plist_eles(*eles);
713 }
714
715 /*
716 void psi_context_decl_func_array_elements(struct psi_context *C,
717 struct psi_decl *fn, struct psi_plist **els)
718 {
719 void *type;
720 size_t i;
721
722 if (fn->func->var->pointer_level > 1) {
723 type = C->ops->typeof_decl(C, PSI_T_POINTER);
724 } else {
725 type = psi_context_decl_type(C, fn->func->type);
726 }
727
728 for (i = 0; i < fn->func->var->array_size; ++i) {
729 void *copy = C->ops->copyof_type(C, type);
730 *els = psi_plist_add(*els, &copy);
731 }
732 }
733
734 void *psi_context_decl_func_type(struct psi_context *C, struct psi_decl *fn)
735 {
736 struct psi_decl_arg *darg = fn->func;
737
738 if (darg->engine.type) {
739 return darg->engine.type;
740 }
741
742 if (darg->var->pointer_level) {
743 if (!darg->var->array_size) {
744 return C->ops->typeof_decl(C, PSI_T_POINTER);
745 } else {
746 C->ops->composite_init(C, darg);
747 return darg->engine.type;
748 }
749 }
750
751 return psi_context_decl_type(C, darg->type);
752 }
753 */
754
755 void psi_context_compile(struct psi_context *C)
756 {
757 psi_context_consts_init(C);
758 psi_context_extvars_init(C);
759 psi_context_impls_init(C);
760 psi_context_decls_init(C);
761
762 /* zend_register_functions depends on EG(current_module) pointing into module */
763 EG(current_module) = zend_hash_str_find_ptr(&module_registry, "psi", sizeof("psi") - 1);
764 if (SUCCESS != zend_register_functions(NULL, C->closures, NULL, MODULE_PERSISTENT)) {
765 C->error(PSI_DATA(C), NULL, PSI_WARNING, "Failed to register functions!");
766 }
767 EG(current_module) = NULL;
768 }
769
770 ZEND_RESULT_CODE psi_context_call(struct psi_context *C, zend_execute_data *execute_data, zval *return_value, struct psi_impl *impl)
771 {
772 struct psi_call_frame *frame;
773
774 frame = psi_call_frame_init(C, impl->decl, impl);
775
776 if (SUCCESS != psi_call_frame_parse_args(frame, execute_data)) {
777 psi_call_frame_free(frame);
778
779 return FAILURE;
780 }
781
782 psi_call_frame_enter(frame);
783
784 if (SUCCESS != psi_call_frame_do_let(frame)) {
785 psi_call_frame_do_return(frame, return_value);
786 psi_call_frame_free(frame);
787
788 return FAILURE;
789 }
790
791 if (SUCCESS != psi_call_frame_do_assert(frame, PSI_ASSERT_PRE)) {
792 psi_call_frame_do_return(frame, return_value);
793 psi_call_frame_free(frame);
794
795 return FAILURE;
796 }
797
798 if (psi_call_frame_num_var_args(frame)) {
799 C->ops->call_va(frame);
800 } else {
801 C->ops->call(frame);
802 }
803
804 if (SUCCESS != psi_call_frame_do_assert(frame, PSI_ASSERT_POST)) {
805 psi_call_frame_do_return(frame, return_value);
806 psi_call_frame_free(frame);
807
808 return FAILURE;
809 }
810
811 psi_call_frame_do_return(frame, return_value);
812 psi_call_frame_do_set(frame);
813 psi_call_frame_do_free(frame);
814 psi_call_frame_free(frame);
815
816 return SUCCESS;
817 }
818
819
820 void psi_context_dtor(struct psi_context *C)
821 {
822 size_t i;
823 zend_function_entry *zfe;
824
825 if (C->decls) {
826 size_t i = 0;
827 struct psi_decl *decl;
828
829 while (psi_plist_get(C->decls, i++, &decl)) {
830 size_t j = 0;
831 struct psi_decl_arg *darg;
832
833 while (psi_plist_get(decl->args, j++, &darg)) {
834 C->ops->composite_dtor(C, darg);
835 }
836 C->ops->composite_dtor(C, decl->func);
837 C->ops->decl_dtor(C, decl);
838 }
839
840 }
841 if (C->vars) {
842 size_t i = 0;
843 struct psi_decl_extvar *evar;
844
845 while (psi_plist_get(C->vars, i++, &evar)) {
846 C->ops->composite_dtor(C, evar->getter->func);
847 C->ops->composite_dtor(C, evar->arg);
848 C->ops->extvar_dtor(C, evar);
849 }
850 }
851 if (C->impls) {
852 size_t i = 0;
853 struct psi_impl *impl;
854
855 while (psi_plist_get(C->impls, i++, &impl)) {
856 struct psi_let_stmt *let;
857 size_t j = 0;
858
859 while (psi_plist_get(impl->stmts.let, j++, &let)) {
860 psi_context_callback_dtor(C, let->exp, impl);
861 }
862
863 C->ops->impl_dtor(C, impl);
864 }
865 }
866
867 if (C->ops->dtor) {
868 C->ops->dtor(C);
869 }
870
871 psi_data_dtor(PSI_DATA(C));
872
873 if (C->data) {
874 for (i = 0; i < C->count; ++i) {
875 psi_data_dtor(&C->data[i]);
876 }
877 free(C->data);
878 }
879
880 if (C->closures) {
881 for (zfe = C->closures; zfe->fname; ++zfe) {
882 pefree((void *) zfe->arg_info, 1);
883 }
884 pefree(C->closures, 1);
885 }
886 }
887
888 void psi_context_free(struct psi_context **C)
889 {
890 if (*C) {
891 psi_context_dtor(*C);
892 free(*C);
893 *C = NULL;
894 }
895 }
896
897 void psi_context_dump(struct psi_dump *dump, struct psi_context *C)
898 {
899 PSI_DUMP(dump, "// psi.engine=%s\n// %lu files\n",
900 C->ops->name, C->count);
901
902 psi_data_dump(dump, PSI_DATA(C));
903
904 #if 0
905 if (C->flags & PSI_DEBUG) {
906 size_t i;
907
908 for (i = 0; i < C->count; ++i) {
909 psi_data_dump(dump, &C->data[i]);
910 }
911 }
912 #endif
913 }