Skip to content

Commit cf16078

Browse files
committed
Optimize ReflectionProperty::getValue(), ::getRawValue(), ::isInitialized(), ::setValue(), ::setRawValue()
- Pass a cache slot to the property handler - Inline the simple case of fetching a declared property - Use the new parameter parsing API This makes these methods 12% to 60% faster. Using the new parameter parsing API had the most impact, by far.
1 parent 4fcbdea commit cf16078

File tree

1 file changed

+86
-32
lines changed

1 file changed

+86
-32
lines changed

ext/reflection/php_reflection.c

Lines changed: 86 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ PHPAPI zend_class_entry *reflection_property_hook_type_ptr;
132132
typedef struct _property_reference {
133133
zend_property_info *prop;
134134
zend_string *unmangled_name;
135+
void *cache_slot[3];
135136
} property_reference;
136137

137138
/* Struct for parameters */
@@ -1506,6 +1507,7 @@ static void reflection_property_factory(zend_class_entry *ce, zend_string *name,
15061507
reference = (property_reference*) emalloc(sizeof(property_reference));
15071508
reference->prop = prop;
15081509
reference->unmangled_name = zend_string_copy(name);
1510+
memset(reference->cache_slot, 0, sizeof(reference->cache_slot));
15091511
intern->ptr = reference;
15101512
intern->ref_type = REF_TYPE_PROPERTY;
15111513
intern->ce = ce;
@@ -5643,6 +5645,7 @@ ZEND_METHOD(ReflectionProperty, __construct)
56435645
reference = (property_reference*) emalloc(sizeof(property_reference));
56445646
reference->prop = dynam_prop ? NULL : property_info;
56455647
reference->unmangled_name = zend_string_copy(name);
5648+
memset(reference->cache_slot, 0, sizeof(reference->cache_slot));
56465649
intern->ptr = reference;
56475650
intern->ref_type = REF_TYPE_PROPERTY;
56485651
intern->ce = ce;
@@ -5794,9 +5797,10 @@ ZEND_METHOD(ReflectionProperty, getValue)
57945797
zval *object = NULL;
57955798
zval *member_p = NULL;
57965799

5797-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|o!", &object) == FAILURE) {
5798-
RETURN_THROWS();
5799-
}
5800+
ZEND_PARSE_PARAMETERS_START(0, 1)
5801+
Z_PARAM_OPTIONAL
5802+
Z_PARAM_OBJECT_EX(object, 1, 0)
5803+
ZEND_PARSE_PARAMETERS_END();
58005804

58015805
GET_REFLECTION_OBJECT_PTR(ref);
58025806

@@ -5813,13 +5817,29 @@ ZEND_METHOD(ReflectionProperty, getValue)
58135817
RETURN_THROWS();
58145818
}
58155819

5816-
/* TODO: Should this always use intern->ce? */
5817-
if (!instanceof_function(Z_OBJCE_P(object), ref->prop ? ref->prop->ce : intern->ce)) {
5818-
_DO_THROW("Given object is not an instance of the class this property was declared in");
5819-
RETURN_THROWS();
5820+
if (ref->cache_slot[0] == Z_OBJCE_P(object)) {
5821+
uintptr_t prop_offset = (uintptr_t) ref->cache_slot[1];
5822+
5823+
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) {
5824+
zval *retval = OBJ_PROP(Z_OBJ_P(object), prop_offset);
5825+
if (EXPECTED(Z_TYPE_INFO_P(retval) != IS_UNDEF)) {
5826+
RETURN_COPY_DEREF(retval);
5827+
}
5828+
}
5829+
} else {
5830+
/* TODO: Should this always use intern->ce? */
5831+
if (!instanceof_function(Z_OBJCE_P(object), ref->prop ? ref->prop->ce : intern->ce)) {
5832+
_DO_THROW("Given object is not an instance of the class this property was declared in");
5833+
RETURN_THROWS();
5834+
}
58205835
}
58215836

