From 5a24ac88539243e9ce531fb254d89ec2944e580d Mon Sep 17 00:00:00 2001
From: Dmitry Stogov <dmitry@zend.com>
Date: Tue, 13 Jan 2015 11:33:00 +0300
Subject: Improved access to object properties (cache property offset instead
 of pointer to property_info).

---
 Zend/zend_object_handlers.c | 150 ++++++++++++++++++++++++++++++--------------
 1 file changed, 104 insertions(+), 46 deletions(-)

(limited to 'Zend/zend_object_handlers.c')

diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index 474ccf57ef..2dde1d18d7 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -286,14 +286,14 @@ static zend_always_inline zend_bool is_derived_class(zend_class_entry *child_cla
 }
 /* }}} */
 
-static zend_always_inline zend_property_info *zend_get_property_info_quick(zend_class_entry *ce, zend_string *member, int silent, int allow_static, void **cache_slot) /* {{{ */
+static zend_always_inline uint32_t zend_get_property_offset(zend_class_entry *ce, zend_string *member, int silent, void **cache_slot) /* {{{ */
 {
 	zval *zv;
 	zend_property_info *property_info = NULL;
 	uint32_t flags;
 
 	if (cache_slot && EXPECTED(ce == CACHED_PTR_EX(cache_slot))) {
-		return CACHED_PTR_EX(cache_slot + 1);
+		return (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1);
 	}
 
 	if (UNEXPECTED(member->val[0] == '\0')) {
@@ -304,7 +304,7 @@ static zend_always_inline zend_property_info *zend_get_property_info_quick(zend_
 				zend_error_noreturn(E_ERROR, "Cannot access property started with '\\0'");
 			}
 		}
-		return ZEND_WRONG_PROPERTY_INFO;
+		return ZEND_WRONG_PROPERTY_OFFSET;
 	}
 
 	if (UNEXPECTED(zend_hash_num_elements(&ce->properties_info) == 0)) {
@@ -326,9 +326,7 @@ static zend_always_inline zend_property_info *zend_get_property_info_quick(zend_
 						if (!silent) {
 							zend_error(E_STRICT, "Accessing static property %s::$%s as non static", ce->name->val, member->val);
 						}
-						if (!allow_static) {
-							return NULL;
-						}
+						return ZEND_DYNAMIC_PROPERTY_OFFSET;
 					}
 					goto exit;
 				}
@@ -345,34 +343,96 @@ static zend_always_inline zend_property_info *zend_get_property_info_quick(zend_
 		&& (zv = zend_hash_find(&EG(scope)->properties_info, member)) != NULL
 		&& ((zend_property_info*)Z_PTR_P(zv))->flags & ZEND_ACC_PRIVATE) {
 		property_info = (zend_property_info*)Z_PTR_P(zv);
-		if (!allow_static && UNEXPECTED((property_info->flags & ZEND_ACC_STATIC) != 0)) {
-			return NULL;
+		if (UNEXPECTED((property_info->flags & ZEND_ACC_STATIC) != 0)) {
+			return ZEND_DYNAMIC_PROPERTY_OFFSET;
 		}
 	} else if (UNEXPECTED(property_info == NULL)) {
 exit_dynamic:
 		if (cache_slot) {
-			CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, NULL);
+			CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, (void*)(intptr_t)ZEND_DYNAMIC_PROPERTY_OFFSET);
 		}
-		return NULL;
+		return ZEND_DYNAMIC_PROPERTY_OFFSET;
 	} else if (UNEXPECTED(property_info == ZEND_WRONG_PROPERTY_INFO)) {
 		/* Information was available, but we were denied access.  Error out. */
 		if (!silent) {
 			zend_error_noreturn(E_ERROR, "Cannot access %s property %s::$%s", zend_visibility_string(flags), ce->name->val, member->val);
 		}
-		return ZEND_WRONG_PROPERTY_INFO;
+		return ZEND_WRONG_PROPERTY_OFFSET;
 	}
 
 exit:
 	if (cache_slot) {
-		CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, property_info);
+		CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, (void*)(intptr_t)property_info->offset);
 	}
