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 *******************************************************************************/
26 #include "php_psi_stdinc.h"
32 #include "zend_exceptions.h"
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
));
39 if ((frame_arg
->zval_ptr
= zptr
)) {
40 ZVAL_DEREF(frame_arg
->zval_ptr
);
42 /* use userland type if the declared type of the vararg is mixed */
44 if (spec
->type
->type
== PSI_T_MIXED
) {
45 switch (Z_TYPE_P(zptr
)) {
47 frame_arg
->va_type
= PSI_T_BOOL
;
51 frame_arg
->va_type
= PSI_T_BOOL
;
55 frame_arg
->va_type
= PSI_T_INT
;
56 ival
->zend
.lval
= Z_LVAL_P(zptr
);
59 frame_arg
->va_type
= PSI_T_FLOAT
;
60 ival
->dval
= Z_DVAL_P(zptr
);
63 frame_arg
->va_type
= PSI_T_STRING
;
64 ival
->zend
.str
= zval_get_string(zptr
);
68 frame_arg
->va_type
= spec
->type
->type
;
73 frame_arg
->ival
= *ival
;
74 frame_arg
->ival_ptr
= &frame_arg
->ival
;
75 frame_arg
->spec
= spec
;
80 void psi_call_frame_argument_free(struct psi_call_frame_argument
*arg
) {
81 switch (arg
->spec
->type
->type
) {
83 if (arg
->ival
.zend
.str
) {
84 zend_string_release(arg
->ival
.zend
.str
);
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);
92 efree(arg
->ival
.zend
.cb
);
98 if (arg
->ival_ptr
&& arg
->ival_ptr
!= &arg
->temp_val
&& arg
->ival_ptr
!= &arg
->ival
) {
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
);
108 frame_sym
= ecalloc(1, sizeof(*frame_sym
) + size
);
109 frame_sym
->ptr
= frame_sym
->ival_ptr
= &frame_sym
->temp_val
;
114 void psi_call_frame_symbol_free(struct psi_call_frame_symbol
*sym
) {
118 static void psi_call_frame_free_argument(zval
*zptr
) {
119 psi_call_frame_argument_free(Z_PTR_P(zptr
));
122 static void psi_call_frame_free_symbol(zval
*zptr
) {
123 psi_call_frame_symbol_free(Z_PTR_P(zptr
));
126 struct psi_call_frame_auto_free
{
128 void (*dtor
)(void *);
131 static void psi_call_frame_free_temp(void *ptr
) {
132 struct psi_call_frame_auto_free
*f
= ptr
;
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
));
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);
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
;
161 frame_sym
= zend_hash_find_ptr(&frame
->symbols
, dvar
->fqn
);
163 frame_sym
= zend_hash_add_ptr(&frame
->symbols
, dvar
->fqn
,
164 psi_call_frame_symbol_init(dvar
));
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
);
175 return zend_hash_add_ptr(&frame
->arguments
,
176 frame_arg
->spec
->var
->name
, frame_arg
);
180 zval
*psi_call_frame_sub_argument(struct psi_call_frame
*frame
,
181 struct psi_impl_var
*inner_var
, zval
*outer_zval
, zend_string
*name
) {
182 struct psi_call_frame_argument
*iarg
;
183 zval
*inner_zval
= zend_symtable_str_find(Z_ARRVAL_P(outer_zval
),
184 &inner_var
->name
->val
[1], inner_var
->name
->len
- 1);
189 SEPARATE_ZVAL(outer_zval
);
190 ZVAL_NULL(&empty_zval
);
191 inner_zval
= zend_symtable_str_update(Z_ARRVAL_P(outer_zval
),
192 &inner_var
->name
->val
[1], inner_var
->name
->len
- 1,
196 iarg
= psi_call_frame_get_argument(frame
, name
);
199 struct psi_call_frame_argument
*frame_arg
;
200 impl_val empty_val
= {0};
201 zend_string
*type_str
= zend_string_init_interned(ZEND_STRL("mixed"), 1);
202 struct psi_impl_arg
*carg_spec
= psi_impl_arg_init(
203 psi_impl_type_init(PSI_T_MIXED
, type_str
),
204 psi_impl_var_copy(inner_var
), NULL
);
206 psi_call_frame_push_auto_ex(frame
, carg_spec
, (void(*)(void*)) psi_impl_arg_free
);
207 frame_arg
= psi_call_frame_argument_init(carg_spec
, &empty_val
, inner_zval
, 0);
208 zend_hash_add_ptr(&frame
->arguments
, name
, frame_arg
);
209 zend_string_release(type_str
);
215 struct psi_call_frame_argument
*psi_call_frame_get_argument(
216 struct psi_call_frame
*frame
, zend_string
*name
) {
217 return zend_hash_find_ptr(&frame
->arguments
, name
);
220 size_t psi_call_frame_num_var_args(struct psi_call_frame
*frame
) {
221 return zend_hash_next_free_element(&frame
->arguments
);
224 size_t psi_call_frame_num_fixed_args(struct psi_call_frame
*frame
) {
225 return psi_plist_count(frame
->decl
->args
);
228 struct psi_call_frame_argument
*psi_call_frame_get_var_argument(
229 struct psi_call_frame
*frame
, zend_long index
) {
230 return zend_hash_index_find_ptr(&frame
->arguments
, index
);
233 void **psi_call_frame_get_arg_pointers(struct psi_call_frame
*frame
) {
234 return frame
->pointers
;
237 void *psi_call_frame_get_rpointer(struct psi_call_frame
*frame
) {
238 return frame
->rpointer
;
241 struct psi_decl
*psi_call_frame_get_decl(struct psi_call_frame
*frame
) {
245 struct psi_impl
*psi_call_frame_get_impl(struct psi_call_frame
*frame
) {
249 struct psi_context
*psi_call_frame_get_context(struct psi_call_frame
*frame
) {
250 return frame
->context
;
253 #if PHP_VERSION_ID < 70300
254 # define PARAM_PROLOGUE(separate) Z_PARAM_PROLOGUE(separate)
256 # define PARAM_PROLOGUE(separate) Z_PARAM_PROLOGUE(1, separate)
258 ZEND_RESULT_CODE
psi_call_frame_parse_args(struct psi_call_frame
*frame
,
259 zend_execute_data
*execute_data
) {
260 size_t i
, argc
= psi_plist_count(frame
->impl
->func
->args
);
261 zend_error_handling zeh
;
263 zend_replace_error_handling(EH_THROW
, zend_exception_get_default(), &zeh
);
268 rv
= zend_parse_parameters_none();
269 zend_restore_error_handling(&zeh
);
273 ZEND_PARSE_PARAMETERS_START(
274 psi_impl_num_min_args(frame
->impl
),
275 frame
->impl
->func
->vararg
? -1 : argc
279 struct psi_impl_arg
*iarg
;
282 if (frame
->impl
->func
->vararg
&& _i
>= argc
) {
283 iarg
= frame
->impl
->func
->vararg
;
286 psi_plist_get(frame
->impl
->func
->args
, _i
, &iarg
);
292 if (PSI_T_BOOL
== iarg
->type
->type
) {
293 Z_PARAM_BOOL(ival
.zend
.bval
);
294 } else if (PSI_T_INT
== iarg
->type
->type
) {
295 Z_PARAM_LONG(ival
.zend
.lval
);
296 } else if (PSI_T_FLOAT
== iarg
->type
->type
|| PSI_T_DOUBLE
== iarg
->type
->type
) {
297 Z_PARAM_DOUBLE(ival
.dval
);
298 } else if (PSI_T_STRING
== iarg
->type
->type
) {
299 Z_PARAM_STR_EX(ival
.zend
.str
, 1, iarg
->var
->reference
);
301 zend_string_addref(ival
.zend
.str
);
303 } else if (PSI_T_ARRAY
== iarg
->type
->type
) {
305 Z_PARAM_ARRAY_EX(tmp
, _optional
|| iarg
->var
->reference
,
306 iarg
->var
->reference
);
307 } else if (PSI_T_OBJECT
== iarg
->type
->type
) {
308 PARAM_PROLOGUE(iarg
->var
->reference
);
309 } else if (PSI_T_MIXED
== iarg
->type
->type
) {
310 PARAM_PROLOGUE(iarg
->var
->reference
);
311 } else if (PSI_T_CALLABLE
== iarg
->type
->type
) {
313 zend_fcall_info_cache fcc
;
315 Z_PARAM_FUNC_EX(fci
, fcc
, 1, 0);
318 ival
.zend
.cb
= ecalloc(1, sizeof(zend_fcall
));
319 ival
.zend
.cb
->fci
= fci
;
320 ival
.zend
.cb
->fcc
= fcc
;
323 error_code
= ZPP_ERROR_FAILURE
;
327 psi_call_frame_new_argument(frame
,
328 psi_call_frame_argument_init(iarg
, &ival
, _arg
, _i
> argc
));
330 if (_i
< _num_args
) {
334 ZEND_PARSE_PARAMETERS_END_EX(
335 zend_restore_error_handling(&zeh
);
339 /* set up defaults */
340 for (i
= EX_NUM_ARGS(); i
< argc
; ++i
) {
341 struct psi_impl_arg
*iarg
;
343 psi_plist_get(frame
->impl
->func
->args
, i
, &iarg
);
346 psi_call_frame_new_argument(frame
, psi_call_frame_argument_init(iarg
,
347 &iarg
->def
->ival
, NULL
, 0));
350 zend_restore_error_handling(&zeh
);
354 void psi_call_frame_enter(struct psi_call_frame
*frame
) {
355 size_t argc
= psi_call_frame_num_fixed_args(frame
);
356 size_t va_count
= psi_call_frame_num_var_args(frame
);
357 size_t rsize
= psi_decl_arg_get_size(frame
->decl
->func
);
358 struct psi_call_frame_symbol
*rv_sym
;
360 /* initialize ffi argument array */
361 frame
->pointers
= ecalloc(argc
+ va_count
+ 1, sizeof(void *));
363 /* initialize return value symbol */
364 rv_sym
= psi_call_frame_fetch_symbol(frame
, frame
->decl
->func
->var
);
365 if (rsize
> sizeof(impl_val
)) {
366 rv_sym
->ival_ptr
= ecalloc(1, rsize
);
368 rv_sym
->ival_ptr
= &rv_sym
->temp_val
;
370 frame
->rpointer
= rv_sym
->ptr
= rv_sym
->ival_ptr
;
373 ZEND_RESULT_CODE
psi_call_frame_do_let(struct psi_call_frame
*frame
) {
375 struct psi_let_stmt
*let
;
376 struct psi_decl_arg
*arg
;
377 size_t argc
= psi_call_frame_num_fixed_args(frame
);
378 size_t va_count
= psi_call_frame_num_var_args(frame
);
380 for (i
= 0; psi_plist_get(frame
->impl
->stmts
.let
, i
, &let
); ++i
) {
381 psi_let_stmt_exec(let
, frame
);
383 for (i
= 0; psi_plist_get(frame
->decl
->args
, i
, &arg
); ++i
) {
384 struct psi_let_stmt
*let
;
385 struct psi_call_frame_symbol
*frame_sym
;
387 let
= psi_impl_get_let(frame
->impl
, arg
->var
);
388 frame_sym
= psi_call_frame_fetch_symbol(frame
, let
->exp
->var
);
389 frame
->pointers
[i
] = frame_sym
->ptr
;
393 for (i
= 0; i
< va_count
; ++i
) {
394 struct psi_call_frame_argument
*frame_arg
;
395 psi_marshal_let let_fn
;
398 frame_arg
= psi_call_frame_get_var_argument(frame
, i
);
399 switch (frame_arg
->va_type
) {
400 case PSI_T_BOOL
: let_fn
= psi_let_boolval
; break;
401 case PSI_T_INT
: let_fn
= psi_let_intval
; break;
403 case PSI_T_DOUBLE
: let_fn
= psi_let_floatval
; break;
404 case PSI_T_STRING
: let_fn
= psi_let_strval
; break;
409 frame_arg
->ival_ptr
= let_fn(&frame_arg
->temp_val
, NULL
, frame_arg
->va_type
,
410 &frame_arg
->ival
, frame_arg
->zval_ptr
, &temp
);
412 psi_call_frame_push_auto(frame
, temp
);
415 frame
->pointers
[argc
+ i
] = frame_arg
->ival_ptr
;
422 ZEND_RESULT_CODE
psi_call_frame_do_assert(struct psi_call_frame
*frame
, enum psi_assert_kind kind
) {
424 struct psi_assert_stmt
*ass
;
426 while (psi_plist_get(frame
->impl
->stmts
.ass
, i
++, &ass
)) {
427 if (ass
->kind
== kind
) {
428 if (!psi_assert_stmt_exec(ass
, frame
)) {
429 psi_assert_stmt_throw(ass
);
438 void psi_call_frame_do_call(struct psi_call_frame
*frame
) {
439 frame
->context
->ops
->call(frame
);
442 void psi_call_frame_do_callback(struct psi_call_frame
*frame
, struct psi_call_frame_callback
*cbdata
)
447 struct psi_let_callback
*cb
= cbdata
->cb
->data
.callback
;
448 zval return_value
, *zargv
= ecalloc(cbdata
->argc
, sizeof(*zargv
));
449 struct psi_call_frame_argument
*frame_arg
;
451 assert(cbdata
->argc
== psi_plist_count(cb
->decl
->args
));
453 /* prepare args for the userland call */
454 for (i
= 0; i
< cbdata
->argc
; ++i
) {
455 struct psi_set_exp
*set_exp
;
456 struct psi_decl_var
*set_var
;
457 struct psi_call_frame_symbol
*set_sym
;
459 psi_plist_get(cb
->args
, i
, &set_exp
);
460 set_var
= psi_set_exp_get_decl_var(set_exp
);
461 set_sym
= psi_call_frame_fetch_symbol(frame
, set_var
);
463 set_sym
->ptr
= cbdata
->argv
[i
];
464 psi_set_exp_exec_ex(set_exp
, &zargv
[i
], set_sym
->ptr
, frame
);
467 frame_arg
= psi_call_frame_get_argument(frame
, cb
->func
->var
->fqn
);
469 /* callback into userland */
470 ZVAL_UNDEF(&return_value
);
471 zend_fcall_info_argp(&frame_arg
->ival_ptr
->zend
.cb
->fci
, cbdata
->argc
, zargv
);
472 rc
= zend_fcall_info_call(&frame_arg
->ival_ptr
->zend
.cb
->fci
,
473 &frame_arg
->ival_ptr
->zend
.cb
->fcc
, &return_value
, NULL
);
474 assert(rc
== SUCCESS
);
476 /* marshal return value of the userland call */
477 frame_arg
->zval_ptr
= &return_value
;
478 retptr
= psi_let_func_exec(cbdata
->cb
, cb
->func
, cb
->decl
->func
, frame
);
479 memcpy(cbdata
->rval
, retptr
, psi_decl_arg_get_size(cb
->decl
->func
));
482 zend_fcall_info_args_clear(&frame_arg
->ival_ptr
->zend
.cb
->fci
, 0);
483 for (i
= 0; i
< cbdata
->argc
; ++i
) {
484 zval_ptr_dtor(&zargv
[i
]);
489 void psi_call_frame_do_return(struct psi_call_frame
*frame
, zval
*return_value
) {
490 struct psi_return_stmt
*ret
;
492 psi_plist_get(frame
->impl
->stmts
.ret
, 0, &ret
);
493 psi_return_stmt_exec(ret
, return_value
, frame
);
496 void psi_call_frame_do_set(struct psi_call_frame
*frame
) {
498 struct psi_set_stmt
*set
;
500 while (psi_plist_get(frame
->impl
->stmts
.set
, i
++, &set
)) {
501 psi_set_stmt_exec(set
, frame
);
505 void psi_call_frame_do_free(struct psi_call_frame
*frame
) {
507 struct psi_free_stmt
*fre
;
509 while (psi_plist_get(frame
->impl
->stmts
.fre
, i
++, &fre
)) {
510 psi_free_stmt_exec(fre
, frame
);
514 void **psi_call_frame_push_auto_ex(struct psi_call_frame
*frame
, void *auto_free
, void (*dtor
)(void*)) {
515 struct psi_call_frame_auto_free f
;
520 zend_llist_add_element(&frame
->temp
, &f
);
521 return &((struct psi_call_frame_auto_free
*) zend_llist_get_last(&frame
->temp
))->data
;
524 void **psi_call_frame_push_auto(struct psi_call_frame
*frame
, void *auto_free
) {
525 return psi_call_frame_push_auto_ex(frame
, auto_free
, NULL
);
528 static void psi_call_frame_local_auto_dtor(void *auto_list
)
530 zend_llist_destroy(auto_list
);
536 void psi_call_frame_free(struct psi_call_frame
*frame
) {
537 zend_hash_destroy(&frame
->arguments
);
538 zend_hash_destroy(&frame
->symbols
);
539 if (frame
->impl
&& frame
->impl
->func
->static_memory
) {
540 zend_llist
*temp
= emalloc(sizeof(*temp
));
543 memcpy(temp
, &frame
->temp
, sizeof(*temp
));
544 ZVAL_OBJ(&zlocal
, psi_object_init_ex(NULL
, temp
, psi_call_frame_local_auto_dtor
));
545 zend_set_local_var(frame
->impl
->func
->name
, &zlocal
, /* force */ 1);
547 zend_llist_destroy(&frame
->temp
);
549 efree(frame
->pointers
);