Skip to content

Commit e920ca8

Browse files
committed
Add consumed_args support for fcall arguments
1 parent dcf6533 commit e920ca8

16 files changed

Lines changed: 248 additions & 7 deletions

UPGRADING.INTERNALS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES
9696
and ZEND_PARSE_PARAMS_THROW have been removed due to being misleading,
9797
since ZPP always throws, unless ZEND_PARSE_PARAMS_QUIET is given. Use
9898
the non-throw versions.
99+
. Added zend_fcall_info.consumed_args together with
100+
zend_fci_consumed_arg(), which allows moving a selected callback argument
101+
instead of copying it in zend_call_function(). Currently only a single
102+
consumed argument is supported.
99103

100104
========================
101105
2. Build system changes

Zend/zend_API.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4243,6 +4243,7 @@ ZEND_API zend_result zend_fcall_info_init(const zval *callable, uint32_t check_f
42434243
fci->param_count = 0;
42444244
fci->params = NULL;
42454245
fci->named_params = NULL;
4246+
fci->consumed_args = 0;
42464247

42474248
return SUCCESS;
42484249
}

Zend/zend_API.h

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

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

346354
BEGIN_EXTERN_C()

Zend/zend_closures.c

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

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

Zend/zend_execute_API.c

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

799800
return zend_call_function(&fci, NULL);
800801
}
@@ -862,6 +863,9 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
862863

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

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

905909
if (EXPECTED(!must_wrap)) {
906-
ZVAL_COPY(param, arg);
910+
if (EXPECTED(consumed_args == 0)
911+
|| !zend_fci_is_consumed_arg(consumed_args, i)
912+
|| Z_ISREF_P(arg)
913+
|| arg != &fci->params[i]) {
914+
ZVAL_COPY(param, arg);
915+
} else {
916+
ZVAL_COPY_VALUE(param, arg);
917+
ZVAL_UNDEF(arg);
918+
}
907919
} else {
908920
Z_TRY_ADDREF_P(arg);
909921
ZVAL_NEW_REF(param, arg);
@@ -1092,6 +1104,7 @@ ZEND_API void zend_call_known_function(
10921104
fci.param_count = param_count;
10931105
fci.params = params;
10941106
fci.named_params = named_params;
1107+
fci.consumed_args = 0;
10951108
ZVAL_UNDEF(&fci.function_name); /* Unused */
10961109

10971110
fcic.function_handler = fn;

ext/dom/xpath_callbacks.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ static zend_result php_dom_xpath_callback_dispatch(php_dom_xpath_callbacks *xpat
406406
fci.param_count = param_count;
407407
fci.params = params;
408408
fci.named_params = NULL;
409+
fci.consumed_args = 0;
409410
ZVAL_STRINGL(&fci.function_name, function_name, function_name_length);
410411

411412
zend_call_function(&fci, NULL);

ext/ffi/ffi.c

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

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

ext/zend_test/test.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,61 @@ static ZEND_FUNCTION(zend_call_method_if_exists)
545545
}
546546
}
547547

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

ext/zend_test/test.stub.php

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

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

308+
function zend_test_call_with_consumed_args(callable $cb, array $args, int $consumed_args): array {}
309+
308310
function zend_test_zend_ini_parse_quantity(string $str): int {}
309311
function zend_test_zend_ini_parse_uquantity(string $str): int {}
310312

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)