-	return property_info;
+	return property_info->offset;
 }
 /* }}} */
 
 ZEND_API zend_property_info *zend_get_property_info(zend_class_entry *ce, zend_string *member, int silent) /* {{{ */
 {
-	return zend_get_property_info_quick(ce, member, silent, 1, NULL);
+	zval *zv;
+	zend_property_info *property_info = NULL;
+	uint32_t flags;
+
+	if (UNEXPECTED(member->val[0] == '\0')) {
+		if (!silent) {
+			if (member->len == 0) {
+				zend_error_noreturn(E_ERROR, "Cannot access empty property");
+			} else {
+				zend_error_noreturn(E_ERROR, "Cannot access property started with '\\0'");
+			}
+		}
+		return ZEND_WRONG_PROPERTY_INFO;
+	}
+
+	if (UNEXPECTED(zend_hash_num_elements(&ce->properties_info) == 0)) {
+		goto exit_dynamic;
+	}
+
+	zv = zend_hash_find(&ce->properties_info, member);
+	if (EXPECTED(zv != NULL)) {
+		property_info = (zend_property_info*)Z_PTR_P(zv);
+		flags = property_info->flags;
+		if (UNEXPECTED((flags & ZEND_ACC_SHADOW) != 0)) {
+			/* if it's a shadow - go to access it's private */
+			property_info = NULL;
+		} else {
+			if (EXPECTED(zend_verify_property_access(property_info, ce) != 0)) {
+				if (UNEXPECTED(!(flags & ZEND_ACC_CHANGED))
+					|| UNEXPECTED((flags & ZEND_ACC_PRIVATE))) {
+					if (UNEXPECTED((flags & ZEND_ACC_STATIC) != 0)) {
+						if (!silent) {
+							zend_error(E_STRICT, "Accessing static property %s::$%s as non static", ce->name->val, member->val);
+						}
+					}
+					goto exit;
+				}
+			} else {
+				/* Try to look in the scope instead */
+				property_info = ZEND_WRONG_PROPERTY_INFO;
+			}
+		}
+	}
+
+	if (EG(scope) != ce
+		&& EG(scope)
+		&& is_derived_class(ce, EG(scope))
+		&& (zv = zend_hash_find(&EG(scope)->properties_info, member)) != NULL
+		&& ((zend_property_info*)Z_PTR_P(zv))->flags & ZEND_ACC_PRIVATE) {
+		property_info = (zend_property_info*)Z_PTR_P(zv);
+	} else if (UNEXPECTED(property_info == NULL)) {
+exit_dynamic:
+		return NULL;
+	} else if (UNEXPECTED(property_info == ZEND_WRONG_PROPERTY_INFO)) {
+		/* Information was available, but we were denied access.  Error out. */
+		if (!silent) {
+			zend_error_noreturn(E_ERROR, "Cannot access %s property %s::$%s", zend_visibility_string(flags), ce->name->val, member->val);
+		}
+		return ZEND_WRONG_PROPERTY_INFO;
+	}
+
+exit:
+	return property_info;
 }
 /* }}} */
 
@@ -390,7 +450,7 @@ ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_inf
 	} else {
 		member = zend_string_copy(prop_info_name);
 	}
-	property_info = zend_get_property_info_quick(zobj->ce, member, 1, 1, NULL);
+	property_info = zend_get_property_info(zobj->ce, member, 1);
 	zend_string_release(member);
 	if (property_info == NULL) {
 		/* undefined public property */
@@ -437,7 +497,7 @@ zval *zend_std_read_property(zval *object, zval *member, int type, void **cache_
 	zend_object *zobj;
 	zval tmp_member;
 	zval *retval;
-	zend_property_info *property_info;
+	uint32_t property_offset;
 
 	zobj = Z_OBJ_P(object);
 
@@ -453,11 +513,11 @@ zval *zend_std_read_property(zval *object, zval *member, int type, void **cache_
 #endif
 
 	/* make zend_get_property_info silent if we have getter - we may want to use it */
-	property_info = zend_get_property_info_quick(zobj->ce, Z_STR_P(member), (type == BP_VAR_IS) || (zobj->ce->__get != NULL), 0, cache_slot);
+	property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), (type == BP_VAR_IS) || (zobj->ce->__get != NULL), cache_slot);
 
-	if (EXPECTED(property_info != ZEND_WRONG_PROPERTY_INFO)) {
-		if (EXPECTED(property_info != NULL)) {
-			retval = OBJ_PROP(zobj, property_info->offset);
+	if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
+		if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+			retval = OBJ_PROP(zobj, property_offset);
 			if (EXPECTED(Z_TYPE_P(retval) != IS_UNDEF)) {
 				goto exit;
 			}
@@ -523,7 +583,7 @@ ZEND_API void zend_std_write_property(zval *object, zval *member, zval *value, v
 	zend_object *zobj;
 	zval tmp_member;
 	zval *variable_ptr;
-	zend_property_info *property_info;
+	uint32_t property_offset;
 
 	zobj = Z_OBJ_P(object);
 
@@ -534,11 +594,11 @@ ZEND_API void zend_std_write_property(zval *object, zval *member, zval *value, v
 		cache_slot = NULL;
 	}
 
-	property_info = zend_get_property_info_quick(zobj->ce, Z_STR_P(member), (zobj->ce->__set != NULL), 0, cache_slot);
+	property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), (zobj->ce->__set != NULL), cache_slot);
 
-	if (EXPECTED(property_info != ZEND_WRONG_PROPERTY_INFO)) {
-		if (EXPECTED(property_info != NULL)) {
-			variable_ptr = OBJ_PROP(zobj, property_info->offset);
+	if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
+		if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+			variable_ptr = OBJ_PROP(zobj, property_offset);
 			if (Z_TYPE_P(variable_ptr) != IS_UNDEF) {
 				goto found;
 			}
@@ -565,7 +625,7 @@ found:
 			}
 			(*guard) &= ~IN_SET;
 			zval_ptr_dtor(&tmp_object);
-		} else if (EXPECTED(property_info != ZEND_WRONG_PROPERTY_INFO)) {
+		} else if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
 			goto write_std_property;
 		} else {
 			if (Z_STRVAL_P(member)[0] == '\0') {
@@ -576,7 +636,7 @@ found:
 				}
 			}
 		}
