Skip to content

Commit f384d8a

Browse files
committed
Re-use class references
1 parent fcaa554 commit f384d8a

22 files changed

+443
-124
lines changed

Zend/zend_API.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,8 +1746,11 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties)
17461746
}
17471747
/* }}} */
17481748

1749-
ZEND_API zend_class_reference *zend_build_class_reference(zend_class_entry *ce, const zend_type_list *type_args)
1749+
ZEND_API zend_class_reference *zend_build_class_reference(zend_name_reference *name_ref, zend_class_entry *ce)
17501750
{
1751+
const zend_type_list *type_args = &name_ref->args;
1752+
zend_string *key = name_ref->key;
1753+
17511754
if (UNEXPECTED(type_args->num_types > ce->num_generic_params)) {
17521755
zend_throw_error(zend_ce_argument_count_error,
17531756
"Class %s expects %s %d generic argument%s, but %d provided",
@@ -1768,9 +1771,9 @@ ZEND_API zend_class_reference *zend_build_class_reference(zend_class_entry *ce,
17681771
return NULL;
17691772
}
17701773

1771-
// TODO: re-use class references
17721774
zend_class_reference *class_ref = emalloc(ZEND_CLASS_REF_SIZE(ce->num_generic_params));
17731775
class_ref->ce = ce;
1776+
class_ref->key = key;
17741777

17751778
uint8_t param_id = 0;
17761779

@@ -1785,6 +1788,11 @@ ZEND_API zend_class_reference *zend_build_class_reference(zend_class_entry *ce,
17851788

17861789
class_ref->args.num_types = param_id;
17871790

1791+
if (ZSTR_HAS_CE_CACHE(key) && ZSTR_VALID_CE_CACHE(key) &&
1792+
(!CG(in_compilation) || (ce->ce_flags & ZEND_ACC_IMMUTABLE))) {
1793+
ZSTR_SET_CE_CACHE(key, class_ref);
1794+
}
1795+
17881796
return class_ref;
17891797
}
17901798

@@ -3360,8 +3368,11 @@ ZEND_API int zend_next_free_module(void) /* {{{ */
33603368
static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class_entry, uint32_t ce_flags) /* {{{ */
33613369
{
33623370
zend_class_entry_storage *ref = (zend_class_entry_storage*) malloc(sizeof(zend_class_entry) + ZEND_CLASS_ENTRY_HEADER_SIZE);
3363-
zend_class_entry *class_entry = zend_init_class_entry_header(ref);
3364-
zend_string *lowercase_name;
3371+
3372+
zend_string *lowercase_name = zend_string_tolower_ex(orig_class_entry->name, EG(current_module)->type == MODULE_PERSISTENT);
3373+
lowercase_name = zend_new_interned_string(lowercase_name);
3374+
3375+
zend_class_entry *class_entry = zend_init_class_entry_header(ref, lowercase_name);
33653376
*class_entry = *orig_class_entry;
33663377

33673378
class_entry->type = ZEND_INTERNAL_CLASS;
@@ -3374,10 +3385,7 @@ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class
33743385
zend_register_functions(class_entry, class_entry->info.internal.builtin_functions, &class_entry->function_table, EG(current_module)->type);
33753386
}
33763387

3377-
lowercase_name = zend_string_tolower_ex(orig_class_entry->name, EG(current_module)->type == MODULE_PERSISTENT);
3378-
lowercase_name = zend_new_interned_string(lowercase_name);
33793388
zend_hash_update_ptr(CG(class_table), lowercase_name, class_entry);
3380-
zend_string_release_ex(lowercase_name, 1);
33813389

33823390
if (class_entry->__tostring && !zend_string_equals_literal(class_entry->name, "Stringable")
33833391
&& !(class_entry->ce_flags & ZEND_ACC_TRAIT)) {
@@ -3803,7 +3811,8 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_
38033811

38043812
cname = zend_string_init_interned(Z_STRVAL_P(callable), clen, 0);
38053813
if (ZSTR_HAS_CE_CACHE(cname) && ZSTR_GET_CE_CACHE(cname)) {
3806-
fcc->calling_scope = ZSTR_GET_CE_CACHE(cname);
3814+
zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(cname);
3815+
fcc->calling_scope = class_ref->ce;
38073816
if (scope && !fcc->object) {
38083817
zend_object *object = zend_get_this_object(frame);
38093818

Zend/zend_API.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_reference
538538
ZEND_API void object_properties_init(zend_object *object, zend_class_entry *class_type);
539539
ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properties);
540540
ZEND_API void object_properties_load(zend_object *object, HashTable *properties);
541-
ZEND_API zend_class_reference *zend_build_class_reference(zend_class_entry *ce, const zend_type_list *type_args);
541+
ZEND_API zend_class_reference *zend_build_class_reference(zend_name_reference *name_ref, zend_class_entry *ce);
542542

543543
ZEND_API void zend_merge_properties(zval *obj, HashTable *properties);
544544

Zend/zend_builtin_functions.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -996,8 +996,9 @@ static inline void class_exists_impl(INTERNAL_FUNCTION_PARAMETERS, int flags, in
996996
ZEND_PARSE_PARAMETERS_END();
997997

998998
if (ZSTR_HAS_CE_CACHE(name)) {
999-
ce = ZSTR_GET_CE_CACHE(name);
1000-
if (ce) {
999+
zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(name);
1000+
if (class_ref) {
1001+
ce = class_ref->ce;
10011002
RETURN_BOOL(((ce->ce_flags & flags) == flags) && !(ce->ce_flags & skip_flags));
10021003
}
10031004
}

Zend/zend_compile.c

Lines changed: 147 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,24 @@ static int zend_add_class_name_literal(zend_string *name) /* {{{ */
633633
}
634634
/* }}} */
635635

636+
static int zend_add_pnr_literal(zend_packed_name_reference pnr) /* {{{ */
637+
{
638+
zend_string *name = ZEND_PNR_GET_NAME(pnr);
639+
640+
zval zpnr;
641+
ZVAL_PNR(&zpnr, pnr);
642+
643+
/* Original PNR */
644+
int ret = zend_add_literal(&zpnr);
645+
646+
/* Lowercased name */
647+
zend_string *lc_name = zend_string_tolower(name);
648+
zend_add_literal_string(&lc_name);
649+
650+
return ret;
651+
}
652+
/* }}} */
653+
636654
static int zend_add_const_name_literal(zend_string *name, bool unqualified) /* {{{ */
637655
{
638656
zend_string *tmp_name;
@@ -1517,6 +1535,105 @@ zend_string *zend_type_to_string_resolved(zend_type type, const zend_class_entry
15171535
return zend_type_to_string_impl(type, scope, 1);
15181536
}
15191537

1538+
#define zend_type_to_key_append_separator() \
1539+
if (smart_str_get_len(key) != orig_len) { \
1540+
smart_str_appendc(key, '|'); \
1541+
}
1542+
1543+
static void zend_type_to_key_impl(smart_str *key, zend_type type) {
1544+
size_t orig_len = smart_str_get_len(key);
1545+
1546+
if (ZEND_TYPE_IS_INTERSECTION(type)) {
1547+
zend_type *list_type;
1548+
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
1549+
smart_str_appendc(key, '&');
1550+
zend_type_to_key_impl(key, *list_type);
1551+
} ZEND_TYPE_LIST_FOREACH_END();
1552+
ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type));
1553+
return;
1554+
} else if (ZEND_TYPE_HAS_LIST(type)) {
1555+
/* A union type might not be a list */
1556+
zend_type *list_type;
1557+
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
1558+
smart_str_appendc(key, '|');
1559+
zend_type_to_key_impl(key, *list_type);
1560+
} ZEND_TYPE_LIST_FOREACH_END();
1561+
} else if (ZEND_TYPE_HAS_PNR(type)) {
1562+
zend_packed_name_reference pnr = ZEND_TYPE_PNR(type);
1563+
if (ZEND_PNR_IS_SIMPLE(pnr)) {
1564+
zend_string *lcname = zend_string_tolower(ZEND_PNR_SIMPLE_GET_NAME(pnr));
1565+
smart_str_append(key, lcname);
1566+
zend_string_release(lcname);
1567+
} else {
1568+
smart_str_append(key, ZEND_PNR_COMPLEX_GET_KEY(pnr));
1569+
}
1570+
} else if (ZEND_TYPE_HAS_CLASS_REF(type)) {
1571+
zend_class_reference *ce_ref = ZEND_TYPE_CLASS_REF(type);
1572+
smart_str_append(key, ce_ref->key);
1573+
} else if (ZEND_TYPE_HAS_GENERIC_PARAM(type)) {
1574+
uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(type);
1575+
smart_str_appendc(key, 0);
1576+
smart_str_appendl(key, (char*)&generic_param_id, sizeof(generic_param_id));
1577+
}
1578+
1579+
uint32_t type_mask = ZEND_TYPE_PURE_MASK(type);
1580+
1581+
if (type_mask == MAY_BE_ANY) {
1582+
smart_str_setl(key, ZSTR_VAL(ZSTR_KNOWN(ZEND_STR_MIXED)), ZSTR_LEN(ZSTR_KNOWN(ZEND_STR_MIXED)));
1583+
return;
1584+
}
1585+
if (type_mask & MAY_BE_STATIC) {
1586+
ZEND_ASSERT(0 && "Unsupported yet");
1587+
}
1588+
if (type_mask & MAY_BE_CALLABLE) {
1589+
zend_type_to_key_append_separator();
1590+
smart_str_append(key, ZSTR_KNOWN(ZEND_STR_CALLABLE));
1591+
}
1592+
if (type_mask & MAY_BE_OBJECT) {
1593+
zend_type_to_key_append_separator();
1594+
smart_str_append(key, ZSTR_KNOWN(ZEND_STR_OBJECT));
1595+
}
1596+
if (type_mask & MAY_BE_ARRAY) {
1597+
zend_type_to_key_append_separator();
1598+
smart_str_append(key, ZSTR_KNOWN(ZEND_STR_ARRAY));
1599+
}
1600+
if (type_mask & MAY_BE_STRING) {
1601+
zend_type_to_key_append_separator();
1602+
smart_str_append(key, ZSTR_KNOWN(ZEND_STR_STRING));
1603+
}
1604+
if (type_mask & MAY_BE_LONG) {
1605+
zend_type_to_key_append_separator();
1606+
smart_str_append(key, ZSTR_KNOWN(ZEND_STR_INT));
1607+
}
1608+
if (type_mask & MAY_BE_DOUBLE) {
1609+
zend_type_to_key_append_separator();
1610+
smart_str_append(key, ZSTR_KNOWN(ZEND_STR_FLOAT));
1611+
}
1612+
if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) {
1613+
zend_type_to_key_append_separator();
1614+
smart_str_append(key, ZSTR_KNOWN(ZEND_STR_BOOL));
1615+
} else if (type_mask & MAY_BE_FALSE) {
1616+
zend_type_to_key_append_separator();
1617+
smart_str_append(key, ZSTR_KNOWN(ZEND_STR_FALSE));
1618+
} else if (type_mask & MAY_BE_TRUE) {
1619+
zend_type_to_key_append_separator();
1620+
smart_str_append(key, ZSTR_KNOWN(ZEND_STR_TRUE));
1621+
}
1622+
if (type_mask & MAY_BE_VOID) {
1623+
zend_type_to_key_append_separator();
1624+
smart_str_append(key, ZSTR_KNOWN(ZEND_STR_VOID));
1625+
}
1626+
if (type_mask & MAY_BE_NEVER) {
1627+
zend_type_to_key_append_separator();
1628+
smart_str_append(key, ZSTR_KNOWN(ZEND_STR_NEVER));
1629+
}
1630+
1631+
if (type_mask & MAY_BE_NULL) {
1632+
zend_type_to_key_append_separator();
1633+
smart_str_append(key, ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE));
1634+
}
1635+
}
1636+
15201637
static bool is_generator_compatible_class_type(zend_string *name) {
15211638
return zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_TRAVERSABLE))
15221639
|| zend_string_equals_literal_ci(name, "Iterator")
@@ -1780,13 +1897,27 @@ static zend_name_reference *zend_compile_name_reference(
17801897
size_t alloc_size = ZEND_CLASS_REF_SIZE(num_types);
17811898
zend_name_reference *ref = zend_arena_alloc(&CG(arena), alloc_size);
17821899
ref->name = name;
1783-
ref->args.num_types = num_types;
1784-
if (list) {
1900+
if (num_types == 0) {
1901+
ref->key = zend_new_interned_string(zend_string_tolower(name));
1902+
} else {
1903+
ZEND_ASSERT(list);
1904+
smart_str key = {0};
1905+
zend_string *lcname = zend_string_tolower(name);
1906+
smart_str_append(&key, lcname);
1907+
zend_string_release(lcname);
1908+
smart_str_appendc(&key, '<');
17851909
for (uint32_t i = 0; i < num_types; i++) {
1910+
if (i != 0) {
1911+
smart_str_appendc(&key, ',');
1912+
}
17861913
zend_ast *type_ast = list->child[i];
17871914
ref->args.types[i] = zend_compile_typename(type_ast);
1915+
zend_type_to_key_impl(&key, ref->args.types[i]);
17881916
}
1917+
smart_str_appendc(&key, '>');
1918+
ref->key = zend_new_interned_string(smart_str_extract(&key));
17891919
}
1920+
ref->args.num_types = num_types;
17901921
return ref;
17911922
}
17921923

@@ -4999,10 +5130,14 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
49995130
zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
50005131
}
50015132

