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