Added Inheritance Cache.

This is a new transparent technology that eliminates overhead of PHP class inheritance.

PHP  classes are compiled and cached (by opcahce) separately, however their "linking" was done at run-time - on each request. The process of "linking" may involve a number of compatibility checks and borrowing methods/properties/constants form parent and traits. This takes significant time, but the result is the same on each request.

Inheritance Cache performs "linking" for unique set of all the depending classes (parent, interfaces, traits, property types, method types involved into compatibility checks) once and stores result in opcache shared memory. As a part of the this patch, I removed limitations for immutable classes (unresolved constants, typed properties and covariant type checks). So now all classes stored in opcache are "immutable". They may be lazily loaded into process memory, if necessary, but this usually occurs just once (on first linking).

The patch shows 8% improvement on Symphony "Hello World" app.
This commit is contained in:
Dmitry Stogov 2021-02-09 22:53:57 +03:00
parent 550aee0be3
commit 4b79dba932
41 changed files with 1598 additions and 1014 deletions

3
NEWS
View File

@ -21,6 +21,9 @@ PHP NEWS
. Fixed bug #80330 (Replace language in APIs and source code/docs).
(Darek Ślusarczyk)
- Opcache:
. Added inheritance cache. (Dmitry)
- OpenSSL:
. Bump minimal OpenSSL version to 1.0.2. (Jakub Zelenka)

33
Zend/tests/anon/015.phpt Normal file
View File

@ -0,0 +1,33 @@
--TEST--
static variables in methods inherited from parent class
--FILE--
<?php
class C {
function foo ($y = null) {
static $x = null;
if (!is_null($y)) {
$x = [$y];
}
return $x;
}
}
$c = new C();
$c->foo(42);
$d = new class extends C {};
var_dump($d->foo());
var_dump($d->foo(24));
var_dump($c->foo());
?>
--EXPECT--
array(1) {
[0]=>
int(42)
}
array(1) {
[0]=>
int(24)
}
array(1) {
[0]=>
int(42)
}

35
Zend/tests/anon/016.phpt Normal file
View File

@ -0,0 +1,35 @@
--TEST--
static variables in methods inherited from parent class (can't cache objects)
--FILE--
<?php
class C {
function foo ($y = null) {
static $x = null;
if (!is_null($y)) {
$x = [$y];
}
return $x;
}
}
$c = new C();
$c->foo(new stdClass);
$d = new class extends C {};
var_dump($d->foo());
var_dump($d->foo(24));
var_dump($c->foo());
?>
--EXPECT--
array(1) {
[0]=>
object(stdClass)#2 (0) {
}
}
array(1) {
[0]=>
int(24)
}
array(1) {
[0]=>
object(stdClass)#2 (0) {
}
}

View File

