Skip to content
Open
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
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ PHP NEWS
- FFI:
. Fixed bug GH-24582 (Memory leaks in ext/ffi parser when finalize_type()
bails out). (matyapiro31, David Carlier)
. Fixed calling conventions inside a nested declarator, such as
"void (__stdcall *f)(void)", being ignored or rejected. (Nicolas Grekas)

- FPM:
. Fixed bug GH-24229 (fcgi_close() passes shutdown() a literal 1, which is
Expand Down
16 changes: 16 additions & 0 deletions ext/ffi/ffi.c
Original file line number Diff line number Diff line change
Expand Up @@ -6497,6 +6497,22 @@ void zend_ffi_make_func_type(zend_ffi_dcl *dcl, HashTable *args, zend_ffi_dcl *n
}
}

/* The calling convention can be inside the parentheses, as in "void (__stdcall *f)(void)" */
if (nested_dcl->abi != ZEND_FFI_ABI_DEFAULT) {
if (dcl->abi != ZEND_FFI_ABI_DEFAULT) {
zend_ffi_cleanup_dcl(nested_dcl);
zend_ffi_cleanup_dcl(dcl);
if (args) {
zend_hash_destroy(args);
pefree(args, FFI_G(persistent));
}
zend_ffi_parser_error("Multiple calling convention specifiers at line %d", FFI_G(line));
return;
}
dcl->abi = nested_dcl->abi;
nested_dcl->abi = ZEND_FFI_ABI_DEFAULT;
}

#ifdef HAVE_FFI_VECTORCALL_PARTIAL
if (dcl->abi == ZEND_FFI_ABI_VECTORCALL && args) {
zend_ulong i;
Expand Down
5 changes: 5 additions & 0 deletions ext/ffi/ffi.g
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,11 @@ nested_declarator_start:
| "__attribute"
| "__attribute__"
| "__declspec"
| "__cdecl"
| "__stdcall"
| "__fastcall"
| "__thiscall"
| "__vectorcall"
| "*"
| "("
| "["
Expand Down
15 changes: 15 additions & 0 deletions ext/ffi/ffi_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -1807,6 +1807,21 @@ static int check_nested_declarator_start(int sym) {
case YY___DECLSPEC:
sym = get_sym();
break;
case YY___CDECL:
sym = get_sym();
break;
case YY___STDCALL:
sym = get_sym();
break;
case YY___FASTCALL:
sym = get_sym();
break;
case YY___THISCALL:
sym = get_sym();
break;
case YY___VECTORCALL:
sym = get_sym();
break;
case YY__STAR:
sym = get_sym();
break;
Expand Down
63 changes: 63 additions & 0 deletions ext/ffi/tests/callconv_nested_declarator.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
--TEST--
FFI: calling convention inside a nested declarator
--EXTENSIONS--
ffi
--INI--
ffi.enable=1
--FILE--
<?php
// The calling convention must apply to the function type whether it is
// written before the parentheses, as in "void __stdcall (*f)(void)",
// or inside them, as in "void (__stdcall *f)(void)"
$cases = [
'declarator' => [
'typedef void %s(%s *F)(int);',
'F',
fn (FFI\CType $type) => $type->getPointerType(),
],
'parameter declarator' => [
'typedef void F(void %s(%s *cb)(int));',
'F*',
fn (FFI\CType $type) => $type->getPointerType()->getFuncParameterType(0)->getPointerType(),
],
'abstract declarator' => [
'',
'void %s(%s *)(int)',
fn (FFI\CType $type) => $type->getPointerType(),
],
];

function func_abi(array $case, string $before, string $inside): int|string {
[$decl, $type, $func] = $case;
try {
$ffi = FFI::cdef(sprintf($decl, $before, $inside));
$type = $ffi->type(sprintf($type, $before, $inside));
return $func($type)->getFuncABI();
} catch (FFI\ParserException $e) {
return $e->getMessage();
}
}

foreach ($cases as $name => $case) {
echo "$name:";
foreach (['__cdecl', '__stdcall', '__fastcall', '__thiscall', '__vectorcall', '__attribute__((fastcall))'] as $callconv) {
$expected = func_abi($case, "$callconv ", '');
$actual = func_abi($case, '', "$callconv ");
if ($expected !== $actual) {
echo "\n $callconv: expected ", var_export($expected, true), ", got ", var_export($actual, true);
}
}
echo " done\n";
}

try {
FFI::cdef('typedef void __cdecl (__cdecl *F)(int);');
} catch (FFI\ParserException $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
declarator: done
parameter declarator: done
abstract declarator: done
Multiple calling convention specifiers at line 1
Loading