1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
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 *******************************************************************************/
26 #include "php_psi_stdinc.h"
32 # define NAMLEN(dirent) strlen ((dirent)->d_name)
34 # define dirent direct
35 # define NAMLEN(dirent) ((dirent)->d_namlen)
36 # ifdef HAVE_SYS_NDIR_H
37 # include <sys/ndir.h>
39 # ifdef HAVE_SYS_DIR_H
50 #if PSI_THREADED_PARSER
54 #include "php_scandir.h"
64 #include "php_psi_posix.h"
66 PHP_MINIT_FUNCTION(psi_context
)
69 struct psi_context_ops
*ops
= NULL
;
72 if (!strcasecmp(PSI_G(engine
), "jit")) {
73 ops
= psi_libjit_ops();
77 ops
= psi_libffi_ops();
81 php_error(E_WARNING
, "No PSI engine found");
86 if (ops
->load
&& !ops
->load()) {
90 if (psi_check_env("PSI_DEBUG")) {
93 if (psi_check_env("PSI_SILENT")) {
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
));
103 PHP_MSHUTDOWN_FUNCTION(psi_context
)
105 if (psi_check_env("PSI_DUMP")) {
106 struct psi_dump dump
= {{.hn
= stdout
}, (psi_dump_cb
) fprintf
};
108 psi_context_dump(&dump
, PSI_G(context
));
111 psi_context_free(&PSI_G(context
));
113 if (PSI_G(ops
)->free
) {
120 struct psi_context
*psi_context_init(struct psi_context
*C
, struct psi_context_ops
*ops
, psi_error_cb error
, unsigned flags
)
123 C
= pemalloc(sizeof(*C
), 1);
125 memset(C
, 0, sizeof(*C
));
127 psi_data_ctor(PSI_DATA(C
), error
, flags
);
130 if (ops
->init
&& !ops
->init(C
)) {
137 static bool psi_context_add(struct psi_context
*C
, struct psi_parser
*P
)
141 struct psi_validate_scope scope
= {0};
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
));
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
);
154 struct psi_context_build_worker
{
156 struct psi_parser parser
;
157 struct psi_parser_input
*input
;
158 char psi_file
[MAXPATHLEN
];
161 static struct psi_context_build_worker
*psi_context_build_worker_init(
162 struct psi_context
*C
, const char *dir
, const char *file
)
164 struct psi_context_build_worker
*w
= pecalloc(1, sizeof(*w
), 1);
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",
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
));
181 #if PSI_THREADED_PARSER
182 static void *psi_context_build_worker_thread(void *thread_ptr
)
184 struct psi_context_build_worker
*thread
= thread_ptr
;
185 psi_parser_parse(&thread
->parser
, thread
->input
);
189 static bool psi_context_build_worker_thread_start(
190 struct psi_context_build_worker
*w
)
196 rc
= pthread_create(&w
->tid
, NULL
, psi_context_build_worker_thread
, w
);
207 w
->parser
.error(PSI_DATA(&w
->parser
), NULL
, PSI_WARNING
,
208 "Failed to start parser thread: %s", strerror(rc
));
215 static bool psi_context_build_worker_exec(struct psi_context_build_worker
*w
)
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
));
222 #if PSI_THREADED_PARSER
223 return psi_context_build_worker_thread_start(w
);
225 return psi_parser_parse(&w
->parser
, w
->input
);
229 static bool psi_context_build_worker_done(struct psi_context_build_worker
*w
)
231 #if PSI_THREADED_PARSER
238 # if HAVE_PTHREAD_TRYJOIN_NP
239 if (0 == pthread_tryjoin_np(w
->tid
, &rval
)) {
244 if (0 == pthread_join(w
->tid
, &rval
)) {
255 static void psi_context_build_worker_dtor(struct psi_context_build_worker
*w
)
257 #if PSI_THREADED_PARSER
260 int rc
= pthread_join(w
->tid
, &rval
);
263 w
->parser
.error(PSI_DATA(&w
->parser
), NULL
, PSI_WARNING
,
264 "Failed to finish parser thread: %s", strerror(errno
));
268 psi_parser_input_free(&w
->input
);
269 psi_parser_dtor(&w
->parser
);
272 static void psi_context_build_worker_free(struct psi_context_build_worker
**w
)
275 psi_context_build_worker_dtor(*w
);
281 static int psi_select_dirent(const struct dirent
*entry
)
284 # define FNM_CASEFOLD 0
286 return 0 == fnmatch("*.psi", entry
->d_name
, FNM_CASEFOLD
);
289 void psi_context_build(struct psi_context
*C
, const char *paths
)
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
);
297 struct dirent
**entries
= NULL
;
300 if ((sep
= strchr(ptr
, ':'))) {
304 n
= php_scandir(ptr
, &entries
, psi_select_dirent
, alphasort
);
307 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
,
308 "Failed to scan PSI directory '%s':%s", strerror(errno
));
310 for (i
= 0; i
< n
; ++i
) {
311 worker
= psi_context_build_worker_init(C
, ptr
, entries
[i
]->d_name
);
313 workers
= psi_plist_add(workers
, &worker
);
324 if (psi_plist_count(workers
)) {
325 struct psi_plist
*running
= psi_plist_init(
326 (psi_plist_dtor
) psi_context_build_worker_free
);
328 #ifdef _SC_NPROCESSORS_ONLN
329 long pool
= sysconf(_SC_NPROCESSORS_ONLN
);
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
);
345 while (psi_plist_get(running
, i
++, &worker
)) {
346 if (psi_context_build_worker_done(worker
)) {
347 psi_context_add(C
, &worker
->parser
);
349 psi_plist_del(running
, --i
, NULL
);
350 psi_context_build_worker_free(&worker
);
352 if (psi_plist_pop(workers
, &worker
)) {
353 psi_plist_add(running
, &worker
);
362 psi_context_compile(C
);
366 static inline bool prefix_match(zend_string
*a
, zend_string
*b
)
370 for (i
= 0; i
< a
->len
&& i
< b
->len
; ++i
) {
371 if (tolower(a
->val
[i
]) != tolower(b
->val
[i
])) {
374 if (i
&& a
->val
[i
] == '_') {
382 static inline void psi_context_consts_init(struct psi_context
*C
)
386 ZEND_CONSTANT_SET_FLAGS(&zc
, CONST_CS
|CONST_PERSISTENT
, EG(current_module
)->module_number
);
392 while (psi_plist_get(C
->consts
, i
++, &c
)) {
394 if (zend_get_constant(c
->name
)) {
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
);
401 zend_register_constant(&zc
);
406 struct psi_decl_enum
*e
;
408 while (psi_plist_get(C
->enums
, i
++, &e
)) {
410 struct psi_decl_enum_item
*item
;
412 while (psi_plist_get(e
->items
, j
++, &item
)) {
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
);
420 name
= strpprintf(0, "psi\\%s\\%s", e
->name
->val
, item
->name
->val
);
423 if (zend_get_constant(name
)) {
424 zend_string_release(name
);
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
);
437 static inline void psi_context_extvars_init(struct psi_context
*C
)
441 struct psi_decl_extvar
*evar
;
443 while (psi_plist_get(C
->vars
, v
++, &evar
)) {
444 C
->ops
->extvar_init(C
, evar
);
449 static inline void psi_context_callback_init(struct psi_context
*C
,
450 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
452 struct psi_let_func
*fn
= let_exp
->data
.func
;
454 switch (let_exp
->kind
) {
455 case PSI_LET_CALLBACK
:
456 C
->ops
->cb_init(C
, let_exp
, impl
);
458 fn
= let_exp
->data
.callback
->func
;
463 struct psi_let_exp
*inner_let
;
465 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
466 psi_context_callback_init(C
, inner_let
, impl
);
475 static inline void psi_context_callback_dtor(struct psi_context
*C
,
476 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
478 struct psi_let_func
*fn
= let_exp
->data
.func
;
480 switch (let_exp
->kind
) {
481 case PSI_LET_CALLBACK
:
482 C
->ops
->cb_dtor(C
, let_exp
, impl
);
484 fn
= let_exp
->data
.callback
->func
;
489 struct psi_let_exp
*cb
;
491 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
492 psi_context_callback_dtor(C
, cb
, impl
);
501 static inline void psi_context_impls_init(struct psi_context
*C
)
504 zend_function_entry
*zfe
= NULL
;
508 struct psi_impl
*impl
;
510 zfe
= pecalloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
), 1);
512 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
513 zend_function_entry
*zf
= &zfe
[nf
];
514 struct psi_let_stmt
*let
;
520 if (!C
->ops
->decl_init(C
, impl
->decl
)) {
523 if (!C
->ops
->impl_init(C
, impl
, &zf
->handler
)) {
526 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
527 psi_context_callback_init(C
, let
->exp
, impl
);
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
);
540 static inline void psi_context_decls_init(struct psi_context
*C
)
544 struct psi_decl
*decl
;
546 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
548 C
->ops
->decl_init(C
, decl
);
554 struct psi_struct_type_data
{
555 struct psi_plist
*els
;
560 static inline void psi_struct_type_pad(struct psi_context
*C
,
561 struct psi_struct_type_data
*data
, size_t padding
)
563 void *ele
= C
->ops
->typeof_decl(C
, PSI_T_INT8
);
566 void *pad
= C
->ops
->copyof_type(C
, ele
);
567 data
->els
= psi_plist_add(data
->els
, &pad
);
571 static inline void psi_struct_type_element(struct psi_context
*C
,
572 struct psi_struct_type_data
*data
, struct psi_decl_arg
*darg
)
576 struct psi_layout type_layout
;
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
;
584 type
= psi_context_decl_arg_full_type(C
, darg
);
585 C
->ops
->layoutof_type(C
, type
, &type_layout
);
587 if (type_layout
.pos
> data
->max_align
) {
588 data
->max_align
= type_layout
.pos
;
591 assert(type_layout
.len
<= darg
->layout
->len
);
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
, ©
);
597 assert(darg
->layout
->len
== type_layout
.len
* (darg
->var
->array_size
?: 1));
598 data
->offset
+= darg
->layout
->len
;
601 static inline void psi_context_decl_struct_type_elements(struct psi_context
*C
,
602 struct psi_decl_struct
*strct
, struct psi_plist
**els
)
605 struct psi_decl_arg
*darg
, *prev
= NULL
;
606 struct psi_struct_type_data data
= {0};
609 while (psi_plist_get(strct
->args
, i
++, &darg
)) {
610 if (prev
&& prev
->layout
->pos
== darg
->layout
->pos
) {
611 /* skip bit fields */
614 psi_struct_type_element(C
, &data
, darg
);
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
);
624 static inline void *psi_context_decl_arg_type(struct psi_context
*C
,
625 struct psi_decl_arg
*darg
)
627 struct psi_decl_type
*real
= psi_decl_type_get_real(darg
->type
);
629 if (real
!= darg
->type
&& darg
->type
->real
.def
->var
->pointer_level
) {
630 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
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
);
639 if (real
->type
== PSI_T_STRUCT
) {
640 C
->ops
->composite_init(C
, darg
);
641 return darg
->engine
.type
;
644 return C
->ops
->typeof_decl(C
, real
->type
);
647 void *psi_context_decl_arg_call_type(struct psi_context
*C
,
648 struct psi_decl_arg
*darg
)
650 if (darg
->var
->pointer_level
) {
651 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
654 return psi_context_decl_arg_type(C
, darg
);
657 void *psi_context_decl_arg_full_type(struct psi_context
*C
,
658 struct psi_decl_arg
*darg
)
660 if (darg
->var
->array_size
) {
661 C
->ops
->composite_init(C
, darg
);
662 return darg
->engine
.type
;
664 if (darg
->var
->pointer_level
) {
665 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
668 return psi_context_decl_arg_type(C
, darg
);
671 void **psi_context_composite_type_elements(struct psi_context
*C
,
672 struct psi_decl_arg
*darg
, struct psi_plist
**eles
)
674 struct psi_decl_type
*dtype
;
675 struct psi_decl_arg
*tmp
;
678 dtype
= psi_decl_type_get_real(darg
->type
);
680 switch (dtype
->type
) {
682 psi_context_decl_struct_type_elements(C
, dtype
->real
.strct
, eles
);
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
, ©
);
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
, ©
);
699 return psi_plist_eles(*eles
);
703 void psi_context_decl_func_array_elements(struct psi_context *C,
704 struct psi_decl *fn, struct psi_plist **els)
709 if (fn->func->var->pointer_level > 1) {
710 type = C->ops->typeof_decl(C, PSI_T_POINTER);
712 type = psi_context_decl_type(C, fn->func->type);
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, ©);
721 void *psi_context_decl_func_type(struct psi_context *C, struct psi_decl *fn)
723 struct psi_decl_arg *darg = fn->func;
725 if (darg->engine.type) {
726 return darg->engine.type;
729 if (darg->var->pointer_level) {
730 if (!darg->var->array_size) {
731 return C->ops->typeof_decl(C, PSI_T_POINTER);
733 C->ops->composite_init(C, darg);
734 return darg->engine.type;
738 return psi_context_decl_type(C, darg->type);
742 void psi_context_compile(struct psi_context
*C
)
744 psi_context_consts_init(C
);
745 psi_context_extvars_init(C
);
746 psi_context_impls_init(C
);
747 psi_context_decls_init(C
);
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!");
754 EG(current_module
) = NULL
;
757 ZEND_RESULT_CODE
psi_context_call(struct psi_context
*C
, zend_execute_data
*execute_data
, zval
*return_value
, struct psi_impl
*impl
)
759 struct psi_call_frame
*frame
;
761 frame
= psi_call_frame_init(C
, impl
->decl
, impl
);
763 if (SUCCESS
!= psi_call_frame_parse_args(frame
, execute_data
)) {
764 psi_call_frame_free(frame
);
769 psi_call_frame_enter(frame
);
771 if (SUCCESS
!= psi_call_frame_do_let(frame
)) {
772 psi_call_frame_do_return(frame
, return_value
);
773 psi_call_frame_free(frame
);
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
);
785 if (psi_call_frame_num_var_args(frame
)) {
786 C
->ops
->call_va(frame
);
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
);
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
);
807 void psi_context_dtor(struct psi_context
*C
)
810 zend_function_entry
*zfe
;
814 struct psi_decl
*decl
;
816 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
818 struct psi_decl_arg
*darg
;
820 while (psi_plist_get(decl
->args
, j
++, &darg
)) {
821 C
->ops
->composite_dtor(C
, darg
);
823 C
->ops
->composite_dtor(C
, decl
->func
);
824 C
->ops
->decl_dtor(C
, decl
);
830 struct psi_decl_extvar
*evar
;
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
);
840 struct psi_impl
*impl
;
842 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
843 struct psi_let_stmt
*let
;
846 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
847 psi_context_callback_dtor(C
, let
->exp
, impl
);
850 C
->ops
->impl_dtor(C
, impl
);
858 psi_data_dtor(PSI_DATA(C
));
861 for (i
= 0; i
< C
->count
; ++i
) {
862 psi_data_dtor(&C
->data
[i
]);
868 for (zfe
= C
->closures
; zfe
->fname
; ++zfe
) {
869 pefree((void *) zfe
->arg_info
, 1);
871 pefree(C
->closures
, 1);
875 void psi_context_free(struct psi_context
**C
)
878 psi_context_dtor(*C
);
884 void psi_context_dump(struct psi_dump
*dump
, struct psi_context
*C
)
886 PSI_DUMP(dump
, "// psi.engine=%s\n// %lu files\n",
887 C
->ops
->name
, C
->count
);
889 psi_data_dump(dump
, PSI_DATA(C
));
892 if (C
->flags
& PSI_DEBUG
) {
895 for (i
= 0; i
< C
->count
; ++i
) {
896 psi_data_dump(dump
, &C
->data
[i
]);