@@ -5301,11 +5301,64 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
53015301 return FAILURE ;
53025302 }
53035303
5304+ /* Bail out if callback is not an FCC */
53045305 zend_ast * callback = args -> child [0 ];
53055306 if (callback -> kind != ZEND_AST_CALL && callback -> kind != ZEND_AST_STATIC_CALL ) {
53065307 return FAILURE ;
53075308 }
53085309
5310+ zend_ast * args_ast = zend_ast_call_get_args (callback );
5311+ if (args_ast -> kind != ZEND_AST_CALLABLE_CONVERT ) {
5312+ return FAILURE ;
5313+ }
5314+
5315+ /* PFAs with non-literal pre-bound arguments are not optimizable because we
5316+ * can't memoize arguments without breaking pass-by-reference.
5317+ * TODO: Support PFAs when the function is known to not receive by ref. */
5318+ zend_ast_fcc * fcc = (zend_ast_fcc * )args_ast ;
5319+ zend_ast_list * fcc_args = zend_ast_get_list (fcc -> args );
5320+ for (uint32_t i = 0 ; i < fcc_args -> children ; i ++ ) {
5321+ zend_ast * arg = fcc_args -> child [i ];
5322+ if (arg -> kind == ZEND_AST_NAMED_ARG ) {
5323+ arg = arg -> child [1 ];
5324+ }
5325+
5326+ if (arg -> kind == ZEND_AST_PLACEHOLDER_ARG ) {
5327+ continue ;
5328+ }
5329+
5330+ if (arg -> kind != ZEND_AST_ZVAL ) {
5331+ return FAILURE ;
5332+ }
5333+ }
5334+
5335+ /* Evaluate class name */
5336+ znode class_node ;
5337+ if (callback -> kind == ZEND_AST_STATIC_CALL ) {
5338+ znode result ;
5339+ zend_compile_expr (& result , callback -> child [0 ]);
5340+ if (result .op_type == IS_CONST ) {
5341+ class_node = result ;
5342+ } else {
5343+ class_node = result ;
5344+ zend_emit_op_tmp (& class_node , ZEND_QM_ASSIGN , & result , NULL );
5345+ }
5346+ } else {
5347+ class_node .op_type = IS_UNUSED ;
5348+ }
5349+
5350+ /* Evaluate function name */
5351+ znode func_node ;
5352+ {
5353+ znode result ;
5354+ zend_compile_expr (& result , callback -> child [callback -> kind == ZEND_AST_CALL ? 0 : 1 ]);
5355+ if (result .op_type == IS_CONST ) {
5356+ func_node = result ;
5357+ } else {
5358+ zend_emit_op_tmp (& func_node , ZEND_QM_ASSIGN , & result , NULL );
5359+ }
5360+ }
5361+
53095362 znode value ;
53105363 value .op_type = IS_TMP_VAR ;
53115364 value .u .op .var = get_temporary_variable ();
@@ -5314,6 +5367,12 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
53145367 zend_ast_create_znode (& value ));
53155368 if (!call_args ) {
53165369 CG (active_op_array )-> T -- ;
5370+ if (func_node .op_type == IS_CONST ) {
5371+ zval_ptr_dtor_nogc (& func_node .u .constant );
5372+ }
5373+ if (class_node .op_type == IS_CONST ) {
5374+ zval_ptr_dtor_nogc (& class_node .u .constant );
5375+ }
53175376 /* The callback is not a FCC/PFA, or is not optimizable */
53185377 return FAILURE ;
53195378 }
@@ -5353,14 +5412,35 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
53535412
53545413 /* loop body */
53555414 znode call_result ;
5415+ zend_ast * func_ast ;
5416+ if (func_node .op_type == IS_CONST ) {
5417+ func_ast = zend_ast_create_znode (& func_node );
5418+ } else {
5419+ znode copy_node ;
5420+ zend_emit_op_tmp (& copy_node , ZEND_COPY_TMP , & func_node , NULL );
5421+ func_ast = zend_ast_create_znode (& copy_node );
5422+ }
53565423 switch (callback -> kind ) {
5357- case ZEND_AST_CALL :
5358- zend_compile_expr (& call_result , zend_ast_create (ZEND_AST_CALL , callback -> child [ 0 ] , call_args ));
5424+ case ZEND_AST_CALL : {
5425+ zend_compile_expr (& call_result , zend_ast_create (ZEND_AST_CALL , func_ast , call_args ));
53595426 break ;
5360- case ZEND_AST_STATIC_CALL :
5361- zend_compile_expr (& call_result , zend_ast_create (ZEND_AST_STATIC_CALL , callback -> child [0 ], callback -> child [1 ], call_args ));
5427+ }
5428+ case ZEND_AST_STATIC_CALL : {
5429+ zend_ast * class_ast ;
5430+ if (class_node .op_type == IS_CONST ) {
5431+ class_ast = zend_ast_create_znode (& class_node );
5432+ } else {
5433+ znode copy_node ;
5434+ zend_emit_op_tmp (& copy_node , ZEND_COPY_TMP , & class_node , NULL );
5435+ class_ast = zend_ast_create_znode (& copy_node );
5436+ }
5437+
5438+ zend_compile_expr (& call_result , zend_ast_create (ZEND_AST_STATIC_CALL , class_ast , func_ast , call_args ));
5439+ zend_ast_destroy (class_ast );
53625440 break ;
5441+ }
53635442 }
5443+ zend_ast_destroy (func_ast );
53645444 opline = zend_emit_op (NULL , ZEND_ADD_ARRAY_ELEMENT , & call_result , & key );
53655445 SET_NODE (opline -> result , result );
53665446 /* end loop body */
@@ -5375,6 +5455,12 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
53755455
53765456 zend_end_loop (opnum_fetch , & reset_node );
53775457 zend_emit_op (NULL , ZEND_FE_FREE , & reset_node , NULL );
5458+ if (func_node .op_type != IS_CONST ) {
5459+ zend_emit_op (NULL , ZEND_FREE , & func_node , NULL );
5460+ }
5461+ if (class_node .op_type != IS_UNUSED && class_node .op_type != IS_CONST ) {
5462+ zend_emit_op (NULL , ZEND_FREE , & class_node , NULL );
5463+ }
53785464
53795465 return SUCCESS ;
53805466}
0 commit comments