Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Implement mechanism for finding prop_info for property slot #3573

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Zend/zend.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ struct _zend_class_entry {
HashTable properties_info;
HashTable constants_table;

struct _zend_property_info **properties_info_table;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opcache might need special handling for this field. At least, add an assertion that it's NULL.

zend_function *constructor;
zend_function *destructor;
zend_function *clone;
Expand Down
11 changes: 11 additions & 0 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -2743,6 +2743,7 @@ ZEND_API zend_class_entry *zend_register_internal_class_ex(zend_class_entry *cla

if (parent_ce) {
zend_do_inheritance(register_class, parent_ce);
zend_build_properties_info_table(register_class);
}
return register_class;
}
Expand Down Expand Up @@ -3715,10 +3716,20 @@ ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, z
property_info->offset = property_info_ptr->offset;
zval_ptr_dtor(&ce->default_properties_table[OBJ_PROP_TO_NUM(property_info->offset)]);
zend_hash_del(&ce->properties_info, name);

ZEND_ASSERT(ce->type == ZEND_INTERNAL_CLASS);
ZEND_ASSERT(ce->properties_info_table != NULL);
ce->properties_info_table[OBJ_PROP_TO_NUM(property_info->offset)] = property_info;
} else {
property_info->offset = OBJ_PROP_TO_OFFSET(ce->default_properties_count);
ce->default_properties_count++;
ce->default_properties_table = perealloc(ce->default_properties_table, sizeof(zval) * ce->default_properties_count, ce->type == ZEND_INTERNAL_CLASS);

/* For user classes this is handled during linking */
if (ce->type == ZEND_INTERNAL_CLASS) {
ce->properties_info_table = perealloc(ce->properties_info_table, sizeof(zend_property_info *) * ce->default_properties_count, 1);
ce->properties_info_table[ce->default_properties_count - 1] = property_info;
}
}
ZVAL_COPY_VALUE(&ce->default_properties_table[OBJ_PROP_TO_NUM(property_info->offset)], property);
}
Expand Down
3 changes: 1 addition & 2 deletions Zend/zend_builtin_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -1198,8 +1198,7 @@ ZEND_FUNCTION(get_object_vars)
continue;
}

ZEND_ASSERT(key);
if (zend_check_property_access(zobj, key) == FAILURE) {
if (zend_check_property_slot_access(zobj, value) == FAILURE) {
continue;
}
unmangle = 1;
Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1622,6 +1622,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify

ce->default_properties_count = 0;
ce->default_static_members_count = 0;
ce->properties_info_table = NULL;

if (nullify_handlers) {
ce->constructor = NULL;
Expand Down Expand Up @@ -6331,6 +6332,7 @@ void zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
CG(zend_lineno) = decl->end_lineno;
ce->ce_flags |= ZEND_ACC_LINKED;
zend_do_inheritance(ce, parent_ce);
zend_build_properties_info_table(ce);
if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
zend_verify_abstract_class(ce);
}
Expand All @@ -6342,6 +6344,7 @@ void zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
} else {
if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) {
zend_string_release(lcname);
zend_build_properties_info_table(ce);
ce->ce_flags |= ZEND_ACC_LINKED;
return;
}
Expand Down
38 changes: 38 additions & 0 deletions Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,42 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa
}
/* }}} */

void zend_build_properties_info_table(zend_class_entry *ce)
{
zend_property_info **table, *prop;
if (ce->default_properties_count == 0) {
return;
}

ZEND_ASSERT(ce->properties_info_table == NULL);
if (ce->type == ZEND_USER_CLASS) {
ce->properties_info_table = table = zend_arena_alloc(&CG(arena),
sizeof(zend_property_info *) * ce->default_properties_count);
} else {
ce->properties_info_table = table = pemalloc(
sizeof(zend_property_info *) * ce->default_properties_count, 1);
}

if (ce->parent && ce->parent->default_properties_count != 0) {
zend_property_info **parent_table = ce->parent->properties_info_table;
memcpy(
table, parent_table,
sizeof(zend_property_info *) * ce->parent->default_properties_count
);

/* Child did not add any new properties, we are done */
if (ce->default_properties_count == ce->parent->default_properties_count) {
return;
}
}

ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
if (prop->ce == ce && (prop->flags & ZEND_ACC_STATIC) == 0) {
table[OBJ_PROP_TO_NUM(prop->offset)] = prop;
}
} ZEND_HASH_FOREACH_END();
}

ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
{
zend_property_info *property_info;
Expand Down Expand Up @@ -1936,6 +1972,8 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_class_entry *parent)
if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
zend_verify_abstract_class(ce);
}

