8000 Fixes for enums · php/php-src@3f62a18 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3f62a18

Browse files
nikiciluuu1994
authored andcommitted
Fixes for enums
1 parent a0a35d9 commit 3f62a18

19 files changed

+218
-169
lines changed

Zend/Optimizer/zend_inference.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3486,7 +3486,7 @@ static zend_always_inline int _zend_update_type_info(
34863486
break;
34873487
case ZEND_FETCH_CONSTANT:
34883488
case ZEND_FETCH_CLASS_CONSTANT:
3489-
UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY, ssa_op->result_def);
3489+
UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY, ssa_op->result_def);
34903490
break;
34913491
case ZEND_STRLEN:
34923492
tmp = MAY_BE_LONG;

Zend/tests/enum/cases-refcount.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ debug_zval_dump(Foo::Bar);
1616

1717
?>
1818
--EXPECT--
19-
object(Foo)#2 (1) refcount(2){
19+
object(Foo)#1 (1) refcount(2){
2020
["name"]=>
21-
string(3) "Bar" refcount(2)
21+
string(3) "Bar" interned
2222
}

Zend/tests/enum/name-property.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ array(1) {
3333
string(3) "Bar"
3434
array(2) {
3535
[0]=>
36-
object(ReflectionProperty)#4 (2) {
36+
object(ReflectionProperty)#3 (2) {
3737
["name"]=>
3838
string(4) "name"
3939
["class"]=>
4040
string(6) "IntFoo"
4141
}
4242
[1]=>
43-
object(ReflectionProperty)#5 (2) {
43+
object(ReflectionProperty)#4 (2) {
4444
["name"]=>
4545
string(5) "value"
4646
["class"]=>

Zend/tests/enum/scalar-int-const-invalid-expr.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ var_dump(Foo::Bar->value);
1111

1212
?>
1313
--EXPECTF--
14-
Fatal error: Unsupported constant expression in %s on line %d
14+
Fatal error: Enum case value must be constant in %s on line %d

Zend/tests/enum/unserialize-refcount.phpt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,23 @@ debug_zval_dump(Foo::Bar);
2323

2424
?>
2525
--EXPECT--
26-
object(Foo)#2 (1) refcount(2){
26+
object(Foo)#1 (1) refcount(2){
2727
["name"]=>
28-
string(3) "Bar" refcount(2)
28+
string(3) "Bar" interned
2929
}
30-
object(Foo)#2 (1) refcount(3){
30+
object(Foo)#1 (1) refcount(3){
3131
["name"]=>
32-
string(3) "Bar" refcount(2)
32+
string(3) "Bar" interned
3333
}
34-
object(Foo)#2 (1) refcount(4){
34+
object(Foo)#1 (1) refcount(4){
3535
["name"]=>
36-
string(3) "Bar" refcount(2)
36+
string(3) "Bar" interned
3737
}
38-
object(Foo)#2 (1) refcount(3){
38+
object(Foo)#1 (1) refcount(3){
3939
["name"]=>
40-
string(3) "Bar" refcount(2)
40+
string(3) "Bar" interned
4141
}
42-
object(Foo)#2 (1) refcount(2){
42+
object(Foo)#1 (1) refcount(2){
4343
["name"]=>
44-
string(3) "Bar" refcount(2)
44+
string(3) "Bar" interned
4545
}

Zend/zend_API.c

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,9 +1337,6 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
13371337
return SUCCESS;
13381338
}
13391339