5002-
opline = zend_emit_op(result, ZEND_NEW, &class_node, NULL);
5133+
opline = zend_emit_op(result, ZEND_NEW, NULL, NULL);
50035134

50045135
if (class_node.op_type == IS_CONST) {
5136+
opline->op1_type = IS_CONST;
5137+
opline->op1.constant = zend_add_pnr_literal(Z_PNR(class_node.u.constant));
50055138
opline->op2.num = zend_alloc_cache_slot();
5139+
} else {
5140+
SET_NODE(opline->op1, &class_node);
50065141
}
50075142

50085143
zend_compile_call_common(&ctor_result, args_ast, NULL, ast->lineno);
@@ -8270,10 +8405,11 @@ static void zend_compile_generic_params(zend_ast *params_ast)
82708405
}
82718406
}
82728407

8273-
zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr) {
8408+
zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr, zend_string *key) {
82748409
zend_class_reference *ref = (zend_class_reference*) ptr;
82758410
zend_class_entry *ce = (zend_class_entry *) ((char *) ptr + ZEND_CLASS_ENTRY_HEADER_SIZE);
82768411
ref->ce = ce;
8412+
ref->key = key;
82778413
ref->args.num_types = 0;
82788414
return ce;
82798415
}
@@ -8287,7 +8423,6 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
82878423
zend_string *name, *lcname;
82888424
void *ce_ref = zend_arena_alloc(&CG(arena),
82898425
sizeof(zend_class_entry) + ZEND_CLASS_ENTRY_HEADER_SIZE);
8290-
zend_class_entry *ce = zend_init_class_entry_header(ce_ref);
82918426
zend_op *opline;
82928427