5822-
member_p = zend_read_property_ex(intern->ce, Z_OBJ_P(object), ref->unmangled_name, 0, &rv);
5837+
zend_class_entry *old_scope = EG(fake_scope);
5838+
EG(fake_scope) = intern->ce;
5839+
member_p = Z_OBJ_P(object)->handlers->read_property(Z_OBJ_P(object),
5840+
ref->unmangled_name, BP_VAR_R, ref->cache_slot, &rv);
5841+
EG(fake_scope) = old_scope;
5842+
58235843
if (member_p != &rv) {
58245844
RETURN_COPY_DEREF(member_p);
58255845
} else {
@@ -5873,7 +5893,10 @@ ZEND_METHOD(ReflectionProperty, setValue)
58735893
Z_PARAM_ZVAL(value)
58745894
ZEND_PARSE_PARAMETERS_END();
58755895

5876-
zend_update_property_ex(intern->ce, object, ref->unmangled_name, value);
5896+
zend_class_entry *old_scope = EG(fake_scope);
5897+
EG(fake_scope) = intern->ce;
5898+
object->handlers->write_property(object, ref->unmangled_name, value, ref->cache_slot);
5899+
EG(fake_scope) = old_scope;
58775900
}
58785901
}
58795902
/* }}} */
@@ -5884,25 +5907,41 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
58845907
property_reference *ref;
58855908
zval *object;
58865909

5887-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &object) == FAILURE) {
5888-
RETURN_THROWS();
5889-
}
5910+
ZEND_PARSE_PARAMETERS_START(1, 1)
5911+
Z_PARAM_OBJECT(object)
5912+
ZEND_PARSE_PARAMETERS_END();
58905913

58915914
GET_REFLECTION_OBJECT_PTR(ref);
58925915

5893-
if (prop_get_flags(ref) & ZEND_ACC_STATIC) {
5894-
_DO_THROW("May not use getRawValue on static properties");
5895-
RETURN_THROWS();
5896-
}
5916+
if (ref->cache_slot[0] == Z_OBJCE_P(object)) {
5917+
uintptr_t prop_offset = (uintptr_t) ref->cache_slot[1];
58975918

5898-
if (!instanceof_function(Z_OBJCE_P(object), intern->ce)) {
5899-
_DO_THROW("Given object is not an instance of the class this property was declared in");
5900-
RETURN_THROWS();
5919+
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) {
5920+
zval *retval = OBJ_PROP(Z_OBJ_P(object), prop_offset);
5921+
if (EXPECTED(Z_TYPE_INFO_P(retval) != IS_UNDEF)) {
5922+
RETURN_COPY_DEREF(retval);
5923+
}
5924+
}
5925+
} else {
5926+
if (prop_get_flags(ref) & ZEND_ACC_STATIC) {
5927+
_DO_THROW("May not use getRawValue on static properties");
5928+
RETURN_THROWS();
5929+
}
5930+
5931+
if (!instanceof_function(Z_OBJCE_P(object), intern->ce)) {
5932+
_DO_THROW("Given object is not an instance of the class this property was declared in");
5933+
RETURN_THROWS();
5934+
}
59015935
}
59025936

