]> granicus.if.org Git - php/commitdiff
Mostly finish class declarations (~50 failing tests)
authorNikita Popov <nikic@php.net>
Mon, 21 Jul 2014 15:14:01 +0000 (17:14 +0200)
committerNikita Popov <nikic@php.net>
Mon, 21 Jul 2014 15:14:01 +0000 (17:14 +0200)
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_language_parser.y

index f15044c2655b914e538ab587bff281d19546e892..1ca439766803d78d8c5c8e6466772c1ccc2dec26 100644 (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;
 }
index 4da630352044e06ccc1f7c57c01e568031a55806..382535be338004e53fcc481e0a68d0b7dbfc5ff1 100644 (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);
 
 
index 67d0504cd4baaa37fd6e82a71f786320769d0a8c..ca1d16cc127daa89573a9137e5b33c48203e17a3 100644 (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; }