Skip to content

Commit 9409958

Browse files
committed
Immutable clases and op_arrays
1 parent 4f1274c commit 9409958

36 files changed

+979
-421
lines changed

UPGRADING.INTERNALS

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ PHP 7.4 INTERNALS UPGRADE NOTES
77
d. Removed zend_check_private()
88
e. php_win32_error_to_msg() memory management
99
f. get_properties_for() handler / Z_OBJDEBUG_P
10+
g. Immutable classes and op_arrays
1011

1112
2. Build system changes
1213
a. Abstract
@@ -98,6 +99,20 @@ PHP 7.4 INTERNALS UPGRADE NOTES
9899
// ...
99100
zend_release_properties(ht);
100101

102+
g. Opcache may make classes and op_arrays immutable. Such classes are marked
103+
by ZEND_ACC_IMMUTABLE flag, they are not going to be copied from opcache
104+
shard memory to process memory and must not be modified at all.
105+
Few related data structures were changed to allow addressing mutable data
106+
structures from immutable ones. This access is implemented through
107+
ZEND_MAP_PTR... abstraction macros and, basically, uses additional level of
108+
indirection. op_array->run_time_cache, op_array->static_variables_ptr,
109+
class_entry->static_members_table and class_entry->iterator_funcs_ptr now
110+
have to be access through ZEND_MAP_PTR... macros.
111+
It's also not allowed to change op_array->reserved[] handles of immutable
112+
op_arrays. Instead, now you have to reserve op_array handle using
113+
zend_get_op_array_extension_handle() during MINIT and access its value
114+
using ZEND_OP_ARRAY_EXTENSION(op_array, handle).
115+
101116
========================
102117
2. Build system changes
103118
========================

Zend/zend.c

Lines changed: 106 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
#include "zend_smart_string.h"
3434
#include "zend_cpuinfo.h"
3535

36+
static size_t global_map_ptr_last = 0;
37+
3638
#ifdef ZTS
3739
ZEND_API int compiler_globals_id;
3840
ZEND_API int executor_globals_id;
@@ -41,7 +43,6 @@ static HashTable *global_class_table = NULL;
4143
static HashTable *global_constants_table = NULL;
4244
static HashTable *global_auto_globals_table = NULL;
4345
static HashTable *global_persistent_list = NULL;
44-
static zend_uintptr_t global_last_static_member = 0;
4546
ZEND_TSRMLS_CACHE_DEFINE()
4647
# define GLOBAL_FUNCTION_TABLE global_function_table
4748
# define GLOBAL_CLASS_TABLE global_class_table
@@ -626,13 +627,22 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{
626627
zend_hash_init_ex(compiler_globals->auto_globals, 8, NULL, auto_global_dtor, 1, 0);
627628
zend_hash_copy(compiler_globals->auto_globals, global_auto_globals_table, auto_global_copy_ctor);
628629

629-
compiler_globals->last_static_member = global_last_static_member;
630-
if (compiler_globals->last_static_member) {
631-
compiler_globals->static_members_table = calloc(compiler_globals->last_static_member + 1, sizeof(zval*));
632-
} else {
633-
compiler_globals->static_members_table = NULL;
634-
}
635630
compiler_globals->script_encoding_list = NULL;
631+
632+
#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
633+
/* Map region is going to be created and resized at run-time. */
634+
compiler_globals->map_ptr_base = NULL;
635+
compiler_globals->map_ptr_size = 0;
636+
compiler_globals->map_ptr_last = global_map_ptr_last;
637+
if (compiler_globals->map_ptr_last) {
638+
/* Allocate map_ptr table */
639+
compiler_globals->map_ptr_size = ZEND_MM_ALIGNED_SIZE_EX(compiler_globals->map_ptr_last, 4096);
640+
compiler_globals->map_ptr_base = pemalloc(compiler_globals->map_ptr_size * sizeof(void*), 1);
641+
memset(compiler_globals->map_ptr_base, 0, compiler_globals->map_ptr_last * sizeof(void*));
642+
}
643+
#else
644+
# error "Unknown ZEND_MAP_PTR_KIND"
645+
#endif
636646
}
637647
/* }}} */
638648