1340-
// Set ZEND_ACC_CONSTANTS_UPDATED flag before loading constants to avoid infinite recursion on constant enums
1341-
class_type->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
1342-
13431340
if (ce_flags & ZEND_ACC_IMMUTABLE) {
13441341
mutable_data = ZEND_MAP_PTR_GET_IMM(class_type->mutable_data);
13451342
if (mutable_data) {
@@ -1354,7 +1351,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
13541351

13551352
if (class_type->parent) {
13561353
if (UNEXPECTED(zend_update_class_constants(class_type->parent) != SUCCESS)) {
1357-
goto failure;
1354+
return FAILURE;
13581355
}
13591356
}
13601357

@@ -1373,7 +1370,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
13731370
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
13741371
val = &c->value;
13751372
if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) {
1376-
goto failure;
1373+
return FAILURE;
13771374
}
13781375
}
13791376
} ZEND_HASH_FOREACH_END();
@@ -1423,22 +1420,23 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
14231420
ZVAL_COPY(&tmp, val);
14241421
if (UNEXPECTED(zval_update_constant_ex(&tmp, prop_info->ce) != SUCCESS)) {
14251422
zval_ptr_dtor(&tmp);
1426-
goto failure;
1423+
return FAILURE;
14271424
}
14281425
/* property initializers must always be evaluated with strict types */;
14291426
if (UNEXPECTED(!zend_verify_property_type(prop_info, &tmp, /* strict */ 1))) {
14301427
zval_ptr_dtor(&tmp);
1431-
goto failure;
1428+
return FAILURE;
14321429
}
14331430
zval_ptr_dtor(val);
14341431
ZVAL_COPY_VALUE(val, &tmp);
14351432
} else if (UNEXPECTED(zval_update_constant_ex(val, prop_info->ce) != SUCCESS)) {
1436-
goto failure;
1433+
return FAILURE;
14371434
}
14381435
}
14391436
} ZEND_HASH_FOREACH_END();
14401437
}
14411438

1439+
ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
14421440
ce_flags &= ~ZEND_ACC_HAS_AST_CONSTANTS;
14431441
ce_flags &= ~ZEND_ACC_HAS_AST_PROPERTIES;
14441442
if (class_type->ce_flags & ZEND_ACC_IMMUTABLE) {
@@ -1454,10 +1452,6 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
14541452
}
14551453

14561454
return SUCCESS;
1457-
1458-
failure:
1459-
class_type->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
1460-
return FAILURE;
14611455
}
14621456
/* }}} */
14631457

Zend/zend_compile.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7679,7 +7679,7 @@ static void zend_compile_enum_case(zend_ast *ast)
76797679
zend_error_noreturn(E_COMPILE_ERROR, "Case can only be used in enums");
76807680
}
76817681

7682-
zend_string *enum_case_name = zend_ast_get_str(ast->child[0]);
7682+
zend_string *enum_case_name = zval_make_interned_string(zend_ast_get_zval(ast->child[0]));
76837683
zend_string *enum_class_name = enum_class->name;
76847684

76857685
zval class_name_zval;
@@ -7704,18 +7704,22 @@ static void zend_compile_enum_case(zend_ast *ast)
77047704
ZSTR_VAL(enum_class_name));
77057705
}
77067706

