summaryrefslogtreecommitdiff
path: root/Zend
diff options
context:
space:
mode:
Diffstat (limited to 'Zend')
-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
20 files changed, 889 insertions, 100 deletions
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;