@@ -5335,11 +5335,64 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
53355335 return FAILURE ;
53365336 }
53375337
5338+ /* Bail out if callback is not a first class callable */
53385339 zend_ast * callback = args -> child [0 ];
53395340 if (callback -> kind != ZEND_AST_CALL && callback -> kind != ZEND_AST_STATIC_CALL ) {
53405341 return FAILURE ;
53415342 }
53425343
5344+ zend_ast * args_ast = zend_ast_call_get_args (callback );
5345+ if (args_ast -> kind != ZEND_AST_CALLABLE_CONVERT ) {
5346+ return FAILURE ;
5347+ }
5348+
5349+ /* PFAs with non-literal pre-bound arguments are not optimizable because we
5350+ * can't memoize arguments without breaking pass-by-reference.
5351+ * TODO: Support PFAs when the function is known to not receive by ref. */
5352+ zend_ast_fcc * fcc = (zend_ast_fcc * )args_ast ;
5353+ zend_ast_list * fcc_args = zend_ast_get_list (fcc -> args );
5354+ for (uint32_t i = 0 ; i < fcc_args -> children ; i ++ ) {
5355+ zend_ast * arg = fcc_args -> child [i ];
5356+ if (arg -> kind == ZEND_AST_NAMED_ARG ) {
5357+ arg = arg -> child [1 ];
5358+ }
5359+
5360+ if (arg -> kind == ZEND_AST_PLACEHOLDER_ARG ) {
5361+ continue ;
5362+ }
5363+
5364+ if (arg -> kind != ZEND_AST_ZVAL ) {
5365+ return FAILURE ;
5366+ }
5367+ }
5368+
5369+ /* Evaluate class name */
5370+ znode class_node ;
5371+ if (callback -> kind == ZEND_AST_STATIC_CALL ) {
5372+ znode result ;
5373+ zend_compile_expr (& result , callback -> child [0 ]);
5374+ if (result .op_type == IS_CONST || result .op_type == IS_TMP_VAR ) {
5375+ class_node = result ;
5376+ } else {
5377+ class_node = result ;
5378+ zend_emit_op_tmp (& class_node , ZEND_QM_ASSIGN , & result , NULL );
5379+ }
5380+ } else {
5381+ class_node .op_type = IS_UNUSED ;
5382+ }
5383+
5384+ /* Evaluate function name */
5385+ znode func_node ;
5386+ {
5387+ znode result ;
5388+ zend_compile_expr (& result , callback -> child [callback -> kind == ZEND_AST_CALL ? 0 : 1 ]);
5389+ if (result .op_type == IS_CONST || result .op_type == IS_TMP_VAR ) {
5390+ func_node = result ;
5391+ } else {
5392+ zend_emit_op_tmp (& func_node , ZEND_QM_ASSIGN , & result , NULL );
5393+ }
5394+ }
5395+
53435396 znode value ;
53445397 value .op_type = IS_TMP_VAR ;
53455398 value .u .op .var = get_temporary_variable ();
@@ -5348,6 +5401,12 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
53485401 zend_ast_create_znode (& value ));
53495402 if (!call_args ) {
53505403 CG (active_op_array )-> T -- ;
5404+ if (func_node .op_type == IS_CONST ) {
5405+ zval_ptr_dtor_nogc (& func_node .u .constant );
5406+ }
5407+ if (class_node .op_type == IS_CONST ) {
5408+ zval_ptr_dtor_nogc (& class_node .u .constant );
5409+ }
53515410 /* The callback is not a FCC/PFA, or is not optimizable */
53525411 return FAILURE ;
53535412 }
@@ -5387,14 +5446,35 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
53875446
53885447 /* loop body */
53895448 znode call_result ;
5449+ zend_ast * func_ast ;
5450+ if (func_node .op_type == IS_CONST ) {
5451+ func_ast = zend_ast_create_znode (& func_node );
5452+ } else {
5453+ znode copy_node ;
5454+ zend_emit_op_tmp (& copy_node , ZEND_COPY_TMP , & func_node , NULL );
5455+ func_ast = zend_ast_create_znode (& copy_node );
5456+ }
53905457 switch (callback -> kind ) {
5391- case ZEND_AST_CALL :
5392- zend_compile_expr (& call_result , zend_ast_create (ZEND_AST_CALL , callback -> child [ 0 ] , call_args ));
5458+ case ZEND_AST_CALL : {
5459+ zend_compile_expr (& call_result , zend_ast_create (ZEND_AST_CALL , func_ast , call_args ));
53935460 break ;
5394- case ZEND_AST_STATIC_CALL :
5395- zend_compile_expr (& call_result , zend_ast_create (ZEND_AST_STATIC_CALL , callback -> child [0 ], callback -> child [1 ], call_args ));
5461+ }
5462+ case ZEND_AST_STATIC_CALL : {
5463+ zend_ast * class_ast ;
5464+ if (class_node .op_type == IS_CONST ) {
5465+ class_ast = zend_ast_create_znode (& class_node );
5466+ } else {
5467+ znode copy_node ;
5468+ zend_emit_op_tmp (& copy_node , ZEND_COPY_TMP , & class_node , NULL );
5469+ class_ast = zend_ast_create_znode (& copy_node );
5470+ }
5471+
5472+ zend_compile_expr (& call_result , zend_ast_create (ZEND_AST_STATIC_CALL , class_ast , func_ast , call_args ));
5473+ zend_ast_destroy (class_ast );
53965474 break ;
5475+ }
53975476 }
5477+ zend_ast_destroy (func_ast );
53985478 opline = zend_emit_op (NULL , ZEND_ADD_ARRAY_ELEMENT , & call_result , & key );
53995479 SET_NODE (opline -> result , result );
54005480 /* end loop body */
@@ -5409,6 +5489,12 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
54095489
54105490 zend_end_loop (opnum_fetch , & reset_node );
54115491 zend_emit_op (NULL , ZEND_FE_FREE , & reset_node , NULL );
5492+ if (func_node .op_type != IS_CONST ) {
5493+ zend_emit_op (NULL , ZEND_FREE , & func_node , NULL );
5494+ }
5495+ if (class_node .op_type != IS_UNUSED && class_node .op_type != IS_CONST ) {
5496+ zend_emit_op (NULL , ZEND_FREE , & class_node , NULL );
5497+ }
54125498
54135499 return SUCCESS ;
54145500}
0 commit comments