8000 Immutable clases and op_arrays by dstogov · Pull Request #3608 · php/php-src · GitHub
[go: up one dir, main page]

Skip to content

Immutable clases and op_arrays #3608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
15 changes: 15 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ PHP 7.4 INTERNALS UPGRADE NOTES
e. php_win32_error_to_msg() memory management
f. get_properties_for() handler / Z_OBJDEBUG_P
g. Required object handlers
h. Immutable classes and op_arrays

2. Build system changes
a. Abstract
Expand Down Expand Up @@ -121,6 +122,20 @@ PHP 7.4 INTERNALS UPGRADE NOTES
It is recommended to initialize object handler structures by copying the
std object handlers and only overwriting those you want to change.

h. Opcache may make classes and op_arrays immutable. Such classes are marked
by ZEND_ACC_IMMUTABLE flag, they are not going to be copied from opcache
shared memory to process memory and must not be modified at all.
Few related data structures were changed to allow addressing mutable data
structures from immutable ones. This access is implemented through
ZEND_MAP_PTR... abstraction macros and, basically, uses additional level of
indirection. op_array->run_time_cache, op_array->static_variables_ptr and
class_entry->static_members_table now have to be accessed through
ZEND_MAP_PTR... macros.
It's also not allowed to change op_array->reserved[] handles of immutable
op_arrays. Instead, now you have to reserve op_array handle using
zend_get_op_array_extension_handle() during MINIT and access its value
using ZEND_OP_ARRAY_EXTENSION(op_array, handle).

========================
2. Build system changes
========================
Expand Down
126 changes: 106 additions & 20 deletions Zend/zend.c
8000 628C
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include "zend_smart_string.h"
#include "zend_cpuinfo.h"

static size_t global_map_ptr_last = 0;

#ifdef ZTS
ZEND_API int compiler_globals_id;
ZEND_API int executor_globals_id;
Expand All @@ -41,7 +43,6 @@ static HashTable *global_class_table = NULL;
static HashTable *global_constants_table = NULL;
static HashTable *global_auto_globals_table = NULL;
static HashTable *global_persistent_list = NULL;
static zend_uintptr_t global_last_static_member = 0;
ZEND_TSRMLS_CACHE_DEFINE()
# define GLOBAL_FUNCTION_TABLE global_function_table
# define GLOBAL_CLASS_TABLE global_class_table
Expand Down Expand Up @@ -626,13 +627,22 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{
zend_hash_init_ex(compiler_globals->auto_globals, 8, NULL, auto_global_dtor, 1, 0);
zend_hash_copy(compiler_globals->auto_globals, global_auto_globals_table, auto_global_copy_ctor);

compiler_globals->last_static_member = global_last_static_member;
if (compiler_globals->last_static_member) {
compiler_globals->static_members_table = calloc(compiler_globals->last_static_member + 1, sizeof(zval*));
} else {
compiler_globals->static_members_table = NULL;
}
compiler_globals->script_encoding_list = NULL;

#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
/* Map region is going to be created and resized at run-time. */
compiler_globals->map_ptr_base = NULL;
compiler_globals->map_ptr_size = 0;
compiler_globals->map_ptr_last = global_map_ptr_last;
if (compiler_globals->map_ptr_last) {
/* Allocate map_ptr table */
compiler_globals->map_ptr_size = ZEND_MM_ALIGNED_SIZE_EX(compiler_globals->map_ptr_last, 4096);
compiler_globals->map_ptr_base = pemalloc(compiler_globals->map_ptr_size * sizeof(void*), 1);
memset(compiler_globals->map_ptr_base, 0, compiler_globals->map_ptr_last * sizeof(void*));
}
#else
# error "Unknown ZEND_MAP_PTR_KIND"
#endif
}
/* }}} */

Expand All @@ -650,13 +660,14 @@ static void compiler_globals_dtor(zend_compiler_globals *compiler_globals) /* {{
zend_hash_destroy(compiler_globals->auto_globals);
free(compiler_globals->auto_globals);
}
if (compiler_globals->static_members_table) {
free(compiler_globals->static_members_table);
}
if (compiler_globals->script_encoding_list) {
pefree((char*)compiler_globals->script_encoding_list, 1);
}
compiler_globals->last_static_member = 0;
if (compiler_globals->map_ptr_base) {
free(compiler_globals->map_ptr_base);
compiler_globals->map_ptr_base = NULL;
compiler_globals->map_ptr_size = 0;
}
}
/* }}} */

