mirror of
https://github.com/php/php-src.git
synced 2024-11-23 09:54:15 +08:00
Implement GH-11934: Allow to pass CData into struct and/or union fields
Co-authored-by: KapitanOczywisty <44417092+KapitanOczywisty@users.noreply.github.com> Closes GH-11935.
This commit is contained in:
parent
223fb08819
commit
0b9702c9ed
4
NEWS
4
NEWS
@ -15,6 +15,10 @@ PHP NEWS
|
||||
. Fixed GH-11952 (Confusing warning when blocking entity loading via
|
||||
libxml_set_external_entity_loader). (nielsdos)
|
||||
|
||||
- FFI:
|
||||
. Implement GH-11934 (Allow to pass CData into struct and/or union fields).
|
||||
(nielsdos, KapitanOczywisty)
|
||||
|
||||
- FPM:
|
||||
. Fixed bug #76067 (system() function call leaks php-fpm listening sockets).
|
||||
(Mikhail Galanin, Jakub Zelenka)
|
||||
|
@ -126,6 +126,10 @@ PHP 8.3 UPGRADE NOTES
|
||||
- CLI
|
||||
. It is now possible to lint multiple files.
|
||||
|
||||
- FFI
|
||||
. It is now possible to assign CData to other CData. This means you can
|
||||
now assign CData to structs and fields.
|
||||
|
||||
- Opcache
|
||||
. opcache_get_status()['scripts'][n]['revalidate'] now contains a Unix
|
||||
timestamp of when the next revalidation of the scripts timestamp is due,
|
||||
|
@ -739,6 +739,16 @@ static zend_always_inline zend_result zend_ffi_zval_to_cdata(void *ptr, zend_ffi
|
||||
zend_string *str;
|
||||
zend_ffi_type_kind kind = type->kind;
|
||||
|
||||
/* Pointer type has special handling of CData */
|
||||
if (kind != ZEND_FFI_TYPE_POINTER && Z_TYPE_P(value) == IS_OBJECT && Z_OBJCE_P(value) == zend_ffi_cdata_ce) {
|
||||
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(value);
|
||||
if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type)) &&
|
||||
type->size == ZEND_FFI_TYPE(cdata->type)->size) {
|
||||
memcpy(ptr, cdata->ptr, type->size);
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
again:
|
||||
switch (kind) {
|
||||
case ZEND_FFI_TYPE_FLOAT:
|
||||
@ -848,14 +858,7 @@ again:
|
||||
case ZEND_FFI_TYPE_STRUCT:
|
||||
case ZEND_FFI_TYPE_ARRAY:
|
||||
default:
|
||||
if (Z_TYPE_P(value) == IS_OBJECT && Z_OBJCE_P(value) == zend_ffi_cdata_ce) {
|
||||
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(value);
|
||||
if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type)) &&
|
||||
type->size == ZEND_FFI_TYPE(cdata->type)->size) {
|
||||
memcpy(ptr, cdata->ptr, type->size);
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
/* Incompatible types, because otherwise the CData check at the entry point would've succeeded. */
|
||||
zend_ffi_assign_incompatible(value, type);
|
||||
return FAILURE;
|
||||
}
|
||||
|
169
ext/ffi/tests/gh11934.phpt
Normal file
169
ext/ffi/tests/gh11934.phpt
Normal file
@ -0,0 +1,169 @@
|
||||
--TEST--
|
||||
Feature GH-11934 (Allow to pass CData into struct and/or union fields)
|
||||
--EXTENSIONS--
|
||||
ffi
|
||||
--INI--
|
||||
ffi.enable=1
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$cdef = FFI::cdef();
|
||||
|
||||
echo "--- Primitive types ---\n";
|
||||
|
||||
// Choose integer numbers off the maximum to test copy
|
||||
$types = [
|
||||
'uint8_t' => 200,
|
||||
'uint16_t' => 16000,
|
||||
'uint32_t' => 1000_000,
|
||||
'uint64_t' => PHP_INT_MAX - 100,
|
||||
'int8_t' => -100,
|
||||
'int16_t' => -16000,
|
||||
'int32_t' => -1000_000,
|
||||
'int64_t' => PHP_INT_MIN + 100,
|
||||
'char' => 'F',
|
||||
'bool' => false,
|
||||
'float' => 1.00,
|
||||
'double' => -1.00,
|
||||
];
|
||||
|
||||
// Positive test
|
||||
foreach ($types as $type => $value) {
|
||||
$source = $cdef->new($type);
|
||||
$source->cdata = $value;
|
||||
$struct = $cdef->new("struct { $type field; }");
|
||||
$struct->field = $source;
|
||||
echo "Positive test $type: ";
|
||||
var_dump($struct->field === $value);
|
||||
}
|
||||
|
||||
// Negative test
|
||||
$dummy = $cdef->new("int[2]");
|
||||
foreach ($types as $type => $value) {
|
||||
$struct = $cdef->new("struct { int field; }");
|
||||
$struct->field = $dummy;
|
||||
}
|
||||
|
||||
// Enum test
|
||||
$enum_definition = "enum { A, B, C, D }";
|
||||
$source = $cdef->new($enum_definition);
|
||||
$source->cdata = 2;
|
||||
$struct = $cdef->new("struct { $enum_definition field; }");
|
||||
$struct->field = $source;
|
||||
echo "Positive test enum: ";
|
||||
var_dump($struct->field === $source->cdata);
|
||||
$struct->field = $dummy;
|
||||
|
||||
echo "--- Struct ---\n";
|
||||
|
||||
// Nested structs
|
||||
$cdef = FFI::cdef("
|
||||
struct inner_struct {
|
||||
int data[1];
|
||||
};
|
||||
struct my_struct {
|
||||
int foo;
|
||||
int bar;
|
||||
struct inner_struct inner;
|
||||
};
|
||||
struct my_nesting_struct {
|
||||
struct my_struct field;
|
||||
};");
|
||||
$source = $cdef->new("struct my_struct");
|
||||
$source->foo = 123;
|
||||
$source->bar = 456;
|
||||
$source->inner->data[0] = 789;
|
||||
$struct = $cdef->new("struct my_nesting_struct");
|
||||
$struct->field = $source;
|
||||
var_dump($struct->field->foo === 123 && $struct->field->bar === 456 && $struct->field->inner->data[0] === 789);
|
||||
|
||||
echo "--- Callback return type ---\n";
|
||||
|
||||
$ffi = FFI::cdef('
|
||||
typedef uint32_t (*test_callback)();
|
||||
typedef struct {
|
||||
test_callback call_me;
|
||||
} my_struct;
|
||||
');
|
||||
|
||||
$struct = $ffi->new('my_struct');
|
||||
$struct->call_me = function () use ($ffi) {
|
||||
$int = $ffi->new('uint32_t');
|
||||
$int->cdata = 42;
|
||||
return $int;
|
||||
};
|
||||
|
||||
var_dump(($struct->call_me)());
|
||||
|
||||
echo "--- Other FFI\CData assignment ---\n";
|
||||
|
||||
$ffi = FFI::cdef('');
|
||||
|
||||
$source = $ffi->new('uint32_t');
|
||||
$source->cdata = 123;
|
||||
$target = $ffi->new('uint32_t');
|
||||
$target->cdata = $source;
|
||||
|
||||
var_dump($target->cdata);
|
||||
|
||||
echo "--- Array element ---\n";
|
||||
|
||||
$ffi = FFI::cdef('');
|
||||
|
||||
$source = $ffi->new('uint32_t');
|
||||
$source->cdata = 123;
|
||||
$target = $ffi->new('uint32_t[1]');
|
||||
$target[0] = $source;
|
||||
|
||||
var_dump($target[0]);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
--- Primitive types ---
|
||||
Positive test uint8_t: bool(true)
|
||||
Positive test uint16_t: bool(true)
|
||||
Positive test uint32_t: bool(true)
|
||||
Positive test uint64_t: bool(true)
|
||||
Positive test int8_t: bool(true)
|
||||
Positive test int16_t: bool(true)
|
||||
Positive test int32_t: bool(true)
|
||||
Positive test int64_t: bool(true)
|
||||
Positive test char: bool(true)
|
||||
Positive test bool: bool(true)
|
||||
Positive test float: bool(true)
|
||||
Positive test double: bool(true)
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
Positive test enum: bool(true)
|
||||
|
||||
Warning: Object of class FFI\CData could not be converted to int in %s on line %d
|
||||
--- Struct ---
|
||||
bool(true)
|
||||
--- Callback return type ---
|
||||
int(42)
|
||||
--- Other FFI\CData assignment ---
|
||||
int(123)
|
||||
--- Array element ---
|
||||
int(123)
|
33
ext/ffi/tests/gh11934b.phpt
Normal file
33
ext/ffi/tests/gh11934b.phpt
Normal file
@ -0,0 +1,33 @@
|
||||
--TEST--
|
||||
Feature GH-11934 (Allow to pass CData into C variables)
|
||||
--EXTENSIONS--
|
||||
ffi
|
||||
zend_test
|
||||
--FILE--
|
||||
<?php
|
||||
require_once __DIR__ . '/utils.inc';
|
||||
$header = <<<HEADER
|
||||
extern int gh11934b_ffi_var_test_cdata;
|
||||
HEADER;
|
||||
|
||||
if (PHP_OS_FAMILY !== 'Windows') {
|
||||
$ffi = FFI::cdef($header);
|
||||
} else {
|
||||
try {
|
||||
$ffi = FFI::cdef($header, 'php_zend_test.dll');
|
||||
} catch (FFI\Exception $ex) {
|
||||
$ffi = FFI::cdef($header, ffi_get_php_dll_name());
|
||||
}
|
||||
}
|
||||
|
||||
$ffi->gh11934b_ffi_var_test_cdata->cdata = 2;
|
||||
var_dump($ffi->gh11934b_ffi_var_test_cdata);
|
||||
$source = $ffi->new('int');
|
||||
$source->cdata = 31;
|
||||
$ffi->gh11934b_ffi_var_test_cdata = $source;
|
||||
var_dump($ffi->gh11934b_ffi_var_test_cdata);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
int(2)
|
||||
int(31)
|
@ -1233,6 +1233,8 @@ PHP_ZEND_TEST_API void bug_gh9090_void_int_char_var(int i, char *fmt, ...) {
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
PHP_ZEND_TEST_API int gh11934b_ffi_var_test_cdata;
|
||||
|
||||
#ifdef HAVE_COPY_FILE_RANGE
|
||||
/**
|
||||
* This function allows us to simulate early return of copy_file_range by setting the limit_copy_file_range ini setting.
|
||||
|
Loading…
Reference in New Issue
Block a user