mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Allow arbitrary const expressions in backed enums
Closes GH-7821 Closes GH-8190 Closes GH-8418
This commit is contained in:
parent
5a855ee8d6
commit
ddc0b490f7
23 changed files with 287 additions and 144 deletions
2
NEWS
2
NEWS
|
@ -7,6 +7,8 @@ PHP NEWS
|
||||||
references). (Nicolas Grekas)
|
references). (Nicolas Grekas)
|
||||||
. Fixed bug GH-8661 (Nullsafe in coalesce triggers undefined variable
|
. Fixed bug GH-8661 (Nullsafe in coalesce triggers undefined variable
|
||||||
warning). (ilutov)
|
warning). (ilutov)
|
||||||
|
. Fixed bug GH-7821 and GH-8418 (Allow arbitrary const expressions in backed
|
||||||
|
enums). (ilutov)
|
||||||
|
|
||||||
- MBString:
|
- MBString:
|
||||||
. Backwards-compatible mappings for 0x5C/0x7E in Shift-JIS are restored,
|
. Backwards-compatible mappings for 0x5C/0x7E in Shift-JIS are restored,
|
||||||
|
|
|
@ -8,6 +8,33 @@ enum Foo: int {
|
||||||
case Baz = 0;
|
case Baz = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(Foo::Bar);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(Foo::Bar);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(Foo::from(42));
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(Foo::tryFrom('bar'));
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
--EXPECTF--
|
--EXPECT--
|
||||||
Fatal error: Duplicate value in enum Foo for cases Bar and Baz in %s on line %s
|
Duplicate value in enum Foo for cases Bar and Baz
|
||||||
|
Duplicate value in enum Foo for cases Bar and Baz
|
||||||
|
Duplicate value in enum Foo for cases Bar and Baz
|
||||||
|
Foo::tryFrom(): Argument #1 ($value) must be of type int, string given
|
||||||
|
|
|
@ -10,6 +10,33 @@ enum Suit: string {
|
||||||
case Spades = 'H';
|
case Spades = 'H';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(Suit::Hearts);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(Suit::Hearts);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(Suit::from(42));
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(Suit::tryFrom('bar'));
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
--EXPECTF--
|
--EXPECT--
|
||||||
Fatal error: Duplicate value in enum Suit for cases Hearts and Spades in %s on line %s
|
Duplicate value in enum Suit for cases Hearts and Spades
|
||||||
|
Duplicate value in enum Suit for cases Hearts and Spades
|
||||||
|
Duplicate value in enum Suit for cases Hearts and Spades
|
||||||
|
Duplicate value in enum Suit for cases Hearts and Spades
|
||||||
|
|
|
@ -11,4 +11,4 @@ var_dump(Foo::Bar->value);
|
||||||
|
|
||||||
?>
|
?>
|
||||||
--EXPECTF--
|
--EXPECTF--
|
||||||
Fatal error: Enum case value must be compile-time evaluatable in %s on line %d
|
Fatal error: Constant expression contains invalid operations in %s on line %d
|
||||||
|
|
|
@ -7,6 +7,33 @@ enum Foo: int {
|
||||||
case Bar = 'bar';
|
case Bar = 'bar';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(Foo::Bar);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(Foo::Bar);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(Foo::from(42));
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(Foo::from('bar'));
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
--EXPECTF--
|
--EXPECT--
|
||||||
Fatal error: Enum case type string does not match enum backing type int in %s on line %d
|
TypeError: Enum case type string does not match enum backing type int
|
||||||
|
TypeError: Enum case type string does not match enum backing type int
|
||||||
|
TypeError: Enum case type string does not match enum backing type int
|
||||||
|
TypeError: Foo::from(): Argument #1 ($value) must be of type int, string given
|
||||||
|
|
26
Zend/tests/enum/gh7821.phpt
Normal file
26
Zend/tests/enum/gh7821.phpt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
--TEST--
|
||||||
|
GH-7821: Can't use arbitrary constant expressions in enum cases
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
public const A = 'A';
|
||||||
|
public const B = 'B';
|
||||||
|
}
|
||||||
|
|
||||||
|
enum B: string implements I {
|
||||||
|
case C = I::A;
|
||||||
|
case D = self::B;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(B::A);
|
||||||
|
var_dump(B::B);
|
||||||
|
var_dump(B::C->value);
|
||||||
|
var_dump(B::D->value);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
string(1) "A"
|
||||||
|
string(1) "B"
|
||||||
|
string(1) "A"
|
||||||
|
string(1) "B"
|
20
Zend/tests/enum/gh8418.phpt
Normal file
20
Zend/tests/enum/gh8418.phpt
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
--TEST--
|
||||||
|
GH-8418: Enum constant expression evaluation doesn't work with warmed cache
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class ExampleClass
|
||||||
|
{
|
||||||
|
public const EXAMPLE_CONST = 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ExampleEnum: int
|
||||||
|
{
|
||||||
|
case ENUM_CASE = ExampleClass::EXAMPLE_CONST;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(ExampleEnum::ENUM_CASE->value);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(42)
|
|
@ -9,4 +9,4 @@ enum Foo {
|
||||||
|
|
||||||
?>
|
?>
|
||||||
--EXPECTF--
|
--EXPECTF--
|
||||||
Fatal error: Case Bar of non-backed enum Foo must not have a value, try adding ": int" to the enum declaration in %s on line %d
|
Fatal error: Case Bar of non-backed enum Foo must not have a value in %s on line %d
|
||||||
|
|
|
@ -9,4 +9,4 @@ enum Foo {
|
||||||
|
|
||||||
?>
|
?>
|
||||||
--EXPECTF--
|
--EXPECTF--
|
||||||
Fatal error: Case Bar of non-backed enum Foo must not have a value, try adding ": int" to the enum declaration in %s on line %d
|
Fatal error: Case Bar of non-backed enum Foo must not have a value in %s on line %d
|
||||||
|
|
|
@ -9,4 +9,4 @@ enum Foo {
|
||||||
|
|
||||||
?>
|
?>
|
||||||
--EXPECTF--
|
--EXPECTF--
|
||||||
Fatal error: Case Bar of non-backed enum Foo must not have a value, try adding ": string" to the enum declaration in %s on line %d
|
Fatal error: Case Bar of non-backed enum Foo must not have a value in %s on line %d
|
||||||
|
|
17
Zend/tests/enum/update-class-constant-failure.phpt
Normal file
17
Zend/tests/enum/update-class-constant-failure.phpt
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
--TEST--
|
||||||
|
Test failure of updating class constants
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
enum Foo: string {
|
||||||
|
const Bar = NONEXISTENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(Foo::Bar);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Uncaught Error: Undefined constant "NONEXISTENT" in %s:%d
|
||||||
|
Stack trace:
|
||||||
|
#0 {main}
|
||||||
|
thrown in %s on line %d
|
|
@ -30,6 +30,7 @@
|
||||||
#include "zend_closures.h"
|
#include "zend_closures.h"
|
||||||
#include "zend_inheritance.h"
|
#include "zend_inheritance.h"
|
||||||
#include "zend_ini.h"
|
#include "zend_ini.h"
|
||||||
|
#include "zend_enum.h"
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
@ -1493,6 +1494,12 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (class_type->type == ZEND_USER_CLASS && class_type->ce_flags & ZEND_ACC_ENUM && class_type->enum_backing_type != IS_UNDEF) {
|
||||||
|
if (zend_enum_build_backed_enum_table(class_type) == FAILURE) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
|
ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
|
||||||
ce_flags &= ~ZEND_ACC_HAS_AST_CONSTANTS;
|
ce_flags &= ~ZEND_ACC_HAS_AST_CONSTANTS;
|
||||||
ce_flags &= ~ZEND_ACC_HAS_AST_PROPERTIES;
|
ce_flags &= ~ZEND_ACC_HAS_AST_PROPERTIES;
|
||||||
|
|
|
@ -776,12 +776,18 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast
|
||||||
zend_string *case_name = zend_ast_get_str(case_name_ast);
|
zend_string *case_name = zend_ast_get_str(case_name_ast);
|
||||||
|
|
||||||
zend_ast *case_value_ast = ast->child[2];
|
zend_ast *case_value_ast = ast->child[2];
|
||||||
zval *case_value_zv = case_value_ast != NULL
|
|
||||||
? zend_ast_get_zval(case_value_ast)
|
zval case_value_zv;
|
||||||
: NULL;
|
ZVAL_UNDEF(&case_value_zv);
|
||||||
|
if (case_value_ast != NULL) {
|
||||||
|
if (UNEXPECTED(zend_ast_evaluate(&case_value_zv, case_value_ast, scope) != SUCCESS)) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
zend_class_entry *ce = zend_lookup_class(class_name);
|
zend_class_entry *ce = zend_lookup_class(class_name);
|
||||||
zend_enum_new(result, ce, case_name, case_value_zv);
|
zend_enum_new(result, ce, case_name, case_value_ast != NULL ? &case_value_zv : NULL);
|
||||||
|
zval_ptr_dtor_nogc(&case_value_zv);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ZEND_AST_CLASS_CONST:
|
case ZEND_AST_CLASS_CONST:
|
||||||
|
|
|
@ -7578,9 +7578,6 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_
|
||||||
ce->enum_backing_type = IS_STRING;
|
ce->enum_backing_type = IS_STRING;
|
||||||
}
|
}
|
||||||
zend_type_release(type, 0);
|
zend_type_release(type, 0);
|
||||||
|
|
||||||
ce->backed_enum_table = emalloc(sizeof(HashTable));
|
|
||||||
zend_hash_init(ce->backed_enum_table, 0, NULL, ZVAL_PTR_DTOR, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */
|
static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */
|
||||||
|
@ -7792,69 +7789,20 @@ static void zend_compile_enum_case(zend_ast *ast)
|
||||||
ZVAL_STR_COPY(&case_name_zval, enum_case_name);
|
ZVAL_STR_COPY(&case_name_zval, enum_case_name);
|
||||||
zend_ast *case_name_ast = zend_ast_create_zval(&case_name_zval);
|
zend_ast *case_name_ast = zend_ast_create_zval(&case_name_zval);
|
||||||
|
|
||||||
zend_ast *case_value_zval_ast = NULL;
|
|
||||||
zend_ast *case_value_ast = ast->child[1];
|
zend_ast *case_value_ast = ast->child[1];
|
||||||
|
// Remove case_value_ast from the original AST to avoid freeing it, as it will be freed by zend_const_expr_to_zval
|
||||||
|
ast->child[1] = NULL;
|
||||||
if (enum_class->enum_backing_type != IS_UNDEF && case_value_ast == NULL) {
|
if (enum_class->enum_backing_type != IS_UNDEF && case_value_ast == NULL) {
|
||||||
zend_error_noreturn(E_COMPILE_ERROR, "Case %s of backed enum %s must have a value",
|
zend_error_noreturn(E_COMPILE_ERROR, "Case %s of backed enum %s must have a value",
|
||||||
ZSTR_VAL(enum_case_name),
|
ZSTR_VAL(enum_case_name),
|
||||||
ZSTR_VAL(enum_class_name));
|
ZSTR_VAL(enum_class_name));
|
||||||
}
|
} else if (enum_class->enum_backing_type == IS_UNDEF && case_value_ast != NULL) {
|
||||||
if (case_value_ast != NULL) {
|
zend_error_noreturn(E_COMPILE_ERROR, "Case %s of non-backed enum %s must not have a value",
|
||||||
zend_eval_const_expr(&ast->child[1]);
|
ZSTR_VAL(enum_case_name),
|
||||||
case_value_ast = ast->child[1];
|
ZSTR_VAL(enum_class_name));
|
||||||
if (case_value_ast->kind != ZEND_AST_ZVAL) {
|
|
||||||
zend_error_noreturn(
|
|
||||||
E_COMPILE_ERROR, "Enum case value must be compile-time evaluatable");
|
|
||||||
}
|
|
||||||
|
|
||||||
zval case_value_zv;
|
|
||||||
ZVAL_COPY(&case_value_zv, zend_ast_get_zval(case_value_ast));
|
|
||||||
if (enum_class->enum_backing_type == IS_UNDEF) {
|
|
||||||
if (Z_TYPE(case_value_zv) == IS_LONG || Z_TYPE(case_value_zv) == IS_STRING) {
|
|
||||||
zend_error_noreturn(E_COMPILE_ERROR, "Case %s of non-backed enum %s must not have a value, try adding \": %s\" to the enum declaration",
|
|
||||||
ZSTR_VAL(enum_case_name),
|
|
||||||
ZSTR_VAL(enum_class_name),
|
|
||||||
zend_zval_type_name(&case_value_zv));
|
|
||||||
} else {
|
|
||||||
zend_error_noreturn(E_COMPILE_ERROR, "Case %s of non-backed enum %s must not have a value",
|
|
||||||
ZSTR_VAL(enum_case_name),
|
|
||||||
ZSTR_VAL(enum_class_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enum_class->enum_backing_type != Z_TYPE(case_value_zv)) {
|
|
||||||
zend_error_noreturn(E_COMPILE_ERROR, "Enum case type %s does not match enum backing type %s",
|
|
||||||
zend_get_type_by_const(Z_TYPE(case_value_zv)),
|
|
||||||
zend_get_type_by_const(enum_class->enum_backing_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
case_value_zval_ast = zend_ast_create_zval(&case_value_zv);
|
|
||||||
Z_TRY_ADDREF(case_name_zval);
|
|
||||||
if (enum_class->enum_backing_type == IS_LONG) {
|
|
||||||
zend_long long_key = Z_LVAL(case_value_zv);
|
|
||||||
zval *existing_case_name = zend_hash_index_find(enum_class->backed_enum_table, long_key);
|
|
||||||
if (existing_case_name) {
|
|
||||||
zend_error_noreturn(E_COMPILE_ERROR, "Duplicate value in enum %s for cases %s and %s",
|
|
||||||
ZSTR_VAL(enum_class_name),
|
|
||||||
Z_STRVAL_P(existing_case_name),
|
|
||||||
ZSTR_VAL(enum_case_name));
|
|
||||||
}
|
|
||||||
zend_hash_index_add_new(enum_class->backed_enum_table, long_key, &case_name_zval);
|
|
||||||
} else {
|
|
||||||
ZEND_ASSERT(enum_class->enum_backing_type == IS_STRING);
|
|
||||||
zend_string *string_key = Z_STR(case_value_zv);
|
|
||||||
zval *existing_case_name = zend_hash_find(enum_class->backed_enum_table, string_key);
|
|
||||||
if (existing_case_name != NULL) {
|
|
||||||
zend_error_noreturn(E_COMPILE_ERROR, "Duplicate value in enum %s for cases %s and %s",
|
|
||||||
ZSTR_VAL(enum_class_name),
|
|
||||||
Z_STRVAL_P(existing_case_name),
|
|
||||||
ZSTR_VAL(enum_case_name));
|
|
||||||
}
|
|
||||||
zend_hash_add_new(enum_class->backed_enum_table, string_key, &case_name_zval);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
zend_ast *const_enum_init_ast = zend_ast_create(ZEND_AST_CONST_ENUM_INIT, class_name_ast, case_name_ast, case_value_zval_ast);
|
zend_ast *const_enum_init_ast = zend_ast_create(ZEND_AST_CONST_ENUM_INIT, class_name_ast, case_name_ast, case_value_ast);
|
||||||
|
|
||||||
zval value_zv;
|
zval value_zv;
|
||||||
zend_const_expr_to_zval(&value_zv, &const_enum_init_ast, /* allow_dynamic */ false);
|
zend_const_expr_to_zval(&value_zv, &const_enum_init_ast, /* allow_dynamic */ false);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "zend_compile.h"
|
#include "zend_compile.h"
|
||||||
#include "zend_enum_arginfo.h"
|
#include "zend_enum_arginfo.h"
|
||||||
#include "zend_interfaces.h"
|
#include "zend_interfaces.h"
|
||||||
|
#include "zend_enum.h"
|
||||||
|
|
||||||
#define ZEND_ENUM_DISALLOW_MAGIC_METHOD(propertyName, methodName) \
|
#define ZEND_ENUM_DISALLOW_MAGIC_METHOD(propertyName, methodName) \
|
||||||
do { \
|
do { \
|
||||||
|
@ -183,6 +184,72 @@ void zend_enum_add_interfaces(zend_class_entry *ce)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zend_result zend_enum_build_backed_enum_table(zend_class_entry *ce)
|
||||||
|
{
|
||||||
|
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_ENUM);
|
||||||
|
ZEND_ASSERT(ce->type == ZEND_USER_CLASS);
|
||||||
|
|
||||||
|
uint32_t backing_type = ce->enum_backing_type;
|
||||||
|
ZEND_ASSERT(backing_type != IS_UNDEF);
|
||||||
|
|
||||||
|
ce->backed_enum_table = emalloc(sizeof(HashTable));
|
||||||
|
zend_hash_init(ce->backed_enum_table, 0, NULL, ZVAL_PTR_DTOR, 0);
|
||||||
|
|
||||||
|
zend_string *enum_class_name = ce->name;
|
||||||
|
|
||||||
|
zend_string *name;
|
||||||
|
zval *val;
|
||||||
|
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(&ce->constants_table, name, val) {
|
||||||
|
zend_class_constant *c = Z_PTR_P(val);
|
||||||
|
if ((ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
zval *c_value = &c->value;
|
||||||
|
zval *case_name = zend_enum_fetch_case_name(Z_OBJ_P(c_value));
|
||||||
|
zval *case_value = zend_enum_fetch_case_value(Z_OBJ_P(c_value));
|
||||||
|
|
||||||
|
if (ce->enum_backing_type != Z_TYPE_P(case_value)) {
|
||||||
|
zend_type_error("Enum case type %s does not match enum backing type %s",
|
||||||
|
zend_get_type_by_const(Z_TYPE_P(case_value)),
|
||||||
|
zend_get_type_by_const(ce->enum_backing_type));
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ce->enum_backing_type == IS_LONG) {
|
||||||
|
zend_long long_key = Z_LVAL_P(case_value);
|
||||||
|
zval *existing_case_name = zend_hash_index_find(ce->backed_enum_table, long_key);
|
||||||
|
if (existing_case_name) {
|
||||||
|
zend_throw_error(NULL, "Duplicate value in enum %s for cases %s and %s",
|
||||||
|
ZSTR_VAL(enum_class_name),
|
||||||
|
Z_STRVAL_P(existing_case_name),
|
||||||
|
ZSTR_VAL(name));
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
zend_hash_index_add_new(ce->backed_enum_table, long_key, case_name);
|
||||||
|
} else {
|
||||||
|
ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
|
||||||
|
zend_string *string_key = Z_STR_P(case_value);
|
||||||
|
zval *existing_case_name = zend_hash_find(ce->backed_enum_table, string_key);
|
||||||
|
if (existing_case_name != NULL) {
|
||||||
|
zend_throw_error(NULL, "Duplicate value in enum %s for cases %s and %s",
|
||||||
|
ZSTR_VAL(enum_class_name),
|
||||||
|
Z_STRVAL_P(existing_case_name),
|
||||||
|
ZSTR_VAL(name));
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
zend_hash_add_new(ce->backed_enum_table, string_key, case_name);
|
||||||
|
}
|
||||||
|
} ZEND_HASH_FOREACH_END();
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
zend_hash_release(ce->backed_enum_table);
|
||||||
|
ce->backed_enum_table = NULL;
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
static ZEND_NAMED_FUNCTION(zend_enum_cases_func)
|
static ZEND_NAMED_FUNCTION(zend_enum_cases_func)
|
||||||
{
|
{
|
||||||
zend_class_entry *ce = execute_data->func->common.scope;
|
zend_class_entry *ce = execute_data->func->common.scope;
|
||||||
|
@ -209,6 +276,12 @@ static ZEND_NAMED_FUNCTION(zend_enum_cases_func)
|
||||||
|
|
||||||
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)
|
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)
|
||||||
{
|
{
|
||||||
|
if (ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
|
||||||
|
if (zend_update_class_constants(ce) == FAILURE) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
zval *case_name_zv;
|
zval *case_name_zv;
|
||||||
if (ce->enum_backing_type == IS_LONG) {
|
if (ce->enum_backing_type == IS_LONG) {
|
||||||
case_name_zv = zend_hash_index_find(ce->backed_enum_table, long_key);
|
case_name_zv = zend_hash_index_find(ce->backed_enum_table, long_key);
|
||||||
|
|
|
@ -29,6 +29,7 @@ extern ZEND_API zend_class_entry *zend_ce_backed_enum;
|
||||||
|
|
||||||
void zend_register_enum_ce(void);
|
void zend_register_enum_ce(void);
|
||||||
void zend_enum_add_interfaces(zend_class_entry *ce);
|
void zend_enum_add_interfaces(zend_class_entry *ce);
|
||||||
|
zend_result zend_enum_build_backed_enum_table(zend_class_entry *ce);
|
||||||
zend_object *zend_enum_new(zval *result, zend_class_entry *ce, zend_string *case_name, zval *backing_value_zv);
|
zend_object *zend_enum_new(zval *result, zend_class_entry *ce, zend_string *case_name, zval *backing_value_zv);
|
||||||
void zend_verify_enum(zend_class_entry *ce);
|
void zend_verify_enum(zend_class_entry *ce);
|
||||||
void zend_enum_register_funcs(zend_class_entry *ce);
|
void zend_enum_register_funcs(zend_class_entry *ce);
|
||||||
|
|
|
@ -330,9 +330,6 @@ ZEND_API void destroy_zend_class(zval *zv)
|
||||||
if (ce->attributes) {
|
if (ce->attributes) {
|
||||||
zend_hash_release(ce->attributes);
|
zend_hash_release(ce->attributes);
|
||||||
}
|
}
|
||||||
if (ce->backed_enum_table) {
|
|
||||||
zend_hash_release(ce->backed_enum_table);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ce->num_interfaces > 0 && !(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
|
if (ce->num_interfaces > 0 && !(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
@ -403,6 +400,9 @@ ZEND_API void destroy_zend_class(zval *zv)
|
||||||
if (ce->num_interfaces > 0 && (ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
|
if (ce->num_interfaces > 0 && (ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
|
||||||
efree(ce->interfaces);
|
efree(ce->interfaces);
|
||||||
}
|
}
|
||||||
|
if (ce->backed_enum_table) {
|
||||||
|
zend_hash_release(ce->backed_enum_table);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ZEND_INTERNAL_CLASS:
|
case ZEND_INTERNAL_CLASS:
|
||||||
if (ce->backed_enum_table) {
|
if (ce->backed_enum_table) {
|
||||||
|
|
|
@ -5896,6 +5896,13 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO
|
||||||
HANDLE_EXCEPTION();
|
HANDLE_EXCEPTION();
|
||||||
}
|
}
|
||||||
value = &c->value;
|
value = &c->value;
|
||||||
|
// Enums require loading of all class constants to build the backed enum table
|
||||||
|
if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
|
||||||
|
if (UNEXPECTED(zend_update_class_constants(ce) == FAILURE)) {
|
||||||
|
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||||
|
HANDLE_EXCEPTION();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(value, c->ce);
|
zval_update_constant_ex(value, c->ce);
|
||||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
if (UNEXPECTED(EG(exception) != NULL)) {
|
||||||
|
|
21
Zend/zend_vm_execute.h
generated
21
Zend/zend_vm_execute.h
generated
|
@ -7069,6 +7069,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS
|
||||||
HANDLE_EXCEPTION();
|
HANDLE_EXCEPTION();
|
||||||
}
|
}
|
||||||
value = &c->value;
|
value = &c->value;
|
||||||
|
// Enums require loading of all class constants to build the backed enum table
|
||||||
|
if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
|
||||||
|
if (UNEXPECTED(zend_update_class_constants(ce) == FAILURE)) {
|
||||||
|
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||||
|
HANDLE_EXCEPTION();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(value, c->ce);
|
zval_update_constant_ex(value, c->ce);
|
||||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
if (UNEXPECTED(EG(exception) != NULL)) {
|
||||||
|
@ -24622,6 +24629,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_
|
||||||
HANDLE_EXCEPTION();
|
HANDLE_EXCEPTION();
|
||||||
}
|
}
|
||||||
value = &c->value;
|
value = &c->value;
|
||||||
|
// Enums require loading of all class constants to build the backed enum table
|
||||||
|
if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
|
||||||
|
if (UNEXPECTED(zend_update_class_constants(ce) == FAILURE)) {
|
||||||
|
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||||
|
HANDLE_EXCEPTION();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(value, c->ce);
|
zval_update_constant_ex(value, c->ce);
|
||||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
if (UNEXPECTED(EG(exception) != NULL)) {
|
||||||
|
@ -33460,6 +33474,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS
|
||||||
HANDLE_EXCEPTION();
|
HANDLE_EXCEPTION();
|
||||||
}
|
}
|
||||||
value = &c->value;
|
value = &c->value;
|
||||||
|
// Enums require loading of all class constants to build the backed enum table
|
||||||
|
if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
|
||||||
|
if (UNEXPECTED(zend_update_class_constants(ce) == FAILURE)) {
|
||||||
|
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||||
|
HANDLE_EXCEPTION();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(value, c->ce);
|
zval_update_constant_ex(value, c->ce);
|
||||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
if (UNEXPECTED(EG(exception) != NULL)) {
|
||||||
|
|
|
@ -848,14 +848,6 @@ static void zend_file_cache_serialize_class(zval *zv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ce->backed_enum_table) {
|
|
||||||
HashTable *ht;
|
|
||||||
SERIALIZE_PTR(ce->backed_enum_table);
|
|
||||||
ht = ce->backed_enum_table;
|
|
||||||
UNSERIALIZE_PTR(ht);
|
|
||||||
zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
|
|
||||||
}
|
|
||||||
|
|
||||||
SERIALIZE_PTR(ce->constructor);
|
SERIALIZE_PTR(ce->constructor);
|
||||||
SERIALIZE_PTR(ce->destructor);
|
SERIALIZE_PTR(ce->destructor);
|
||||||
SERIALIZE_PTR(ce->clone);
|
SERIALIZE_PTR(ce->clone);
|
||||||
|
@ -1645,12 +1637,6 @@ static void zend_file_cache_unserialize_class(zval *zv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ce->backed_enum_table) {
|
|
||||||
UNSERIALIZE_PTR(ce->backed_enum_table);
|
|
||||||
zend_file_cache_unserialize_hash(
|
|
||||||
ce->backed_enum_table, script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
UNSERIALIZE_PTR(ce->constructor);
|
UNSERIALIZE_PTR(ce->constructor);
|
||||||
UNSERIALIZE_PTR(ce->destructor);
|
UNSERIALIZE_PTR(ce->destructor);
|
||||||
UNSERIALIZE_PTR(ce->clone);
|
UNSERIALIZE_PTR(ce->clone);
|
||||||
|
|
|
@ -336,35 +336,6 @@ uint32_t zend_accel_get_class_name_map_ptr(zend_string *type_name)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HashTable *zend_persist_backed_enum_table(HashTable *backed_enum_table)
|
|
||||||
{
|
|
||||||
HashTable *ptr;
|
|
||||||
zend_hash_persist(backed_enum_table);
|
|
||||||
|
|
||||||
if (HT_IS_PACKED(backed_enum_table)) {
|
|
||||||
zval *zv;
|
|
||||||
|
|
||||||
ZEND_HASH_PACKED_FOREACH_VAL(backed_enum_table, zv) {
|
|
||||||
zend_persist_zval(zv);
|
|
||||||
} ZEND_HASH_FOREACH_END();
|
|
||||||
} else {
|
|
||||||
Bucket *p;
|
|
||||||
|
|
||||||
ZEND_HASH_MAP_FOREACH_BUCKET(backed_enum_table, p) {
|
|
||||||
if (p->key != NULL) {
|
|
||||||
zend_accel_store_interned_string(p->key);
|
|
||||||
}
|
|
||||||
zend_persist_zval(&p->val);
|
|
||||||
} ZEND_HASH_FOREACH_END();
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr = zend_shared_memdup_free(backed_enum_table, sizeof(HashTable));
|
|
||||||
GC_SET_REFCOUNT(ptr, 2);
|
|
||||||
GC_TYPE_INFO(ptr) = GC_ARRAY | ((IS_ARRAY_IMMUTABLE|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT);
|
|
||||||
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void zend_persist_type(zend_type *type) {
|
static void zend_persist_type(zend_type *type) {
|
||||||
if (ZEND_TYPE_HAS_LIST(*type)) {
|
if (ZEND_TYPE_HAS_LIST(*type)) {
|
||||||
zend_type_list *list = ZEND_TYPE_LIST(*type);
|
zend_type_list *list = ZEND_TYPE_LIST(*type);
|
||||||
|
@ -1072,9 +1043,7 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ce->backed_enum_table) {
|
ZEND_ASSERT(ce->backed_enum_table == NULL);
|
||||||
ce->backed_enum_table = zend_persist_backed_enum_table(ce->backed_enum_table);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ce;
|
return ce;
|
||||||
|
|
|
@ -547,27 +547,6 @@ void zend_persist_class_entry_calc(zend_class_entry *ce)
|
||||||
ADD_SIZE(sizeof(zend_trait_precedence*) * (i + 1));
|
ADD_SIZE(sizeof(zend_trait_precedence*) * (i + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ce->backed_enum_table) {
|
|
||||||
ADD_SIZE(sizeof(HashTable));
|
|
||||||
zend_hash_persist_calc(ce->backed_enum_table);
|
|
||||||
if (HT_IS_PACKED(ce->backed_enum_table)) {
|
|
||||||
zval *zv;
|
|
||||||
|
|
||||||
ZEND_HASH_PACKED_FOREACH_VAL(ce->backed_enum_table, zv) {
|
|
||||||
zend_persist_zval_calc(zv);
|
|
||||||
} ZEND_HASH_FOREACH_END();
|
|
||||||
} else {
|
|
||||||
Bucket *p;
|
|
||||||
|
|
||||||
ZEND_HASH_MAP_FOREACH_BUCKET(ce->backed_enum_table, p) {
|
|
||||||
if (p->key != NULL) {
|
|
||||||
ADD_INTERNED_STRING(p->key);
|
|
||||||
}
|
|
||||||
zend_persist_zval_calc(&p->val);
|
|
||||||
} ZEND_HASH_FOREACH_END();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1485,7 +1485,7 @@ static void reflection_enum_case_factory(zend_class_entry *ce, zend_string *name
|
||||||
{
|
{
|
||||||
reflection_object *intern;
|
reflection_object *intern;
|
||||||
|
|
||||||
zend_class_entry *case_reflection_class = ce->backed_enum_table == IS_UNDEF
|
zend_class_entry *case_reflection_class = ce->enum_backing_type == IS_UNDEF
|
||||||
? reflection_enum_unit_case_ptr
|
? reflection_enum_unit_case_ptr
|
||||||
: reflection_enum_backed_case_ptr;
|
: reflection_enum_backed_case_ptr;
|
||||||
reflection_instantiate(case_reflection_class, object);
|
reflection_instantiate(case_reflection_class, object);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue