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
{
157 #if PSI_THREADED_PARSER
160 struct psi_parser parser
;
161 struct psi_parser_input
*input
;
162 char psi_file
[PATH_MAX
];
165 static struct psi_context_build_worker
*psi_context_build_worker_init(
166 struct psi_context
*C
, const char *dir
, const char *file
)
168 struct psi_context_build_worker
*w
= pecalloc(1, sizeof(*w
), 1);
170 if (PATH_MAX
<= slprintf(w
->psi_file
, PATH_MAX
, "%s/%s", dir
, file
)) {
171 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Path to PSI file too long: %s/%s",
176 if (!psi_parser_init(&w
->parser
, C
->error
, C
->flags
)) {
177 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to init PSI parser (%s): %s",
178 w
->psi_file
, strerror(errno
));
185 #if PSI_THREADED_PARSER
186 static void *psi_context_build_worker_thread(void *thread_ptr
)
188 struct psi_context_build_worker
*thread
= thread_ptr
;
189 psi_parser_parse(&thread
->parser
, thread
->input
);
193 static bool psi_context_build_worker_thread_start(
194 struct psi_context_build_worker
*w
)
200 rc
= pthread_create(&w
->tid
, NULL
, psi_context_build_worker_thread
, w
);
211 w
->parser
.error(PSI_DATA(&w
->parser
), NULL
, PSI_WARNING
,
212 "Failed to start parser thread: %s", strerror(rc
));
219 static bool psi_context_build_worker_exec(struct psi_context_build_worker
*w
)
221 if (!(w
->input
= psi_parser_open_file(&w
->parser
, w
->psi_file
, true))) {
222 w
->parser
.error(PSI_DATA(&w
->parser
), NULL
, PSI_WARNING
,
223 "Failed to open PSI file (%s): %s", w
->psi_file
, strerror(errno
));
226 #if PSI_THREADED_PARSER
227 return psi_context_build_worker_thread_start(w
);
229 return psi_parser_parse(&w
->parser
, w
->input
);
233 static bool psi_context_build_worker_done(struct psi_context_build_worker
*w
)
235 #if PSI_THREADED_PARSER
242 # if HAVE_PTHREAD_TRYJOIN_NP
243 if (0 == pthread_tryjoin_np(w
->tid
, &rval
)) {
248 if (0 == pthread_join(w
->tid
, &rval
)) {
259 static void psi_context_build_worker_dtor(struct psi_context_build_worker
*w
)
261 #if PSI_THREADED_PARSER
264 int rc
= pthread_join(w
->tid
, &rval
);
267 w
->parser
.error(PSI_DATA(&w
->parser
), NULL
, PSI_WARNING
,
268 "Failed to finish parser thread: %s", strerror(errno
));
272 psi_parser_input_free(&w
->input
);
273 psi_parser_dtor(&w
->parser
);
276 static void psi_context_build_worker_free(struct psi_context_build_worker
**w
)
279 psi_context_build_worker_dtor(*w
);
285 static int psi_select_dirent(const struct dirent
*entry
)
288 # define FNM_CASEFOLD 0
290 return 0 == fnmatch("*.psi", entry
->d_name
, FNM_CASEFOLD
);
293 void psi_context_build(struct psi_context
*C
, const char *paths
)
295 char *sep
= NULL
, *cpy
= strdup(paths
), *ptr
= cpy
;
296 struct psi_context_build_worker
*worker
;
297 struct psi_plist
*workers
= psi_plist_init(
298 (psi_plist_dtor
) psi_context_build_worker_free
);
301 struct dirent
**entries
= NULL
;
304 if ((sep
= strchr(ptr
, ':'))) {
308 n
= php_scandir(ptr
, &entries
, psi_select_dirent
, alphasort
);
312 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
,
313 "Failed to scan PSI directory '%s%s%s': %s",
314 *ptr
== '/' ? "" : getcwd(cwd
, PATH_MAX
),
315 *ptr
!= '/' && *ptr
!= '.' ? "/" : "",
316 ptr
, strerror(errno
));
318 for (i
= 0; i
< n
; ++i
) {
319 worker
= psi_context_build_worker_init(C
, ptr
, entries
[i
]->d_name
);
320 PSI_DEBUG_PRINT(C
, "PSI: init worker(%p) for %s/%s\n",
321 worker
, ptr
, entries
[i
]->d_name
);
323 workers
= psi_plist_add(workers
, &worker
);
334 if (psi_plist_count(workers
)) {
335 struct psi_plist
*running
= psi_plist_init(
336 (psi_plist_dtor
) psi_context_build_worker_free
);
338 #ifdef _SC_NPROCESSORS_ONLN
339 long pool
= sysconf(_SC_NPROCESSORS_ONLN
);
344 while (psi_plist_count(workers
) && active
< pool
) {
345 if (psi_plist_pop(workers
, &worker
)) {
346 PSI_DEBUG_PRINT(C
, "PSI: starting worker %p\n", worker
);
347 if (psi_context_build_worker_exec(worker
)) {
348 running
= psi_plist_add(running
, &worker
);
356 while (psi_plist_get(running
, i
++, &worker
)) {
357 if (psi_context_build_worker_done(worker
)) {
358 PSI_DEBUG_PRINT(C
, "PSI: collecting worker %p\n", worker
);
359 psi_context_add(C
, &worker
->parser
);
361 psi_plist_del(running
, --i
, NULL
);
362 psi_context_build_worker_free(&worker
);
364 if (psi_plist_pop(workers
, &worker
)) {
365 PSI_DEBUG_PRINT(C
, "PSI: starting worker %p\n", worker
);
366 if (psi_context_build_worker_exec(worker
)) {
367 running
= psi_plist_add(running
, &worker
);
375 psi_plist_free(running
);
377 psi_plist_free(workers
);
379 psi_context_compile(C
);
383 static inline bool prefix_match(zend_string
*a
, zend_string
*b
)
387 for (i
= 0; i
< a
->len
&& i
< b
->len
; ++i
) {
388 if (tolower(a
->val
[i
]) != tolower(b
->val
[i
])) {
391 if (i
&& a
->val
[i
] == '_') {
399 static inline void psi_context_consts_init(struct psi_context
*C
)
403 ZEND_CONSTANT_SET_FLAGS(&zc
, CONST_CS
|CONST_PERSISTENT
, EG(current_module
)->module_number
);
409 while (psi_plist_get(C
->consts
, i
++, &c
)) {
411 if (zend_get_constant(c
->name
)) {
415 zc
.name
= zend_string_copy(c
->name
);
416 psi_impl_def_val_get_zval(c
->val
, c
->type
? c
->type
->type
: PSI_T_MIXED
, &zc
.value
);
418 zend_register_constant(&zc
);
423 struct psi_decl_enum
*e
;
425 while (psi_plist_get(C
->enums
, i
++, &e
)) {
427 struct psi_decl_enum_item
*item
;
429 while (psi_plist_get(e
->items
, j
++, &item
)) {
432 if (psi_decl_type_is_anon(e
->name
, "enum")
433 || prefix_match(e
->name
, item
->name
)) {
434 name
= strpprintf(0, "psi\\%s", item
->name
->val
);
437 name
= strpprintf(0, "psi\\%s\\%s", e
->name
->val
, item
->name
->val
);
440 if (zend_get_constant(name
)) {
441 zend_string_release(name
);
445 zc
.name
= zend_string_dup(name
, 1);
446 ZVAL_LONG(&zc
.value
, psi_num_exp_get_long(item
->num
, NULL
, NULL
));
447 zend_register_constant(&zc
);
448 zend_string_release(name
);
454 static inline void psi_context_extvars_init(struct psi_context
*C
)
458 struct psi_decl_extvar
*evar
;
460 while (psi_plist_get(C
->vars
, v
++, &evar
)) {
461 C
->ops
->extvar_init(C
, evar
);
466 static inline void psi_context_callback_init(struct psi_context
*C
,
467 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
469 struct psi_let_func
*fn
= let_exp
->data
.func
;
471 switch (let_exp
->kind
) {
472 case PSI_LET_CALLBACK
:
473 C
->ops
->cb_init(C
, let_exp
, impl
);
475 fn
= let_exp
->data
.callback
->func
;
480 struct psi_let_exp
*inner_let
;
482 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
483 psi_context_callback_init(C
, inner_let
, impl
);
492 static inline void psi_context_callback_dtor(struct psi_context
*C
,
493 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
495 struct psi_let_func
*fn
= let_exp
->data
.func
;
497 switch (let_exp
->kind
) {
498 case PSI_LET_CALLBACK
:
499 C
->ops
->cb_dtor(C
, let_exp
, impl
);
501 fn
= let_exp
->data
.callback
->func
;
506 struct psi_let_exp
*cb
;
508 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
509 psi_context_callback_dtor(C
, cb
, impl
);
518 static inline void psi_context_impls_init(struct psi_context
*C
)
521 zend_function_entry
*zfe
= NULL
;
525 struct psi_impl
*impl
;
527 zfe
= pecalloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
), 1);
529 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
530 zend_function_entry
*zf
= &zfe
[nf
];
531 struct psi_let_stmt
*let
;
537 if (!C
->ops
->decl_init(C
, impl
->decl
)) {
540 if (!C
->ops
->impl_init(C
, impl
, &zf
->handler
)) {
543 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
544 psi_context_callback_init(C
, let
->exp
, impl
);
547 zf
->fname
= impl
->func
->name
->val
+ (impl
->func
->name
->val
[0] == '\\');
548 zf
->num_args
= psi_plist_count(impl
->func
->args
);
549 zf
->arg_info
= psi_internal_arginfo(impl
);
557 static inline void psi_context_decls_init(struct psi_context
*C
)
561 struct psi_decl
*decl
;
563 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
565 C
->ops
->decl_init(C
, decl
);
571 struct psi_struct_type_data
{
572 struct psi_plist
*els
;
577 static inline void psi_struct_type_pad(struct psi_context
*C
,
578 struct psi_struct_type_data
*data
, size_t padding
)
580 void *ele
= C
->ops
->typeof_decl(C
, PSI_T_INT8
);
583 void *pad
= C
->ops
->copyof_type(C
, ele
);
584 data
->els
= psi_plist_add(data
->els
, &pad
);
588 static inline void psi_struct_type_element(struct psi_context
*C
,
589 struct psi_struct_type_data
*data
, struct psi_decl_arg
*darg
)
593 struct psi_layout type_layout
;
595 if (darg
->layout
->pos
) {
596 assert(data
->offset
<= darg
->layout
->pos
);
597 psi_struct_type_pad(C
, data
, darg
->layout
->pos
- data
->offset
);
598 data
->offset
= darg
->layout
->pos
;
601 type
= psi_context_decl_arg_full_type(C
, darg
);
602 C
->ops
->layoutof_type(C
, type
, &type_layout
);
604 if (type_layout
.pos
> data
->max_align
) {
605 data
->max_align
= type_layout
.pos
;
608 assert(type_layout
.len
<= darg
->layout
->len
);
610 for (i
= 0; i
< (darg
->var
->array_size
?: 1); ++i
) {
611 copy
= C
->ops
->copyof_type(C
, type
);
612 data
->els
= psi_plist_add(data
->els
, ©
);
614 assert(darg
->layout
->len
== type_layout
.len
* (darg
->var
->array_size
?: 1));
615 data
->offset
+= darg
->layout
->len
;
618 static inline void psi_context_decl_struct_type_elements(struct psi_context
*C
,
619 struct psi_decl_struct
*strct
, struct psi_plist
**els
)
622 struct psi_decl_arg
*darg
, *prev
= NULL
;
623 struct psi_struct_type_data data
= {0};
626 while (psi_plist_get(strct
->args
, i
++, &darg
)) {
627 if (prev
&& prev
->layout
->pos
== darg
->layout
->pos
) {
628 /* skip bit fields */
631 psi_struct_type_element(C
, &data
, darg
);
634 data
.offset
= (data
.offset
+ data
.max_align
- 1) & ~(data
.max_align
- 1);
635 assert(data
.offset
<= strct
->size
);
636 psi_struct_type_pad(C
, &data
, strct
->size
- data
.offset
);
641 static inline void *psi_context_decl_arg_type(struct psi_context
*C
,
642 struct psi_decl_arg
*darg
)
644 struct psi_decl_type
*real
= psi_decl_type_get_real(darg
->type
);
646 if (real
!= darg
->type
&& darg
->type
->real
.def
->var
->pointer_level
) {
647 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
650 if (real
->type
== PSI_T_UNION
) {
651 struct psi_decl_arg
*arg
;
652 psi_plist_get(real
->real
.unn
->args
, 0, &arg
);
653 return psi_context_decl_arg_full_type(C
, arg
);
656 if (real
->type
== PSI_T_STRUCT
) {
657 C
->ops
->composite_init(C
, darg
);
658 return darg
->engine
.type
;
661 return C
->ops
->typeof_decl(C
, real
->type
);
664 void *psi_context_decl_arg_call_type(struct psi_context
*C
,
665 struct psi_decl_arg
*darg
)
667 if (darg
->var
->pointer_level
) {
668 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
671 return psi_context_decl_arg_type(C
, darg
);
674 void *psi_context_decl_arg_full_type(struct psi_context
*C
,
675 struct psi_decl_arg
*darg
)
677 if (darg
->var
->array_size
) {
678 C
->ops
->composite_init(C
, darg
);
679 return darg
->engine
.type
;
681 if (darg
->var
->pointer_level
) {
682 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
685 return psi_context_decl_arg_type(C
, darg
);
688 void **psi_context_composite_type_elements(struct psi_context
*C
,
689 struct psi_decl_arg
*darg
, struct psi_plist
**eles
)
691 struct psi_decl_type
*dtype
;
692 struct psi_decl_arg
*tmp
;
696 dtype
= psi_decl_type_get_real(darg
->type
);
698 switch (dtype
->type
) {
700 psi_context_decl_struct_type_elements(C
, dtype
->real
.strct
, eles
);
703 if (psi_plist_bottom(dtype
->real
.unn
->args
, &tmp
)) {
704 type
= psi_context_decl_arg_full_type(C
, tmp
);
705 copy
= C
->ops
->copyof_type(C
, type
);
706 *eles
= psi_plist_add(*eles
, ©
);
710 type
= psi_context_decl_arg_type(C
, darg
);
711 for (i
= 0; i
< darg
->var
->array_size
; ++i
) {
712 copy
= C
->ops
->copyof_type(C
, type
);
713 *eles
= psi_plist_add(*eles
, ©
);
717 return psi_plist_eles(*eles
);
720 void psi_context_compile(struct psi_context
*C
)
722 psi_context_consts_init(C
);
723 psi_context_extvars_init(C
);
724 psi_context_impls_init(C
);
725 psi_context_decls_init(C
);
727 /* zend_register_functions depends on EG(current_module) pointing into module */
728 EG(current_module
) = zend_hash_str_find_ptr(&module_registry
, "psi", sizeof("psi") - 1);
729 if (SUCCESS
!= zend_register_functions(NULL
, C
->closures
, NULL
, MODULE_PERSISTENT
)) {
730 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to register functions!");
732 EG(current_module
) = NULL
;
735 bool psi_context_call(struct psi_context
*C
, zend_execute_data
*execute_data
, zval
*return_value
, struct psi_impl
*impl
)
737 struct psi_call_frame
*frame
;
739 frame
= psi_call_frame_init(C
, impl
->decl
, impl
);
741 if (!psi_call_frame_parse_args(frame
, execute_data
)) {
742 psi_call_frame_free(frame
);
747 psi_call_frame_enter(frame
);
749 if (!psi_call_frame_do_let(frame
)) {
750 psi_call_frame_do_return(frame
, return_value
);
751 psi_call_frame_free(frame
);
756 if (!psi_call_frame_do_assert(frame
, PSI_ASSERT_PRE
)) {
757 psi_call_frame_do_return(frame
, return_value
);
758 psi_call_frame_free(frame
);
763 if (psi_call_frame_num_var_args(frame
)) {
764 C
->ops
->call_va(frame
);
769 if (!psi_call_frame_do_assert(frame
, PSI_ASSERT_POST
)) {
770 psi_call_frame_do_return(frame
, return_value
);
771 psi_call_frame_free(frame
);
776 psi_call_frame_do_return(frame
, return_value
);
777 psi_call_frame_do_set(frame
);
778 psi_call_frame_do_free(frame
);
779 psi_call_frame_free(frame
);
785 void psi_context_dtor(struct psi_context
*C
)
788 zend_function_entry
*zfe
;
792 struct psi_decl
*decl
;
794 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
796 struct psi_decl_arg
*darg
;
798 while (psi_plist_get(decl
->args
, j
++, &darg
)) {
799 C
->ops
->composite_dtor(C
, darg
);
801 C
->ops
->composite_dtor(C
, decl
->func
);
802 C
->ops
->decl_dtor(C
, decl
);
808 struct psi_decl_extvar
*evar
;
810 while (psi_plist_get(C
->vars
, i
++, &evar
)) {
811 C
->ops
->composite_dtor(C
, evar
->getter
->func
);
812 C
->ops
->composite_dtor(C
, evar
->arg
);
813 C
->ops
->extvar_dtor(C
, evar
);
818 struct psi_impl
*impl
;
820 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
821 struct psi_let_stmt
*let
;
824 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
825 psi_context_callback_dtor(C
, let
->exp
, impl
);
828 C
->ops
->impl_dtor(C
, impl
);
836 psi_data_dtor(PSI_DATA(C
));
839 for (i
= 0; i
< C
->count
; ++i
) {
840 psi_data_dtor(&C
->data
[i
]);
846 for (zfe
= C
->closures
; zfe
->fname
; ++zfe
) {
847 pefree((void *) zfe
->arg_info
, 1);
849 pefree(C
->closures
, 1);
853 void psi_context_free(struct psi_context
**C
)
856 psi_context_dtor(*C
);
862 void psi_context_dump(struct psi_dump
*dump
, struct psi_context
*C
)
864 PSI_DUMP(dump
, "// psi.engine=%s\n// %lu files\n",
865 C
->ops
->name
, C
->count
);
867 psi_data_dump(dump
, PSI_DATA(C
));
870 if (C
->flags
& PSI_DEBUG
) {
873 for (i
= 0; i
< C
->count
; ++i
) {
874 psi_data_dump(dump
, &C
->data
[i
]);