summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--Zend/tests/anon/015.phpt33
-rw-r--r--Zend/tests/anon/016.phpt35
-rw-r--r--Zend/zend.c1
-rw-r--r--Zend/zend.h25
-rw-r--r--Zend/zend_API.c159
-rw-r--r--Zend/zend_API.h33
-rw-r--r--Zend/zend_builtin_functions.c5
-rw-r--r--Zend/zend_compile.c34
-rw-r--r--Zend/zend_compile.h13
-rw-r--r--Zend/zend_constants.c2
-rw-r--r--Zend/zend_execute.c24
-rw-r--r--Zend/zend_execute_API.c16
-rw-r--r--Zend/zend_globals.h2
-rw-r--r--Zend/zend_inheritance.c474
-rw-r--r--Zend/zend_inheritance.h7
-rw-r--r--Zend/zend_map_ptr.h12
-rw-r--r--Zend/zend_opcode.c62
-rw-r--r--Zend/zend_types.h32
-rw-r--r--Zend/zend_vm_def.h8
-rw-r--r--Zend/zend_vm_execute.h12
-rw-r--r--ext/opcache/ZendAccelerator.c474
-rw-r--r--ext/opcache/ZendAccelerator.h4
-rw-r--r--ext/opcache/jit/zend_jit_x86.dasc26
-rw-r--r--ext/opcache/tests/bug78014.phpt14
-rw-r--r--ext/opcache/tests/preload_004.phpt11
-rw-r--r--ext/opcache/tests/preload_009.phpt12
-rw-r--r--ext/opcache/tests/preload_012.phpt15
-rw-r--r--ext/opcache/tests/preload_ind.phpt3
-rw-r--r--ext/opcache/tests/preload_loadable_classes_2.phpt16
-rw-r--r--ext/opcache/tests/preload_loadable_classes_3.phpt12
-rw-r--r--ext/opcache/tests/preload_unresolved_prop_type.phpt9
-rw-r--r--ext/opcache/zend_accelerator_util_funcs.c458
-rw-r--r--ext/opcache/zend_file_cache.c77
-rw-r--r--ext/opcache/zend_persist.c250
-rw-r--r--ext/opcache/zend_persist.h4
-rw-r--r--ext/opcache/zend_persist_calc.c167
-rw-r--r--ext/opcache/zend_shared_alloc.c33
-rw-r--r--ext/opcache/zend_shared_alloc.h2
-rw-r--r--ext/reflection/php_reflection.c26
-rw-r--r--main/main.c1
41 files changed, 1595 insertions, 1011 deletions
diff --git a/NEWS b/NEWS
index 14b189c1a1..5d6db8eb52 100644
--- a/NEWS
+++ b/NEWS
@@ -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)
diff --git a/Zend/tests/anon/015.phpt b/Zend/tests/anon/015.phpt
new file mode 100644
index 0000000000..324ebe880a
--- /dev/null
+++ b/Zend/tests/anon/015.phpt
@@ -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)
+}
diff --git a/Zend/tests/anon/016.phpt b/Zend/tests/anon/016.phpt
new file mode 100644
index 0000000000..4cde6dfeab
--- /dev/null
+++ b/Zend/tests/anon/016.phpt
@@ -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) {
+ }
+}
diff --git a/Zend/zend.c b/Zend/zend.c
index 4da7991db6..4518322622 100644
--- a/Zend/zend.c
+++ b/Zend/zend.c
@@ -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. */
diff --git a/Zend/zend.h b/Zend/zend.h
index 6a2a834d93..44f3baac46 100644
--- a/Zend/zend.h
+++ b/Zend/zend.h
@@ -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;
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index 5f558c4930..abfbb411a5 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -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)) {
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index 89eb6312d6..6c867b2869 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -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);
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index fa65d1ca07..afabfa97f3 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -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;
}
}
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 65fa186234..d617547f81 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -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;
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index ffc2f7baa4..fbab084b2b 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -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);
diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c
index 03ab10b523..9534190f8d 100644
--- a/Zend/zend_constants.c
+++ b/Zend/zend_constants.c
@@ -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));
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 93cee93dbd..5338a0ae43 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -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)) {
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index 4a6ff1bdbc..7c32f3a7fb 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -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;
diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h
index 6d67325fe1..73dac228cc 100644
--- a/Zend/zend_globals.h
+++ b/Zend/zend_globals.h
@@ -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;
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 16e31a10d8..32d03acb4a 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -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;
+ } 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;
+ }
+ }
+
+ if (!CG(current_linking_class)) {
+ is_cacheable = 0;
}
+ CG(current_linking_class) = orig_linking_class;
- 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);
+ 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);
}
}
- return SUCCESS;
+ 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;
}
/* }}} */
diff --git a/Zend/zend_inheritance.h b/Zend/zend_inheritance.h
index e82910f052..c67032f129 100644
--- a/Zend/zend_inheritance.h
+++ b/Zend/zend_inheritance.h
@@ -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()
diff --git a/Zend/zend_map_ptr.h b/Zend/zend_map_ptr.h
index c6930473cf..c014f225a3 100644
--- a/Zend/zend_map_ptr.h
+++ b/Zend/zend_map_ptr.h
@@ -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)
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index ae9bf2d74d..abb75b8026 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -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) {
diff --git a/Zend/zend_types.h b/Zend/zend_types.h
index da6792ba7b..bb8073e1d5 100644
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@ -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)
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 4b927461a2..d09fcd39b8 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -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();
}
}
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 2f1f6fc298..f27f3fd5e1 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -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;
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index 088bca6ebf..9819e7acdd 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -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;
}
+
+ zend_string_release(key);
}
- 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;
- }
- }
+ } 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();
+
- 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;
+ 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 */
diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h
index 1d151b982e..d8cf261ed2 100644
--- a/ext/opcache/ZendAccelerator.h
+++ b/ext/opcache/ZendAccelerator.h
@@ -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;
diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc
index 5700a92ff1..df496cc1a4 100644
--- a/ext/opcache/jit/zend_jit_x86.dasc
+++ b/ext/opcache/jit/zend_jit_x86.dasc
@@ -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;
diff --git a/ext/opcache/tests/bug78014.phpt b/ext/opcache/tests/bug78014.phpt
index 3b4d332828..63fc1b6db1 100644
--- a/ext/opcache/tests/bug78014.phpt
+++ b/ext/opcache/tests/bug78014.phpt
@@ -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)
diff --git a/ext/opcache/tests/preload_004.phpt b/ext/opcache/tests/preload_004.phpt
index f4dd45a062..c8006dae1c 100644
--- a/ext/opcache/tests/preload_004.phpt
+++ b/ext/opcache/tests/preload_004.phpt
@@ -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
diff --git a/ext/opcache/tests/preload_009.phpt b/ext/opcache/tests/preload_009.phpt
index a13b504cd1..88d0df5fdd 100644
--- a/ext/opcache/tests/preload_009.phpt
+++ b/ext/opcache/tests/preload_009.phpt
@@ -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"
diff --git a/ext/opcache/tests/preload_012.phpt b/ext/opcache/tests/preload_012.phpt
index 7c5ad9aa1a..0ca2ae15b9 100644
--- a/ext/opcache/tests/preload_012.phpt
+++ b/ext/opcache/tests/preload_012.phpt
@@ -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
diff --git a/ext/opcache/tests/preload_ind.phpt b/ext/opcache/tests/preload_ind.phpt
index 158fc15ab6..4f0c625b08 100644
--- a/ext/opcache/tests/preload_ind.phpt
+++ b/ext/opcache/tests/preload_ind.phpt
@@ -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
diff --git a/ext/opcache/tests/preload_loadable_classes_2.phpt b/ext/opcache/tests/preload_loadable_classes_2.phpt
index 665f3157e2..8d4e8e3712 100644
--- a/ext/opcache/tests/preload_loadable_classes_2.phpt
+++ b/ext/opcache/tests/preload_loadable_classes_2.phpt
@@ -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)
diff --git a/ext/opcache/tests/preload_loadable_classes_3.phpt b/ext/opcache/tests/preload_loadable_classes_3.phpt
index 97309a6fcc..b12cc9d3cd 100644
--- a/ext/opcache/tests/preload_loadable_classes_3.phpt
+++ b/ext/opcache/tests/preload_loadable_classes_3.phpt
@@ -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)
+}
diff --git a/ext/opcache/tests/preload_unresolved_prop_type.phpt b/ext/opcache/tests/preload_unresolved_prop_type.phpt
index 3e2accd19e..9e0b3570ee 100644
--- a/ext/opcache/tests/preload_unresolved_prop_type.phpt
+++ b/ext/opcache/tests/preload_unresolved_prop_type.phpt
@@ -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===
diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c
index d3011da941..f17f7e8076 100644
--- a/ext/opcache/zend_accelerator_util_funcs.c
+++ b/ext/opcache/zend_accelerator_util_funcs.c
@@ -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 (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);
- }
+ 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)) {
/* 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) {
diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c
index 62a2d261ff..56a26eec5d 100644
--- a/ext/opcache/zend_file_cache.c
+++ b/ext/opcache/zend_file_cache.c
@@ -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)
diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c
index 9bdba91639..a95c614f2a 100644
--- a/ext/opcache/zend_persist.c
+++ b/ext/opcache/zend_persist.c
@@ -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) {
diff --git a/ext/opcache/zend_persist.h b/ext/opcache/zend_persist.h
index d5eca3f195..38afac39d9 100644
--- a/ext/opcache/zend_persist.h
+++ b/ext/opcache/zend_persist.h
@@ -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 */
diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c
index 7731b9a970..5728414eec 100644
--- a/ext/opcache/zend_persist_calc.c
+++ b/ext/opcache/zend_persist_calc.c
@@ -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);
-
- 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(sizeof(zend_class_entry));
- 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;
diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c
index 698c2884ca..a078a31c0f 100644
--- a/ext/opcache/zend_shared_alloc.c
+++ b/ext/opcache/zend_shared_alloc.c
@@ -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)
diff --git a/ext/opcache/zend_shared_alloc.h b/ext/opcache/zend_shared_alloc.h
index 8d37376570..7b67c3c454 100644
--- a/ext/opcache/zend_shared_alloc.h
+++ b/ext/opcache/zend_shared_alloc.h
@@ -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);
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index b0c1067474..73c6926b0d 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -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);
diff --git a/main/main.c b/main/main.c
index b5115663b8..ac7170c88a 100644
--- a/main/main.c
+++ b/main/main.c
@@ -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);