@ -660,6 +660,7 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{
zend_hash_copy(compiler_globals->auto_globals, global_auto_globals_table, auto_global_copy_ctor);
compiler_globals->script_encoding_list = NULL;
compiler_globals->current_linking_class = 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. */

View File

@ -107,6 +107,28 @@ typedef struct _zend_trait_alias {
uint32_t modifiers;
} zend_trait_alias;
typedef struct _zend_class_mutable_data {
zval *default_properties_table;
HashTable *constants_table;
uint32_t ce_flags;
} zend_class_mutable_data;
typedef struct _zend_class_dependency {
zend_string *name;
zend_class_entry *ce;
} zend_class_dependency;
typedef struct _zend_inheritance_cache_entry zend_inheritance_cache_entry;
struct _zend_inheritance_cache_entry {
zend_inheritance_cache_entry *next;
zend_class_entry *ce;
zend_class_entry *parent;
zend_class_dependency *dependencies;
uint32_t dependencies_count;
zend_class_entry *traits_and_interfaces[1];
};
struct _zend_class_entry {
char type;
zend_string *name;
@ -127,6 +149,9 @@ struct _zend_class_entry {
HashTable properties_info;
HashTable constants_table;
ZEND_MAP_PTR_DEF(zend_class_mutable_data*, mutable_data);
zend_inheritance_cache_entry *inheritance_cache;
struct _zend_property_info **properties_info_table;
zend_function *constructor;

View File

@ -1213,39 +1213,147 @@ ZEND_API void zend_merge_properties(zval *obj, HashTable *properties) /* {{{ */
}
/* }}} */
static zend_class_mutable_data *zend_allocate_mutable_data(zend_class_entry *class_type) /* {{{ */
{
zend_class_mutable_data *mutable_data;
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_IMMUTABLE);
ZEND_ASSERT(ZEND_MAP_PTR(class_type->mutable_data) != NULL);
ZEND_ASSERT(ZEND_MAP_PTR_GET_IMM(class_type->mutable_data) == NULL);
mutable_data = zend_arena_alloc(&CG(arena), sizeof(zend_class_mutable_data));
memset(mutable_data, 0, sizeof(zend_class_mutable_data));
mutable_data->ce_flags = class_type->ce_flags;
ZEND_MAP_PTR_SET_IMM(class_type->mutable_data, mutable_data);
return mutable_data;
}
/* }}} */
ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type) /* {{{ */
{
zend_class_mutable_data *mutable_data;
HashTable *constants_table;
zend_string *key;
zend_class_constant *new_c, *c;
constants_table = zend_arena_alloc(&CG(arena), sizeof(HashTable));
zend_hash_init(constants_table, zend_hash_num_elements(&class_type->constants_table), NULL, NULL, 0);
zend_hash_extend(constants_table, zend_hash_num_elements(&class_type->constants_table), 0);
ZEND_HASH_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) {
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
memcpy(new_c, c, sizeof(zend_class_constant));
c = new_c;
}
_zend_hash_append_ptr(constants_table, key, c);
} ZEND_HASH_FOREACH_END();
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_IMMUTABLE);
ZEND_ASSERT(ZEND_MAP_PTR(class_type->mutable_data) != NULL);
mutable_data = ZEND_MAP_PTR_GET_IMM(class_type->mutable_data);
if (!mutable_data) {
mutable_data = zend_allocate_mutable_data(class_type);
}
mutable_data->constants_table = constants_table;
return constants_table;
}
ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /* {{{ */
{
if (!(class_type->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
zend_class_constant *c;
zval *val;
zend_property_info *prop_info;
zend_class_mutable_data *mutable_data = NULL;
zval *default_properties_table = NULL;
zval *static_members_table = NULL;
zend_class_constant *c;
zval *val;
zend_property_info *prop_info;
uint32_t ce_flags;
if (class_type->parent) {
if (UNEXPECTED(zend_update_class_constants(class_type->parent) != SUCCESS)) {
return FAILURE;
ce_flags = class_type->ce_flags;
if (ce_flags & ZEND_ACC_CONSTANTS_UPDATED) {
return SUCCESS;
}
if (ce_flags & ZEND_ACC_IMMUTABLE) {
mutable_data = ZEND_MAP_PTR_GET_IMM(class_type->mutable_data);
if (mutable_data) {
ce_flags = mutable_data->ce_flags;
if (ce_flags & ZEND_ACC_CONSTANTS_UPDATED) {
return SUCCESS;
}
} else {
mutable_data = zend_allocate_mutable_data(class_type);
}
}
ZEND_HASH_FOREACH_PTR(&class_type->constants_table, c) {
val = &c->value;
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
if (class_type->parent) {
if (UNEXPECTED(zend_update_class_constants(class_type->parent) != SUCCESS)) {
return FAILURE;
}
}
if (ce_flags & ZEND_ACC_HAS_AST_CONSTANTS) {
HashTable *constants_table;
if (ce_flags & ZEND_ACC_IMMUTABLE) {
constants_table = mutable_data->constants_table;
if (!constants_table) {
constants_table = zend_separate_class_constants_table(class_type);
}
} else {
constants_table = &class_type->constants_table;
}
ZEND_HASH_FOREACH_PTR(constants_table, c) {
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
val = &c->value;
if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) {
return FAILURE;
}
}
} ZEND_HASH_FOREACH_END();
}
if (class_type->default_static_members_count && !CE_STATIC_MEMBERS(class_type)) {
if (class_type->type == ZEND_INTERNAL_CLASS || (class_type->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED))) {
if (class_type->default_static_members_count) {
static_members_table = CE_STATIC_MEMBERS(class_type);
if (!static_members_table) {
if (class_type->type == ZEND_INTERNAL_CLASS || (ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED))) {
zend_class_init_statics(class_type);
static_members_table = CE_STATIC_MEMBERS(class_type);
}
}
}
default_properties_table = class_type->default_properties_table;
if ((ce_flags & ZEND_ACC_IMMUTABLE)
&& (ce_flags & ZEND_ACC_HAS_AST_PROPERTIES)) {
zval *src, *dst, *end;
default_properties_table = mutable_data->default_properties_table;
if (!default_properties_table) {
default_properties_table = zend_arena_alloc(&CG(arena), sizeof(zval) * class_type->default_properties_count);
src = class_type->default_properties_table;
dst = default_properties_table;
end = dst + class_type->default_properties_count;
do {
ZVAL_COPY_VALUE_PROP(dst, src);
src++;
dst++;
} while (dst != end);
mutable_data->default_properties_table = default_properties_table;
}
}
if (ce_flags & (ZEND_ACC_HAS_AST_PROPERTIES|ZEND_ACC_HAS_AST_STATICS)) {
ZEND_HASH_FOREACH_PTR(&class_type->properties_info, prop_info) {
if (prop_info->flags & ZEND_ACC_STATIC) {
val = CE_STATIC_MEMBERS(class_type) + prop_info->offset;
val = static_members_table + prop_info->offset;
} else {
val = (zval*)((char*)class_type->default_properties_table + prop_info->offset - OBJ_PROP_TO_OFFSET(0));
val = (zval*)((char*)default_properties_table + prop_info->offset - OBJ_PROP_TO_OFFSET(0));
}
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
if (ZEND_TYPE_IS_SET(prop_info->type)) {
@ -1268,8 +1376,21 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
}
}
} ZEND_HASH_FOREACH_END();
}
class_type->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
ce_flags &= ~ZEND_ACC_HAS_AST_CONSTANTS;
ce_flags &= ~ZEND_ACC_HAS_AST_PROPERTIES;
if (class_type->ce_flags & ZEND_ACC_IMMUTABLE) {
ce_flags &= ~ZEND_ACC_HAS_AST_STATICS;
if (mutable_data) {
mutable_data->ce_flags = ce_flags;
}
} else {
if (!(ce_flags & ZEND_ACC_PRELOADED)) {
ce_flags &= ~ZEND_ACC_HAS_AST_STATICS;
}
class_type->ce_flags = ce_flags;
}
return SUCCESS;
@ -1279,7 +1400,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
static zend_always_inline void _object_properties_init(zend_object *object, zend_class_entry *class_type) /* {{{ */
{
if (class_type->default_properties_count) {
zval *src = class_type->default_properties_table;
zval *src = CE_DEFAULT_PROPERTIES_TABLE(class_type);
zval *dst = object->properties_table;
zval *end = src + class_type->default_properties_count;
@ -3809,6 +3930,11 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z
property_info = zend_arena_alloc(&CG(arena), sizeof(zend_property_info));
if (Z_TYPE_P(property) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
if (access_type & ZEND_ACC_STATIC) {
ce->ce_flags |= ZEND_ACC_HAS_AST_STATICS;
} else {
ce->ce_flags |= ZEND_ACC_HAS_AST_PROPERTIES;
}
}
}
@ -4124,6 +4250,7 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c
c->ce = ce;
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
}
if (!zend_hash_add_ptr(&ce->constants_table, name, c)) {

View File

@ -278,6 +278,12 @@ typedef struct _zend_fcall_info_cache {
#define CE_STATIC_MEMBERS(ce) \
((zval*)ZEND_MAP_PTR_GET((ce)->static_members_table))
#define CE_CONSTANTS_TABLE(ce) \
zend_class_constants_table(ce)
#define CE_DEFAULT_PROPERTIES_TABLE(ce) \
zend_class_default_properties_table(ce)
#define ZEND_FCI_INITIALIZED(fci) ((fci).size != 0)
ZEND_API int zend_next_free_module(void);
@ -382,6 +388,33 @@ ZEND_API void zend_declare_class_constant_stringl(zend_class_entry *ce, const ch
ZEND_API void zend_declare_class_constant_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value);
ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type);
ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type);
static zend_always_inline HashTable *zend_class_constants_table(zend_class_entry *ce) {
if ((ce->ce_flags & ZEND_ACC_HAS_AST_CONSTANTS)
&& (ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
zend_class_mutable_data *mutable_data =
(zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
if (mutable_data && mutable_data->constants_table) {
return mutable_data->constants_table;
} else {
return zend_separate_class_constants_table(ce);
}
} else {
return &ce->constants_table;
}
}
static zend_always_inline zval *zend_class_default_properties_table(zend_class_entry *ce) {
if ((ce->ce_flags & ZEND_ACC_HAS_AST_PROPERTIES)
&& (ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
zend_class_mutable_data *mutable_data =
(zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
return mutable_data->default_properties_table;
} else {
return ce->default_properties_table;
}
}
ZEND_API void zend_update_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, zval *value);
ZEND_API void zend_update_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zval *value);

View File

@ -703,6 +703,7 @@ static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, bool s
zend_property_info *prop_info;
zval *prop, prop_copy;
zend_string *key;
zval *default_properties_table = CE_DEFAULT_PROPERTIES_TABLE(ce);
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->properties_info, key, prop_info) {
if (((prop_info->flags & ZEND_ACC_PROTECTED) &&
@ -716,7 +717,7 @@ static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, bool s
prop = &ce->default_static_members_table[prop_info->offset];
ZVAL_DEINDIRECT(prop);
} else if (!statics && (prop_info->flags & ZEND_ACC_STATIC) == 0) {
prop = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop_info->offset)];
prop = &default_properties_table[OBJ_PROP_TO_NUM(prop_info->offset)];
}
if (!prop) {
continue;
@ -734,7 +735,7 @@ static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, bool s
/* this is necessary to make it able to work with default array
* properties, returned to user */
if (Z_OPT_TYPE_P(prop) == IS_CONSTANT_AST) {
if (UNEXPECTED(zval_update_constant_ex(prop, NULL) != SUCCESS)) {
if (UNEXPECTED(zval_update_constant_ex(prop, ce) != SUCCESS)) {
return;
}
}

View File

@ -417,6 +417,8 @@ void init_compiler(void) /* {{{ */
CG(delayed_variance_obligations) = NULL;
CG(delayed_autoloads) = NULL;
CG(unlinked_uses) = NULL;
CG(current_linking_class) = NULL;
}
/* }}} */
@ -428,7 +430,6 @@ void shutdown_compiler(void) /* {{{ */
zend_stack_destroy(&CG(loop_var_stack));
zend_stack_destroy(&CG(delayed_oplines_stack));
zend_stack_destroy(&CG(short_circuiting_opnums));
zend_arena_destroy(CG(arena));
if (CG(delayed_variance_obligations)) {
zend_hash_destroy(CG(delayed_variance_obligations));
@ -440,6 +441,12 @@ void shutdown_compiler(void) /* {{{ */
FREE_HASHTABLE(CG(delayed_autoloads));
CG(delayed_autoloads) = NULL;
}
if (CG(unlinked_uses)) {
zend_hash_destroy(CG(unlinked_uses));
FREE_HASHTABLE(CG(unlinked_uses));
CG(unlinked_uses) = NULL;
}
CG(current_linking_class) = NULL;
}
/* }}} */
@ -1136,7 +1143,8 @@ ZEND_API zend_result do_bind_class(zval *lcname, zend_string *lc_parent_name) /*
return FAILURE;
}
if (zend_do_link_class(ce, lc_parent_name) == FAILURE) {
ce = zend_do_link_class(ce, lc_parent_name, Z_STR_P(lcname));
if (!ce) {
/* Reload bucket pointer, the hash table may have been reallocated */
zv = zend_hash_find(EG(class_table), Z_STR_P(lcname));
zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, Z_STR_P(rtd_key));
@ -1187,13 +1195,23 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop
if (ZEND_TYPE_HAS_CE(*list_type)) {
str = add_type_string(str, ZEND_TYPE_CE(*list_type)->name);
} else {
zend_string *resolved = resolve_class_name(ZEND_TYPE_NAME(*list_type), scope);
str = add_type_string(str, resolved);
zend_string_release(resolved);
if (ZEND_TYPE_HAS_CE_CACHE(*list_type)
&& ZEND_TYPE_CE_CACHE(*list_type)) {
str = add_type_string(str, ZEND_TYPE_CE_CACHE(*list_type)->name);
} else {
zend_string *resolved = resolve_class_name(ZEND_TYPE_NAME(*list_type), scope);
str = add_type_string(str, resolved);
zend_string_release(resolved);
}
}
} ZEND_TYPE_LIST_FOREACH_END();
} else if (ZEND_TYPE_HAS_NAME(type)) {
str = resolve_class_name(ZEND_TYPE_NAME(type), scope);
if (ZEND_TYPE_HAS_CE_CACHE(type)
&& ZEND_TYPE_CE_CACHE(type)) {
str = zend_string_copy(ZEND_TYPE_CE_CACHE(type)->name);
} else {
str = resolve_class_name(ZEND_TYPE_NAME(type), scope);
}
} else if (ZEND_TYPE_HAS_CE(type)) {
str = zend_string_copy(ZEND_TYPE_CE(type)->name);
}
@ -1354,7 +1372,8 @@ ZEND_API void zend_do_delayed_early_binding(zend_op_array *op_array, uint32_t fi
zend_class_entry *parent_ce = zend_hash_find_ex_ptr(EG(class_table), lc_parent_name, 1);
if (parent_ce) {
if (zend_try_early_bind(ce, parent_ce, Z_STR_P(lcname), zv)) {
ce = zend_try_early_bind(ce, parent_ce, Z_STR_P(lcname), zv);
if (ce) {
/* Store in run-time cache */
((void**)((char*)run_time_cache + opline->extended_value))[0] = ce;
}
@ -1831,6 +1850,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_hand
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
ce->info.user.doc_comment = NULL;
}
ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
ce->default_properties_count = 0;
ce->default_static_members_count = 0;

View File

@ -233,7 +233,7 @@ typedef struct _zend_oparray_context {
/* op_array or class is preloaded | | | */
#define ZEND_ACC_PRELOADED (1 << 10) /* X | X | | */
/* | | | */
/* Class Flags (unused: 23...) | | | */
/* Class Flags (unused: 28...) | | | */
/* =========== | | | */
/* | | | */
/* Special class types | | | */
@ -285,6 +285,16 @@ typedef struct _zend_oparray_context {
/* stored in opcache (may be partially) | | | */
#define ZEND_ACC_CACHED (1 << 22) /* X | | | */
/* | | | */
/* temporary flag used during delayed variance checks | | | */
#define ZEND_ACC_CACHEABLE (1 << 23) /* X | | | */
/* | | | */
#define ZEND_ACC_HAS_AST_CONSTANTS (1 << 24) /* X | | | */
#define ZEND_ACC_HAS_AST_PROPERTIES (1 << 25) /* X | | | */
#define ZEND_ACC_HAS_AST_STATICS (1 << 26) /* X | | | */
/* | | | */
/* loaded from file cache to process memory | | | */
#define ZEND_ACC_FILE_CACHED (1 << 27) /* X | | | */
/* | | | */
/* Function Flags (unused: 27-30) | | | */
/* ============== | | | */
/* | | | */
@ -802,6 +812,7 @@ ZEND_API int open_file_for_scanning(zend_file_handle *file_handle);
ZEND_API void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size);
ZEND_API void destroy_op_array(zend_op_array *op_array);
ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle);
ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce);
ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce);
ZEND_API void zend_cleanup_internal_classes(void);
ZEND_API void zend_type_release(zend_type type, bool persistent);

View File

@ -374,7 +374,7 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope,
ce = zend_fetch_class(class_name, flags);
}
if (ce) {
c = zend_hash_find_ptr(&ce->constants_table, constant_name);
c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), constant_name);
if (c == NULL) {
if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) {
zend_throw_error(NULL, "Undefined constant %s::%s", ZSTR_VAL(class_name), ZSTR_VAL(constant_name));

View File

@ -857,7 +857,17 @@ static bool zend_check_and_resolve_property_class_type(
if (ZEND_TYPE_HAS_LIST(info->type)) {
zend_type *list_type;
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(info->type), list_type) {
if (ZEND_TYPE_HAS_NAME(*list_type)) {
if (ZEND_TYPE_HAS_CE_CACHE(*list_type)) {
ce = ZEND_TYPE_CE_CACHE(*list_type);
if (!ce) {
zend_string *name = ZEND_TYPE_NAME(*list_type);
ce = resolve_single_class_type(name, info->ce);
if (UNEXPECTED(!ce)) {
continue;
}
ZEND_TYPE_SET_CE_CACHE(*list_type, ce);
}
} else if (ZEND_TYPE_HAS_NAME(*list_type)) {
zend_string *name = ZEND_TYPE_NAME(*list_type);
ce = resolve_single_class_type(name, info->ce);
if (!ce) {
@ -874,7 +884,17 @@ static bool zend_check_and_resolve_property_class_type(
} ZEND_TYPE_LIST_FOREACH_END();
return 0;
} else {
if (UNEXPECTED(ZEND_TYPE_HAS_NAME(info->type))) {
if (ZEND_TYPE_HAS_CE_CACHE(info->type)) {
ce = ZEND_TYPE_CE_CACHE(info->type);
if (!ce) {
zend_string *name = ZEND_TYPE_NAME(info->type);
ce = resolve_single_class_type(name, info->ce);
if (UNEXPECTED(!ce)) {
return 0;
}
ZEND_TYPE_SET_CE_CACHE(info->type, ce);
}
} else if (UNEXPECTED(ZEND_TYPE_HAS_NAME(info->type))) {
zend_string *name = ZEND_TYPE_NAME(info->type);
ce = resolve_single_class_type(name, info->ce);
if (UNEXPECTED(!ce)) {

View File

@ -295,9 +295,15 @@ void shutdown_executor(void) /* {{{ */
} ZEND_HASH_FOREACH_END();
ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) {
zend_class_entry *ce = Z_PTR_P(zv);
if (ce->default_static_members_count) {
zend_cleanup_internal_class_data(ce);
}
if (ZEND_MAP_PTR(ce->mutable_data) && ZEND_MAP_PTR_GET_IMM(ce->mutable_data)) {
zend_cleanup_mutable_class_data(ce);
}
if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
zend_op_array *op_array;
ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
@ -1059,7 +1065,15 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
if ((flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED) ||
((flags & ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED) &&
(ce->ce_flags & ZEND_ACC_NEARLY_LINKED))) {
ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES;
if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
if (!CG(unlinked_uses)) {
ALLOC_HASHTABLE(CG(unlinked_uses));
zend_hash_init(CG(unlinked_uses), 0, NULL, NULL, 0);
}
zend_hash_index_add_empty_element(CG(unlinked_uses), (zend_long)(zend_uintptr_t)ce);
} else {
ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES;
}
return ce;
}
return NULL;

View File

@ -126,6 +126,8 @@ struct _zend_compiler_globals {
HashTable *delayed_variance_obligations;
HashTable *delayed_autoloads;
HashTable *unlinked_uses;
zend_class_entry *current_linking_class;
uint32_t rtd_key_counter;

View File

@ -27,6 +27,9 @@
#include "zend_operators.h"
#include "zend_exceptions.h"
ZEND_API zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) = NULL;
ZEND_API zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies) = NULL;
static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *dependency_ce);
static void add_compatibility_obligation(
zend_class_entry *ce, const zend_function *child_fn, zend_class_entry *child_scope,
@ -368,6 +371,47 @@ typedef enum {
INHERITANCE_SUCCESS = 1,
} inheritance_status;
static void track_class_dependency(zend_class_entry *ce, zend_string *class_name)
{
HashTable *ht;
if (!CG(current_linking_class) || ce == CG(current_linking_class)) {
return;
} else if (!class_name) {
class_name = ce->name;
} else if (zend_string_equals_literal_ci(class_name, "self")
|| zend_string_equals_literal_ci(class_name, "parent")) {
return;
}
ht = (HashTable*)CG(current_linking_class)->inheritance_cache;
#ifndef ZEND_WIN32
if (ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
#else
if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
#endif
// TODO: dependency on not-immutable class ???
if (ht) {
zend_hash_destroy(ht);
FREE_HASHTABLE(ht);
CG(current_linking_class)->inheritance_cache = NULL;
}
CG(current_linking_class)->ce_flags &= ~ZEND_ACC_CACHEABLE;
CG(current_linking_class) = NULL;
return;
}
/* Record dependency */
if (!ht) {
ALLOC_HASHTABLE(ht);
zend_hash_init(ht, 0, NULL, NULL, 0);
CG(current_linking_class)->inheritance_cache = (zend_inheritance_cache_entry*)ht;
}
zend_hash_add_ptr(ht, class_name, ce);
}
static inheritance_status zend_perform_covariant_class_type_check(
zend_class_entry *fe_scope, zend_string *fe_class_name, zend_class_entry *fe_ce,
zend_class_entry *proto_scope, zend_type proto_type,
@ -381,6 +425,7 @@ static inheritance_status zend_perform_covariant_class_type_check(
if (!fe_ce) {
have_unresolved = 1;
} else {
track_class_dependency(fe_ce, fe_class_name);
return INHERITANCE_SUCCESS;
}
}
@ -389,6 +434,7 @@ static inheritance_status zend_perform_covariant_class_type_check(
if (!fe_ce) {
have_unresolved = 1;
} else if (unlinked_instanceof(fe_ce, zend_ce_traversable)) {
track_class_dependency(fe_ce, fe_class_name);
return INHERITANCE_SUCCESS;
}
}
@ -396,8 +442,9 @@ static inheritance_status zend_perform_covariant_class_type_check(
zend_type *single_type;
ZEND_TYPE_FOREACH(proto_type, single_type) {
zend_class_entry *proto_ce;
zend_string *proto_class_name = NULL;
if (ZEND_TYPE_HAS_NAME(*single_type)) {
zend_string *proto_class_name =
proto_class_name =
resolve_class_name(proto_scope, ZEND_TYPE_NAME(*single_type));
if (zend_string_equals_ci(fe_class_name, proto_class_name)) {
return INHERITANCE_SUCCESS;
@ -416,6 +463,8 @@ static inheritance_status zend_perform_covariant_class_type_check(
if (!fe_ce || !proto_ce) {
have_unresolved = 1;
} else if (unlinked_instanceof(fe_ce, proto_ce)) {
track_class_dependency(fe_ce, fe_class_name);
track_class_dependency(proto_ce, proto_class_name);
return INHERITANCE_SUCCESS;
}
} ZEND_TYPE_FOREACH_END();
@ -1139,6 +1188,12 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa
} else if (!(Z_ACCESS_FLAGS(parent_const->value) & ZEND_ACC_PRIVATE)) {
if (Z_TYPE(parent_const->value) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
if (ce->parent->ce_flags & ZEND_ACC_IMMUTABLE) {
c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
memcpy(c, parent_const, sizeof(zend_class_constant));
parent_const = c;
}
}
if (ce->type & ZEND_INTERNAL_CLASS) {
c = pemalloc(sizeof(zend_class_constant), 1);
@ -1251,6 +1306,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
ZVAL_COPY_OR_DUP_PROP(dst, src);
if (Z_OPT_TYPE_P(dst) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
ce->ce_flags |= ZEND_ACC_HAS_AST_PROPERTIES;
}
continue;
} while (dst != end);
@ -1261,6 +1317,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
ZVAL_COPY_PROP(dst, src);
if (Z_OPT_TYPE_P(dst) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
ce->ce_flags |= ZEND_ACC_HAS_AST_PROPERTIES;
}
continue;
} while (dst != end);
@ -1323,6 +1380,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
}
if (Z_TYPE_P(Z_INDIRECT_P(dst)) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
ce->ce_flags |= ZEND_ACC_HAS_AST_STATICS;
}
} while (dst != end);
} else {
@ -1434,6 +1492,12 @@ static void do_inherit_iface_constant(zend_string *name, zend_class_constant *c,
zend_class_constant *ct;
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
if (iface->ce_flags & ZEND_ACC_IMMUTABLE) {
ct = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
memcpy(ct, c, sizeof(zend_class_constant));
c = ct;
}
}
if (ce->type & ZEND_INTERNAL_CLASS) {
ct = pemalloc(sizeof(zend_class_constant), 1);
@ -2073,37 +2137,13 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
}
/* }}} */
static void zend_do_bind_traits(zend_class_entry *ce) /* {{{ */
static void zend_do_bind_traits(zend_class_entry *ce, zend_class_entry **traits) /* {{{ */
{
HashTable **exclude_tables;
zend_class_entry **aliases;
zend_class_entry **traits, *trait;
uint32_t i, j;
ZEND_ASSERT(ce->num_traits > 0);
traits = emalloc(sizeof(zend_class_entry*) * ce->num_traits);
for (i = 0; i < ce->num_traits; i++) {
trait = zend_fetch_class_by_name(ce->trait_names[i].name,
ce->trait_names[i].lc_name, ZEND_FETCH_CLASS_TRAIT);
if (UNEXPECTED(trait == NULL)) {
return;
}
if (UNEXPECTED(!(trait->ce_flags & ZEND_ACC_TRAIT))) {
zend_error_noreturn(E_ERROR, "%s cannot use %s - it is not a trait", ZSTR_VAL(ce->name), ZSTR_VAL(trait->name));
return;
}
for (j = 0; j < i; j++) {
if (traits[j] == trait) {
/* skip duplications */
trait = NULL;
break;
}
}
traits[i] = trait;
}
/* complete initialization of trait strutures in ce */
zend_traits_init_trait_structures(ce, traits, &exclude_tables, &aliases);
@ -2120,8 +2160,6 @@ static void zend_do_bind_traits(zend_class_entry *ce) /* {{{ */
/* then flatten the properties into it, to, mostly to notfiy developer about problems */
zend_do_traits_property_binding(ce, traits);
efree(traits);
}
/* }}} */
@ -2293,7 +2331,12 @@ static int check_variance_obligation(zval *zv) {
if (obligation->type == OBLIGATION_DEPENDENCY) {
zend_class_entry *dependency_ce = obligation->dependency_ce;
if (dependency_ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
zend_class_entry *orig_linking_class = CG(current_linking_class);
CG(current_linking_class) =
(dependency_ce->ce_flags & ZEND_ACC_CACHEABLE) ? dependency_ce : NULL;
resolve_delayed_variance_obligations(dependency_ce);
CG(current_linking_class) = orig_linking_class;
}
if (!(dependency_ce->ce_flags & ZEND_ACC_LINKED)) {
return ZEND_HASH_APPLY_KEEP;
@ -2401,7 +2444,10 @@ static void check_unrecoverable_load_failure(zend_class_entry *ce) {
* to remove the class from the class table and throw an exception, because there is already
* a dependence on the inheritance hierarchy of this specific class. Instead we fall back to
* a fatal error, as would happen if we did not allow exceptions in the first place. */
if (ce->ce_flags & ZEND_ACC_HAS_UNLINKED_USES) {
if ((ce->ce_flags & ZEND_ACC_HAS_UNLINKED_USES)
|| ((ce->ce_flags & ZEND_ACC_IMMUTABLE)
&& CG(unlinked_uses)
&& zend_hash_index_del(CG(unlinked_uses), (zend_long)(zend_uintptr_t)ce) == SUCCESS)) {
zend_string *exception_str;
zval exception_zv;
ZEND_ASSERT(EG(exception) && "Exception must have been thrown");
@ -2413,13 +2459,167 @@ static void check_unrecoverable_load_failure(zend_class_entry *ce) {
}
}
ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name) /* {{{ */
#define zend_update_inherited_handler(handler) do { \
if (ce->handler == (zend_function*)op_array) { \
ce->handler = (zend_function*)new_op_array; \
} \
} while (0)
static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce)
{
zend_class_entry *ce;
Bucket *p, *end;
ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry));
memcpy(ce, pce, sizeof(zend_class_entry));
ce->ce_flags &= ~ZEND_ACC_IMMUTABLE;
ce->refcount = 1;
ce->inheritance_cache = NULL;
ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
/* properties */
if (ce->default_properties_table) {
zval *dst = emalloc(sizeof(zval) * ce->default_properties_count);
zval *src = ce->default_properties_table;
zval *end = src + ce->default_properties_count;
ce->default_properties_table = dst;
for (; src != end; src++, dst++) {
ZVAL_COPY_VALUE_PROP(dst, src);
}
}
/* methods */
ce->function_table.pDestructor = ZEND_FUNCTION_DTOR;
if (!(HT_FLAGS(&ce->function_table) & HASH_FLAG_UNINITIALIZED)) {
p = emalloc(HT_SIZE(&ce->function_table));
memcpy(p, HT_GET_DATA_ADDR(&ce->function_table), HT_USED_SIZE(&ce->function_table));
HT_SET_DATA_ADDR(&ce->function_table, p);
p = ce->function_table.arData;
end = p + ce->function_table.nNumUsed;
for (; p != end; p++) {
zend_op_array *op_array, *new_op_array;
void ***run_time_cache_ptr;
op_array = Z_PTR(p->val);
ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
ZEND_ASSERT(op_array->scope == pce);
ZEND_ASSERT(op_array->prototype == NULL);
new_op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array) + sizeof(void*));
Z_PTR(p->val) = new_op_array;
memcpy(new_op_array, op_array, sizeof(zend_op_array));
run_time_cache_ptr = (void***)(new_op_array + 1);
*run_time_cache_ptr = NULL;
new_op_array->fn_flags &= ~ZEND_ACC_IMMUTABLE;
new_op_array->scope = ce;
ZEND_MAP_PTR_INIT(new_op_array->run_time_cache, run_time_cache_ptr);
ZEND_MAP_PTR_INIT(new_op_array->static_variables_ptr, &new_op_array->static_variables);
zend_update_inherited_handler(constructor);
zend_update_inherited_handler(destructor);
zend_update_inherited_handler(clone);
zend_update_inherited_handler(__get);
zend_update_inherited_handler(__set);
zend_update_inherited_handler(__call);
zend_update_inherited_handler(__isset);
zend_update_inherited_handler(__unset);
zend_update_inherited_handler(__tostring);
zend_update_inherited_handler(__callstatic);
zend_update_inherited_handler(__debugInfo);
zend_update_inherited_handler(__serialize);
zend_update_inherited_handler(__unserialize);
}
}
/* static members */
if (ce->default_static_members_table) {
zval *dst = emalloc(sizeof(zval) * ce->default_static_members_count);
zval *src = ce->default_static_members_table;
zval *end = src + ce->default_static_members_count;
ce->default_static_members_table = dst;
for (; src != end; src++, dst++) {
ZVAL_COPY_VALUE(dst, src);
}
}
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
/* properties_info */
if (!(HT_FLAGS(&ce->properties_info) & HASH_FLAG_UNINITIALIZED)) {
p = emalloc(HT_SIZE(&ce->properties_info));
memcpy(p, HT_GET_DATA_ADDR(&ce->properties_info), HT_USED_SIZE(&ce->properties_info));
HT_SET_DATA_ADDR(&ce->properties_info, p);
p = ce->properties_info.arData;
end = p + ce->properties_info.nNumUsed;
for (; p != end; p++) {
zend_property_info *prop_info, *new_prop_info;
prop_info = Z_PTR(p->val);
ZEND_ASSERT(prop_info->ce == pce);
new_prop_info= zend_arena_alloc(&CG(arena), sizeof(zend_property_info));
Z_PTR(p->val) = new_prop_info;
memcpy(new_prop_info, prop_info, sizeof(zend_property_info));
new_prop_info->ce = ce;
if (ZEND_TYPE_HAS_LIST(new_prop_info->type)) {
zend_type_list *new_list;
zend_type_list *list = ZEND_TYPE_LIST(new_prop_info->type);
new_list = zend_arena_alloc(&CG(arena), ZEND_TYPE_LIST_SIZE(list->num_types));
memcpy(new_list, list, ZEND_TYPE_LIST_SIZE(list->num_types));
ZEND_TYPE_SET_PTR(new_prop_info->type, list);
ZEND_TYPE_FULL_MASK(new_prop_info->type) |= _ZEND_TYPE_ARENA_BIT;
}
}
}
/* constants table */
if (!(HT_FLAGS(&ce->constants_table) & HASH_FLAG_UNINITIALIZED)) {
p = emalloc(HT_SIZE(&ce->constants_table));
memcpy(p, HT_GET_DATA_ADDR(&ce->constants_table), HT_USED_SIZE(&ce->constants_table));
HT_SET_DATA_ADDR(&ce->constants_table, p);
p = ce->constants_table.arData;
end = p + ce->constants_table.nNumUsed;
for (; p != end; p++) {
zend_class_constant *c, *new_c;
c = Z_PTR(p->val);
ZEND_ASSERT(c->ce == pce);
new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
Z_PTR(p->val) = new_c;
memcpy(new_c, c, sizeof(zend_class_constant));
new_c->ce = ce;
}
}
return ce;
}
#ifndef ZEND_WIN32
# define UPDATE_IS_CACHEABLE(ce) do { \
if ((ce)->type == ZEND_USER_CLASS) { \
is_cacheable &= (ce)->ce_flags; \
} \
} while (0)
#else
// TODO: ASLR may cause different addresses in different workers ???
# define UPDATE_IS_CACHEABLE(ce) do { \
is_cacheable &= (ce)->ce_flags; \
} while (0)
#endif
ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name, zend_string *key) /* {{{ */
{
/* Load parent/interface dependencies first, so we can still gracefully abort linking
* with an exception and remove the class from the class table. This is only possible
* if no variance obligations on the current class have been added during autoloading. */
zend_class_entry *parent = NULL;
zend_class_entry **interfaces = NULL;
zend_class_entry **traits_and_interfaces = NULL;
zend_class_entry *proto = NULL;
zend_class_entry *orig_linking_class;
uint32_t is_cacheable = ce->ce_flags & ZEND_ACC_IMMUTABLE;
uint32_t i, j;
zval *zv;
if (ce->parent_name) {
parent = zend_fetch_class_by_name(
@ -2427,13 +2627,43 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa
ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION);
if (!parent) {
check_unrecoverable_load_failure(ce);
return FAILURE;
return NULL;
}
UPDATE_IS_CACHEABLE(parent);
}
if (ce->num_traits || ce->num_interfaces) {
traits_and_interfaces = emalloc(sizeof(zend_class_entry*) * (ce->num_traits + ce->num_interfaces));
for (i = 0; i < ce->num_traits; i++) {
zend_class_entry *trait = zend_fetch_class_by_name(ce->trait_names[i].name,
ce->trait_names[i].lc_name, ZEND_FETCH_CLASS_TRAIT);
if (UNEXPECTED(trait == NULL)) {
efree(traits_and_interfaces);
return NULL;
}
if (UNEXPECTED(!(trait->ce_flags & ZEND_ACC_TRAIT))) {
zend_error_noreturn(E_ERROR, "%s cannot use %s - it is not a trait", ZSTR_VAL(ce->name), ZSTR_VAL(trait->name));
efree(traits_and_interfaces);
return NULL;
}
for (j = 0; j < i; j++) {
if (traits_and_interfaces[j] == trait) {
/* skip duplications */
trait = NULL;
break;
}
}
traits_and_interfaces[i] = trait;
if (trait) {
UPDATE_IS_CACHEABLE(trait);
}
}
}
if (ce->num_interfaces) {
/* Also copy the parent interfaces here, so we don't need to reallocate later. */
uint32_t i, num_parent_interfaces = parent ? parent->num_interfaces : 0;
uint32_t num_parent_interfaces = parent ? parent->num_interfaces : 0;
interfaces = emalloc(
sizeof(zend_class_entry *) * (ce->num_interfaces + num_parent_interfaces));
if (num_parent_interfaces) {
@ -2448,12 +2678,61 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa
if (!iface) {
check_unrecoverable_load_failure(ce);
efree(interfaces);
return FAILURE;
efree(traits_and_interfaces);
return NULL;
}
interfaces[num_parent_interfaces + i] = iface;
traits_and_interfaces[ce->num_traits + i] = iface;
if (iface) {
UPDATE_IS_CACHEABLE(iface);
}
}
}
if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
if (is_cacheable) {
if (zend_inheritance_cache_get && zend_inheritance_cache_add) {
zend_class_entry *ret = zend_inheritance_cache_get(ce, parent, traits_and_interfaces);
if (ret) {
if (traits_and_interfaces) {
efree(traits_and_interfaces);
}
if (traits_and_interfaces) {
efree(interfaces);
}
zv = zend_hash_find_ex(CG(class_table), key, 1);
Z_CE_P(zv) = ret;
return ret;
}
} else {
is_cacheable = 0;
}
proto = ce;
}
/* Lazy class loading */
ce = zend_lazy_class_load(ce);
zv = zend_hash_find_ex(CG(class_table), key, 1);
Z_CE_P(zv) = ce;
if (CG(unlinked_uses)
&& zend_hash_index_del(CG(unlinked_uses), (zend_long)(zend_uintptr_t)proto) == SUCCESS) {
ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES;
}
} else if (ce->ce_flags & ZEND_ACC_FILE_CACHED) {
/* Lazy class loading */
ce = zend_lazy_class_load(ce);
ce->ce_flags &= ~ZEND_ACC_FILE_CACHED;
zv = zend_hash_find_ex(CG(class_table), key, 1);
Z_CE_P(zv) = ce;
if (CG(unlinked_uses)
&& zend_hash_index_del(CG(unlinked_uses), (zend_long)(zend_uintptr_t)proto) == SUCCESS) {
ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES;
}
}
orig_linking_class = CG(current_linking_class);
CG(current_linking_class) = is_cacheable ? ce : NULL;
if (parent) {
if (!(parent->ce_flags & ZEND_ACC_LINKED)) {
add_dependency_obligation(ce, parent);
@ -2461,7 +2740,7 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa
zend_do_inheritance(ce, parent);
}
if (ce->num_traits) {
zend_do_bind_traits(ce);
zend_do_bind_traits(ce, traits_and_interfaces);
}
if (interfaces) {
zend_do_implement_interfaces(ce, interfaces);
@ -2478,19 +2757,53 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa
if (!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE)) {
ce->ce_flags |= ZEND_ACC_LINKED;
return SUCCESS;
}
ce->ce_flags |= ZEND_ACC_NEARLY_LINKED;
load_delayed_classes();
if (ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
resolve_delayed_variance_obligations(ce);
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
report_variance_errors(ce);
} else {
ce->ce_flags |= ZEND_ACC_NEARLY_LINKED;
if (CG(current_linking_class)) {
ce->ce_flags |= ZEND_ACC_CACHEABLE;
}
load_delayed_classes();
if (ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
resolve_delayed_variance_obligations(ce);
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
CG(current_linking_class) = orig_linking_class;
report_variance_errors(ce);
}
}
if (ce->ce_flags & ZEND_ACC_CACHEABLE) {
ce->ce_flags &= ~ZEND_ACC_CACHEABLE;
} else {
CG(current_linking_class) = NULL;
}
}
return SUCCESS;
if (!CG(current_linking_class)) {
is_cacheable = 0;
}
CG(current_linking_class) = orig_linking_class;
if (is_cacheable) {
HashTable *ht = (HashTable*)ce->inheritance_cache;
zend_class_entry *new_ce;
ce->inheritance_cache = NULL;
new_ce = zend_inheritance_cache_add(ce, proto, parent, traits_and_interfaces, ht);
if (new_ce) {
zv = zend_hash_find_ex(CG(class_table), key, 1);
ce = new_ce;
Z_CE_P(zv) = ce;
}
if (ht) {
zend_hash_destroy(ht);
FREE_HASHTABLE(ht);
}
}
if (traits_and_interfaces) {
efree(traits_and_interfaces);
}
return ce;
}
/* }}} */
@ -2539,21 +2852,66 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e
}
/* }}} */
bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding) /* {{{ */
zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding) /* {{{ */
{
inheritance_status status = zend_can_early_bind(ce, parent_ce);
inheritance_status status;
zend_class_entry *proto = NULL;
zend_class_entry *orig_linking_class;
uint32_t is_cacheable = ce->ce_flags & ZEND_ACC_IMMUTABLE;
UPDATE_IS_CACHEABLE(parent_ce);
if (is_cacheable) {
if (zend_inheritance_cache_get && zend_inheritance_cache_add) {
zend_class_entry *ret = zend_inheritance_cache_get(ce, parent_ce, NULL);
if (ret) {
if (delayed_early_binding) {
if (UNEXPECTED(zend_hash_set_bucket_key(EG(class_table), (Bucket*)delayed_early_binding, lcname) == NULL)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
return NULL;
}
Z_CE_P(delayed_early_binding) = ret;
} else {
if (UNEXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ret) == NULL)) {
return NULL;
}
}
return ret;
}
} else {
is_cacheable = 0;
}
proto = ce;
}
orig_linking_class = CG(current_linking_class);
CG(current_linking_class) = NULL;
status = zend_can_early_bind(ce, parent_ce);
CG(current_linking_class) = orig_linking_class;
if (EXPECTED(status != INHERITANCE_UNRESOLVED)) {
if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
/* Lazy class loading */
ce = zend_lazy_class_load(ce);
} else if (ce->ce_flags & ZEND_ACC_FILE_CACHED) {
/* Lazy class loading */
ce = zend_lazy_class_load(ce);
ce->ce_flags &= ~ZEND_ACC_FILE_CACHED;
}
if (delayed_early_binding) {
if (UNEXPECTED(zend_hash_set_bucket_key(EG(class_table), (Bucket*)delayed_early_binding, lcname) == NULL)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
return 0;
return NULL;
}
Z_CE_P(delayed_early_binding) = ce;
} else {
if (UNEXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) == NULL)) {
return 0;
return NULL;
}
}
orig_linking_class = CG(current_linking_class);
CG(current_linking_class) = is_cacheable ? ce : NULL;
zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
if (parent_ce && parent_ce->num_interfaces) {
zend_do_inherit_interfaces(ce, parent_ce);
@ -2564,8 +2922,28 @@ bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend
}
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE));
ce->ce_flags |= ZEND_ACC_LINKED;
return 1;
CG(current_linking_class) = orig_linking_class;
if (is_cacheable) {
HashTable *ht = (HashTable*)ce->inheritance_cache;
zend_class_entry *new_ce;
ce->inheritance_cache = NULL;
new_ce = zend_inheritance_cache_add(ce, proto, parent_ce, NULL, ht);
if (new_ce) {
zval *zv = zend_hash_find_ex(CG(class_table), lcname, 1);
ce = new_ce;
Z_CE_P(zv) = ce;
}
if (ht) {
zend_hash_destroy(ht);
FREE_HASHTABLE(ht);
}
}
return ce;
}
return 0;
return NULL;
}
/* }}} */

