diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 0696512342ea2..fbcc439e4958f 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -170,6 +170,8 @@ typedef struct _zend_ffi { #define ZEND_FFI_SIZEOF_ARG \ MAX(FFI_SIZEOF_ARG, sizeof(double)) +#define ZEND_FFI_NO_ARG_OFFSET ((uint32_t)-1) + typedef struct _zend_ffi_cdata { zend_object std; zend_ffi_type *type; @@ -209,9 +211,9 @@ static ZEND_FUNCTION(ffi_trampoline); static ZEND_COLD void zend_ffi_return_unsupported(zend_ffi_type *type); static ZEND_COLD void zend_ffi_pass_unsupported(zend_ffi_type *type); static ZEND_COLD void zend_ffi_assign_incompatible(zval *arg, zend_ffi_type *type); - +static void zend_ffi_type_hash_dtor(zval *zv); #if FFI_CLOSURES -static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value); +static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value, uint32_t n); #endif static zend_always_inline void zend_ffi_type_dtor(zend_ffi_type *type) /* {{{ */ @@ -264,6 +266,11 @@ static int zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *s if (dst_type->kind == ZEND_FFI_TYPE_VOID || src_type->kind == ZEND_FFI_TYPE_VOID) { return 1; + } else if(dst_type->kind == ZEND_FFI_TYPE_STRUCT && + src_type->kind == ZEND_FFI_TYPE_STRUCT) { + if(strcmp(ZSTR_VAL(dst_type->record.tag_name), ZSTR_VAL(src_type->record.tag_name)) == 0) { + return 1; + } } } else if (dst_type->kind == ZEND_FFI_TYPE_ARRAY && (dst_type->array.length == src_type->array.length || @@ -512,6 +519,7 @@ static zend_always_inline void zend_ffi_cdata_to_zval(zend_ffi_cdata *cdata, voi ZVAL_STRING(rv, *(char**)ptr); return; } + if (!cdata) { if (is_ret) { cdata = zend_ffi_cdata_to_zval_slow_ret(ptr, type, flags); @@ -745,7 +753,7 @@ static zend_always_inline int zend_ffi_zval_to_cdata(void *ptr, zend_ffi_type *t } #if FFI_CLOSURES } else if (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_FUNC) { - void *callback = zend_ffi_create_callback(ZEND_FFI_TYPE(type->pointer.type), value); + void *callback = zend_ffi_create_callback(ZEND_FFI_TYPE(type->pointer.type), value, ZEND_FFI_NO_ARG_OFFSET); if (callback) { *(void**)ptr = callback; @@ -883,34 +891,43 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v } /* }}} */ -static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ */ +static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value, uint32_t n) /* {{{ */ { zend_fcall_info_cache fcc; char *error = NULL; + char *invalid_error = "an"; + char *occur_error = ""; uint32_t arg_count; void *code; void *callback; zend_ffi_callback_data *callback_data; - if (type->attr & ZEND_FFI_ATTR_VARIADIC) { - zend_throw_error(zend_ffi_exception_ce, "Variadic function closures are not supported"); + if(n != ZEND_FFI_NO_ARG_OFFSET) { + zend_spprintf(&occur_error, 0, ", parameter %u occur", n + 1); + zend_spprintf(&invalid_error, 0, "parameter %u to be", n + 1); + } + + if(type->attr & ZEND_FFI_ATTR_VARIADIC) { + zend_throw_error(zend_ffi_exception_ce, "Variadic function closures are not supported%s", occur_error); return NULL; } if (!zend_is_callable_ex(value, NULL, 0, NULL, &fcc, &error)) { - zend_throw_error(zend_ffi_exception_ce, "Attempt to assign an invalid callback, %s", error); + zend_throw_error(zend_ffi_exception_ce, "Attempt to assign %s invalid callback, %s", invalid_error, error); return NULL; } + + arg_count = type->func.args ? zend_hash_num_elements(type->func.args) : 0; if (arg_count < fcc.function_handler->common.required_num_args) { - zend_throw_error(zend_ffi_exception_ce, "Attempt to assign an invalid callback, insufficient number of arguments"); + zend_throw_error(zend_ffi_exception_ce, "Attempt to assign %s invalid callback, insufficient number of arguments", invalid_error); return NULL; } callback = ffi_closure_alloc(sizeof(ffi_closure), &code); if (!callback) { - zend_throw_error(zend_ffi_exception_ce, "Cannot allocate callback"); + zend_throw_error(zend_ffi_exception_ce, "Cannot allocate callback%s", occur_error); return NULL; } @@ -946,14 +963,14 @@ static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ * } if (ffi_prep_cif(&callback_data->cif, type->func.abi, callback_data->arg_count, callback_data->ret_type, callback_data->arg_types) != FFI_OK) { - zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback CIF"); + zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback CIF%s", occur_error); efree(callback_data); ffi_closure_free(callback); return NULL; } if (ffi_prep_closure_loc(callback, &callback_data->cif, zend_ffi_callback_trampoline, callback_data, code) != FFI_OK) { - zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback"); + zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback%s", occur_error); efree(callback_data); ffi_closure_free(callback); return NULL; @@ -2459,7 +2476,6 @@ static int zend_ffi_pass_arg(zval *arg, zend_ffi_type *type, ffi_type **pass_typ return SUCCESS; } else if (Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) { zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg); - if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type))) { if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) { if (!cdata->ptr) { @@ -2474,7 +2490,7 @@ static int zend_ffi_pass_arg(zval *arg, zend_ffi_type *type, ffi_type **pass_typ } #if FFI_CLOSURES } else if (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_FUNC) { - void *callback = zend_ffi_create_callback(ZEND_FFI_TYPE(type->pointer.type), arg); + void *callback = zend_ffi_create_callback(ZEND_FFI_TYPE(type->pointer.type), arg, n); if (callback) { *(void**)arg_values[n] = callback; @@ -2679,12 +2695,12 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */ ret = do_alloca(MAX(ret_type->size, sizeof(ffi_arg)), ret_use_heap); ffi_call(&cif, addr, ret, arg_values); - for (n = 0; n < arg_count; n++) { if (arg_types[n]->type == FFI_TYPE_STRUCT) { efree(arg_types[n]); } } + if (ret_type->type == FFI_TYPE_STRUCT) { efree(ret_type); } @@ -2834,6 +2850,7 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ FFI_G(symbols) = NULL; FFI_G(tags) = NULL; + if (code) { /* Parse C definitions */ FFI_G(default_type_attr) = ZEND_FFI_ATTR_STORED; @@ -2884,7 +2901,6 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ FFI_G(symbols) = NULL; FFI_G(tags) = NULL; - RETURN_OBJ(&ffi->std); } /* }}} */ diff --git a/ext/ffi/tests/400.phpt b/ext/ffi/tests/400.phpt new file mode 100644 index 0000000000000..3b1e381cca9d7 --- /dev/null +++ b/ext/ffi/tests/400.phpt @@ -0,0 +1,48 @@ +--TEST-- +FFI 400: Call each other type struct of multiple FFI instance +--SKIPIF-- + + +--INI-- +ffi.enable=1 +--FILE-- +new('zend_file_handle'); +try { + $ffi2->zend_stream_init_filename(FFI::addr($c1), __FILE__); + echo 'Success'; +} catch (FFI\Exception $e) { + echo $e; +} + +?> +--EXPECT-- +Success