Skip to content

Commit d16e6f5

Browse files
authored
Generate C enums from internal enums, introduce Z_PARAM_ENUM() (#20917)
Update gen_stubs.php to generate C enums from internal enums, when the stub is annotated with @generate-c-enums. Enum values can be compared to the result of zend_enum_fetch_case_id(zend_object*). The generated enums are added to separate files named {$extensionName}_decl.h, so that it's possible to include these from anywhere. _arginfo.h files would generate warnings if we tried to include them in a compilation unit that doesn't call the register_{$class} functions, for instance. Introduce Z_PARAM_ENUM(). * Make ZEND_AST_CONST_ENUM_INIT a 4-children node * Store enum case id in ZEND_AST_CONST_ENUM_INIT * Store enum case id in instance * Expose enum case_id internally * Generate C enum for internal enums * Introduce Z_PARAM_ENUM() * Port extensions
1 parent 4a1cca7 commit d16e6f5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+579
-210
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
# Collapse generated files within git and pull request diff.
2323
**/*_arginfo.h linguist-generated -diff
24+
**/*_decl.h linguist-generated -diff
2425
/main/debug_gdb_scripts.c linguist-generated -diff
2526
/Zend/zend_vm_execute.h linguist-generated -diff
2627
/Zend/zend_vm_handlers.h linguist-generated -diff

UPGRADING.INTERNALS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,20 @@ PHP 8.6 INTERNALS UPGRADE NOTES
6060
automatically unwrap references when the result of the call is stored in an
6161
IS_TMP_VAR variable. This may be achieved by calling the
6262
zend_return_unwrap_ref() function.
63+
. The php_math_round_mode_from_enum() function now takes a
64+
zend_enum_RoundingMode parameter.
65+
. Added Z_PARAM_ENUM().
66+
. Added zend_enum_fetch_case_id().
6367

6468
========================
6569
2. Build system changes
6670
========================
6771

72+
. build/gen_stub.php may now generate a _decl.h file in addition to
73+
the _arginfo.h file, if the stub declares enums and is annotated with
74+
@generate-c-enums. For each enum the file will contain a C enum. Enum values
75+
can be compared to the result of zend_enum_fetch_case_id(zend_object*).
76+
6877
========================
6978
3. Module changes
7079
========================

Zend/zend_API.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,6 +2009,13 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string
20092009
#define Z_PARAM_OBJ_OF_CLASS_OR_LONG_OR_NULL(dest_obj, _ce, dest_long, is_null) \
20102010
Z_PARAM_OBJ_OF_CLASS_OR_LONG_EX(dest_obj, _ce, dest_long, is_null, 1)
20112011

2012+
#define Z_PARAM_ENUM(dest, _ce) \
2013+
{ \
2014+
zend_object *_tmp = NULL; \
2015+
Z_PARAM_OBJ_OF_CLASS(_tmp, _ce); \
2016+
dest = zend_enum_fetch_case_id(_tmp); \
2017+
}
2018+
20122019
/* old "p" */
20132020
#define Z_PARAM_PATH_EX(dest, dest_len, check_null, deref) \
20142021
Z_PARAM_PROLOGUE(deref, 0); \

Zend/zend_ast.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -995,10 +995,13 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
995995
zend_ast *class_name_ast = ast->child[0];
996996
zend_string *class_name = zend_ast_get_str(class_name_ast);
997997

998-
zend_ast *case_name_ast = ast->child[1];
998+
zend_ast *case_id_ast = ast->child[1];
999+
int case_id = (int)Z_LVAL_P(zend_ast_get_zval(case_id_ast));
1000+
1001+
zend_ast *case_name_ast = ast->child[2];
9991002
zend_string *case_name = zend_ast_get_str(case_name_ast);
10001003

1001-
zend_ast *case_value_ast = ast->child[2];
1004+
zend_ast *case_value_ast = ast->child[3];
10021005

10031006
zval case_value_zv;
10041007
ZVAL_UNDEF(&case_value_zv);
@@ -1009,7 +1012,7 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
10091012
}
10101013

