Skip to content

Commit 315f409

Browse files
authored
Always use CE_CACHE, remove TYPE_HAS_CE (#7336)
Currently, CE_CACHE on strings is only used with opcache interned strings. This patch extends usage to non-opcache interned strings as well. This means that most type strings can now make use of CE_CACHE even if opcache is not loaded, which allows us to remove TYPE_HAS_CE kind, and fix some discrepancies depending on whether a type stores a resolved or non-resolved name. There are two cases where CE_CACHE will not be used: * When opcache is not used and a permanent interned string (that is not an internal class name) is used as a type name during the request. In this case we can't allocate a map_ptr index for the permanent string, as it would be not be in the permanent map_ptr index space. * When opcache is used but the script is not cached (e.g. eval'd code or opcache full). If opcache is used, we can't allocate additional map_ptr indexes at runtime, because they may conflict with indexes allocated by opcache. In these two cases we would end up not using CE caching for property types (argument/return types still have the separate cache slot).
1 parent 7d2a2c7 commit 315f409

15 files changed

+123
-276
lines changed

Zend/Optimizer/zend_inference.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,9 +2269,7 @@ static uint32_t zend_convert_type(const zend_script *script, zend_type type, zen
22692269
if (pce) {
22702270
/* As we only have space to store one CE,
22712271
* we use a plain object type for class unions. */
2272-
if (ZEND_TYPE_HAS_CE(type)) {
2273-
*pce = ZEND_TYPE_CE(type);
2274-
} else if (ZEND_TYPE_HAS_NAME(type)) {
2272+
if (ZEND_TYPE_HAS_NAME(type)) {
22752273
zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(type));
22762274
*pce = zend_optimizer_get_class_entry(script, lcname);
22772275
zend_string_release_ex(lcname, 0);

Zend/tests/objects_008.phpt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
--TEST--
22
method overloading with different method signature
3-
--SKIPIF--
4-
<?php
5-
if (getenv('SKIP_PRELOAD')) die('xfail Difference in class name casing');
6-
?>
73
--FILE--
84
<?php
95

Zend/zend.c

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "Optimizer/zend_optimizer.h"
3939

4040
static size_t global_map_ptr_last = 0;
41+
static bool startup_done = false;
4142

4243
#ifdef ZTS
4344
ZEND_API int compiler_globals_id;
@@ -1018,40 +1019,6 @@ void zend_register_standard_ini_entries(void) /* {{{ */
10181019
}
10191020
/* }}} */
10201021

1021-
static zend_class_entry *resolve_type_name(zend_string *type_name) {
1022-
zend_string *lc_type_name = zend_string_tolower(type_name);
1023-
zend_class_entry *ce = zend_hash_find_ptr(CG(class_table), lc_type_name);
1024-
1025-
ZEND_ASSERT(ce && ce->type == ZEND_INTERNAL_CLASS);
1026-
zend_string_release(lc_type_name);
1027-
return ce;
1028-
}
1029-
1030-
static void zend_resolve_property_types(void) /* {{{ */
1031-
{
1032-
zend_class_entry *ce;
1033-
zend_property_info *prop_info;
1034-
1035-
ZEND_HASH_FOREACH_PTR(CG(class_table), ce) {
1036-
if (ce->type != ZEND_INTERNAL_CLASS) {
1037-
continue;
1038-
}
1039-
1040-
if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(ce))) {
1041-
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
1042-
zend_type *single_type;
1043-
ZEND_TYPE_FOREACH(prop_info->type, single_type) {
1044-
if (ZEND_TYPE_HAS_NAME(*single_type)) {
1045-
zend_string *type_name = ZEND_TYPE_NAME(*single_type);
1046-
ZEND_TYPE_SET_CE(*single_type, resolve_type_name(type_name));
1047-
zend_string_release(type_name);
1048-
}
1049-
} ZEND_TYPE_FOREACH_END();
1050-
} ZEND_HASH_FOREACH_END();
1051-
}
1052-
} ZEND_HASH_FOREACH_END();
1053-
}
1054-
/* }}} */
10551022

10561023
/* Unlink the global (r/o) copies of the class, function and constant tables,
10571024
* and use a fresh r/w copy for the startup thread
@@ -1065,7 +1032,7 @@ zend_result zend_post_startup(void) /* {{{ */
10651032
zend_executor_globals *executor_globals = ts_resource(executor_globals_id);
10661033
#endif
10671034

1068-
zend_resolve_property_types();
1035+
startup_done = true;
10691036

10701037
if (zend_post_startup_cb) {
10711038
zend_result (*cb)(void) = zend_post_startup_cb;
@@ -1164,6 +1131,7 @@ void zend_shutdown(void) /* {{{ */
11641131
zend_destroy_rsrc_list_dtors();
11651132

11661133
zend_optimizer_shutdown();
1134+
startup_done = false;
11671135
}
11681136
/* }}} */
11691137

