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