10111014
zend_class_entry *ce = zend_lookup_class(class_name);
1012-
zend_enum_new(result, ce, case_name, case_value_ast != NULL ? &case_value_zv : NULL);
1015+
zend_enum_new(result, ce, case_id, case_name, case_value_ast != NULL ? &case_value_zv : NULL);
10131016
zval_ptr_dtor_nogc(&case_value_zv);
10141017
break;
10151018
}

Zend/zend_ast.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,15 @@ enum _zend_ast_kind {
168168
ZEND_AST_CONST_ELEM,
169169
ZEND_AST_CLASS_CONST_GROUP,
170170

171-
// Pseudo node for initializing enums
172-
ZEND_AST_CONST_ENUM_INIT,
173-
174171
/* 4 child nodes */
175172
ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT,
176173
ZEND_AST_FOREACH,
177174
ZEND_AST_ENUM_CASE,
178175
ZEND_AST_PROP_ELEM,
179176

177+
// Pseudo node for initializing enums
178+
ZEND_AST_CONST_ENUM_INIT,
179+
180180
/* 5 child nodes */
181181

182182
/* 6 child nodes */

Zend/zend_compile.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9756,6 +9756,11 @@ static void zend_compile_enum_case(zend_ast *ast)
97569756
ZVAL_STR_COPY(&class_name_zval, enum_class_name);
97579757
zend_ast *class_name_ast = zend_ast_create_zval(&class_name_zval);
97589758

9759+
zval case_id_zval;
9760+
int case_id = zend_enum_next_case_id(enum_class);
9761+
ZVAL_LONG(&case_id_zval, case_id);
9762+
zend_ast *case_id_ast = zend_ast_create_zval(&case_id_zval);
9763+
97599764
zval case_name_zval;
97609765
ZVAL_STR_COPY(&case_name_zval, enum_case_name);
97619766
zend_ast *case_name_ast = zend_ast_create_zval(&case_name_zval);
@@ -9773,7 +9778,8 @@ static void zend_compile_enum_case(zend_ast *ast)
97739778
ZSTR_VAL(enum_class_name));
97749779
}
97759780

9776-
zend_ast *const_enum_init_ast = zend_ast_create(ZEND_AST_CONST_ENUM_INIT, class_name_ast, case_name_ast, case_value_ast);
9781+
zend_ast *const_enum_init_ast = zend_ast_create(ZEND_AST_CONST_ENUM_INIT,
9782+
class_name_ast, case_id_ast, case_name_ast, case_value_ast);
97779783

97789784
zval value_zv;
97799785
zend_const_expr_to_zval(&value_zv, &const_enum_init_ast, /* allow_dynamic */ false);
@@ -12669,7 +12675,7 @@ static void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
1266912675
zend_eval_const_expr(&ast->child[1]);
1267012676
return;
1267112677
case ZEND_AST_CONST_ENUM_INIT:
12672-
zend_eval_const_expr(&ast->child[2]);
12678+
zend_eval_const_expr(&ast->child[3]);
1267312679
return;
1267412680
case ZEND_AST_PROP:
1267512681
case ZEND_AST_NULLSAFE_PROP:

