Mostly finish class declarations (~50 failing tests)

This commit is contained in:
Nikita Popov 2014-07-21 17:14:01 +02:00
parent b24bda6be1
commit 005315510a
3 changed files with 88 additions and 26 deletions

View file

@ -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;
}

View file

@ -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);

View file

@ -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; }