}
}
+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];
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;
{ $$.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: