From: Andi Gutmans Date: Mon, 29 Oct 2001 17:19:02 +0000 (+0000) Subject: - Initial support for nested class definitions X-Git-Tag: ChangeLog~489 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=26578c386d7a6bfaf7d5a884e3dab02797dba7db;p=php - Initial support for nested class definitions --- diff --git a/Zend/zend.h b/Zend/zend.h index 8169ff329c..4b055d8ace 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -277,6 +277,7 @@ struct _zend_class_entry { HashTable function_table; HashTable default_properties; + HashTable class_table; zend_function_entry *builtin_functions; /* handlers */ diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 4afdea8939..0c7ae5c161 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -901,6 +901,22 @@ void zend_do_begin_dynamic_function_call(znode *function_name TSRMLS_DC) } +void do_fetch_class(znode *result, znode *class_entry, znode *class_name TSRMLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); + + opline->opcode = ZEND_FETCH_CLASS; + if (class_entry) { + opline->op1 = *class_entry; + } else { + SET_UNUSED(opline->op1); + } + opline->op2 = *class_name; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->result.op_type = IS_CONST; /* FIXME: Hack so that INIT_FCALL_BY_NAME still knows this is a class */ + *result = opline->result; +} + void zend_do_begin_class_member_function_call(znode *class_name, znode *function_name TSRMLS_DC) { unsigned char *ptr = NULL; @@ -1644,30 +1660,29 @@ void zend_do_default_before_statement(znode *case_list, znode *default_token TSR } -void zend_do_begin_class_declaration(znode *class_name, znode *parent_class_name TSRMLS_DC) +void zend_do_begin_class_declaration(znode *class_token, znode *class_name, znode *parent_class_name TSRMLS_DC) { - zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); + zend_op *opline; int runtime_inheritance = 0; - - if (CG(active_class_entry)) { - zend_error(E_COMPILE_ERROR, "Class declarations may not be nested"); - return; - } - CG(class_entry).type = ZEND_USER_CLASS; - CG(class_entry).name = class_name->u.constant.value.str.val; - CG(class_entry).name_length = class_name->u.constant.value.str.len; - CG(class_entry).refcount = (int *) emalloc(sizeof(int)); - *CG(class_entry).refcount = 1; - CG(class_entry).constants_updated = 0; + zend_class_entry new_class_entry; + + class_token->u.previously_active_class_entry = CG(active_class_entry); + new_class_entry.type = ZEND_USER_CLASS; + new_class_entry.name = class_name->u.constant.value.str.val; + new_class_entry.name_length = class_name->u.constant.value.str.len; + new_class_entry.refcount = (int *) emalloc(sizeof(int)); + *new_class_entry.refcount = 1; + new_class_entry.constants_updated = 0; - zend_str_tolower(CG(class_entry).name, CG(class_entry).name_length); + zend_str_tolower(new_class_entry.name, new_class_entry.name_length); - zend_hash_init(&CG(class_entry).function_table, 10, NULL, ZEND_FUNCTION_DTOR, 0); - zend_hash_init(&CG(class_entry).default_properties, 10, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_init(&new_class_entry.function_table, 10, NULL, ZEND_FUNCTION_DTOR, 0); + zend_hash_init(&new_class_entry.class_table, 10, NULL, ZEND_CLASS_DTOR, 0); + zend_hash_init(&new_class_entry.default_properties, 10, NULL, ZVAL_PTR_DTOR, 0); - CG(class_entry).handle_function_call = NULL; - CG(class_entry).handle_property_set = NULL; - CG(class_entry).handle_property_get = NULL; + new_class_entry.handle_function_call = NULL; + new_class_entry.handle_property_set = NULL; + new_class_entry.handle_property_get = NULL; /* code for inheritance from parent class */ if (parent_class_name) { @@ -1679,29 +1694,38 @@ void zend_do_begin_class_declaration(znode *class_name, znode *parent_class_name CG(active_ce_parent_class_name).value.str.val = estrndup(parent_class_name->u.constant.value.str.val, parent_class_name->u.constant.value.str.len); CG(active_ce_parent_class_name).value.str.len = parent_class_name->u.constant.value.str.len; - if (zend_hash_find(CG(class_table), parent_class_name->u.constant.value.str.val, parent_class_name->u.constant.value.str.len+1, (void **) &parent_class)==SUCCESS) { + if (zend_hash_find(CG(active_class_entry)?&CG(active_class_entry)->class_table:CG(class_table), parent_class_name->u.constant.value.str.val, parent_class_name->u.constant.value.str.len+1, (void **) &parent_class)==SUCCESS) { /* copy functions */ - zend_hash_copy(&CG(class_entry).function_table, &parent_class->function_table, (copy_ctor_func_t) function_add_ref, &tmp_zend_function, sizeof(zend_function)); + zend_hash_copy(&new_class_entry.function_table, &parent_class->function_table, (copy_ctor_func_t) function_add_ref, &tmp_zend_function, sizeof(zend_function)); /* copy default properties */ - zend_hash_copy(&CG(class_entry).default_properties, &parent_class->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); + zend_hash_copy(&new_class_entry.default_properties, &parent_class->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); /* copy overloaded handlers */ - CG(class_entry).handle_function_call = parent_class->handle_function_call; - CG(class_entry).handle_property_get = parent_class->handle_property_get; - CG(class_entry).handle_property_set = parent_class->handle_property_set; + new_class_entry.handle_function_call = parent_class->handle_function_call; + new_class_entry.handle_property_get = parent_class->handle_property_get; + new_class_entry.handle_property_set = parent_class->handle_property_set; - CG(class_entry).parent = parent_class; + new_class_entry.parent = parent_class; zval_dtor(&parent_class_name->u.constant); } else { runtime_inheritance = 1; - CG(class_entry).parent = NULL; + new_class_entry.parent = NULL; } } else { - CG(class_entry).parent = NULL; + new_class_entry.parent = NULL; + } + + if (CG(active_class_entry)) { + if (runtime_inheritance) { + zend_error(E_ERROR, "Only first level classes can inherit from undefined classes"); + } + zend_hash_update(&CG(active_class_entry)->class_table, new_class_entry.name, new_class_entry.name_length+1, &new_class_entry, sizeof(zend_class_entry), (void **) &CG(active_class_entry)); + return; } + opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_DECLARE_FUNCTION_OR_CLASS; opline->op1.op_type = IS_CONST; build_runtime_defined_function_key(&opline->op1.u.constant, &class_name->u.constant, opline TSRMLS_CC); @@ -1711,32 +1735,32 @@ void zend_do_begin_class_declaration(znode *class_name, znode *parent_class_name if (runtime_inheritance) { char *full_class_name; - opline->op2.u.constant.value.str.len = parent_class_name->u.constant.value.str.len+1+CG(class_entry).name_length; + opline->op2.u.constant.value.str.len = parent_class_name->u.constant.value.str.len+1+new_class_entry.name_length; full_class_name = opline->op2.u.constant.value.str.val = (char *) emalloc(opline->op2.u.constant.value.str.len+1); memcpy(full_class_name, parent_class_name->u.constant.value.str.val, parent_class_name->u.constant.value.str.len); full_class_name += parent_class_name->u.constant.value.str.len; full_class_name[0] = ':'; full_class_name++; - memcpy(full_class_name, CG(class_entry).name, CG(class_entry).name_length); + memcpy(full_class_name, new_class_entry.name, new_class_entry.name_length); zval_dtor(&parent_class_name->u.constant); - full_class_name += CG(class_entry).name_length; + full_class_name += new_class_entry.name_length; full_class_name[0] = 0; opline->extended_value = ZEND_DECLARE_INHERITED_CLASS; } else { - opline->op2.u.constant.value.str.val = estrndup(CG(class_entry).name, CG(class_entry).name_length); - opline->op2.u.constant.value.str.len = CG(class_entry).name_length; + opline->op2.u.constant.value.str.val = estrndup(new_class_entry.name, new_class_entry.name_length); + opline->op2.u.constant.value.str.len = new_class_entry.name_length; opline->extended_value = ZEND_DECLARE_CLASS; } - zend_hash_update(CG(class_table), opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, &CG(class_entry), sizeof(zend_class_entry), (void **) &CG(active_class_entry)); + zend_hash_update(CG(class_table), opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, &new_class_entry, sizeof(zend_class_entry), (void **) &CG(active_class_entry)); } -void zend_do_end_class_declaration(TSRMLS_D) +void zend_do_end_class_declaration(znode *class_token TSRMLS_DC) { do_inherit_parent_constructor(CG(active_class_entry)); - CG(active_class_entry) = NULL; + CG(active_class_entry) = class_token->u.previously_active_class_entry; if (CG(active_ce_parent_class_name).value.str.val) { efree(CG(active_ce_parent_class_name).value.str.val); CG(active_ce_parent_class_name).value.str.val = NULL; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index e900990051..17d88c85e9 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -52,6 +52,7 @@ typedef struct _znode { zend_uint opline_num; /* Needs to be signed */ zend_uint fetch_type; zend_op_array *op_array; + zend_class_entry *previously_active_class_entry; /* Used at compile-time */ struct { zend_uint var; /* dummy */ zend_uint type; @@ -271,6 +272,7 @@ void zend_do_receive_arg(int op, znode *var, znode *offset, znode *initializatio int zend_do_begin_function_call(znode *function_name TSRMLS_DC); void zend_do_begin_method_call(znode *object, znode *function_name TSRMLS_DC); void zend_do_begin_dynamic_function_call(znode *function_name TSRMLS_DC); +void do_fetch_class(znode *result, znode *class_entry, znode *class_name TSRMLS_DC); void zend_do_begin_class_member_function_call(znode *class_name, znode *function_name TSRMLS_DC); void zend_do_end_function_call(znode *function_name, znode *result, znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC); void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC); @@ -300,8 +302,8 @@ void zend_do_case_before_statement(znode *case_list, znode *case_token, znode *c void zend_do_case_after_statement(znode *result, znode *case_token TSRMLS_DC); void zend_do_default_before_statement(znode *case_list, znode *default_token TSRMLS_DC); -void zend_do_begin_class_declaration(znode *class_name, znode *parent_class_name TSRMLS_DC); -void zend_do_end_class_declaration(TSRMLS_D); +void zend_do_begin_class_declaration(znode *class_token, znode *class_name, znode *parent_class_name TSRMLS_DC); +void zend_do_end_class_declaration(znode *class_token TSRMLS_DC); void zend_do_declare_property(znode *var_name, znode *value TSRMLS_DC); void zend_do_fetch_property(znode *result, znode *object, znode *property TSRMLS_DC); @@ -536,7 +538,7 @@ int zendlex(znode *zendlval TSRMLS_DC); #define ZEND_THROW 108 #define ZEND_NAMESPACE 109 - +#define ZEND_FETCH_CLASS 110 /* end of block */ diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 01a75dfd83..aed95cef97 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1484,6 +1484,19 @@ binary_assign_op_addr: { FREE_OP(EX(Ts), &EX(opline)->op2, EG(free_op2)); } NEXT_OPCODE(); + case ZEND_FETCH_CLASS: + { + if (EX(opline)->op1.op_type == IS_UNUSED) { + if (zend_hash_find(EG(class_table), EX(opline)->op2.u.constant.value.str.val, EX(opline)->op2.u.constant.value.str.len+1, &EX(Ts)[EX(opline)->result.u.var].EA.class_entry) == FAILURE) { + zend_error(E_ERROR, "Class '%s' not found", EX(opline)->op2.u.constant.value.str.val); + } + } else { + if (zend_hash_find(&EX(Ts)[EX(opline)->op1.u.var].EA.class_entry->class_table, EX(opline)->op2.u.constant.value.str.val, EX(opline)->op2.u.constant.value.str.len+1, &EX(Ts)[EX(opline)->result.u.var].EA.class_entry) == FAILURE) { + zend_error(E_ERROR, "Class '%s' not found", EX(opline)->op2.u.constant.value.str.val); + } + } + NEXT_OPCODE(); + } case ZEND_INIT_FCALL_BY_NAME: { zval *function_name; zend_function *function; @@ -1510,8 +1523,7 @@ binary_assign_op_addr: { zend_str_tolower(tmp.value.str.val, tmp.value.str.len); if (EX(opline)->op1.op_type != IS_UNUSED) { - if (EX(opline)->op1.op_type==IS_CONST) { /* used for class_name::function() */ - zend_class_entry *ce; + if (EX(opline)->op1.op_type==IS_CONST) { /* used for class::function() */ zval **object_ptr_ptr; if (zend_hash_find(EG(active_symbol_table), "this", sizeof("this"), (void **) &object_ptr_ptr)==FAILURE) { @@ -1522,10 +1534,7 @@ binary_assign_op_addr: { EX(object).ptr = *object_ptr_ptr; EX(object).ptr->refcount++; /* For this pointer */ } - if (zend_hash_find(EG(class_table), EX(opline)->op1.u.constant.value.str.val, EX(opline)->op1.u.constant.value.str.len+1, (void **) &ce)==FAILURE) { /* class doesn't exist */ - zend_error(E_ERROR, "Undefined class name '%s'", EX(opline)->op1.u.constant.value.str.val); - } - active_function_table = &ce->function_table; + active_function_table = &EX(Ts)[EX(opline)->op1.u.var].EA.class_entry->function_table; } else { /* used for member function calls */ EX(object).ptr = get_zval_ptr(&EX(opline)->op1, EX(Ts), &EG(free_op1), BP_VAR_R); diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 9c25e8c0ef..7d78cda398 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -44,6 +44,7 @@ typedef union _temp_variable { } data; unsigned char type; + zend_class_entry *class_entry; } EA; } temp_variable; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 67d15c3146..10694bd2c9 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -66,7 +66,7 @@ struct _zend_compiler_globals { zend_stack object_stack; zend_stack declare_stack; - zend_class_entry class_entry, *active_class_entry; + zend_class_entry *active_class_entry; zval active_ce_parent_class_name; /* variables for list() compilation */ diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index aa2e5f2687..67b6af8f99 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -237,8 +237,8 @@ unticked_declaration_statement: '(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } | T_OLD_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 0, $3.op_type TSRMLS_CC); } parameter_list '(' inner_statement_list ')' ';' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } - | T_CLASS T_STRING { zend_do_begin_class_declaration(&$2, NULL TSRMLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(TSRMLS_C); } - | T_CLASS T_STRING T_EXTENDS T_STRING { zend_do_begin_class_declaration(&$2, &$4 TSRMLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(TSRMLS_C); } + | T_CLASS T_STRING { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, TSRMLS_C); } + | T_CLASS T_STRING T_EXTENDS T_STRING { zend_do_begin_class_declaration(&$1, &$2, &$4 TSRMLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, TSRMLS_C); } ; @@ -391,7 +391,8 @@ class_statement: parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } | T_OLD_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 1, $3.op_type TSRMLS_CC); } parameter_list '(' inner_statement_list ')' ';' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } - + | T_CLASS T_STRING { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, TSRMLS_C); } + | T_CLASS T_STRING T_EXTENDS T_STRING { zend_do_begin_class_declaration(&$1, &$2, &$4 TSRMLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, TSRMLS_C); } ; is_reference: @@ -498,7 +499,7 @@ function_call: T_STRING '(' { $2.u.opline_num = zend_do_begin_function_call(&$1 TSRMLS_CC); } function_call_parameter_list ')' { zend_do_end_function_call(&$1, &$$, &$4, 0, $2.u.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } - | T_STRING T_PAAMAYIM_NEKUDOTAYIM static_or_variable_string '(' { zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } + | parse_class_entry T_PAAMAYIM_NEKUDOTAYIM static_or_variable_string '(' { zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } function_call_parameter_list ')' { zend_do_end_function_call(&$3, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} | cvar_without_objects '(' { zend_do_end_variable_parse(BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_dynamic_function_call(&$1 TSRMLS_CC); } @@ -506,6 +507,10 @@ function_call: { zend_do_end_function_call(&$1, &$$, &$4, 0, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} ; +parse_class_entry: + parse_class_entry T_PAAMAYIM_NEKUDOTAYIM T_STRING { do_fetch_class(&$$, &$1, &$3 TSRMLS_CC); } + | T_STRING { do_fetch_class(&$$, NULL, &$1 TSRMLS_CC); } +; static_or_variable_string: T_STRING { $$ = $1; } diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 6462d3e497..08da904c35 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -117,12 +117,14 @@ ZEND_API void destroy_zend_class(zend_class_entry *ce) efree(ce->refcount); zend_hash_destroy(&ce->function_table); zend_hash_destroy(&ce->default_properties); + zend_hash_destroy(&ce->class_table); break; case ZEND_INTERNAL_CLASS: free(ce->name); free(ce->refcount); zend_hash_destroy(&ce->function_table); zend_hash_destroy(&ce->default_properties); + /* zend_hash_destroy(&ce->class_table); FIXME: Make sure this is initialized */ break; } }