7707-
if (!zend_is_allowed_in_const_expr(case_value_ast->kind)) {
7707+
zend_eval_const_expr(&ast->child[1]);
7708+
case_value_ast = ast->child[1];
7709+
if (case_value_ast->kind != ZEND_AST_ZVAL) {
77087710
zend_error_noreturn(E_COMPILE_ERROR, "Enum case value must be constant");
77097711
}
7712+
77107713
zval case_value_zv;
7711-
zend_ast_evaluate(&case_value_zv, case_value_ast, enum_class);
7714+
ZVAL_COPY(&case_value_zv, zend_ast_get_zval(case_value_ast));
77127715
if (enum_class->enum_scalar_type != Z_TYPE(case_value_zv)) {
77137716
zend_error_noreturn(E_COMPILE_ERROR, "Enum case type %s does not match enum scalar type %s",
77147717
zend_get_type_by_const(Z_TYPE(case_value_zv)),
77157718
zend_get_type_by_const(enum_class->enum_scalar_type));
77167719
}
77177720
case_value_zval_ast = zend_ast_create_zval(&case_value_zv);
77187721

7722+
Z_TRY_ADDREF(case_name_zval);
77197723
if (enum_class->enum_scalar_type == IS_LONG) {
77207724
zend_long long_key = Z_LVAL(case_value_zv);
77217725
zval *existing_case_name = zend_hash_index_find(enum_class->enum_scalar_table, long_key);
@@ -7745,7 +7749,7 @@ static void zend_compile_enum_case(zend_ast *ast)
77457749
zval value_zv;
77467750
zend_const_expr_to_zval(&value_zv, &const_enum_init_ast);
77477751
zend_class_constant *c = zend_declare_class_constant_ex(enum_class, enum_case_name, &value_zv, ZEND_ACC_PUBLIC, NULL);
7748-
c->value.u2.access_flags |= ZEND_CLASS_CONST_IS_CASE;
7752+
Z_ACCESS_FLAGS(c->value) |= ZEND_CLASS_CONST_IS_CASE;
77497753

77507754
zend_ast *attr_ast = ast->child[2];
77517755
if (attr_ast) {

Zend/zend_enum.c

Lines changed: 69 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
#include "zend.h"
2020
#include "zend_API.h"
2121
#include "zend_compile.h"
22-
#include "zend_interfaces_arginfo.h"
22+
#include "zend_enum_arginfo.h"
2323

2424
#define ZEND_ENUM_PROPERTY_ERROR() \
2525
zend_throw_error(NULL, "Enum properties are immutable")
@@ -31,16 +31,16 @@
3131
} \
3232
} while (0);
3333

34+
ZEND_API zend_class_entry *zend_ce_unit_enum;
35+
ZEND_API zend_class_entry *zend_ce_scalar_enum;
36+
3437
static zend_object_handlers enum_handlers;
3538

3639
zend_object *zend_enum_new(zval *result, zend_class_entry *ce, zend_string *case_name, zval *scalar_zv)
3740< 10000 div class="diff-text-inner">{
38-
// Temporarily remove the ZEND_ACC_ENUM flag to allow instantiation
39-
ce->ce_flags &= ~ZEND_ACC_ENUM;
40-
object_init_ex(result, ce);
41-
ce->ce_flags |= ZEND_ACC_ENUM;
41+
zend_object *zobj = zend_objects_new(ce);
42+
ZVAL_OBJ(result, zobj);
4243

43-
zend_object *zobj = Z_OBJ_P(result);
4444
ZVAL_STR_COPY(OBJ_PROP_NUM(zobj, 0), case_name);
4545
if (scalar_zv != NULL) {
4646
ZVAL_COPY(OBJ_PROP_NUM(zobj, 1), scalar_zv);
@@ -123,7 +123,7 @@ static zval *zend_enum_read_property(zend_object *zobj, zend_string *name, int t
123123
return zend_std_read_property(zobj, name, type, cache_slot, rv);
124124
}
125125

126-
ZEND_COLD zval *zend_enum_write_property(zend_object *object, zend_string *member, zval *value, void **cache_slot)
126+
static ZEND_COLD zval *zend_enum_write_property(zend_object *object, zend_string *member, zval *value, void **cache_slot)
127127
{
128128
ZEND_ENUM_PROPERTY_ERROR();
129129
return &EG(uninitialized_zval);
@@ -134,12 +134,12 @@ static ZEND_COLD void zend_enum_unset_property(zend_object *object, zend_string
134134
ZEND_ENUM_PROPERTY_ERROR();
135135
}
136136

137-
ZEND_API zval *zend_enum_get_property_ptr_ptr(zend_object *zobj, zend_string *name, int type, void **cache_slot)
137+
static zval *zend_enum_get_property_ptr_ptr(zend_object *zobj, zend_string *name, int type, void **cache_slot)
138138
{
139139
return NULL;
140140
}
141141

142-
int zend_enum_compare_objects(zval *o1, zval *o2)
142+
static int zend_enum_compare_objects(zval *o1, zval *o2)
143143
{
144144
if (Z_TYPE_P(o1) != IS_OBJECT || Z_TYPE_P(o2) != IS_OBJECT) {
145145
return ZEND_UNCOMPARABLE;
@@ -148,8 +148,46 @@ int zend_enum_compare_objects(zval *o1, zval *o2)
148148
return Z_OBJ_P(o1) == Z_OBJ_P(o2) ? 0 : 1;
149149
}
150150

151+
static int zend_implement_unit_enum(zend_class_entry *interface, zend_class_entry *class_type)
152+
{
153+
if (class_type->ce_flags & ZEND_ACC_ENUM) {
154+
return SUCCESS;
155+
}
156+
157+
zend_error_noreturn(E_ERROR, "Non-enum class %s cannot implement interface %s",
158+
ZSTR_VAL(class_type->name),
159+
ZSTR_VAL(interface->name));
160+
161+
return FAILURE;
162+
}
163+
164+
static int zend_implement_scalar_enum(zend_class_entry *interface, zend_class_entry *class_type)
165+
{
166+
if (!(class_type->ce_flags & ZEND_ACC_ENUM)) {
167+
zend_error_noreturn(E_ERROR, "Non-enum class %s cannot implement interface %s",
168+
ZSTR_VAL(class_type->name),
169+
ZSTR_VAL(interface->name));
170+
return FAILURE;
171+
}
172+
173+
if (class_type->enum_scalar_type == IS_UNDEF) {
174+
zend_error_noreturn(E_ERROR, "Non-scalar enum %s cannot implement interface %s",
175+
ZSTR_VAL(class_type->name),
176+
ZSTR_VAL(interface->name));
177+
return FAILURE;
178+
}
179+
180+
return SUCCESS;
181+
}
182+
151183
void zend_register_enum_ce(void)
152184
{
185+
zend_ce_unit_enum = register_class_UnitEnum();
186+
zend_ce_unit_enum->interface_gets_implemented = zend_implement_unit_enum;
187+
188+
zend_ce_scalar_enum = register_class_ScalarEnum(zend_ce_unit_enum);
189+
zend_ce_scalar_enum->interface_gets_implemented = zend_implement_scalar_enum;
190+
153191
memcpy(&enum_handlers, &std_object_handlers, sizeof(zend_object_handlers));
154192
enum_handlers.read_property = zend_enum_read_property;
155193
enum_handlers.write_property = zend_enum_write_property;
@@ -190,8 +228,8 @@ static ZEND_NAMED_FUNCTION(zend_enum_cases_func)
190228

191229
array_init(return_value);
192230

193-
ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
194-
if (!(c->value.u2.access_flags & ZEND_CLASS_CONST_IS_CASE)) {
231+
ZEND_HASH_FOREACH_PTR(CE_CONSTANTS_TABLE(ce), c) {
232+
if (!(Z_ACCESS_FLAGS(c->value) & ZEND_CLASS_CONST_IS_CASE)) {
195233
continue;
196234
}
197235
zval *zv = &c->value;
@@ -243,9 +281,8 @@ static void zend_enum_from_base(INTERNAL_FUNCTION_PARAMETERS, bool try)
243281
}
244282

245283
ZEND_ASSERT(Z_TYPE_P(case_name_zv) == IS_STRING);
246-
zval *case_const_zv = zend_hash_find(&ce->constants_table, Z_STR_P(case_name_zv));
247-
ZEND_ASSERT(case_const_zv != NULL);
248-
zend_class_constant *c = Z_PTR_P(case_const_zv);
284+
zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), Z_STR_P(case_name_zv));
285+
ZEND_ASSERT(c != NULL);
249286
zval *case_zv = &c->value;
250287
if (Z_TYPE_P(case_zv) == IS_CONSTANT_AST) {
251288
zval_update_constant_ex(case_zv, c->ce);
@@ -269,45 +306,49 @@ static ZEND_NAMED_FUNCTION(zend_enum_try_from_func)
269306

270307
void zend_enum_register_funcs(zend_class_entry *ce)
271308
{
272-
zend_string *cases_func_name = zend_string_init("cases", strlen("cases"), 1);
273-
zend_internal_function *cases_function = malloc(sizeof(zend_internal_function));
309+
const uint32_t fn_flags =
310+
ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_ARENA_ALLOCATED;
311+
zend_internal_function *cases_function =
312+
zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
274313
memset(cases_function, 0, sizeof(zend_internal_function));
275314
cases_function->type = ZEND_INTERNAL_FUNCTION;
276315
cases_function->module = EG(current_module);
277316
cases_function->handler = zend_enum_cases_func;
278-
cases_function->function_name = cases_func_name;
317+
cases_function->function_name = ZSTR_KNOWN(ZEND_STR_CASES);
279318
cases_function->scope = ce;
280-
cases_function->fn_flags = ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_HAS_RETURN_TYPE;
319+
cases_function->fn_flags = fn_flags;
281320
cases_function->arg_info = (zend_internal_arg_info *) (arginfo_class_UnitEnum_cases + 1);
282-
zend_hash_add_ptr(&ce->function_table, cases_func_name, cases_function);
321+
zend_hash_add_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_CASES), cases_function);
283322

284323
if (ce->enum_scalar_type != IS_UNDEF) {
285-
zend_string *from_func_name = zend_string_init("from", strlen("from"), 1);
286-
zend_internal_function *from_function = malloc(sizeof(zend_internal_function));
324+
zend_internal_function *from_function =
325+
zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
287326
memset(from_function, 0, sizeof(zend_internal_function));
288327
from_function->type = ZEND_INTERNAL_FUNCTION;
289328
from_function->module = EG(current_module);
290329
from_function->handler = zend_enum_from_func;
291-
from_function->function_name = from_func_name;
330+
from_function->function_name = ZSTR_KNOWN(ZEND_STR_FROM);
292331
from_function->scope = ce;
293-
from_function->fn_flags = ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_HAS_RETURN_TYPE;
332+
from_function->fn_flags = fn_flags;
294333
from_function->num_args = 1;
295334
from_function->required_num_args = 1;
296335
from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_ScalarEnum_from + 1);
297-
zend_hash_add_ptr(&ce->function_table, from_func_name, from_function);
336+
zend_hash_add_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_FROM), from_function);
298337

299-
zend_internal_function *try_from_function = malloc(sizeof(zend_internal_function));
338+
zend_internal_function *try_from_function =
339+
zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
300340
memset(try_from_function, 0, sizeof(zend_internal_function));
301341
try_from_function->type = ZEND_INTERNAL_FUNCTION;
302342
try_from_function->module = EG(current_module);
303343
try_from_function->handler = zend_enum_try_from_func;
304-
try_from_function->function_name = zend_string_init("tryFrom", strlen("tryFrom"), 1);
344+
try_from_function->function_name = ZSTR_KNOWN(ZEND_STR_TRYFROM);
305345
try_from_function->scope = ce;
306-
try_from_function->fn_flags = ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_HAS_RETURN_TYPE;
346+
try_from_function->fn_flags = fn_flags;
307347
try_from_function->num_args = 1;
308348
try_from_function->required_num_args = 1;
309349
try_from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_ScalarEnum_tryFrom + 1);
310-
zend_hash_add_ptr(&ce->function_table, zend_string_init("tryfrom", strlen("tryfrom"), 1), try_from_function);
350+
zend_hash_add_ptr(
351+
&ce->function_table, ZSTR_KNOWN(ZEND_STR_TRYFROM_LOWERCASE), try_from_function);
311352
}
312353
}
313354

Zend/zend_enum.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
#include "zend.h"
2323
#include "zend_types.h"
2424

25+
BEGIN_EXTERN_C()
26+
27+
extern ZEND_API zend_class_entry *zend_ce_unit_enum;
28+
extern ZEND_API zend_class_entry *zend_ce_scalar_enum;
7DAC
29+
2530
void zend_register_enum_ce(void);
2631
void zend_enum_add_interfaces(zend_class_entry *ce);
2732
zend_object *zend_enum_new(zval *result, zend_class_entry *ce, zend_string *case_name, zval *scalar_zv);
@@ -31,4 +36,6 @@ void zend_enum_register_props(zend_class_entry *ce);
3136
zval *zend_enum_fetch_case_name(zend_object *zobj);
3237
zval *zend_enum_fetch_case_value(zend_object *zobj);
3338

39+
END_EXTERN_C()
40+
3441
#endif /* ZEND_ENUM_H */

0 commit comments

Comments
 (0)
0