Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

# Collapse generated files within git and pull request diff.
**/*_arginfo.h linguist-generated -diff
**/*_decl.h linguist-generated -diff
/main/debug_gdb_scripts.c linguist-generated -diff
/Zend/zend_vm_execute.h linguist-generated -diff
/Zend/zend_vm_handlers.h linguist-generated -diff
Expand Down
7 changes: 7 additions & 0 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -2009,6 +2009,13 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string
#define Z_PARAM_OBJ_OF_CLASS_OR_LONG_OR_NULL(dest_obj, _ce, dest_long, is_null) \
Z_PARAM_OBJ_OF_CLASS_OR_LONG_EX(dest_obj, _ce, dest_long, is_null, 1)

#define Z_PARAM_ENUM(dest, _ce) \
{ \
zend_object *_tmp = NULL; \
Z_PARAM_OBJ_OF_CLASS(_tmp, _ce); \
dest = zend_enum_fetch_case_id(_tmp); \
}

/* old "p" */
#define Z_PARAM_PATH_EX(dest, dest_len, check_null, deref) \
Z_PARAM_PROLOGUE(deref, 0); \
Expand Down
9 changes: 6 additions & 3 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -995,10 +995,13 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
zend_ast *class_name_ast = ast->child[0];
zend_string *class_name = zend_ast_get_str(class_name_ast);

zend_ast *case_name_ast = ast->child[1];
zend_ast *case_id_ast = ast->child[1];
int case_id = (int)Z_LVAL_P(zend_ast_get_zval(case_id_ast));

zend_ast *case_name_ast = ast->child[2];
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[3];

zval case_value_zv;
ZVAL_UNDEF(&case_value_zv);
Expand All @@ -1009,7 +1012,7 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
}

zend_class_entry *ce = zend_lookup_class(class_name);
zend_enum_new(result, ce, case_name, case_value_ast != NULL ? &case_value_zv : NULL);
zend_enum_new(result, ce, case_id, case_name, case_value_ast != NULL ? &case_value_zv : NULL);
zval_ptr_dtor_nogc(&case_value_zv);
break;
}
Expand Down
6 changes: 3 additions & 3 deletions Zend/zend_ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,15 @@ enum _zend_ast_kind {
ZEND_AST_CONST_ELEM,
ZEND_AST_CLASS_CONST_GROUP,

// Pseudo node for initializing enums
ZEND_AST_CONST_ENUM_INIT,

/* 4 child nodes */
ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT,
ZEND_AST_FOREACH,
ZEND_AST_ENUM_CASE,
ZEND_AST_PROP_ELEM,

// Pseudo node for initializing enums
ZEND_AST_CONST_ENUM_INIT,

/* 5 child nodes */

/* 6 child nodes */
Expand Down
10 changes: 8 additions & 2 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -9565,6 +9565,11 @@ static void zend_compile_enum_case(zend_ast *ast)
ZVAL_STR_COPY(&class_name_zval, enum_class_name);
zend_ast *class_name_ast = zend_ast_create_zval(&class_name_zval);

zval case_id_zval;
int case_id = zend_enum_next_case_id(enum_class);
ZVAL_LONG(&case_id_zval, case_id);
zend_ast *case_id_ast = zend_ast_create_zval(&case_id_zval);

zval case_name_zval;
ZVAL_STR_COPY(&case_name_zval, enum_case_name);
zend_ast *case_name_ast = zend_ast_create_zval(&case_name_zval);
Expand All @@ -9582,7 +9587,8 @@ static void zend_compile_enum_case(zend_ast *ast)
ZSTR_VAL(enum_class_name));
}

zend_ast *const_enum_init_ast = zend_ast_create(ZEND_AST_CONST_ENUM_INIT, class_name_ast, case_name_ast, case_value_ast);
zend_ast *const_enum_init_ast = zend_ast_create(ZEND_AST_CONST_ENUM_INIT,
class_name_ast, case_id_ast, case_name_ast, case_value_ast);

zval value_zv;
zend_const_expr_to_zval(&value_zv, &const_enum_init_ast, /* allow_dynamic */ false);
Expand Down Expand Up @@ -12448,7 +12454,7 @@ static void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
zend_eval_const_expr(&ast->child[1]);
return;
case ZEND_AST_CONST_ENUM_INIT:
zend_eval_const_expr(&ast->child[2]);
zend_eval_const_expr(&ast->child[3]);
return;
case ZEND_AST_PROP:
case ZEND_AST_NULLSAFE_PROP:
Expand Down
65 changes: 50 additions & 15 deletions Zend/zend_enum.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,16 @@ static zend_arg_info zarginfo_class_UnitEnum_cases[sizeof(arginfo_class_UnitEnum
static zend_arg_info zarginfo_class_BackedEnum_from[sizeof(arginfo_class_BackedEnum_from)/sizeof(zend_internal_arg_info)];
static zend_arg_info zarginfo_class_BackedEnum_tryFrom[sizeof(arginfo_class_BackedEnum_tryFrom)/sizeof(zend_internal_arg_info)];

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, int case_id, zend_string *case_name, zval *backing_value_zv)
{
zend_object *zobj = zend_objects_new(ce);
zend_enum_obj *intern = zend_object_alloc(sizeof(zend_enum_obj), ce);

zend_object_std_init(&intern->std, ce);
object_properties_init(&intern->std, ce);

intern->case_id = case_id;

zend_object *zobj = &intern->std;
GC_ADD_FLAGS(zobj, GC_NOT_COLLECTABLE);
ZVAL_OBJ(result, zobj);

Expand Down Expand Up @@ -170,6 +177,7 @@ void zend_register_enum_ce(void)
zend_ce_backed_enum->interface_gets_implemented = zend_implement_backed_enum;

memcpy(&zend_enum_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
zend_enum_object_handlers.offset = XtOffsetOf(zend_enum_obj, std);
zend_enum_object_handlers.clone_obj = NULL;
zend_enum_object_handlers.compare = zend_objects_not_comparable;
}
Expand Down Expand Up @@ -539,16 +547,18 @@ ZEND_API zend_class_entry *zend_register_internal_enum(
}

static zend_ast_ref *create_enum_case_ast(
zend_string *class_name, zend_string *case_name, zval *value) {
zend_string *class_name, int case_id, zend_string *case_name,
zval *value) {
// TODO: Use custom node type for enum cases?
size_t size = sizeof(zend_ast_ref) + zend_ast_size(3)
+ (value ? 3 : 2) * sizeof(zend_ast_zval);
const size_t num_children = ZEND_AST_CONST_ENUM_INIT >> ZEND_AST_NUM_CHILDREN_SHIFT;
size_t size = sizeof(zend_ast_ref) + zend_ast_size(num_children)
+ (value ? num_children : num_children-1) * sizeof(zend_ast_zval);
char *p = pemalloc(size, 1);
zend_ast_ref *ref = (zend_ast_ref *) p; p += sizeof(zend_ast_ref);
GC_SET_REFCOUNT(ref, 1);
GC_TYPE_INFO(ref) = GC_CONSTANT_AST | GC_PERSISTENT | GC_IMMUTABLE;

zend_ast *ast = (zend_ast *) p; p += zend_ast_size(3);
zend_ast *ast = (zend_ast *) p; p += zend_ast_size(num_children);
ast->kind = ZEND_AST_CONST_ENUM_INIT;
ast->attr = 0;
ast->lineno = 0;
Expand All @@ -563,24 +573,47 @@ static zend_ast_ref *create_enum_case_ast(
ast->child[1] = (zend_ast *) p; p += sizeof(zend_ast_zval);
ast->child[1]->kind = ZEND_AST_ZVAL;
ast->child[1]->attr = 0;
ZEND_ASSERT(ZSTR_IS_INTERNED(case_name));
ZVAL_STR(zend_ast_get_zval(ast->child[1]), case_name);
ZVAL_LONG(zend_ast_get_zval(ast->child[1]), case_id);
Z_LINENO_P(zend_ast_get_zval(ast->child[1])) = 0;

ast->child[2] = (zend_ast *) p; p += sizeof(zend_ast_zval);
ast->child[2]->kind = ZEND_AST_ZVAL;
ast->child[2]->attr = 0;
ZEND_ASSERT(ZSTR_IS_INTERNED(case_name));
ZVAL_STR(zend_ast_get_zval(ast->child[2]), case_name);
Z_LINENO_P(zend_ast_get_zval(ast->child[2])) = 0;

if (value) {
ast->child[2] = (zend_ast *) p; p += sizeof(zend_ast_zval);
ast->child[2]->kind = ZEND_AST_ZVAL;
ast->child[2]->attr = 0;
ast->child[3] = (zend_ast *) p; p += sizeof(zend_ast_zval);
ast->child[3]->kind = ZEND_AST_ZVAL;
ast->child[3]->attr = 0;
ZEND_ASSERT(!Z_REFCOUNTED_P(value));
ZVAL_COPY_VALUE(zend_ast_get_zval(ast->child[2]), value);
Z_LINENO_P(zend_ast_get_zval(ast->child[2])) = 0;
ZVAL_COPY_VALUE(zend_ast_get_zval(ast->child[3]), value);
Z_LINENO_P(zend_ast_get_zval(ast->child[3])) = 0;
} else {
ast->child[2] = NULL;
ast->child[3] = NULL;
}

return ref;
}

int zend_enum_next_case_id(zend_class_entry *enum_class)
{
ZEND_HASH_REVERSE_FOREACH_VAL(&enum_class->constants_table, zval *zv) {
zend_class_constant *c = Z_PTR_P(zv);
if (!(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE)) {
continue;
}
ZEND_ASSERT(Z_TYPE(c->value) == IS_CONSTANT_AST);
zend_ast *ast = Z_ASTVAL(c->value);

ZEND_ASSERT(ast->kind == ZEND_AST_CONST_ENUM_INIT);
return Z_LVAL_P(zend_ast_get_zval(ast->child[1])) + 1;
} ZEND_HASH_FOREACH_END();

return 1;
}

ZEND_API void zend_enum_add_case(zend_class_entry *ce, zend_string *case_name, zval *value)
{
if (value) {
Expand All @@ -602,9 +635,11 @@ ZEND_API void zend_enum_add_case(zend_class_entry *ce, zend_string *case_name, z
ZEND_ASSERT(ce->enum_backing_type == IS_UNDEF);
}

int case_id = zend_enum_next_case_id(ce);

zval ast_zv;
Z_TYPE_INFO(ast_zv) = IS_CONSTANT_AST;
Z_AST(ast_zv) = create_enum_case_ast(ce->name, case_name, value);
Z_AST(ast_zv) = create_enum_case_ast(ce->name, case_id, case_name, value);
zend_class_constant *c = zend_declare_class_constant_ex(
ce, case_name, &ast_zv, ZEND_ACC_PUBLIC, NULL);
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_CLASS_CONST_IS_CASE;
Expand Down
19 changes: 18 additions & 1 deletion Zend/zend_enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,25 @@ extern ZEND_API zend_class_entry *zend_ce_unit_enum;
extern ZEND_API zend_class_entry *zend_ce_backed_enum;
extern ZEND_API zend_object_handlers zend_enum_object_handlers;

typedef struct _zend_enum_obj {
int case_id;
zend_object std;
} zend_enum_obj;

static inline zend_enum_obj *zend_enum_obj_from_obj(zend_object *zobj) {
ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM);
return (zend_enum_obj*)((char*)(zobj) - XtOffsetOf(zend_enum_obj, std));
}

void zend_enum_startup(void);
void zend_register_enum_ce(void);
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, int case_id, zend_string *case_name, zval *backing_value_zv);
void zend_verify_enum(const zend_class_entry *ce);
void zend_enum_register_funcs(zend_class_entry *ce);
void zend_enum_register_props(zend_class_entry *ce);
int zend_enum_next_case_id(zend_class_entry *enum_class);

ZEND_API zend_class_entry *zend_register_internal_enum(
const char *name, uint8_t type, const zend_function_entry *functions);
Expand All @@ -47,6 +58,12 @@ ZEND_API zend_object *zend_enum_get_case(zend_class_entry *ce, zend_string *name
ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *name);
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);

static zend_always_inline int zend_enum_fetch_case_id(zend_object *zobj)
{
ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assert is redundant with the one in zend_enum_obj_from_obj(). I also don't see how it could help with codegen.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I though about this when adding this assert, but if we consider functions as opaque APIs, we don't know that zend_enum_obj_from_obj() has redundant asserts.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is fair, but in this case it is probably reasonable to expect zend_enum_obj_from_obj() to do the verification (if necessary), since that's the purpose of the function, especially since a failed assert is always a programmer error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No particularly strong feelings either way, though.

return zend_enum_obj_from_obj(zobj)->case_id;
}

static zend_always_inline zval *zend_enum_fetch_case_name(zend_object *zobj)
{
ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM);
Expand Down
74 changes: 69 additions & 5 deletions build/gen_stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly =
$stubFilenameWithoutExtension = str_replace(".stub.php", "", $stubFile);
$arginfoFile = "{$stubFilenameWithoutExtension}_arginfo.h";
$legacyFile = "{$stubFilenameWithoutExtension}_legacy_arginfo.h";
$declFile = "{$stubFilenameWithoutExtension}_decl.h";

$stubCode = file_get_contents($stubFile);
$stubHash = sha1(str_replace("\r\n", "\n", $stubCode));
Expand Down Expand Up @@ -122,20 +123,25 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly =
return $fileInfo;
}

$arginfoCode = generateArgInfoCode(
[$arginfoCode, $declCode] = generateArgInfoCode(
basename($stubFilenameWithoutExtension),
$fileInfo,
$context->allConstInfos,
$stubHash
);
if ($context->forceRegeneration || $stubHash !== $oldStubHash) {
reportFilePutContents($arginfoFile, $arginfoCode);
if ($declCode !== '') {
reportFilePutContents($declFile, $declCode);
} else if (file_exists($declCode)) {
unlink($declCode);
}
}

if ($fileInfo->shouldGenerateLegacyArginfo()) {
$legacyFileInfo = $fileInfo->getLegacyVersion();

$arginfoCode = generateArgInfoCode(
[$arginfoCode] = generateArgInfoCode(
basename($stubFilenameWithoutExtension),
$legacyFileInfo,
$context->allConstInfos,
Expand Down Expand Up @@ -3278,7 +3284,7 @@ protected function addModifiersToFieldSynopsis(DOMDocument $doc, DOMElement $fie
}

class EnumCaseInfo {
private /* readonly */ string $name;
public /* readonly */ string $name;
private /* readonly */ ?Expr $value;

public function __construct(string $name, ?Expr $value) {
Expand Down Expand Up @@ -3661,6 +3667,38 @@ function (Name $item) {
return $code;
}

public function getCDeclarations(): string
{
if ($this->type !== "enum") {
return '';
}

$code = '';

if ($this->cond) {
$code .= "#if {$this->cond}\n";
}

$cEnumName = 'zend_enum_' . str_replace('\\', '_', $this->name->toString());

$code .= "typedef enum {$cEnumName} {\n";

$i = 1;
foreach ($this->enumCaseInfos as $case) {
$cName = 'ZEND_ENUM_' . str_replace('\\', '_', $this->name->toString()) . '_' . $case->name;
$code .= "\t{$cName} = {$i},\n";
$i++;
}

$code .= "} {$cEnumName};\n";

if ($this->cond) {
$code .= "#endif\n";
}

return $code;
}

private function getFlagsByPhpVersion(): VersionFlags
{
$php70Flags = [];
Expand Down Expand Up @@ -4515,6 +4553,19 @@ public function generateClassEntryCode(array $allConstInfos): string {

return $code;
}

public function generateClassEntryCDeclarations(): string {
$code = "";

foreach ($this->classInfos as $class) {
$cdecl = $class->getCDeclarations();
if ($cdecl !== '') {
$code .= "\n" . $cdecl;
}
}

return $code;
}
}

class DocCommentTag {
Expand Down Expand Up @@ -5150,13 +5201,14 @@ function generateCodeWithConditions(

/**
* @param array<string, ConstInfo> $allConstInfos
* @return array{string, string}
*/
function generateArgInfoCode(
string $stubFilenameWithoutExtension,
FileInfo $fileInfo,
array $allConstInfos,
string $stubHash
): string {
): array {
$code = "/* This is a generated file, edit the .stub.php file instead.\n"
. " * Stub hash: $stubHash */\n";

Expand Down Expand Up @@ -5250,7 +5302,19 @@ static function (FuncInfo $funcInfo) use ($fileInfo, &$generatedFunctionDeclarat
$code .= $fileInfo->generateClassEntryCode($allConstInfos);
}

return $code;

$declCode = $fileInfo->generateClassEntryCDeclarations();
if ($declCode !== '') {
$headerName = "ZEND_" . strtoupper($stubFilenameWithoutExtension) . "_DECL_{$stubHash}_H";
$declCode = "/* This is a generated file, edit the .stub.php file instead. */\n"
. "\n"
. "#ifndef {$headerName}\n"
. "#define {$headerName}\n"
. $declCode . "\n"
. "#endif /* {$headerName} */\n";
}

return [$code, $declCode];
}

/** @param FuncInfo[] $funcInfos */
Expand Down
Loading
Loading