Zend/zend_enum.c

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,16 @@ static zend_arg_info zarginfo_class_UnitEnum_cases[sizeof(arginfo_class_UnitEnum
4040
static zend_arg_info zarginfo_class_BackedEnum_from[sizeof(arginfo_class_BackedEnum_from)/sizeof(zend_internal_arg_info)];
4141
static zend_arg_info zarginfo_class_BackedEnum_tryFrom[sizeof(arginfo_class_BackedEnum_tryFrom)/sizeof(zend_internal_arg_info)];
4242

43-
zend_object *zend_enum_new(zval *result, zend_class_entry *ce, zend_string *case_name, zval *backing_value_zv)
43+
zend_object *zend_enum_new(zval *result, zend_class_entry *ce, int case_id, zend_string *case_name, zval *backing_value_zv)
4444
{
45-
zend_object *zobj = zend_objects_new(ce);
45+
zend_enum_obj *intern = zend_object_alloc(sizeof(*intern), ce);
46+
47+
zend_object_std_init(&intern->std, ce);
48+
object_properties_init(&intern->std, ce);
49+
50+
intern->case_id = case_id;
51+
52+
zend_object *zobj = &intern->std;
4653
GC_ADD_FLAGS(zobj, GC_NOT_COLLECTABLE);
4754
ZVAL_OBJ(result, zobj);
4855

@@ -170,6 +177,7 @@ void zend_register_enum_ce(void)
170177
zend_ce_backed_enum->interface_gets_implemented = zend_implement_backed_enum;
171178

172179
memcpy(&zend_enum_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
180+
zend_enum_object_handlers.offset = XtOffsetOf(zend_enum_obj, std);
173181
zend_enum_object_handlers.clone_obj = NULL;
174182
zend_enum_object_handlers.compare = zend_objects_not_comparable;
175183
}
@@ -539,16 +547,18 @@ ZEND_API zend_class_entry *zend_register_internal_enum(
539547
}
540548

541549
static zend_ast_ref *create_enum_case_ast(
542-
zend_string *class_name, zend_string *case_name, zval *value) {
550+
zend_string *class_name, int case_id, zend_string *case_name,
551+
zval *value) {
543552
// TODO: Use custom node type for enum cases?
544-
size_t size = sizeof(zend_ast_ref) + zend_ast_size(3)
545-
+ (value ? 3 : 2) * sizeof(zend_ast_zval);
553+
const size_t num_children = ZEND_AST_CONST_ENUM_INIT >> ZEND_AST_NUM_CHILDREN_SHIFT;
554+
size_t size = sizeof(zend_ast_ref) + zend_ast_size(num_children)
555+
+ (value ? num_children : num_children-1) * sizeof(zend_ast_zval);
546556
char *p = pemalloc(size, 1);
547557
zend_ast_ref *ref = (zend_ast_ref *) p; p += sizeof(zend_ast_ref);
548558
GC_SET_REFCOUNT(ref, 1);
549559
GC_TYPE_INFO(ref) = GC_CONSTANT_AST | GC_PERSISTENT | GC_IMMUTABLE;
550560

551-
zend_ast *ast = (zend_ast *) p; p += zend_ast_size(3);
561+
zend_ast *ast = (zend_ast *) p; p += zend_ast_size(num_children);
552562
ast->kind = ZEND_AST_CONST_ENUM_INIT;
553563
ast->attr = 0;
554564
ast->lineno = 0;
@@ -563,24 +573,47 @@ static zend_ast_ref *create_enum_case_ast(
563573
ast->child[1] = (zend_ast *) p; p += sizeof(zend_ast_zval);
564574
ast->child[1]->kind = ZEND_AST_ZVAL;
565575
ast->child[1]->attr = 0;
566-
ZEND_ASSERT(ZSTR_IS_INTERNED(case_name));
567-
ZVAL_STR(zend_ast_get_zval(ast->child[1]), case_name);
576+
ZVAL_LONG(zend_ast_get_zval(ast->child[1]), case_id);
568577
Z_LINENO_P(zend_ast_get_zval(ast->child[1])) = 0;
569578

579+
ast->child[2] = (zend_ast *) p; p += sizeof(zend_ast_zval);
580+
ast->child[2]->kind = ZEND_AST_ZVAL;
581+
ast->child[2]->attr = 0;
582+
ZEND_ASSERT(ZSTR_IS_INTERNED(case_name));
583+
ZVAL_STR(zend_ast_get_zval(ast->child[2]), case_name);
584+
Z_LINENO_P(zend_ast_get_zval(ast->child[2])) = 0;
585+
570586
if (value) {
571-
ast->child[2] = (zend_ast *) p; p += sizeof(zend_ast_zval);
572-
ast->child[2]->kind = ZEND_AST_ZVAL;
573-
ast->child[2]->attr = 0;
587+
ast->child[3] = (zend_ast *) p; p += sizeof(zend_ast_zval);
588+
ast->child[3]->kind = ZEND_AST_ZVAL;
589+
ast->child[3]->attr = 0;
574590
ZEND_ASSERT(!Z_REFCOUNTED_P(value));
575-
ZVAL_COPY_VALUE(zend_ast_get_zval(ast->child[2]), value);
576-
Z_LINENO_P(zend_ast_get_zval(ast->child[2])) = 0;
591+
ZVAL_COPY_VALUE(zend_ast_get_zval(ast->child[3]), value);
592+
Z_LINENO_P(zend_ast_get_zval(ast->child[3])) = 0;
577593
} else {
578-
ast->child[2] = NULL;
594+
ast->child[3] = NULL;
579595
}
580596

581597
return ref;
582598
}
583599

600+
int zend_enum_next_case_id(zend_class_entry *enum_class)
601+
{
602+
ZEND_HASH_REVERSE_FOREACH_VAL(&enum_class->constants_table, zval *zv) {
603+
zend_class_constant *c = Z_PTR_P(zv);
604+
if (!(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE)) {
605+
continue;
606+
}
607+
ZEND_ASSERT(Z_TYPE(c->value) == IS_CONSTANT_AST);
608+
zend_ast *ast = Z_ASTVAL(c->value);
609+
610+
ZEND_ASSERT(ast->kind == ZEND_AST_CONST_ENUM_INIT);
611+
return Z_LVAL_P(zend_ast_get_zval(ast->child[1])) + 1;
612+
} ZEND_HASH_FOREACH_END();
613+
614+
return 1;
615+
}
616+
584617
ZEND_API void zend_enum_add_case(zend_class_entry *ce, zend_string *case_name, zval *value)
585618
{
586619
if (value) {
@@ -602,9 +635,11 @@ ZEND_API void zend_enum_add_case(zend_class_entry *ce, zend_string *case_name, z
602635
ZEND_ASSERT(ce->enum_backing_type == IS_UNDEF);
603636
}
604637

638+
int case_id = zend_enum_next_case_id(ce);
639+
605640
zval ast_zv;
606641
Z_TYPE_INFO(ast_zv) = IS_CONSTANT_AST;
607-
Z_AST(ast_zv) = create_enum_case_ast(ce->name, case_name, value);
642+
Z_AST(ast_zv) = create_enum_case_ast(ce->name, case_id, case_name, value);
608643
zend_class_constant *c = zend_declare_class_constant_ex(
609644
ce, case_name, &ast_zv, ZEND_ACC_PUBLIC, NULL);
610645
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_CLASS_CONST_IS_CASE;

Zend/zend_enum.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,25 @@ extern ZEND_API zend_class_entry *zend_ce_unit_enum;
3030
extern ZEND_API zend_class_entry *zend_ce_backed_enum;
3131
extern ZEND_API zend_object_handlers zend_enum_object_handlers;
3232

33+
typedef struct zend_enum_obj {
34+
int case_id;
35+
zend_object std;
36+
} zend_enum_obj;
37+
38+
static inline zend_enum_obj *zend_enum_obj_from_obj(zend_object *zobj) {
39+
ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM);
40+
return (zend_enum_obj*)((char*)(zobj) - XtOffsetOf(zend_enum_obj, std));
41+
}
42+
3343
void zend_enum_startup(void);
3444
void zend_register_enum_ce(void);
3545
void zend_enum_add_interfaces(zend_class_entry *ce);
3646
zend_result zend_enum_build_backed_enum_table(zend_class_entry *ce);
37-
zend_object *zend_enum_new(zval *result, zend_class_entry *ce, zend_string *case_name, zval *backing_value_zv);
47+
zend_object *zend_enum_new(zval *result, zend_class_entry *ce, int case_id, zend_string *case_name, zval *backing_value_zv);
3848
void zend_verify_enum(const zend_class_entry *ce);
3949
void zend_enum_register_funcs(zend_class_entry *ce);
4050
void zend_enum_register_props(zend_class_entry *ce);
51+
int zend_enum_next_case_id(zend_class_entry *enum_class);
4152

4253
ZEND_API zend_class_entry *zend_register_internal_enum(
4354
const char *name, uint8_t type, const zend_function_entry *functions);
@@ -47,6 +58,12 @@ ZEND_API zend_object *zend_enum_get_case(zend_class_entry *ce, zend_string *name
4758
ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *name);
4859
ZEND_API zend_result zend_enum_get_case_by_value(zend_object **result, zend_class_entry *ce, zend_long long_key, zend_string *string_key, bool try_from);
4960

61+
static zend_always_inline int zend_enum_fetch_case_id(zend_object *zobj)
62+
{
63+
ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM);
64+
return zend_enum_obj_from_obj(zobj)->case_id;
65+
}
66+
5067
static zend_always_inline zval *zend_enum_fetch_case_name(zend_object *zobj)
5168
{
5269
ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM);

0 commit comments

Comments
 (0)