Expand Down Expand Up @@ -879,6 +890,22 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions) /
#ifdef ZEND_WIN32
zend_get_windows_version_info(&EG(windows_version_info));
#endif
# if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR
/* Create a map region, used for indirect pointers from shared to
* process memory. It's allocatred once and never resized.
* All processes must map it into the same address space.
*/
CG(map_ptr_size) = 1024 * 1024; // TODO: initial size ???
CG(map_ptr_last) = 0;
CG(map_ptr_base) = pemalloc(CG(map_ptr_size) * sizeof(void*), 1);
# elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
/* Map region is going to be created and resized at run-time. */
CG(map_ptr_base) = NULL;
CG(map_ptr_size) = 0;
CG(map_ptr_last) = 0;
# else
# error "Unknown ZEND_MAP_PTR_KIND"
# endif
#endif
EG(error_reporting) = E_ALL & ~E_NOTICE;

Expand Down Expand Up @@ -931,7 +958,7 @@ int zend_post_startup(void) /* {{{ */
*GLOBAL_FUNCTION_TABLE = *compiler_globals->function_table;
*GLOBAL_CLASS_TABLE = *compiler_globals->class_table;
*GLOBAL_CONSTANTS_TABLE = *executor_globals->zend_constants;
global_last_static_member = compiler_globals->last_static_member;
global_map_ptr_last = compiler_globals->map_ptr_last;

short_tags_default = CG(short_tags);
compiler_options_default = CG(compiler_options);
Expand All @@ -950,6 +977,8 @@ int zend_post_startup(void) /* {{{ */
executor_globals_ctor(executor_globals);
global_persistent_list = &EG(persistent_list);
zend_copy_ini_directives();
#else
global_map_ptr_last = CG(map_ptr_last);
#endif

if (zend_post_startup_cb) {
Expand Down Expand Up @@ -996,6 +1025,12 @@ void zend_shutdown(void) /* {{{ */
GLOBAL_CLASS_TABLE = NULL;
GLOBAL_AUTO_GLOBALS_TABLE = NULL;
GLOBAL_CONSTANTS_TABLE = NULL;
#else
if (CG(map_ptr_base)) {
free(CG(map_ptr_base));
CG(map_ptr_base) = NULL;
CG(map_ptr_size) = 0;
}
#endif
zend_destroy_rsrc_list_dtors();
}
Expand Down Expand Up @@ -1077,17 +1112,12 @@ ZEND_API void zend_activate(void) /* {{{ */
init_compiler();
init_executor();
startup_scanner();
if (CG(map_ptr_last)) {
memset(CG(map_ptr_base), 0, CG(map_ptr_last) * sizeof(void*));
}
}
/* }}} */

#ifdef ZTS
void zend_reset_internal_classes(void) /* {{{ */
{
CG(last_static_member) = global_last_static_member;
}
/* }}} */
#endif