-	} else if (EXPECTED(property_info != ZEND_WRONG_PROPERTY_INFO)) {
+	} else if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
 		zval tmp;
 
 write_std_property:
@@ -589,10 +649,8 @@ write_std_property:
 				Z_ADDREF_P(value);
 			}
 		}
-		if (EXPECTED(property_info != NULL) &&
-		    EXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0)) {
-
-			ZVAL_COPY_VALUE(OBJ_PROP(zobj, property_info->offset), value);
+		if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+			ZVAL_COPY_VALUE(OBJ_PROP(zobj, property_offset), value);
 		} else {
 			if (!zobj->properties) {
 				rebuild_object_properties(zobj);
@@ -695,7 +753,7 @@ static zval *zend_std_get_property_ptr_ptr(zval *object, zval *member, int type,
 	zend_object *zobj;
 	zend_string *name;
 	zval *retval = NULL;
-	zend_property_info *property_info;
+	uint32_t property_offset;
 
 	zobj = Z_OBJ_P(object);
 	if (EXPECTED(Z_TYPE_P(member) == IS_STRING)) {
@@ -708,11 +766,11 @@ static zval *zend_std_get_property_ptr_ptr(zval *object, zval *member, int type,
 	fprintf(stderr, "Ptr object #%d property: %s\n", Z_OBJ_HANDLE_P(object), name->val);
 #endif
 
-	property_info = zend_get_property_info_quick(zobj->ce, name, (zobj->ce->__get != NULL), 0, cache_slot);
+	property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__get != NULL), cache_slot);
 
-	if (EXPECTED(property_info != ZEND_WRONG_PROPERTY_INFO)) {
-		if (EXPECTED(property_info != NULL)) {
-			retval = OBJ_PROP(zobj, property_info->offset);
+	if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
+		if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+			retval = OBJ_PROP(zobj, property_offset);
 			if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
 				if (EXPECTED(!zobj->ce->__get) ||
 				    UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) {
@@ -757,7 +815,7 @@ static void zend_std_unset_property(zval *object, zval *member, void **cache_slo
 {
 	zend_object *zobj;
 	zval tmp_member;
-	zend_property_info *property_info;
+	uint32_t property_offset;
 
 	zobj = Z_OBJ_P(object);
 
@@ -768,11 +826,11 @@ static void zend_std_unset_property(zval *object, zval *member, void **cache_slo
 		cache_slot = NULL;
 	}
 
-	property_info = zend_get_property_info_quick(zobj->ce, Z_STR_P(member), (zobj->ce->__unset != NULL), 0, cache_slot);
+	property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), (zobj->ce->__unset != NULL), cache_slot);
 
-	if (EXPECTED(property_info != ZEND_WRONG_PROPERTY_INFO)) {
-		if (EXPECTED(property_info != NULL)) {
-			zval *slot = OBJ_PROP(zobj, property_info->offset);
+	if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
+		if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+			zval *slot = OBJ_PROP(zobj, property_offset);
 
 			if (Z_TYPE_P(slot) != IS_UNDEF) {
 				zval_ptr_dtor(slot);
@@ -1340,7 +1398,7 @@ static int zend_std_has_property(zval *object, zval *member, int has_set_exists,
 	int result;
 	zval *value = NULL;
 	zval tmp_member;
-	zend_property_info *property_info;
+	uint32_t property_offset;
 
 	zobj = Z_OBJ_P(object);
 
@@ -1351,11 +1409,11 @@ static int zend_std_has_property(zval *object, zval *member, int has_set_exists,
 		cache_slot = NULL;
 	}
 
-	property_info = zend_get_property_info_quick(zobj->ce, Z_STR_P(member), 1, 0, cache_slot);
+	property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), 1, cache_slot);
 
-	if (EXPECTED(property_info != ZEND_WRONG_PROPERTY_INFO)) {
-		if (EXPECTED(property_info != NULL)) {
-			value = OBJ_PROP(zobj, property_info->offset);
+	if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
+		if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+			value = OBJ_PROP(zobj, property_offset);
 			if (Z_TYPE_P(value) != IS_UNDEF) {
 				goto found;
 			}
-- 
cgit v1.2.1