Add first-class callables

Support acquiring a Closure to a callable using the syntax
func(...), $obj->method(...), etc. This is essentially a
shortcut for Closure::fromCallable().

RFC: https://wiki.php.net/rfc/first_class_callable_syntax

Closes GH-7019.

Co-Authored-By: Nikita Popov <nikita.ppv@gmail.com>
This commit is contained in:
Joe Watkins 2021-05-20 08:14:56 +02:00 committed by Nikita Popov
parent 42cb5b5fa8
commit d0b09a7be4
40 changed files with 1078 additions and 515 deletions

View file

@ -35543,6 +35543,24 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FUNC_GET_ARGS_SPEC_UNUSED_UNUS
ZEND_VM_NEXT_OPCODE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALLABLE_CONVERT_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_execute_data *call = EX(call);
zend_closure_from_frame(EX_VAR(opline->result.var), call);
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
OBJ_RELEASE(Z_OBJ(call->This));
}
EX(call) = call->prev_execute_data;
zend_vm_stack_free_call_frame(call);
ZEND_VM_NEXT_OPCODE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -53549,6 +53567,7 @@ ZEND_API void execute_ex(zend_execute_data *ex)
(void*)&&ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED_LABEL,
(void*)&&ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_LABEL,
(void*)&&ZEND_VERIFY_NEVER_TYPE_SPEC_UNUSED_UNUSED_LABEL,
(void*)&&ZEND_CALLABLE_CONVERT_SPEC_UNUSED_UNUSED_LABEL,
(void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL,
(void*)&&ZEND_JMP_FORWARD_SPEC_LABEL,
(void*)&&ZEND_NULL_LABEL,
@ -57783,6 +57802,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_FUNC_GET_ARGS_SPEC_UNUSED_UNUSED)
ZEND_FUNC_GET_ARGS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_CALLABLE_CONVERT_SPEC_UNUSED_UNUSED):
VM_TRACE(ZEND_CALLABLE_CONVERT_SPEC_UNUSED_UNUSED)
ZEND_CALLABLE_CONVERT_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CV):
VM_TRACE(ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CV)
ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -61589,6 +61612,7 @@ void zend_vm_init(void)
ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED_HANDLER,
ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_HANDLER,
ZEND_VERIFY_NEVER_TYPE_SPEC_UNUSED_UNUSED_HANDLER,
ZEND_CALLABLE_CONVERT_SPEC_UNUSED_UNUSED_HANDLER,
ZEND_RECV_NOTYPE_SPEC_HANDLER,
ZEND_JMP_FORWARD_SPEC_HANDLER,
ZEND_NULL_HANDLER,
@ -62697,7 +62721,8 @@ void zend_vm_init(void)
2546,
2547,
2548,
3452
2549,
3453
};
#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)
zend_opcode_handler_funcs = labels;
@ -62870,7 +62895,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2551 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2552 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@ -62878,7 +62903,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2576 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2577 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@ -62886,7 +62911,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2601 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2602 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@ -62897,17 +62922,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2626 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 2627 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2651 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 2652 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2676 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 2677 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_MUL:
@ -62918,17 +62943,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2701 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2702 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2726 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2727 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2751 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2752 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_IDENTICAL:
@ -62939,14 +62964,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2776 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2777 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2851 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2852 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) {
spec = 3076 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 3077 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_NOT_IDENTICAL:
@ -62957,14 +62982,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2926 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2927 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3001 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 3002 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) {
spec = 3081 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 3082 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_EQUAL:
@ -62975,12 +63000,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2776 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2777 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2851 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2852 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_NOT_EQUAL:
@ -62991,12 +63016,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2926 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2927 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3001 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 3002 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_SMALLER:
@ -63004,12 +63029,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3086 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 3087 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3161 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 3162 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_IS_SMALLER_OR_EQUAL:
@ -63017,74 +63042,74 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3236 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 3237 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3311 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 3312 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_QM_ASSIGN:
if (op1_info == MAY_BE_LONG) {
spec = 3398 | SPEC_RULE_OP1;
spec = 3399 | SPEC_RULE_OP1;
} else if (op1_info == MAY_BE_DOUBLE) {
spec = 3403 | SPEC_RULE_OP1;
spec = 3404 | SPEC_RULE_OP1;
} else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) {
spec = 3408 | SPEC_RULE_OP1;
spec = 3409 | SPEC_RULE_OP1;
}
break;
case ZEND_PRE_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3386 | SPEC_RULE_RETVAL;
spec = 3387 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
spec = 3388 | SPEC_RULE_RETVAL;
spec = 3389 | SPEC_RULE_RETVAL;
}
break;
case ZEND_PRE_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3390 | SPEC_RULE_RETVAL;
spec = 3391 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
spec = 3392 | SPEC_RULE_RETVAL;
spec = 3393 | SPEC_RULE_RETVAL;
}
break;
case ZEND_POST_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3394;
} else if (op1_info == MAY_BE_LONG) {
spec = 3395;
} else if (op1_info == MAY_BE_LONG) {
spec = 3396;
}
break;
case ZEND_POST_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3396;
} else if (op1_info == MAY_BE_LONG) {
spec = 3397;
} else if (op1_info == MAY_BE_LONG) {
spec = 3398;
}
break;
case ZEND_JMP:
if (OP_JMP_ADDR(op, op->op1) > op) {
spec = 2550;
spec = 2551;
}
break;
case ZEND_RECV:
if (op->op2.num == MAY_BE_ANY) {
spec = 2549;
spec = 2550;
}
break;
case ZEND_SEND_VAL:
if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) {
spec = 3448;
spec = 3449;
}
break;
case ZEND_SEND_VAR_EX:
if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
spec = 3443 | SPEC_RULE_OP1;
spec = 3444 | SPEC_RULE_OP1;
}
break;
case ZEND_FE_FETCH_R:
if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) {
spec = 3450 | SPEC_RULE_RETVAL;
spec = 3451 | SPEC_RULE_RETVAL;
}
break;
case ZEND_FETCH_DIM_R:
@ -63092,17 +63117,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3413 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3414 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_SEND_VAL_EX:
if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) {
spec = 3449;
spec = 3450;
}
break;
case ZEND_SEND_VAR:
if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
spec = 3438 | SPEC_RULE_OP1;
spec = 3439 | SPEC_RULE_OP1;
}
break;
case ZEND_BW_OR: