be8bae9cbdf7b8385b5ef3abe7f157ea1e4e9089
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 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
);
129 assert(ops
->call
!= NULL
);
130 assert(ops
->compile
!= NULL
);
135 static int psi_select_dirent(const struct dirent
*entry
)
138 # define FNM_CASEFOLD 0
140 return 0 == fnmatch("*.psi", entry
->d_name
, FNM_CASEFOLD
);
143 static bool psi_context_add(struct psi_context
*C
, struct psi_parser
*P
)
147 struct psi_validate_scope scope
= {0};
149 C
->data
= safe_perealloc(C
->data
, (C
->count
+ 1), sizeof(*C
->data
), 0, 1);
150 D
= psi_data_exchange(&C
->data
[C
->count
++], PSI_DATA(P
));
152 psi_validate_scope_ctor(&scope
);
153 scope
.cpp
= P
->preproc
;
154 valid
= psi_validate(&scope
, PSI_DATA(C
), D
);
155 psi_validate_scope_dtor(&scope
);
160 void psi_context_build(struct psi_context
*C
, const char *paths
)
163 char *sep
= NULL
, *cpy
= strdup(paths
), *ptr
= cpy
;
164 struct dirent
**entries
;
167 sep
= strchr(ptr
, ':');
174 n
= php_scandir(ptr
, &entries
, psi_select_dirent
, alphasort
);
177 for (i
= 0; i
< n
; ++i
) {
178 char psi
[MAXPATHLEN
];
180 struct psi_parser_input
*I
;
182 if (MAXPATHLEN
<= slprintf(psi
, MAXPATHLEN
, "%s/%s", ptr
, entries
[i
]->d_name
)) {
183 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Path to PSI file too long: %s/%s",
184 ptr
, entries
[i
]->d_name
);
186 if (!psi_parser_init(&P
, C
->error
, C
->flags
)) {
187 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to init PSI parser (%s): %s",
188 psi
, strerror(errno
));
191 if (!(I
= psi_parser_open_file(&P
, psi
, true))) {
192 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to open PSI file (%s): %s",
193 psi
, strerror(errno
));
196 psi_parser_parse(&P
, I
);
197 psi_context_add(C
, &P
);
199 psi_parser_input_free(&I
);
204 for (i
= 0; i
< n
; ++i
) {
214 if (psi_context_compile(C
) && SUCCESS
!= zend_register_functions(NULL
, C
->closures
, NULL
, MODULE_PERSISTENT
)) {
215 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to register functions!");
221 zend_function_entry
*psi_context_compile(struct psi_context
*C
)
225 ZEND_CONSTANT_SET_FLAGS(&zc
, CONST_CS
|CONST_PERSISTENT
, EG(current_module
)->module_number
);
231 while (psi_plist_get(C
->consts
, i
++, &c
)) {
233 if (zend_get_constant(c
->name
)) {
237 zc
.name
= zend_string_copy(c
->name
);
239 switch (c
->type
? c
->type
->type
: c
->val
->type
) {
241 ZVAL_BOOL(&zc
.value
, c
->val
->ival
.zend
.bval
);
244 ZVAL_LONG(&zc
.value
, c
->val
->ival
.zend
.lval
);
248 ZVAL_DOUBLE(&zc
.value
, c
->val
->ival
.dval
);
251 case PSI_T_QUOTED_STRING
:
252 ZVAL_NEW_STR(&zc
.value
, zend_string_copy(c
->val
->ival
.zend
.str
));
253 if (ZSTR_IS_INTERNED(Z_STR(zc
.value
))) {
254 Z_TYPE_FLAGS(zc
.value
) = 0;
262 zend_register_constant(&zc
);
268 struct psi_decl_enum
*e
;
270 while (psi_plist_get(C
->enums
, i
++, &e
)) {
272 struct psi_decl_enum_item
*item
;
274 while (psi_plist_get(e
->items
, j
++, &item
)) {
277 if (psi_decl_type_is_anon(e
->name
, "enum")) {
278 name
= strpprintf(0, "psi\\%s", item
->name
->val
);
280 name
= strpprintf(0, "psi\\%s\\%s", e
->name
->val
, item
->name
->val
);
283 if (zend_get_constant(name
)) {
284 zend_string_release(name
);
288 zc
.name
= zend_string_dup(name
, 1);
289 ZVAL_LONG(&zc
.value
, psi_num_exp_get_long(item
->num
, NULL
, NULL
));
290 zend_register_constant(&zc
);
291 zend_string_release(name
);
296 return C
->closures
= C
->ops
->compile(C
);
300 ZEND_RESULT_CODE
psi_context_call(struct psi_context
*C
, zend_execute_data
*execute_data
, zval
*return_value
, struct psi_impl
*impl
)
302 struct psi_call_frame
*frame
;
304 frame
= psi_call_frame_init(C
, impl
->decl
, impl
);
306 if (SUCCESS
!= psi_call_frame_parse_args(frame
, execute_data
)) {
307 psi_call_frame_free(frame
);
312 psi_call_frame_enter(frame
);
314 if (SUCCESS
!= psi_call_frame_do_let(frame
)) {
315 psi_call_frame_do_return(frame
, return_value
);
316 psi_call_frame_free(frame
);
321 if (SUCCESS
!= psi_call_frame_do_assert(frame
, PSI_ASSERT_PRE
)) {
322 psi_call_frame_do_return(frame
, return_value
);
323 psi_call_frame_free(frame
);
330 if (SUCCESS
!= psi_call_frame_do_assert(frame
, PSI_ASSERT_POST
)) {
331 psi_call_frame_do_return(frame
, return_value
);
332 psi_call_frame_free(frame
);
337 psi_call_frame_do_return(frame
, return_value
);
338 psi_call_frame_do_set(frame
);
339 psi_call_frame_do_free(frame
);
340 psi_call_frame_free(frame
);
346 void psi_context_dtor(struct psi_context
*C
)
349 zend_function_entry
*zfe
;
355 psi_data_dtor(PSI_DATA(C
));
358 for (i
= 0; i
< C
->count
; ++i
) {
359 psi_data_dtor(&C
->data
[i
]);
365 for (zfe
= C
->closures
; zfe
->fname
; ++zfe
) {
366 free((void *) zfe
->arg_info
);
372 void psi_context_free(struct psi_context
**C
)
375 psi_context_dtor(*C
);
381 void psi_context_dump(struct psi_dump
*dump
, struct psi_context
*C
)
385 PSI_DUMP(dump
, "// psi.engine=%s\n// %lu files\n",
386 (char *) C
->ops
->query(C
, PSI_CONTEXT_QUERY_SELF
, NULL
),
389 psi_data_dump(dump
, PSI_DATA(C
));
392 if (C
->flags
& PSI_DEBUG
) {
393 for (i
= 0; i
< C
->count
; ++i
) {
394 psi_data_dump(fd
, &C
->data
[i
]);