Skip to content

Commit 5729c32

Browse files
committed
Add consumed_args support for fcall arguments
1 parent 1fa8c0f commit 5729c32

16 files changed

+248
-7
lines changed

UPGRADING.INTERNALS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES
8383
. The INI_ORIG_{INT|STR|FLT|BOOL}() macros have been removed as they are
8484
unused. If this behaviour is required fall back to the zend_ini_*
8585
functions.
86+
. Added zend_fcall_info.consumed_args together with
87+
zend_fci_consumed_arg(), which allows moving a selected callback argument
88+
instead of copying it in zend_call_function(). Currently only a single
89+
consumed argument is supported.
8690

8791
========================
8892
2. Build system changes

Zend/zend_API.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4240,6 +4240,7 @@ ZEND_API zend_result zend_fcall_info_init(const zval *callable, uint32_t check_f
42404240
fci->param_count = 0;
42414241
fci->params = NULL;
42424242
fci->named_params = NULL;
4243+
fci->consumed_args = 0;
42434244

42444245
return SUCCESS;
42454246
}

Zend/zend_API.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ typedef struct _zend_fcall_info {
4949
zval *params;
5050
zend_object *object;
5151
uint32_t param_count;
52+
uint32_t consumed_args;
5253
/* This hashtable can also contain positional arguments (with integer keys),
5354
* which will be appended to the normal params[]. This makes it easier to
5455
* integrate APIs like call_user_func_array(). The usual restriction that
@@ -340,6 +341,13 @@ typedef struct _zend_fcall_info_cache {
340341
#define ZEND_FCI_INITIALIZED(fci) ((fci).size != 0)
341342
#define ZEND_FCC_INITIALIZED(fcc) ((fcc).function_handler != NULL)
342343

344+
static zend_always_inline uint32_t zend_fci_consumed_arg(uint32_t arg_index) {
345+
return arg_index < 32 ? (UINT32_C(1) << arg_index) : UINT32_C(0);
346+
}
347+
static zend_always_inline bool zend_fci_is_consumed_arg(uint32_t consumed_args, uint32_t arg_index) {
348+
return arg_index < 32 && (consumed_args & (UINT32_C(1) << arg_index));
349+
}
350+
343351
ZEND_API int zend_next_free_module(void);
344352

345353
BEGIN_EXTERN_C()

Zend/zend_closures.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ ZEND_METHOD(Closure, call)
155155
fci_cache.object = fci.object = newobj;
156156

157157
fci.size = sizeof(fci);
158+
fci.consumed_args = 0;
158159
ZVAL_OBJ(&fci.function_name, &closure->std);
159160
ZVAL_UNDEF(&closure_result);
160161
fci.retval = &closure_result;

Zend/zend_execute_API.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,7 @@ zend_result _call_user_function_impl(zval *object, zval *function_name, zval *re
796796
fci.param_count = param_count;
797797
fci.params = params;
798798
fci.named_params = named_params;
799+
fci.consumed_args = 0;
799800

800801
return zend_call_function(&fci, NULL);
801802
}
@@ -863,6 +864,9 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
863864

864865
call = zend_vm_stack_push_call_frame(call_info,
865866
func, fci->param_count, object_or_called_scope);
867+
uint32_t consumed_args = fci->param_count ? fci->consumed_args : 0;
868+
869+
ZEND_ASSERT((consumed_args & (consumed_args - 1)) == 0);
866870

867871
for (uint32_t i = 0; i < fci->param_count; i++) {
868872
zval *param = ZEND_CALL_ARG(call, i+1);
@@ -904,7 +908,15 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
904908
}
905909

906910
if (EXPECTED(!must_wrap)) {
907-
ZVAL_COPY(param, arg);
911+
if (EXPECTED(consumed_args == 0)
912+
|| !zend_fci_is_consumed_arg(consumed_args, i)
913+
|| Z_ISREF_P(arg)
914+
|| arg != &fci->params[i]) {
915+
ZVAL_COPY(param, arg);
916+
} else {
917+
ZVAL_COPY_VALUE(param, arg);
918+
ZVAL_UNDEF(arg);
919+
}
908920
} else {
909921
Z_TRY_ADDREF_P(arg);
910922
ZVAL_NEW_REF(param, arg);
@@ -1090,6 +1102,7 @@ ZEND_API void zend_call_known_function(
10901102
fci.param_count = param_count;
10911103
fci.params = params;
10921104
fci.named_params = named_params;
1105+
fci.consumed_args = 0;
10931106
ZVAL_UNDEF(&fci.function_name); /* Unused */
10941107

10951108
fcic.function_handler = fn;

ext/dom/xpath_callbacks.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ static zend_result php_dom_xpath_callback_dispatch(php_dom_xpath_callbacks *xpat
408408
fci.param_count = param_count;
409409
fci.params = params;
410410
fci.named_params = NULL;
411+
fci.consumed_args = 0;
411412
ZVAL_STRINGL(&fci.function_name, function_name, function_name_length);
412413

413414
zend_call_function(&fci, NULL);

ext/ffi/ffi.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,7 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v
950950
fci.object = NULL;
951951
fci.param_count = callback_data->arg_count;
952952
fci.named_params = NULL;
953+
fci.consumed_args = 0;
953954

954955
if (callback_data->type->func.args) {
955956
int n = 0;

ext/zend_test/test.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,61 @@ static ZEND_FUNCTION(zend_call_method_if_exists)
548548
}
549549
}
550550

551+
static ZEND_FUNCTION(zend_test_call_with_consumed_args)
552+
{
553+
zend_fcall_info fci = empty_fcall_info;
554+
zend_fcall_info_cache fcc = empty_fcall_info_cache;
555+
zval *args;
556+
zend_long consumed_args;
557+
zval retval;
558+
uint32_t actual_consumed_args = 0;
559+
uint32_t i;
560+
zend_result call_result;
561+
562+
ZEND_PARSE_PARAMETERS_START(3, 3)
563+
Z_PARAM_FUNC(fci, fcc)
564+
Z_PARAM_ARRAY(args)
565+
Z_PARAM_LONG(consumed_args)
566+
ZEND_PARSE_PARAMETERS_END();
567+
568+
if (UNEXPECTED(consumed_args < 0 || consumed_args > UINT32_MAX)) {
569+
zend_argument_value_error(3, "must be between 0 and 4294967295");
570+
RETURN_THROWS();
571+
}
572+
573+
zend_fcall_info_args(&fci, args);
574+
575+
ZVAL_UNDEF(&retval);
576+
fci.retval = &retval;
577+
fci.consumed_args = (uint32_t) consumed_args;
578+
579+
call_result = zend_call_function(&fci, &fcc);
580+
581+
for (i = 0; i < fci.param_count && i < 32; i++) {
582+
if (Z_ISUNDEF(fci.params[i])) {
583+
actual_consumed_args |= (1u << i);
584+
}
585+
}
586+
587+
zend_fcall_info_args_clear(&fci, true);
588+
589+
if (call_result == FAILURE || EG(exception)) {
590+
if (!Z_ISUNDEF(retval)) {
591+
zval_ptr_dtor(&retval);
592+
}
593+
RETURN_THROWS();
594+
}
595+
596+
array_init(return_value);
597+
add_assoc_long(return_value, "consumed_args", actual_consumed_args);
598+
599+
if (Z_ISUNDEF(retval)) {
600+
add_assoc_null(return_value, "retval");
601+
} else {
602+
add_assoc_zval(return_value, "retval", &retval);
603+
}
604+
}
605+
551606
static ZEND_FUNCTION(zend_get_unit_enum)
552607
{
553608
ZEND_PARSE_PARAMETERS_NONE();

ext/zend_test/test.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,8 @@ function zend_object_init_with_constructor(string $class, mixed ...$args): mixed
303303

304304
function zend_call_method_if_exists(object $obj, string $method, mixed ...$args): mixed {}
305305

306+
function zend_test_call_with_consumed_args(callable $cb, array $args, int $consumed_args): array {}
307+
306308
function zend_test_zend_ini_parse_quantity(string $str): int {}
307309
function zend_test_zend_ini_parse_uquantity(string $str): int {}
308310

ext/zend_test/test_arginfo.h

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)