82938428
zend_class_entry *original_ce = CG(active_class_entry);
@@ -8327,6 +8462,8 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
83278462
}
83288463
lcname = zend_new_interned_string(lcname);
83298464

8465+
zend_class_entry *ce = zend_init_class_entry_header(ce_ref, lcname);
8466+
83308467
ce->type = ZEND_USER_CLASS;
83318468
ce->name = name;
83328469
zend_initialize_class_data(ce, 1);
@@ -8361,6 +8498,11 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
83618498
if (generic_params_ast) {
83628499
zend_compile_generic_params(generic_params_ast);
83638500
}
8501+
8502+
if (!(decl->flags & ZEND_ACC_ANON_CLASS) &&
8503+
ce->num_generic_params > 0 && ce->num_required_generic_params == 0) {
8504+
zend_alloc_ce_cache(ZEND_CE_TO_REF(ce)->key);
8505+
}
83648506
}
83658507

83668508
if (extends_ast) {

Zend/zend_compile.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,7 @@ void zend_free_internal_arg_info(zend_internal_function *function);
885885
ZEND_API void destroy_zend_function(zend_function *function);
886886
ZEND_API void zend_function_dtor(zval *zv);
887887
ZEND_API void destroy_zend_class(zval *zv);
888+
ZEND_API void destroy_zend_class_reference(zval *zv);
888889
void zend_class_add_ref(zval *zv);
889890

890891
ZEND_API zend_string *zend_mangle_property_name(const char *src1, size_t src1_length, const char *src2, size_t src2_length, bool internal);
@@ -900,6 +901,7 @@ static zend_always_inline const char *zend_get_unmangled_property_name(const zen
900901

901902
#define ZEND_FUNCTION_DTOR zend_function_dtor
902903
#define ZEND_CLASS_DTOR destroy_zend_class
904+
#define ZEND_GENERIC_CLASS_DTOR destroy_zend_class_reference
903905

904906
typedef bool (*zend_needs_live_range_cb)(zend_op_array *op_array, zend_op *opline);
905907
ZEND_API void zend_recalc_live_ranges(
@@ -909,7 +911,7 @@ ZEND_API void pass_two(zend_op_array *op_array);
909911
ZEND_API bool zend_is_compiling(void);
910912
ZEND_API char *zend_make_compiled_string_description(const char *name);
911913
ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_handlers);
912-
zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr);
914+
zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr, zend_string *key);
913915
uint32_t zend_get_class_fetch_type(const zend_string *name);
914916
ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc);
915917
ZEND_API bool zend_is_smart_branch(const zend_op *opline);

