X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;f=src%2Fcall.c;fp=src%2Fcall.c;h=46a1dde419cf32876cb591e3aeb03dcdf8f49dff;hb=9bcb1df0786a8193d65949c857baaba2f4296e84;hp=0000000000000000000000000000000000000000;hpb=5e240548ba570610ce0dbc248a2b7654a0e080fa;p=m6w6%2Fext-psi diff --git a/src/call.c b/src/call.c new file mode 100644 index 0000000..46a1dde --- /dev/null +++ b/src/call.c @@ -0,0 +1,545 @@ +/******************************************************************************* + Copyright (c) 2016, Michael Wallner . + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#include "php_psi_stdinc.h" +#include "context.h" +#include "data.h" +#include "call.h" + +#include "php.h" +#include "zend_exceptions.h" + +struct psi_call_frame_argument *psi_call_frame_argument_init(struct psi_impl_arg *spec, + impl_val *ival, zval *zptr, int is_vararg) { + struct psi_call_frame_argument *frame_arg = ecalloc(1, sizeof(*frame_arg)); + + + if ((frame_arg->zval_ptr = zptr)) { + ZVAL_DEREF(frame_arg->zval_ptr); + + /* use userland type if the declared type of the vararg is mixed */ + if (is_vararg) { + if (spec->type->type == PSI_T_MIXED) { + switch (Z_TYPE_P(zptr)) { + case IS_TRUE: + frame_arg->va_type = PSI_T_BOOL; + ival->zend.bval = 1; + break; + case IS_FALSE: + frame_arg->va_type = PSI_T_BOOL; + ival->zend.bval = 0; + break; + case IS_LONG: + frame_arg->va_type = PSI_T_INT; + ival->zend.lval = Z_LVAL_P(zptr); + break; + case IS_DOUBLE: + frame_arg->va_type = PSI_T_FLOAT; + ival->dval = Z_DVAL_P(zptr); + break; + default: + frame_arg->va_type = PSI_T_STRING; + ival->zend.str = zval_get_string(zptr); + break; + } + } else { + frame_arg->va_type = spec->type->type; + } + } + } + + frame_arg->ival = *ival; + frame_arg->ival_ptr = &frame_arg->ival; + frame_arg->spec = spec; + + return frame_arg; +} + +void psi_call_frame_argument_free(struct psi_call_frame_argument *arg) { + switch (arg->spec->type->type) { + case PSI_T_STRING: + if (arg->ival.zend.str) { + zend_string_release(arg->ival.zend.str); + } + break; + case PSI_T_CALLABLE: + if (arg->ival.zend.cb) { + if (arg->ival.zend.cb->fci.size) { + zend_fcall_info_args_clear(&arg->ival.zend.cb->fci, 1); + } + efree(arg->ival.zend.cb); + } + break; + default: + break; + } + if (arg->ival_ptr && arg->ival_ptr != &arg->temp_val && arg->ival_ptr != &arg->ival) { + efree(arg->ival_ptr); + } + efree(arg); +} + +struct psi_call_frame_symbol *psi_call_frame_symbol_init(struct psi_decl_var *dvar) { + struct psi_call_frame_symbol *frame_sym; + size_t size = psi_decl_type_get_size(dvar->arg->type, NULL); + + frame_sym = ecalloc(1, sizeof(*frame_sym) + size); + frame_sym->ptr = frame_sym->ival_ptr = &frame_sym->temp_val; + + return frame_sym; +} + +void psi_call_frame_symbol_free(struct psi_call_frame_symbol *sym) { + efree(sym); +} + +static void psi_call_frame_free_argument(zval *zptr) { + psi_call_frame_argument_free(Z_PTR_P(zptr)); +} + +static void psi_call_frame_free_symbol(zval *zptr) { + psi_call_frame_symbol_free(Z_PTR_P(zptr)); +} + +struct psi_call_frame_auto_free { + void *data; + void (*dtor)(void *); +}; + +static void psi_call_frame_free_temp(void *ptr) { + struct psi_call_frame_auto_free *f = ptr; + + if (f->data) { + if (f->dtor) { + f->dtor(&f->data); + } else { + efree(f->data); + } + } +} + +struct psi_call_frame *psi_call_frame_init(struct psi_context *C, struct psi_decl *decl, struct psi_impl *impl) { + struct psi_call_frame *frame = ecalloc(1, sizeof(*frame)); + + frame->context = C; + frame->decl = decl; + frame->impl = impl; + + zend_hash_init(&frame->arguments, 8, NULL, psi_call_frame_free_argument, 0); + zend_hash_init(&frame->symbols, 8, NULL, psi_call_frame_free_symbol, 0); + zend_llist_init(&frame->temp, sizeof(struct psi_call_frame_auto_free), psi_call_frame_free_temp, 0); + + return frame; +} + +struct psi_call_frame_symbol *psi_call_frame_fetch_symbol( + struct psi_call_frame *frame, struct psi_decl_var *dvar) { + struct psi_call_frame_symbol *frame_sym; + + frame_sym = zend_hash_str_find_ptr(&frame->symbols, dvar->fqn, strlen(dvar->fqn)); + if (!frame_sym) { + frame_sym = zend_hash_str_add_ptr(&frame->symbols, dvar->fqn, strlen(dvar->fqn), + psi_call_frame_symbol_init(dvar)); + } + return frame_sym; +} + +zval *psi_call_frame_new_argument(struct psi_call_frame *frame, + struct psi_call_frame_argument *frame_arg) { + if (frame_arg->va_type) { + /* varargs are just appended with numeric indices */ + return zend_hash_next_index_insert_ptr(&frame->arguments, frame_arg); + } else { + return zend_hash_str_add_ptr(&frame->arguments, + frame_arg->spec->var->name, strlen(frame_arg->spec->var->name), + frame_arg); + } +} + +zval *psi_call_frame_sub_argument(struct psi_call_frame *frame, + struct psi_impl_var *inner_var, zval *outer_zval, const char *name) { + struct psi_call_frame_argument *iarg; + zval *inner_zval = zend_symtable_str_find(Z_ARRVAL_P(outer_zval), + &inner_var->name[1], strlen(&inner_var->name[1])); + + if (!inner_zval) { + zval empty_zval; + + ZVAL_NULL(&empty_zval); + inner_zval = zend_symtable_str_update(Z_ARRVAL_P(outer_zval), + &inner_var->name[1], strlen(&inner_var->name[1]), + &empty_zval); + } + + iarg = psi_call_frame_get_argument(frame, name); + + if (!iarg) { + struct psi_call_frame_argument *frame_arg; + impl_val empty_val = {0}; + struct psi_impl_arg *carg_spec = psi_impl_arg_init( + psi_impl_type_init(PSI_T_MIXED, "mixed"), + psi_impl_var_copy(inner_var), NULL); + + psi_call_frame_push_auto_ex(frame, carg_spec, (void(*)(void*)) psi_impl_arg_free); + frame_arg = psi_call_frame_argument_init(carg_spec, &empty_val, inner_zval, 0); + zend_hash_str_add_ptr(&frame->arguments, name, strlen(name), frame_arg); + } + + return inner_zval; +} + +struct psi_call_frame_argument *psi_call_frame_get_argument( + struct psi_call_frame *frame, const char *name) { + return zend_hash_str_find_ptr(&frame->arguments, name, strlen(name)); +} + +size_t psi_call_frame_num_var_args(struct psi_call_frame *frame) { + return zend_hash_next_free_element(&frame->arguments); +} + +size_t psi_call_frame_num_fixed_args(struct psi_call_frame *frame) { + return psi_plist_count(frame->decl->args); +} + +struct psi_call_frame_argument *psi_call_frame_get_var_argument( + struct psi_call_frame *frame, zend_long index) { + return zend_hash_index_find_ptr(&frame->arguments, index); +} + +void **psi_call_frame_get_arg_pointers(struct psi_call_frame *frame) { + return frame->pointers; +} + +ZEND_RESULT_CODE psi_call_frame_parse_args(struct psi_call_frame *frame, + zend_execute_data *execute_data) { + size_t i, argc = psi_plist_count(frame->impl->func->args); + zend_error_handling zeh; + + zend_replace_error_handling(EH_THROW, zend_exception_get_default(), &zeh); + + if (!argc) { + ZEND_RESULT_CODE rv; + + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh); + return rv; + } + + ZEND_PARSE_PARAMETERS_START( + psi_impl_num_min_args(frame->impl), + frame->impl->func->vararg ? -1 : argc + ) + nextarg: { + + struct psi_impl_arg *iarg; + impl_val ival = {0}; + + if (frame->impl->func->vararg && _i >= argc) { + iarg = frame->impl->func->vararg; + Z_PARAM_OPTIONAL; + } else { + psi_plist_get(frame->impl->func->args, _i, &iarg); + if (iarg->def) { + Z_PARAM_OPTIONAL; + } + } + + if (PSI_T_BOOL == iarg->type->type) { + Z_PARAM_BOOL(ival.zend.bval); + } else if (PSI_T_INT == iarg->type->type) { + Z_PARAM_LONG(ival.zend.lval); + } else if (PSI_T_FLOAT == iarg->type->type || PSI_T_DOUBLE == iarg->type->type) { + Z_PARAM_DOUBLE(ival.dval); + } else if (PSI_T_STRING == iarg->type->type) { + Z_PARAM_STR_EX(ival.zend.str, 1, iarg->var->reference); + if (ival.zend.str) { + zend_string_addref(ival.zend.str); + } + } else if (PSI_T_ARRAY == iarg->type->type) { + zval *tmp; + Z_PARAM_ARRAY_EX(tmp, _optional || iarg->var->reference, + iarg->var->reference); + } else if (PSI_T_OBJECT == iarg->type->type) { + Z_PARAM_PROLOGUE(0); + } else if (PSI_T_MIXED == iarg->type->type) { + Z_PARAM_PROLOGUE(0); + } else if (PSI_T_CALLABLE == iarg->type->type) { + zend_fcall_info fci; + zend_fcall_info_cache fcc; + + Z_PARAM_FUNC_EX(fci, fcc, 1, 0); + + if (fci.size) { + ival.zend.cb = ecalloc(1, sizeof(zend_fcall)); + ival.zend.cb->fci = fci; + ival.zend.cb->fcc = fcc; + } + } else { + error_code = ZPP_ERROR_FAILURE; + break; + } + + psi_call_frame_new_argument(frame, + psi_call_frame_argument_init(iarg, &ival, _arg, _i > argc)); + + if (_i < _num_args) { + goto nextarg; + } + } + ZEND_PARSE_PARAMETERS_END_EX( + zend_restore_error_handling(&zeh); + return FAILURE; + ); + + /* set up defaults */ + for (i = EX_NUM_ARGS(); i < argc; ++i) { + struct psi_impl_arg *iarg; + + psi_plist_get(frame->impl->func->args, i, &iarg); + + assert(iarg->def); + psi_call_frame_new_argument(frame, psi_call_frame_argument_init(iarg, + &iarg->def->ival, NULL, 0)); + } + + zend_restore_error_handling(&zeh); + return SUCCESS; +} + +void psi_call_frame_enter(struct psi_call_frame *frame) { + size_t argc = psi_call_frame_num_fixed_args(frame); + size_t va_count = psi_call_frame_num_var_args(frame); + size_t rsize = psi_decl_arg_get_size(frame->decl->func); + struct psi_call_frame_symbol *rv_sym; + + /* initialize ffi argument array */ + frame->pointers = ecalloc(argc + va_count + 1, sizeof(void *)); + + /* initialize return value symbol */ + rv_sym = psi_call_frame_fetch_symbol(frame, frame->decl->func->var); + if (rsize > sizeof(impl_val)) { + rv_sym->ival_ptr = ecalloc(1, rsize); + } else { + rv_sym->ival_ptr = &rv_sym->temp_val; + } + frame->rpointer = rv_sym->ptr = rv_sym->ival_ptr; +} + +ZEND_RESULT_CODE psi_call_frame_do_let(struct psi_call_frame *frame) { + size_t i; + struct psi_let_stmt *let; + struct psi_decl_arg *arg; + size_t argc = psi_call_frame_num_fixed_args(frame); + size_t va_count = psi_call_frame_num_var_args(frame); + + for (i = 0; psi_plist_get(frame->impl->stmts.let, i, &let); ++i) { + psi_let_stmt_exec(let, frame); + } + for (i = 0; psi_plist_get(frame->decl->args, i, &arg); ++i) { + struct psi_let_stmt *let; + struct psi_call_frame_symbol *frame_sym; + + let = psi_impl_get_let(frame->impl, arg->var); + frame_sym = psi_call_frame_fetch_symbol(frame, let->exp->var); + frame->pointers[i] = frame_sym->ptr; + } + /* varargs */ + if (va_count) { + for (i = 0; i < va_count; ++i) { + struct psi_call_frame_argument *frame_arg; + psi_marshal_let let_fn; + void *temp = NULL; + + frame_arg = psi_call_frame_get_var_argument(frame, i); + switch (frame_arg->va_type) { + case PSI_T_BOOL: let_fn = psi_let_boolval; break; + case PSI_T_INT: let_fn = psi_let_intval; break; + case PSI_T_FLOAT: + case PSI_T_DOUBLE: let_fn = psi_let_floatval; break; + case PSI_T_STRING: let_fn = psi_let_strval; break; + default: + assert(0); + } + + frame_arg->ival_ptr = let_fn(&frame_arg->temp_val, NULL, frame_arg->va_type, + &frame_arg->ival, frame_arg->zval_ptr, &temp); + if (temp) { + psi_call_frame_push_auto(frame, temp); + } + + frame->pointers[argc + i] = frame_arg->ival_ptr; + } + } + + return SUCCESS; +} + +void psi_call_frame_do_call(struct psi_call_frame *frame) { + size_t va_count = psi_call_frame_num_var_args(frame); + + if (va_count) { + void **va_types = ecalloc(va_count, sizeof(void *)); + size_t i; + + for (i = 0; i < va_count; ++i) { + struct psi_call_frame_argument *frame_arg; + + frame_arg = psi_call_frame_get_var_argument(frame, i); + va_types[i] = frame->context->ops->query(frame->context, + PSI_CONTEXT_QUERY_TYPE, &frame_arg->va_type); + } + + frame->context->ops->call_va(frame->context, + frame, + frame->decl, + frame->rpointer, + frame->pointers, + va_count, + va_types); + + efree(va_types); + } else { + frame->context->ops->call(frame->context, + frame, + frame->decl, + frame->rpointer, + frame->pointers); + } +} + +void psi_call_frame_do_callback(struct psi_call_frame *frame, struct psi_call_frame_callback *cbdata) +{ + size_t i; + void *retptr; + ZEND_RESULT_CODE rc; + struct psi_let_callback *cb = cbdata->cb->data.callback; + zval return_value, *zargv = ecalloc(cbdata->argc, sizeof(*zargv)); + struct psi_call_frame_argument *frame_arg; + + assert(cbdata->argc == psi_plist_count(cb->decl->args)); + + /* prepare args for the userland call */ + for (i = 0; i < cbdata->argc; ++i) { + struct psi_set_exp *set_exp; + struct psi_decl_var *set_var; + struct psi_call_frame_symbol *set_sym; + + psi_plist_get(cb->args, i, &set_exp); + set_var = psi_set_exp_get_decl_var(set_exp); + set_sym = psi_call_frame_fetch_symbol(frame, set_var); + + set_sym->ptr = cbdata->argv[i]; + psi_set_exp_exec_ex(set_exp, &zargv[i], set_sym->ptr, frame); + } + + frame_arg = psi_call_frame_get_argument(frame, cb->func->var->fqn); + + /* callback into userland */ + ZVAL_UNDEF(&return_value); + zend_fcall_info_argp(&frame_arg->ival_ptr->zend.cb->fci, cbdata->argc, zargv); + rc = zend_fcall_info_call(&frame_arg->ival_ptr->zend.cb->fci, + &frame_arg->ival_ptr->zend.cb->fcc, &return_value, NULL); + assert(rc == SUCCESS); + + /* marshal return value of the userland call */ + frame_arg->zval_ptr = &return_value; + retptr = psi_let_func_exec(cbdata->cb, cb->func, cb->decl->func, frame); + memcpy(cbdata->rval, retptr, psi_decl_arg_get_size(cb->decl->func)); + + /* cleanup */ + zend_fcall_info_args_clear(&frame_arg->ival_ptr->zend.cb->fci, 0); + for (i = 0; i < cbdata->argc; ++i) { + zval_ptr_dtor(&zargv[i]); + } + efree(zargv); +} + +void psi_call_frame_do_return(struct psi_call_frame *frame, zval *return_value) { + struct psi_return_stmt *ret; + + psi_plist_get(frame->impl->stmts.ret, 0, &ret); + psi_return_stmt_exec(ret, return_value, frame); +} + +void psi_call_frame_do_set(struct psi_call_frame *frame) { + size_t i = 0; + struct psi_set_stmt *set; + + while (psi_plist_get(frame->impl->stmts.set, i++, &set)) { + psi_set_stmt_exec(set, frame); + } +} + +void psi_call_frame_do_free(struct psi_call_frame *frame) { + size_t i = 0; + struct psi_free_stmt *fre; + + while (psi_plist_get(frame->impl->stmts.fre, i++, &fre)) { + psi_free_stmt_exec(fre, frame); + } +} + +void **psi_call_frame_push_auto_ex(struct psi_call_frame *frame, void *auto_free, void (*dtor)(void*)) { + struct psi_call_frame_auto_free f; + + f.data = auto_free; + f.dtor = dtor; + + zend_llist_add_element(&frame->temp, &f); + return &((struct psi_call_frame_auto_free *) zend_llist_get_last(&frame->temp))->data; +} + +void **psi_call_frame_push_auto(struct psi_call_frame *frame, void *auto_free) { + return psi_call_frame_push_auto_ex(frame, auto_free, NULL); +} + +static void psi_call_frame_local_auto_dtor(void *auto_list) +{ + zend_llist_destroy(auto_list); + efree(auto_list); +} + +#include "php_psi.h" + +void psi_call_frame_free(struct psi_call_frame *frame) { + zend_hash_destroy(&frame->arguments); + zend_hash_destroy(&frame->symbols); + if (frame->impl && frame->impl->func->static_memory) { + zend_llist *temp = emalloc(sizeof(*temp)); + zval zlocal; + + memcpy(temp, &frame->temp, sizeof(*temp)); + ZVAL_OBJ(&zlocal, psi_object_init_ex(NULL, temp, psi_call_frame_local_auto_dtor)); + zend_set_local_var_str(frame->impl->func->name, + strlen(frame->impl->func->name), &zlocal, /* force */ 1); + } else { + zend_llist_destroy(&frame->temp); + } + efree(frame->pointers); + efree(frame); +} + + +