set psi as module for register functions
[m6w6/ext-psi] / src / context.c
1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
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.
13
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 *******************************************************************************/
25
26 #include "php_psi_stdinc.h"
27
28 #include "php.h"
29
30 #ifdef HAVE_DIRENT_H
31 # include <dirent.h>
32 # define NAMLEN(dirent) strlen ((dirent)->d_name)
33 #else
34 # define dirent direct
35 # define NAMLEN(dirent) ((dirent)->d_namlen)
36 # ifdef HAVE_SYS_NDIR_H
37 # include <sys/ndir.h>
38 # endif
39 # ifdef HAVE_SYS_DIR_H
40 # include <sys/dir.h>
41 # endif
42 # ifdef HAVE_NDIR_H
43 # include <ndir.h>
44 # endif
45 #endif
46
47 #include <fnmatch.h>
48
49 #include "php_scandir.h"
50 #include "php_psi.h"
51 #include "calc.h"
52 #include "call.h"
53 #include "libjit.h"
54 #include "libffi.h"
55
56 #include "token.h"
57 #include "parser.h"
58
59 #include "php_psi_posix.h"
60
61 PHP_MINIT_FUNCTION(psi_context)
62 {
63 unsigned flags = 0;
64 struct psi_context_ops *ops = NULL;
65
66 #ifdef HAVE_LIBJIT
67 if (!strcasecmp(PSI_G(engine), "jit")) {
68 ops = psi_libjit_ops();
69 } else
70 #endif
71 #ifdef HAVE_LIBFFI
72 ops = psi_libffi_ops();
73 #endif
74
75 if (!ops) {
76 php_error(E_WARNING, "No PSI engine found");
77 return FAILURE;
78 }
79
80 PSI_G(ops) = ops;
81 if (ops->load && SUCCESS != ops->load()) {
82 return FAILURE;
83 }
84
85 if (psi_check_env("PSI_DEBUG")) {
86 flags |= PSI_DEBUG;
87 }
88 if (psi_check_env("PSI_SILENT")) {
89 flags |= PSI_SILENT;
90 }
91
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));
94
95 return SUCCESS;
96 }
97
98 PHP_MSHUTDOWN_FUNCTION(psi_context)
99 {
100 if (psi_check_env("PSI_DUMP")) {
101 struct psi_dump dump = {{.hn = stdout}, (psi_dump_cb) fprintf};
102
103 psi_context_dump(&dump, PSI_G(context));
104 }
105
106 psi_context_free(&PSI_G(context));
107
108 if (PSI_G(ops)->free) {
109 PSI_G(ops)->free();
110 }
111
112 return SUCCESS;
113 }
114
115 struct psi_context *psi_context_init(struct psi_context *C, struct psi_context_ops *ops, psi_error_cb error, unsigned flags)
116 {
117 if (!C) {
118 C = pemalloc(sizeof(*C), 1);
119 }
120 memset(C, 0, sizeof(*C));
121
122 psi_data_ctor(PSI_DATA(C), error, flags);
123 C->ops = ops;
124
125 if (ops->init) {
126 ops->init(C);
127 }
128
129 assert(ops->call != NULL);
130 assert(ops->compile != NULL);
131
132 return C;
133 }
134
135 static int psi_select_dirent(const struct dirent *entry)
136 {
137 #ifndef FNM_CASEFOLD
138 # define FNM_CASEFOLD 0
139 #endif
140 return 0 == fnmatch("*.psi", entry->d_name, FNM_CASEFOLD);
141 }
142
143 static bool psi_context_add(struct psi_context *C, struct psi_parser *P)
144 {
145 bool valid;
146 struct psi_data *D;
147 struct psi_validate_scope scope = {0};
148
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));
151
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);
156
157 return valid;
158 }
159
160 void psi_context_build(struct psi_context *C, const char *paths)
161 {
162 int i, n;
163 char *sep = NULL, *cpy = strdup(paths), *ptr = cpy;
164 struct dirent **entries;
165
166 do {
167 sep = strchr(ptr, ':');
168
169 if (sep) {
170 *sep = 0;
171 }
172
173 entries = NULL;
174 n = php_scandir(ptr, &entries, psi_select_dirent, alphasort);
175
176 if (n > 0) {
177 for (i = 0; i < n; ++i) {
178 char psi[MAXPATHLEN];
179 struct psi_parser P;
180 struct psi_parser_input *I;
181
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);
185 }
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));
189 continue;
190 }
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));
194 continue;
195 }
196 psi_parser_parse(&P, I);
197 psi_context_add(C, &P);
198 psi_parser_dtor(&P);
199 psi_parser_input_free(&I);
200 }
201 }
202
203 if (entries) {
204 for (i = 0; i < n; ++i) {
205 free(entries[i]);
206 }
207 free(entries);
208 }
209
210 ptr = sep + 1;
211 } while (sep);
212
213
214 if (psi_context_compile(C)) {
215 /* zend_register_functions depends on EG(current_module) pointing into module */
216 EG(current_module) = zend_hash_str_find_ptr(&module_registry, "psi", sizeof("psi") - 1);
217 if (SUCCESS != zend_register_functions(NULL, C->closures, NULL, MODULE_PERSISTENT)) {
218 C->error(PSI_DATA(C), NULL, PSI_WARNING, "Failed to register functions!");
219 }
220 EG(current_module) = NULL;
221 }
222
223 free(cpy);
224 }
225
226 #include <ctype.h>
227 static inline bool prefix_match(zend_string *a, zend_string *b)
228 {
229 size_t i;
230
231 for (i = 0; i < a->len && i < b->len; ++i) {
232 if (tolower(a->val[i]) != tolower(b->val[i])) {
233 return false;
234 }
235 if (i && a->val[i] == '_') {
236 break;
237 }
238 }
239
240 return true;
241 }
242
243 zend_function_entry *psi_context_compile(struct psi_context *C)
244 {
245 zend_constant zc;
246
247 ZEND_CONSTANT_SET_FLAGS(&zc, CONST_CS|CONST_PERSISTENT, EG(current_module)->module_number);
248
249 if (C->consts) {
250 size_t i = 0;
251 struct psi_const *c;
252
253 while (psi_plist_get(C->consts, i++, &c)) {
254
255 if (zend_get_constant(c->name)) {
256 continue;
257 }
258
259 zc.name = zend_string_copy(c->name);
260 psi_impl_def_val_get_zval(c->val, c->type ? c->type->type : PSI_T_MIXED, &zc.value);
261
262 zend_register_constant(&zc);
263 }
264 }
265
266 if (C->enums) {
267 size_t i = 0;
268 struct psi_decl_enum *e;
269
270 while (psi_plist_get(C->enums, i++, &e)) {
271 size_t j = 0;
272 struct psi_decl_enum_item *item;
273
274 while (psi_plist_get(e->items, j++, &item)) {
275 zend_string *name;
276
277 if (psi_decl_type_is_anon(e->name, "enum")
278 || prefix_match(e->name, item->name)) {
279 name = strpprintf(0, "psi\\%s", item->name->val);
280 } else {
281
282 name = strpprintf(0, "psi\\%s\\%s", e->name->val, item->name->val);
283 }
284
285 if (zend_get_constant(name)) {
286 zend_string_release(name);
287 continue;
288 }
289
290 zc.name = zend_string_dup(name, 1);
291 ZVAL_LONG(&zc.value, psi_num_exp_get_long(item->num, NULL, NULL));
292 zend_register_constant(&zc);
293 zend_string_release(name);
294 }
295 }
296 }
297
298 return C->closures = C->ops->compile(C);
299 }
300
301
302 ZEND_RESULT_CODE psi_context_call(struct psi_context *C, zend_execute_data *execute_data, zval *return_value, struct psi_impl *impl)
303 {
304 struct psi_call_frame *frame;
305
306 frame = psi_call_frame_init(C, impl->decl, impl);
307
308 if (SUCCESS != psi_call_frame_parse_args(frame, execute_data)) {
309 psi_call_frame_free(frame);
310
311 return FAILURE;
312 }
313
314 psi_call_frame_enter(frame);
315
316 if (SUCCESS != psi_call_frame_do_let(frame)) {
317 psi_call_frame_do_return(frame, return_value);
318 psi_call_frame_free(frame);
319
320 return FAILURE;
321 }
322
323 if (SUCCESS != psi_call_frame_do_assert(frame, PSI_ASSERT_PRE)) {
324 psi_call_frame_do_return(frame, return_value);
325 psi_call_frame_free(frame);
326
327 return FAILURE;
328 }
329
330 C->ops->call(frame);
331
332 if (SUCCESS != psi_call_frame_do_assert(frame, PSI_ASSERT_POST)) {
333 psi_call_frame_do_return(frame, return_value);
334 psi_call_frame_free(frame);
335
336 return FAILURE;
337 }
338
339 psi_call_frame_do_return(frame, return_value);
340 psi_call_frame_do_set(frame);
341 psi_call_frame_do_free(frame);
342 psi_call_frame_free(frame);
343
344 return SUCCESS;
345 }
346
347
348 void psi_context_dtor(struct psi_context *C)
349 {
350 size_t i;
351 zend_function_entry *zfe;
352
353 if (C->ops->dtor) {
354 C->ops->dtor(C);
355 }
356
357 psi_data_dtor(PSI_DATA(C));
358
359 if (C->data) {
360 for (i = 0; i < C->count; ++i) {
361 psi_data_dtor(&C->data[i]);
362 }
363 free(C->data);
364 }
365
366 if (C->closures) {
367 for (zfe = C->closures; zfe->fname; ++zfe) {
368 free((void *) zfe->arg_info);
369 }
370 free(C->closures);
371 }
372 }
373
374 void psi_context_free(struct psi_context **C)
375 {
376 if (*C) {
377 psi_context_dtor(*C);
378 free(*C);
379 *C = NULL;
380 }
381 }
382
383 void psi_context_dump(struct psi_dump *dump, struct psi_context *C)
384 {
385 PSI_DUMP(dump, "// psi.engine=%s\n// %lu files\n",
386 (char *) C->ops->query(C, PSI_CONTEXT_QUERY_SELF, NULL),
387 C->count);
388
389 psi_data_dump(dump, PSI_DATA(C));
390
391 #if 0
392 if (C->flags & PSI_DEBUG) {
393 size_t i;
394
395 for (i = 0; i < C->count; ++i) {
396 psi_data_dump(dump, &C->data[i]);
397 }
398 }
399 #endif
400 }