@@ -733,10 +733,29 @@ static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */
733733}
734734/* }}} */
735735
736+ static zend_class_entry * zend_closure_rt_cache_scope (zend_function * func , void * * run_time_cache , bool is_fake , zend_class_entry * default_scope )
737+ {
738+ ZEND_ASSERT (!(func -> op_array .fn_flags & ZEND_ACC_HEAP_RT_CACHE ));
739+
740+ if (is_fake ) {
741+ return func -> op_array .scope ;
742+ }
743+
744+ /* Zero RT cache is valid for any scope */
745+ if (func -> op_array .cache_size == 0 ) {
746+ return default_scope ;
747+ }
748+
749+ ZEND_ASSERT (func -> common .fn_flags & ZEND_ACC_CLOSURE );
750+ ZEND_ASSERT (!(func -> common .fn_flags & ZEND_ACC_FAKE_CLOSURE ));
751+
752+ return CACHED_PTR_EX (run_time_cache - 1 );
753+ }
754+
736755static void zend_create_closure_ex (zval * res , zend_function * func , zend_class_entry * scope , zend_class_entry * called_scope , zval * this_ptr , bool is_fake ) /* {{{ */
737756{
738757 zend_closure * closure ;
739- void * ptr ;
758+ void * * ptr ;
740759
741760 object_init_ex (res , zend_ce_closure );
742761
@@ -777,19 +796,22 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en
777796 /* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */
778797 ptr = ZEND_MAP_PTR_GET (func -> op_array .run_time_cache );
779798 if (!ptr
780- || func -> common .scope != scope
781799 || (func -> common .fn_flags & ZEND_ACC_HEAP_RT_CACHE )
800+ || zend_closure_rt_cache_scope (func , ptr , is_fake , scope ) != scope
782801 ) {
783- if (!ptr
784- && (func -> common .fn_flags & ZEND_ACC_CLOSURE )
785- && (func -> common .scope == scope ||
786- !(func -> common .fn_flags & ZEND_ACC_IMMUTABLE ))) {
802+ if (func -> op_array .cache_size == 0 ) {
803+ ptr = zend_arena_alloc (& CG (arena ), 0 );
804+ ZEND_MAP_PTR_SET (func -> op_array .run_time_cache , ptr );
805+ closure -> func .op_array .fn_flags &= ~ZEND_ACC_HEAP_RT_CACHE ;
806+ } else if (!ptr
807+ && func -> common .fn_flags & ZEND_ACC_CLOSURE ) {
808+ ZEND_ASSERT (!(func -> common .fn_flags & ZEND_ACC_FAKE_CLOSURE ));
787809 /* If a real closure is used for the first time, we create a shared runtime cache
788- * and remember which scope it is for. */
789- if ( func -> common . scope != scope ) {
790- func -> common . scope = scope ;
791- }
792- ptr = zend_arena_alloc ( & CG ( arena ), func -> op_array . cache_size );
810+ * and remember which scope it is for. The scope is stored in
811+ * an extra slot before the run_time_cache. */
812+ size_t extra_slot_size = sizeof ( void * ) ;
813+ ptr = zend_arena_alloc ( & CG ( arena ), func -> op_array . cache_size + extra_slot_size ) + extra_slot_size ;
814+ CACHE_PTR_EX ( ptr - 1 , scope );
793815 ZEND_MAP_PTR_SET (func -> op_array .run_time_cache , ptr );
794816 closure -> func .op_array .fn_flags &= ~ZEND_ACC_HEAP_RT_CACHE ;
795817 } else {
0 commit comments