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