void zend_call_destructors(void) /* {{{ */
{
zend_try {
Expand Down Expand Up @@ -1619,6 +1649,62 @@ void free_estring(char **str_p) /* {{{ */
}
/* }}} */

ZEND_API void zend_map_ptr_reset(void)
{
CG(map_ptr_last) = global_map_ptr_last;
}

ZEND_API void *zend_map_ptr_new(void)
{
void **ptr;

if (CG(map_ptr_last) >= CG(map_ptr_size)) {
#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR
// TODO: error ???
ZEND_ASSERT(0);
#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
/* Grow map_ptr table */
CG(map_ptr_size) = ZEND_MM_ALIGNED_SIZE_EX(CG(map_ptr_last) + 1, 4096);
CG(map_ptr_base) = perealloc(CG(map_ptr_base), CG(map_ptr_size) * sizeof(void*), 1);
#else
# error "Unknown ZEND_MAP_PTR_KIND"
#endif
}
ptr = (void**)CG(map_ptr_base) + CG(map_ptr_last);
*ptr = NULL;
CG(map_ptr_last)++;
#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR
return ptr;
#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
return ZEND_MAP_PTR_PTR2OFFSET(ptr);
#else
# error "Unknown ZEND_MAP_PTR_KIND"
#endif
}

ZEND_API void zend_map_ptr_extend(size_t last)
{
if (last > CG(map_ptr_last)) {
void **ptr;

if (last >= CG(map_ptr_size)) {
#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR
/* This may never happen */
ZEND_ASSERT(0);
#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
/* Grow map_ptr table */
CG(map_ptr_size) = ZEND_MM_ALIGNED_SIZE_EX(last, 4096);
CG(map_ptr_base) = perealloc(CG(map_ptr_base), CG(map_ptr_size) * sizeof(void*), 1);
#else
# error "Unknown ZEND_MAP_PTR_KIND"
#endif
}
ptr = (void**)CG(map_ptr_base) + CG(map_ptr_last);
memset(ptr, 0, (last - CG(map_ptr_last)) * sizeof(void*));
CG(map_ptr_last) = last;
}
}

/*
* Local variables:
* tab-width: 4
Expand Down
9 changes: 3 additions & 6 deletions Zend/zend.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#define ZEND_ENGINE_3

#include "zend_types.h"
#include "zend_map_ptr.h"
#include "zend_errors.h"
#include "zend_alloc.h"
#include "zend_llist.h"
Expand Down Expand Up @@ -127,10 +128,7 @@ struct _zend_class_entry {
int default_static_members_count;
zval *default_properties_table;
zval *default_static_members_table;
union {
zval *static_members_table;
zend_uintptr_t static_members_table_idx;
};
ZEND_MAP_PTR_DEF(zval *, static_members_table);
HashTable function_table;
HashTable properties_info;
HashTable constants_table;
Expand Down Expand Up @@ -271,9 +269,8 @@ ZEND_API void zend_activate_modules(void);
ZEND_API void zend_deactivate_modules(void);
ZEND_API void zend_post_deactivate_modules(void);

void zend_reset_internal_classes(void);

ZEND_API void free_estring(char **str_p);

END_EXTERN_C()

/* output support */
Expand Down
22 changes: 10 additions & 12 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -2795,7 +2795,9 @@ ZEND_API int zend_register_class_alias_ex(const char *name, size_t name_len, zen
ce = zend_hash_add_ptr(CG(class_table), lcname, ce);
zend_string_release_ex(lcname, 0);
if (ce) {
ce->refcount++;
if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
ce->refcount++;
}
return SUCCESS;
}
return FAILURE;
Expand Down Expand Up @@ -3696,18 +3698,14 @@ ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, z
ce->default_static_members_table = perealloc(ce->default_static_members_table, sizeof(zval) * ce->default_static_members_count, ce->type == ZEND_INTERNAL_CLASS);
}
ZVAL_COPY_VALUE(&ce->default_static_members_table[property_info->offset], property);
if (ce->type == ZEND_USER_CLASS) {
ce->static_members_table = ce->default_static_members_table;
#ifdef ZTS
} else if (!ce->static_members_table_idx) {
CG(last_static_member)++;
ce->static_members_table_idx = CG(last_static_member);
if (CG(static_members_table)) {
/* Support for run-time declaration: dl() */
CG(static_members_table) = realloc(CG(static_members_table), (CG(last_static_member) + 1) * sizeof(zval*));
CG(static_members_table)[ce->static_members_table_idx] = NULL;
if (!ZEND_MAP_PTR(ce->static_members_table)) {
ZEND_ASSERT(ce->type == ZEND_INTERNAL_CLASS);
if (!EG(current_execute_data)) {
ZEND_MAP_PTR_NEW(ce->static_members_table);
} else {
/* internal class loaded by dl() */
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
}
#endif
}
} else {
if ((property_info_ptr = zend_hash_find_ptr(&ce->properties_info, name)) != NULL &&
Expand Down
7 changes: 2 additions & 5 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,8 @@ typedef struct _zend_fcall_info_cache {
#define INIT_NS_CLASS_ENTRY(class_container, ns, class_name, functions) \
INIT_CLASS_ENTRY(class_container, ZEND_NS_NAME(ns, class_name), functions)

#ifdef ZTS
# define CE_STATIC_MEMBERS(ce) (((ce)->type==ZEND_USER_CLASS)?(ce)->static_members_table:CG(static_members_table)[(ce)->static_members_table_idx])
#else
# define CE_STATIC_MEMBERS(ce) ((ce)->static_members_table)
#endif
#define CE_STATIC_MEMBERS(ce) \
((zval*)ZEND_MAP_PTR_GET((ce)->static_members_table))

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

Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_builtin_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -1769,7 +1769,7 @@ static int copy_class_or_interface_name(zval *el, int num_args, va_list args, ze

if ((hash_key->key && ZSTR_VAL(hash_key->key)[0] != 0)
&& (comply_mask == (ce->ce_flags & mask))) {
if (ce->refcount > 1 &&
if ((ce->refcount > 1 || (ce->ce_flags & ZEND_ACC_IMMUTABLE)) &&
!same_name(hash_key->key, ce->name)) {
add_next_index_str(array, zend_string_copy(hash_key->key));
} else {
Expand Down
Loading
0