Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 31 additions & 15 deletions ext/ffi/ffi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) /* {{{ */
Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -2884,7 +2901,6 @@ ZEND_METHOD(FFI, cdef) /* {{{ */

FFI_G(symbols) = NULL;
FFI_G(tags) = NULL;

RETURN_OBJ(&ffi->std);
}
/* }}} */
Expand Down
48 changes: 48 additions & 0 deletions ext/ffi/tests/400.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
--TEST--
FFI 400: Call each other type struct of multiple FFI instance
--SKIPIF--
<?php
require_once('skipif.inc');
?>
<?php
try {
FFI::cdef("extern void *zend_printf;");
} catch (Throwable $e) {
die('skip PHP symbols not available');
}
?>
--INI--
ffi.enable=1
--FILE--
<?php
$cdef = '
typedef struct _zend_file_handle {
union {
void* *fp;
void* stream;
} handle;
const char *filename;
void* *opened_path;
void* type;
/* free_filename is used by wincache */
/* TODO: Clean up filename vs opened_path mess */
void* free_filename;
char *buf;
size_t len;
} zend_file_handle;
void zend_stream_init_filename(zend_file_handle *handle, const char *filename);
';

$ffi1 = FFI::cdef($cdef);
$ffi2 = FFI::cdef($cdef);
$c1 = $ffi1->new('zend_file_handle');
try {
$ffi2->zend_stream_init_filename(FFI::addr($c1), __FILE__);
echo 'Success';
} catch (FFI\Exception $e) {
echo $e;
}

?>
--EXPECT--
Success