View File

@ -30,11 +30,14 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
#define zend_do_inheritance(ce, parent_ce) \
zend_do_inheritance_ex(ce, parent_ce, 0)
ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name);
ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name, zend_string *key);
void zend_verify_abstract_class(zend_class_entry *ce);
void zend_build_properties_info_table(zend_class_entry *ce);
bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding);
zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding);
ZEND_API extern zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces);
ZEND_API extern zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies);
END_EXTERN_C()

View File

@ -37,9 +37,13 @@
type * ZEND_MAP_PTR(name)
# define ZEND_MAP_PTR_GET(ptr) \
(*(ZEND_MAP_PTR(ptr)))
# define ZEND_MAP_PTR_GET_IMM(ptr) \
ZEND_MAP_PTR_GET(ptr)
# define ZEND_MAP_PTR_SET(ptr, val) do { \
(*(ZEND_MAP_PTR(ptr))) = (val); \
} while (0)
# define ZEND_MAP_PTR_SET_IMM(ptr, val) \
ZEND_MAP_PTR_SET(ptr, val)
# define ZEND_MAP_PTR_INIT(ptr, val) do { \
ZEND_MAP_PTR(ptr) = (val); \
} while (0)
@ -51,6 +55,8 @@
# define ZEND_MAP_PTR_SET_REAL_BASE(base, ptr) do { \
base = (ptr); \
} while (0)
# define ZEND_MAP_PTR_OFFSET2PTR(ptr) \
((void**)((char*)CG(map_ptr_base) + (uintptr_t)ZEND_MAP_PTR(ptr)))
#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
# define ZEND_MAP_PTR(ptr) \
ptr ## __ptr
@ -66,6 +72,8 @@
(*(ZEND_MAP_PTR_IS_OFFSET(ptr) ? \
ZEND_MAP_PTR_OFFSET2PTR(ptr) : \
((void**)(ZEND_MAP_PTR(ptr)))))
# define ZEND_MAP_PTR_GET_IMM(ptr) \
(*ZEND_MAP_PTR_OFFSET2PTR(ptr))
# define ZEND_MAP_PTR_SET(ptr, val) do { \
void **__p = (void**)(ZEND_MAP_PTR(ptr)); \
if (ZEND_MAP_PTR_IS_OFFSET(ptr)) { \
@ -73,6 +81,10 @@
} \
*__p = (val); \
} while (0)
# define ZEND_MAP_PTR_SET_IMM(ptr, val) do { \
void **__p = ZEND_MAP_PTR_OFFSET2PTR(ptr); \
*__p = (val); \
} while (0)
# define ZEND_MAP_PTR_INIT(ptr, val) do { \
ZEND_MAP_PTR(ptr) = (val); \
} while (0)

View File

