Skip to content

Commit 76bb4a4

Browse files
committed
Zend: fix zend_fcc_closure_equals_ex() implementation
Fake closures for internal functions could not return that they were equal due to the zif_handler being wrapped
1 parent d58e95a commit 76bb4a4

3 files changed

Lines changed: 66 additions & 42 deletions

File tree

‎Zend/zend_API.c‎

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4100,51 +4100,15 @@ ZEND_API zend_string *zend_get_callable_name_ex(const zval *callable, const zend
41004100
}
41014101
/* }}} */
41024102

4103-
static bool zend_fcc_function_handler_equals(const zend_function *func1, const zend_function *func2) /* {{{ */
4103+
ZEND_API bool zend_fcc_closure_equals_ex(const zend_fcall_info_cache* a, const zend_fcall_info_cache* b)
41044104
{
4105-
if (func1 == func2) {
4106-
return true;
4107-
}
4108-
4109-
const bool fake_closure1 = (func1->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
4110-
const bool fake_closure2 = (func2->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
4111-
4112-
if (!fake_closure1 && !fake_closure2) {
4113-
return false;
4114-
}
4115-
if (((func1->common.fn_flags & ZEND_ACC_CLOSURE) && !fake_closure1) ||
4116-
((func2->common.fn_flags & ZEND_ACC_CLOSURE) && !fake_closure2)) {
4117-
return false;
4118-
}
4119-
if (func1->type != func2->type ||
4120-
func1->common.scope != func2->common.scope ||
4121-
!zend_string_equals(func1->common.function_name, func2->common.function_name)) {
4122-
return false;
4123-
}
4124-
4125-
if (func1->type == ZEND_USER_FUNCTION) {
4126-
return func1->op_array.opcodes == func2->op_array.opcodes;
4127-
}
4128-
4129-
return func1->internal_function.handler == func2->internal_function.handler;
4130-
}
4131-
/* }}} */
4132-
4133-
ZEND_API bool zend_fcc_closure_equals_ex(const zend_fcall_info_cache* a, const zend_fcall_info_cache* b) /* {{{ */
4134-
{
4135-
const zend_function *func1 = a->function_handler;
4136-
const zend_function *func2 = b->function_handler;
4105+
const zend_function *fn_a = a->function_handler;
4106+
const zend_object *closure_zobj_a = a->closure && a->closure->ce == zend_ce_closure ? a->closure : NULL;
4107+
const zend_function *fn_b = b->function_handler;
4108+
const zend_object *closure_zobj_b = b->closure && b->closure->ce == zend_ce_closure ? b->closure : NULL;
41374109

4138-
if (a->closure && a->closure->ce == zend_ce_closure) {
4139-
func1 = zend_get_closure_method_def(a->closure);
4140-
}
4141-
if (b->closure && b->closure->ce == zend_ce_closure) {
4142-
func2 = zend_get_closure_method_def(b->closure);
4143-
}
4144-
4145-
return zend_fcc_function_handler_equals(func1, func2);
4110+
return zend_fn_closure_equals_ex(fn_a, closure_zobj_a, fn_b, closure_zobj_b);
41464111
}
4147-
/* }}} */
41484112

41494113
ZEND_API bool zend_is_callable_at_frame(
41504114
const zval *callable, zend_object *object, const zend_execute_data *frame,

‎Zend/zend_closures.c‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,61 @@ static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */
535535
}
536536
/* }}} */
537537

538+
ZEND_API bool zend_fn_closure_equals_ex(
539+
const zend_function *fn_a, const zend_object *closure_zobj_a,
540+
const zend_function *fn_b, const zend_object *closure_zobj_b
541+
) {
542+
const zend_closure *closure_a = NULL;
543+
bool is_closure_a_fake = false;
544+
545+
const zend_closure *closure_b = NULL;
546+
bool is_closure_b_fake = false;
547+
548+
if (closure_zobj_a) {
549+
closure_a = (const zend_closure *) closure_zobj_a;
550+
is_closure_a_fake = zend_closure_is_fake(closure_a);
551+
fn_a = &closure_a->func;
552+
}
553+
if (closure_zobj_b) {
554+
closure_b = (const zend_closure *) closure_zobj_b;
555+
is_closure_b_fake = zend_closure_is_fake(closure_b);
556+
fn_b = &closure_b->func;
557+
}
558+
559+
// TODO is this even possible as closure allocates a new zend_function pointer on creation?
560+
if (fn_a == fn_b) {
561+
return true;
562+
}
563+
564+
if (!is_closure_a_fake && !is_closure_b_fake) {
565+
return false;
566+
}
567+
if ((closure_a && !is_closure_a_fake) || (closure_b && !is_closure_b_fake)) {
568+
return false;
569+
}
570+
if (fn_a->type != fn_b->type ||
571+
fn_a->common.scope != fn_b->common.scope ||
572+
!zend_string_equals(fn_a->common.function_name, fn_b->common.function_name)) {
573+
return false;
574+
}
575+
576+
if (fn_a->type == ZEND_USER_FUNCTION) {
577+
return fn_a->op_array.opcodes == fn_b->op_array.opcodes;
578+
} else {
579+
zif_handler zif_a = fn_a->internal_function.handler;
580+
zif_handler zif_b = fn_b->internal_function.handler;
581+
582+
if (closure_a) {
583+
zif_a = closure_a->orig_internal_handler;
584+
}
585+
if (closure_b) {
586+
zif_b = closure_b->orig_internal_handler;
587+
}
588+
589+
return zif_a == zif_b;
590+
}
591+
}
592+
538593
ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {{{ */
539594
{
540595
zend_closure *closure = (zend_closure *)object;

‎Zend/zend_closures.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *obj);
4141
ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj);
4242
ZEND_API zend_object* zend_get_closure_this_ptr(zval *obj);
4343

44+
ZEND_API bool zend_fn_closure_equals_ex(
45+
const zend_function *fn_a, const zend_object *closure_zobj_a,
46+
const zend_function *fn_b, const zend_object *closure_zobj_b
47+
);
48+
4449
END_EXTERN_C()
4550

4651
#endif

0 commit comments

Comments
 (0)