@@ -3864,6 +3864,38 @@ static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM
3864
3864
}
3865
3865
/* }}} */
3866
3866
3867
+ static bool prepare_in_place_array_modify_if_possible (const zend_execute_data * execute_data , const zval * arg )
3868
+ {
3869
+ ZEND_ASSERT (HT_IS_PACKED (Z_ARRVAL_P (arg )));
3870
+
3871
+ /* 2 refs: the CV and the argument */
3872
+ if (Z_REFCOUNT_P (arg ) != 2 ) {
3873
+ return false;
3874
+ }
3875
+ /* If it has holes, it might get sequentialized */
3876
+ if (!HT_IS_WITHOUT_HOLES (Z_ARRVAL_P (arg ))) {
3877
+ return false;
3878
+ }
3879
+ /* Immutable => no modification allowed */
3880
+ if (GC_FLAGS (Z_ARRVAL_P (arg )) & IS_ARRAY_IMMUTABLE ) {
3881
+ return false;
3882
+ }
3883
+
3884
+ const zend_op * call_opline = execute_data -> prev_execute_data -> opline ;
3885
+ const zend_op * next_opline = call_opline + 1 ;
3886
+ zval * var = ZEND_CALL_VAR (execute_data -> prev_execute_data , next_opline -> op1 .var );
3887
+
3888
+ /* Must be an assignment to the same array */
3889
+ if (next_opline -> opcode != ZEND_ASSIGN || next_opline -> op2 .var != call_opline -> result .var || Z_ARRVAL_P (arg ) != Z_ARRVAL_P (var )) {
3890
+ return false;
3891
+ }
3892
+
3893
+ /* Must set the CV to NULL so we don't destroy the array on assignment */
3894
+ ZVAL_NULL (var );
3895
+
3896
+ return true;
3897
+ }
3898
+
3867
3899
static zend_always_inline void php_array_merge_wrapper (INTERNAL_FUNCTION_PARAMETERS , int recursive ) /* {{{ */
3868
3900
{
3869
3901
zval * args = NULL ;
@@ -3925,22 +3957,35 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET
3925
3957
3926
3958
arg = args ;
3927
3959
src = Z_ARRVAL_P (arg );
3928
- /* copy first array */
3929
- array_init_size (return_value , count );
3930
- dest = Z_ARRVAL_P (return_value );
3960
+ bool add_ref = false;
3961
+ /* copy first array if necessary */
3931
3962
if (HT_IS_PACKED (src )) {
3932
- zend_hash_real_init_packed (dest );
3933
- ZEND_HASH_FILL_PACKED (dest ) {
3934
- ZEND_HASH_PACKED_FOREACH_VAL (src , src_entry ) {
3935
- if (UNEXPECTED (Z_ISREF_P (src_entry ) &&
3936
- Z_REFCOUNT_P (src_entry ) == 1 )) {
3937
- src_entry = Z_REFVAL_P (src_entry );
3938
- }
3939
- Z_TRY_ADDREF_P (src_entry );
3940
- ZEND_HASH_FILL_ADD (src_entry );
3941
- } ZEND_HASH_FOREACH_END ();
3942
- } ZEND_HASH_FILL_END ();
3963
+ if (prepare_in_place_array_modify_if_possible (execute_data , arg )) {
3964
+ /* Make RC 1 such that the array may be modified, add_ref will make sure the refcount gets back to 2 at the end */
3965
+ GC_DELREF (src );
3966
+ add_ref = true;
3967
+ dest = src ;
3968
+ ZVAL_ARR (return_value , dest );
3969
+ } else {
3970
+ array_init_size (return_value , count );
3971
+ dest = Z_ARRVAL_P (return_value );
3972
+
3973
+ zend_hash_real_init_packed (dest );
3974
+ ZEND_HASH_FILL_PACKED (dest ) {
3975
+ ZEND_HASH_PACKED_FOREACH_VAL (src , src_entry ) {
3976
+ if (UNEXPECTED (Z_ISREF_P (src_entry ) &&
3977
+ Z_REFCOUNT_P (src_entry ) == 1 )) {
3978
+ src_entry = Z_REFVAL_P (src_entry );
3979
+ }
3980
+ Z_TRY_ADDREF_P (src_entry );
3981
+ ZEND_HASH_FILL_ADD (src_entry );
3982
+ } ZEND_HASH_FOREACH_END ();
3983
+ } ZEND_HASH_FILL_END ();
3984
+ }
3943
3985
} else {
3986
+ array_init_size (return_value , count );
3987
+ dest = Z_ARRVAL_P (return_value );
3988
+
3944
3989
zend_string * string_key ;
3945
3990
zend_hash_real_init_mixed (dest );
3946
3991
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL (src , string_key , src_entry ) {
@@ -3967,6 +4012,10 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET
3967
4012
php_array_merge (dest , Z_ARRVAL_P (arg ));
3968
4013
}
3969
4014
}
4015
+
4016
+ if (add_ref ) {
4017
+ GC_ADDREF (src );
4018
+ }
3970
4019
}
3971
4020
/* }}} */
3972
4021
0 commit comments