Partial port of class declarations

This commit is contained in:
Nikita Popov 2014-07-21 16:34:45 +02:00
parent 461bce5222
commit b24bda6be1
4 changed files with 136 additions and 25 deletions

View File

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

View File

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

View File

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

View File

@ -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: