@@ -183,7 +183,7 @@ ZEND_METHOD(Closure, call)
183
183
ZEND_METHOD (Closure , bind )
184
184
{
185
185
zval * newthis , * zclosure , * scope_arg = NULL ;
186
- zend_closure * closure , * new_closure ;
186
+ zend_closure * closure ;
187
187
zend_class_entry * ce , * called_scope ;
188
188
189
189
if (zend_parse_method_parameters (ZEND_NUM_ARGS (), getThis (), "Oo!|z" , & zclosure , zend_ce_closure , & newthis , & scope_arg ) == FAILURE ) {
@@ -223,15 +223,6 @@ ZEND_METHOD(Closure, bind)
223
223
}
224
224
225
225
zend_create_closure (return_value , & closure -> func , ce , called_scope , newthis );
226
- new_closure = (zend_closure * ) Z_OBJ_P (return_value );
227
-
228
- /* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
229
- if (ZEND_USER_CODE (closure -> func .type ) && (closure -> func .common .scope != new_closure -> func .common .scope || (closure -> func .op_array .fn_flags & ZEND_ACC_NO_RT_ARENA ))) {
230
- new_closure -> func .op_array .run_time_cache = emalloc (new_closure -> func .op_array .cache_size );
231
- memset (new_closure -> func .op_array .run_time_cache , 0 , new_closure -> func .op_array .cache_size );
232
-
233
- new_closure -> func .op_array .fn_flags |= ZEND_ACC_NO_RT_ARENA ;
234
- }
235
226
}
236
227
/* }}} */
237
228
@@ -666,9 +657,24 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
666
657
closure -> func .op_array .static_variables =
667
658
zend_array_dup (closure -> func .op_array .static_variables );
668
659
}
669
- if (UNEXPECTED (!closure -> func .op_array .run_time_cache )) {
670
- closure -> func .op_array .run_time_cache = func -> op_array .run_time_cache = zend_arena_alloc (& CG (arena ), func -> op_array .cache_size );
671
- memset (func -> op_array .run_time_cache , 0 , func -> op_array .cache_size );
660
+
661
+ /* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */
662
+ if (!closure -> func .op_array .run_time_cache
663
+ || func -> common .scope != scope
664
+ || (func -> common .fn_flags & ZEND_ACC_NO_RT_ARENA )
665
+ ) {
666
+ if (!func -> op_array .run_time_cache && (func -> common .fn_flags & ZEND_ACC_CLOSURE )) {
667
+ /* If a real closure is used for the first time, we create a shared runtime cache
668
+ * and remember which scope it is for. */
669
+ func -> common .scope = scope ;
670
+ func -> op_array .run_time_cache = zend_arena_alloc (& CG (arena ), func -> op_array .cache_size );
671
+ closure -> func .op_array .run_time_cache = func -> op_array .run_time_cache ;
672
+ } else {
673
+ /* Otherwise, we use a non-shared runtime cache */
674
+ closure -> func .op_array .run_time_cache = emalloc (func -> op_array .cache_size );
675
+ closure -> func .op_array .fn_flags |= ZEND_ACC_NO_RT_ARENA ;
676
+ }
677
+ memset (closure -> func .op_array .run_time_cache , 0 , func -> op_array .cache_size );
672
678
}
673
679
if (closure -> func .op_array .refcount ) {
674
680
(* closure -> func .op_array .refcount )++ ;
0 commit comments