marshal: array return values
authorMichael Wallner <mike@php.net>
Thu, 26 Jul 2018 07:46:16 +0000 (09:46 +0200)
committerMichael Wallner <mike@php.net>
Thu, 26 Jul 2018 07:46:16 +0000 (09:46 +0200)
src/libffi.c
src/marshal.c
src/types/decl_arg.c
src/types/decl_var.c

index e30966a..99f5c5f 100644 (file)
 # endif
 #endif
 
+struct psi_ffi_context {
+       ffi_cif signature;
+       ffi_type *params[2];
+};
+
+struct psi_ffi_impl_info {
+       struct psi_context *context;
+       struct psi_call_frame *frame;
+
+       void *code;
+       ffi_closure *closure;
+};
+
+struct psi_ffi_callback_info {
+       struct psi_ffi_impl_info *impl_info;
+       struct psi_let_exp *let_exp;
+
+       void *code;
+       ffi_closure *closure;
+};
+
+struct psi_ffi_decl_info {
+       ffi_cif signature;
+       ffi_type *rv_array;
+       ffi_type *params[1];
+};
+
 static void *psi_ffi_closure_alloc(size_t s, void **code)
 {
 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
@@ -110,7 +137,8 @@ static ffi_type *ffi_type_sint128;
 static ffi_type *ffi_type_uint128;
 #endif
 
-static inline ffi_type *psi_ffi_decl_arg_type(struct psi_decl_arg *darg);
+static inline ffi_type *psi_ffi_decl_arg_type(struct psi_decl_arg *darg,
+               struct psi_decl *fn);
 
 static inline ffi_type *psi_ffi_token_type(token_t t) {
        switch (t) {
@@ -173,7 +201,7 @@ static inline ffi_type *psi_ffi_impl_type(token_t impl_type) {
        }
        return NULL;
 }
-static void psi_ffi_struct_type_dtor(void *type) {
+static void psi_ffi_type_dtor(void *type) {
        ffi_type *strct = type;
 
        if (strct->elements) {
@@ -200,77 +228,99 @@ static size_t psi_ffi_struct_type_pad(ffi_type **els, size_t padding) {
        return padding;
 }
 
-static ffi_type **psi_ffi_struct_type_elements(struct psi_decl_struct *strct) {
-       size_t i = 0, argc, nels = 0, offset = 0, maxalign = 0, last_arg_pos = -1;
-       ffi_type **tmp, **els;
-       struct psi_decl_arg *darg;
+struct psi_ffi_struct_element_storage {
+       ffi_type **els;
+       size_t nels;
+       size_t argc;
+       size_t offset;
+       size_t max_align;
+       size_t last_arg_pos;
+};
 
-       argc = psi_plist_count(strct->args);
-       els = calloc(argc + 1, sizeof(*els));
+static inline void psi_ffi_struct_type_element(
+               struct psi_ffi_struct_element_storage *s, struct psi_decl_arg *darg) {
 
-       while (psi_plist_get(strct->args, i++, &darg)) {
-               ffi_type *type;
-               size_t padding;
+       ffi_type *type, **tmp;
+       size_t padding;
 
-               if (darg->layout->pos == last_arg_pos) {
-                       /* skip bit fields */
-                       continue;
-               }
-               last_arg_pos = darg->layout->pos;
+       if (darg->layout->pos == s->last_arg_pos) {
+               /* skip bit fields */
+               return;
+       }
+       s->last_arg_pos = darg->layout->pos;
 
-               type = malloc(sizeof(*type));
-               *type = *psi_ffi_decl_arg_type(darg);
+       type = malloc(sizeof(*type));
+       *type = *psi_ffi_decl_arg_type(darg, NULL);
 
-               if (type->alignment > maxalign) {
-                       maxalign = type->alignment;
-               }
+       if (type->alignment > s->max_align) {
+               s->max_align = type->alignment;
+       }
 
-               assert(type->size <= darg->layout->len);
-               if ((padding = psi_offset_padding(darg->layout->pos - offset, type->alignment))) {
-                       if (nels + padding + 1 > argc) {
-                               argc += padding;
-                               tmp = realloc(els, (argc + 1) * sizeof(*els));
-                               if (tmp) {
-                                       els = tmp;
-                               } else {
-                                       free(els);
-                                       return NULL;
-                               }
-                               els[argc] = NULL;
+       assert(type->size <= darg->layout->len);
+       if ((padding = psi_offset_padding(darg->layout->pos - s->offset, type->alignment))) {
+               if (s->nels + padding + 1 > s->argc) {
+                       s->argc += padding;
+                       tmp = realloc(s->els, (s->argc + 1) * sizeof(*s->els));
+                       if (tmp) {
+                               s->els = tmp;
+                       } else {
+                               free(s->els);
+                               abort();
                        }
-                       psi_ffi_struct_type_pad(&els[nels], padding);
-                       nels += padding;
-                       offset += padding;
+                       s->els[s->argc] = NULL;
                }
-               assert(offset == darg->layout->pos);
+               psi_ffi_struct_type_pad(&s->els[s->nels], padding);
+               s->nels += padding;
+               s->offset += padding;
+       }
+       assert(s->offset == darg->layout->pos);
 
-               offset = (offset + darg->layout->len + type->alignment - 1) & ~(type->alignment - 1);
-               els[nels++] = type;
+       s->offset = (s->offset + darg->layout->len + type->alignment - 1) & ~(type->alignment - 1);
+       s->els[s->nels++] = type;
+}
+
+static ffi_type **psi_ffi_struct_type_elements(struct psi_decl_struct *strct) {
+       size_t i = 0;
+       ffi_type **tmp;
+       struct psi_decl_arg *darg;
+       struct psi_ffi_struct_element_storage s = {0};
+
+       s.last_arg_pos = -1;
+       s.argc = psi_plist_count(strct->args);
+       s.els = calloc(s.argc + 1, sizeof(*s.els));
+
+       while (psi_plist_get(strct->args, i++, &darg)) {
+               psi_ffi_struct_type_element(&s, darg);
        }
 
        /* apply struct alignment padding */
-       offset = (offset + maxalign - 1) & ~(maxalign - 1);
+       s.offset = (s.offset + s.max_align - 1) & ~(s.max_align - 1);
 
-       assert(offset <= strct->size);
-       if (offset < strct->size) {
-               size_t padding = strct->size - offset;
+       assert(s.offset <= strct->size);
+       if (s.offset < strct->size) { /* WTF? */
+               size_t padding = strct->size - s.offset;
 
-               tmp = realloc(els, (padding + argc + 1) * sizeof(*els));
+               tmp = realloc(s.els, (padding + s.argc + 1) * sizeof(*s.els));
                if (tmp) {
-                       els = tmp;
+                       s.els = tmp;
                } else {
-                       free(els);
+                       free(s.els);
                        return NULL;
                }
-               psi_ffi_struct_type_pad(&els[nels], padding);
-               els[argc + padding] = NULL;
+               psi_ffi_struct_type_pad(&s.els[s.nels], padding);
+               s.els[s.argc + padding] = NULL;
        }
 
-       return els;
+       return s.els;
 }
+
 static inline ffi_type *psi_ffi_decl_type(struct psi_decl_type *type) {
        struct psi_decl_type *real = psi_decl_type_get_real(type);
 
+       if (real != type && type->real.def->var->pointer_level) {
+               return &ffi_type_pointer;
+       }
+
        switch (real->type) {
        case PSI_T_STRUCT:
                if (!real->real.strct->engine.type) {
@@ -281,7 +331,7 @@ static inline ffi_type *psi_ffi_decl_type(struct psi_decl_type *type) {
                        strct->elements = psi_ffi_struct_type_elements(real->real.strct);
 
                        real->real.strct->engine.type = strct;
-                       real->real.strct->engine.dtor = psi_ffi_struct_type_dtor;
+                       real->real.strct->engine.dtor = psi_ffi_type_dtor;
                }
 
                return real->real.strct->engine.type;
@@ -290,15 +340,49 @@ static inline ffi_type *psi_ffi_decl_type(struct psi_decl_type *type) {
                {
                        struct psi_decl_arg *arg;
                        psi_plist_get(real->real.unn->args, 0, &arg);
-                       return psi_ffi_decl_arg_type(arg);
+                       return psi_ffi_decl_arg_type(arg, NULL);
                }
 
        default:
-               return psi_ffi_token_type(real->type);
+               break;
        }
+
+       return psi_ffi_token_type(real->type);
+}
+
+static inline ffi_type *psi_ffi_decl_func_array_type(struct psi_decl *fn) {
+       struct psi_ffi_decl_info *info = fn->info;
+       struct psi_ffi_struct_element_storage s = {0};
+       struct psi_layout l;
+       size_t i;
+
+       s.last_arg_pos = -1;
+       s.argc = fn->func->var->array_size;
+       s.els = calloc(s.argc + 1, sizeof(*s.els));
+
+       assert(!fn->func->layout);
+       l.pos = 0;
+       l.len = psi_decl_arg_get_size(fn->func);
+
+       fn->func->layout = &l;
+       psi_ffi_struct_type_element(&s, fn->func);
+       fn->func->layout = NULL;
+
+       info->rv_array = calloc(1, sizeof(ffi_type));
+       info->rv_array->type = FFI_TYPE_STRUCT;
+       info->rv_array->size = 0;
+       info->rv_array->elements = s.els;
+
+       return info->rv_array;
 }
-static inline ffi_type *psi_ffi_decl_arg_type(struct psi_decl_arg *darg) {
+
+static inline ffi_type *psi_ffi_decl_arg_type(struct psi_decl_arg *darg,
+               struct psi_decl *fn) {
        if (darg->var->pointer_level) {
+               if (darg->var->array_size && fn) {
+                       /* mimic a struct resembling the array return type of fn */
+                       return psi_ffi_decl_func_array_type(fn);
+               }
                return &ffi_type_pointer;
        } else {
                return psi_ffi_decl_type(darg->type);
@@ -321,32 +405,6 @@ static inline ffi_abi psi_ffi_abi(const char *convention) {
        return FFI_DEFAULT_ABI;
 }
 
-struct psi_ffi_context {
-       ffi_cif signature;
-       ffi_type *params[2];
-};
-
-struct psi_ffi_impl_info {
-       struct psi_context *context;
-       struct psi_call_frame *frame;
-
-       void *code;
-       ffi_closure *closure;
-};
-
-struct psi_ffi_callback_info {
-       struct psi_ffi_impl_info *impl_info;
-       struct psi_let_exp *let_exp;
-
-       void *code;
-       ffi_closure *closure;
-};
-
-struct psi_ffi_decl_info {
-       ffi_cif signature;
-       ffi_type *params[1];
-};
-
 static inline struct psi_ffi_decl_info *psi_ffi_decl_init(struct psi_decl *decl) {
        if (!decl->info) {
                int rc;
@@ -354,18 +412,19 @@ static inline struct psi_ffi_decl_info *psi_ffi_decl_init(struct psi_decl *decl)
                struct psi_decl_arg *arg;
                struct psi_ffi_decl_info *info = calloc(1, sizeof(*info) + 2 * c * sizeof(void *));
 
+               decl->info = info;
+
                for (i = 0; psi_plist_get(decl->args, i, &arg); ++i) {
-                       info->params[i] = psi_ffi_decl_arg_type(arg);
+                       info->params[i] = psi_ffi_decl_arg_type(arg, NULL);
                }
                info->params[c] = NULL;
 
                rc = ffi_prep_cif(&info->signature, psi_ffi_abi(decl->abi->convention),
-                               c, psi_ffi_decl_arg_type(decl->func), info->params);
+                               c, psi_ffi_decl_arg_type(decl->func, decl), info->params);
 
                if (FFI_OK != rc) {
                        free(info);
-               } else {
-                       decl->info = info;
+                       decl->info = NULL;
                }
        }
 
@@ -374,6 +433,11 @@ static inline struct psi_ffi_decl_info *psi_ffi_decl_init(struct psi_decl *decl)
 
 static inline void psi_ffi_decl_dtor(struct psi_decl *decl) {
        if (decl->info) {
+               struct psi_ffi_decl_info *info = decl->info;
+
+               if (info->rv_array) {
+                       psi_ffi_type_dtor(info->rv_array);
+               }
                free(decl->info);
                decl->info = NULL;
        }
@@ -563,11 +627,18 @@ struct psi_ffi_extvar_info {
        } set;
 };
 
-static inline ffi_status psi_ffi_extvar_init(struct psi_decl_extvar *evar,
-               ffi_type *type) {
+static inline ffi_status psi_ffi_extvar_init(struct psi_decl_extvar *evar) {
        struct psi_ffi_extvar_info *info = calloc(1, sizeof(*info));
+       ffi_type *type;
        ffi_status rc;
 
+       evar->info = info;
+
+       psi_ffi_decl_init(evar->getter);
+       psi_ffi_decl_init(evar->setter);
+
+       type = psi_ffi_decl_arg_type(evar->arg, evar->getter);
+
        rc = ffi_prep_cif(&info->get.signature, FFI_DEFAULT_ABI, 0,
                        type, NULL);
        if (FFI_OK != rc) {
@@ -591,7 +662,6 @@ static inline ffi_status psi_ffi_extvar_init(struct psi_decl_extvar *evar,
                return rc;
        }
 
-       evar->info = info;
        evar->getter->sym = info->get.code;
        evar->setter->sym = info->set.code;
 
@@ -673,9 +743,7 @@ static zend_function_entry *psi_ffi_compile(struct psi_context *C)
        zend_function_entry *zfe = NULL;
 
        while (psi_plist_get(C->vars, v++, &evar)) {
-               ffi_type *type = psi_ffi_decl_arg_type(evar->arg);
-
-               if (FFI_OK == psi_ffi_extvar_init(evar, type)) {
+               if (FFI_OK == psi_ffi_extvar_init(evar)) {
                        /* */
                }
        }
index d48cb8b..834a38f 100644 (file)
@@ -526,7 +526,9 @@ void psi_set_to_string(zval *return_value, struct psi_set_exp *set, impl_val *re
        impl_val *ptr = deref_impl_val(ret_val, var);
        char *str;
 
-       if (var->arg->var->array_size) {
+       /* holy moly, this breaks arrays of pointers to char,
+        * like e.g. tzname */
+       if (var->arg->var->array_size && var->arg->var->pointer_level == 1) {
                str = (char *) ptr;
        } else {
                str = ptr->ptr;
@@ -689,8 +691,8 @@ void psi_set_to_array_counted(zval *return_value, struct psi_set_exp *set, impl_
        count = psi_num_exp_get_long(sub_exp->data.num, frame, NULL);
        psi_plist_get(set->inner, 1, &sub_exp);
 
-       for (ptr = (char *) ret_val; 0 < count--; ptr += size) {
        size = psi_decl_var_get_size(psi_set_exp_get_decl_var(sub_exp));
+       for (ptr = (char *) ret_val; 0 < count--; ptr += size) {
                zval ele;
 
                ZVAL_NULL(&ele);
index 9c57168..32607d1 100644 (file)
@@ -105,9 +105,6 @@ void psi_decl_arg_dump(int fd, struct psi_decl_arg *arg, unsigned level)
                        }
                        dprintf(fd, "))");
                }
-               if (arg->var->array_size) {
-                       dprintf(fd, "[%u]", arg->var->array_size);
-               }
        } else {
                psi_decl_type_dump(fd, arg->type, level);
                dprintf(fd, " ");
@@ -118,7 +115,7 @@ void psi_decl_arg_dump(int fd, struct psi_decl_arg *arg, unsigned level)
 bool psi_decl_arg_validate(struct psi_data *data, struct psi_decl_arg *arg,
                struct psi_validate_scope *scope)
 {
-       if (!psi_decl_type_validate(data, arg->type, arg, scope)) {
+       if (!psi_decl_type_validate(data, arg->type, NULL, scope)) {
                 if (!arg->var->pointer_level) {
                        data->error(data, arg->type->token, PSI_WARNING,
                                        "Cannot use '%s' as type for '%s'%s%s", arg->type->name,
index 7f04127..990b9ae 100644 (file)
@@ -80,7 +80,7 @@ void psi_decl_var_dump(int fd, struct psi_decl_var *var)
        dprintf(fd, "%s%s",
                        psi_t_indirection(var->pointer_level - !!var->array_size),
                        var->name ? var->name : "/**/");
-       if (var->array_size) {
+       if (var->array_size && var->arg->type->type != PSI_T_FUNCTION) {
                dprintf(fd, "[%u]", var->array_size);
        }
 }