ffi: improve support for functions returning arrays
[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 struct psi_context *psi_context_init(struct psi_context *C, struct psi_context_ops *ops, psi_error_cb error, unsigned flags)
62 {
63 if (!C) {
64 C = malloc(sizeof(*C));
65 }
66 memset(C, 0, sizeof(*C));
67
68 psi_data_ctor(PSI_DATA(C), error, flags);
69 C->ops = ops;
70
71 if (ops->init) {
72 ops->init(C);
73 }
74
75 assert(ops->call != NULL);
76 assert(ops->compile != NULL);
77
78 return C;
79 }
80
81 static int psi_select_dirent(const struct dirent *entry)
82 {
83 #ifndef FNM_CASEFOLD
84 # define FNM_CASEFOLD 0
85 #endif
86 return 0 == fnmatch("*.psi", entry->d_name, FNM_CASEFOLD);
87 }
88
89 static bool psi_context_add(struct psi_context *C, struct psi_parser *P)
90 {
91 bool valid;
92 struct psi_data *D;
93 struct psi_validate_scope scope = {0};
94
95 C->data = realloc(C->data, (C->count + 1) * sizeof(*C->data));
96 D = psi_data_exchange(&C->data[C->count++], PSI_DATA(P));
97
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);
102
103 return valid;
104 }
105
106 void psi_context_build(struct psi_context *C, const char *paths)
107 {
108 int i, n;
109 char *sep = NULL, *cpy = strdup(paths), *ptr = cpy;
110 struct dirent **entries;
111
112 do {
113 sep = strchr(ptr, ':');
114
115 if (sep) {
116 *sep = 0;
117 }
118
119 entries = NULL;
120 n = php_scandir(ptr, &entries, psi_select_dirent, alphasort);
121
122 if (n > 0) {
123 for (i = 0; i < n; ++i) {
124 char psi[MAXPATHLEN];
125 struct psi_parser P;
126 struct psi_parser_input *I;
127
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);
131 }
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));
135 continue;
136 }
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));
140 continue;
141 }
142 psi_parser_parse(&P, I);
143 psi_context_add(C, &P);
144 psi_parser_dtor(&P);
145 free(I);
146 }
147 }
148
149 if (entries) {
150 for (i = 0; i < n; ++i) {
151 free(entries[i]);
152 }
153 free(entries);
154 }
155
156 ptr = sep + 1;
157 } while (sep);
158
159
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!");
162 }
163
164 free(cpy);
165 }
166
167 zend_function_entry *psi_context_compile(struct psi_context *C)
168 {
169 zend_constant zc;
170
171 ZEND_CONSTANT_SET_FLAGS(&zc, CONST_CS|CONST_PERSISTENT, EG(current_module)->module_number);
172
173 if (C->consts) {
174 size_t i = 0;
175 struct psi_const *c;
176
177 while (psi_plist_get(C->consts, i++, &c)) {
178
179 if (zend_get_constant_str(c->name, strlen(c->name))) {
180 continue;
181 }
182
183 zc.name = zend_string_init(c->name, strlen(c->name), 1);
184
185 switch (c->type->type) {
186 case PSI_T_BOOL:
187 ZVAL_BOOL(&zc.value, c->val->ival.zend.bval);
188 break;
189 case PSI_T_INT:
190 ZVAL_LONG(&zc.value, c->val->ival.zend.lval);
191 break;
192 case PSI_T_FLOAT:
193 case PSI_T_DOUBLE:
194 ZVAL_DOUBLE(&zc.value, c->val->ival.dval);
195 break;
196 case PSI_T_STRING:
197 case PSI_T_QUOTED_STRING:
198 ZVAL_NEW_STR(&zc.value, zend_string_copy(c->val->ival.zend.str));
199 break;
200 default:
201 assert(0);
202 break;
203 }
204
205 zend_register_constant(&zc);
206 }
207 }
208
209 if (C->enums) {
210 size_t i = 0;
211 struct psi_decl_enum *e;
212
213 while (psi_plist_get(C->enums, i++, &e)) {
214 size_t j = 0;
215 struct psi_decl_enum_item *item;
216
217 while (psi_plist_get(e->items, j++, &item)) {
218 zend_string *name;
219
220 if (psi_decl_type_is_anon(e->name, "enum")) {
221 name = strpprintf(0, "psi\\%s", item->name);
222 } else {
223 name = strpprintf(0, "psi\\%s\\%s", e->name, item->name);
224 }
225
226 if (zend_get_constant(name)) {
227 zend_string_release(name);
228 continue;
229 }
230
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);
235 }
236 }
237 }
238
239 return C->closures = C->ops->compile(C);
240 }
241
242
243 ZEND_RESULT_CODE psi_context_call(struct psi_context *C, zend_execute_data *execute_data, zval *return_value, struct psi_impl *impl)
244 {
245 struct psi_call_frame *frame;
246
247 frame = psi_call_frame_init(C, impl->decl, impl);
248
249 if (SUCCESS != psi_call_frame_parse_args(frame, execute_data)) {
250 psi_call_frame_free(frame);
251
252 return FAILURE;
253 }
254
255 psi_call_frame_enter(frame);
256
257 if (SUCCESS != psi_call_frame_do_let(frame)) {
258 psi_call_frame_do_return(frame, return_value);
259 psi_call_frame_free(frame);
260
261 return FAILURE;
262 }
263
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);
267
268 return FAILURE;
269 }
270
271 C->ops->call(frame);
272
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);
276
277 return FAILURE;
278 }
279
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);
284
285 return SUCCESS;
286 }
287
288
289 void psi_context_dtor(struct psi_context *C)
290 {
291 size_t i;
292 zend_function_entry *zfe;
293
294 if (C->ops->dtor) {
295 C->ops->dtor(C);
296 }
297
298 psi_data_dtor(PSI_DATA(C));
299
300 if (C->data) {
301 for (i = 0; i < C->count; ++i) {
302 psi_data_dtor(&C->data[i]);
303 }
304 free(C->data);
305 }
306
307 if (C->closures) {
308 for (zfe = C->closures; zfe->fname; ++zfe) {
309 free((void *) zfe->arg_info);
310 }
311 free(C->closures);
312 }
313 }
314
315 void psi_context_free(struct psi_context **C)
316 {
317 if (*C) {
318 psi_context_dtor(*C);
319 free(*C);
320 *C = NULL;
321 }
322 }
323
324 void psi_context_dump(struct psi_context *C, int fd)
325 {
326 size_t i;
327 struct psi_data *D;
328
329 dprintf(fd, "// psi.engine=%s\n// %lu files\n",
330 (char *) C->ops->query(C, PSI_CONTEXT_QUERY_SELF, NULL),
331 C->count);
332
333 for (i = 0; i < C->count; ++i) {
334 psi_data_dump(fd, &C->data[i]);
335 }
336 }