@@ -1892,3 +1860,29 @@ ZEND_API void zend_map_ptr_extend(size_t last)
18921860
CG(map_ptr_last) = last;
18931861
}
18941862
}
1863+
1864+
ZEND_API void zend_alloc_ce_cache(zend_string *type_name)
1865+
{
1866+
if (ZSTR_HAS_CE_CACHE(type_name) || !ZSTR_IS_INTERNED(type_name)) {
1867+
return;
1868+
}
1869+
1870+
if ((GC_FLAGS(type_name) & IS_STR_PERMANENT) && startup_done) {
1871+
/* Don't allocate slot on permanent interned string outside module startup.
1872+
* The cache slot would no longer be valid on the next request. */
1873+
return;
1874+
}
1875+
1876+
if (zend_string_equals_literal_ci(type_name, "self")
1877+
|| zend_string_equals_literal_ci(type_name, "parent")) {
1878+
return;
1879+
}
1880+
1881+
/* We use the refcount to keep map_ptr of corresponding type */
1882+
uint32_t ret;
1883+
do {
1884+
ret = (uint32_t)(uintptr_t)zend_map_ptr_new();
1885+
} while (ret <= 2);
1886+
GC_ADD_FLAGS(type_name, IS_STR_CLASS_NAME_MAP_PTR);
1887+
GC_SET_REFCOUNT(type_name, ret);
1888+
}

Zend/zend_API.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3053,6 +3053,7 @@ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class
30533053

30543054
class_entry->type = ZEND_INTERNAL_CLASS;
30553055
zend_initialize_class_data(class_entry, 0);
3056+
zend_alloc_ce_cache(class_entry->name);
30563057
class_entry->ce_flags = orig_class_entry->ce_flags | ce_flags | ZEND_ACC_CONSTANTS_UPDATED | ZEND_ACC_LINKED | ZEND_ACC_RESOLVED_PARENT | ZEND_ACC_RESOLVED_INTERFACES;
30573058
class_entry->info.internal.module = EG(current_module);
30583059

@@ -4126,6 +4127,17 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z
41264127
property_info->ce = ce;
41274128
property_info->type = type;
41284129

4130+
if (is_persistent_class(ce)) {
4131+
zend_type *single_type;
4132+
ZEND_TYPE_FOREACH(property_info->type, single_type) {
4133+
if (ZEND_TYPE_HAS_NAME(*single_type)) {
4134+
zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*single_type));
4135+
ZEND_TYPE_SET_PTR(*single_type, name);
4136+
zend_alloc_ce_cache(name);
4137+
}
4138+
} ZEND_TYPE_FOREACH_END();
4139+
}
4140+
41294141
zend_hash_update_ptr(&ce->properties_info, name, property_info);
41304142

41314143
return property_info;

Zend/zend_compile.c

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,43 +1202,13 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop
12021202
zend_type *list_type;
12031203
bool is_intersection = ZEND_TYPE_IS_INTERSECTION(type);
12041204
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
1205-
if (ZEND_TYPE_HAS_CE(*list_type)) {
1206-
str = add_type_string(str, ZEND_TYPE_CE(*list_type)->name, is_intersection);
1207-
} else {
1208-
zend_string *name = ZEND_TYPE_NAME(*list_type);
1209-
1210-
if (ZSTR_HAS_CE_CACHE(name)
1211-
&& ZSTR_GET_CE_CACHE(name)) {
1212-
zend_class_entry *ce = ZSTR_GET_CE_CACHE(name);
1213-
if (ce->ce_flags & ZEND_ACC_ANON_CLASS) {
1214-
zend_string *tmp = zend_string_init(ZSTR_VAL(ce->name), strlen(ZSTR_VAL(ce->name)), 0);
1215-
str = add_type_string(str, tmp, is_intersection);
1216-
} else {
1217-
str = add_type_string(str, ce->name, is_intersection);
1218-
}
1219-
} else {
1220-
zend_string *resolved = resolve_class_name(name, scope);
1221-
str = add_type_string(str, resolved, is_intersection);
1222-
zend_string_release(resolved);
1223-
}
1224-
}
1205+
zend_string *name = ZEND_TYPE_NAME(*list_type);
1206+
zend_string *resolved = resolve_class_name(name, scope);
1207+
str = add_type_string(str, resolved, is_intersection);
1208+
zend_string_release(resolved);
12251209
} ZEND_TYPE_LIST_FOREACH_END();
12261210
} else if (ZEND_TYPE_HAS_NAME(type)) {
1227-
zend_string *name = ZEND_TYPE_NAME(type);
1228-
1229-
if (ZSTR_HAS_CE_CACHE(name)
1230-
&& ZSTR_GET_CE_CACHE(name)) {
1231-
zend_class_entry *ce = ZSTR_GET_CE_CACHE(name);
1232-
if (ce->ce_flags & ZEND_ACC_ANON_CLASS) {
1233-
str = zend_string_init(ZSTR_VAL(ce->name), strlen(ZSTR_VAL(ce->name)), 0);
1234-
} else {
1235-
str = zend_string_copy(ce->name);
1236-
}
1237-
} else {
1238-
str = resolve_class_name(name, scope);
1239-
}
1240-
} else if (ZEND_TYPE_HAS_CE(type)) {
1241-
str = zend_string_copy(ZEND_TYPE_CE(type)->name);
1211+
str = resolve_class_name(ZEND_TYPE_NAME(type), scope);
12421212
}
12431213

12441214
uint32_t type_mask = ZEND_TYPE_PURE_MASK(type);
@@ -6232,6 +6202,8 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
62326202
}
62336203
}
62346204

6205+
class_name = zend_new_interned_string(class_name);
6206+
zend_alloc_ce_cache(class_name);
62356207
return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 0, 0);
62366208
}
62376209
}
@@ -7687,6 +7659,9 @@ void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{
76877659
ce->type = ZEND_USER_CLASS;
76887660
ce->name = name;
76897661
zend_initialize_class_data(ce, 1);
7662+
if (!(decl->flags & ZEND_ACC_ANON_CLASS)) {
7663+
zend_alloc_ce_cache(ce->name);
7664+
}
76907665

76917666
if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
76927667
ce->ce_flags |= ZEND_ACC_PRELOADED;

Zend/zend_execute.c

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -851,11 +851,6 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_readonly_property_modification_error(
851851

852852
static zend_class_entry *resolve_single_class_type(zend_string *name, zend_class_entry *self_ce) {
853853
if (zend_string_equals_literal_ci(name, "self")) {
854-
/* We need to explicitly check for this here, to avoid updating the type in the trait and
855-
* later using the wrong "self" when the trait is used in a class. */
856-
if (UNEXPECTED((self_ce->ce_flags & ZEND_ACC_TRAIT) != 0)) {
857-
return NULL;
858-
}
859854
return self_ce;
860855
} else if (zend_string_equals_literal_ci(name, "parent")) {
861856
return self_ce->parent;
@@ -866,26 +861,16 @@ static zend_class_entry *resolve_single_class_type(zend_string *name, zend_class
866861

867862
static zend_always_inline zend_class_entry *zend_ce_from_type(
868863
zend_property_info *info, zend_type *type) {
869-
if (UNEXPECTED(!ZEND_TYPE_HAS_NAME(*type))) {
870-
ZEND_ASSERT(ZEND_TYPE_HAS_CE(*type));
871-
return ZEND_TYPE_CE(*type);
872-
}
873-
864+
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*type));
874865
zend_string *name = ZEND_TYPE_NAME(*type);
875-
zend_class_entry *ce;
876866
if (ZSTR_HAS_CE_CACHE(name)) {
877-
ce = ZSTR_GET_CE_CACHE(name);
867+
zend_class_entry *ce = ZSTR_GET_CE_CACHE(name);
878868
if (!ce) {
879869
ce = zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
880870
}
881-
} else {
882-
ce = resolve_single_class_type(name, info->ce);
883-
if (ce && !(info->ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
884-
zend_string_release(name);
885-
ZEND_TYPE_SET_CE(*type, ce);
886-
}
871+
return ce;
887872
}
888-
return ce;
873+
return resolve_single_class_type(name, info->ce);
889874
}
890875

891876
static bool zend_check_and_resolve_property_class_type(

Zend/zend_inheritance.c

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,6 @@ static inheritance_status zend_is_intersection_subtype_of_class(
430430

431431
if (!proto_ce) proto_ce = lookup_class(proto_scope, proto_class_name);
432432
fe_ce = lookup_class(fe_scope, fe_class_name);
433-
} else if (ZEND_TYPE_HAS_CE(*single_type)) {
434-
if (!proto_ce) proto_ce = lookup_class(proto_scope, proto_class_name);
435-
fe_ce = ZEND_TYPE_CE(*single_type);
436433
} else {
437434
/* standard type in an intersection type is impossible,
438435
* because it would be a fatal compile error */
@@ -456,8 +453,9 @@ static inheritance_status zend_is_intersection_subtype_of_class(
456453

457454
/* Check whether a single class proto type is a subtype of a potentially complex fe_type. */
458455
static inheritance_status zend_is_class_subtype_of_type(
459-
zend_class_entry *fe_scope, zend_string *fe_class_name, zend_class_entry *fe_ce,
456+
zend_class_entry *fe_scope, zend_string *fe_class_name,
460457
zend_class_entry *proto_scope, zend_type proto_type) {
458+
zend_class_entry *fe_ce = NULL;
461459
bool have_unresolved = 0;
462460

463461
/* If the parent has 'object' as a return type, any class satisfies the co-variant check */
@@ -503,9 +501,6 @@ static inheritance_status zend_is_class_subtype_of_type(
503501

504502
if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name);
505503
proto_ce = lookup_class(proto_scope, proto_class_name);
506-
} else if (ZEND_TYPE_HAS_CE(*single_type)) {
507-
if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name);
508-
proto_ce = ZEND_TYPE_CE(*single_type);
509504
} else {
510505
/* standard type */
511506
ZEND_ASSERT(!is_intersection);
@@ -535,16 +530,10 @@ static inheritance_status zend_is_class_subtype_of_type(
535530
return is_intersection ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
536531
}
537532

538-
static zend_string *get_class_from_type(
539-
zend_class_entry **ce, zend_class_entry *scope, zend_type single_type) {
533+
static zend_string *get_class_from_type(zend_class_entry *scope, zend_type single_type) {
540534
if (ZEND_TYPE_HAS_NAME(single_type)) {
541-
*ce = NULL;
542535
return resolve_class_name(scope, ZEND_TYPE_NAME(single_type));
543536
}
544-
if (ZEND_TYPE_HAS_CE(single_type)) {
545-
*ce = ZEND_TYPE_CE(single_type);
546-
return (*ce)->name;
547-
}
548537
return NULL;
549538
}
550539

@@ -617,14 +606,11 @@ static inheritance_status zend_perform_covariant_type_check(
617606
if (proto_type_mask & (MAY_BE_OBJECT|MAY_BE_ITERABLE)) {
618607
bool any_class = (proto_type_mask & MAY_BE_OBJECT) != 0;
619608
ZEND_TYPE_FOREACH(fe_type, single_type) {
620-
zend_class_entry *fe_ce;
621-
zend_string *fe_class_name = get_class_from_type(&fe_ce, fe_scope, *single_type);
609+
zend_string *fe_class_name = get_class_from_type(fe_scope, *single_type);
622610
if (!fe_class_name) {
623611
continue;
624612
}
625-
if (!fe_ce) {
626-
fe_ce = lookup_class(fe_scope, fe_class_name);
627-
}
613+
zend_class_entry *fe_ce = lookup_class(fe_scope, fe_class_name);
628614
if (fe_ce) {
629615
if (any_class || unlinked_instanceof(fe_ce, zend_ce_traversable)) {
630616
track_class_dependency(fe_ce, fe_class_name);
@@ -643,13 +629,12 @@ static inheritance_status zend_perform_covariant_type_check(
643629
early_exit_status =
644630
ZEND_TYPE_IS_INTERSECTION(proto_type) ? INHERITANCE_ERROR : INHERITANCE_SUCCESS;
645631
ZEND_TYPE_FOREACH(proto_type, single_type) {
646-
zend_class_entry *proto_ce;
647-
zend_string *proto_class_name =
648-
get_class_from_type(&proto_ce, proto_scope, *single_type);
632+
zend_string *proto_class_name = get_class_from_type(proto_scope, *single_type);
649633
if (!proto_class_name) {
650634
continue;
651635
}
652636

637+
zend_class_entry *proto_ce = NULL;
653638
inheritance_status status = zend_is_intersection_subtype_of_class(
654639
fe_scope, fe_type, proto_scope, proto_class_name, proto_ce);
655640
if (status == early_exit_status) {
@@ -666,14 +651,13 @@ static inheritance_status zend_perform_covariant_type_check(
666651
* whether proto_type is a union or intersection (only the inner check differs). */
667652
early_exit_status = INHERITANCE_ERROR;
668653
ZEND_TYPE_FOREACH(fe_type, single_type) {
669-
zend_class_entry *fe_ce;
670-
zend_string *fe_class_name = get_class_from_type(&fe_ce, fe_scope, *single_type);
654+
zend_string *fe_class_name = get_class_from_type(fe_scope, *single_type);
671655
if (!fe_class_name) {
672656
continue;
673657
}
674658

675659
inheritance_status status = zend_is_class_subtype_of_type(
676-
fe_scope, fe_class_name, fe_ce, proto_scope, proto_type);
660+
fe_scope, fe_class_name, proto_scope, proto_type);
677661
if (status == early_exit_status) {
678662
return status;
679663
}

Zend/zend_map_ptr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,5 +103,6 @@
103103
ZEND_API void zend_map_ptr_reset(void);
104104
ZEND_API void *zend_map_ptr_new(void);
105105
ZEND_API void zend_map_ptr_extend(size_t last);
106+
ZEND_API void zend_alloc_ce_cache(zend_string *type_name);
106107

107108
#endif /* ZEND_MAP_PTR_H */

0 commit comments

Comments
 (0)