@@ -650,13 +660,14 @@ static void compiler_globals_dtor(zend_compiler_globals *compiler_globals) /* {{
650660
zend_hash_destroy(compiler_globals->auto_globals);
651661
free(compiler_globals->auto_globals);
652662
}
653-
if (compiler_globals->static_members_table) {
654-
free(compiler_globals->static_members_table);
655-
}
656663
if (compiler_globals->script_encoding_list) {
657664
pefree((char*)compiler_globals->script_encoding_list, 1);
658665
}
659-
compiler_globals->last_static_member = 0;
666+
if (compiler_globals->map_ptr_base) {
667+
free(compiler_globals->map_ptr_base);
668+
compiler_globals->map_ptr_base = NULL;
669+
compiler_globals->map_ptr_size = 0;
670+
}
660671
}
661672
/* }}} */
662673

@@ -879,6 +890,22 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions) /
879890
#ifdef ZEND_WIN32
880891
zend_get_windows_version_info(&EG(windows_version_info));
881892
#endif
893+
# if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR
894+
/* Create a map region, used for indirect pointers from shared to
895+
* process memory. It's allocatred once and never resized.
896+
* All processes must map it into the same address space.
897+
*/
898+
CG(map_ptr_size) = 1024 * 1024; // TODO: initial size ???
899+
CG(map_ptr_last) = 0;
900+
CG(map_ptr_base) = pemalloc(CG(map_ptr_size) * sizeof(void*), 1);
901+
# elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
902+
/* Map region is going to be created and resized at run-time. */
903+
CG(map_ptr_base) = NULL;
904+
CG(map_ptr_size) = 0;
905+
CG(map_ptr_last) = 0;
906+
# else
907+
# error "Unknown ZEND_MAP_PTR_KIND"
908+
# endif
882909
#endif
883910
EG(error_reporting) = E_ALL & ~E_NOTICE;
884911

@@ -931,7 +958,7 @@ int zend_post_startup(void) /* {{{ */
931958
*GLOBAL_FUNCTION_TABLE = *compiler_globals->function_table;
932959
*GLOBAL_CLASS_TABLE = *compiler_globals->class_table;
933960
*GLOBAL_CONSTANTS_TABLE = *executor_globals->zend_constants;
934-
global_last_static_member = compiler_globals->last_static_member;
961+
global_map_ptr_last = compiler_globals->map_ptr_last;
935962

936963
short_tags_default = CG(short_tags);
937964
compiler_options_default = CG(compiler_options);
@@ -950,6 +977,8 @@ int zend_post_startup(void) /* {{{ */
950977
executor_globals_ctor(executor_globals);
951978
global_persistent_list = &EG(persistent_list);
952979
zend_copy_ini_directives();
980+
#else
981+
global_map_ptr_last = CG(map_ptr_last);
953982
#endif
954983

955984
if (zend_post_startup_cb) {
@@ -996,6 +1025,12 @@ void zend_shutdown(void) /* {{{ */
9961025
GLOBAL_CLASS_TABLE = NULL;
9971026
GLOBAL_AUTO_GLOBALS_TABLE = NULL;
9981027
GLOBAL_CONSTANTS_TABLE = NULL;
1028+
#else
1029+
if (CG(map_ptr_base)) {
1030+
free(CG(map_ptr_base));
1031+
CG(map_ptr_base) = NULL;
1032+
CG(map_ptr_size) = 0;
1033+
}
9991034
#endif
10001035
zend_destroy_rsrc_list_dtors();
10011036
}
@@ -1077,17 +1112,12 @@ ZEND_API void zend_activate(void) /* {{{ */
10771112
init_compiler();
10781113
init_executor();
10791114
startup_scanner();
1115+
if (CG(map_ptr_last)) {
1116+
memset(CG(map_ptr_base), 0, CG(map_ptr_last) * sizeof(void*));
1117+
}
10801118
}
10811119
/* }}} */
10821120

