travis: update
[m6w6/ext-psi] / src / call.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 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #else
29 # include "php_config.h"
30 #endif
31
32 #include "context.h"
33 #include "data.h"
34 #include "call.h"
35
36 #include "php_psi.h"
37
38 #include "php.h"
39 #include "zend_exceptions.h"
40
41 struct psi_call_frame_argument *psi_call_frame_argument_init(struct psi_impl_arg *spec,
42 impl_val *ival, zval *zptr, int is_vararg) {
43 struct psi_call_frame_argument *frame_arg = ecalloc(1, sizeof(*frame_arg));
44
45
46 if ((frame_arg->zval_ptr = zptr)) {
47 ZVAL_DEREF(frame_arg->zval_ptr);
48
49 /* use userland type if the declared type of the vararg is mixed */
50 if (is_vararg) {
51 if (spec->type->type == PSI_T_MIXED) {
52 switch (Z_TYPE_P(zptr)) {
53 case IS_TRUE:
54 frame_arg->va_type = PSI_T_BOOL;
55 ival->zend.bval = 1;
56 break;
57 case IS_FALSE:
58 frame_arg->va_type = PSI_T_BOOL;
59 ival->zend.bval = 0;
60 break;
61 case IS_LONG:
62 frame_arg->va_type = PSI_T_INT;
63 ival->zend.lval = Z_LVAL_P(zptr);
64 break;
65 case IS_DOUBLE:
66 frame_arg->va_type = PSI_T_FLOAT;
67 ival->dval = Z_DVAL_P(zptr);
68 break;
69 default:
70 frame_arg->va_type = PSI_T_STRING;
71 ival->zend.str = zval_get_string(zptr);
72 break;
73 }
74 } else {
75 frame_arg->va_type = spec->type->type;
76 }
77 }
78 }
79
80 frame_arg->ival = *ival;
81 frame_arg->ival_ptr = &frame_arg->ival;
82 frame_arg->spec = spec;
83
84 return frame_arg;
85 }
86
87 void psi_call_frame_argument_free(struct psi_call_frame_argument *arg) {
88 switch (arg->spec->type->type) {
89 case PSI_T_STRING:
90 if (arg->ival.zend.str) {
91 zend_string_release(arg->ival.zend.str);
92 }
93 break;
94 case PSI_T_CALLABLE:
95 if (arg->ival.zend.cb) {
96 if (arg->ival.zend.cb->fci.size) {
97 zend_fcall_info_args_clear(&arg->ival.zend.cb->fci, 1);
98 }
99 efree(arg->ival.zend.cb);
100 }
101 break;
102 default:
103 break;
104 }
105 if (arg->ival_ptr && arg->ival_ptr != &arg->temp_val && arg->ival_ptr != &arg->ival) {
106 efree(arg->ival_ptr);
107 }
108 efree(arg);
109 }
110
111 struct psi_call_frame_symbol *psi_call_frame_symbol_init(struct psi_decl_var *dvar) {
112 struct psi_call_frame_symbol *frame_sym;
113 size_t size = psi_decl_type_get_size(dvar->arg->type, NULL);
114
115 frame_sym = ecalloc(1, sizeof(*frame_sym) + size);
116 frame_sym->ptr = frame_sym->ival_ptr = &frame_sym->temp_val;
117
118 return frame_sym;
119 }
120
121 void psi_call_frame_symbol_free(struct psi_call_frame_symbol *sym) {
122 efree(sym);
123 }
124
125 static void psi_call_frame_free_argument(zval *zptr) {
126 psi_call_frame_argument_free(Z_PTR_P(zptr));
127 }
128
129 static void psi_call_frame_free_symbol(zval *zptr) {
130 psi_call_frame_symbol_free(Z_PTR_P(zptr));
131 }
132
133 struct psi_call_frame_auto_free {
134 void *data;
135 void (*dtor)(void *);
136 };
137
138 static void psi_call_frame_free_temp(void *ptr) {
139 struct psi_call_frame_auto_free *f = ptr;
140
141 if (f->data) {
142 if (f->dtor) {
143 f->dtor(&f->data);
144 } else {
145 efree(f->data);
146 }
147 }
148 }
149
150 struct psi_call_frame *psi_call_frame_init(struct psi_context *C, struct psi_decl *decl, struct psi_impl *impl) {
151 struct psi_call_frame *frame = ecalloc(1, sizeof(*frame));
152
153 frame->context = C;
154 frame->decl = decl;
155 frame->impl = impl;
156
157 zend_hash_init(&frame->arguments, 8, NULL, psi_call_frame_free_argument, 0);
158 zend_hash_init(&frame->symbols, 8, NULL, psi_call_frame_free_symbol, 0);
159 zend_llist_init(&frame->temp, sizeof(struct psi_call_frame_auto_free), psi_call_frame_free_temp, 0);
160
161 return frame;
162 }
163
164 struct psi_call_frame_symbol *psi_call_frame_fetch_symbol(
165 struct psi_call_frame *frame, struct psi_decl_var *dvar) {
166 struct psi_call_frame_symbol *frame_sym;
167
168 frame_sym = zend_hash_find_ptr(&frame->symbols, dvar->fqn);
169 if (!frame_sym) {
170 frame_sym = zend_hash_add_ptr(&frame->symbols, dvar->fqn,
171 psi_call_frame_symbol_init(dvar));
172 }
173 return frame_sym;
174 }
175
176 zval *psi_call_frame_new_argument(struct psi_call_frame *frame,
177 struct psi_call_frame_argument *frame_arg) {
178 if (frame_arg->va_type) {
179 /* varargs are just appended with numeric indices */
180 return zend_hash_next_index_insert_ptr(&frame->arguments, frame_arg);
181 } else {
182 return zend_hash_add_ptr(&frame->arguments,
183 frame_arg->spec->var->name, frame_arg);
184 }
185 }
186
187 zval *psi_call_frame_sub_argument(struct psi_call_frame *frame,
188 struct psi_impl_var *inner_var, zval *outer_zval, zend_string *name) {
189 struct psi_call_frame_argument *iarg;
190 zval *inner_zval = zend_symtable_str_find(Z_ARRVAL_P(outer_zval),
191 &inner_var->name->val[1], inner_var->name->len - 1);
192
193 if (!inner_zval) {
194 zval empty_zval;
195
196 SEPARATE_ZVAL(outer_zval);
197 ZVAL_NULL(&empty_zval);
198 inner_zval = zend_symtable_str_update(Z_ARRVAL_P(outer_zval),
199 &inner_var->name->val[1], inner_var->name->len - 1,
200 &empty_zval);
201 }
202
203 iarg = psi_call_frame_get_argument(frame, name);
204
205 if (!iarg) {
206 struct psi_call_frame_argument *frame_arg;
207 impl_val empty_val = {0};
208 zend_string *type_str = psi_string_init_interned(ZEND_STRL("mixed"), 1);
209 struct psi_impl_arg *carg_spec = psi_impl_arg_init(
210 psi_impl_type_init(PSI_T_MIXED, type_str),
211 psi_impl_var_copy(inner_var), NULL);
212
213 psi_call_frame_push_auto_ex(frame, carg_spec, (void(*)(void*)) psi_impl_arg_free);
214 frame_arg = psi_call_frame_argument_init(carg_spec, &empty_val, inner_zval, 0);
215 zend_hash_add_ptr(&frame->arguments, name, frame_arg);
216 zend_string_release(type_str);
217 }
218
219 return inner_zval;
220 }
221
222 struct psi_call_frame_argument *psi_call_frame_get_argument(
223 struct psi_call_frame *frame, zend_string *name) {
224 return zend_hash_find_ptr(&frame->arguments, name);
225 }
226
227 size_t psi_call_frame_num_var_args(struct psi_call_frame *frame) {
228 zend_long nfe = zend_hash_next_free_element(&frame->arguments);
229 return nfe > 0 ? nfe : 0;
230 }
231
232 size_t psi_call_frame_num_fixed_args(struct psi_call_frame *frame) {
233 return psi_plist_count(frame->decl->args);
234 }
235
236 struct psi_call_frame_argument *psi_call_frame_get_var_argument(
237 struct psi_call_frame *frame, zend_long index) {
238 return zend_hash_index_find_ptr(&frame->arguments, index);
239 }
240
241 void **psi_call_frame_get_arg_pointers(struct psi_call_frame *frame) {
242 return frame->pointers;
243 }
244
245 void *psi_call_frame_get_rpointer(struct psi_call_frame *frame) {
246 return frame->rpointer;
247 }
248
249 struct psi_decl *psi_call_frame_get_decl(struct psi_call_frame *frame) {
250 return frame->decl;
251 }
252
253 struct psi_impl *psi_call_frame_get_impl(struct psi_call_frame *frame) {
254 return frame->impl;
255 }
256
257 struct psi_context *psi_call_frame_get_context(struct psi_call_frame *frame) {
258 return frame->context;
259 }
260
261 #if PHP_VERSION_ID < 70300
262 # define PARAM_PROLOGUE(separate) Z_PARAM_PROLOGUE(separate)
263 #else
264 # define PARAM_PROLOGUE(separate) Z_PARAM_PROLOGUE(1, separate)
265 #endif
266 bool psi_call_frame_parse_args(struct psi_call_frame *frame,
267 zend_execute_data *execute_data) {
268 size_t i, argc = psi_plist_count(frame->impl->func->args);
269 zend_error_handling zeh;
270
271 zend_replace_error_handling(EH_THROW, zend_exception_get_default(), &zeh);
272
273 if (!argc) {
274 ZEND_RESULT_CODE rv;
275
276 rv = zend_parse_parameters_none();
277 zend_restore_error_handling(&zeh);
278 return rv == SUCCESS;
279 }
280
281 ZEND_PARSE_PARAMETERS_START(
282 psi_impl_num_min_args(frame->impl),
283 frame->impl->func->vararg ? -1 : argc
284 )
285 nextarg: {
286
287 struct psi_impl_arg *iarg;
288 impl_val ival = {0};
289
290 if (frame->impl->func->vararg && _i >= argc) {
291 iarg = frame->impl->func->vararg;
292 Z_PARAM_OPTIONAL;
293 } else {
294 psi_plist_get(frame->impl->func->args, _i, &iarg);
295 if (iarg->def) {
296 Z_PARAM_OPTIONAL;
297 }
298 }
299
300 if (PSI_T_BOOL == iarg->type->type) {
301 Z_PARAM_BOOL(ival.zend.bval);
302 } else if (PSI_T_INT == iarg->type->type) {
303 Z_PARAM_LONG(ival.zend.lval);
304 } else if (PSI_T_FLOAT == iarg->type->type || PSI_T_DOUBLE == iarg->type->type) {
305 Z_PARAM_DOUBLE(ival.dval);
306 } else if (PSI_T_STRING == iarg->type->type) {
307 Z_PARAM_STR_EX(ival.zend.str, 1, iarg->var->reference);
308 if (ival.zend.str) {
309 zend_string_addref(ival.zend.str);
310 }
311 } else if (PSI_T_ARRAY == iarg->type->type) {
312 zval *tmp;
313 Z_PARAM_ARRAY_EX(tmp, _optional || iarg->var->reference,
314 iarg->var->reference);
315 } else if (PSI_T_OBJECT == iarg->type->type) {
316 PARAM_PROLOGUE(iarg->var->reference);
317 } else if (PSI_T_MIXED == iarg->type->type) {
318 PARAM_PROLOGUE(iarg->var->reference);
319 } else if (PSI_T_CALLABLE == iarg->type->type) {
320 zend_fcall_info fci;
321 zend_fcall_info_cache fcc;
322
323 Z_PARAM_FUNC_EX(fci, fcc, 1, 0);
324
325 if (fci.size) {
326 ival.zend.cb = ecalloc(1, sizeof(zend_fcall));
327 ival.zend.cb->fci = fci;
328 ival.zend.cb->fcc = fcc;
329 }
330 } else {
331 _error_code = ZPP_ERROR_FAILURE;
332 break;
333 }
334
335 psi_call_frame_new_argument(frame,
336 psi_call_frame_argument_init(iarg, &ival, _arg, _i > argc));
337
338 if (_i < _num_args) {
339 goto nextarg;
340 }
341 }
342 ZEND_PARSE_PARAMETERS_END_EX(
343 zend_restore_error_handling(&zeh);
344 return false;
345 );
346
347 /* set up defaults */
348 for (i = EX_NUM_ARGS(); i < argc; ++i) {
349 struct psi_impl_arg *iarg;
350
351 psi_plist_get(frame->impl->func->args, i, &iarg);
352
353 assert(iarg->def);
354 psi_call_frame_new_argument(frame, psi_call_frame_argument_init(iarg,
355 &iarg->def->ival, NULL, 0));
356 }
357
358 zend_restore_error_handling(&zeh);
359 return true;
360 }
361
362 void psi_call_frame_enter(struct psi_call_frame *frame) {
363 size_t argc = psi_call_frame_num_fixed_args(frame);
364 size_t va_count = psi_call_frame_num_var_args(frame);
365 size_t rsize = psi_decl_arg_get_size(frame->decl->func);
366 struct psi_call_frame_symbol *rv_sym;
367
368 /* initialize ffi argument array */
369 frame->pointers = ecalloc(argc + va_count + 1, sizeof(void *));
370
371 /* initialize return value symbol */
372 rv_sym = psi_call_frame_fetch_symbol(frame, frame->decl->func->var);
373 if (rsize > sizeof(impl_val)) {
374 rv_sym->ival_ptr = ecalloc(1, rsize);
375 } else {
376 rv_sym->ival_ptr = &rv_sym->temp_val;
377 }
378 frame->rpointer = rv_sym->ptr = rv_sym->ival_ptr;
379 }
380
381 bool psi_call_frame_do_let(struct psi_call_frame *frame) {
382 size_t i;
383 struct psi_let_stmt *let;
384 struct psi_decl_arg *arg;
385 size_t argc = psi_call_frame_num_fixed_args(frame);
386 size_t va_count = psi_call_frame_num_var_args(frame);
387
388 for (i = 0; psi_plist_get(frame->impl->stmts.let, i, &let); ++i) {
389 psi_let_stmt_exec(let, frame);
390 }
391 for (i = 0; psi_plist_get(frame->decl->args, i, &arg); ++i) {
392 struct psi_let_stmt *let;
393 struct psi_call_frame_symbol *frame_sym;
394
395 let = psi_impl_get_let(frame->impl, arg->var);
396 frame_sym = psi_call_frame_fetch_symbol(frame, let->exp->var);
397 frame->pointers[i] = frame_sym->ptr;
398 }
399 /* varargs */
400 if (va_count) {
401 for (i = 0; i < va_count; ++i) {
402 struct psi_call_frame_argument *frame_arg;
403 psi_marshal_let let_fn;
404 void *temp = NULL;
405
406 frame_arg = psi_call_frame_get_var_argument(frame, i);
407 if (!frame_arg) {
408 return false;
409 }
410 switch (frame_arg->va_type) {
411 case PSI_T_BOOL: let_fn = psi_let_boolval; break;
412 case PSI_T_INT: let_fn = psi_let_intval; break;
413 case PSI_T_FLOAT:
414 case PSI_T_DOUBLE: let_fn = psi_let_floatval; break;
415 case PSI_T_STRING: let_fn = psi_let_strval; break;
416 default:
417 assert(0);
418 return false;
419 }
420
421 frame_arg->ival_ptr = let_fn(&frame_arg->temp_val, NULL, frame_arg->va_type,
422 &frame_arg->ival, frame_arg->zval_ptr, &temp);
423 if (temp) {
424 psi_call_frame_push_auto(frame, temp);
425 }
426
427 frame->pointers[argc + i] = frame_arg->ival_ptr;
428 }
429 }
430
431 return true;
432 }
433
434 bool psi_call_frame_do_assert(struct psi_call_frame *frame, enum psi_assert_kind kind) {
435 size_t i = 0;
436 struct psi_assert_stmt *ass;
437
438 while (psi_plist_get(frame->impl->stmts.ass, i++, &ass)) {
439 if (ass->kind == kind) {
440 if (!psi_assert_stmt_exec(ass, frame)) {
441 psi_assert_stmt_throw(ass);
442 return false;
443 }
444 }
445 }
446
447 return true;
448 }
449
450 void psi_call_frame_do_call(struct psi_call_frame *frame) {
451 frame->context->ops->call(frame);
452 }
453
454 void psi_call_frame_do_callback(struct psi_call_frame *frame, struct psi_call_frame_callback *cbdata)
455 {
456 size_t i;
457 void *retptr;
458 ZEND_RESULT_CODE rc;
459 struct psi_let_callback *cb = cbdata->cb->data.callback;
460 zval return_value, *zargv = ecalloc(cbdata->argc, sizeof(*zargv));
461 struct psi_call_frame_argument *frame_arg;
462
463 assert(cbdata->argc == psi_plist_count(cb->decl->args));
464
465 /* prepare args for the userland call */
466 for (i = 0; i < cbdata->argc; ++i) {
467 struct psi_set_exp *set_exp;
468 struct psi_decl_var *set_var;
469 struct psi_call_frame_symbol *set_sym;
470
471 psi_plist_get(cb->args, i, &set_exp);
472 set_var = psi_set_exp_get_decl_var(set_exp);
473 set_sym = psi_call_frame_fetch_symbol(frame, set_var);
474
475 set_sym->ptr = cbdata->argv[i];
476 psi_set_exp_exec_ex(set_exp, &zargv[i], set_sym->ptr, frame);
477 }
478
479 frame_arg = psi_call_frame_get_argument(frame, cb->func->var->fqn);
480 if (!frame_arg) {
481 return;
482 }
483
484 /* callback into userland */
485 ZVAL_UNDEF(&return_value);
486 zend_fcall_info_argp(&frame_arg->ival_ptr->zend.cb->fci, cbdata->argc, zargv);
487 rc = zend_fcall_info_call(&frame_arg->ival_ptr->zend.cb->fci,
488 &frame_arg->ival_ptr->zend.cb->fcc, &return_value, NULL);
489 assert(rc == SUCCESS);
490 (void) rc;
491
492 /* marshal return value of the userland call */
493 frame_arg->zval_ptr = &return_value;
494 retptr = psi_let_func_exec(cbdata->cb, cb->func, cb->decl->func, frame);
495 memcpy(cbdata->rval, retptr, psi_decl_arg_get_size(cb->decl->func));
496
497 /* cleanup */
498 zend_fcall_info_args_clear(&frame_arg->ival_ptr->zend.cb->fci, 0);
499 for (i = 0; i < cbdata->argc; ++i) {
500 zval_ptr_dtor(&zargv[i]);
501 }
502 efree(zargv);
503 }
504
505 void psi_call_frame_do_return(struct psi_call_frame *frame, zval *return_value) {
506 struct psi_return_stmt *ret;
507
508 psi_plist_get(frame->impl->stmts.ret, 0, &ret);
509 psi_return_stmt_exec(ret, return_value, frame);
510 }
511
512 void psi_call_frame_do_set(struct psi_call_frame *frame) {
513 size_t i = 0;
514 struct psi_set_stmt *set;
515
516 while (psi_plist_get(frame->impl->stmts.set, i++, &set)) {
517 psi_set_stmt_exec(set, frame);
518 }
519 }
520
521 void psi_call_frame_do_free(struct psi_call_frame *frame) {
522 size_t i = 0;
523 struct psi_free_stmt *fre;
524
525 while (psi_plist_get(frame->impl->stmts.fre, i++, &fre)) {
526 psi_free_stmt_exec(fre, frame);
527 }
528 }
529
530 void **psi_call_frame_push_auto_ex(struct psi_call_frame *frame, void *auto_free, void (*dtor)(void*)) {
531 struct psi_call_frame_auto_free f;
532
533 f.data = auto_free;
534 f.dtor = dtor;
535
536 zend_llist_add_element(&frame->temp, &f);
537 return &((struct psi_call_frame_auto_free *) zend_llist_get_last(&frame->temp))->data;
538 }
539
540 void **psi_call_frame_push_auto(struct psi_call_frame *frame, void *auto_free) {
541 return psi_call_frame_push_auto_ex(frame, auto_free, NULL);
542 }
543
544 static void psi_call_frame_local_auto_dtor(void *auto_list)
545 {
546 zend_llist_destroy(auto_list);
547 efree(auto_list);
548 }
549
550 void psi_call_frame_free(struct psi_call_frame *frame) {
551 zend_hash_destroy(&frame->arguments);
552 zend_hash_destroy(&frame->symbols);
553 if (frame->impl && frame->impl->func->static_memory) {
554 zend_llist *temp = emalloc(sizeof(*temp));
555 zval zlocal;
556
557 memcpy(temp, &frame->temp, sizeof(*temp));
558 ZVAL_OBJ(&zlocal, psi_object_init_ex(NULL, temp, psi_call_frame_local_auto_dtor));
559 zend_set_local_var(frame->impl->func->name, &zlocal, /* force */ 1);
560 } else {
561 zend_llist_destroy(&frame->temp);
562 }
563 efree(frame->pointers);
564 efree(frame);
565 }
566
567
568