From: Zeev Suraski Date: Wed, 4 Feb 2004 16:30:15 +0000 (+0000) Subject: Reinstate early-binding for classes. X-Git-Tag: php-5.0.0b4RC1~141 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c5d842279c592cc2637d2c355761741cc03ab2a0;p=php Reinstate early-binding for classes. Note that this is available for downwards compatibility only - and it doesn't work if you use new features (namely, interfaces). Generally, people should declare their classes before using them, but we just didn't want hell to break loose (c) --- diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e979bb7883..022ed12a2d 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1980,7 +1980,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry } -ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, HashTable *class_table, int compile_time) +ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, zend_bool compile_time) { zend_function *function; @@ -2012,7 +2012,7 @@ ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, HashTa } -ZEND_API zend_class_entry *do_bind_class(zend_op *opline, HashTable *function_table, HashTable *class_table TSRMLS_DC) +ZEND_API zend_class_entry *do_bind_class(zend_op *opline, HashTable *class_table, zend_bool compile_time TSRMLS_DC) { zend_class_entry *ce, **pce; @@ -2025,14 +2025,23 @@ ZEND_API zend_class_entry *do_bind_class(zend_op *opline, HashTable *function_ta ce->refcount++; if (zend_hash_add(class_table, opline->op2.u.constant.value.str.val, opline->op2.u.constant.value.str.len+1, &ce, sizeof(zend_class_entry *), NULL)==FAILURE) { ce->refcount--; - zend_error(E_COMPILE_ERROR, "Cannot redeclare class %s", opline->op2.u.constant.value.str.val); + if (!compile_time) { + /* If we're in compile time, in practice, it's quite possible + * that we'll never reach this class declaration at runtime, + * so we shut up about it. This allows the if (!defined('FOO')) { return; } + * approach to work. + */ + zend_error(E_COMPILE_ERROR, "Cannot redeclare class %s", opline->op2.u.constant.value.str.val); + } return NULL; } else { + zend_verify_abstract_class(ce TSRMLS_CC); return ce; } } -ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *function_table, HashTable *class_table, zend_class_entry *parent_ce TSRMLS_DC) + +ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *class_table, zend_class_entry *parent_ce, zend_bool compile_time TSRMLS_DC) { zend_class_entry *ce, **pce; int found_ce; @@ -2040,7 +2049,14 @@ ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *f found_ce = zend_hash_find(class_table, opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, (void **) &pce); if (found_ce == FAILURE) { - zend_error(E_COMPILE_ERROR, "Cannot redeclare class %s", opline->op2.u.constant.value.str.val); + if (!compile_time) { + /* If we're in compile time, in practice, it's quite possible + * that we'll never reach this class declaration at runtime, + * so we shut up about it. This allows the if (!defined('FOO')) { return; } + * approach to work. + */ + zend_error(E_COMPILE_ERROR, "Cannot redeclare class %s", opline->op2.u.constant.value.str.val); + } return NULL; } else { ce = *pce; @@ -2068,16 +2084,56 @@ ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *f void zend_do_early_binding(TSRMLS_D) { zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1]; + HashTable *table; while (opline->opcode == ZEND_TICKS && opline > CG(active_op_array)->opcodes) { opline--; } - if (do_bind_function(opline, CG(function_table), CG(class_table), 1) == FAILURE) { - return; + switch (opline->opcode) { + case ZEND_DECLARE_FUNCTION: + if (do_bind_function(opline, CG(function_table), 1) == FAILURE) { + return; + } + table = CG(function_table); + break; + case ZEND_VERIFY_ABSTRACT_CLASS: { + zend_op *verify_abstract_class_op = opline; + + opline--; + if (opline->opcode == ZEND_DECLARE_CLASS) { + if (do_bind_class(opline, CG(class_table), 1 TSRMLS_CC) == NULL) { + return; + } + } else if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS) { + zval *parent_name = &(opline-1)->op2.u.constant; + zend_class_entry **pce; + + if (zend_lookup_class(Z_STRVAL_P(parent_name), Z_STRLEN_P(parent_name), &pce TSRMLS_CC) == FAILURE) { + return; + } + if (do_bind_inherited_class(opline, CG(class_table), *pce, 1 TSRMLS_CC) == NULL) { + return; + } + } else { + /* We currently don't early-bind classes that implement interfaces */ + return; + } + table = CG(class_table); + /* clear the verify_abstract_class op */ + init_op(verify_abstract_class_op TSRMLS_CC); + SET_UNUSED(verify_abstract_class_op->op1); + SET_UNUSED(verify_abstract_class_op->op2); + verify_abstract_class_op->opcode = ZEND_NOP; + } + + break; + default: + zend_error(E_COMPILE_ERROR, "Invalid binding type"); + return; } - zend_hash_del(CG(function_table), opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len); + zend_hash_del(table, opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len); zval_dtor(&opline->op1.u.constant); zval_dtor(&opline->op2.u.constant); opline->opcode = ZEND_NOP; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 98226c21cf..f138561671 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -367,9 +367,9 @@ void zend_do_begin_catch(znode *try_token, znode *catch_class, znode *catch_var, void zend_do_end_catch(znode *try_token TSRMLS_DC); void zend_do_throw(znode *expr TSRMLS_DC); -ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, HashTable *class_table, int compile_time); -ZEND_API zend_class_entry *do_bind_class(zend_op *opline, HashTable *function_table, HashTable *class_table TSRMLS_DC); -ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *function_table, HashTable *class_table, zend_class_entry *parent_ce TSRMLS_DC); +ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, zend_bool compile_time); +ZEND_API zend_class_entry *do_bind_class(zend_op *opline, HashTable *class_table, zend_bool compile_time TSRMLS_DC); +ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *class_table, zend_class_entry *parent_ce, zend_bool compile_time TSRMLS_DC); ZEND_API void zend_do_inherit_interfaces(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC); ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC); void zend_do_implements_interface(znode *interface_znode TSRMLS_DC); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 7ac7a1910c..5e8209e3d2 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4006,21 +4006,21 @@ int zend_ext_fcall_end_handler(ZEND_OPCODE_HANDLER_ARGS) int zend_declare_class_handler(ZEND_OPCODE_HANDLER_ARGS) { - EX_T(opline->result.u.var).class_entry = do_bind_class(opline, EG(function_table), EG(class_table) TSRMLS_CC); + EX_T(opline->result.u.var).class_entry = do_bind_class(opline, EG(class_table), 0 TSRMLS_CC); NEXT_OPCODE(); } int zend_declare_inherited_class_handler(ZEND_OPCODE_HANDLER_ARGS) { - EX_T(opline->result.u.var).class_entry = do_bind_inherited_class(opline, EG(function_table), EG(class_table), EX_T(opline->extended_value).class_entry TSRMLS_CC); + EX_T(opline->result.u.var).class_entry = do_bind_inherited_class(opline, EG(class_table), EX_T(opline->extended_value).class_entry, 0 TSRMLS_CC); NEXT_OPCODE(); } int zend_declare_function_handler(ZEND_OPCODE_HANDLER_ARGS) { - do_bind_function(opline, EG(function_table), EG(class_table), 0); + do_bind_function(opline, EG(function_table), 0); NEXT_OPCODE(); } @@ -4107,52 +4107,9 @@ int zend_handle_exception_handler(ZEND_OPCODE_HANDLER_ARGS) } -#define MAX_ABSTRACT_INFO_CNT 3 -#define MAX_ABSTRACT_INFO_FMT "%s%s%s%s" - -#define DISPLAY_ABSTRACT_FN(idx) \ - ai.afn[idx] ? ZEND_FN_SCOPE_NAME(ai.afn[idx]) : "", \ - ai.afn[idx] ? "::" : "", \ - ai.afn[idx] ? ai.afn[idx]->common.function_name : "", \ - ai.afn[idx] && ai.afn[idx+1] ? ", " : (ai.afn[idx] && ai.cnt >= MAX_ABSTRACT_INFO_CNT ? ", ..." : "") - - -typedef struct _zend_abstract_info { - zend_function *afn[MAX_ABSTRACT_INFO_CNT+1]; - int cnt; -} zend_abstract_info; - - -int zend_verify_abstract_class_function(zend_function *fn, zend_abstract_info *ai TSRMLS_DC) -{ - if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { - if (ai->cnt < MAX_ABSTRACT_INFO_CNT) { - ai->afn[ai->cnt] = fn; - } - ai->cnt++; - } - return 0; -} - - int zend_verify_abstract_class_handler(ZEND_OPCODE_HANDLER_ARGS) { - zend_class_entry *ce = EX_T(opline->op1.u.var).class_entry; - zend_abstract_info ai; - - if ((ce->ce_flags & ZEND_ACC_ABSTRACT) && !(ce->ce_flags & ZEND_ACC_ABSTRACT_CLASS)) { - memset(&ai, 0, sizeof(ai)); - - zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t) zend_verify_abstract_class_function, &ai TSRMLS_CC); - - zend_error(E_ERROR, "Class %s contains %d abstract methods and must therefore be declared abstract (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")", - ce->name, ai.cnt, - DISPLAY_ABSTRACT_FN(0), - DISPLAY_ABSTRACT_FN(1), - DISPLAY_ABSTRACT_FN(2) - ); - } - + zend_verify_abstract_class(EX_T(opline->op1.u.var).class_entry TSRMLS_CC); NEXT_OPCODE(); } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 1c3af83f6a..3f5d5cc4f8 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -145,7 +145,7 @@ ZEND_API void zend_timeout(int dummy); ZEND_API zend_class_entry *zend_fetch_class(char *class_name, uint class_name_len, int fetch_type TSRMLS_DC); void zend_throw_exception_internal(zval *exception TSRMLS_DC); ZEND_API void zend_clear_exception(TSRMLS_D); - +void zend_verify_abstract_class(zend_class_entry *ce TSRMLS_DC); #ifdef ZEND_WIN32 void zend_init_timeout_thread(); @@ -161,8 +161,6 @@ void zend_shutdown_timeout_thread(); void zend_assign_to_variable_reference(znode *result, zval **variable_ptr_ptr, zval **value_ptr_ptr, temp_variable *Ts TSRMLS_DC); -#define IS_STRING_OFFSET 2 - /* The following tries to resolve the classname of a zval of type object. * Since it is slow it should be only used in error messages. */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 3267829f7d..c443678f47 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1225,6 +1225,51 @@ ZEND_API void zend_clear_exception(TSRMLS_D) } +#define MAX_ABSTRACT_INFO_CNT 3 +#define MAX_ABSTRACT_INFO_FMT "%s%s%s%s" +#define DISPLAY_ABSTRACT_FN(idx) \ + ai.afn[idx] ? ZEND_FN_SCOPE_NAME(ai.afn[idx]) : "", \ + ai.afn[idx] ? "::" : "", \ + ai.afn[idx] ? ai.afn[idx]->common.function_name : "", \ + ai.afn[idx] && ai.afn[idx+1] ? ", " : (ai.afn[idx] && ai.cnt >= MAX_ABSTRACT_INFO_CNT ? ", ..." : "") + +typedef struct _zend_abstract_info { + zend_function *afn[MAX_ABSTRACT_INFO_CNT+1]; + int cnt; +} zend_abstract_info; + + +static int zend_verify_abstract_class_function(zend_function *fn, zend_abstract_info *ai TSRMLS_DC) +{ + if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { + if (ai->cnt < MAX_ABSTRACT_INFO_CNT) { + ai->afn[ai->cnt] = fn; + } + ai->cnt++; + } + return 0; +} + + +void zend_verify_abstract_class(zend_class_entry *ce TSRMLS_DC) +{ + zend_abstract_info ai; + + if ((ce->ce_flags & ZEND_ACC_ABSTRACT) && !(ce->ce_flags & ZEND_ACC_ABSTRACT_CLASS)) { + memset(&ai, 0, sizeof(ai)); + + zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t) zend_verify_abstract_class_function, &ai TSRMLS_CC); + + zend_error(E_ERROR, "Class %s contains %d abstract methods and must therefore be declared abstract (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")", + ce->name, ai.cnt, + DISPLAY_ABSTRACT_FN(0), + DISPLAY_ABSTRACT_FN(1), + DISPLAY_ABSTRACT_FN(2) + ); + } +} + + /* * Local variables: * tab-width: 4 diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 8540ded223..d127af4297 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -156,7 +156,7 @@ top_statement_list: top_statement: statement | function_declaration_statement { zend_do_early_binding(TSRMLS_C); } - | class_declaration_statement + | class_declaration_statement { zend_do_early_binding(TSRMLS_C); } ;