From: Nikita Popov Date: Mon, 21 Jul 2014 15:14:01 +0000 (+0200) Subject: Mostly finish class declarations (~50 failing tests) X-Git-Tag: POST_AST_MERGE^2~107 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=005315510a6c4ef2974c9927609db36e4cc2407c;p=php Mostly finish class declarations (~50 failing tests) --- diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f15044c265..1ca4397668 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6045,7 +6045,7 @@ void zend_begin_method_decl( ) { zend_class_entry *ce = CG(active_class_entry); zend_bool in_interface = (ce->ce_flags & ZEND_ACC_INTERFACE) != 0; - zend_bool in_trait = (ce->ce_flags & ZEND_ACC_TRAIT) != 0; + zend_bool in_trait = ZEND_CE_IS_TRAIT(ce); zend_bool is_public = (op_array->fn_flags & ZEND_ACC_PUBLIC) != 0; zend_bool is_static = (op_array->fn_flags & ZEND_ACC_STATIC) != 0; @@ -6424,7 +6424,7 @@ void zend_compile_class_const_decl(zend_ast *ast TSRMLS_DC) { zend_string *name = Z_STR_P(zend_ast_get_zval(name_ast)); zval value_zv; - if (ce->ce_flags & ZEND_ACC_TRAIT) { + if (ZEND_CE_IS_TRAIT(ce)) { zend_error_noreturn(E_COMPILE_ERROR, "Traits cannot have constants"); return; } @@ -6574,6 +6574,35 @@ void zend_compile_use_trait(zend_ast *ast TSRMLS_DC) { } } +void zend_compile_implements(znode *class_node, zend_ast *ast TSRMLS_DC) { + zend_uint i; + for (i = 0; i < ast->children; ++i) { + zend_ast *class_ast = ast->child[i]; + zend_string *name = Z_STR_P(zend_ast_get_zval(class_ast)); + + zend_op *opline; + + /* Traits can not implement interfaces */ + if (ZEND_CE_IS_TRAIT(CG(active_class_entry))) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as interface on '%s' " + "since it is a Trait", name->val, CG(active_class_entry)->name->val); + } + + if (!zend_is_const_default_class_ref(class_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use '%s' as interface name as it is reserved", name->val); + } + + opline = emit_op(NULL, ZEND_ADD_INTERFACE, class_node, NULL TSRMLS_CC); + opline->extended_value = ZEND_FETCH_CLASS_INTERFACE; + opline->op2_type = IS_CONST; + opline->op2.constant = zend_add_class_name_literal(CG(active_op_array), + zend_resolve_class_name_ast(class_ast TSRMLS_CC) TSRMLS_CC); + + CG(active_class_entry)->num_interfaces++; + } +} + void zend_compile_class_decl(zend_ast *ast TSRMLS_DC) { zend_ast_decl *decl = (zend_ast_decl *) ast; zend_ast *extends_ast = decl->child[0]; @@ -6583,7 +6612,7 @@ void zend_compile_class_decl(zend_ast *ast TSRMLS_DC) { zend_string *name = decl->name, *lcname, *import_name = NULL; zend_class_entry *ce = emalloc(sizeof(zend_class_entry)); zend_op *opline; - znode extends_node; + znode declare_node, extends_node; if (CG(active_class_entry)) { zend_error_noreturn(E_COMPILE_ERROR, "Class declarations may not be nested"); @@ -6637,7 +6666,7 @@ void zend_compile_class_decl(zend_ast *ast TSRMLS_DC) { } if (extends_ast) { - if (ce->ce_flags & ZEND_ACC_TRAIT) { + if (ZEND_CE_IS_TRAIT(ce)) { zend_error_noreturn(E_COMPILE_ERROR, "A trait (%s) cannot extend a class. " "Traits can only be composed from other traits with the 'use' keyword. Error", name->val); @@ -6651,10 +6680,14 @@ void zend_compile_class_decl(zend_ast *ast TSRMLS_DC) { zend_compile_class_ref(&extends_node, extends_ast TSRMLS_CC); } - opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->result.var = get_temporary_variable(CG(active_op_array)); opline->result_type = IS_VAR; + GET_NODE(&declare_node, opline->result); + + // TODO.AST drop this + GET_NODE(&CG(implementing_class), opline->result); + opline->op2_type = IS_CONST; LITERAL_STR(opline->op2, lcname); @@ -6676,9 +6709,43 @@ void zend_compile_class_decl(zend_ast *ast TSRMLS_DC) { CG(active_class_entry) = ce; + if (implements_ast) { + zend_compile_implements(&declare_node, implements_ast TSRMLS_CC); + } + zend_compile_stmt(stmt_ast TSRMLS_CC); - // TODO.AST traits, interfaces, extends, etc + // TODO.AST validity checks + + /* Check for traits and proceed like with interfaces. + * The only difference will be a combined handling of them in the end. + * Thus, we need another opcode here. */ + if (ce->num_traits > 0) { + ce->traits = NULL; + ce->num_traits = 0; + ce->ce_flags |= ZEND_ACC_IMPLEMENT_TRAITS; + + emit_op(NULL, ZEND_BIND_TRAITS, &declare_node, NULL TSRMLS_CC); + } + + if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) + && (extends_ast || ce->num_interfaces > 0) + ) { + zend_verify_abstract_class(ce TSRMLS_CC); + if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS)) { + do_verify_abstract_class(TSRMLS_C); + } + } + + /* Inherit interfaces; reset number to zero, we need it for above check and + * will restore it during actual implementation. + * The ZEND_ACC_IMPLEMENT_INTERFACES flag disables double call to + * zend_verify_abstract_class() */ + if (ce->num_interfaces > 0) { + ce->interfaces = NULL; + ce->num_interfaces = 0; + ce->ce_flags |= ZEND_ACC_IMPLEMENT_INTERFACES; + } CG(active_class_entry) = NULL; } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 4da6303520..382535be33 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -226,6 +226,8 @@ typedef struct _zend_try_catch_element { /* function has arguments with type hinting */ #define ZEND_ACC_HAS_TYPE_HINTS 0x10000000 +#define ZEND_CE_IS_TRAIT(ce) (((ce)->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) + char *zend_visibility_string(zend_uint fn_flags); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 67d0504cd4..ca1d16cc12 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -415,12 +415,12 @@ unticked_class_declaration_statement: { $$.u.ast = zend_ast_create_decl(ZEND_AST_CLASS, $1.EA, $1.u.op.opline_num, CG(zend_lineno), LANG_SCNG(yy_text), $5.u.op.ptr, Z_STR($2.u.constant), $3.u.ast, $4.u.ast, $7.u.ast); AS($$); } - /*| interface_entry T_STRING - { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); } - interface_extends_list - '{' - class_statement_list - '}' { AS($6); zend_do_end_class_declaration(&$1, NULL TSRMLS_CC); }*/ + | interface_entry T_STRING interface_extends_list + { $$.u.op.ptr = CG(doc_comment); CG(doc_comment) = NULL; } + '{' class_statement_list '}' + { $$.u.ast = zend_ast_create_decl(ZEND_AST_CLASS, $1.EA, $1.u.op.opline_num, + CG(zend_lineno), LANG_SCNG(yy_text), $4.u.op.ptr, + Z_STR($2.u.constant), NULL, $3.u.ast, $6.u.ast); AS($$); } ; @@ -431,20 +431,20 @@ class_entry_type: | T_FINAL T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_FINAL_CLASS; } ; +interface_entry: + T_INTERFACE { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_INTERFACE; } +; + extends_from: /* empty */ { $$.u.ast = NULL; } | T_EXTENDS name { $$.u.ast = $2.u.ast; } ; -interface_entry: - T_INTERFACE { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_INTERFACE; } +interface_extends_list: + /* empty */ { $$.u.ast = NULL; } + | T_EXTENDS name_list { $$.u.ast = $2.u.ast; } ; -/*interface_extends_list: - /* empty / - | T_EXTENDS interface_list -;*/ - implements_list: /* empty */ { $$.u.ast = NULL; } | T_IMPLEMENTS name_list { $$.u.ast = $2.u.ast; } @@ -936,13 +936,6 @@ class_name: | name { $$.u.ast = $1.u.ast; } ; -fully_qualified_class_name: - namespace_name { $$ = $1; } - | T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$, &$$, &$3 TSRMLS_CC); } - | T_NS_SEPARATOR namespace_name { zval tmp; ZVAL_NEW_STR(&tmp, STR_ALLOC(Z_STRLEN($2.u.constant)+1, 0)); Z_STRVAL(tmp)[0] = '\\'; memcpy(Z_STRVAL(tmp) + 1, Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); if (Z_DELREF($2.u.constant) == 0) {efree(Z_STR($2.u.constant));} Z_STR($2.u.constant) = Z_STR(tmp); $$ = $2; } -; - - class_name_reference: class_name { $$.u.ast = $1.u.ast; } | new_variable { $$.u.ast = $1.u.ast; }