59035937
if (!ref->prop || !ref->prop->hooks || !ref->prop->hooks[ZEND_PROPERTY_HOOK_GET]) {
59045938
zval rv;
5905-
zval *member_p = zend_read_property_ex(intern->ce, Z_OBJ_P(object), ref->unmangled_name, 0, &rv);
5939+
zend_class_entry *old_scope = EG(fake_scope);
5940+
EG(fake_scope) = intern->ce;
5941+
zval *member_p = Z_OBJ_P(object)->handlers->read_property(
5942+
Z_OBJ_P(object), ref->unmangled_name, BP_VAR_R,
5943+
ref->cache_slot, &rv);
5944+
EG(fake_scope) = old_scope;
59065945

59075946
if (member_p != &rv) {
59085947
RETURN_COPY_DEREF(member_p);
@@ -5921,7 +5960,10 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
59215960
static void reflection_property_set_raw_value(property_reference *ref, reflection_object *intern, zend_object *object, zval *value)
59225961
{
59235962
if (!ref->prop || !ref->prop->hooks || !ref->prop->hooks[ZEND_PROPERTY_HOOK_SET]) {
5924-
zend_update_property_ex(intern->ce, object, ref->unmangled_name, value);
5963+
zend_class_entry *old_scope = EG(fake_scope);
5964+
EG(fake_scope) = intern->ce;
5965+
object->handlers->write_property(object, ref->unmangled_name, value, ref->cache_slot);
5966+
EG(fake_scope) = old_scope;
59255967
} else {
59265968
zend_function *func = zend_get_property_hook_trampoline(ref->prop, ZEND_PROPERTY_HOOK_SET, ref->unmangled_name);
59275969
zend_call_known_instance_method_with_1_params(func, object, NULL, value);
@@ -5942,9 +5984,10 @@ ZEND_METHOD(ReflectionProperty, setRawValue)
59425984
RETURN_THROWS();
59435985
}
59445986

5945-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "oz", &object, &value) == FAILURE) {
5946-
RETURN_THROWS();
5947-
}
5987+
ZEND_PARSE_PARAMETERS_START(2, 2) {
5988+
Z_PARAM_OBJECT(object)
5989+
Z_PARAM_ZVAL(value)
5990+
} ZEND_PARSE_PARAMETERS_END();
59485991

59495992
reflection_property_set_raw_value(ref, intern, Z_OBJ_P(object), value);
59505993
}
@@ -6116,9 +6159,10 @@ ZEND_METHOD(ReflectionProperty, isInitialized)
61166159
zval *object = NULL;
61176160
zval *member_p = NULL;
61186161

6119-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|o!", &object) == FAILURE) {
6120-
RETURN_THROWS();
6121-
}
6162+
ZEND_PARSE_PARAMETERS_START(0, 1)
6163+
Z_PARAM_OPTIONAL
6164+
Z_PARAM_OBJECT_EX(object, 1, 0)
6165+
ZEND_PARSE_PARAMETERS_END();
61226166

61236167
GET_REFLECTION_OBJECT_PTR(ref);
61246168

@@ -6137,15 +6181,25 @@ ZEND_METHOD(ReflectionProperty, isInitialized)
61376181
RETURN_THROWS();
61386182
}
61396183

6140-
/* TODO: Should this always use intern->ce? */
6141-
if (!instanceof_function(Z_OBJCE_P(object), ref->prop ? ref->prop->ce : intern->ce)) {
6142-
_DO_THROW("Given object is not an instance of the class this property was declared in");
6143-
RETURN_THROWS();
6184+
if (ref->cache_slot[0] == Z_OBJCE_P(object)) {
6185+
uintptr_t prop_offset = (uintptr_t) ref->cache_slot[1];
6186+
6187+
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) {
6188+
zval *value = OBJ_PROP(Z_OBJ_P(object), prop_offset);
6189+
RETURN_BOOL(Z_TYPE_INFO_P(value) != IS_UNDEF);
6190+
}
6191+
} else {
6192+
/* TODO: Should this always use intern->ce? */
6193+
if (!instanceof_function(Z_OBJCE_P(object), ref->prop ? ref->prop->ce : intern->ce)) {
6194+
_DO_THROW("Given object is not an instance of the class this property was declared in");
6195+
RETURN_THROWS();
6196+
}
61446197
}
61456198

61466199
old_scope = EG(fake_scope);
61476200
EG(fake_scope) = intern->ce;
6148-
retval = Z_OBJ_HT_P(object)->has_property(Z_OBJ_P(object), ref->unmangled_name, ZEND_PROPERTY_EXISTS, NULL);
6201+
retval = Z_OBJ_HT_P(object)->has_property(Z_OBJ_P(object),
6202+
ref->unmangled_name, ZEND_PROPERTY_EXISTS, ref->cache_slot);
61496203
EG(fake_scope) = old_scope;
61506204

61516205
RETVAL_BOOL(retval);

0 commit comments

Comments
 (0)