1083-
#ifdef ZTS
1084-
void zend_reset_internal_classes(void) /* {{{ */
1085-
{
1086-
CG(last_static_member) = global_last_static_member;
1087-
}
1088-
/* }}} */
1089-
#endif
1090-
10911121
void zend_call_destructors(void) /* {{{ */
10921122
{
10931123
zend_try {
@@ -1619,6 +1649,62 @@ void free_estring(char **str_p) /* {{{ */
16191649
}
16201650
/* }}} */
16211651

1652+
ZEND_API void zend_map_ptr_reset(void)
1653+
{
1654+
CG(map_ptr_last) = global_map_ptr_last;
1655+
}
1656+
1657+
ZEND_API void *zend_map_ptr_new(void)
1658+
{
1659+
void **ptr;
1660+
1661+
if (CG(map_ptr_last) >= CG(map_ptr_size)) {
1662+
#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR
1663+
// TODO: error ???
1664+
ZEND_ASSERT(0);
1665+
#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
1666+
/* Grow map_ptr table */
1667+
CG(map_ptr_size) = ZEND_MM_ALIGNED_SIZE_EX(CG(map_ptr_last) + 1, 4096);
1668+
CG(map_ptr_base) = perealloc(CG(map_ptr_base), CG(map_ptr_size) * sizeof(void*), 1);
1669+
#else
1670+
# error "Unknown ZEND_MAP_PTR_KIND"
1671+
#endif
1672+
}
1673+
ptr = (void**)CG(map_ptr_base) + CG(map_ptr_last);
1674+
*ptr = NULL;
1675+
CG(map_ptr_last)++;
1676+
#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR
1677+
return ptr;
1678+
#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
1679+
return (void*)((CG(map_ptr_last) * sizeof(void*)) - (sizeof(void*) - 1));
1680+
#else
1681+
# error "Unknown ZEND_MAP_PTR_KIND"
1682+
#endif
1683+
}
1684+
1685+
ZEND_API void zend_map_ptr_extend(size_t last)
1686+
{
1687+
if (last > CG(map_ptr_last)) {
1688+
void **ptr;
1689+
1690+
if (last >= CG(map_ptr_size)) {
1691+
#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR
1692+
/* This may never happen */
1693+
ZEND_ASSERT(0);
1694+
#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
1695+
/* Grow map_ptr table */
1696+
CG(map_ptr_size) = ZEND_MM_ALIGNED_SIZE_EX(last, 4096);
1697+
CG(map_ptr_base) = perealloc(CG(map_ptr_base), CG(map_ptr_size) * sizeof(void*), 1);
1698+
#else
1699+
# error "Unknown ZEND_MAP_PTR_KIND"
1700+
#endif
1701+
}
1702+
ptr = (void**)CG(map_ptr_base) + CG(map_ptr_last);
1703+
memset(ptr, 0, (last - CG(map_ptr_last)) * sizeof(void*));
1704+
CG(map_ptr_last) = last;
1705+
}
1706+
}
1707+
16221708
/*
16231709
* Local variables:
16241710
* tab-width: 4

Zend/zend.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#define ZEND_ENGINE_3
2626

2727
#include "zend_types.h"
28+
#include "zend_map_ptr.h"
2829
#include "zend_errors.h"
2930
#include "zend_alloc.h"
3031
#include "zend_llist.h"
@@ -127,10 +128,7 @@ struct _zend_class_entry {
127128
int default_static_members_count;
128129
zval *default_properties_table;
129130
zval *default_static_members_table;
130-
union {
131-
zval *static_members_table;
132-
zend_uintptr_t static_members_table_idx;
133-
};
131+
ZEND_MAP_PTR_DEF(zval *, static_members_table);
134132
HashTable function_table;
135133
HashTable properties_info;
136134
HashTable constants_table;
@@ -150,7 +148,7 @@ struct _zend_class_entry {
150148
zend_function *unserialize_func;
151149

152150
/* allocated only if class implements Iterator or IteratorAggregate interface */
153-
zend_class_iterator_funcs *iterator_funcs_ptr;
151+
ZEND_MAP_PTR_DEF(zend_class_iterator_funcs *, iterator_funcs_ptr);
154152

155153
/* handlers */
156154
union {
@@ -271,9 +269,8 @@ ZEND_API void zend_activate_modules(void);
271269
ZEND_API void zend_deactivate_modules(void);
272270
ZEND_API void zend_post_deactivate_modules(void);
273271

274-
void zend_reset_internal_classes(void);
275-
276272
ZEND_API void free_estring(char **str_p);
273+
277274
END_EXTERN_C()
278275

279276
/* output support */

Zend/zend_API.c

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2795,7 +2795,9 @@ ZEND_API int zend_register_class_alias_ex(const char *name, size_t name_len, zen
27952795
ce = zend_hash_add_ptr(CG(class_table), lcname, ce);
27962796
zend_string_release_ex(lcname, 0);
27972797
if (ce) {
2798-
ce->refcount++;
2798+
if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
2799+
ce->refcount++;
2800+
}
27992801
return SUCCESS;
28002802
}
28012803
return FAILURE;
@@ -3696,18 +3698,14 @@ ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, z
36963698
ce->default_static_members_table = perealloc(ce->default_static_members_table, sizeof(zval) * ce->default_static_members_count, ce->type == ZEND_INTERNAL_CLASS);
36973699
}
36983700
ZVAL_COPY_VALUE(&ce->default_static_members_table[property_info->offset], property);
3699-
if (ce->type == ZEND_USER_CLASS) {
3700-
ce->static_members_table = ce->default_static_members_table;
3701-
#ifdef ZTS
3702-
} else if (!ce->static_members_table_idx) {
3703-
CG(last_static_member)++;
3704-
ce->static_members_table_idx = CG(last_static_member);
3705-
if (CG(static_members_table)) {
3706-
/* Support for run-time declaration: dl() */
3707-
CG(static_members_table) = realloc(CG(static_members_table), (CG(last_static_member) + 1) * sizeof(zval*));
3708-
CG(static_members_table)[ce->static_members_table_idx] = NULL;
3701+
if (!ZEND_MAP_PTR(ce->static_members_table)) {
3702+
ZEND_ASSERT(ce->type == ZEND_INTERNAL_CLASS);
3703+
if (!EG(current_execute_data)) {
3704+
ZEND_MAP_PTR_NEW(ce->static_members_table);
3705+
} else {
3706+
/* internal class loaded by dl() */
3707+
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
37093708
}
3710-
#endif
37113709
}
37123710
} else {
37133711
if ((property_info_ptr = zend_hash_find_ptr(&ce->properties_info, name)) != NULL &&

Zend/zend_API.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ typedef struct _zend_fcall_info_cache {
219219
class_container.trait_precedences = NULL; \
220220
class_container.interfaces = NULL; \
221221
class_container.get_iterator = NULL; \
222-
class_container.iterator_funcs_ptr = NULL; \
222+
ZEND_MAP_PTR_INIT(class_container.iterator_funcs_ptr, NULL); \
223223
class_container.info.internal.module = NULL; \
224224
class_container.info.internal.builtin_functions = functions; \
225225
}
@@ -228,11 +228,8 @@ typedef struct _zend_fcall_info_cache {
228228
#define INIT_NS_CLASS_ENTRY(class_container, ns, class_name, functions) \
229229
INIT_CLASS_ENTRY(class_container, ZEND_NS_NAME(ns, class_name), functions)
230230

231-
#ifdef ZTS
232-
# define CE_STATIC_MEMBERS(ce) (((ce)->type==ZEND_USER_CLASS)?(ce)->static_members_table:CG(static_members_table)[(ce)->static_members_table_idx])
233-
#else
234-
# define CE_STATIC_MEMBERS(ce) ((ce)->static_members_table)
235-
#endif
231+
#define CE_STATIC_MEMBERS(ce) \
232+
((zval*)ZEND_MAP_PTR_GET((ce)->static_members_table))
236233

237234
#define ZEND_FCI_INITIALIZED(fci) ((fci).size != 0)
238235

Zend/zend_builtin_functions.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1770,7 +1770,7 @@ static int copy_class_or_interface_name(zval *el, int num_args, va_list args, ze
17701770

17711771
if ((hash_key->key && ZSTR_VAL(hash_key->key)[0] != 0)
17721772
&& (comply_mask == (ce->ce_flags & mask))) {
1773-
if (ce->refcount > 1 &&
1773+
if ((ce->refcount > 1 || (ce->ce_flags & ZEND_ACC_IMMUTABLE)) &&
17741774
!same_name(hash_key->key, ce->name)) {
17751775
add_next_index_str(array, zend_string_copy(hash_key->key));
17761776
} else {

0 commit comments

Comments
 (0)