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 zend_long nfe
= zend_hash_next_free_element(&frame
->arguments
);
229 return nfe
> 0 ? nfe
: 0;
232 size_t psi_call_frame_num_fixed_args(struct psi_call_frame
*frame
) {
233 return psi_plist_count(frame
->decl
->args
);
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
);
241 void **psi_call_frame_get_arg_pointers(struct psi_call_frame
*frame
) {
242 return frame
->pointers
;
245 void *psi_call_frame_get_rpointer(struct psi_call_frame
*frame
) {
246 return frame
->rpointer
;
249 struct psi_decl
*psi_call_frame_get_decl(struct psi_call_frame
*frame
) {
253 struct psi_impl
*psi_call_frame_get_impl(struct psi_call_frame
*frame
) {
257 struct psi_context
*psi_call_frame_get_context(struct psi_call_frame
*frame
) {
258 return frame
->context
;
261 #if PHP_VERSION_ID < 70300
262 # define PARAM_PROLOGUE(separate) Z_PARAM_PROLOGUE(separate)
264 # define PARAM_PROLOGUE(separate) Z_PARAM_PROLOGUE(1, separate)
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
;
271 zend_replace_error_handling(EH_THROW
, zend_exception_get_default(), &zeh
);
276 rv
= zend_parse_parameters_none();
277 zend_restore_error_handling(&zeh
);
278 return rv
== SUCCESS
;
281 ZEND_PARSE_PARAMETERS_START(
282 psi_impl_num_min_args(frame
->impl
),
283 frame
->impl
->func
->vararg
? -1 : argc
287 struct psi_impl_arg
*iarg
;
290 if (frame
->impl
->func
->vararg
&& _i
>= argc
) {
291 iarg
= frame
->impl
->func
->vararg
;
294 psi_plist_get(frame
->impl
->func
->args
, _i
, &iarg
);
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
);
309 zend_string_addref(ival
.zend
.str
);
311 } else if (PSI_T_ARRAY
== iarg
->type
->type
) {
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
) {
321 zend_fcall_info_cache fcc
;
323 Z_PARAM_FUNC_EX(fci
, fcc
, 1, 0);
326 ival
.zend
.cb
= ecalloc(1, sizeof(zend_fcall
));
327 ival
.zend
.cb
->fci
= fci
;
328 ival
.zend
.cb
->fcc
= fcc
;
331 _error_code
= ZPP_ERROR_FAILURE
;
335 psi_call_frame_new_argument(frame
,
336 psi_call_frame_argument_init(iarg
, &ival
, _arg
, _i
> argc
));
338 if (_i
< _num_args
) {
342 ZEND_PARSE_PARAMETERS_END_EX(
343 zend_restore_error_handling(&zeh
);
347 /* set up defaults */
348 for (i
= EX_NUM_ARGS(); i
< argc
; ++i
) {
349 struct psi_impl_arg
*iarg
;
351 psi_plist_get(frame
->impl
->func
->args
, i
, &iarg
);
354 psi_call_frame_new_argument(frame
, psi_call_frame_argument_init(iarg
,
355 &iarg
->def
->ival
, NULL
, 0));
358 zend_restore_error_handling(&zeh
);
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
;
368 /* initialize ffi argument array */
369 frame
->pointers
= ecalloc(argc
+ va_count
+ 1, sizeof(void *));
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
);
376 rv_sym
->ival_ptr
= &rv_sym
->temp_val
;
378 frame
->rpointer
= rv_sym
->ptr
= rv_sym
->ival_ptr
;
381 bool psi_call_frame_do_let(struct psi_call_frame
*frame
) {
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
);
388 for (i
= 0; psi_plist_get(frame
->impl
->stmts
.let
, i
, &let
); ++i
) {
389 psi_let_stmt_exec(let
, frame
);
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
;
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
;
401 for (i
= 0; i
< va_count
; ++i
) {
402 struct psi_call_frame_argument
*frame_arg
;
403 psi_marshal_let let_fn
;
406 frame_arg
= psi_call_frame_get_var_argument(frame
, i
);
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;
414 case PSI_T_DOUBLE
: let_fn
= psi_let_floatval
; break;
415 case PSI_T_STRING
: let_fn
= psi_let_strval
; break;
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
);
424 psi_call_frame_push_auto(frame
, temp
);
427 frame
->pointers
[argc
+ i
] = frame_arg
->ival_ptr
;
434 bool psi_call_frame_do_assert(struct psi_call_frame
*frame
, enum psi_assert_kind kind
) {
436 struct psi_assert_stmt
*ass
;
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
);
450 void psi_call_frame_do_call(struct psi_call_frame
*frame
) {
451 frame
->context
->ops
->call(frame
);
454 void psi_call_frame_do_callback(struct psi_call_frame
*frame
, struct psi_call_frame_callback
*cbdata
)
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
;
463 assert(cbdata
->argc
== psi_plist_count(cb
->decl
->args
));
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
;
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
);
475 set_sym
->ptr
= cbdata
->argv
[i
];
476 psi_set_exp_exec_ex(set_exp
, &zargv
[i
], set_sym
->ptr
, frame
);
479 frame_arg
= psi_call_frame_get_argument(frame
, cb
->func
->var
->fqn
);
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
);
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
));
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
]);
505 void psi_call_frame_do_return(struct psi_call_frame
*frame
, zval
*return_value
) {
506 struct psi_return_stmt
*ret
;
508 psi_plist_get(frame
->impl
->stmts
.ret
, 0, &ret
);
509 psi_return_stmt_exec(ret
, return_value
, frame
);
512 void psi_call_frame_do_set(struct psi_call_frame
*frame
) {
514 struct psi_set_stmt
*set
;
516 while (psi_plist_get(frame
->impl
->stmts
.set
, i
++, &set
)) {
517 psi_set_stmt_exec(set
, frame
);
521 void psi_call_frame_do_free(struct psi_call_frame
*frame
) {
523 struct psi_free_stmt
*fre
;
525 while (psi_plist_get(frame
->impl
->stmts
.fre
, i
++, &fre
)) {
526 psi_free_stmt_exec(fre
, frame
);
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
;
536 zend_llist_add_element(&frame
->temp
, &f
);
537 return &((struct psi_call_frame_auto_free
*) zend_llist_get_last(&frame
->temp
))->data
;
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
);
544 static void psi_call_frame_local_auto_dtor(void *auto_list
)
546 zend_llist_destroy(auto_list
);
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
));
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);
561 zend_llist_destroy(&frame
->temp
);
563 efree(frame
->pointers
);