zend_build_properties_info_table(ce);
}
/* }}} */

Expand Down
1 change: 1 addition & 0 deletions Zend/zend_inheritance.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_class_entry *parent_

void zend_verify_abstract_class(zend_class_entry *ce);
void zend_check_deprecated_constructor(const zend_class_entry *ce);
void zend_build_properties_info_table(zend_class_entry *ce);

END_EXTERN_C()

Expand Down
26 changes: 26 additions & 0 deletions Zend/zend_object_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,32 @@ ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_inf
}
/* }}} */

ZEND_API int zend_check_property_info_access(zend_property_info *prop_info) /* {{{ */
{
zend_class_entry *scope;
if (prop_info->flags & ZEND_ACC_PUBLIC) {
return SUCCESS;
}

if (UNEXPECTED(EG(fake_scope))) {
scope = EG(fake_scope);
} else {
scope = zend_get_executed_scope();
}

if (prop_info->ce == scope) {
return SUCCESS;
}

if ((prop_info->flags & ZEND_ACC_PROTECTED)
&& is_protected_compatible_scope(prop_info->ce, scope)) {
return SUCCESS;
}

return FAILURE;
}
/* }}} */

static void zend_property_guard_dtor(zval *el) /* {{{ */ {
uint32_t *ptr = (uint32_t*)Z_PTR_P(el);
if (EXPECTED(!(((zend_uintptr_t)ptr) & 1))) {
Expand Down
15 changes: 15 additions & 0 deletions Zend/zend_object_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,21 @@ ZEND_API HashTable *zend_get_properties_for(zval *obj, zend_prop_purpose purpose
} \
} while (0)

ZEND_API int zend_check_property_info_access(struct _zend_property_info *prop_info);

static inline struct _zend_property_info *zend_get_property_info_for_slot(zend_object *obj, zval *slot)
{
struct _zend_property_info **table = obj->ce->properties_info_table;
intptr_t prop_num = slot - obj->properties_table;
ZEND_ASSERT(prop_num >= 0 && prop_num < obj->ce->default_properties_count);
return table[prop_num];
}

static inline int zend_check_property_slot_access(zend_object *obj, zval *slot) /* {{{ */
{
return zend_check_property_info_access(zend_get_property_info_for_slot(obj, slot));
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no reason to inline this function. Better, merge it with zend_check_property_info_access().

#define zend_free_trampoline(func) do { \
if ((func) == &EG(trampoline)) { \
EG(trampoline).common.function_name = NULL; \
Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ ZEND_API void destroy_zend_class(zval *zv)
if (ce->num_interfaces > 0) {
free(ce->interfaces);
}
if (ce->properties_info_table) {
free(ce->properties_info_table);
}
free(ce);
break;
}
Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -5876,7 +5876,7 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
value = Z_INDIRECT_P(value);
value_type = Z_TYPE_INFO_P(value);
if (EXPECTED(value_type != IS_UNDEF)
&& EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
&& EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) {
break;
}
} else {
Expand Down Expand Up @@ -6026,7 +6026,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
value = Z_INDIRECT_P(value);
value_type = Z_TYPE_INFO_P(value);
if (EXPECTED(value_type != IS_UNDEF)
&& EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
&& EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) {
break;
}
} else {
Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -21352,7 +21352,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE
value = Z_INDIRECT_P(value);
value_type = Z_TYPE_INFO_P(value);
if (EXPECTED(value_type != IS_UNDEF)
&& EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
&& EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) {
break;
}
} else {
Expand Down Expand Up @@ -21502,7 +21502,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
value = Z_INDIRECT_P(value);
value_type = Z_TYPE_INFO_P(value);
if (EXPECTED(value_type != IS_UNDEF)
&& EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
&& EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) {
break;
}
} else {
Expand Down
10 changes: 10 additions & 0 deletions ext/opcache/zend_accelerator_util_funcs.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,16 @@ static void zend_class_copy_ctor(zend_class_entry **pce)
/* constants table */
zend_hash_clone_constants(&ce->constants_table, &old_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) {
zend_class_name *interface_names;

Expand Down
22 changes: 22 additions & 0 deletions ext/opcache/zend_file_cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,19 @@ static void zend_file_cache_serialize_class(zval *zv,
SERIALIZE_STR(ce->info.user.doc_comment);
zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info);

if (ce->properties_info_table) {
uint32_t i;
zend_property_info **table;

SERIALIZE_PTR(ce->properties_info_table);
table = ce->properties_info_table;
UNSERIALIZE_PTR(table);

for (i = 0; i < ce->default_properties_count; i++) {
SERIALIZE_PTR(table[i]);
}
}

if (ce->num_interfaces) {
uint32_t i;
zend_class_name *interface_names;
Expand Down Expand Up @@ -1294,6 +1307,15 @@ static void zend_file_cache_unserialize_class(zval *zv,
zend_file_cache_unserialize_hash(&ce->properties_info,
script, buf, zend_file_cache_unserialize_prop_info, NULL);

if (ce->properties_info_table) {
uint32_t i;
UNSERIALIZE_PTR(ce->properties_info_table);

for (i = 0; i < ce->default_properties_count; i++) {
UNSERIALIZE_PTR(ce->properties_info_table[i]);
}
}

if (ce->num_interfaces) {
uint32_t i;

Expand Down
13 changes: 13 additions & 0 deletions ext/opcache/zend_persist.c
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,19 @@ static void zend_persist_class_entry(zval *zv)
zend_hash_persist(&ce->properties_info, zend_persist_property_info);
HT_FLAGS(&ce->properties_info) &= (HASH_FLAG_INITIALIZED | HASH_FLAG_STATIC_KEYS);

if (ce->properties_info_table) {
int i;

size_t size = sizeof(zend_property_info *) * ce->default_properties_count;
memcpy(ZCG(arena_mem), ce->properties_info_table, size);
ce->properties_info_table = ZCG(arena_mem);
ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(size));

for (i = 0; i < ce->default_properties_count; i++) {
ce->properties_info_table[i] = zend_shared_alloc_get_xlat_entry(ce->properties_info_table[i]);
}
}

if (ce->num_interfaces) {
uint32_t i = 0;

Expand Down
4 changes: 4 additions & 0 deletions ext/opcache/zend_persist_calc.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ static void zend_persist_class_entry_calc(zval *zv)

zend_hash_persist_calc(&ce->properties_info, zend_persist_property_info_calc);

if (ce->properties_info_table) {
ADD_ARENA_SIZE(sizeof(zend_property_info *) * ce->default_properties_count);
}

if (ce->num_interfaces) {
uint32_t i;

Expand Down
28 changes: 16 additions & 12 deletions ext/standard/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,29 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
}
arg_sep_len = strlen(arg_sep);

ZEND_HASH_FOREACH_KEY_VAL_IND(ht, idx, key, zdata) {
/* handling for private & protected object properties */
ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, zdata) {
if (key) {
if (ZSTR_VAL(key)[0] == '\0' && type != NULL) {
const char *tmp;
prop_name = ZSTR_VAL(key);
prop_len = ZSTR_LEN(key);
} else {
prop_name = NULL;
prop_len = 0;
}

if (Z_TYPE_P(zdata) == IS_INDIRECT) {
zdata = Z_INDIRECT_P(zdata);
if (Z_ISUNDEF_P(zdata)) {
continue;
}

zend_object *zobj = Z_OBJ_P(type);
if (zend_check_property_access(zobj, key) != SUCCESS) {
if (type) {
const char *tmp;
if (zend_check_property_slot_access(Z_OBJ_P(type), zdata) != SUCCESS) {
/* private or protected property access outside of the class */
continue;
}
zend_unmangle_property_name_ex(key, &tmp, &prop_name, &prop_len);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We, probably, may get property name from property_info->name, instead of unmangling.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

property_info->name is a mangled name though. Unmangling is unavoidable here.

} else {
prop_name = ZSTR_VAL(key);
prop_len = ZSTR_LEN(key);
}
} else {
prop_name = NULL;
prop_len = 0;
}

ZVAL_DEREF(zdata);
Expand Down