Skip to content

Commit

Permalink
Add syntax for optional interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
tontonsb committed Dec 28, 2024
1 parent 65524e5 commit 660ce19
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 16 deletions.
8 changes: 7 additions & 1 deletion Zend/zend.h
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ typedef struct _zend_class_name {
zend_string *lc_name;
} zend_class_name;

typedef struct _zend_interface_name {
zend_string *name;
zend_string *lc_name;
bool is_optional;
} zend_interface_name;

typedef struct _zend_trait_method_reference {
zend_string *method_name;
zend_string *class_name;
Expand Down Expand Up @@ -210,7 +216,7 @@ struct _zend_class_entry {
/* class_entry or string(s) depending on ZEND_ACC_LINKED */
union {
zend_class_entry **interfaces;
zend_class_name *interface_names;
zend_interface_name *interface_names;
};

zend_class_name *trait_names;
Expand Down
8 changes: 5 additions & 3 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -8039,12 +8039,13 @@ static void add_stringable_interface(zend_class_entry *ce) {

ce->num_interfaces++;
ce->interface_names =
erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
erealloc(ce->interface_names, sizeof(zend_interface_name) * ce->num_interfaces);
// TODO: Add known interned strings instead?
ce->interface_names[ce->num_interfaces - 1].name =
ZSTR_INIT_LITERAL("Stringable", 0);
ce->interface_names[ce->num_interfaces - 1].lc_name =
ZSTR_INIT_LITERAL("stringable", 0);
ce->interface_names[ce->num_interfaces - 1].is_optional = false;
}

static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name, bool has_body) /* {{{ */
Expand Down Expand Up @@ -8950,16 +8951,17 @@ static void zend_compile_implements(zend_ast *ast) /* {{{ */
{
zend_ast_list *list = zend_ast_get_list(ast);
zend_class_entry *ce = CG(active_class_entry);
zend_class_name *interface_names;
zend_interface_name *interface_names;
uint32_t i;

interface_names = emalloc(sizeof(zend_class_name) * list->children);
interface_names = emalloc(sizeof(zend_interface_name) * list->children);

for (i = 0; i < list->children; ++i) {
zend_ast *class_ast = list->child[i];
interface_names[i].name =
zend_resolve_const_class_name_reference(class_ast, "interface name");
interface_names[i].lc_name = zend_string_tolower(interface_names[i].name);
interface_names[i].is_optional = ZEND_CLASS_NAME_OPTIONAL & class_ast->attr;
}

ce->num_interfaces = list->children;
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
#define ZEND_NAME_NOT_FQ 1
#define ZEND_NAME_RELATIVE 2

#define ZEND_CLASS_NAME_OPTIONAL 4

/* ZEND_FETCH_ flags in class name AST of new const expression must not clash with ZEND_NAME_ flags */
#define ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT 2

Expand Down
4 changes: 3 additions & 1 deletion Zend/zend_enum.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,16 @@ void zend_enum_add_interfaces(zend_class_entry *ce)

ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES));

ce->interface_names = erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
ce->interface_names = erealloc(ce->interface_names, sizeof(zend_interface_name) * ce->num_interfaces);

ce->interface_names[num_interfaces_before].name = zend_string_copy(zend_ce_unit_enum->name);
ce->interface_names[num_interfaces_before].lc_name = ZSTR_INIT_LITERAL("unitenum", 0);
ce->interface_names[num_interfaces_before].is_optional = false;

if (ce->enum_backing_type != IS_UNDEF) {
ce->interface_names[num_interfaces_before + 1].name = zend_string_copy(zend_ce_backed_enum->name);
ce->interface_names[num_interfaces_before + 1].lc_name = ZSTR_INIT_LITERAL("backedenum", 0);
ce->interface_names[num_interfaces_before + 1].is_optional = false;
}

ce->default_object_handlers = &zend_enum_object_handlers;
Expand Down
13 changes: 11 additions & 2 deletions Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -3513,23 +3513,32 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
}
}

size_t num_implementable_interfaces = 0;
if (ce->num_interfaces) {
for (i = 0; i < ce->num_interfaces; i++) {
zend_class_entry *iface = zend_fetch_class_by_name(
ce->interface_names[i].name, ce->interface_names[i].lc_name,
ZEND_FETCH_CLASS_INTERFACE |
ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION);
ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION |
(ce->interface_names[i].is_optional ? ZEND_FETCH_CLASS_SILENT : 0));

// Optional interfaces are skipped if they don't exist.
if (!iface && ce->interface_names[i].is_optional) {
continue;
}

if (!iface) {
check_unrecoverable_load_failure(ce);
free_alloca(traits_and_interfaces, use_heap);
return NULL;
}
traits_and_interfaces[ce->num_traits + i] = iface;
traits_and_interfaces[ce->num_traits + num_implementable_interfaces++] = iface;
if (iface) {
UPDATE_IS_CACHEABLE(iface);
}
}
}
ce->num_interfaces = num_implementable_interfaces;

#ifndef ZEND_WIN32
if (ce->ce_flags & ZEND_ACC_ENUM) {
Expand Down
23 changes: 17 additions & 6 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> function_name non_empty_member_modifiers
%type <ast> property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body
%type <ast> optional_parameter_list
%type <ast> interface_name_list interface_name

%type <num> returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers
%type <num> method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers
Expand Down Expand Up @@ -355,10 +356,10 @@ legacy_namespace_name:
;

name:
T_STRING { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; }
| T_NAME_QUALIFIED { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; }
| T_NAME_FULLY_QUALIFIED { $$ = $1; $$->attr = ZEND_NAME_FQ; }
| T_NAME_RELATIVE { $$ = $1; $$->attr = ZEND_NAME_RELATIVE; }
T_STRING { $$ = $1; $$->attr |= ZEND_NAME_NOT_FQ; }
| T_NAME_QUALIFIED { $$ = $1; $$->attr |= ZEND_NAME_NOT_FQ; }
| T_NAME_FULLY_QUALIFIED { $$ = $1; $$->attr |= ZEND_NAME_FQ; }
| T_NAME_RELATIVE { $$ = $1; $$->attr |= ZEND_NAME_RELATIVE; }
;

attribute_decl:
Expand Down Expand Up @@ -666,12 +667,12 @@ extends_from:

interface_extends_list:
%empty { $$ = NULL; }
| T_EXTENDS class_name_list { $$ = $2; }
| T_EXTENDS interface_name_list { $$ = $2; }
;

implements_list:
%empty { $$ = NULL; }
| T_IMPLEMENTS class_name_list { $$ = $2; }
| T_IMPLEMENTS interface_name_list { $$ = $2; }
;

foreach_variable:
Expand Down Expand Up @@ -974,6 +975,11 @@ class_name_list:
| class_name_list ',' class_name { $$ = zend_ast_list_add($1, $3); }
;

interface_name_list:
interface_name { $$ = zend_ast_create_list(1, ZEND_AST_NAME_LIST, $1); }
| interface_name_list ',' interface_name { $$ = zend_ast_list_add($1, $3); }
;

trait_adaptations:
';' { $$ = NULL; }
| '{' '}' { $$ = NULL; }
Expand Down Expand Up @@ -1411,6 +1417,11 @@ class_name:
| name { $$ = $1; }
;

interface_name:
class_name { $$ = $1; }
| '?' name { $$ = $2; $$->attr |= ZEND_CLASS_NAME_OPTIONAL; }
;

class_name_reference:
class_name { $$ = $1; }
| new_variable { $$ = $1; }
Expand Down
3 changes: 2 additions & 1 deletion ext/opcache/zend_file_cache.c
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@ static void zend_file_cache_serialize_class(zval *zv,

if (ce->num_interfaces) {
uint32_t i;
zend_class_name *interface_names;
zend_interface_name *interface_names;

ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED));

Expand All @@ -803,6 +803,7 @@ static void zend_file_cache_serialize_class(zval *zv,
for (i = 0; i < ce->num_interfaces; i++) {
SERIALIZE_STR(interface_names[i].name);
SERIALIZE_STR(interface_names[i].lc_name);
// SERIALIZE_BOOL(interface_names[i].is_optional) ?
}
}

Expand Down
3 changes: 2 additions & 1 deletion ext/opcache/zend_persist.c
Original file line number Diff line number Diff line change
Expand Up @@ -1048,8 +1048,9 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce)
for (i = 0; i < ce->num_interfaces; i++) {
zend_accel_store_interned_string(ce->interface_names[i].name);
zend_accel_store_interned_string(ce->interface_names[i].lc_name);
// TODO: should we store .is_optional here? How?
}
ce->interface_names = zend_shared_memdup_free(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
ce->interface_names = zend_shared_memdup_free(ce->interface_names, sizeof(zend_interface_name) * ce->num_interfaces);
}

if (ce->num_traits) {
Expand Down
2 changes: 1 addition & 1 deletion ext/opcache/zend_persist_calc.c
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ void zend_persist_class_entry_calc(zend_class_entry *ce)
ADD_INTERNED_STRING(ce->interface_names[i].name);
ADD_INTERNED_STRING(ce->interface_names[i].lc_name);
}
ADD_SIZE(sizeof(zend_class_name) * ce->num_interfaces);
ADD_SIZE(sizeof(zend_interface_name) * ce->num_interfaces);
}
}

Expand Down

0 comments on commit 660ce19

Please sign in to comment.