Zend/zend_constants.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,10 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *
307307
zval *ret_constant = NULL;
308308

309309
if (ZSTR_HAS_CE_CACHE(class_name)) {
310-
ce = ZSTR_GET_CE_CACHE(class_name);
311-
if (!ce) {
310+
zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(class_name);
311+
if (class_ref) {
312+
ce = class_ref->ce;
313+
} else {
312314
ce = zend_fetch_class(class_name, flags);
313315
}
314316
} else if (zend_string_equals_literal_ci(class_name, "self")) {

Zend/zend_exceptions.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include "zend_builtin_functions.h"
2525
#include "zend_interfaces.h"
2626
#include "zend_exceptions.h"
27+
#include "zend_operators.h"
28+
#include "zend_string.h"
2729
#include "zend_types.h"
2830
#include "zend_vm.h"
2931
#include "zend_dtrace.h"
@@ -798,10 +800,12 @@ void zend_register_default_exception(void) /* {{{ */
798800
zend_init_exception_class_entry(zend_ce_unhandled_match_error);
799801

800802
INIT_CLASS_ENTRY((*zend_ce_unwind_exit), "UnwindExit", NULL);
801-
zend_init_class_entry_header(&zend_ces_unwind_exit);
803+
zend_init_class_entry_header(&zend_ces_unwind_exit,
804+
zend_new_interned_string(zend_string_tolower_ex(zend_ce_unwind_exit->name, true)));
802805

803806
INIT_CLASS_ENTRY((*zend_ce_graceful_exit), "GracefulExit", NULL);
804-
zend_init_class_entry_header(&zend_ces_graceful_exit);
807+
zend_init_class_entry_header(&zend_ces_graceful_exit,
808+
zend_new_interned_string(zend_string_tolower_ex(zend_ce_graceful_exit->name, true)));
805809
}
806810
/* }}} */
807811

0 commit comments

Comments
 (0)