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