@ -163,7 +163,7 @@ ZEND_API void zend_function_dtor(zval *zv)
ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce)
{
if (CE_STATIC_MEMBERS(ce)) {
if (ZEND_MAP_PTR(ce->static_members_table) && CE_STATIC_MEMBERS(ce)) {
zval *static_members = CE_STATIC_MEMBERS(ce);
zval *p = static_members;
zval *end = p + ce->default_static_members_count;
@ -255,18 +255,76 @@ static void _destroy_zend_class_traits_info(zend_class_entry *ce)
}
}
ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce)
{
zend_class_mutable_data *mutable_data = ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
if (mutable_data) {
HashTable *constants_table;
zval *p;
constants_table = mutable_data->constants_table;
if (constants_table && constants_table != &ce->constants_table) {
zend_class_constant *c;
ZEND_HASH_FOREACH_PTR(constants_table, c) {
zval_ptr_dtor_nogc(&c->value);
} ZEND_HASH_FOREACH_END();
zend_hash_destroy(constants_table);
mutable_data->constants_table = NULL;
}
p = mutable_data->default_properties_table;
if (p && p != ce->default_properties_table) {
zval *end = p + ce->default_properties_count;
while (p < end) {
zval_ptr_dtor_nogc(p);
p++;
}
mutable_data->default_properties_table = NULL;
}
ZEND_MAP_PTR_SET_IMM(ce->mutable_data, NULL);
}
}
ZEND_API void destroy_zend_class(zval *zv)
{
zend_property_info *prop_info;
zend_class_entry *ce = Z_PTR_P(zv);
zend_function *fn;
if (ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED)) {
if (ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED|ZEND_ACC_FILE_CACHED)) {
zend_op_array *op_array;
if (ce->default_static_members_count) {
zend_cleanup_internal_class_data(ce);
}
if (!(ce->ce_flags & ZEND_ACC_FILE_CACHED)) {
if (ZEND_MAP_PTR(ce->mutable_data) && ZEND_MAP_PTR_GET_IMM(ce->mutable_data)) {
zend_cleanup_mutable_class_data(ce);
}
} else {
zend_class_constant *c;
zval *p, *end;
ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
if (c->ce == ce) {
zval_ptr_dtor_nogc(&c->value);
}
} ZEND_HASH_FOREACH_END();
p = ce->default_properties_table;
end = p + ce->default_properties_count;
while (p < end) {
zval_ptr_dtor_nogc(p);
p++;
}
}
if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
if (op_array->type == ZEND_USER_FUNCTION) {

View File

@ -130,7 +130,7 @@ typedef struct {
* are only supported since C++20). */
void *ptr;
uint32_t type_mask;
/* TODO: We could use the extra 32-bit of padding on 64-bit systems. */
uint32_t ce_cache__ptr; /* map_ptr offset */
} zend_type;
typedef struct {
@ -138,13 +138,15 @@ typedef struct {
zend_type types[1];
} zend_type_list;
#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 24
#define _ZEND_TYPE_MASK ((1u << 24) - 1)
#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 25
#define _ZEND_TYPE_MASK ((1u << 25) - 1)
/* Only one of these bits may be set. */
#define _ZEND_TYPE_NAME_BIT (1u << 23)
#define _ZEND_TYPE_CE_BIT (1u << 22)
#define _ZEND_TYPE_LIST_BIT (1u << 21)
#define _ZEND_TYPE_NAME_BIT (1u << 24)
#define _ZEND_TYPE_CE_BIT (1u << 23)
#define _ZEND_TYPE_LIST_BIT (1u << 22)
#define _ZEND_TYPE_KIND_MASK (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_CE_BIT|_ZEND_TYPE_NAME_BIT)
/* CE cached in map_ptr area */
#define _ZEND_TYPE_CACHE_BIT (1u << 21)
/* Whether the type list is arena allocated */
#define _ZEND_TYPE_ARENA_BIT (1u << 20)
/* Type mask excluding the flags above. */
@ -167,6 +169,9 @@ typedef struct {
#define ZEND_TYPE_HAS_LIST(t) \
((((t).type_mask) & _ZEND_TYPE_LIST_BIT) != 0)
#define ZEND_TYPE_HAS_CE_CACHE(t) \
((((t).type_mask) & _ZEND_TYPE_CACHE_BIT) != 0)
#define ZEND_TYPE_USES_ARENA(t) \
((((t).type_mask) & _ZEND_TYPE_ARENA_BIT) != 0)
@ -185,6 +190,13 @@ typedef struct {
#define ZEND_TYPE_LIST(t) \
((zend_type_list *) (t).ptr)
#define ZEND_TYPE_CE_CACHE(t) \
(*(zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR((t).ce_cache))
#define ZEND_TYPE_SET_CE_CACHE(t, ce) do { \
*((zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR((t).ce_cache)) = ce; \
} while (0)
#define ZEND_TYPE_LIST_SIZE(num_types) \
(sizeof(zend_type_list) + ((num_types) - 1) * sizeof(zend_type))
@ -254,10 +266,10 @@ typedef struct {
(((t).type_mask & _ZEND_TYPE_NULLABLE_BIT) != 0)
#define ZEND_TYPE_INIT_NONE(extra_flags) \
{ NULL, (extra_flags) }
{ NULL, (extra_flags), 0 }
#define ZEND_TYPE_INIT_MASK(_type_mask) \
{ NULL, (_type_mask) }
{ NULL, (_type_mask), 0 }
#define ZEND_TYPE_INIT_CODE(code, allow_null, extra_flags) \
ZEND_TYPE_INIT_MASK(((code) == _IS_BOOL ? MAY_BE_BOOL : ((code) == IS_MIXED ? MAY_BE_ANY : (1 << (code)))) \
@ -265,10 +277,10 @@ typedef struct {
#define ZEND_TYPE_INIT_PTR(ptr, type_kind, allow_null, extra_flags) \
{ (void *) (ptr), \
(type_kind) | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0) | (extra_flags) }
(type_kind) | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0) | (extra_flags), 0 }
#define ZEND_TYPE_INIT_PTR_MASK(ptr, type_mask) \
{ (void *) (ptr), (type_mask) }
{ (void *) (ptr), (type_mask), 0 }
#define ZEND_TYPE_INIT_CE(_ce, allow_null, extra_flags) \
ZEND_TYPE_INIT_PTR(_ce, _ZEND_TYPE_CE_BIT, allow_null, extra_flags)

View File

@ -5821,7 +5821,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO
}
}
zv = zend_hash_find_ex(&ce->constants_table, Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
zv = zend_hash_find_ex(CE_CONSTANTS_TABLE(ce), Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
if (EXPECTED(zv != NULL)) {
c = Z_PTR_P(zv);
scope = EX(func)->op_array.scope;
@ -7526,7 +7526,8 @@ ZEND_VM_HANDLER(145, ZEND_DECLARE_CLASS_DELAYED, CONST, CONST)
if (UNEXPECTED(!zv)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
} else {
if (zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2))) == FAILURE) {
ce = zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2)), Z_STR_P(lcname));
if (!ce) {
/* Reload bucket pointer, the hash table may have been reallocated */
zv = zend_hash_find(EG(class_table), Z_STR_P(lcname));
zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, Z_STR_P(lcname + 1));
@ -7567,7 +7568,8 @@ ZEND_VM_HANDLER(146, ZEND_DECLARE_ANON_CLASS, ANY, ANY, CACHE_SLOT)
ce = Z_CE_P(zv);
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
SAVE_OPLINE();
if (zend_do_link_class(ce, (OP2_TYPE == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL) == FAILURE) {
ce = zend_do_link_class(ce, (OP2_TYPE == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL, rtd_key);
if (!ce) {
HANDLE_EXCEPTION();
}
}

View File

@ -2813,7 +2813,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLE
ce = Z_CE_P(zv);
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
SAVE_OPLINE();
if (zend_do_link_class(ce, (opline->op2_type == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL) == FAILURE) {
ce = zend_do_link_class(ce, (opline->op2_type == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL, rtd_key);
if (!ce) {
HANDLE_EXCEPTION();
}
}
@ -6848,7 +6849,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS
}
}
zv = zend_hash_find_ex(&ce->constants_table, Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
zv = zend_hash_find_ex(CE_CONSTANTS_TABLE(ce), Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
if (EXPECTED(zv != NULL)) {
c = Z_PTR_P(zv);
scope = EX(func)->op_array.scope;
@ -7175,7 +7176,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CLASS_DELAYED_SPEC_CON
if (UNEXPECTED(!zv)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
} else {
if (zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2))) == FAILURE) {
ce = zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2)), Z_STR_P(lcname));
if (!ce) {
/* Reload bucket pointer, the hash table may have been reallocated */
zv = zend_hash_find(EG(class_table), Z_STR_P(lcname));
zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, Z_STR_P(lcname + 1));
@ -24194,7 +24196,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_
}
}
zv = zend_hash_find_ex(&ce->constants_table, Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
zv = zend_hash_find_ex(CE_CONSTANTS_TABLE(ce), Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
if (EXPECTED(zv != NULL)) {
c = Z_PTR_P(zv);
scope = EX(func)->op_array.scope;
@ -32617,7 +32619,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS
}
}
zv = zend_hash_find_ex(&ce->constants_table, Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
zv = zend_hash_find_ex(CE_CONSTANTS_TABLE(ce), Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
if (EXPECTED(zv != NULL)) {
c = Z_PTR_P(zv);
scope = EX(func)->op_array.scope;

View File

@ -118,6 +118,8 @@ bool fallback_process = 0; /* process uses file cache fallback */
#endif
static zend_op_array *(*accelerator_orig_compile_file)(zend_file_handle *file_handle, int type);
static zend_class_entry* (*accelerator_orig_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces);
static zend_class_entry* (*accelerator_orig_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies);
static zend_result (*accelerator_orig_zend_stream_open_function)(const char *filename, zend_file_handle *handle );
static zend_string *(*accelerator_orig_zend_resolve_path)(const char *filename, size_t filename_len);
static void (*accelerator_orig_zend_error_cb)(int type, const char *error_filename, const uint32_t error_lineno, zend_string *message);
@ -2249,6 +2251,269 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
return zend_accel_load_script(persistent_script, from_shared_memory);
}
static zend_inheritance_cache_entry* zend_accel_inheritance_cache_find(zend_inheritance_cache_entry *entry, zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, bool *needs_autoload_ptr)
{
uint32_t i;
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_IMMUTABLE);
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED));
while (entry) {
bool found = 1;
bool needs_autoload = 0;
if (entry->parent != parent) {
found = 0;
} else {
for (i = 0; i < ce->num_traits + ce->num_interfaces; i++) {
if (entry->traits_and_interfaces[i] != traits_and_interfaces[i]) {
found = 0;
break;
}
}
if (found && entry->dependencies) {
for (i = 0; i < entry->dependencies_count; i++) {
zend_class_entry *ce = zend_lookup_class_ex(entry->dependencies[i].name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
if (ce != entry->dependencies[i].ce) {
if (!ce) {
needs_autoload = 1;
} else {
found = 0;
break;
}
}
}
}
}
if (found) {
*needs_autoload_ptr = needs_autoload;
return entry;
}
entry = entry->next;
}
return NULL;
}
static zend_class_entry* zend_accel_inheritance_cache_get(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces)
{
uint32_t i;
bool needs_autoload;
zend_inheritance_cache_entry *entry = ce->inheritance_cache;
while (entry) {
entry = zend_accel_inheritance_cache_find(entry, ce, parent, traits_and_interfaces, &needs_autoload);
if (entry) {
if (!needs_autoload) {
zend_map_ptr_extend(ZCSG(map_ptr_last));
return entry->ce;
}
for (i = 0; i < entry->dependencies_count; i++) {
zend_class_entry *ce = zend_lookup_class_ex(entry->dependencies[i].name, NULL, 0);
if (ce == NULL) {
return NULL;
}
}
}
}
return NULL;
}
static bool is_array_cacheable(zval *zv)
{
zval *p;
ZEND_HASH_FOREACH_VAL(Z_ARR_P(zv), p) {
if (Z_REFCOUNTED_P(p)) {
if (Z_TYPE_P(p) == IS_ARRAY) {
if (!is_array_cacheable(p)) {
/* Can't cache */
return 0;
}
} else if (Z_TYPE_P(p) == IS_OBJECT || Z_TYPE_P(p) == IS_RESOURCE || Z_TYPE_P(p) == IS_REFERENCE) {
/* Can't cache */
return 0;
}
}
} ZEND_HASH_FOREACH_END();
return 1;
}
static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies)
{
zend_persistent_script dummy;
size_t size;
uint32_t i;
bool needs_autoload;
zend_class_entry *new_ce;
zend_inheritance_cache_entry *entry;
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_IMMUTABLE));
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_LINKED);
if (!ZCG(accelerator_enabled) ||
(ZCSG(restart_in_progress) && accel_restart_is_active())) {
return NULL;
}
if (traits_and_interfaces && dependencies) {
for (i = 0; i < proto->num_traits + proto->num_interfaces; i++) {
if (traits_and_interfaces[i]) {
zend_hash_del(dependencies, traits_and_interfaces[i]->name);
}
}
}
if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
zend_op_array *op_array;
zval *zv;
ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
if (op_array->type == ZEND_USER_FUNCTION
&& op_array->static_variables
&& !(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) {
if (UNEXPECTED(GC_REFCOUNT(op_array->static_variables) > 1)) {
GC_DELREF(op_array->static_variables);
op_array->static_variables = zend_array_dup(op_array->static_variables);
}
ZEND_HASH_FOREACH_VAL(op_array->static_variables, zv) {
if (Z_ISREF_P(zv)) {
zend_reference *ref = Z_REF_P(zv);
ZVAL_COPY_VALUE(zv, &ref->val);
if (GC_DELREF(ref) == 0) {
efree_size(ref, sizeof(zend_reference));
}
}
if (Z_REFCOUNTED_P(zv)) {
if (Z_TYPE_P(zv) == IS_ARRAY) {
if (!is_array_cacheable(zv)) {
/* Can't cache */
return NULL;
}
SEPARATE_ARRAY(zv);
} else if (Z_TYPE_P(zv) == IS_OBJECT || Z_TYPE_P(zv) == IS_RESOURCE) {
/* Can't cache */
return NULL;
}
}
} ZEND_HASH_FOREACH_END();
}
} ZEND_HASH_FOREACH_END();
}
SHM_UNPROTECT();
zend_shared_alloc_lock();
entry = ce->inheritance_cache;
while (entry) {
entry = zend_accel_inheritance_cache_find(entry, ce, parent, traits_and_interfaces, &needs_autoload);
if (entry) {
if (!needs_autoload) {
zend_shared_alloc_unlock();
SHM_PROTECT();
zend_map_ptr_extend(ZCSG(map_ptr_last));
return entry->ce;
}
ZEND_ASSERT(0); // entry = entry->next; // This shouldn't be posible ???
}
}
zend_shared_alloc_init_xlat_table();
memset(&dummy, 0, sizeof(dummy));
dummy.size = ZEND_ALIGNED_SIZE(
sizeof(zend_inheritance_cache_entry) -
sizeof(void*) +
(sizeof(void*) * (proto->num_traits + proto->num_interfaces)));
if (dependencies) {
dummy.size += ZEND_ALIGNED_SIZE(zend_hash_num_elements(dependencies) * sizeof(zend_class_dependency));
}
ZCG(current_persistent_script) = &dummy;
zend_persist_class_entry_calc(ce);
size = dummy.size;
zend_shared_alloc_clear_xlat_table();
#if ZEND_MM_ALIGNMENT < 8
/* Align to 8-byte boundary */
ZCG(mem) = zend_shared_alloc(size + 8);
#else
ZCG(mem) = zend_shared_alloc(size);
#endif
if (!ZCG(mem)) {
zend_shared_alloc_destroy_xlat_table();
zend_shared_alloc_unlock();
SHM_PROTECT();
return NULL;
}
#if ZEND_MM_ALIGNMENT < 8
/* Align to 8-byte boundary */
ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 7L) & ~7L);
#endif
memset(ZCG(mem), 0, size);
entry = (zend_inheritance_cache_entry*)ZCG(mem);
ZCG(mem) = (char*)ZCG(mem) +
ZEND_ALIGNED_SIZE(
(sizeof(zend_inheritance_cache_entry) -
sizeof(void*) +
(sizeof(void*) * (proto->num_traits + proto->num_interfaces))));
entry->parent = parent;
for (i = 0; i < proto->num_traits + proto->num_interfaces; i++) {
entry->traits_and_interfaces[i] = traits_and_interfaces[i];
}
if (dependencies && zend_hash_num_elements(dependencies)) {
zend_string *dep_name;
zend_class_entry *dep_ce;
i = 0;
entry->dependencies_count = zend_hash_num_elements(dependencies);
entry->dependencies = (zend_class_dependency*)ZCG(mem);
ZEND_HASH_FOREACH_STR_KEY_PTR(dependencies, dep_name, dep_ce) {
#if ZEND_DEBUG
ZEND_ASSERT(zend_accel_in_shm(dep_name));
#endif
entry->dependencies[i].name = dep_name;
entry->dependencies[i].ce = dep_ce;
i++;
} ZEND_HASH_FOREACH_END();
ZCG(mem) = (char*)ZCG(mem) + zend_hash_num_elements(dependencies) * sizeof(zend_class_dependency);
}
entry->ce = new_ce = zend_persist_class_entry(ce);
zend_update_parent_ce(new_ce);
entry->next = proto->inheritance_cache;
proto->inheritance_cache = entry;
zend_shared_alloc_destroy_xlat_table();
zend_shared_alloc_unlock();
SHM_PROTECT();
/* Consistency check */
if ((char*)entry + size != (char*)ZCG(mem)) {
zend_accel_error(
((char*)entry + size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
"Internal error: wrong class size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
ZSTR_VAL(ce->name),
(size_t)entry,
(size_t)((char *)entry + size),
(size_t)ZCG(mem));
}
zend_map_ptr_extend(ZCSG(map_ptr_last));
return new_ce;
}
#ifdef ZEND_WIN32
static int accel_gen_uname_id(void)
{
@ -3123,7 +3388,19 @@ file_cache_fallback:
accel_use_shm_interned_strings();
}
return accel_finish_startup();
if (accel_finish_startup() != SUCCESS) {
return FAILURE;
}
if (ZCG(enabled) && accel_startup_ok) {
/* Override inheritance cache callbaks */
accelerator_orig_inheritance_cache_get = zend_inheritance_cache_get;
accelerator_orig_inheritance_cache_add = zend_inheritance_cache_add;
zend_inheritance_cache_get = zend_accel_inheritance_cache_get;
zend_inheritance_cache_add = zend_accel_inheritance_cache_add;
}
return SUCCESS;
}
static void (*orig_post_shutdown_cb)(void);
@ -3170,6 +3447,8 @@ void accel_shutdown(void)
}
zend_compile_file = accelerator_orig_compile_file;
zend_inheritance_cache_get = accelerator_orig_inheritance_cache_get;
zend_inheritance_cache_add = accelerator_orig_inheritance_cache_add;
if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
ini_entry->on_modify = orig_include_path_on_modify;
@ -3472,34 +3751,6 @@ try_again:
}
}
static void get_unresolved_initializer(zend_class_entry *ce, const char **kind, const char **name) {
zend_string *key;
zend_class_constant *c;
zend_property_info *prop;
*kind = "unknown";
*name = "";
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
*kind = "constant ";
*name = ZSTR_VAL(key);
}
} ZEND_HASH_FOREACH_END();
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->properties_info, key, prop) {
zval *val;
if (prop->flags & ZEND_ACC_STATIC) {
val = &ce->default_static_members_table[prop->offset];
} else {
val = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop->offset)];
}
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
*kind = (prop->flags & ZEND_ACC_STATIC) ? "static property $" : "property $";
*name = ZSTR_VAL(key);
}
} ZEND_HASH_FOREACH_END();
}
static bool preload_needed_types_known(zend_class_entry *ce);
static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, const char **name) {
zend_class_entry *p;
@ -3515,16 +3766,6 @@ static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, con
*name = ZSTR_VAL(ce->parent_name);
return;
}
if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
*kind = "Parent with unresolved initializers ";
*name = ZSTR_VAL(ce->parent_name);
return;
}
if (!(p->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
*kind = "Parent with unresolved property types ";
*name = ZSTR_VAL(ce->parent_name);
return;
}
}
if (ce->num_interfaces) {
@ -3559,12 +3800,11 @@ static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, con
static bool preload_try_resolve_constants(zend_class_entry *ce)
{
bool ok, changed;
bool ok, changed, was_changed = 0;
zend_class_constant *c;
zval *val;
EG(exception) = (void*)(uintptr_t)-1; /* prevent error reporting */
CG(in_compilation) = 1; /* prevent autoloading */
do {
ok = 1;
changed = 0;
@ -3572,62 +3812,65 @@ static bool preload_try_resolve_constants(zend_class_entry *ce)
val = &c->value;
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
if (EXPECTED(zval_update_constant_ex(val, c->ce) == SUCCESS)) {
changed = 1;
was_changed = changed = 1;
} else {
ok = 0;
}
}
} ZEND_HASH_FOREACH_END();
if (ok) {
ce->ce_flags &= ~ZEND_ACC_HAS_AST_CONSTANTS;
}
if (ce->default_properties_count) {
uint32_t i;
bool resolved = 1;
for (i = 0; i < ce->default_properties_count; i++) {
val = &ce->default_properties_table[i];
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
zend_property_info *prop = ce->properties_info_table[i];
if (UNEXPECTED(zval_update_constant_ex(val, prop->ce) != SUCCESS)) {
ok = 0;
resolved = ok = 0;
}
}
}
if (resolved) {
ce->ce_flags &= ~ZEND_ACC_HAS_AST_PROPERTIES;
}
}
if (ce->default_static_members_count) {
uint32_t count = ce->parent ? ce->default_static_members_count - ce->parent->default_static_members_count : ce->default_static_members_count;
bool resolved = 1;
val = ce->default_static_members_table + ce->default_static_members_count - 1;
while (count) {
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
if (UNEXPECTED(zval_update_constant_ex(val, ce) != SUCCESS)) {
ok = 0;
resolved = ok = 0;
}
}
val--;
count--;
}
if (resolved) {
ce->ce_flags &= ~ZEND_ACC_HAS_AST_STATICS;
}
}
} while (changed && !ok);
EG(exception) = NULL;
CG(in_compilation) = 0;
return ok;
if (ok) {
ce->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
}
return ok || was_changed;
}
static zend_class_entry *preload_fetch_resolved_ce(zend_string *name, zend_class_entry *self_ce) {
static zend_class_entry *preload_fetch_resolved_ce(zend_string *name) {
zend_string *lcname = zend_string_tolower(name);
zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lcname);
zend_string_release(lcname);
if (!ce) {
return NULL;
}
if (ce == self_ce) {
/* Ignore the following requirements if this is the class referring to itself */
return ce;
}
if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
return NULL;
}
if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
return NULL;
}
return ce;
}
@ -3641,7 +3884,7 @@ static bool preload_try_resolve_property_types(zend_class_entry *ce)
ZEND_TYPE_FOREACH(prop->type, single_type) {
if (ZEND_TYPE_HAS_NAME(*single_type)) {
zend_class_entry *p =
preload_fetch_resolved_ce(ZEND_TYPE_NAME(*single_type), ce);
preload_fetch_resolved_ce(ZEND_TYPE_NAME(*single_type));
if (!p) {
ok = 0;
continue;
@ -3650,6 +3893,9 @@ static bool preload_try_resolve_property_types(zend_class_entry *ce)
}
} ZEND_TYPE_FOREACH_END();
} ZEND_HASH_FOREACH_END();
if (ok) {
ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;
}
}
return ok;
@ -3778,12 +4024,6 @@ static void preload_link(void)
parent = zend_hash_find_ptr(EG(class_table), key);
zend_string_release(key);
if (!parent) continue;
if (!(parent->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
continue;
}
if (!(parent->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
continue;
}
}
if (ce->num_interfaces) {
@ -3794,10 +4034,6 @@ static void preload_link(void)
found = 0;
break;
}
if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
found = 0;
break;
}
}
if (!found) continue;
}
@ -3822,11 +4058,8 @@ static void preload_link(void)
continue;
}
{
zend_string *key = zend_string_tolower(ce->name);
zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, key);
zend_string_release(key);
}
key = zend_string_tolower(ce->name);
zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, key);
if (EXPECTED(zv)) {
/* Set filename & lineno information for inheritance errors */
@ -3842,7 +4075,8 @@ static void preload_link(void)
} else {
CG(zend_lineno) = ce->info.user.line_start;
}
if (zend_do_link_class(ce, NULL) == FAILURE) {
ce = zend_do_link_class(ce, NULL, key);
if (!ce) {
ZEND_ASSERT(0 && "Class linking failed?");
}
CG(in_compilation) = 0;
@ -3850,22 +4084,41 @@ static void preload_link(void)
changed = 1;
}
}
if (ce->ce_flags & ZEND_ACC_LINKED) {
if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
if ((ce->ce_flags & ZEND_ACC_TRAIT) /* don't update traits */
|| preload_try_resolve_constants(ce)) {
ce->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
changed = 1;
}
}
if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
if ((ce->ce_flags & ZEND_ACC_TRAIT) /* don't update traits */
|| preload_try_resolve_property_types(ce)) {
ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;
zend_string_release(key);
}
} ZEND_HASH_FOREACH_END();
} while (changed);
/* Resolve property types */
ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) {
ce = Z_PTR_P(zv);
if (ce->type == ZEND_INTERNAL_CLASS) {
break;
}
if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
if (!(ce->ce_flags & ZEND_ACC_TRAIT)) {
preload_try_resolve_property_types(ce);
}
}
} ZEND_HASH_FOREACH_END();
do {
changed = 0;
ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) {
ce = Z_PTR_P(zv);
if (ce->type == ZEND_INTERNAL_CLASS) {
break;
}
if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
if (!(ce->ce_flags & ZEND_ACC_TRAIT)) { /* don't update traits */
CG(in_compilation) = 1; /* prevent autoloading */
if (preload_try_resolve_constants(ce)) {
changed = 1;
}
CG(in_compilation) = 0;
}
}
} ZEND_HASH_FOREACH_END();
@ -3895,18 +4148,6 @@ static void preload_link(void)
ZSTR_VAL(ce->name), kind, name);
}
zend_string_release(key);
} else if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
const char *kind, *name;
get_unresolved_initializer(ce, &kind, &name);
zend_error_at(
E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start,
"Can't preload class %s with unresolved initializer for %s%s",
ZSTR_VAL(ce->name), kind, name);
} else if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
zend_error_at(
E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start,
"Can't preload class %s with unresolved property types",
ZSTR_VAL(ce->name));
} else {
continue;
}
@ -3960,7 +4201,7 @@ static inline int preload_update_class_constants(zend_class_entry *ce) {
* maybe-uninitialized analysis. */
int result;
zend_try {
result = zend_update_class_constants(ce);
result = preload_try_resolve_constants(ce) ? SUCCESS : FAILURE;
} zend_catch {
result = FAILURE;
} zend_end_try();
@ -3976,13 +4217,6 @@ static zend_class_entry *preload_load_prop_type(zend_property_info *prop, zend_s
} else {
ce = zend_lookup_class(name);
}
if (ce) {
return ce;
}
zend_error_noreturn(E_ERROR,
"Failed to load class %s used by typed property %s::$%s during preloading",
ZSTR_VAL(name), ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name));
return ce;
}
@ -4008,12 +4242,7 @@ static void preload_ensure_classes_loadable() {
}
if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
if (preload_update_class_constants(ce) == FAILURE) {
zend_error_noreturn(E_ERROR,
"Failed to resolve initializers of class %s during preloading",
ZSTR_VAL(ce->name));
}
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED);
preload_update_class_constants(ce);
}
if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
@ -4025,7 +4254,9 @@ static void preload_ensure_classes_loadable() {
if (ZEND_TYPE_HAS_NAME(*single_type)) {
zend_class_entry *ce = preload_load_prop_type(
prop, ZEND_TYPE_NAME(*single_type));
ZEND_TYPE_SET_CE(*single_type, ce);
if (ce) {
ZEND_TYPE_SET_CE(*single_type, ce);
}
}
} ZEND_TYPE_FOREACH_END();
} ZEND_HASH_FOREACH_END();
@ -4558,15 +4789,8 @@ static int accel_preload(const char *config, bool in_child)
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_PRELOADED);
if (ce->default_static_members_count) {
zend_cleanup_internal_class_data(ce);
if (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) {
int i;
for (i = 0; i < ce->default_static_members_count; i++) {
if (Z_TYPE(ce->default_static_members_table[i]) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
break;
}
}
if (ce->ce_flags & ZEND_ACC_HAS_AST_STATICS) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
}
}
if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
@ -4701,8 +4925,6 @@ static int accel_preload(const char *config, bool in_child)
SHM_PROTECT();
HANDLE_UNBLOCK_INTERRUPTIONS();
ZEND_ASSERT(ZCSG(preload_script)->arena_size == 0);
preload_load();
/* Store individual scripts with unlinked classes */

