This is a new transparent technology that eliminates overhead of PHP class inheritance.
PHP classes are compiled and cached (by opcahce) separately, however their "linking" was done at run-time - on each request. The process of "linking" may involve a number of compatibility checks and borrowing methods/properties/constants form parent and traits. This takes significant time, but the result is the same on each request.
Inheritance Cache performs "linking" for unique set of all the depending classes (parent, interfaces, traits, property types, method types involved into compatibility checks) once and stores result in opcache shared memory. As a part of the this patch, I removed limitations for immutable classes (unresolved constants, typed properties and covariant type checks). So now all classes stored in opcache are "immutable". They may be lazily loaded into process memory, if necessary, but this usually occurs just once (on first linking).
The patch shows 8% improvement on Symphony "Hello World" app.
. 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)
--- /dev/null
+--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)
+}
--- /dev/null
+--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) {
+ }
+}
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. */
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;
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;
}
/* }}} */
+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)) {
}
}
} 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;
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;
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;
+ }
}
}
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)) {
#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);
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);
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) &&
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;
/* 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;
}
}
CG(delayed_variance_obligations) = NULL;
CG(delayed_autoloads) = NULL;
+ CG(unlinked_uses) = NULL;
+ CG(current_linking_class) = NULL;
}
/* }}} */
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));
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;
}
/* }}} */
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));
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);
}
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;
}
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;
/* 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 | | | */
/* 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) | | | */
/* ============== | | | */
/* | | | */
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);
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));
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) {
} 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)) {
} 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) {
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;
HashTable *delayed_variance_obligations;
HashTable *delayed_autoloads;
+ HashTable *unlinked_uses;
+ zend_class_entry *current_linking_class;
uint32_t rtd_key_counter;
#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,
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,
if (!fe_ce) {
have_unresolved = 1;
} else {
+ track_class_dependency(fe_ce, fe_class_name);
return INHERITANCE_SUCCESS;
}
}
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;
}
}
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;
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();
} 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);
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);
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);
}
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 {
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);
}
/* }}} */
-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);
/* then flatten the properties into it, to, mostly to notfiy developer about problems */
zend_do_traits_property_binding(ce, traits);
-
- efree(traits);
}
/* }}} */
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;
* 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");
}
}
-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(
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) {
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);
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);
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;
}
/* }}} */
}
/* }}} */
-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);
}
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;
}
/* }}} */
#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()
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)
# 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
(*(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)) { \
} \
*__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)
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;
}
}
+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) {
* 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 {
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. */
#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)
#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))
(((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)))) \
#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)
}
}
- 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;
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));
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();
}
}
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();
}
}
}
}
- 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;
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));
}
}
- 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;
}
}
- 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;
#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);
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)
{
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);
}
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;
}
}
-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;
*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) {
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;
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;
}
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;
}
} ZEND_TYPE_FOREACH_END();
} ZEND_HASH_FOREACH_END();
+ if (ok) {
+ ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;
+ }
}
return ok;
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) {
found = 0;
break;
}
- if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
- found = 0;
- break;
- }
}
if (!found) continue;
}
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 */
} 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;
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();
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;
}
* 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();
} 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;
}
}
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)) {
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();
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) {
SHM_PROTECT();
HANDLE_UNBLOCK_INTERRUPTIONS();
- ZEND_ASSERT(ZCSG(preload_script)->arena_size == 0);
-
preload_load();
/* Store individual scripts with unlinked classes */
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
#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;
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
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) {
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
}
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;
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
}
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;
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
}
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;
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
}
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;
?>
--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)
<?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
<?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"
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
?>
--FILE--
OK
---EXPECTF--
-Warning: Can't preload class C with unresolved property types in %s on line %d
+--EXPECT--
OK
<?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)
<?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)
+}
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===
#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);
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;
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;
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) {
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) {
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");
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;
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(
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;
}
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);
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");
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);
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);
}
}
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)
}
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 */
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++) {
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);
}
}
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:
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));
uint32_t i;
zval *v;
+ if (zend_accel_in_shm(attributes)) {
+ return attributes;
+ }
+
zend_hash_persist(attributes);
ZEND_HASH_FOREACH_VAL(attributes, v) {
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));
}
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();
}
if (scope) {
op_array->scope = scope;
}
+
if (op_array->prototype) {
zend_function *ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype);
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);
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);
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;
efree(op_array->opcodes);
op_array->opcodes = new_opcodes;
- ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
}
if (op_array->filename) {
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++;
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;
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;
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;
}
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;
if (prop->attributes) {
prop->attributes = zend_persist_attributes(prop->attributes);
}
- zend_persist_type(&prop->type, 1);
+ zend_persist_type(&prop->type);
return prop;
}
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) {
}
}
-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) {
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);
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);
}
} 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);
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]) {
}
}
+ 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;
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) {
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)) {
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();
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) {
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 */
#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)))
}
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:
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;
}
}
-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;
}
}
- 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;
}
}
+ 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;
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);
}
}
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)
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. */
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);
}
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);
}
}
-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) {
}
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);
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);
} 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) {
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);
}
}
ADD_SIZE(sizeof(zend_trait_precedence*) * (i + 1));
}
}
-
- if (ce->iterator_funcs_ptr) {
- ADD_SIZE(sizeof(zend_class_iterator_funcs));
- }
}
}
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();
}
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;
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;
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;
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) {
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)
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);
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);
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));
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();
}
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();
}
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);
{
reflection_object *intern;
zend_class_entry *ce;
+ HashTable *constants_table;
zend_class_constant *c;
zend_string *name;
}
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);
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);
} 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);