]> granicus.if.org Git - php/commitdiff
Port trait uses
authorNikita Popov <nikic@php.net>
Sat, 19 Jul 2014 20:39:01 +0000 (22:39 +0200)
committerNikita Popov <nikic@php.net>
Sat, 19 Jul 2014 20:39:01 +0000 (22:39 +0200)
Zend/zend_ast.h
Zend/zend_compile.c
Zend/zend_language_parser.y

index bb184cd9a8b6d6a97cab8de0aa56e500f2acbd5d..dd260249a4a731bc89a0412e0b5c42b29124e2f8 100644 (file)
@@ -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
index d19e377d681576f98fb2187f5a5428283e1be509..25793293959ae2c8bd4d93595d7e596757532bc6 100644 (file)
@@ -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;
index 73809490842ccd2ed8a95ff9e2338b8f76b71098..373dfef21ded73070463bd293a11e88de379157d 100644 (file)
@@ -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: