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 struct psi_context
*psi_context_init(struct psi_context
*C
, struct psi_context_ops
*ops
, psi_error_cb error
, unsigned flags
)
64 C
= malloc(sizeof(*C
));
66 memset(C
, 0, sizeof(*C
));
68 psi_data_ctor(PSI_DATA(C
), error
, flags
);
75 assert(ops
->call
!= NULL
);
76 assert(ops
->compile
!= NULL
);
81 static int psi_select_dirent(const struct dirent
*entry
)
84 # define FNM_CASEFOLD 0
86 return 0 == fnmatch("*.psi", entry
->d_name
, FNM_CASEFOLD
);
89 static bool psi_context_add(struct psi_context
*C
, struct psi_parser
*P
)
93 struct psi_validate_scope scope
= {0};
95 C
->data
= realloc(C
->data
, (C
->count
+ 1) * sizeof(*C
->data
));
96 D
= psi_data_exchange(&C
->data
[C
->count
++], PSI_DATA(P
));
98 psi_validate_scope_ctor(&scope
);
99 scope
.defs
= &P
->preproc
->defs
;
100 valid
= psi_validate(&scope
, PSI_DATA(C
), D
);
101 psi_validate_scope_dtor(&scope
);
106 void psi_context_build(struct psi_context
*C
, const char *paths
)
109 char *sep
= NULL
, *cpy
= strdup(paths
), *ptr
= cpy
;
110 struct dirent
**entries
;
113 sep
= strchr(ptr
, ':');
120 n
= php_scandir(ptr
, &entries
, psi_select_dirent
, alphasort
);
123 for (i
= 0; i
< n
; ++i
) {
124 char psi
[MAXPATHLEN
];
126 struct psi_parser_input
*I
;
128 if (MAXPATHLEN
<= slprintf(psi
, MAXPATHLEN
, "%s/%s", ptr
, entries
[i
]->d_name
)) {
129 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Path to PSI file too long: %s/%s",
130 ptr
, entries
[i
]->d_name
);
132 if (!psi_parser_init(&P
, C
->error
, C
->flags
)) {
133 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to init PSI parser (%s): %s",
134 psi
, strerror(errno
));
137 if (!(I
= psi_parser_open_file(&P
, psi
, true))) {
138 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to open PSI file (%s): %s",
139 psi
, strerror(errno
));
142 psi_parser_parse(&P
, I
);
143 psi_context_add(C
, &P
);
150 for (i
= 0; i
< n
; ++i
) {
160 if (psi_context_compile(C
) && SUCCESS
!= zend_register_functions(NULL
, C
->closures
, NULL
, MODULE_PERSISTENT
)) {
161 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to register functions!");
167 zend_function_entry
*psi_context_compile(struct psi_context
*C
)
171 ZEND_CONSTANT_SET_FLAGS(&zc
, CONST_CS
|CONST_PERSISTENT
, EG(current_module
)->module_number
);
177 while (psi_plist_get(C
->consts
, i
++, &c
)) {
179 if (zend_get_constant_str(c
->name
, strlen(c
->name
))) {
183 zc
.name
= zend_string_init(c
->name
, strlen(c
->name
), 1);
185 switch (c
->type
->type
) {
187 ZVAL_BOOL(&zc
.value
, c
->val
->ival
.zend
.bval
);
190 ZVAL_LONG(&zc
.value
, c
->val
->ival
.zend
.lval
);
194 ZVAL_DOUBLE(&zc
.value
, c
->val
->ival
.dval
);
197 case PSI_T_QUOTED_STRING
:
198 ZVAL_NEW_STR(&zc
.value
, zend_string_copy(c
->val
->ival
.zend
.str
));
205 zend_register_constant(&zc
);
211 struct psi_decl_enum
*e
;
213 while (psi_plist_get(C
->enums
, i
++, &e
)) {
215 struct psi_decl_enum_item
*item
;
217 while (psi_plist_get(e
->items
, j
++, &item
)) {
220 if (psi_decl_type_is_anon(e
->name
, "enum")) {
221 name
= strpprintf(0, "psi\\%s", item
->name
);
223 name
= strpprintf(0, "psi\\%s\\%s", e
->name
, item
->name
);
226 if (zend_get_constant(name
)) {
227 zend_string_release(name
);
231 zc
.name
= zend_string_dup(name
, 1);
232 ZVAL_LONG(&zc
.value
, psi_num_exp_get_long(item
->num
, NULL
, NULL
));
233 zend_register_constant(&zc
);
234 zend_string_release(name
);
239 return C
->closures
= C
->ops
->compile(C
);
243 ZEND_RESULT_CODE
psi_context_call(struct psi_context
*C
, zend_execute_data
*execute_data
, zval
*return_value
, struct psi_impl
*impl
)
245 struct psi_call_frame
*frame
;
247 frame
= psi_call_frame_init(C
, impl
->decl
, impl
);
249 if (SUCCESS
!= psi_call_frame_parse_args(frame
, execute_data
)) {
250 psi_call_frame_free(frame
);
255 psi_call_frame_enter(frame
);
257 if (SUCCESS
!= psi_call_frame_do_let(frame
)) {
258 psi_call_frame_do_return(frame
, return_value
);
259 psi_call_frame_free(frame
);
264 if (SUCCESS
!= psi_call_frame_do_assert(frame
, PSI_ASSERT_PRE
)) {
265 psi_call_frame_do_return(frame
, return_value
);
266 psi_call_frame_free(frame
);
273 if (SUCCESS
!= psi_call_frame_do_assert(frame
, PSI_ASSERT_POST
)) {
274 psi_call_frame_do_return(frame
, return_value
);
275 psi_call_frame_free(frame
);
280 psi_call_frame_do_return(frame
, return_value
);
281 psi_call_frame_do_set(frame
);
282 psi_call_frame_do_free(frame
);
283 psi_call_frame_free(frame
);
289 void psi_context_dtor(struct psi_context
*C
)
292 zend_function_entry
*zfe
;
298 psi_data_dtor(PSI_DATA(C
));
301 for (i
= 0; i
< C
->count
; ++i
) {
302 psi_data_dtor(&C
->data
[i
]);
308 for (zfe
= C
->closures
; zfe
->fname
; ++zfe
) {
309 free((void *) zfe
->arg_info
);
315 void psi_context_free(struct psi_context
**C
)
318 psi_context_dtor(*C
);
324 void psi_context_dump(struct psi_context
*C
, int fd
)
328 dprintf(fd
, "// psi.engine=%s\n// %lu files\n",
329 (char *) C
->ops
->query(C
, PSI_CONTEXT_QUERY_SELF
, NULL
),
332 for (i
= 0; i
< C
->count
; ++i
) {
333 psi_data_dump(fd
, &C
->data
[i
]);