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 *******************************************************************************/
34 # define NAMLEN(dirent) strlen ((dirent)->d_name)
36 # define dirent direct
37 # define NAMLEN(dirent) ((dirent)->d_namlen)
38 # ifdef HAVE_SYS_NDIR_H
39 # include <sys/ndir.h>
41 # ifdef HAVE_SYS_DIR_H
52 #if PSI_THREADED_PARSER
56 #include "php_scandir.h"
66 PHP_MINIT_FUNCTION(psi_context
);
67 PHP_MINIT_FUNCTION(psi_context
)
70 struct psi_context_ops
*ops
= NULL
;
73 if (!strcasecmp(PSI_G(engine
), "jit")) {
74 ops
= psi_libjit_ops();
78 ops
= psi_libffi_ops();
82 php_error(E_WARNING
, "No PSI engine found");
87 if (ops
->load
&& !ops
->load()) {
91 if (psi_check_env("PSI_DEBUG")) {
94 if (psi_check_env("PSI_SILENT")) {
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
));
104 PHP_MSHUTDOWN_FUNCTION(psi_context
);
105 PHP_MSHUTDOWN_FUNCTION(psi_context
)
107 if (psi_check_env("PSI_DUMP")) {
108 struct psi_dump dump
= {{.hn
= stdout
}, (psi_dump_cb
) fprintf
};
110 psi_context_dump(&dump
, PSI_G(context
));
113 psi_context_free(&PSI_G(context
));
115 if (PSI_G(ops
)->free
) {
122 struct psi_context
*psi_context_init(struct psi_context
*C
, struct psi_context_ops
*ops
, psi_error_cb error
, unsigned flags
)
125 C
= pemalloc(sizeof(*C
), 1);
127 memset(C
, 0, sizeof(*C
));
129 psi_data_ctor(PSI_DATA(C
), error
, flags
);
132 if (ops
->init
&& !ops
->init(C
)) {
139 static bool psi_context_add(struct psi_context
*C
, struct psi_parser
*P
)
143 struct psi_validate_scope scope
= {0};
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
));
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
);
156 struct psi_context_build_worker
{
158 struct psi_parser parser
;
159 struct psi_parser_input
*input
;
160 char psi_file
[MAXPATHLEN
];
163 static struct psi_context_build_worker
*psi_context_build_worker_init(
164 struct psi_context
*C
, const char *dir
, const char *file
)
166 struct psi_context_build_worker
*w
= pecalloc(1, sizeof(*w
), 1);
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",
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
));
183 #if PSI_THREADED_PARSER
184 static void *psi_context_build_worker_thread(void *thread_ptr
)
186 struct psi_context_build_worker
*thread
= thread_ptr
;
187 psi_parser_parse(&thread
->parser
, thread
->input
);
191 static bool psi_context_build_worker_thread_start(
192 struct psi_context_build_worker
*w
)
198 rc
= pthread_create(&w
->tid
, NULL
, psi_context_build_worker_thread
, w
);
209 w
->parser
.error(PSI_DATA(&w
->parser
), NULL
, PSI_WARNING
,
210 "Failed to start parser thread: %s", strerror(rc
));
217 static bool psi_context_build_worker_exec(struct psi_context_build_worker
*w
)
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
));
224 #if PSI_THREADED_PARSER
225 return psi_context_build_worker_thread_start(w
);
227 return psi_parser_parse(&w
->parser
, w
->input
);
231 static bool psi_context_build_worker_done(struct psi_context_build_worker
*w
)
233 #if PSI_THREADED_PARSER
240 # if HAVE_PTHREAD_TRYJOIN_NP
241 if (0 == pthread_tryjoin_np(w
->tid
, &rval
)) {
246 if (0 == pthread_join(w
->tid
, &rval
)) {
257 static void psi_context_build_worker_dtor(struct psi_context_build_worker
*w
)
259 #if PSI_THREADED_PARSER
262 int rc
= pthread_join(w
->tid
, &rval
);
265 w
->parser
.error(PSI_DATA(&w
->parser
), NULL
, PSI_WARNING
,
266 "Failed to finish parser thread: %s", strerror(errno
));
270 psi_parser_input_free(&w
->input
);
271 psi_parser_dtor(&w
->parser
);
274 static void psi_context_build_worker_free(struct psi_context_build_worker
**w
)
277 psi_context_build_worker_dtor(*w
);
283 static int psi_select_dirent(const struct dirent
*entry
)
286 # define FNM_CASEFOLD 0
288 return 0 == fnmatch("*.psi", entry
->d_name
, FNM_CASEFOLD
);
291 void psi_context_build(struct psi_context
*C
, const char *paths
)
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
);
299 struct dirent
**entries
= NULL
;
302 if ((sep
= strchr(ptr
, ':'))) {
306 n
= php_scandir(ptr
, &entries
, psi_select_dirent
, alphasort
);
309 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
,
310 "Failed to scan PSI directory '%s':%s", strerror(errno
));
312 for (i
= 0; i
< n
; ++i
) {
313 worker
= psi_context_build_worker_init(C
, ptr
, entries
[i
]->d_name
);
315 workers
= psi_plist_add(workers
, &worker
);
326 if (psi_plist_count(workers
)) {
327 struct psi_plist
*running
= psi_plist_init(
328 (psi_plist_dtor
) psi_context_build_worker_free
);
330 #ifdef _SC_NPROCESSORS_ONLN
331 long pool
= sysconf(_SC_NPROCESSORS_ONLN
);
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
);
347 while (psi_plist_get(running
, i
++, &worker
)) {
348 if (psi_context_build_worker_done(worker
)) {
349 psi_context_add(C
, &worker
->parser
);
351 psi_plist_del(running
, --i
, NULL
);
352 psi_context_build_worker_free(&worker
);
354 if (psi_plist_pop(workers
, &worker
)) {
355 psi_plist_add(running
, &worker
);
364 psi_context_compile(C
);
368 static inline bool prefix_match(zend_string
*a
, zend_string
*b
)
372 for (i
= 0; i
< a
->len
&& i
< b
->len
; ++i
) {
373 if (tolower(a
->val
[i
]) != tolower(b
->val
[i
])) {
376 if (i
&& a
->val
[i
] == '_') {
384 static inline void psi_context_consts_init(struct psi_context
*C
)
388 ZEND_CONSTANT_SET_FLAGS(&zc
, CONST_CS
|CONST_PERSISTENT
, EG(current_module
)->module_number
);
394 while (psi_plist_get(C
->consts
, i
++, &c
)) {
396 if (zend_get_constant(c
->name
)) {
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
);
403 zend_register_constant(&zc
);
408 struct psi_decl_enum
*e
;
410 while (psi_plist_get(C
->enums
, i
++, &e
)) {
412 struct psi_decl_enum_item
*item
;
414 while (psi_plist_get(e
->items
, j
++, &item
)) {
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
);
422 name
= strpprintf(0, "psi\\%s\\%s", e
->name
->val
, item
->name
->val
);
425 if (zend_get_constant(name
)) {
426 zend_string_release(name
);
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
);
439 static inline void psi_context_extvars_init(struct psi_context
*C
)
443 struct psi_decl_extvar
*evar
;
445 while (psi_plist_get(C
->vars
, v
++, &evar
)) {
446 C
->ops
->extvar_init(C
, evar
);
451 static inline void psi_context_callback_init(struct psi_context
*C
,
452 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
454 struct psi_let_func
*fn
= let_exp
->data
.func
;
456 switch (let_exp
->kind
) {
457 case PSI_LET_CALLBACK
:
458 C
->ops
->cb_init(C
, let_exp
, impl
);
460 fn
= let_exp
->data
.callback
->func
;
465 struct psi_let_exp
*inner_let
;
467 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
468 psi_context_callback_init(C
, inner_let
, impl
);
477 static inline void psi_context_callback_dtor(struct psi_context
*C
,
478 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
480 struct psi_let_func
*fn
= let_exp
->data
.func
;
482 switch (let_exp
->kind
) {
483 case PSI_LET_CALLBACK
:
484 C
->ops
->cb_dtor(C
, let_exp
, impl
);
486 fn
= let_exp
->data
.callback
->func
;
491 struct psi_let_exp
*cb
;
493 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
494 psi_context_callback_dtor(C
, cb
, impl
);
503 static inline void psi_context_impls_init(struct psi_context
*C
)
506 zend_function_entry
*zfe
= NULL
;
510 struct psi_impl
*impl
;
512 zfe
= pecalloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
), 1);
514 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
515 zend_function_entry
*zf
= &zfe
[nf
];
516 struct psi_let_stmt
*let
;
522 if (!C
->ops
->decl_init(C
, impl
->decl
)) {
525 if (!C
->ops
->impl_init(C
, impl
, &zf
->handler
)) {
528 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
529 psi_context_callback_init(C
, let
->exp
, impl
);
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
);
542 static inline void psi_context_decls_init(struct psi_context
*C
)
546 struct psi_decl
*decl
;
548 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
550 C
->ops
->decl_init(C
, decl
);
556 struct psi_struct_type_data
{
557 struct psi_plist
*els
;
562 static inline void psi_struct_type_pad(struct psi_context
*C
,
563 struct psi_struct_type_data
*data
, size_t padding
)
565 void *ele
= C
->ops
->typeof_decl(C
, PSI_T_INT8
);
568 void *pad
= C
->ops
->copyof_type(C
, ele
);
569 data
->els
= psi_plist_add(data
->els
, &pad
);
573 static inline void psi_struct_type_element(struct psi_context
*C
,
574 struct psi_struct_type_data
*data
, struct psi_decl_arg
*darg
)
578 struct psi_layout type_layout
;
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
;
586 type
= psi_context_decl_arg_full_type(C
, darg
);
587 C
->ops
->layoutof_type(C
, type
, &type_layout
);
589 if (type_layout
.pos
> data
->max_align
) {
590 data
->max_align
= type_layout
.pos
;
593 assert(type_layout
.len
<= darg
->layout
->len
);
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
, ©
);
599 assert(darg
->layout
->len
== type_layout
.len
* (darg
->var
->array_size
?: 1));
600 data
->offset
+= darg
->layout
->len
;
603 static inline void psi_context_decl_struct_type_elements(struct psi_context
*C
,
604 struct psi_decl_struct
*strct
, struct psi_plist
**els
)
607 struct psi_decl_arg
*darg
, *prev
= NULL
;
608 struct psi_struct_type_data data
= {0};
611 while (psi_plist_get(strct
->args
, i
++, &darg
)) {
612 if (prev
&& prev
->layout
->pos
== darg
->layout
->pos
) {
613 /* skip bit fields */
616 psi_struct_type_element(C
, &data
, darg
);
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
);
626 static inline void *psi_context_decl_arg_type(struct psi_context
*C
,
627 struct psi_decl_arg
*darg
)
629 struct psi_decl_type
*real
= psi_decl_type_get_real(darg
->type
);
631 if (real
!= darg
->type
&& darg
->type
->real
.def
->var
->pointer_level
) {
632 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
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
);
641 if (real
->type
== PSI_T_STRUCT
) {
642 C
->ops
->composite_init(C
, darg
);
643 return darg
->engine
.type
;
646 return C
->ops
->typeof_decl(C
, real
->type
);
649 void *psi_context_decl_arg_call_type(struct psi_context
*C
,
650 struct psi_decl_arg
*darg
)
652 if (darg
->var
->pointer_level
) {
653 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
656 return psi_context_decl_arg_type(C
, darg
);
659 void *psi_context_decl_arg_full_type(struct psi_context
*C
,
660 struct psi_decl_arg
*darg
)
662 if (darg
->var
->array_size
) {
663 C
->ops
->composite_init(C
, darg
);
664 return darg
->engine
.type
;
666 if (darg
->var
->pointer_level
) {
667 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
670 return psi_context_decl_arg_type(C
, darg
);
673 void **psi_context_composite_type_elements(struct psi_context
*C
,
674 struct psi_decl_arg
*darg
, struct psi_plist
**eles
)
676 struct psi_decl_type
*dtype
;
677 struct psi_decl_arg
*tmp
;
680 dtype
= psi_decl_type_get_real(darg
->type
);
682 switch (dtype
->type
) {
684 psi_context_decl_struct_type_elements(C
, dtype
->real
.strct
, eles
);
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
, ©
);
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
, ©
);
701 return psi_plist_eles(*eles
);
705 void psi_context_decl_func_array_elements(struct psi_context *C,
706 struct psi_decl *fn, struct psi_plist **els)
711 if (fn->func->var->pointer_level > 1) {
712 type = C->ops->typeof_decl(C, PSI_T_POINTER);
714 type = psi_context_decl_type(C, fn->func->type);
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, ©);
723 void *psi_context_decl_func_type(struct psi_context *C, struct psi_decl *fn)
725 struct psi_decl_arg *darg = fn->func;
727 if (darg->engine.type) {
728 return darg->engine.type;
731 if (darg->var->pointer_level) {
732 if (!darg->var->array_size) {
733 return C->ops->typeof_decl(C, PSI_T_POINTER);
735 C->ops->composite_init(C, darg);
736 return darg->engine.type;
740 return psi_context_decl_type(C, darg->type);
744 void psi_context_compile(struct psi_context
*C
)
746 psi_context_consts_init(C
);
747 psi_context_extvars_init(C
);
748 psi_context_impls_init(C
);
749 psi_context_decls_init(C
);
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!");
756 EG(current_module
) = NULL
;
759 ZEND_RESULT_CODE
psi_context_call(struct psi_context
*C
, zend_execute_data
*execute_data
, zval
*return_value
, struct psi_impl
*impl
)
761 struct psi_call_frame
*frame
;
763 frame
= psi_call_frame_init(C
, impl
->decl
, impl
);
765 if (SUCCESS
!= psi_call_frame_parse_args(frame
, execute_data
)) {
766 psi_call_frame_free(frame
);
771 psi_call_frame_enter(frame
);
773 if (SUCCESS
!= psi_call_frame_do_let(frame
)) {
774 psi_call_frame_do_return(frame
, return_value
);
775 psi_call_frame_free(frame
);
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
);
787 if (psi_call_frame_num_var_args(frame
)) {
788 C
->ops
->call_va(frame
);
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
);
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
);
809 void psi_context_dtor(struct psi_context
*C
)
812 zend_function_entry
*zfe
;
816 struct psi_decl
*decl
;
818 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
820 struct psi_decl_arg
*darg
;
822 while (psi_plist_get(decl
->args
, j
++, &darg
)) {
823 C
->ops
->composite_dtor(C
, darg
);
825 C
->ops
->composite_dtor(C
, decl
->func
);
826 C
->ops
->decl_dtor(C
, decl
);
832 struct psi_decl_extvar
*evar
;
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
);
842 struct psi_impl
*impl
;
844 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
845 struct psi_let_stmt
*let
;
848 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
849 psi_context_callback_dtor(C
, let
->exp
, impl
);
852 C
->ops
->impl_dtor(C
, impl
);
860 psi_data_dtor(PSI_DATA(C
));
863 for (i
= 0; i
< C
->count
; ++i
) {
864 psi_data_dtor(&C
->data
[i
]);
870 for (zfe
= C
->closures
; zfe
->fname
; ++zfe
) {
871 pefree((void *) zfe
->arg_info
, 1);
873 pefree(C
->closures
, 1);
877 void psi_context_free(struct psi_context
**C
)
880 psi_context_dtor(*C
);
886 void psi_context_dump(struct psi_dump
*dump
, struct psi_context
*C
)
888 PSI_DUMP(dump
, "// psi.engine=%s\n// %lu files\n",
889 C
->ops
->name
, C
->count
);
891 psi_data_dump(dump
, PSI_DATA(C
));
894 if (C
->flags
& PSI_DEBUG
) {
897 for (i
= 0; i
< C
->count
; ++i
) {
898 psi_data_dump(dump
, &C
->data
[i
]);