1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
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 *******************************************************************************/
29 # include "php_config.h"
39 #include "zend_exceptions.h"
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
));
46 if ((frame_arg
->zval_ptr
= zptr
)) {
47 ZVAL_DEREF(frame_arg
->zval_ptr
);
49 /* use userland type if the declared type of the vararg is mixed */
51 if (spec
->type
->type
== PSI_T_MIXED
) {
52 switch (Z_TYPE_P(zptr
)) {
54 frame_arg
->va_type
= PSI_T_BOOL
;
58 frame_arg
->va_type
= PSI_T_BOOL
;
62 frame_arg
->va_type
= PSI_T_INT
;
63 ival
->zend
.lval
= Z_LVAL_P(zptr
);
66 frame_arg
->va_type
= PSI_T_FLOAT
;
67 ival
->dval
= Z_DVAL_P(zptr
);
70 frame_arg
->va_type
= PSI_T_STRING
;
71 ival
->zend
.str
= zval_get_string(zptr
);
75 frame_arg
->va_type
= spec
->type
->type
;
80 frame_arg
->ival
= *ival
;
81 frame_arg
->ival_ptr
= &frame_arg
->ival
;
82 frame_arg
->spec
= spec
;
87 void psi_call_frame_argument_free(struct psi_call_frame_argument
*arg
) {
88 switch (arg
->spec
->type
->type
) {
90 if (arg
->ival
.zend
.str
) {
91 zend_string_release(arg
->ival
.zend
.str
);
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);
99 efree(arg
->ival
.zend
.cb
);
105 if (arg
->ival_ptr
&& arg
->ival_ptr
!= &arg
->temp_val
&& arg
->ival_ptr
!= &arg
->ival
) {
106 efree(arg
->ival_ptr
);
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
);
115 frame_sym
= ecalloc(1, sizeof(*frame_sym
) + size
);
116 frame_sym
->ptr
= frame_sym
->ival_ptr
= &frame_sym
->temp_val
;
121 void psi_call_frame_symbol_free(struct psi_call_frame_symbol
*sym
) {
125 static void psi_call_frame_free_argument(zval
*zptr
) {
126 psi_call_frame_argument_free(Z_PTR_P(zptr
));
129 static void psi_call_frame_free_symbol(zval
*zptr
) {
130 psi_call_frame_symbol_free(Z_PTR_P(zptr
));
133 struct psi_call_frame_auto_free
{
135 void (*dtor
)(void *);
138 static void psi_call_frame_free_temp(void *ptr
) {
139 struct psi_call_frame_auto_free
*f
= ptr
;
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
));
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);
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
;
168 frame_sym
= zend_hash_find_ptr(&frame
->symbols
, dvar
->fqn
);
170 frame_sym
= zend_hash_add_ptr(&frame
->symbols
, dvar
->fqn
,
171 psi_call_frame_symbol_init(dvar
));
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
);
182 return zend_hash_add_ptr(&frame
->arguments
,
183 frame_arg
->spec
->var
->name
, frame_arg
);
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);
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,
203 iarg
= psi_call_frame_get_argument(frame
, name
);
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
);
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
);
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
);
227 size_t psi_call_frame_num_var_args(struct psi_call_frame
*frame
) {
228 return zend_hash_next_free_element(&frame
->arguments
);
231 size_t psi_call_frame_num_fixed_args(struct psi_call_frame
*frame
) {
232 return psi_plist_count(frame
->decl
->args
);
235 struct psi_call_frame_argument
*psi_call_frame_get_var_argument(
236 struct psi_call_frame
*frame
, zend_long index
) {
237 return zend_hash_index_find_ptr(&frame
->arguments
, index
);
240 void **psi_call_frame_get_arg_pointers(struct psi_call_frame
*frame
) {
241 return frame
->pointers
;
244 void *psi_call_frame_get_rpointer(struct psi_call_frame
*frame
) {
245 return frame
->rpointer
;
248 struct psi_decl
*psi_call_frame_get_decl(struct psi_call_frame
*frame
) {
252 struct psi_impl
*psi_call_frame_get_impl(struct psi_call_frame
*frame
) {
256 struct psi_context
*psi_call_frame_get_context(struct psi_call_frame
*frame
) {
257 return frame
->context
;
260 #if PHP_VERSION_ID < 70300
261 # define PARAM_PROLOGUE(separate) Z_PARAM_PROLOGUE(separate)
263 # define PARAM_PROLOGUE(separate) Z_PARAM_PROLOGUE(1, separate)
265 bool psi_call_frame_parse_args(struct psi_call_frame
*frame
,
266 zend_execute_data
*execute_data
) {
267 size_t i
, argc
= psi_plist_count(frame
->impl
->func
->args
);
268 zend_error_handling zeh
;
270 zend_replace_error_handling(EH_THROW
, zend_exception_get_default(), &zeh
);
275 rv
= zend_parse_parameters_none();
276 zend_restore_error_handling(&zeh
);
277 return rv
== SUCCESS
;
280 ZEND_PARSE_PARAMETERS_START(
281 psi_impl_num_min_args(frame
->impl
),
282 frame
->impl
->func
->vararg
? -1 : argc
286 struct psi_impl_arg
*iarg
;
289 if (frame
->impl
->func
->vararg
&& _i
>= argc
) {
290 iarg
= frame
->impl
->func
->vararg
;
293 psi_plist_get(frame
->impl
->func
->args
, _i
, &iarg
);
299 if (PSI_T_BOOL
== iarg
->type
->type
) {
300 Z_PARAM_BOOL(ival
.zend
.bval
);
301 } else if (PSI_T_INT
== iarg
->type
->type
) {
302 Z_PARAM_LONG(ival
.zend
.lval
);
303 } else if (PSI_T_FLOAT
== iarg
->type
->type
|| PSI_T_DOUBLE
== iarg
->type
->type
) {
304 Z_PARAM_DOUBLE(ival
.dval
);
305 } else if (PSI_T_STRING
== iarg
->type
->type
) {
306 Z_PARAM_STR_EX(ival
.zend
.str
, 1, iarg
->var
->reference
);
308 zend_string_addref(ival
.zend
.str
);
310 } else if (PSI_T_ARRAY
== iarg
->type
->type
) {
312 Z_PARAM_ARRAY_EX(tmp
, _optional
|| iarg
->var
->reference
,
313 iarg
->var
->reference
);
314 } else if (PSI_T_OBJECT
== iarg
->type
->type
) {
315 PARAM_PROLOGUE(iarg
->var
->reference
);
316 } else if (PSI_T_MIXED
== iarg
->type
->type
) {
317 PARAM_PROLOGUE(iarg
->var
->reference
);
318 } else if (PSI_T_CALLABLE
== iarg
->type
->type
) {
320 zend_fcall_info_cache fcc
;
322 Z_PARAM_FUNC_EX(fci
, fcc
, 1, 0);
325 ival
.zend
.cb
= ecalloc(1, sizeof(zend_fcall
));
326 ival
.zend
.cb
->fci
= fci
;
327 ival
.zend
.cb
->fcc
= fcc
;
330 error_code
= ZPP_ERROR_FAILURE
;
334 psi_call_frame_new_argument(frame
,
335 psi_call_frame_argument_init(iarg
, &ival
, _arg
, _i
> argc
));
337 if (_i
< _num_args
) {
341 ZEND_PARSE_PARAMETERS_END_EX(
342 zend_restore_error_handling(&zeh
);
346 /* set up defaults */
347 for (i
= EX_NUM_ARGS(); i
< argc
; ++i
) {
348 struct psi_impl_arg
*iarg
;
350 psi_plist_get(frame
->impl
->func
->args
, i
, &iarg
);
353 psi_call_frame_new_argument(frame
, psi_call_frame_argument_init(iarg
,
354 &iarg
->def
->ival
, NULL
, 0));
357 zend_restore_error_handling(&zeh
);
361 void psi_call_frame_enter(struct psi_call_frame
*frame
) {
362 size_t argc
= psi_call_frame_num_fixed_args(frame
);
363 size_t va_count
= psi_call_frame_num_var_args(frame
);
364 size_t rsize
= psi_decl_arg_get_size(frame
->decl
->func
);
365 struct psi_call_frame_symbol
*rv_sym
;
367 /* initialize ffi argument array */
368 frame
->pointers
= ecalloc(argc
+ va_count
+ 1, sizeof(void *));
370 /* initialize return value symbol */
371 rv_sym
= psi_call_frame_fetch_symbol(frame
, frame
->decl
->func
->var
);
372 if (rsize
> sizeof(impl_val
)) {
373 rv_sym
->ival_ptr
= ecalloc(1, rsize
);
375 rv_sym
->ival_ptr
= &rv_sym
->temp_val
;
377 frame
->rpointer
= rv_sym
->ptr
= rv_sym
->ival_ptr
;
380 bool psi_call_frame_do_let(struct psi_call_frame
*frame
) {
382 struct psi_let_stmt
*let
;
383 struct psi_decl_arg
*arg
;
384 size_t argc
= psi_call_frame_num_fixed_args(frame
);
385 size_t va_count
= psi_call_frame_num_var_args(frame
);
387 for (i
= 0; psi_plist_get(frame
->impl
->stmts
.let
, i
, &let
); ++i
) {
388 psi_let_stmt_exec(let
, frame
);
390 for (i
= 0; psi_plist_get(frame
->decl
->args
, i
, &arg
); ++i
) {
391 struct psi_let_stmt
*let
;
392 struct psi_call_frame_symbol
*frame_sym
;
394 let
= psi_impl_get_let(frame
->impl
, arg
->var
);
395 frame_sym
= psi_call_frame_fetch_symbol(frame
, let
->exp
->var
);
396 frame
->pointers
[i
] = frame_sym
->ptr
;
400 for (i
= 0; i
< va_count
; ++i
) {
401 struct psi_call_frame_argument
*frame_arg
;
402 psi_marshal_let let_fn
;
405 frame_arg
= psi_call_frame_get_var_argument(frame
, i
);
406 switch (frame_arg
->va_type
) {
407 case PSI_T_BOOL
: let_fn
= psi_let_boolval
; break;
408 case PSI_T_INT
: let_fn
= psi_let_intval
; break;
410 case PSI_T_DOUBLE
: let_fn
= psi_let_floatval
; break;
411 case PSI_T_STRING
: let_fn
= psi_let_strval
; break;
416 frame_arg
->ival_ptr
= let_fn(&frame_arg
->temp_val
, NULL
, frame_arg
->va_type
,
417 &frame_arg
->ival
, frame_arg
->zval_ptr
, &temp
);
419 psi_call_frame_push_auto(frame
, temp
);
422 frame
->pointers
[argc
+ i
] = frame_arg
->ival_ptr
;
429 bool psi_call_frame_do_assert(struct psi_call_frame
*frame
, enum psi_assert_kind kind
) {
431 struct psi_assert_stmt
*ass
;
433 while (psi_plist_get(frame
->impl
->stmts
.ass
, i
++, &ass
)) {
434 if (ass
->kind
== kind
) {
435 if (!psi_assert_stmt_exec(ass
, frame
)) {
436 psi_assert_stmt_throw(ass
);
445 void psi_call_frame_do_call(struct psi_call_frame
*frame
) {
446 frame
->context
->ops
->call(frame
);
449 void psi_call_frame_do_callback(struct psi_call_frame
*frame
, struct psi_call_frame_callback
*cbdata
)
454 struct psi_let_callback
*cb
= cbdata
->cb
->data
.callback
;
455 zval return_value
, *zargv
= ecalloc(cbdata
->argc
, sizeof(*zargv
));
456 struct psi_call_frame_argument
*frame_arg
;
458 assert(cbdata
->argc
== psi_plist_count(cb
->decl
->args
));
460 /* prepare args for the userland call */
461 for (i
= 0; i
< cbdata
->argc
; ++i
) {
462 struct psi_set_exp
*set_exp
;
463 struct psi_decl_var
*set_var
;
464 struct psi_call_frame_symbol
*set_sym
;
466 psi_plist_get(cb
->args
, i
, &set_exp
);
467 set_var
= psi_set_exp_get_decl_var(set_exp
);
468 set_sym
= psi_call_frame_fetch_symbol(frame
, set_var
);
470 set_sym
->ptr
= cbdata
->argv
[i
];
471 psi_set_exp_exec_ex(set_exp
, &zargv
[i
], set_sym
->ptr
, frame
);
474 frame_arg
= psi_call_frame_get_argument(frame
, cb
->func
->var
->fqn
);
476 /* callback into userland */
477 ZVAL_UNDEF(&return_value
);
478 zend_fcall_info_argp(&frame_arg
->ival_ptr
->zend
.cb
->fci
, cbdata
->argc
, zargv
);
479 rc
= zend_fcall_info_call(&frame_arg
->ival_ptr
->zend
.cb
->fci
,
480 &frame_arg
->ival_ptr
->zend
.cb
->fcc
, &return_value
, NULL
);
481 assert(rc
== SUCCESS
);
484 /* marshal return value of the userland call */
485 frame_arg
->zval_ptr
= &return_value
;
486 retptr
= psi_let_func_exec(cbdata
->cb
, cb
->func
, cb
->decl
->func
, frame
);
487 memcpy(cbdata
->rval
, retptr
, psi_decl_arg_get_size(cb
->decl
->func
));
490 zend_fcall_info_args_clear(&frame_arg
->ival_ptr
->zend
.cb
->fci
, 0);
491 for (i
= 0; i
< cbdata
->argc
; ++i
) {
492 zval_ptr_dtor(&zargv
[i
]);
497 void psi_call_frame_do_return(struct psi_call_frame
*frame
, zval
*return_value
) {
498 struct psi_return_stmt
*ret
;
500 psi_plist_get(frame
->impl
->stmts
.ret
, 0, &ret
);
501 psi_return_stmt_exec(ret
, return_value
, frame
);
504 void psi_call_frame_do_set(struct psi_call_frame
*frame
) {
506 struct psi_set_stmt
*set
;
508 while (psi_plist_get(frame
->impl
->stmts
.set
, i
++, &set
)) {
509 psi_set_stmt_exec(set
, frame
);
513 void psi_call_frame_do_free(struct psi_call_frame
*frame
) {
515 struct psi_free_stmt
*fre
;
517 while (psi_plist_get(frame
->impl
->stmts
.fre
, i
++, &fre
)) {
518 psi_free_stmt_exec(fre
, frame
);
522 void **psi_call_frame_push_auto_ex(struct psi_call_frame
*frame
, void *auto_free
, void (*dtor
)(void*)) {
523 struct psi_call_frame_auto_free f
;
528 zend_llist_add_element(&frame
->temp
, &f
);
529 return &((struct psi_call_frame_auto_free
*) zend_llist_get_last(&frame
->temp
))->data
;
532 void **psi_call_frame_push_auto(struct psi_call_frame
*frame
, void *auto_free
) {
533 return psi_call_frame_push_auto_ex(frame
, auto_free
, NULL
);
536 static void psi_call_frame_local_auto_dtor(void *auto_list
)
538 zend_llist_destroy(auto_list
);
542 void psi_call_frame_free(struct psi_call_frame
*frame
) {
543 zend_hash_destroy(&frame
->arguments
);
544 zend_hash_destroy(&frame
->symbols
);
545 if (frame
->impl
&& frame
->impl
->func
->static_memory
) {
546 zend_llist
*temp
= emalloc(sizeof(*temp
));
549 memcpy(temp
, &frame
->temp
, sizeof(*temp
));
550 ZVAL_OBJ(&zlocal
, psi_object_init_ex(NULL
, temp
, psi_call_frame_local_auto_dtor
));
551 zend_set_local_var(frame
->impl
->func
->name
, &zlocal
, /* force */ 1);
553 zend_llist_destroy(&frame
->temp
);
555 efree(frame
->pointers
);