interned strings++
[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 psi_context_dump(PSI_G(context), STDOUT_FILENO);
102 }
103
104 psi_context_free(&PSI_G(context));
105
106 if (PSI_G(ops)->free) {
107 PSI_G(ops)->free();
108 }
109
110 return SUCCESS;
111 }
112
113 struct psi_context *psi_context_init(struct psi_context *C, struct psi_context_ops *ops, psi_error_cb error, unsigned flags)
114 {
115 if (!C) {
116 C = pemalloc(sizeof(*C), 1);
117 }
118 memset(C, 0, sizeof(*C));
119
120 psi_data_ctor(PSI_DATA(C), error, flags);
121 C->ops = ops;
122
123 if (ops->init) {
124 ops->init(C);
125 }
126
127 assert(ops->call != NULL);
128 assert(ops->compile != NULL);
129
130 return C;
131 }
132
133 static int psi_select_dirent(const struct dirent *entry)
134 {
135 #ifndef FNM_CASEFOLD
136 # define FNM_CASEFOLD 0
137 #endif
138 return 0 == fnmatch("*.psi", entry->d_name, FNM_CASEFOLD);
139 }
140
141 static bool psi_context_add(struct psi_context *C, struct psi_parser *P)
142 {
143 bool valid;
144 struct psi_data *D;
145 struct psi_validate_scope scope = {0};
146
147 C->data = safe_perealloc(C->data, (C->count + 1), sizeof(*C->data), 0, 1);
148 D = psi_data_exchange(&C->data[C->count++], PSI_DATA(P));
149
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);
154
155 return valid;
156 }
157
158 void psi_context_build(struct psi_context *C, const char *paths)
159 {
160 int i, n;
161 char *sep = NULL, *cpy = strdup(paths), *ptr = cpy;
162 struct dirent **entries;
163
164 do {
165 sep = strchr(ptr, ':');
166
167 if (sep) {
168 *sep = 0;
169 }
170
171 entries = NULL;
172 n = php_scandir(ptr, &entries, psi_select_dirent, alphasort);
173
174 if (n > 0) {
175 for (i = 0; i < n; ++i) {
176 char psi[MAXPATHLEN];
177 struct psi_parser P;
178 struct psi_parser_input *I;
179
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);
183 }
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));
187 continue;
188 }
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));
192 continue;
193 }
194 psi_parser_parse(&P, I);
195 psi_context_add(C, &P);
196 psi_parser_dtor(&P);
197 psi_parser_input_free(&I);
198 }
199 }
200
201 if (entries) {
202 for (i = 0; i < n; ++i) {
203 free(entries[i]);
204 }
205 free(entries);
206 }
207
208 ptr = sep + 1;
209 } while (sep);
210
211
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!");
214 }
215
216 free(cpy);
217 }
218
219 zend_function_entry *psi_context_compile(struct psi_context *C)
220 {
221 zend_constant zc;
222
223 ZEND_CONSTANT_SET_FLAGS(&zc, CONST_CS|CONST_PERSISTENT, EG(current_module)->module_number);
224
225 if (C->consts) {
226 size_t i = 0;
227 struct psi_const *c;
228
229 while (psi_plist_get(C->consts, i++, &c)) {
230
231 if (zend_get_constant(c->name)) {
232 continue;
233 }
234
235 zc.name = zend_string_copy(c->name);
236
237 switch (c->type->type) {
238 case PSI_T_BOOL:
239 ZVAL_BOOL(&zc.value, c->val->ival.zend.bval);
240 break;
241 case PSI_T_INT:
242 ZVAL_LONG(&zc.value, c->val->ival.zend.lval);
243 break;
244 case PSI_T_FLOAT:
245 case PSI_T_DOUBLE:
246 ZVAL_DOUBLE(&zc.value, c->val->ival.dval);
247 break;
248 case PSI_T_STRING:
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;
253 }
254 break;
255 default:
256 assert(0);
257 break;
258 }
259
260 zend_register_constant(&zc);
261 }
262 }
263
264 if (C->enums) {
265 size_t i = 0;
266 struct psi_decl_enum *e;
267
268 while (psi_plist_get(C->enums, i++, &e)) {
269 size_t j = 0;
270 struct psi_decl_enum_item *item;
271
272 while (psi_plist_get(e->items, j++, &item)) {
273 zend_string *name;
274
275 if (psi_decl_type_is_anon(e->name, "enum")) {
276 name = strpprintf(0, "psi\\%s", item->name->val);
277 } else {
278 name = strpprintf(0, "psi\\%s\\%s", e->name->val, item->name->val);
279 }
280
281 if (zend_get_constant(name)) {
282 zend_string_release(name);
283 continue;
284 }
285
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);
290 }
291 }
292 }
293
294 return C->closures = C->ops->compile(C);
295 }
296
297
298 ZEND_RESULT_CODE psi_context_call(struct psi_context *C, zend_execute_data *execute_data, zval *return_value, struct psi_impl *impl)
299 {
300 struct psi_call_frame *frame;
301
302 frame = psi_call_frame_init(C, impl->decl, impl);
303
304 if (SUCCESS != psi_call_frame_parse_args(frame, execute_data)) {
305 psi_call_frame_free(frame);
306
307 return FAILURE;
308 }
309
310 psi_call_frame_enter(frame);
311
312 if (SUCCESS != psi_call_frame_do_let(frame)) {
313 psi_call_frame_do_return(frame, return_value);
314 psi_call_frame_free(frame);
315
316 return FAILURE;
317 }
318
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);
322
323 return FAILURE;
324 }
325
326 C->ops->call(frame);
327
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);
331
332 return FAILURE;
333 }
334
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);
339
340 return SUCCESS;
341 }
342
343
344 void psi_context_dtor(struct psi_context *C)
345 {
346 size_t i;
347 zend_function_entry *zfe;
348
349 if (C->ops->dtor) {
350 C->ops->dtor(C);
351 }
352
353 psi_data_dtor(PSI_DATA(C));
354
355 if (C->data) {
356 for (i = 0; i < C->count; ++i) {
357 psi_data_dtor(&C->data[i]);
358 }
359 free(C->data);
360 }
361
362 if (C->closures) {
363 for (zfe = C->closures; zfe->fname; ++zfe) {
364 free((void *) zfe->arg_info);
365 }
366 free(C->closures);
367 }
368 }
369
370 void psi_context_free(struct psi_context **C)
371 {
372 if (*C) {
373 psi_context_dtor(*C);
374 free(*C);
375 *C = NULL;
376 }
377 }
378
379 void psi_context_dump(struct psi_context *C, int fd)
380 {
381 size_t i;
382
383 dprintf(fd, "// psi.engine=%s\n// %lu files\n",
384 (char *) C->ops->query(C, PSI_CONTEXT_QUERY_SELF, NULL),
385 C->count);
386
387 for (i = 0; i < C->count; ++i) {
388 psi_data_dump(fd, &C->data[i]);
389 }
390 }