@@ -371,3 +371,140 @@ void zend_enum_register_props(zend_class_entry *ce)
371
371
zend_declare_typed_property (ce , ZSTR_KNOWN (ZEND_STR_VALUE ), & value_default_value , ZEND_ACC_PUBLIC , NULL , value_type );
372
372
}
373
373
}
374
+
375
+ static const zend_function_entry unit_enum_methods [] = {
376
+ ZEND_NAMED_ME (cases , zend_enum_cases_func , arginfo_class_UnitEnum_cases , ZEND_ACC_PUBLIC | ZEND_ACC_STATIC )
377
+ ZEND_FE_END
378
+ };
379
+
380
+ static const zend_function_entry backed_enum_methods [] = {
381
+ ZEND_NAMED_ME (cases , zend_enum_cases_func , arginfo_class_UnitEnum_cases , ZEND_ACC_PUBLIC | ZEND_ACC_STATIC )
382
+ ZEND_NAMED_ME (from , zend_enum_from_func , arginfo_class_BackedEnum_from , ZEND_ACC_PUBLIC | ZEND_ACC_STATIC )
383
+ ZEND_NAMED_ME (tryFrom , zend_enum_try_from_func , arginfo_class_BackedEnum_tryFrom , ZEND_ACC_PUBLIC | ZEND_ACC_STATIC )
384
+ ZEND_FE_END
385
+ };
386
+
387
+ ZEND_API zend_class_entry * zend_register_internal_enum (
388
+ const char * name , zend_uchar type , zend_function_entry * functions )
389
+ {
390
+ ZEND_ASSERT (type == IS_UNDEF || type == IS_LONG || type == IS_STRING );
391
+
392
+ zend_class_entry tmp_ce ;
393
+ INIT_CLASS_ENTRY_EX (tmp_ce , name , strlen (name ), functions );
394
+
395
+ zend_class_entry * ce = zend_register_internal_class (& tmp_ce );
396
+ ce -> ce_flags |= ZEND_ACC_ENUM ;
397
+ ce -> enum_backing_type = type ;
398
+ if (type != IS_UNDEF ) {
399
+ ce -> backed_enum_table = pemalloc (sizeof (HashTable ), 1 );
400
+ zend_hash_init (ce -> backed_enum_table , 0 , NULL , ZVAL_PTR_DTOR , 1 );
401
+ }
402
+
403
+ zend_enum_register_props (ce );
404
+ if (type == IS_UNDEF ) {
405
+ zend_register_functions (
406
+ ce , unit_enum_methods , & ce -> function_table , EG (current_module )-> type );
407
+ zend_class_implements (ce , 1 , zend_ce_unit_enum );
408
+ } else {
409
+ zend_register_functions (
410
+ ce , backed_enum_methods , & ce -> function_table , EG (current_module )-> type );
411
+ zend_class_implements (ce , 1 , zend_ce_backed_enum );
412
+ }
413
+
414
+ return ce ;
415
+ }
416
+
417
+ static zend_ast_ref * create_enum_case_ast (
418
+ zend_string * class_name , zend_string * case_name , zval * value ) {
419
+ // TODO: Use custom node type for enum cases?
420
+ size_t num_children = value ? 3 : 2 ;
421
+ size_t size = sizeof (zend_ast_ref ) + zend_ast_size (num_children )
422
+ + num_children * sizeof (zend_ast_zval );
423
+ char * p = pemalloc (size , 1 );
424
+ zend_ast_ref * ref = (zend_ast_ref * ) p ; p += sizeof (zend_ast_ref );
425
+ GC_SET_REFCOUNT (ref , 1 );
426
+ GC_TYPE_INFO (ref ) = GC_CONSTANT_AST | GC_PERSISTENT | GC_IMMUTABLE ;
427
+
428
+ zend_ast * ast = (zend_ast * ) p ; p += zend_ast_size (3 );
429
+ ast -> kind = ZEND_AST_CONST_ENUM_INIT ;
430
+ ast -> attr = 0 ;
431
+ ast -> lineno = 0 ;
432
+
433
+ ast -> child [0 ] = (zend_ast * ) p ; p += sizeof (zend_ast_zval );
434
+ ast -> child [0 ]-> kind = ZEND_AST_ZVAL ;
435
+ ast -> child [0 ]-> attr = 0 ;
436
+ ZEND_ASSERT (ZSTR_IS_INTERNED (class_name ));
437
+ ZVAL_STR (zend_ast_get_zval (ast -> child [0 ]), class_name );
438
+
439
+ ast -> child [1 ] = (zend_ast * ) p ; p += sizeof (zend_ast_zval );
440
+ ast -> child [1 ]-> kind = ZEND_AST_ZVAL ;
441
+ ast -> child [1 ]-> attr = 0 ;
442
+ ZEND_ASSERT (ZSTR_IS_INTERNED (case_name ));
443
+ ZVAL_STR (zend_ast_get_zval (ast -> child [1 ]), case_name );
444
+
445
+ if (value ) {
446
+ ast -> child [2 ] = (zend_ast * ) p ; p += sizeof (zend_ast_zval );
447
+ ast -> child [2 ]-> kind = ZEND_AST_ZVAL ;
448
+ ast -> child [2 ]-> attr = 0 ;
449
+ ZEND_ASSERT (!Z_REFCOUNTED_P (value ));
450
+ ZVAL_COPY_VALUE (zend_ast_get_zval (ast -> child [2 ]), value );
451
+ } else {
452
+ ast -> child [2 ] = NULL ;
453
+ }
454
+
455
+ return ref ;
456
+ }
457
+
458
+ ZEND_API void zend_enum_add_case (zend_class_entry * ce , zend_string * case_name , zval * value )
459
+ {
460
+ if (value ) {
461
+ ZEND_ASSERT (ce -> enum_backing_type == Z_TYPE_P (value ));
462
+ if (Z_TYPE_P (value ) == IS_STRING && !ZSTR_IS_INTERNED (Z_STR_P (value ))) {
463
+ zval_make_interned_string (value );
464
+ }
465
+
466
+ zval case_name_zv ;
467
+ ZVAL_STR (& case_name_zv , case_name );
468
+ if (Z_TYPE_P (value ) == IS_LONG ) {
469
+ zend_hash_index_add_new (ce -> backed_enum_table , Z_LVAL_P (value ), & case_name_zv );
470
+ } else {
471
+ zend_hash_add_new (ce -> backed_enum_table , Z_STR_P (value ), & case_name_zv );
472
+ }
473
+ } else {
474
+ ZEND_ASSERT (ce -> enum_backing_type == IS_UNDEF );
475
+ }
476
+
477
+ zval ast_zv ;
478
+ Z_TYPE_INFO (ast_zv ) = IS_CONSTANT_AST ;
479
+ Z_AST (ast_zv ) = create_enum_case_ast (ce -> name , case_name , value );
480
+ zend_class_constant * c = zend_declare_class_constant_ex (
481
+ ce , case_name , & ast_zv , ZEND_ACC_PUBLIC , NULL );
482
+ ZEND_CLASS_CONST_FLAGS (c ) |= ZEND_CLASS_CONST_IS_CASE ;
483
+ }
484
+
485
+ ZEND_API void zend_enum_add_case_cstr (zend_class_entry * ce , const char * name , zval * value )
486
+ {
487
+ zend_string * name_str = zend_string_init_interned (name , strlen (name ), 1 );
488
+ zend_enum_add_case (ce , name_str , value );
489
+ zend_string_release (name_str );
490
+ }
491
+
492
+ ZEND_API zend_object * zend_enum_get_case (zend_class_entry * ce , zend_string * name ) {
493
+ zend_class_constant * c = zend_hash_find_ptr (CE_CONSTANTS_TABLE (ce ), name );
494
+ ZEND_ASSERT (ZEND_CLASS_CONST_FLAGS (c ) & ZEND_CLASS_CONST_IS_CASE );
495
+
496
+ if (Z_TYPE (c -> value ) == IS_CONSTANT_AST ) {
497
+ if (zval_update_constant_ex (& c -> value , c -> ce ) == FAILURE ) {
498
+ ZEND_UNREACHABLE ();
499
+ }
500
+ }
501
+ ZEND_ASSERT (Z_TYPE (c -> value ) == IS_OBJECT );
502
+ return Z_OBJ (c -> value );
503
+ }
504
+
505
+ ZEND_API zend_object * zend_enum_get_case_cstr (zend_class_entry * ce , const char * name ) {
506
+ zend_string * name_str = zend_string_init (name , strlen (name ), 0 );
507
+ zend_object * result = zend_enum_get_case (ce , name_str );
508
+ zend_string_release (name_str );
509
+ return result ;
510
+ }
0 commit comments