View File

@ -129,8 +129,6 @@ typedef struct _zend_persistent_script {
void *mem; /* shared memory area used by script structures */
size_t size; /* size of used shared memory */
void *arena_mem; /* part that should be copied into process */
size_t arena_size;
/* All entries that shouldn't be counted in the ADLER32
* checksum must be declared in this struct
@ -227,9 +225,7 @@ typedef struct _zend_accel_globals {
#endif
/* preallocated shared-memory block to save current script */
void *mem;
void *arena_mem;
zend_persistent_script *current_persistent_script;
bool is_immutable_class;
/* Temporary storage for warnings before they are moved into persistent_script. */
bool record_warnings;
uint32_t num_warnings;

View File

@ -9878,7 +9878,10 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
if (func && !(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
if (ZEND_MAP_PTR_IS_OFFSET(func->op_array.run_time_cache)) {
| MEM_OP2_2_ZTS add, r2, aword, compiler_globals, map_ptr_base, r1
} else if (!zend_accel_in_shm(func->op_array.opcodes)) {
} else if ((func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)
&& (!func->op_array.scope || (func->op_array.scope->ce_flags & ZEND_ACC_LINKED))) {
| MEM_OP2_2_ZTS add, r2, aword, compiler_globals, map_ptr_base, r1
} else {
/* the called op_array may be not persisted yet */
| test r2, 1
| jz >1
@ -12611,11 +12614,12 @@ static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zen
return 1;
}
static zend_property_info* zend_get_known_property_info(zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
static zend_property_info* zend_get_known_property_info(const zend_op_array *op_array, zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
{
zend_property_info *info = NULL;
if (!ce ||
if ((on_this && (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) ||
!ce ||
!(ce->ce_flags & ZEND_ACC_LINKED) ||
(ce->ce_flags & ZEND_ACC_TRAIT) ||
ce->create_object) {
@ -12745,7 +12749,7 @@ static int zend_jit_fetch_obj(dasm_State **Dst,
member = RT_CONSTANT(opline, opline->op2);
ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
prop_info = zend_get_known_property_info(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename);
prop_info = zend_get_known_property_info(op_array, ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename);
if (opline->op1_type == IS_UNUSED || use_this) {
| GET_ZVAL_PTR FCARG1a, this_addr
@ -12784,7 +12788,7 @@ static int zend_jit_fetch_obj(dasm_State **Dst,
}
if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
prop_info = zend_get_known_property_info(trace_ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename);
prop_info = zend_get_known_property_info(op_array, trace_ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename);
if (prop_info) {
ce = trace_ce;
ce_is_instanceof = 0;
@ -13140,7 +13144,7 @@ static int zend_jit_incdec_obj(dasm_State **Dst,
member = RT_CONSTANT(opline, opline->op2);
ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
name = Z_STR_P(member);
prop_info = zend_get_known_property_info(ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
prop_info = zend_get_known_property_info(op_array, ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
if (opline->op1_type == IS_UNUSED || use_this) {
| GET_ZVAL_PTR FCARG1a, this_addr
@ -13188,7 +13192,7 @@ static int zend_jit_incdec_obj(dasm_State **Dst,
}
if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
prop_info = zend_get_known_property_info(trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
prop_info = zend_get_known_property_info(op_array, trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
if (prop_info) {
ce = trace_ce;
ce_is_instanceof = 0;
@ -13514,7 +13518,7 @@ static int zend_jit_assign_obj_op(dasm_State **Dst,
member = RT_CONSTANT(opline, opline->op2);
ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
name = Z_STR_P(member);
prop_info = zend_get_known_property_info(ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
prop_info = zend_get_known_property_info(op_array, ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
if (opline->op1_type == IS_UNUSED || use_this) {
| GET_ZVAL_PTR FCARG1a, this_addr
@ -13571,7 +13575,7 @@ static int zend_jit_assign_obj_op(dasm_State **Dst,
}
if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
prop_info = zend_get_known_property_info(trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
prop_info = zend_get_known_property_info(op_array, trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
if (prop_info) {
ce = trace_ce;
ce_is_instanceof = 0;
@ -13847,7 +13851,7 @@ static int zend_jit_assign_obj(dasm_State **Dst,
member = RT_CONSTANT(opline, opline->op2);
ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
name = Z_STR_P(member);
prop_info = zend_get_known_property_info(ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
prop_info = zend_get_known_property_info(op_array, ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
if (opline->op1_type == IS_UNUSED || use_this) {
| GET_ZVAL_PTR FCARG1a, this_addr
@ -13903,7 +13907,7 @@ static int zend_jit_assign_obj(dasm_State **Dst,
}
if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
prop_info = zend_get_known_property_info(trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
prop_info = zend_get_known_property_info(op_array, trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
if (prop_info) {
ce = trace_ce;
ce_is_instanceof = 0;

View File

@ -12,18 +12,8 @@ if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows
?>
--FILE--
<?php
class B extends A {
function foo(): int { return 24; }
}
$c = new C;
var_dump($c->foo());
?>
--EXPECTF--
Warning: Can't preload unlinked class C: Parent with unresolved initializers B in %s on line %d
Warning: Can't preload class B with unresolved initializer for constant X in %s on line %d
Fatal error: Uncaught Error: Class "C" not found in %sbug78014.php:5
Stack trace:
#0 {main}
thrown in %sbug78014.php on line 5
--EXPECT--
int(42)

View File

@ -9,13 +9,16 @@ opcache.preload={PWD}/preload_undef_const.inc
<?php
require_once('skipif.inc');
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
if (getenv('SKIP_ASAN')) die('xfail Startup failure leak');
?>
--FILE--
<?php
var_dump(class_exists('Foo'));
try {
new Foo();
} catch (Throwable $ex) {
echo $ex->getMessage() . "\n";
}
?>
--EXPECT--
Fatal error: Undefined constant self::DOES_NOT_EXIST in Unknown on line 0
Fatal error: Failed to resolve initializers of class Foo during preloading in Unknown on line 0
bool(true)
Undefined constant self::DOES_NOT_EXIST

View File

@ -9,14 +9,18 @@ opcache.preload={PWD}/preload_undef_const_2.inc
<?php
require_once('skipif.inc');
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
if (getenv('SKIP_ASAN')) die('xfail Startup failure leak');
?>
--FILE--
<?php
var_dump(trait_exists('T'));
var_dump(class_exists('Foo'));
try {
new Foo();
} catch (Throwable $ex) {
echo $ex->getMessage() . "\n";
}
?>
--EXPECT--
Fatal error: Undefined constant "UNDEF" in Unknown on line 0
Fatal error: Failed to resolve initializers of class Foo during preloading in Unknown on line 0
bool(true)
bool(true)
Undefined constant "UNDEF"

View File

@ -11,5 +11,16 @@ require_once('skipif.inc');
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
?>
--FILE--
--EXPECTF--
Warning: Can't preload class Test with unresolved initializer for constant C in %s on line %d
<?php
class Foo {
}
var_dump(class_exists('Test'));
try {
new Test();
} catch (Throwable $ex) {
echo $ex->getMessage() . "\n";
}
?>
--EXPECT--
bool(true)
Undefined constant Foo::BAR

View File

@ -12,6 +12,5 @@ if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows
?>
--FILE--
OK
--EXPECTF--
Warning: Can't preload class C with unresolved property types in %s on line %d
--EXPECT--
OK

View File

@ -9,11 +9,17 @@ opcache.preload={PWD}/preload_loadable_classes_2.inc
<?php
require_once('skipif.inc');
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
if (getenv('SKIP_ASAN')) die('xfail Startup failure leak');
?>
--FILE--
Unreachable
<?php
const UNDEF = 1;
class Foo {
const UNDEF = 2;
}
var_dump(class_exists("Test"));
var_dump(Test::X);
var_dump(Test::Y);
--EXPECT--
Fatal error: Undefined constant "UNDEF" in Unknown on line 0
Fatal error: Failed to resolve initializers of class Test during preloading in Unknown on line 0
bool(true)
int(1)
int(2)

View File

@ -9,9 +9,15 @@ opcache.preload={PWD}/preload_loadable_classes_3.inc
<?php
require_once('skipif.inc');
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
if (getenv('SKIP_ASAN')) die('xfail Startup failure leak');
?>
--FILE--
Unreachable
<?php
class Foo {
}
var_dump(new Test);
?>
--EXPECT--
Fatal error: Failed to load class Foo used by typed property Test::$prop during preloading in Unknown on line 0
object(Test)#1 (0) {
["prop":protected]=>
uninitialized(Foo)
}

View File

@ -11,7 +11,12 @@ require_once('skipif.inc');
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
?>
--FILE--
<?php
class Unknown {
}
$x = new Test;
$x->prop = new Unknown;
?>
===DONE===
--EXPECTF--
Warning: Can't preload class Test with unresolved property types in %s on line %d
--EXPECT--
===DONE===

View File

@ -25,13 +25,6 @@
#include "zend_persist.h"
#include "zend_shared_alloc.h"
#define IN_ARENA(ptr) \
((void*)(ptr) >= ZCG(current_persistent_script)->arena_mem && \
(void*)(ptr) < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size))
#define ARENA_REALLOC(ptr) \
(void*)(((char*)(ptr)) + ((char*)ZCG(arena_mem) - (char*)ZCG(current_persistent_script)->arena_mem))
typedef int (*id_function_t)(void *, void *);
typedef void (*unique_copy_ctor_func_t)(void *pElement);
@ -131,238 +124,6 @@ void zend_accel_move_user_classes(HashTable *src, uint32_t count, zend_script *s
src->pDestructor = orig_dtor;
}
static void zend_hash_clone_constants(HashTable *ht)
{
Bucket *p, *end;
zend_class_constant *c;
if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
return;
}
p = emalloc(HT_SIZE(ht));
memcpy(p, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht));
HT_SET_DATA_ADDR(ht, p);
p = ht->arData;
end = p + ht->nNumUsed;
for (; p != end; p++) {
ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF);
c = Z_PTR(p->val);
if (IN_ARENA(c)) {
c = ARENA_REALLOC(c);
Z_PTR(p->val) = c;
if (IN_ARENA(c->ce)) {
c->ce = ARENA_REALLOC(c->ce);
}
}
}
}
static void zend_hash_clone_methods(HashTable *ht)
{
Bucket *p, *end;
zend_op_array *new_entry;
ht->pDestructor = ZEND_FUNCTION_DTOR;
if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
return;
}
p = emalloc(HT_SIZE(ht));
memcpy(p, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht));
HT_SET_DATA_ADDR(ht, p);
p = ht->arData;
end = p + ht->nNumUsed;
for (; p != end; p++) {
ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF);
new_entry = Z_PTR(p->val);
if (IN_ARENA(new_entry)) {
new_entry = ARENA_REALLOC(new_entry);
Z_PTR(p->val) = new_entry;
if (IN_ARENA(new_entry->scope)) {
new_entry->scope = ARENA_REALLOC(new_entry->scope);
/* update prototype */
if (IN_ARENA(new_entry->prototype)) {
new_entry->prototype = ARENA_REALLOC(new_entry->prototype);
}
}
if (IN_ARENA(ZEND_MAP_PTR(new_entry->run_time_cache))) {
ZEND_MAP_PTR_INIT(new_entry->run_time_cache, ARENA_REALLOC(ZEND_MAP_PTR(new_entry->run_time_cache)));
}
ZEND_MAP_PTR_INIT(new_entry->static_variables_ptr, &new_entry->static_variables);
}
}
}
static void zend_hash_clone_prop_info(HashTable *ht)
{
Bucket *p, *end;
zend_property_info *prop_info;
if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
return;
}
p = emalloc(HT_SIZE(ht));
memcpy(p, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht));
HT_SET_DATA_ADDR(ht, p);
p = ht->arData;
end = p + ht->nNumUsed;
for (; p != end; p++) {
ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF);
prop_info = Z_PTR(p->val);
if (IN_ARENA(prop_info)) {
prop_info = ARENA_REALLOC(prop_info);
Z_PTR(p->val) = prop_info;
if (IN_ARENA(prop_info->ce)) {
prop_info->ce = ARENA_REALLOC(prop_info->ce);
}
if (ZEND_TYPE_HAS_LIST(prop_info->type)) {
zend_type_list *list = ZEND_TYPE_LIST(prop_info->type);
if (IN_ARENA(list)) {
list = ARENA_REALLOC(list);
ZEND_TYPE_SET_PTR(prop_info->type, list);
zend_type *list_type;
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(prop_info->type), list_type) {
if (ZEND_TYPE_HAS_CE(*list_type)) {
zend_class_entry *ce = ZEND_TYPE_CE(*list_type);
if (IN_ARENA(ce)) {
ce = ARENA_REALLOC(ce);
ZEND_TYPE_SET_PTR(*list_type, ce);
}
}
} ZEND_TYPE_LIST_FOREACH_END();
}
} else if (ZEND_TYPE_HAS_CE(prop_info->type)) {
zend_class_entry *ce = ZEND_TYPE_CE(prop_info->type);
if (IN_ARENA(ce)) {
ce = ARENA_REALLOC(ce);
ZEND_TYPE_SET_PTR(prop_info->type, ce);
}
}
}
}
}
#define zend_update_inherited_handler(handler) \
{ \
if (ce->handler != NULL && IN_ARENA(ce->handler)) { \
ce->handler = ARENA_REALLOC(ce->handler); \
} \
}
/* Protects class' refcount, copies default properties, functions and class name */
static void zend_class_copy_ctor(zend_class_entry **pce)
{
zend_class_entry *ce = *pce;
zval *src, *dst, *end;
*pce = ce = ARENA_REALLOC(ce);
ce->refcount = 1;
if ((ce->ce_flags & ZEND_ACC_LINKED) && IN_ARENA(ce->parent)) {
ce->parent = ARENA_REALLOC(ce->parent);
}
if (ce->default_properties_table) {
dst = emalloc(sizeof(zval) * ce->default_properties_count);
src = ce->default_properties_table;
end = src + ce->default_properties_count;
ce->default_properties_table = dst;
for (; src != end; src++, dst++) {
ZVAL_COPY_VALUE_PROP(dst, src);
}
}
zend_hash_clone_methods(&ce->function_table);
/* static members */
if (ce->default_static_members_table) {
int i, end;
zend_class_entry *parent = !(ce->ce_flags & ZEND_ACC_LINKED) ? NULL : ce->parent;
dst = emalloc(sizeof(zval) * ce->default_static_members_count);
src = ce->default_static_members_table;
ce->default_static_members_table = dst;
i = ce->default_static_members_count - 1;
/* Copy static properties in this class */
end = parent ? parent->default_static_members_count : 0;
for (; i >= end; i--) {
zval *p = &dst[i];
ZVAL_COPY_VALUE(p, &src[i]);
}
/* Create indirections to static properties from parent classes */
while (parent && parent->default_static_members_table) {
end = parent->parent ? parent->parent->default_static_members_count : 0;
for (; i >= end; i--) {
zval *p = &dst[i];
ZVAL_INDIRECT(p, &parent->default_static_members_table[i]);
}
parent = parent->parent;
}
}
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
/* properties_info */
zend_hash_clone_prop_info(&ce->properties_info);
/* constants table */
zend_hash_clone_constants(&ce->constants_table);
if (ce->properties_info_table) {
int i;
ce->properties_info_table = ARENA_REALLOC(ce->properties_info_table);
for (i = 0; i < ce->default_properties_count; i++) {
if (IN_ARENA(ce->properties_info_table[i])) {
ce->properties_info_table[i] = ARENA_REALLOC(ce->properties_info_table[i]);
}
}
}
if (ce->num_interfaces) {
if (ce->ce_flags & ZEND_ACC_LINKED) {
zend_class_entry **interfaces = emalloc(sizeof(zend_class_entry*) * ce->num_interfaces);
uint32_t i;
for (i = 0; i < ce->num_interfaces; i++) {
if (IN_ARENA(ce->interfaces[i])) {
interfaces[i] = ARENA_REALLOC(ce->interfaces[i]);
} else {
interfaces[i] = ce->interfaces[i];
}
}
ce->interfaces = interfaces;
}
}
zend_update_inherited_handler(constructor);
zend_update_inherited_handler(destructor);
zend_update_inherited_handler(clone);
zend_update_inherited_handler(__get);
zend_update_inherited_handler(__set);
zend_update_inherited_handler(__call);
zend_update_inherited_handler(__isset);
zend_update_inherited_handler(__unset);
zend_update_inherited_handler(__tostring);
zend_update_inherited_handler(__callstatic);
zend_update_inherited_handler(__debugInfo);
zend_update_inherited_handler(__serialize);
zend_update_inherited_handler(__unserialize);
}
static void zend_accel_function_hash_copy(HashTable *target, HashTable *source)
{
zend_function *function1, *function2;
@ -452,196 +213,6 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source)
return;
}
static void zend_accel_class_hash_copy_from_shm(HashTable *target, HashTable *source)
{
Bucket *p, *end;
zval *t;
zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0);
p = source->arData;
end = p + source->nNumUsed;
for (; p != end; p++) {
ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF);
ZEND_ASSERT(p->key);
t = zend_hash_find_ex(target, p->key, 1);
if (UNEXPECTED(t != NULL)) {
if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) {
/* See comment in zend_accel_function_hash_copy(). */
continue;
} else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) {
zend_class_entry *ce1 = Z_PTR(p->val);
if (!(ce1->ce_flags & ZEND_ACC_ANON_CLASS)) {
CG(in_compilation) = 1;
zend_set_compiled_filename(ce1->info.user.filename);
CG(zend_lineno) = ce1->info.user.line_start;
zend_error(E_ERROR,
"Cannot declare %s %s, because the name is already in use",
zend_get_object_type(ce1), ZSTR_VAL(ce1->name));
return;
}
continue;
}
} else {
t = _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1);
if (!(((zend_class_entry*)Z_PTR_P(t))->ce_flags & ZEND_ACC_IMMUTABLE)) {
zend_class_copy_ctor((zend_class_entry**)&Z_PTR_P(t));
}
}
}
target->nInternalPointer = 0;
return;
}
#if __has_feature(memory_sanitizer)
# define fast_memcpy memcpy
#elif defined(__AVX__)
# include <nmmintrin.h>
# if defined(__GNUC__) && defined(__i386__)
static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size)
{
size_t delta = (char*)dest - (char*)src;
__asm__ volatile (
".align 16\n\t"
".LL0%=:\n\t"
"prefetchnta 0x40(%1)\n\t"
"vmovaps (%1), %%ymm0\n\t"
"vmovaps 0x20(%1), %%ymm1\n\t"
"vmovaps %%ymm0, (%1,%2)\n\t"
"vmovaps %%ymm1, 0x20(%1,%2)\n\t"
"addl $0x40, %1\n\t"
"subl $0x40, %0\n\t"
"ja .LL0%="
: "+r"(size),
"+r"(src)
: "r"(delta)
: "cc", "memory", "%ymm0", "%ymm1");
}
# elif defined(__GNUC__) && defined(__x86_64__)
static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size)
{
size_t delta = (char*)dest - (char*)src;
__asm__ volatile (
".align 16\n\t"
".LL0%=:\n\t"
"prefetchnta 0x40(%1)\n\t"
"vmovaps (%1), %%ymm0\n\t"
"vmovaps 0x20(%1), %%ymm1\n\t"
"vmovaps %%ymm0, (%1,%2)\n\t"
"vmovaps %%ymm1, 0x20(%1,%2)\n\t"
"addq $0x40, %1\n\t"
"subq $0x40, %0\n\t"
"ja .LL0%="
: "+r"(size),
"+r"(src)
: "r"(delta)
: "cc", "memory", "%ymm0", "%ymm1");
}
# else
static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size)
{
__m256 *dqdest = (__m256*)dest;
const __m256 *dqsrc = (const __m256*)src;
const __m256 *end = (const __m256*)((const char*)src + size);
do {
#ifdef PHP_WIN32
_mm_prefetch((const char *)(dqsrc + 2), _MM_HINT_NTA);
#else
_mm_prefetch(dqsrc + 2, _MM_HINT_NTA);
#endif
__m256 ymm0 = _mm256_load_ps((const float *)(dqsrc + 0));
__m256 ymm1 = _mm256_load_ps((const float *)(dqsrc + 1));
dqsrc += 2;
_mm256_store_ps((float *)(dqdest + 0), ymm0);
_mm256_store_ps((float *)(dqdest + 1), ymm1);
dqdest += 2;
} while (dqsrc != end);
}
# endif
#elif defined(__SSE2__)
# include <emmintrin.h>
# if defined(__GNUC__) && defined(__i386__)
static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size)
{
size_t delta = (char*)dest - (char*)src;
__asm__ volatile (
".align 16\n\t"
".LL0%=:\n\t"
"prefetchnta 0x40(%1)\n\t"
"movdqa (%1), %%xmm0\n\t"
"movdqa 0x10(%1), %%xmm1\n\t"
"movdqa 0x20(%1), %%xmm2\n\t"
"movdqa 0x30(%1), %%xmm3\n\t"
"movdqa %%xmm0, (%1,%2)\n\t"
"movdqa %%xmm1, 0x10(%1,%2)\n\t"
"movdqa %%xmm2, 0x20(%1,%2)\n\t"
"movdqa %%xmm3, 0x30(%1,%2)\n\t"
"addl $0x40, %1\n\t"
"subl $0x40, %0\n\t"
"ja .LL0%="
: "+r"(size),
"+r"(src)
: "r"(delta)
: "cc", "memory", "%xmm0", "%xmm1", "%xmm1", "%xmm2");
}
# elif defined(__GNUC__) && defined(__x86_64__) && !defined(__ILP32__)
static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size)
{
size_t delta = (char*)dest - (char*)src;
__asm__ volatile (
".align 16\n\t"
".LL0%=:\n\t"
"prefetchnta 0x40(%1)\n\t"
"movdqa (%1), %%xmm0\n\t"
"movdqa 0x10(%1), %%xmm1\n\t"
"movdqa 0x20(%1), %%xmm2\n\t"
"movdqa 0x30(%1), %%xmm3\n\t"
"movdqa %%xmm0, (%1,%2)\n\t"
"movdqa %%xmm1, 0x10(%1,%2)\n\t"
"movdqa %%xmm2, 0x20(%1,%2)\n\t"
"movdqa %%xmm3, 0x30(%1,%2)\n\t"
"addq $0x40, %1\n\t"
"subq $0x40, %0\n\t"
"ja .LL0%="
: "+r"(size),
"+r"(src)
: "r"(delta)
: "cc", "memory", "%xmm0", "%xmm1", "%xmm1", "%xmm2");
}
# else
static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size)
{
__m128i *dqdest = (__m128i*)dest;
const __m128i *dqsrc = (const __m128i*)src;
const __m128i *end = (const __m128i*)((const char*)src + size);
do {
#ifdef PHP_WIN32
_mm_prefetch((const char *)(dqsrc + 4), _MM_HINT_NTA);
#else
_mm_prefetch(dqsrc + 4, _MM_HINT_NTA);
#endif
__m128i xmm0 = _mm_load_si128(dqsrc + 0);
__m128i xmm1 = _mm_load_si128(dqsrc + 1);
__m128i xmm2 = _mm_load_si128(dqsrc + 2);
__m128i xmm3 = _mm_load_si128(dqsrc + 3);
dqsrc += 4;
_mm_store_si128(dqdest + 0, xmm0);
_mm_store_si128(dqdest + 1, xmm1);
_mm_store_si128(dqdest + 2, xmm2);
_mm_store_si128(dqdest + 3, xmm3);
dqdest += 4;
} while (dqsrc != end);
}
# endif
#endif
zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory)
{
zend_op_array *op_array;
@ -654,27 +225,11 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script,
zend_accel_function_hash_copy(CG(function_table), &persistent_script->script.function_table);
}
if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) {
zend_accel_class_hash_copy(CG(class_table), &persistent_script->script.class_table);
}
if (EXPECTED(from_shared_memory)) {
ZCG(current_persistent_script) = persistent_script;
ZCG(arena_mem) = NULL;
if (EXPECTED(persistent_script->arena_size)) {
#if defined(__AVX__) || defined(__SSE2__)
/* Target address must be aligned to 64-byte boundary */
_mm_prefetch(persistent_script->arena_mem, _MM_HINT_NTA);
ZCG(arena_mem) = zend_arena_alloc(&CG(arena), persistent_script->arena_size + 64);
ZCG(arena_mem) = (void*)(((zend_uintptr_t)ZCG(arena_mem) + 63L) & ~63L);
fast_memcpy(ZCG(arena_mem), persistent_script->arena_mem, persistent_script->arena_size);
#else
ZCG(arena_mem) = zend_arena_alloc(&CG(arena), persistent_script->arena_size);
memcpy(ZCG(arena_mem), persistent_script->arena_mem, persistent_script->arena_size);
#endif
}
/* Copy all the necessary stuff from shared memory to regular memory, and protect the shared script */
if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) {
zend_accel_class_hash_copy_from_shm(CG(class_table), &persistent_script->script.class_table);
}
/* Register __COMPILER_HALT_OFFSET__ constant */
if (persistent_script->compiler_halt_offset != 0 &&
persistent_script->script.filename) {
@ -688,12 +243,7 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script,
zend_string_release_ex(name, 0);
}
ZCG(current_persistent_script) = NULL;
zend_map_ptr_extend(ZCSG(map_ptr_last));
} else /* if (!from_shared_memory) */ {
if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) {
zend_accel_class_hash_copy(CG(class_table), &persistent_script->script.class_table);
}
}
if (persistent_script->script.first_early_binding_opline != (uint32_t)-1) {

View File

@ -450,6 +450,9 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
zend_file_cache_metainfo *info,
void *buf)
{
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, NULL);
ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
/* Check whether this op_array has already been serialized. */
if (IS_SERIALIZED(op_array->opcodes)) {
ZEND_ASSERT(op_array->scope && "Only method op_arrays should be shared");
@ -465,13 +468,6 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
}
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
if (op_array->fn_flags & ZEND_ACC_IMMUTABLE) {
ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
} else {
SERIALIZE_PTR(ZEND_MAP_PTR(op_array->run_time_cache));
}
if (op_array->scope) {
if (UNEXPECTED(zend_shared_alloc_get_xlat_entry(op_array->opcodes))) {
op_array->refcount = (uint32_t*)(intptr_t)-1;
@ -855,7 +851,8 @@ static void zend_file_cache_serialize_class(zval *zv,
SERIALIZE_PTR(ce->iterator_funcs_ptr);
}
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
ZEND_MAP_PTR_INIT(ce->static_members_table, NULL);
ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
}
static void zend_file_cache_serialize_warnings(
@ -901,7 +898,6 @@ static void zend_file_cache_serialize(zend_persistent_script *script,
zend_file_cache_serialize_op_array(&new_script->script.main_op_array, script, info, buf);
zend_file_cache_serialize_warnings(new_script, info, buf);
SERIALIZE_PTR(new_script->arena_mem);
new_script->mem = NULL;
}
@ -1209,6 +1205,13 @@ static void zend_file_cache_unserialize_type(
zend_string *type_name = ZEND_TYPE_NAME(*type);
UNSERIALIZE_STR(type_name);
ZEND_TYPE_SET_PTR(*type, type_name);
if (!(script->corrupted)) {
// TODO: we may use single map_ptr slot for each class name ???
type->type_mask |= _ZEND_TYPE_CACHE_BIT;
type->ce_cache__ptr = (uint32_t)(uintptr_t)zend_map_ptr_new();
} else {
type->type_mask &= ~_ZEND_TYPE_CACHE_BIT;
}
} else if (ZEND_TYPE_HAS_CE(*type)) {
zend_class_entry *ce = ZEND_TYPE_CE(*type);
UNSERIALIZE_PTR(ce);
@ -1220,6 +1223,26 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
zend_persistent_script *script,
void *buf)
{
if (!(script->corrupted)
&& op_array != &script->script.main_op_array) {
op_array->fn_flags |= ZEND_ACC_IMMUTABLE;
if (op_array->static_variables) {
ZEND_MAP_PTR_NEW(op_array->static_variables_ptr);
} else {
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
}
ZEND_MAP_PTR_NEW(op_array->run_time_cache);
} else {
op_array->fn_flags &= ~ZEND_ACC_IMMUTABLE;
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
if (op_array != &script->script.main_op_array) {
ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*)));
ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
} else {
ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
}
}
/* Check whether this op_array has already been unserialized. */
if (IS_UNSERIALIZED(op_array->opcodes)) {
ZEND_ASSERT(op_array->scope && "Only method op_arrays should be shared");
@ -1235,26 +1258,6 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR);
}
if (op_array->fn_flags & ZEND_ACC_IMMUTABLE) {
if (op_array->static_variables) {
ZEND_MAP_PTR_NEW(op_array->static_variables_ptr);
} else {
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
}
ZEND_MAP_PTR_NEW(op_array->run_time_cache);
} else {
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
if (ZEND_MAP_PTR(op_array->run_time_cache)) {
if (script->corrupted) {
/* Not in SHM: Use serialized arena pointer. */
UNSERIALIZE_PTR(ZEND_MAP_PTR(op_array->run_time_cache));
} else {
/* In SHM: Allocate new pointer. */
ZEND_MAP_PTR_NEW(op_array->run_time_cache);
}
}
}
if (op_array->refcount) {
op_array->refcount = NULL;
UNSERIALIZE_PTR(op_array->literals);
@ -1605,10 +1608,20 @@ static void zend_file_cache_unserialize_class(zval *zv,
UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_next);
}
if (ce->ce_flags & ZEND_ACC_IMMUTABLE && ce->default_static_members_table) {
ZEND_MAP_PTR_NEW(ce->static_members_table);
if (!(script->corrupted)) {
ce->ce_flags |= ZEND_ACC_IMMUTABLE;
ce->ce_flags &= ~ZEND_ACC_FILE_CACHED;
if (ce->ce_flags & ZEND_ACC_IMMUTABLE && ce->default_static_members_table) {
ZEND_MAP_PTR_NEW(ce->static_members_table);
} else {
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
}
ZEND_MAP_PTR_NEW(ce->mutable_data);
} else {
ce->ce_flags &= ~ZEND_ACC_IMMUTABLE;
ce->ce_flags |= ZEND_ACC_FILE_CACHED;
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
}
}
@ -1637,8 +1650,6 @@ static void zend_file_cache_unserialize(zend_persistent_script *script,
script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR);
zend_file_cache_unserialize_op_array(&script->script.main_op_array, script, buf);
zend_file_cache_unserialize_warnings(script, buf);
UNSERIALIZE_PTR(script->arena_mem);
}
zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handle)

View File

@ -115,7 +115,11 @@ static void zend_hash_persist(HashTable *ht)
}
if (HT_FLAGS(ht) & HASH_FLAG_PACKED) {
void *data = HT_GET_DATA_ADDR(ht);
data = zend_shared_memdup_free(data, HT_USED_SIZE(ht));
if (GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) {
data = zend_shared_memdup(data, HT_USED_SIZE(ht));
} else {
data = zend_shared_memdup_free(data, HT_USED_SIZE(ht));
}
HT_SET_DATA_ADDR(ht, data);
} else if (ht->nNumUsed > HT_MIN_SIZE && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) {
/* compact table */
@ -133,7 +137,9 @@ static void zend_hash_persist(HashTable *ht)
ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE((hash_size * sizeof(uint32_t)) + (ht->nNumUsed * sizeof(Bucket))));
HT_HASH_RESET(ht);
memcpy(ht->arData, old_buckets, ht->nNumUsed * sizeof(Bucket));
efree(old_data);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
efree(old_data);
}
/* rehash */
for (idx = 0; idx < ht->nNumUsed; idx++) {
@ -150,7 +156,9 @@ static void zend_hash_persist(HashTable *ht)
ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */
ZCG(mem) = (void*)((char*)data + ZEND_ALIGNED_SIZE(HT_USED_SIZE(ht)));
memcpy(data, old_data, HT_USED_SIZE(ht));
efree(old_data);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
efree(old_data);
}
HT_SET_DATA_ADDR(ht, data);
}
}
@ -206,28 +214,21 @@ static void zend_persist_zval(zval *z)
if (!Z_REFCOUNTED_P(z)) {
Z_ARR_P(z) = zend_shared_memdup_put(Z_ARR_P(z), sizeof(zend_array));
zend_hash_persist(Z_ARRVAL_P(z));
ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL_P(z), p) {
if (p->key) {
zend_accel_memdup_interned_string(p->key);
}
zend_persist_zval(&p->val);
} ZEND_HASH_FOREACH_END();
} else {
GC_REMOVE_FROM_BUFFER(Z_ARR_P(z));
Z_ARR_P(z) = zend_shared_memdup_put_free(Z_ARR_P(z), sizeof(zend_array));
zend_hash_persist(Z_ARRVAL_P(z));
ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL_P(z), p) {
if (p->key) {
zend_accel_store_interned_string(p->key);
}
zend_persist_zval(&p->val);
} ZEND_HASH_FOREACH_END();
/* make immutable array */
Z_TYPE_FLAGS_P(z) = 0;
GC_SET_REFCOUNT(Z_COUNTED_P(z), 2);
GC_ADD_FLAGS(Z_COUNTED_P(z), IS_ARRAY_IMMUTABLE);
}
zend_hash_persist(Z_ARRVAL_P(z));
ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL_P(z), p) {
if (p->key) {
zend_accel_store_interned_string(p->key);
}
zend_persist_zval(&p->val);
} ZEND_HASH_FOREACH_END();
/* make immutable array */
Z_TYPE_FLAGS_P(z) = 0;
GC_SET_REFCOUNT(Z_COUNTED_P(z), 2);
GC_ADD_FLAGS(Z_COUNTED_P(z), IS_ARRAY_IMMUTABLE);
}
break;
case IS_CONSTANT_AST:
@ -235,7 +236,7 @@ static void zend_persist_zval(zval *z)
if (new_ptr) {
Z_AST_P(z) = new_ptr;
Z_TYPE_FLAGS_P(z) = 0;
} else {
} else if (!zend_accel_in_shm(Z_AST_P(z))) {
zend_ast_ref *old_ref = Z_AST_P(z);
Z_AST_P(z) = zend_shared_memdup_put(Z_AST_P(z), sizeof(zend_ast_ref));
zend_persist_ast(GC_AST(old_ref));
@ -258,6 +259,10 @@ static HashTable *zend_persist_attributes(HashTable *attributes)
uint32_t i;
zval *v;
if (zend_accel_in_shm(attributes)) {
return attributes;
}
zend_hash_persist(attributes);
ZEND_HASH_FOREACH_VAL(attributes, v) {
@ -285,17 +290,12 @@ static HashTable *zend_persist_attributes(HashTable *attributes)
return ptr;
}
static void zend_persist_type(zend_type *type, bool use_arena) {
static void zend_persist_type(zend_type *type) {
if (ZEND_TYPE_HAS_LIST(*type)) {
zend_type_list *list = ZEND_TYPE_LIST(*type);
if (ZEND_TYPE_USES_ARENA(*type)) {
if (!ZCG(is_immutable_class) && use_arena) {
list = zend_shared_memdup_arena_put(list, ZEND_TYPE_LIST_SIZE(list->num_types));
} else {
/* Moved from arena to SHM because type list was fully resolved. */
list = zend_shared_memdup_put(list, ZEND_TYPE_LIST_SIZE(list->num_types));
ZEND_TYPE_FULL_MASK(*type) &= ~_ZEND_TYPE_ARENA_BIT;
}
list = zend_shared_memdup_put(list, ZEND_TYPE_LIST_SIZE(list->num_types));
ZEND_TYPE_FULL_MASK(*type) &= ~_ZEND_TYPE_ARENA_BIT;
} else {
list = zend_shared_memdup_put_free(list, ZEND_TYPE_LIST_SIZE(list->num_types));
}
@ -308,6 +308,11 @@ static void zend_persist_type(zend_type *type, bool use_arena) {
zend_string *type_name = ZEND_TYPE_NAME(*single_type);
zend_accel_store_interned_string(type_name);
ZEND_TYPE_SET_PTR(*single_type, type_name);
if (!ZCG(current_persistent_script)->corrupted) {
// TODO: we may use single map_ptr slot for each class name ???
single_type->type_mask |= _ZEND_TYPE_CACHE_BIT;
single_type->ce_cache__ptr = (uint32_t)(uintptr_t)zend_map_ptr_new();
}
}
} ZEND_TYPE_FOREACH_END();
}
@ -352,6 +357,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
if (scope) {
op_array->scope = scope;
}
if (op_array->prototype) {
zend_function *ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype);
@ -367,7 +373,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
op_array->static_variables = zend_shared_alloc_get_xlat_entry(op_array->static_variables);
ZEND_ASSERT(op_array->static_variables != NULL);
}
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
if (op_array->literals) {
op_array->literals = zend_shared_alloc_get_xlat_entry(op_array->literals);
ZEND_ASSERT(op_array->literals != NULL);
@ -435,7 +440,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
op_array->prototype = NULL;
}
if (op_array->static_variables) {
if (op_array->static_variables && !zend_accel_in_shm(op_array->static_variables)) {
Bucket *p;
zend_hash_persist(op_array->static_variables);
@ -449,7 +454,12 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
GC_SET_REFCOUNT(op_array->static_variables, 2);
GC_TYPE_INFO(op_array->static_variables) = GC_ARRAY | ((IS_ARRAY_IMMUTABLE|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT);
}
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
if (op_array->scope
&& !(op_array->fn_flags & ZEND_ACC_CLOSURE)
&& (op_array->scope->ce_flags & ZEND_ACC_CACHED)) {
return;
}
if (op_array->literals) {
zval *p, *end;
@ -551,7 +561,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
efree(op_array->opcodes);
op_array->opcodes = new_opcodes;
ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
}
if (op_array->filename) {
@ -575,7 +584,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
if (arg_info[i].name) {
zend_accel_store_interned_string(arg_info[i].name);
}
zend_persist_type(&arg_info[i].type, 0);
zend_persist_type(&arg_info[i].type);
}
if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
arg_info++;
@ -636,13 +645,11 @@ static void zend_persist_op_array(zval *zv)
ZEND_MAP_PTR_NEW(op_array->static_variables_ptr);
}
} else {
ZEND_MAP_PTR_INIT(op_array->run_time_cache, ZCG(arena_mem));
ZCG(arena_mem) = (void*)(((char*)ZCG(arena_mem)) + ZEND_ALIGNED_SIZE(sizeof(void*)));
ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
}
}
static void zend_persist_class_method(zval *zv)
static void zend_persist_class_method(zval *zv, zend_class_entry *ce)
{
zend_op_array *op_array = Z_PTR_P(zv);
zend_op_array *old_op_array;
@ -654,11 +661,7 @@ static void zend_persist_class_method(zval *zv)
if (old_op_array) {
Z_PTR_P(zv) = old_op_array;
} else {
if (ZCG(is_immutable_class)) {
op_array = Z_PTR_P(zv) = zend_shared_memdup_put(op_array, sizeof(zend_internal_function));
} else {
op_array = Z_PTR_P(zv) = zend_shared_memdup_arena_put(op_array, sizeof(zend_internal_function));
}
op_array = Z_PTR_P(zv) = zend_shared_memdup_put(op_array, sizeof(zend_internal_function));
if (op_array->scope) {
void *persist_ptr;
@ -676,6 +679,14 @@ static void zend_persist_class_method(zval *zv)
return;
}
if ((op_array->fn_flags & ZEND_ACC_IMMUTABLE)
&& !op_array->static_variables
&& !ZCG(current_persistent_script)->corrupted
&& zend_accel_in_shm(op_array)) {
zend_shared_alloc_register_xlat_entry(op_array, op_array);
return;
}
old_op_array = zend_shared_alloc_get_xlat_entry(op_array);
if (old_op_array) {
Z_PTR_P(zv) = old_op_array;
@ -692,33 +703,27 @@ static void zend_persist_class_method(zval *zv)
}
return;
}
if (ZCG(is_immutable_class)) {
op_array = Z_PTR_P(zv) = zend_shared_memdup_put(op_array, sizeof(zend_op_array));
} else {
op_array = Z_PTR_P(zv) = zend_shared_memdup_arena_put(op_array, sizeof(zend_op_array));
}
op_array = Z_PTR_P(zv) = zend_shared_memdup_put(op_array, sizeof(zend_op_array));
zend_persist_op_array_ex(op_array, NULL);
if (ZCG(is_immutable_class)) {
if ((ce->ce_flags & ZEND_ACC_LINKED)
&& (ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
op_array->fn_flags |= ZEND_ACC_IMMUTABLE;
ZEND_MAP_PTR_NEW(op_array->run_time_cache);
if (op_array->static_variables) {
ZEND_MAP_PTR_NEW(op_array->static_variables_ptr);
}
} else {
ZEND_MAP_PTR_INIT(op_array->run_time_cache, ZCG(arena_mem));
ZCG(arena_mem) = (void*)(((char*)ZCG(arena_mem)) + ZEND_ALIGNED_SIZE(sizeof(void*)));
ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
}
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
}
}
static zend_property_info *zend_persist_property_info(zend_property_info *prop)
{
zend_class_entry *ce;
if (ZCG(is_immutable_class)) {
prop = zend_shared_memdup_put(prop, sizeof(zend_property_info));
} else {
prop = zend_shared_memdup_arena_put(prop, sizeof(zend_property_info));
}
prop = zend_shared_memdup_put(prop, sizeof(zend_property_info));
ce = zend_shared_alloc_get_xlat_entry(prop->ce);
if (ce) {
prop->ce = ce;
@ -738,7 +743,7 @@ static zend_property_info *zend_persist_property_info(zend_property_info *prop)
if (prop->attributes) {
prop->attributes = zend_persist_attributes(prop->attributes);
}
zend_persist_type(&prop->type, 1);
zend_persist_type(&prop->type);
return prop;
}
@ -751,11 +756,7 @@ static void zend_persist_class_constant(zval *zv)
Z_PTR_P(zv) = c;
return;
}
if (ZCG(is_immutable_class)) {
c = Z_PTR_P(zv) = zend_shared_memdup_put(Z_PTR_P(zv), sizeof(zend_class_constant));
} else {
c = Z_PTR_P(zv) = zend_shared_memdup_arena_put(Z_PTR_P(zv), sizeof(zend_class_constant));
}
c = Z_PTR_P(zv) = zend_shared_memdup_put(Z_PTR_P(zv), sizeof(zend_class_constant));
zend_persist_zval(&c->value);
ce = zend_shared_alloc_get_xlat_entry(c->ce);
if (ce) {
@ -783,39 +784,28 @@ static void zend_persist_class_constant(zval *zv)
}
}
static void zend_persist_class_entry(zval *zv)
zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce)
{
Bucket *p;
zend_class_entry *orig_ce = Z_PTR_P(zv), *ce = orig_ce;
zend_class_entry *ce = orig_ce;
if (ce->type == ZEND_USER_CLASS) {
/* The same zend_class_entry may be reused by class_alias */
zend_class_entry *new_ce = zend_shared_alloc_get_xlat_entry(ce);
if (new_ce) {
Z_PTR_P(zv) = new_ce;
return;
return new_ce;
}
if ((ce->ce_flags & ZEND_ACC_LINKED)
&& (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)
&& (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)
&& !ZCG(current_persistent_script)->corrupted) {
ZCG(is_immutable_class) = 1;
ce = Z_PTR_P(zv) = zend_shared_memdup_put(ce, sizeof(zend_class_entry));
ce = zend_shared_memdup_put(ce, sizeof(zend_class_entry));
if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) {
ce->ce_flags |= ZEND_ACC_IMMUTABLE;
} else {
ZCG(is_immutable_class) = 0;
ce = Z_PTR_P(zv) = zend_shared_memdup_arena_put(ce, sizeof(zend_class_entry));
}
ce->ce_flags |= ZEND_ACC_CACHED;
zend_accel_store_interned_string(ce->name);
if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) {
zend_accel_store_interned_string(ce->parent_name);
}
ce->inheritance_cache = NULL;
zend_hash_persist(&ce->function_table);
ZEND_HASH_FOREACH_BUCKET(&ce->function_table, p) {
ZEND_ASSERT(p->key != NULL);
zend_accel_store_interned_string(p->key);
zend_persist_class_method(&p->val);
zend_persist_class_method(&p->val, ce);
} ZEND_HASH_FOREACH_END();
HT_FLAGS(&ce->function_table) &= (HASH_FLAG_UNINITIALIZED | HASH_FLAG_STATIC_KEYS);
if (ce->default_properties_table) {
@ -826,6 +816,15 @@ static void zend_persist_class_entry(zval *zv)
zend_persist_zval(&ce->default_properties_table[i]);
}
}
if ((ce->ce_flags & ZEND_ACC_IMMUTABLE)
&& (ce->ce_flags & ZEND_ACC_LINKED)
&& !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
ZEND_MAP_PTR_NEW(ce->mutable_data);
} else if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
} else {
ce->ce_flags |= ZEND_ACC_FILE_CACHED;
}
if (ce->default_static_members_table) {
int i;
ce->default_static_members_table = zend_shared_memdup_free(ce->default_static_members_table, sizeof(zval) * ce->default_static_members_count);
@ -837,7 +836,11 @@ static void zend_persist_class_entry(zval *zv)
zend_persist_zval(&ce->default_static_members_table[i]);
}
if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
ZEND_MAP_PTR_NEW(ce->static_members_table);
if (ce->ce_flags & ZEND_ACC_LINKED) {
ZEND_MAP_PTR_NEW(ce->static_members_table);
} else {
ZEND_MAP_PTR_INIT(ce->static_members_table, NULL);
}
} else {
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
}
@ -853,23 +856,6 @@ static void zend_persist_class_entry(zval *zv)
} ZEND_HASH_FOREACH_END();
HT_FLAGS(&ce->constants_table) &= (HASH_FLAG_UNINITIALIZED | HASH_FLAG_STATIC_KEYS);
if (ce->info.user.filename) {
zend_accel_store_string(ce->info.user.filename);
}
if (ce->info.user.doc_comment) {
if (ZCG(accel_directives).save_comments) {
zend_accel_store_interned_string(ce->info.user.doc_comment);
} else {
if (!zend_shared_alloc_get_xlat_entry(ce->info.user.doc_comment)) {
zend_shared_alloc_register_xlat_entry(ce->info.user.doc_comment, ce->info.user.doc_comment);
zend_string_release_ex(ce->info.user.doc_comment, 0);
}
ce->info.user.doc_comment = NULL;
}
}
if (ce->attributes) {
ce->attributes = zend_persist_attributes(ce->attributes);
}
zend_hash_persist(&ce->properties_info);
ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, p) {
zend_property_info *prop = Z_PTR(p->val);
@ -895,13 +881,8 @@ static void zend_persist_class_entry(zval *zv)
size_t size = sizeof(zend_property_info *) * ce->default_properties_count;
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_LINKED);
if (ZCG(is_immutable_class)) {
ce->properties_info_table = zend_shared_memdup(
ce->properties_info_table, size);
} else {
ce->properties_info_table = zend_shared_memdup_arena(
ce->properties_info_table, size);
}
ce->properties_info_table = zend_shared_memdup(
ce->properties_info_table, size);
for (i = 0; i < ce->default_properties_count; i++) {
if (ce->properties_info_table[i]) {
@ -914,6 +895,41 @@ static void zend_persist_class_entry(zval *zv)
}
}
if (ce->iterator_funcs_ptr) {
ce->iterator_funcs_ptr = zend_shared_memdup(ce->iterator_funcs_ptr, sizeof(zend_class_iterator_funcs));
}
if (ce->ce_flags & ZEND_ACC_CACHED) {
return ce;
}
ce->ce_flags |= ZEND_ACC_CACHED;
zend_accel_store_interned_string(ce->name);
if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) {
zend_accel_store_interned_string(ce->parent_name);
}
if (ce->info.user.filename) {
zend_accel_store_string(ce->info.user.filename);
}
if (ce->info.user.doc_comment) {
if (ZCG(accel_directives).save_comments) {
zend_accel_store_interned_string(ce->info.user.doc_comment);
} else {
if (!zend_shared_alloc_get_xlat_entry(ce->info.user.doc_comment)) {
zend_shared_alloc_register_xlat_entry(ce->info.user.doc_comment, ce->info.user.doc_comment);
zend_string_release_ex(ce->info.user.doc_comment, 0);
}
ce->info.user.doc_comment = NULL;
}
}
if (ce->attributes) {
ce->attributes = zend_persist_attributes(ce->attributes);
}
if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_LINKED)) {
uint32_t i = 0;
@ -973,14 +989,12 @@ static void zend_persist_class_entry(zval *zv)
ce->trait_precedences, sizeof(zend_trait_precedence*) * (i + 1));
}
}
if (ce->iterator_funcs_ptr) {
ce->iterator_funcs_ptr = zend_shared_memdup(ce->iterator_funcs_ptr, sizeof(zend_class_iterator_funcs));
}
}
return ce;
}
static void zend_update_parent_ce(zend_class_entry *ce)
void zend_update_parent_ce(zend_class_entry *ce)
{
if (ce->ce_flags & ZEND_ACC_LINKED) {
if (ce->parent) {
@ -1145,7 +1159,7 @@ static void zend_accel_persist_class_table(HashTable *class_table)
ZEND_HASH_FOREACH_BUCKET(class_table, p) {
ZEND_ASSERT(p->key != NULL);
zend_accel_store_interned_string(p->key);
zend_persist_class_entry(&p->val);
Z_CE(p->val) = zend_persist_class_entry(Z_CE(p->val));
} ZEND_HASH_FOREACH_END();
ZEND_HASH_FOREACH_BUCKET(class_table, p) {
if (EXPECTED(Z_TYPE(p->val) != IS_ALIAS_PTR)) {
@ -1198,9 +1212,6 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script
ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */
#endif
script->arena_mem = ZCG(arena_mem) = ZCG(mem);
ZCG(mem) = (void*)((char*)ZCG(mem) + script->arena_size);
#ifdef HAVE_JIT
if (JIT_G(on) && for_shm) {
zend_jit_unprotect();
@ -1217,6 +1228,11 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script
zend_persist_op_array(&p->val);
} ZEND_HASH_FOREACH_END();
zend_persist_op_array_ex(&script->script.main_op_array, script);
if (!script->corrupted) {
ZEND_MAP_PTR_INIT(script->script.main_op_array.run_time_cache, NULL);
}
ZEND_MAP_PTR_INIT(script->script.main_op_array.static_variables_ptr,
&script->script.main_op_array.static_variables);
zend_persist_warnings(script);
if (for_shm) {

View File

@ -25,4 +25,8 @@
uint32_t zend_accel_script_persist_calc(zend_persistent_script *script, const char *key, unsigned int key_length, int for_shm);
zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, const char **key, unsigned int key_length, int for_shm);
void zend_persist_class_entry_calc(zend_class_entry *ce);
zend_class_entry *zend_persist_class_entry(zend_class_entry *ce);
void zend_update_parent_ce(zend_class_entry *ce);
#endif /* ZEND_PERSIST_H */

View File

@ -29,15 +29,6 @@
#define ADD_DUP_SIZE(m,s) ZCG(current_persistent_script)->size += zend_shared_memdup_size((void*)m, s)
#define ADD_SIZE(m) ZCG(current_persistent_script)->size += ZEND_ALIGNED_SIZE(m)
#define ADD_ARENA_SIZE(m) ZCG(current_persistent_script)->arena_size += ZEND_ALIGNED_SIZE(m)
#define ADD_SIZE_EX(m) do { \
if (ZCG(is_immutable_class)) { \
ADD_SIZE(m); \
} else { \
ADD_ARENA_SIZE(m); \
} \
} while (0)
# define ADD_STRING(str) ADD_DUP_SIZE((str), _ZSTR_STRUCT_SIZE(ZSTR_LEN(str)))
@ -129,10 +120,12 @@ static void zend_persist_zval_calc(zval *z)
}
break;
case IS_CONSTANT_AST:
size = zend_shared_memdup_size(Z_AST_P(z), sizeof(zend_ast_ref));
if (size) {
ADD_SIZE(size);
zend_persist_ast_calc(Z_ASTVAL_P(z));
if (!zend_accel_in_shm(Z_AST_P(z))) {
size = zend_shared_memdup_size(Z_AST_P(z), sizeof(zend_ast_ref));
if (size) {
ADD_SIZE(size);
zend_persist_ast_calc(Z_ASTVAL_P(z));
}
}
break;
default:
@ -143,7 +136,8 @@ static void zend_persist_zval_calc(zval *z)
static void zend_persist_attributes_calc(HashTable *attributes)
{
if (!zend_shared_alloc_get_xlat_entry(attributes)) {
if (!zend_shared_alloc_get_xlat_entry(attributes)
&& !zend_accel_in_shm(attributes)) {
zend_attribute *attr;
uint32_t i;
@ -166,14 +160,10 @@ static void zend_persist_attributes_calc(HashTable *attributes)
}
}
static void zend_persist_type_calc(zend_type *type, bool use_arena)
static void zend_persist_type_calc(zend_type *type)
{
if (ZEND_TYPE_HAS_LIST(*type)) {
if (ZEND_TYPE_USES_ARENA(*type) && !ZCG(is_immutable_class) && use_arena) {
ADD_ARENA_SIZE(ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(*type)->num_types));
} else {
ADD_SIZE(ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(*type)->num_types));
}
ADD_SIZE(ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(*type)->num_types));
}
zend_type *single_type;
@ -198,13 +188,15 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
}
}
if (op_array->scope && zend_shared_alloc_get_xlat_entry(op_array->opcodes)) {
/* already stored */
ADD_SIZE(ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist_calc(op_array)));
return;
if (op_array->scope) {
if (zend_shared_alloc_get_xlat_entry(op_array->opcodes)) {
/* already stored */
ADD_SIZE(ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist_calc(op_array)));
return;
}
}
if (op_array->static_variables) {
if (op_array->static_variables && !zend_accel_in_shm(op_array->static_variables)) {
if (!zend_shared_alloc_get_xlat_entry(op_array->static_variables)) {
Bucket *p;
@ -219,6 +211,12 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
}
}
if (op_array->scope
&& !(op_array->fn_flags & ZEND_ACC_CLOSURE)
&& (op_array->scope->ce_flags & ZEND_ACC_CACHED)) {
return;
}
if (op_array->literals) {
zval *p = op_array->literals;
zval *end = p + op_array->last_literal;
@ -254,7 +252,7 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
if (arg_info[i].name) {
ADD_INTERNED_STRING(arg_info[i].name);
}
zend_persist_type_calc(&arg_info[i].type, 0);
zend_persist_type_calc(&arg_info[i].type);
}
}
@ -293,9 +291,6 @@ static void zend_persist_op_array_calc(zval *zv)
ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
ADD_SIZE(sizeof(zend_op_array));
zend_persist_op_array_calc_ex(Z_PTR_P(zv));
if (ZCG(current_persistent_script)->corrupted) {
ADD_ARENA_SIZE(sizeof(void*));
}
}
static void zend_persist_class_method_calc(zval *zv)
@ -308,21 +303,26 @@ static void zend_persist_class_method_calc(zval *zv)
if (op_array->fn_flags & ZEND_ACC_ARENA_ALLOCATED) {
old_op_array = zend_shared_alloc_get_xlat_entry(op_array);
if (!old_op_array) {
ADD_SIZE_EX(sizeof(zend_internal_function));
ADD_SIZE(sizeof(zend_internal_function));
zend_shared_alloc_register_xlat_entry(op_array, Z_PTR_P(zv));
}
}
return;
}
if ((op_array->fn_flags & ZEND_ACC_IMMUTABLE)
&& !op_array->static_variables
&& !ZCG(current_persistent_script)->corrupted
&& zend_accel_in_shm(op_array)) {
zend_shared_alloc_register_xlat_entry(op_array, op_array);
return;
}
old_op_array = zend_shared_alloc_get_xlat_entry(op_array);
if (!old_op_array) {
ADD_SIZE_EX(sizeof(zend_op_array));
ADD_SIZE(sizeof(zend_op_array));
zend_persist_op_array_calc_ex(Z_PTR_P(zv));
zend_shared_alloc_register_xlat_entry(op_array, Z_PTR_P(zv));
if (!ZCG(is_immutable_class)) {
ADD_ARENA_SIZE(sizeof(void*));
}
} else {
/* If op_array is shared, the function name refcount is still incremented for each use,
* so we need to release it here. We remembered the original function name in xlat. */
@ -336,9 +336,9 @@ static void zend_persist_class_method_calc(zval *zv)
static void zend_persist_property_info_calc(zend_property_info *prop)
{
ADD_SIZE_EX(sizeof(zend_property_info));
ADD_SIZE(sizeof(zend_property_info));
ADD_INTERNED_STRING(prop->name);
zend_persist_type_calc(&prop->type, 1);
zend_persist_type_calc(&prop->type);
if (ZCG(accel_directives).save_comments && prop->doc_comment) {
ADD_STRING(prop->doc_comment);
}
@ -353,7 +353,7 @@ static void zend_persist_class_constant_calc(zval *zv)
if (!zend_shared_alloc_get_xlat_entry(c)) {
zend_shared_alloc_register_xlat_entry(c, c);
ADD_SIZE_EX(sizeof(zend_class_constant));
ADD_SIZE(sizeof(zend_class_constant));
zend_persist_zval_calc(&c->value);
if (ZCG(accel_directives).save_comments && c->doc_comment) {
ADD_STRING(c->doc_comment);
@ -364,29 +364,8 @@ static void zend_persist_class_constant_calc(zval *zv)
}
}
static void check_property_type_resolution(zend_class_entry *ce) {
zend_property_info *prop;
if (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) {
/* Preloading might have computed this already. */
return;
}
if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) {
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
zend_type *single_type;
ZEND_TYPE_FOREACH(prop->type, single_type) {
if (ZEND_TYPE_HAS_NAME(*single_type)) {
return;
}
} ZEND_TYPE_FOREACH_END();
} ZEND_HASH_FOREACH_END();
}
ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;
}
static void zend_persist_class_entry_calc(zval *zv)
void zend_persist_class_entry_calc(zend_class_entry *ce)
{
zend_class_entry *ce = Z_PTR_P(zv);
Bucket *p;
if (ce->type == ZEND_USER_CLASS) {
@ -396,19 +375,8 @@ static void zend_persist_class_entry_calc(zval *zv)
}
zend_shared_alloc_register_xlat_entry(ce, ce);
check_property_type_resolution(ce);
ADD_SIZE(sizeof(zend_class_entry));
ZCG(is_immutable_class) =
(ce->ce_flags & ZEND_ACC_LINKED) &&
(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) &&
(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) &&
!ZCG(current_persistent_script)->corrupted;
ADD_SIZE_EX(sizeof(zend_class_entry));
ADD_INTERNED_STRING(ce->name);
if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) {
ADD_INTERNED_STRING(ce->parent_name);
}
zend_hash_persist_calc(&ce->function_table);
ZEND_HASH_FOREACH_BUCKET(&ce->function_table, p) {
ZEND_ASSERT(p->key != NULL);
@ -440,16 +408,6 @@ static void zend_persist_class_entry_calc(zval *zv)
zend_persist_class_constant_calc(&p->val);
} ZEND_HASH_FOREACH_END();
if (ce->info.user.filename) {
ADD_STRING(ce->info.user.filename);
}
if (ZCG(accel_directives).save_comments && ce->info.user.doc_comment) {
ADD_STRING(ce->info.user.doc_comment);
}
if (ce->attributes) {
zend_persist_attributes_calc(ce->attributes);
}
zend_hash_persist_calc(&ce->properties_info);
ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, p) {
zend_property_info *prop = Z_PTR(p->val);
@ -461,7 +419,36 @@ static void zend_persist_class_entry_calc(zval *zv)
} ZEND_HASH_FOREACH_END();
if (ce->properties_info_table) {
ADD_SIZE_EX(sizeof(zend_property_info *) * ce->default_properties_count);
ADD_SIZE(sizeof(zend_property_info *) * ce->default_properties_count);
}
if (ce->num_interfaces && (ce->ce_flags & ZEND_ACC_LINKED)) {
ADD_SIZE(sizeof(zend_class_entry*) * ce->num_interfaces);
}
if (ce->iterator_funcs_ptr) {
ADD_SIZE(sizeof(zend_class_iterator_funcs));
}
if (ce->ce_flags & ZEND_ACC_CACHED) {
return;
}
ADD_INTERNED_STRING(ce->name);
if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) {
ADD_INTERNED_STRING(ce->parent_name);
}
if (ce->info.user.filename) {
ADD_STRING(ce->info.user.filename);
}
if (ZCG(accel_directives).save_comments && ce->info.user.doc_comment) {
ADD_STRING(ce->info.user.doc_comment);
}
if (ce->attributes) {
zend_persist_attributes_calc(ce->attributes);
}
if (ce->num_interfaces) {
@ -473,8 +460,6 @@ static void zend_persist_class_entry_calc(zval *zv)
ADD_INTERNED_STRING(ce->interface_names[i].lc_name);
}
ADD_SIZE(sizeof(zend_class_name) * ce->num_interfaces);
} else {
ADD_SIZE(sizeof(zend_class_entry*) * ce->num_interfaces);
}
}
@ -523,10 +508,6 @@ static void zend_persist_class_entry_calc(zval *zv)
ADD_SIZE(sizeof(zend_trait_precedence*) * (i + 1));
}
}
if (ce->iterator_funcs_ptr) {
ADD_SIZE(sizeof(zend_class_iterator_funcs));
}
}
}
@ -538,7 +519,7 @@ static void zend_accel_persist_class_table_calc(HashTable *class_table)
ZEND_HASH_FOREACH_BUCKET(class_table, p) {
ZEND_ASSERT(p->key != NULL);
ADD_INTERNED_STRING(p->key);
zend_persist_class_entry_calc(&p->val);
zend_persist_class_entry_calc(Z_CE(p->val));
} ZEND_HASH_FOREACH_END();
}
@ -557,8 +538,6 @@ uint32_t zend_accel_script_persist_calc(zend_persistent_script *new_persistent_s
new_persistent_script->mem = NULL;
new_persistent_script->size = 0;
new_persistent_script->arena_mem = NULL;
new_persistent_script->arena_size = 0;
new_persistent_script->corrupted = 0;
ZCG(current_persistent_script) = new_persistent_script;
@ -595,12 +574,6 @@ uint32_t zend_accel_script_persist_calc(zend_persistent_script *new_persistent_s
zend_persist_op_array_calc_ex(&new_persistent_script->script.main_op_array);
zend_persist_warnings_calc(new_persistent_script);
#if defined(__AVX__) || defined(__SSE2__)
/* Align size to 64-byte boundary */
new_persistent_script->arena_size = (new_persistent_script->arena_size + 63) & ~63;
#endif
new_persistent_script->size += new_persistent_script->arena_size;
new_persistent_script->corrupted = 0;
ZCG(current_persistent_script) = NULL;

View File

@ -375,7 +375,7 @@ int zend_shared_memdup_size(void *source, size_t size)
return ZEND_ALIGNED_SIZE(size);
}
static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, bool arena, bool get_xlat, bool set_xlat, bool free_source)
static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, bool get_xlat, bool set_xlat, bool free_source)
{
void *old_p, *retval;
zend_ulong key;
@ -388,13 +388,8 @@ static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, b
return old_p;
}
}
if (arena) {
retval = ZCG(arena_mem);
ZCG(arena_mem) = (void*)(((char*)ZCG(arena_mem)) + ZEND_ALIGNED_SIZE(size));
} else {
retval = ZCG(mem);
ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size));
}
retval = ZCG(mem);
ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size));
memcpy(retval, source, size);
if (set_xlat) {
if (!get_xlat) {
@ -411,42 +406,32 @@ static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, b
void *zend_shared_memdup_get_put_free(void *source, size_t size)
{
return _zend_shared_memdup(source, size, 0, 1, 1, 1);
return _zend_shared_memdup(source, size, 1, 1, 1);
}
void *zend_shared_memdup_put_free(void *source, size_t size)
{
return _zend_shared_memdup(source, size, 0, 0, 1, 1);
return _zend_shared_memdup(source, size, 0, 1, 1);
}
void *zend_shared_memdup_free(void *source, size_t size)
{
return _zend_shared_memdup(source, size, 0, 0, 0, 1);
return _zend_shared_memdup(source, size, 0, 0, 1);
}
void *zend_shared_memdup_get_put(void *source, size_t size)
{
return _zend_shared_memdup(source, size, 0, 1, 1, 0);
return _zend_shared_memdup(source, size, 1, 1, 0);
}
void *zend_shared_memdup_put(void *source, size_t size)
{
return _zend_shared_memdup(source, size, 0, 0, 1, 0);
return _zend_shared_memdup(source, size, 0, 1, 0);
}
void *zend_shared_memdup(void *source, size_t size)
{
return _zend_shared_memdup(source, size, 0, 0, 0, 0);
}
void *zend_shared_memdup_arena_put(void *source, size_t size)
{
return _zend_shared_memdup(source, size, 1, 0, 1, 0);
}
void *zend_shared_memdup_arena(void *source, size_t size)
{
return _zend_shared_memdup(source, size, 1, 0, 0, 0);
return _zend_shared_memdup(source, size, 0, 0, 0);
}
void zend_shared_alloc_safe_unlock(void)

View File

@ -139,8 +139,6 @@ void *zend_shared_memdup_free(void *source, size_t size);
void *zend_shared_memdup_get_put(void *source, size_t size);
void *zend_shared_memdup_put(void *source, size_t size);
void *zend_shared_memdup(void *source, size_t size);
void *zend_shared_memdup_arena_put(void *source, size_t size);
void *zend_shared_memdup_arena(void *source, size_t size);
int zend_shared_memdup_size(void *p, size_t size);

View File

@ -386,7 +386,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char
zend_string *key;
zend_class_constant *c;
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
ZEND_HASH_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, c) {
_class_const_string(str, ZSTR_VAL(key), c, ZSTR_VAL(sub_indent));
if (UNEXPECTED(EG(exception))) {
zend_string_release(sub_indent);
@ -2948,8 +2948,14 @@ ZEND_METHOD(ReflectionUnionType, getTypes)
append_type(return_value, *list_type);
} ZEND_TYPE_LIST_FOREACH_END();
} else if (ZEND_TYPE_HAS_NAME(param->type)) {
append_type(return_value,
(zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), 0, 0));
if (ZEND_TYPE_HAS_CE_CACHE(param->type)
&& ZEND_TYPE_CE_CACHE(param->type)) {
append_type(return_value,
(zend_type) ZEND_TYPE_INIT_CE(ZEND_TYPE_CE_CACHE(param->type), 0, 0));
} else {
append_type(return_value,
(zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), 0, 0));
}
} else if (ZEND_TYPE_HAS_CE(param->type)) {
append_type(return_value,
(zend_type) ZEND_TYPE_INIT_CE(ZEND_TYPE_CE(param->type), 0, 0));
@ -3548,7 +3554,7 @@ ZEND_METHOD(ReflectionClassConstant, __construct)
object = ZEND_THIS;
intern = Z_REFLECTION_P(object);
if ((constant = zend_hash_find_ptr(&ce->constants_table, constname)) == NULL) {
if ((constant = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), constname)) == NULL) {
zend_throw_exception_ex(reflection_exception_ptr, 0, "Constant %s::%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(constname));
RETURN_THROWS();
}
@ -4444,7 +4450,7 @@ ZEND_METHOD(ReflectionClass, getConstants)
GET_REFLECTION_OBJECT_PTR(ce);
array_init(return_value);
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, constant) {
ZEND_HASH_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, constant) {
if (UNEXPECTED(zval_update_constant_ex(&constant->value, ce) != SUCCESS)) {
RETURN_THROWS();
}
@ -4478,7 +4484,7 @@ ZEND_METHOD(ReflectionClass, getReflectionConstants)
GET_REFLECTION_OBJECT_PTR(ce);
array_init(return_value);
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, name, constant) {
ZEND_HASH_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), name, constant) {
if (Z_ACCESS_FLAGS(constant->value) & filter) {
zval class_const;
reflection_class_constant_factory(name, constant, &class_const);
@ -4493,6 +4499,7 @@ ZEND_METHOD(ReflectionClass, getConstant)
{
reflection_object *intern;
zend_class_entry *ce;
HashTable *constants_table;
zend_class_constant *c;
zend_string *name;
@ -4501,12 +4508,13 @@ ZEND_METHOD(ReflectionClass, getConstant)
}
GET_REFLECTION_OBJECT_PTR(ce);
ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
constants_table = CE_CONSTANTS_TABLE(ce);
ZEND_HASH_FOREACH_PTR(constants_table, c) {
if (UNEXPECTED(zval_update_constant_ex(&c->value, ce) != SUCCESS)) {
RETURN_THROWS();
}
} ZEND_HASH_FOREACH_END();
if ((c = zend_hash_find_ptr(&ce->constants_table, name)) == NULL) {
if ((c = zend_hash_find_ptr(constants_table, name)) == NULL) {
RETURN_FALSE;
}
ZVAL_COPY_OR_DUP(return_value, &c->value);
@ -4526,7 +4534,7 @@ ZEND_METHOD(ReflectionClass, getReflectionConstant)
RETURN_THROWS();
}
if ((constant = zend_hash_find_ptr(&ce->constants_table, name)) == NULL) {
if ((constant = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), name)) == NULL) {
RETURN_FALSE;
}
reflection_class_constant_factory(name, constant, return_value);

View File

@ -1826,6 +1826,7 @@ void php_request_shutdown(void *dummy)
} zend_end_try();
/* 15. Free Willy (here be crashes) */
zend_arena_destroy(CG(arena));
zend_interned_strings_deactivate();
zend_try {
shutdown_memory_manager(CG(unclean_shutdown) || !report_memleaks, 0);