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
49 #include "php_scandir.h"
59 #include "php_psi_posix.h"
61 PHP_MINIT_FUNCTION(psi_context
)
64 struct psi_context_ops
*ops
= NULL
;
67 if (!strcasecmp(PSI_G(engine
), "jit")) {
68 ops
= psi_libjit_ops();
72 ops
= psi_libffi_ops();
76 php_error(E_WARNING
, "No PSI engine found");
81 if (ops
->load
&& !ops
->load()) {
85 if (psi_check_env("PSI_DEBUG")) {
88 if (psi_check_env("PSI_SILENT")) {
92 PSI_G(context
) = psi_context_init(NULL
, PSI_G(ops
), psi_error_wrapper
, flags
);
93 psi_context_build(PSI_G(context
), PSI_G(directory
));
98 PHP_MSHUTDOWN_FUNCTION(psi_context
)
100 if (psi_check_env("PSI_DUMP")) {
101 struct psi_dump dump
= {{.hn
= stdout
}, (psi_dump_cb
) fprintf
};
103 psi_context_dump(&dump
, PSI_G(context
));
106 psi_context_free(&PSI_G(context
));
108 if (PSI_G(ops
)->free
) {
115 struct psi_context
*psi_context_init(struct psi_context
*C
, struct psi_context_ops
*ops
, psi_error_cb error
, unsigned flags
)
118 C
= pemalloc(sizeof(*C
), 1);
120 memset(C
, 0, sizeof(*C
));
122 psi_data_ctor(PSI_DATA(C
), error
, flags
);
125 if (ops
->init
&& !ops
->init(C
)) {
132 static int psi_select_dirent(const struct dirent
*entry
)
135 # define FNM_CASEFOLD 0
137 return 0 == fnmatch("*.psi", entry
->d_name
, FNM_CASEFOLD
);
140 static bool psi_context_add(struct psi_context
*C
, struct psi_parser
*P
)
144 struct psi_validate_scope scope
= {0};
146 C
->data
= safe_perealloc(C
->data
, (C
->count
+ 1), sizeof(*C
->data
), 0, 1);
147 D
= psi_data_exchange(&C
->data
[C
->count
++], PSI_DATA(P
));
149 psi_validate_scope_ctor(&scope
);
150 scope
.cpp
= P
->preproc
;
151 valid
= psi_validate(&scope
, PSI_DATA(C
), D
);
152 psi_validate_scope_dtor(&scope
);
157 void psi_context_build(struct psi_context
*C
, const char *paths
)
160 char *sep
= NULL
, *cpy
= strdup(paths
), *ptr
= cpy
;
161 struct dirent
**entries
;
164 sep
= strchr(ptr
, ':');
171 n
= php_scandir(ptr
, &entries
, psi_select_dirent
, alphasort
);
174 for (i
= 0; i
< n
; ++i
) {
175 char psi
[MAXPATHLEN
];
177 struct psi_parser_input
*I
;
179 if (MAXPATHLEN
<= slprintf(psi
, MAXPATHLEN
, "%s/%s", ptr
, entries
[i
]->d_name
)) {
180 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Path to PSI file too long: %s/%s",
181 ptr
, entries
[i
]->d_name
);
183 if (!psi_parser_init(&P
, C
->error
, C
->flags
)) {
184 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to init PSI parser (%s): %s",
185 psi
, strerror(errno
));
188 if (!(I
= psi_parser_open_file(&P
, psi
, true))) {
189 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to open PSI file (%s): %s",
190 psi
, strerror(errno
));
193 psi_parser_parse(&P
, I
);
194 psi_context_add(C
, &P
);
196 psi_parser_input_free(&I
);
201 for (i
= 0; i
< n
; ++i
) {
212 psi_context_compile(C
);
216 static inline bool prefix_match(zend_string
*a
, zend_string
*b
)
220 for (i
= 0; i
< a
->len
&& i
< b
->len
; ++i
) {
221 if (tolower(a
->val
[i
]) != tolower(b
->val
[i
])) {
224 if (i
&& a
->val
[i
] == '_') {
232 static inline void psi_context_consts_init(struct psi_context
*C
)
236 ZEND_CONSTANT_SET_FLAGS(&zc
, CONST_CS
|CONST_PERSISTENT
, EG(current_module
)->module_number
);
242 while (psi_plist_get(C
->consts
, i
++, &c
)) {
244 if (zend_get_constant(c
->name
)) {
248 zc
.name
= zend_string_copy(c
->name
);
249 psi_impl_def_val_get_zval(c
->val
, c
->type
? c
->type
->type
: PSI_T_MIXED
, &zc
.value
);
251 zend_register_constant(&zc
);
256 struct psi_decl_enum
*e
;
258 while (psi_plist_get(C
->enums
, i
++, &e
)) {
260 struct psi_decl_enum_item
*item
;
262 while (psi_plist_get(e
->items
, j
++, &item
)) {
265 if (psi_decl_type_is_anon(e
->name
, "enum")
266 || prefix_match(e
->name
, item
->name
)) {
267 name
= strpprintf(0, "psi\\%s", item
->name
->val
);
270 name
= strpprintf(0, "psi\\%s\\%s", e
->name
->val
, item
->name
->val
);
273 if (zend_get_constant(name
)) {
274 zend_string_release(name
);
278 zc
.name
= zend_string_dup(name
, 1);
279 ZVAL_LONG(&zc
.value
, psi_num_exp_get_long(item
->num
, NULL
, NULL
));
280 zend_register_constant(&zc
);
281 zend_string_release(name
);
287 static inline void psi_context_extvars_init(struct psi_context
*C
)
291 struct psi_decl_extvar
*evar
;
293 while (psi_plist_get(C
->vars
, v
++, &evar
)) {
294 C
->ops
->extvar_init(C
, evar
);
299 static inline void psi_context_callback_init(struct psi_context
*C
,
300 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
302 struct psi_let_func
*fn
= let_exp
->data
.func
;
304 switch (let_exp
->kind
) {
305 case PSI_LET_CALLBACK
:
306 C
->ops
->cb_init(C
, let_exp
, impl
);
308 fn
= let_exp
->data
.callback
->func
;
313 struct psi_let_exp
*inner_let
;
315 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
316 psi_context_callback_init(C
, inner_let
, impl
);
325 static inline void psi_context_callback_dtor(struct psi_context
*C
,
326 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
328 struct psi_let_func
*fn
= let_exp
->data
.func
;
330 switch (let_exp
->kind
) {
331 case PSI_LET_CALLBACK
:
332 C
->ops
->cb_dtor(C
, let_exp
, impl
);
334 fn
= let_exp
->data
.callback
->func
;
339 struct psi_let_exp
*cb
;
341 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
342 psi_context_callback_dtor(C
, cb
, impl
);
351 static inline void psi_context_impls_init(struct psi_context
*C
)
354 zend_function_entry
*zfe
= NULL
;
358 struct psi_impl
*impl
;
360 zfe
= pecalloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
), 1);
362 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
363 zend_function_entry
*zf
= &zfe
[nf
];
364 struct psi_let_stmt
*let
;
370 if (!C
->ops
->decl_init(C
, impl
->decl
)) {
373 if (!C
->ops
->impl_init(C
, impl
, &zf
->handler
)) {
376 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
377 psi_context_callback_init(C
, let
->exp
, impl
);
380 zf
->fname
= impl
->func
->name
->val
+ (impl
->func
->name
->val
[0] == '\\');
381 zf
->num_args
= psi_plist_count(impl
->func
->args
);
382 zf
->arg_info
= psi_internal_arginfo(impl
);
390 static inline void psi_context_decls_init(struct psi_context
*C
)
394 struct psi_decl
*decl
;
396 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
398 C
->ops
->decl_init(C
, decl
);
404 struct psi_struct_type_data
{
405 struct psi_plist
*els
;
410 static inline void psi_struct_type_pad(struct psi_context
*C
,
411 struct psi_struct_type_data
*data
, size_t padding
)
413 void *ele
= C
->ops
->typeof_decl(C
, PSI_T_INT8
);
416 void *pad
= C
->ops
->copyof_type(C
, ele
);
417 data
->els
= psi_plist_add(data
->els
, &pad
);
421 static inline void psi_struct_type_element(struct psi_context
*C
,
422 struct psi_struct_type_data
*data
, struct psi_decl_arg
*darg
)
426 struct psi_layout type_layout
;
428 if (darg
->layout
->pos
) {
429 assert(data
->offset
<= darg
->layout
->pos
);
430 psi_struct_type_pad(C
, data
, darg
->layout
->pos
- data
->offset
);
431 data
->offset
= darg
->layout
->pos
;
434 type
= psi_context_decl_arg_full_type(C
, darg
);
435 C
->ops
->layoutof_type(C
, type
, &type_layout
);
437 if (type_layout
.pos
> data
->max_align
) {
438 data
->max_align
= type_layout
.pos
;
441 assert(type_layout
.len
<= darg
->layout
->len
);
443 for (i
= 0; i
< (darg
->var
->array_size
?: 1); ++i
) {
444 copy
= C
->ops
->copyof_type(C
, type
);
445 data
->els
= psi_plist_add(data
->els
, ©
);
447 assert(darg
->layout
->len
== type_layout
.len
* (darg
->var
->array_size
?: 1));
448 data
->offset
+= darg
->layout
->len
;
451 static inline void psi_context_decl_struct_type_elements(struct psi_context
*C
,
452 struct psi_decl_struct
*strct
, struct psi_plist
**els
)
455 struct psi_decl_arg
*darg
, *prev
= NULL
;
456 struct psi_struct_type_data data
= {0};
459 while (psi_plist_get(strct
->args
, i
++, &darg
)) {
460 if (prev
&& prev
->layout
->pos
== darg
->layout
->pos
) {
461 /* skip bit fields */
464 psi_struct_type_element(C
, &data
, darg
);
467 data
.offset
= (data
.offset
+ data
.max_align
- 1) & ~(data
.max_align
- 1);
468 assert(data
.offset
<= strct
->size
);
469 psi_struct_type_pad(C
, &data
, strct
->size
- data
.offset
);
474 static inline void *psi_context_decl_arg_type(struct psi_context
*C
,
475 struct psi_decl_arg
*darg
)
477 struct psi_decl_type
*real
= psi_decl_type_get_real(darg
->type
);
479 if (real
!= darg
->type
&& darg
->type
->real
.def
->var
->pointer_level
) {
480 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
483 if (real
->type
== PSI_T_UNION
) {
484 struct psi_decl_arg
*arg
;
485 psi_plist_get(real
->real
.unn
->args
, 0, &arg
);
486 return psi_context_decl_arg_full_type(C
, arg
);
489 if (real
->type
== PSI_T_STRUCT
) {
490 C
->ops
->composite_init(C
, darg
);
491 return darg
->engine
.type
;
494 return C
->ops
->typeof_decl(C
, real
->type
);
497 void *psi_context_decl_arg_call_type(struct psi_context
*C
,
498 struct psi_decl_arg
*darg
)
500 if (darg
->var
->pointer_level
) {
501 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
504 return psi_context_decl_arg_type(C
, darg
);
507 void *psi_context_decl_arg_full_type(struct psi_context
*C
,
508 struct psi_decl_arg
*darg
)
510 if (darg
->var
->array_size
) {
511 C
->ops
->composite_init(C
, darg
);
512 return darg
->engine
.type
;
514 if (darg
->var
->pointer_level
) {
515 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
518 return psi_context_decl_arg_type(C
, darg
);
521 void **psi_context_composite_type_elements(struct psi_context
*C
,
522 struct psi_decl_arg
*darg
, struct psi_plist
**eles
)
524 struct psi_decl_type
*dtype
;
525 struct psi_decl_arg
*tmp
;
528 dtype
= psi_decl_type_get_real(darg
->type
);
530 switch (dtype
->type
) {
532 psi_context_decl_struct_type_elements(C
, dtype
->real
.strct
, eles
);
535 if (psi_plist_bottom(dtype
->real
.unn
->args
, &tmp
)) {
536 type
= psi_context_decl_arg_full_type(C
, tmp
);
537 copy
= C
->ops
->copyof_type(C
, type
);
538 *eles
= psi_plist_add(*eles
, ©
);
542 type
= psi_context_decl_arg_type(C
, darg
);
543 for (size_t i
= 0; i
< darg
->var
->array_size
; ++i
) {
544 copy
= C
->ops
->copyof_type(C
, type
);
545 *eles
= psi_plist_add(*eles
, ©
);
549 return psi_plist_eles(*eles
);
553 void psi_context_decl_func_array_elements(struct psi_context *C,
554 struct psi_decl *fn, struct psi_plist **els)
559 if (fn->func->var->pointer_level > 1) {
560 type = C->ops->typeof_decl(C, PSI_T_POINTER);
562 type = psi_context_decl_type(C, fn->func->type);
565 for (i = 0; i < fn->func->var->array_size; ++i) {
566 void *copy = C->ops->copyof_type(C, type);
567 *els = psi_plist_add(*els, ©);
571 void *psi_context_decl_func_type(struct psi_context *C, struct psi_decl *fn)
573 struct psi_decl_arg *darg = fn->func;
575 if (darg->engine.type) {
576 return darg->engine.type;
579 if (darg->var->pointer_level) {
580 if (!darg->var->array_size) {
581 return C->ops->typeof_decl(C, PSI_T_POINTER);
583 C->ops->composite_init(C, darg);
584 return darg->engine.type;
588 return psi_context_decl_type(C, darg->type);
592 void psi_context_compile(struct psi_context
*C
)
594 psi_context_consts_init(C
);
595 psi_context_extvars_init(C
);
596 psi_context_impls_init(C
);
597 psi_context_decls_init(C
);
599 /* zend_register_functions depends on EG(current_module) pointing into module */
600 EG(current_module
) = zend_hash_str_find_ptr(&module_registry
, "psi", sizeof("psi") - 1);
601 if (SUCCESS
!= zend_register_functions(NULL
, C
->closures
, NULL
, MODULE_PERSISTENT
)) {
602 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to register functions!");
604 EG(current_module
) = NULL
;
607 ZEND_RESULT_CODE
psi_context_call(struct psi_context
*C
, zend_execute_data
*execute_data
, zval
*return_value
, struct psi_impl
*impl
)
609 struct psi_call_frame
*frame
;
611 frame
= psi_call_frame_init(C
, impl
->decl
, impl
);
613 if (SUCCESS
!= psi_call_frame_parse_args(frame
, execute_data
)) {
614 psi_call_frame_free(frame
);
619 psi_call_frame_enter(frame
);
621 if (SUCCESS
!= psi_call_frame_do_let(frame
)) {
622 psi_call_frame_do_return(frame
, return_value
);
623 psi_call_frame_free(frame
);
628 if (SUCCESS
!= psi_call_frame_do_assert(frame
, PSI_ASSERT_PRE
)) {
629 psi_call_frame_do_return(frame
, return_value
);
630 psi_call_frame_free(frame
);
635 if (psi_call_frame_num_var_args(frame
)) {
636 C
->ops
->call_va(frame
);
641 if (SUCCESS
!= psi_call_frame_do_assert(frame
, PSI_ASSERT_POST
)) {
642 psi_call_frame_do_return(frame
, return_value
);
643 psi_call_frame_free(frame
);
648 psi_call_frame_do_return(frame
, return_value
);
649 psi_call_frame_do_set(frame
);
650 psi_call_frame_do_free(frame
);
651 psi_call_frame_free(frame
);
657 void psi_context_dtor(struct psi_context
*C
)
660 zend_function_entry
*zfe
;
664 struct psi_decl
*decl
;
666 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
668 struct psi_decl_arg
*darg
;
670 while (psi_plist_get(decl
->args
, j
++, &darg
)) {
671 C
->ops
->composite_dtor(C
, darg
);
673 C
->ops
->composite_dtor(C
, decl
->func
);
674 C
->ops
->decl_dtor(C
, decl
);
680 struct psi_decl_extvar
*evar
;
682 while (psi_plist_get(C
->vars
, i
++, &evar
)) {
683 C
->ops
->composite_dtor(C
, evar
->getter
->func
);
684 C
->ops
->composite_dtor(C
, evar
->arg
);
685 C
->ops
->extvar_dtor(C
, evar
);
690 struct psi_impl
*impl
;
692 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
693 struct psi_let_stmt
*let
;
696 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
697 psi_context_callback_dtor(C
, let
->exp
, impl
);
700 C
->ops
->impl_dtor(C
, impl
);
708 psi_data_dtor(PSI_DATA(C
));
711 for (i
= 0; i
< C
->count
; ++i
) {
712 psi_data_dtor(&C
->data
[i
]);
718 for (zfe
= C
->closures
; zfe
->fname
; ++zfe
) {
719 pefree((void *) zfe
->arg_info
, 1);
721 pefree(C
->closures
, 1);
725 void psi_context_free(struct psi_context
**C
)
728 psi_context_dtor(*C
);
734 void psi_context_dump(struct psi_dump
*dump
, struct psi_context
*C
)
736 PSI_DUMP(dump
, "// psi.engine=%s\n// %lu files\n",
737 C
->ops
->name
, C
->count
);
739 psi_data_dump(dump
, PSI_DATA(C
));
742 if (C
->flags
& PSI_DEBUG
) {
745 for (i
= 0; i
< C
->count
; ++i
) {
746 psi_data_dump(dump
, &C
->data
[i
]);