From: Nikita Popov Date: Mon, 21 Jul 2014 14:34:45 +0000 (+0200) Subject: Partial port of class declarations X-Git-Tag: POST_AST_MERGE^2~108 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b24bda6be1723aa7428e1558ecbfcc9ca9fb4817;p=php Partial port of class declarations --- diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index b62bae9c85..aea1c81740 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -350,6 +350,7 @@ ZEND_API void zend_ast_destroy(zend_ast *ast) case ZEND_AST_FUNC_DECL: case ZEND_AST_CLOSURE: case ZEND_AST_METHOD: + case ZEND_AST_CLASS: { zend_ast_decl *decl = (zend_ast_decl *) ast; STR_RELEASE(decl->name); diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 32316afa3c..784e43adec 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -116,6 +116,8 @@ enum _zend_ast_kind { ZEND_AST_TRAIT_PRECEDENCE, ZEND_AST_TRAIT_ALIAS, ZEND_AST_METHOD_REFERENCE, + + ZEND_AST_CLASS, }; typedef unsigned short zend_ast_kind; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index d21dde7732..f15044c265 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6574,6 +6574,115 @@ void zend_compile_use_trait(zend_ast *ast TSRMLS_DC) { } } +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]; + zend_ast *implements_ast = decl->child[1]; + zend_ast *stmt_ast = decl->child[2]; + + zend_string *name = decl->name, *lcname, *import_name = NULL; + zend_class_entry *ce = emalloc(sizeof(zend_class_entry)); + zend_op *opline; + znode extends_node; + + if (CG(active_class_entry)) { + zend_error_noreturn(E_COMPILE_ERROR, "Class declarations may not be nested"); + return; + } + + if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(name->val, name->len)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as class name as it is reserved", + name->val); + } + + lcname = STR_ALLOC(name->len, 0); + zend_str_tolower_copy(lcname->val, name->val, name->len); + + if (CG(current_import)) { + import_name = zend_hash_find_ptr(CG(current_import), lcname); + } + + if (Z_TYPE(CG(current_namespace)) != IS_UNDEF) { + name = zend_concat_names(Z_STRVAL(CG(current_namespace)), Z_STRLEN(CG(current_namespace)), + name->val, name->len); + + STR_RELEASE(lcname); + lcname = STR_ALLOC(name->len, 0); + zend_str_tolower_copy(lcname->val, name->val, name->len); + } else { + STR_ADDREF(name); + } + + if (import_name) { + char *import_name_lc = zend_str_tolower_dup(import_name->val, import_name->len); + if (lcname->len != import_name->len + || memcmp(import_name_lc, lcname->val, lcname->len) + ) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare class %s " + "because the name is already in use", name->val); + } + efree(import_name_lc); + } + + ce->type = ZEND_USER_CLASS; + ce->name = zend_new_interned_string(name TSRMLS_CC); + zend_initialize_class_data(ce, 1 TSRMLS_CC); + + ce->ce_flags |= decl->flags; + ce->info.user.filename = zend_get_compiled_filename(TSRMLS_C); + ce->info.user.line_start = decl->start_lineno; + ce->info.user.line_end = decl->end_lineno; + if (decl->doc_comment) { + ce->info.user.doc_comment = STR_COPY(decl->doc_comment); + } + + if (extends_ast) { + if (ce->ce_flags & ZEND_ACC_TRAIT) { + 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); + } + + if (!zend_is_const_default_class_ref(extends_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use '%s' as class name as it is reserved", name->val); + } + + 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; + opline->op2_type = IS_CONST; + LITERAL_STR(opline->op2, lcname); + + if (extends_ast) { + opline->opcode = ZEND_DECLARE_INHERITED_CLASS; + opline->extended_value = extends_node.u.op.var; + } else { + opline->opcode = ZEND_DECLARE_CLASS; + } + + { + zval key; + build_runtime_defined_function_key(&key, lcname, decl->lex_pos TSRMLS_CC); + opline->op1_type = IS_CONST; + opline->op1.constant = zend_add_literal(CG(active_op_array), &key TSRMLS_CC); + + zend_hash_update_ptr(CG(class_table), Z_STR(key), ce); + } + + CG(active_class_entry) = ce; + + zend_compile_stmt(stmt_ast TSRMLS_CC); + + // TODO.AST traits, interfaces, extends, etc + + CG(active_class_entry) = NULL; +} + 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]; @@ -7423,6 +7532,9 @@ void zend_compile_stmt(zend_ast *ast TSRMLS_DC) { case ZEND_AST_USE_TRAIT: zend_compile_use_trait(ast TSRMLS_CC); break; + case ZEND_AST_CLASS: + zend_compile_class_decl(ast TSRMLS_CC); + break; default: { znode result; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 77c4ef614c..67d0504cd4 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -409,18 +409,18 @@ is_variadic: ; unticked_class_declaration_statement: - class_entry_type T_STRING extends_from - { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } - implements_list - '{' - class_statement_list - '}' { zend_do_end_class_declaration(&$1, &$3 TSRMLS_CC); } - | interface_entry T_STRING + class_entry_type T_STRING extends_from implements_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), $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 - '}' { zend_do_end_class_declaration(&$1, NULL TSRMLS_CC); } + '}' { AS($6); zend_do_end_class_declaration(&$1, NULL TSRMLS_CC); }*/ ; @@ -432,27 +432,22 @@ class_entry_type: ; extends_from: - /* empty */ { $$.op_type = IS_UNUSED; } - | T_EXTENDS fully_qualified_class_name { zend_do_fetch_class(&$$, &$2 TSRMLS_CC); } + /* 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 */ +/*interface_extends_list: + /* empty / | T_EXTENDS interface_list -; +;*/ implements_list: - /* empty */ - | T_IMPLEMENTS interface_list -; - -interface_list: - fully_qualified_class_name { zend_do_implements_interface(&$1 TSRMLS_CC); } - | interface_list ',' fully_qualified_class_name { zend_do_implements_interface(&$3 TSRMLS_CC); } + /* empty */ { $$.u.ast = NULL; } + | T_IMPLEMENTS name_list { $$.u.ast = $2.u.ast; } ; foreach_variable: @@ -621,23 +616,24 @@ static_var: class_statement_list: class_statement_list class_statement + { $$.u.ast = zend_ast_dynamic_add($1.u.ast, $2.u.ast); } | /* empty */ + { $$.u.ast = zend_ast_create_dynamic(ZEND_AST_STMT_LIST); } ; class_statement: variable_modifiers property_list ';' - { $$.u.ast = $2.u.ast; $$.u.ast->attr = Z_LVAL($1.u.constant); AS($$); } + { $$.u.ast = $2.u.ast; $$.u.ast->attr = Z_LVAL($1.u.constant); } | class_const_list ';' { $$.u.ast = $1.u.ast; - if (CG(doc_comment)) { STR_RELEASE(CG(doc_comment)); CG(doc_comment) = NULL; } - AS($$); } + if (CG(doc_comment)) { STR_RELEASE(CG(doc_comment)); CG(doc_comment) = NULL; } } | T_USE name_list trait_adaptations - { $$.u.ast = zend_ast_create_binary(ZEND_AST_USE_TRAIT, $2.u.ast, $3.u.ast); AS($$); } + { $$.u.ast = zend_ast_create_binary(ZEND_AST_USE_TRAIT, $2.u.ast, $3.u.ast); } | method_modifiers function returns_ref T_STRING '(' parameter_list ')' method_body { $$.u.ast = zend_ast_create_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($$); } + Z_STR($4.u.constant), $6.u.ast, NULL, $8.u.ast); } ; name_list: