From: Nikita Popov Date: Sat, 19 Jul 2014 20:39:01 +0000 (+0200) Subject: Port trait uses X-Git-Tag: POST_AST_MERGE^2~116 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=291bcfe4f1bdcc60814047f3f2e819d1d48af197;p=php Port trait uses --- diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index bb184cd9a8..dd260249a4 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -109,6 +109,13 @@ enum _zend_ast_kind { ZEND_AST_CLASS_CONST_DECL, ZEND_AST_CONST_ELEM, + + ZEND_AST_USE_TRAIT, + ZEND_AST_NAME_LIST, + ZEND_AST_TRAIT_ADAPTATIONS, + ZEND_AST_TRAIT_PRECEDENCE, + ZEND_AST_TRAIT_ALIAS, + ZEND_AST_METHOD_REFERENCE, }; typedef unsigned short zend_ast_kind; @@ -228,7 +235,6 @@ static inline zend_ast *zend_ast_create_cast(zend_uint type, zend_ast *op0) { #define AC(znode) AST_COMPILE(&znode, znode.u.ast) #define AS(znode) AST_COMPILE_STMT(znode.u.ast) -#define AZ(znode) ((znode).u.ast = AST_ZNODE(&znode)) #define AN(znode) ((znode).u.ast = NULL) #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index d19e377d68..2579329395 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6548,6 +6548,138 @@ void zend_compile_class_const_decl(zend_ast *ast TSRMLS_DC) { } } +static zend_trait_method_reference *zend_compile_method_ref(zend_ast *ast TSRMLS_DC) { + zend_ast *class_ast = ast->child[0]; + zend_ast *method_ast = ast->child[1]; + + zend_trait_method_reference *method_ref = emalloc(sizeof(zend_trait_method_reference)); + method_ref->ce = NULL; + method_ref->method_name = STR_COPY(Z_STR_P(zend_ast_get_zval(method_ast))); + + if (class_ast) { + zend_string *name = Z_STR_P(zend_ast_get_zval(class_ast)); + zend_bool is_fully_qualified = class_ast->attr; + method_ref->class_name = zend_resolve_class_name(name, is_fully_qualified TSRMLS_CC); + } else { + method_ref->class_name = NULL; + } + + return method_ref; +} + +static zend_string **zend_compile_name_list(zend_ast *ast TSRMLS_DC) { + zend_string **names = safe_emalloc(sizeof(zend_string *), ast->children + 1, 0); + zend_uint i; + + for (i = 0; i < ast->children; ++i) { + zend_ast *name_ast = ast->child[i]; + zend_string *name = Z_STR_P(zend_ast_get_zval(name_ast)); + zend_bool is_fully_qualified = name_ast->attr; + names[i] = zend_resolve_class_name(name, is_fully_qualified TSRMLS_CC); + } + + names[ast->children] = NULL; + + return names; +} + +static void zend_compile_trait_precedence(zend_ast *ast TSRMLS_DC) { + zend_ast *method_ref_ast = ast->child[0]; + zend_ast *insteadof_ast = ast->child[1]; + + zend_trait_precedence *precedence = emalloc(sizeof(zend_trait_precedence)); + precedence->trait_method = zend_compile_method_ref(method_ref_ast TSRMLS_CC); + precedence->exclude_from_classes + = (void *) zend_compile_name_list(insteadof_ast TSRMLS_CC); + + zend_add_to_list(&CG(active_class_entry)->trait_precedences, precedence TSRMLS_CC); +} + +static void zend_compile_trait_alias(zend_ast *ast TSRMLS_DC) { + zend_ast *method_ref_ast = ast->child[0]; + zend_ast *alias_ast = ast->child[1]; + zend_uint modifiers = ast->attr; + + zend_trait_alias *alias; + + if (modifiers == ZEND_ACC_STATIC) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'static' as method modifier"); + } else if (modifiers == ZEND_ACC_ABSTRACT) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'abstract' as method modifier"); + } else if (modifiers == ZEND_ACC_FINAL) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'final' as method modifier"); + } + + alias = emalloc(sizeof(zend_trait_alias)); + alias->trait_method = zend_compile_method_ref(method_ref_ast TSRMLS_CC); + alias->modifiers = modifiers; + + if (alias_ast) { + alias->alias = STR_COPY(Z_STR_P(zend_ast_get_zval(alias_ast))); + } else { + alias->alias = NULL; + } + + zend_add_to_list(&CG(active_class_entry)->trait_aliases, alias TSRMLS_CC); +} + +void zend_compile_use_trait(zend_ast *ast TSRMLS_DC) { + zend_ast *traits_ast = ast->child[0]; + zend_ast *adaptations_ast = ast->child[1]; + zend_class_entry *ce = CG(active_class_entry); + zend_op *opline; + zend_uint i; + + for (i = 0; i < traits_ast->children; ++i) { + zend_ast *trait_ast = traits_ast->child[i]; + zend_string *name = Z_STR_P(zend_ast_get_zval(trait_ast)); + + znode name_node; + + if (ce->ce_flags & ZEND_ACC_INTERFACE) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use traits inside of interfaces. " + "%s is used in %s", name->val, ce->name->val); + } + + switch (zend_get_class_fetch_type(name->val, name->len)) { + case ZEND_FETCH_CLASS_SELF: + case ZEND_FETCH_CLASS_PARENT: + case ZEND_FETCH_CLASS_STATIC: + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as trait name " + "as it is reserved", name->val); + break; + } + + zend_compile_expr(&name_node, trait_ast TSRMLS_CC); + zend_resolve_class_name_old(&name_node TSRMLS_CC); + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_ADD_TRAIT; + SET_NODE(opline->op1, &CG(implementing_class)); + opline->extended_value = ZEND_FETCH_CLASS_TRAIT; + opline->op2_type = IS_CONST; + opline->op2.constant = zend_add_class_name_literal(CG(active_op_array), &name_node.u.constant TSRMLS_CC); + ce->num_traits++; + } + + if (!adaptations_ast) { + return; + } + + for (i = 0; i < adaptations_ast->children; ++i) { + zend_ast *adaptation_ast = adaptations_ast->child[i]; + switch (adaptation_ast->kind) { + case ZEND_AST_TRAIT_PRECEDENCE: + zend_compile_trait_precedence(adaptation_ast TSRMLS_CC); + break; + case ZEND_AST_TRAIT_ALIAS: + zend_compile_trait_alias(adaptation_ast TSRMLS_CC); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + } +} + void zend_compile_binary_op(znode *result, zend_ast *ast TSRMLS_DC) { zend_ast *left_ast = ast->child[0]; zend_ast *right_ast = ast->child[1]; @@ -7391,6 +7523,9 @@ void zend_compile_stmt(zend_ast *ast TSRMLS_DC) { case ZEND_AST_CLASS_CONST_DECL: zend_compile_class_const_decl(ast TSRMLS_CC); break; + case ZEND_AST_USE_TRAIT: + zend_compile_use_trait(ast TSRMLS_CC); + break; default: { znode result; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 7380949084..373dfef21d 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -632,68 +632,65 @@ class_statement: { $$.u.ast = $1.u.ast; if (CG(doc_comment)) { STR_RELEASE(CG(doc_comment)); CG(doc_comment) = NULL; } AS($$); } - | trait_use_statement + | T_USE name_list trait_adaptations + { $$.u.ast = zend_ast_create_binary(ZEND_AST_USE_TRAIT, $2.u.ast, $3.u.ast); AS($$); } | method_modifiers function returns_ref T_STRING '(' parameter_list ')' method_body { $$.u.ast = zend_ast_create_func_decl(ZEND_AST_METHOD, $3.EA | Z_LVAL($1.u.constant), $2.EA, CG(zend_lineno), LANG_SCNG(yy_text), $2.u.op.ptr, Z_STR($4.u.constant), $6.u.ast, NULL, $8.u.ast); AS($$); } ; -trait_use_statement: - T_USE trait_list trait_adaptations -; - -trait_list: - fully_qualified_class_name { zend_do_use_trait(&$1 TSRMLS_CC); } - | trait_list ',' fully_qualified_class_name { zend_do_use_trait(&$3 TSRMLS_CC); } +name_list: + name { $$.u.ast = zend_ast_create_dynamic_and_add(ZEND_AST_NAME_LIST, $1.u.ast); } + | name_list ',' name { $$.u.ast = zend_ast_dynamic_add($1.u.ast, $3.u.ast); } ; trait_adaptations: - ';' - | '{' trait_adaptation_list '}' + ';' { $$.u.ast = NULL; } + | '{' '}' { $$.u.ast = NULL; } + | '{' trait_adaptation_list '}' { $$.u.ast = $2.u.ast; } ; trait_adaptation_list: - /* empty */ - | non_empty_trait_adaptation_list -; - -non_empty_trait_adaptation_list: - trait_adaptation_statement - | non_empty_trait_adaptation_list trait_adaptation_statement + trait_adaptation + { $$.u.ast = zend_ast_create_dynamic_and_add(ZEND_AST_TRAIT_ADAPTATIONS, $1.u.ast); } + | trait_adaptation_list trait_adaptation + { $$.u.ast = zend_ast_dynamic_add($1.u.ast, $2.u.ast); } ; -trait_adaptation_statement: - trait_precedence ';' - | trait_alias ';' +trait_adaptation: + trait_precedence ';' { $$.u.ast = $1.u.ast; } + | trait_alias ';' { $$.u.ast = $1.u.ast; } ; trait_precedence: - trait_method_reference_fully_qualified T_INSTEADOF trait_reference_list { zend_add_trait_precedence(&$1, &$3 TSRMLS_CC); } + absolute_trait_method_reference T_INSTEADOF name_list + { $$.u.ast = zend_ast_create_binary(ZEND_AST_TRAIT_PRECEDENCE, $1.u.ast, $3.u.ast); } ; -trait_reference_list: - fully_qualified_class_name { zend_resolve_class_name_old(&$1 TSRMLS_CC); zend_init_list(&$$.u.op.ptr, Z_STR($1.u.constant) TSRMLS_CC); } - | trait_reference_list ',' fully_qualified_class_name { zend_resolve_class_name_old(&$3 TSRMLS_CC); zend_add_to_list(&$1.u.op.ptr, Z_STR($3.u.constant) TSRMLS_CC); $$ = $1; } +trait_alias: + trait_method_reference T_AS trait_modifiers T_STRING + { $$.u.ast = zend_ast_create_ex(2, ZEND_AST_TRAIT_ALIAS, + Z_LVAL($3.u.constant), $1.u.ast, AST_ZVAL(&$4)); } + | trait_method_reference T_AS member_modifier + { $$.u.ast = zend_ast_create_ex(2, ZEND_AST_TRAIT_ALIAS, + Z_LVAL($3.u.constant), $1.u.ast, NULL); } ; trait_method_reference: - T_STRING { zend_prepare_reference(&$$, NULL, &$1 TSRMLS_CC); } - | trait_method_reference_fully_qualified { $$ = $1; } + T_STRING + { $$.u.ast = zend_ast_create_binary(ZEND_AST_METHOD_REFERENCE, NULL, AST_ZVAL(&$1)); } + | absolute_trait_method_reference { $$.u.ast = $1.u.ast; } ; -trait_method_reference_fully_qualified: - fully_qualified_class_name T_PAAMAYIM_NEKUDOTAYIM T_STRING { zend_prepare_reference(&$$, &$1, &$3 TSRMLS_CC); } -; - -trait_alias: - trait_method_reference T_AS trait_modifiers T_STRING { zend_add_trait_alias(&$1, &$3, &$4 TSRMLS_CC); } - | trait_method_reference T_AS member_modifier { zend_add_trait_alias(&$1, &$3, NULL TSRMLS_CC); } +absolute_trait_method_reference: + name T_PAAMAYIM_NEKUDOTAYIM T_STRING + { $$.u.ast = zend_ast_create_binary(ZEND_AST_METHOD_REFERENCE, $1.u.ast, AST_ZVAL(&$3)); } ; trait_modifiers: - /* empty */ { Z_LVAL($$.u.constant) = 0x0; } /* No change of methods visibility */ - | member_modifier { $$ = $1; } /* REM: Keep in mind, there are not only visibility modifiers */ + /* empty */ { Z_LVAL($$.u.constant) = 0; } + | member_modifier { $$ = $1; } ; method_body: