Skip to content

Commit 024d5f4

Browse files
committed
Cache method overrides of ArrayAccess in zend_class_entry
Previously, code such as subclasses of SplFixedArray would check for method overrides when instantiating the objects. This optimization was mentioned as a followup to GH-6552
1 parent 8ba1267 commit 024d5f4

13 files changed

+112
-98
lines changed

Zend/zend.h

+2
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ struct _zend_class_entry {
183183

184184
/* allocated only if class implements Iterator or IteratorAggregate interface */
185185
zend_class_iterator_funcs *iterator_funcs_ptr;
186+
/* allocated only if class implements ArrayAccess interface */
187+
zend_class_arrayaccess_funcs *arrayaccess_funcs_ptr;
186188

187189
/* handlers */
188190
union {

Zend/zend_API.h

+1
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ typedef struct _zend_fcall_info_cache {
292292
class_container.interfaces = NULL; \
293293
class_container.get_iterator = NULL; \
294294
class_container.iterator_funcs_ptr = NULL; \
295+
class_container.arrayaccess_funcs_ptr = NULL; \
295296
class_container.info.internal.module = NULL; \
296297
class_container.info.internal.builtin_functions = functions; \
297298
}

Zend/zend_compile.c

+1
Original file line numberDiff line numberDiff line change
@@ -1802,6 +1802,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_hand
18021802
ce->create_object = NULL;
18031803
ce->get_iterator = NULL;
18041804
ce->iterator_funcs_ptr = NULL;
1805+
ce->arrayaccess_funcs_ptr = NULL;
18051806
ce->get_static_method = NULL;
18061807
ce->parent = NULL;
18071808
ce->parent_name = NULL;

Zend/zend_interfaces.c

+23
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,28 @@ static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry
359359
}
360360
/* }}} */
361361

362+
/* {{{ zend_implement_arrayaccess */
363+
static int zend_implement_arrayaccess(zend_class_entry *interface, zend_class_entry *class_type)
364+
{
365+
ZEND_ASSERT(!class_type->arrayaccess_funcs_ptr && "ArrayAccess funcs already set?");
366+
zend_class_arrayaccess_funcs *funcs_ptr = class_type->type == ZEND_INTERNAL_CLASS
367+
? pemalloc(sizeof(zend_class_arrayaccess_funcs), 1)
368+
: zend_arena_alloc(&CG(arena), sizeof(zend_class_arrayaccess_funcs));
369+
class_type->arrayaccess_funcs_ptr = funcs_ptr;
370+
371+
funcs_ptr->zf_offsetget = zend_hash_str_find_ptr(
372+
&class_type->function_table, "offsetget", sizeof("offsetget") - 1);
373+
funcs_ptr->zf_offsetexists = zend_hash_str_find_ptr(
374+
&class_type->function_table, "offsetexists", sizeof("offsetexists") - 1);
375+
funcs_ptr->zf_offsetset = zend_hash_str_find_ptr(
376+
&class_type->function_table, "offsetset", sizeof("offsetset") - 1);
377+
funcs_ptr->zf_offsetunset = zend_hash_str_find_ptr(
378+
&class_type->function_table, "offsetunset", sizeof("offsetunset") - 1);
379+
380+
return SUCCESS;
381+
}
382+
/* }}} */
383+
362384
/* {{{ zend_user_serialize */
363385
ZEND_API int zend_user_serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data)
364386
{
@@ -616,6 +638,7 @@ ZEND_API void zend_register_interfaces(void)
616638
zend_ce_serializable->interface_gets_implemented = zend_implement_serializable;
617639

618640
zend_ce_arrayaccess = register_class_ArrayAccess();
641+
zend_ce_arrayaccess->interface_gets_implemented = zend_implement_arrayaccess;
619642

620643
zend_ce_countable = register_class_Countable();
621644

Zend/zend_iterators.h

+7
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ typedef struct _zend_class_iterator_funcs {
7272
zend_function *zf_rewind;
7373
} zend_class_iterator_funcs;
7474

75+
typedef struct _zend_class_arrayaccess_funcs {
76+
zend_function *zf_offsetget;
77+
zend_function *zf_offsetexists;
78+
zend_function *zf_offsetset;
79+
zend_function *zf_offsetunset;
80+
} zend_class_arrayaccess_funcs;
81+
7582
BEGIN_EXTERN_C()
7683
/* given a zval, returns stuff that can be used to iterate it. */
7784
ZEND_API zend_object_iterator* zend_iterator_unwrap(zval *array_ptr);

Zend/zend_object_handlers.c

+15-22
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,9 @@ ZEND_API zval *zend_std_read_dimension(zend_object *object, zval *offset, int ty
908908
zend_class_entry *ce = object->ce;
909909
zval tmp_offset;
910910

911-
if (EXPECTED(zend_class_implements_interface(ce, zend_ce_arrayaccess) != 0)) {
911+
/* arrayaccess_funcs_ptr is set if (and only if) the class implements zend_ce_arrayaccess */
912+
zend_class_arrayaccess_funcs *funcs = ce->arrayaccess_funcs_ptr;
913+
if (EXPECTED(funcs)) {
912914
if (offset == NULL) {
913915
/* [] construct */
914916
ZVAL_NULL(&tmp_offset);
@@ -918,9 +920,7 @@ ZEND_API zval *zend_std_read_dimension(zend_object *object, zval *offset, int ty
918920

919921
GC_ADDREF(object);
920922
if (type == BP_VAR_IS) {
921-
zend_function *offsetexists =
922-
zend_hash_find_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_OFFSETEXISTS));
923-
zend_call_known_instance_method_with_1_params(offsetexists, object, rv, &tmp_offset);
923+
zend_call_known_instance_method_with_1_params(funcs->zf_offsetexists, object, rv, &tmp_offset);
924924
if (UNEXPECTED(Z_ISUNDEF_P(rv))) {
925925
OBJ_RELEASE(object);
926926
zval_ptr_dtor(&tmp_offset);
@@ -935,9 +935,7 @@ ZEND_API zval *zend_std_read_dimension(zend_object *object, zval *offset, int ty
935935
zval_ptr_dtor(rv);
936936
}
937937

938-
zend_function *offsetget =
939-
zend_hash_find_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_OFFSETGET));
940-
zend_call_known_instance_method_with_1_params(offsetget, object, rv, &tmp_offset);
938+
zend_call_known_instance_method_with_1_params(funcs->zf_offsetget, object, rv, &tmp_offset);
941939

942940
OBJ_RELEASE(object);
943941
zval_ptr_dtor(&tmp_offset);
@@ -961,16 +959,15 @@ ZEND_API void zend_std_write_dimension(zend_object *object, zval *offset, zval *
961959
zend_class_entry *ce = object->ce;
962960
zval tmp_offset;
963961

964-
if (EXPECTED(zend_class_implements_interface(ce, zend_ce_arrayaccess) != 0)) {
962+
zend_class_arrayaccess_funcs *funcs = ce->arrayaccess_funcs_ptr;
963+
if (EXPECTED(funcs)) {
965964
if (!offset) {
966965
ZVAL_NULL(&tmp_offset);
967966
} else {
968967
ZVAL_COPY_DEREF(&tmp_offset, offset);
969968
}
970969
GC_ADDREF(object);
971-
zend_function *offsetset =
972-
zend_hash_find_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_OFFSETSET));
973-
zend_call_known_instance_method_with_2_params(offsetset, object, NULL, &tmp_offset, value);
970+
zend_call_known_instance_method_with_2_params(funcs->zf_offsetset, object, NULL, &tmp_offset, value);
974971
OBJ_RELEASE(object);
975972
zval_ptr_dtor(&tmp_offset);
976973
} else {
@@ -985,18 +982,15 @@ ZEND_API int zend_std_has_dimension(zend_object *object, zval *offset, int check
985982
zval retval, tmp_offset;
986983
int result;
987984

988-
if (EXPECTED(zend_class_implements_interface(ce, zend_ce_arrayaccess) != 0)) {
985+
zend_class_arrayaccess_funcs *funcs = ce->arrayaccess_funcs_ptr;
986+
if (EXPECTED(funcs)) {
989987
ZVAL_COPY_DEREF(&tmp_offset, offset);
990988
GC_ADDREF(object);
991-
zend_function *offsetexists =
992-
zend_hash_find_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_OFFSETEXISTS));
993-
zend_call_known_instance_method_with_1_params(offsetexists, object, &retval, &tmp_offset);
989+
zend_call_known_instance_method_with_1_params(funcs->zf_offsetexists, object, &retval, &tmp_offset);
994990
result = i_zend_is_true(&retval);
995991
zval_ptr_dtor(&retval);
996992
if (check_empty && result && EXPECTED(!EG(exception))) {
997-
zend_function *offsetget =
998-
zend_hash_find_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_OFFSETGET));
999-
zend_call_known_instance_method_with_1_params(offsetget, object, &retval, &tmp_offset);
993+
zend_call_known_instance_method_with_1_params(funcs->zf_offsetget, object, &retval, &tmp_offset);
1000994
result = i_zend_is_true(&retval);
1001995
zval_ptr_dtor(&retval);
1002996
}
@@ -1170,12 +1164,11 @@ ZEND_API void zend_std_unset_dimension(zend_object *object, zval *offset) /* {{{
11701164
zend_class_entry *ce = object->ce;
11711165
zval tmp_offset;
11721166

1173-
if (zend_class_implements_interface(ce, zend_ce_arrayaccess)) {
1167+
zend_class_arrayaccess_funcs *funcs = ce->arrayaccess_funcs_ptr;
1168+
if (EXPECTED(funcs)) {
11741169
ZVAL_COPY_DEREF(&tmp_offset, offset);
11751170
GC_ADDREF(object);
1176-
zend_function *offsetunset =
1177-
zend_hash_find_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_OFFSETUNSET));
1178-
zend_call_known_instance_method_with_1_params(offsetunset, object, NULL, &tmp_offset);
1171+
zend_call_known_instance_method_with_1_params(funcs->zf_offsetunset, object, NULL, &tmp_offset);
11791172
OBJ_RELEASE(object);
11801173
zval_ptr_dtor(&tmp_offset);
11811174
} else {

Zend/zend_opcode.c

+3
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,9 @@ ZEND_API void destroy_zend_class(zval *zv)
469469
if (ce->iterator_funcs_ptr) {
470470
free(ce->iterator_funcs_ptr);
471471
}
472+
if (ce->arrayaccess_funcs_ptr) {
473+
free(ce->arrayaccess_funcs_ptr);
474+
}
472475
if (ce->num_interfaces > 0) {
473476
free(ce->interfaces);
474477
}

Zend/zend_string.h

-4
Original file line numberDiff line numberDiff line change
@@ -571,10 +571,6 @@ EMPTY_SWITCH_DEFAULT_CASE()
571571
_(ZEND_STR_AUTOGLOBAL_SERVER, "_SERVER") \
572572
_(ZEND_STR_AUTOGLOBAL_ENV, "_ENV") \
573573
_(ZEND_STR_AUTOGLOBAL_REQUEST, "_REQUEST") \
574-
_(ZEND_STR_OFFSETGET, "offsetget") \
575-
_(ZEND_STR_OFFSETSET, "offsetset") \
576-
_(ZEND_STR_OFFSETEXISTS, "offsetexists") \
577-
_(ZEND_STR_OFFSETUNSET, "offsetunset") \
578574
_(ZEND_STR_COUNT, "count") \
579575

580576

ext/opcache/zend_file_cache.c

+15
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,14 @@ static void zend_file_cache_serialize_class(zval *zv,
880880
SERIALIZE_PTR(ce->iterator_funcs_ptr);
881881
}
882882

883+
if (ce->arrayaccess_funcs_ptr) {
884+
SERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetget);
885+
SERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetexists);
886+
SERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetset);
887+
SERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetunset);
888+
SERIALIZE_PTR(ce->arrayaccess_funcs_ptr);
889+
}
890+
883891
ZEND_MAP_PTR_INIT(ce->static_members_table, NULL);
884892
ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
885893
}
@@ -1670,6 +1678,13 @@ static void zend_file_cache_unserialize_class(zval *zv,
16701678
UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_current);
16711679
UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_next);
16721680
}
1681+
if (ce->arrayaccess_funcs_ptr) {
1682+
UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr);
1683+
UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetget);
1684+
UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetexists);
1685+
UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetset);
1686+
UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetunset);
1687+
}
16731688

16741689
if (!(script->corrupted)) {
16751690
ce->ce_flags |= ZEND_ACC_IMMUTABLE;

ext/opcache/zend_persist.c

+11
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,9 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce)
985985
if (ce->iterator_funcs_ptr) {
986986
ce->iterator_funcs_ptr = zend_shared_memdup(ce->iterator_funcs_ptr, sizeof(zend_class_iterator_funcs));
987987
}
988+
if (ce->arrayaccess_funcs_ptr) {
989+
ce->arrayaccess_funcs_ptr = zend_shared_memdup(ce->arrayaccess_funcs_ptr, sizeof(zend_class_arrayaccess_funcs));
990+
}
988991

989992
if (ce->ce_flags & ZEND_ACC_CACHED) {
990993
return ce;
@@ -1135,6 +1138,14 @@ void zend_update_parent_ce(zend_class_entry *ce)
11351138
ce->iterator_funcs_ptr->zf_next = zend_hash_str_find_ptr(&ce->function_table, "next", sizeof("next") - 1);
11361139
}
11371140
}
1141+
1142+
if (ce->arrayaccess_funcs_ptr) {
1143+
ZEND_ASSERT(zend_class_implements_interface(ce, zend_ce_arrayaccess));
1144+
ce->arrayaccess_funcs_ptr->zf_offsetget = zend_hash_str_find_ptr(&ce->function_table, "offsetget", sizeof("offsetget") - 1);
1145+
ce->arrayaccess_funcs_ptr->zf_offsetexists = zend_hash_str_find_ptr(&ce->function_table, "offsetexists", sizeof("offsetexists") - 1);
1146+
ce->arrayaccess_funcs_ptr->zf_offsetset = zend_hash_str_find_ptr(&ce->function_table, "offsetset", sizeof("offsetset") - 1);
1147+
ce->arrayaccess_funcs_ptr->zf_offsetunset = zend_hash_str_find_ptr(&ce->function_table, "offsetunset", sizeof("offsetunset") - 1);
1148+
}
11381149
}
11391150

11401151
/* update methods */

ext/opcache/zend_persist_calc.c

+3
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,9 @@ void zend_persist_class_entry_calc(zend_class_entry *ce)
470470
if (ce->iterator_funcs_ptr) {
471471
ADD_SIZE(sizeof(zend_class_iterator_funcs));
472472
}
473+
if (ce->arrayaccess_funcs_ptr) {
474+
ADD_SIZE(sizeof(zend_class_arrayaccess_funcs));
475+
}
473476

474477
if (ce->ce_flags & ZEND_ACC_CACHED) {
475478
return;

0 commit comments

Comments
 (0)