+
+void exec_let_func_arrval_inner(struct psi_let_func *func,
+ struct psi_decl_arg *darg, struct psi_decl_arg *inner_decl_arg,
+ struct psi_call_frame_argument *frame_arg,
+ struct psi_let_exp *inner_let_exp, void *container,
+ struct psi_call_frame *frame)
+{
+ struct psi_let_func *inner_func = psi_let_exp_get_func(inner_let_exp);
+ struct psi_impl_var *inner_var = inner_func->var;
+
+ if (psi_decl_type_get_real(darg->type)->type == PSI_T_UNION) {
+ /* skip if there's no entry in the outer_zval;
+ * we only want to set supplied data on unions
+ */
+ if (!zend_symtable_str_exists(Z_ARRVAL_P(frame_arg->zval_ptr),
+ &inner_var->name[1], strlen(&inner_var->name[1]))) {
+ return;
+ }
+ }
+ psi_call_frame_sub_argument(frame, inner_var, frame_arg->zval_ptr,
+ inner_func->var->fqn);
+
+ /* example from dm_store/dbm_fetch with arrval($string) conversion:
+ let key = arrval($key,
+ dptr = strval($0),
+ dsize = strlen($0)
+ );
+ # ---
+ darg = key
+ frame_arg = $key
+ inner_var = $0
+ full_func_var_name = $key.0
+ inner_decl_arg = key.dptr
+ */
+ psi_let_exp_exec(inner_let_exp, inner_decl_arg,
+ ((char *) container) + inner_decl_arg->layout->pos,
+ inner_decl_arg->layout->len, frame);
+}
+
+static void *exec_let_func_arrval(struct psi_let_exp *val,
+ struct psi_let_func *func, struct psi_decl_arg *darg,
+ struct psi_call_frame *frame);
+
+void exec_let_func_arrval_seq(struct psi_let_func *func,
+ struct psi_decl_arg *darg, struct psi_decl_type *darg_type,
+ struct psi_call_frame_argument *frame_arg,
+ struct psi_let_exp *inner_let_exp, void *container,
+ struct psi_call_frame *frame)
+{
+ zval *zval_ptr;
+ psi_marshal_let let_fn;
+ size_t i = 0, size;
+ struct psi_decl_var *dvar;
+
+ if (inner_let_exp->var) {
+ /* arrval($foo, *foo = intval($foo)); */
+ dvar = inner_let_exp->var;
+ } else {
+ /* arrval($foo, intval($foo)); */
+ dvar = psi_decl_var_copy(darg->var);
+ assert(dvar->pointer_level);
+ --dvar->pointer_level;
+ }
+
+ dvar->pointer_level += inner_let_exp->is_reference;
+ size = psi_decl_var_get_size(dvar);
+ dvar->pointer_level -= inner_let_exp->is_reference;
+
+ let_fn = locate_let_func_fn(inner_let_exp->data.func->type);
+
+ ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(frame_arg->zval_ptr), zval_ptr)
+ {
+ void *temp = NULL;
+ impl_val val = {0}, *ptr, *sub;
+
+ if (let_fn) {
+ ptr = let_fn(&val, darg_type, 0, NULL, zval_ptr, &temp);
+ if (temp) {
+ psi_call_frame_push_auto(frame, temp);
+ }
+ } else if (func->type == PSI_T_ARRVAL) {
+ ptr = exec_let_func_arrval(inner_let_exp,
+ inner_let_exp->data.func, darg, frame);
+ } else {
+ assert(0);
+ }
+
+ sub = deref_impl_val(ptr, dvar);
+
+ memcpy(&((char *) container)[size * i++], &sub, size);
+ }
+ ZEND_HASH_FOREACH_END();
+
+ if (dvar != inner_let_exp->var) {
+ psi_decl_var_free(&dvar);
+ }
+}
+
+static void *exec_let_func_arrval(struct psi_let_exp *val,
+ struct psi_let_func *func, struct psi_decl_arg *darg,
+ struct psi_call_frame *frame)
+{
+ void *container = NULL;
+ struct psi_call_frame_argument *frame_arg;
+ struct psi_decl_type *darg_type;
+ struct psi_plist *darg_members;
+
+ darg_members = psi_decl_type_get_args(darg->type, &darg_type);
+ frame_arg = psi_call_frame_get_argument(frame, func->var->fqn);
+
+ if (frame_arg->zval_ptr && Z_TYPE_P(frame_arg->zval_ptr) != IS_ARRAY) {
+ convert_to_array(frame_arg->zval_ptr);
+ }
+
+ if (darg_members && func->inner) {
+ /* struct or union
+ * arrval($foo,
+ * str = strval($str),
+ * num = intval($num));
+ */
+ size_t i = 0, size;
+ struct psi_let_exp *inner;
+ struct psi_decl_arg *darg_member;
+
+ val->var->pointer_level += val->is_reference;
+ size = psi_decl_var_get_size(val->var);
+ container = ecalloc(1, size);
+ val->var->pointer_level -= val->is_reference;
+
+ if (frame_arg->zval_ptr) {
+ while (psi_plist_get(func->inner, i++, &inner)) {
+ darg_member = psi_decl_arg_get_by_name(darg_members,
+ psi_let_exp_get_decl_var_name(inner));
+
+ exec_let_func_arrval_inner(func, darg, darg_member, frame_arg,
+ inner, container, frame);
+ }
+ }
+ } else if (func->inner) {
+ /* array
+ * arrval($foo, foo = intval($foo))
+ */
+ size_t arcount;
+ struct psi_let_exp *inner;
+
+ if (!frame_arg->zval_ptr) {
+ return NULL;
+ }
+ assert(psi_plist_count(func->inner) == 1);
+
+ arcount = zend_array_count(Z_ARRVAL_P(frame_arg->zval_ptr));
+ psi_plist_get(func->inner, 0, &inner);
+
+ inner->var->pointer_level += inner->is_reference;
+ container = ecalloc(arcount + 1, psi_decl_var_get_size(inner->var));
+ inner->var->pointer_level -= inner->is_reference;
+
+ exec_let_func_arrval_seq(func, darg, darg_type, frame_arg, inner,
+ container, frame);
+ } else {
+ assert(0);
+ }
+
+ return *psi_call_frame_push_auto(frame, container);
+}
+
+void *psi_let_func_exec(struct psi_let_exp *val, struct psi_let_func *func,
+ struct psi_decl_arg *darg, struct psi_call_frame *frame)
+{
+ struct psi_call_frame_symbol *frame_sym;
+ psi_marshal_let let_fn = locate_let_func_fn(func->type);
+
+ frame_sym = psi_call_frame_fetch_symbol(frame, val->var);
+
+ if (let_fn) {
+ void *temp = NULL;
+ struct psi_call_frame_argument *iarg;
+
+ iarg = psi_call_frame_get_argument(frame, func->var->fqn);
+
+ assert(iarg);
+
+ frame_sym->ival_ptr = let_fn(&frame_sym->temp_val,
+ psi_decl_type_get_real(darg->type),
+ iarg->spec ? iarg->spec->type->type : 0, iarg->ival_ptr,
+ iarg->zval_ptr, &temp);
+ if (temp) {
+ psi_call_frame_push_auto(frame, temp);
+ }
+ } else if (func->type == PSI_T_ARRVAL) {
+ frame_sym->ival_ptr = exec_let_func_arrval(val, func, darg, frame);
+ }
+
+ return frame_sym->ival_ptr;
+}