22fd915c8a73c0d81d0b49a13fdc99afee873935
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
&& SUCCESS
!= 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 psi_context_dump(PSI_G(context
), STDOUT_FILENO
);
104 psi_context_free(&PSI_G(context
));
106 if (PSI_G(ops
)->free
) {
113 struct psi_context
*psi_context_init(struct psi_context
*C
, struct psi_context_ops
*ops
, psi_error_cb error
, unsigned flags
)
116 C
= malloc(sizeof(*C
));
118 memset(C
, 0, sizeof(*C
));
120 psi_data_ctor(PSI_DATA(C
), error
, flags
);
127 assert(ops
->call
!= NULL
);
128 assert(ops
->compile
!= NULL
);
133 static int psi_select_dirent(const struct dirent
*entry
)
136 # define FNM_CASEFOLD 0
138 return 0 == fnmatch("*.psi", entry
->d_name
, FNM_CASEFOLD
);
141 static bool psi_context_add(struct psi_context
*C
, struct psi_parser
*P
)
145 struct psi_validate_scope scope
= {0};
147 C
->data
= realloc(C
->data
, (C
->count
+ 1) * sizeof(*C
->data
));
148 D
= psi_data_exchange(&C
->data
[C
->count
++], PSI_DATA(P
));
150 psi_validate_scope_ctor(&scope
);
151 scope
.defs
= &P
->preproc
->defs
;
152 valid
= psi_validate(&scope
, PSI_DATA(C
), D
);
153 psi_validate_scope_dtor(&scope
);
158 void psi_context_build(struct psi_context
*C
, const char *paths
)
161 char *sep
= NULL
, *cpy
= strdup(paths
), *ptr
= cpy
;
162 struct dirent
**entries
;
165 sep
= strchr(ptr
, ':');
172 n
= php_scandir(ptr
, &entries
, psi_select_dirent
, alphasort
);
175 for (i
= 0; i
< n
; ++i
) {
176 char psi
[MAXPATHLEN
];
178 struct psi_parser_input
*I
;
180 if (MAXPATHLEN
<= slprintf(psi
, MAXPATHLEN
, "%s/%s", ptr
, entries
[i
]->d_name
)) {
181 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Path to PSI file too long: %s/%s",
182 ptr
, entries
[i
]->d_name
);
184 if (!psi_parser_init(&P
, C
->error
, C
->flags
)) {
185 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to init PSI parser (%s): %s",
186 psi
, strerror(errno
));
189 if (!(I
= psi_parser_open_file(&P
, psi
, true))) {
190 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to open PSI file (%s): %s",
191 psi
, strerror(errno
));
194 psi_parser_parse(&P
, I
);
195 psi_context_add(C
, &P
);
197 psi_parser_input_free(&I
);
202 for (i
= 0; i
< n
; ++i
) {
212 if (psi_context_compile(C
) && SUCCESS
!= zend_register_functions(NULL
, C
->closures
, NULL
, MODULE_PERSISTENT
)) {
213 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to register functions!");
219 zend_function_entry
*psi_context_compile(struct psi_context
*C
)
223 ZEND_CONSTANT_SET_FLAGS(&zc
, CONST_CS
|CONST_PERSISTENT
, EG(current_module
)->module_number
);
229 while (psi_plist_get(C
->consts
, i
++, &c
)) {
231 if (zend_get_constant(c
->name
)) {
235 zc
.name
= zend_string_copy(c
->name
);
237 switch (c
->type
->type
) {
239 ZVAL_BOOL(&zc
.value
, c
->val
->ival
.zend
.bval
);
242 ZVAL_LONG(&zc
.value
, c
->val
->ival
.zend
.lval
);
246 ZVAL_DOUBLE(&zc
.value
, c
->val
->ival
.dval
);
249 case PSI_T_QUOTED_STRING
:
250 ZVAL_NEW_STR(&zc
.value
, zend_string_copy(c
->val
->ival
.zend
.str
));
251 if (ZSTR_IS_INTERNED(Z_STR(zc
.value
))) {
252 Z_TYPE_FLAGS(zc
.value
) = 0;
260 zend_register_constant(&zc
);
266 struct psi_decl_enum
*e
;
268 while (psi_plist_get(C
->enums
, i
++, &e
)) {
270 struct psi_decl_enum_item
*item
;
272 while (psi_plist_get(e
->items
, j
++, &item
)) {
275 if (psi_decl_type_is_anon(e
->name
, "enum")) {
276 name
= strpprintf(0, "psi\\%s", item
->name
->val
);
278 name
= strpprintf(0, "psi\\%s\\%s", e
->name
->val
, item
->name
->val
);
281 if (zend_get_constant(name
)) {
282 zend_string_release(name
);
286 zc
.name
= zend_string_dup(name
, 1);
287 ZVAL_LONG(&zc
.value
, psi_num_exp_get_long(item
->num
, NULL
, NULL
));
288 zend_register_constant(&zc
);
289 zend_string_release(name
);
294 return C
->closures
= C
->ops
->compile(C
);
298 ZEND_RESULT_CODE
psi_context_call(struct psi_context
*C
, zend_execute_data
*execute_data
, zval
*return_value
, struct psi_impl
*impl
)
300 struct psi_call_frame
*frame
;
302 frame
= psi_call_frame_init(C
, impl
->decl
, impl
);
304 if (SUCCESS
!= psi_call_frame_parse_args(frame
, execute_data
)) {
305 psi_call_frame_free(frame
);
310 psi_call_frame_enter(frame
);
312 if (SUCCESS
!= psi_call_frame_do_let(frame
)) {
313 psi_call_frame_do_return(frame
, return_value
);
314 psi_call_frame_free(frame
);
319 if (SUCCESS
!= psi_call_frame_do_assert(frame
, PSI_ASSERT_PRE
)) {
320 psi_call_frame_do_return(frame
, return_value
);
321 psi_call_frame_free(frame
);
328 if (SUCCESS
!= psi_call_frame_do_assert(frame
, PSI_ASSERT_POST
)) {
329 psi_call_frame_do_return(frame
, return_value
);
330 psi_call_frame_free(frame
);
335 psi_call_frame_do_return(frame
, return_value
);
336 psi_call_frame_do_set(frame
);
337 psi_call_frame_do_free(frame
);
338 psi_call_frame_free(frame
);
344 void psi_context_dtor(struct psi_context
*C
)
347 zend_function_entry
*zfe
;
353 psi_data_dtor(PSI_DATA(C
));
356 for (i
= 0; i
< C
->count
; ++i
) {
357 psi_data_dtor(&C
->data
[i
]);
363 for (zfe
= C
->closures
; zfe
->fname
; ++zfe
) {
364 free((void *) zfe
->arg_info
);
370 void psi_context_free(struct psi_context
**C
)
373 psi_context_dtor(*C
);
379 void psi_context_dump(struct psi_context
*C
, int fd
)
383 dprintf(fd
, "// psi.engine=%s\n// %lu files\n",
384 (char *) C
->ops
->query(C
, PSI_CONTEXT_QUERY_SELF
, NULL
),
387 for (i
= 0; i
< C
->count
; ++i
) {
388 psi_data_dump(fd
, &C
->data
[i
]);