diff options
Diffstat (limited to 'Zend/zend_execute_API.c')
-rw-r--r-- | Zend/zend_execute_API.c | 269 |
1 files changed, 123 insertions, 146 deletions
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index aba82196ad..f6f1eed26f 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 Zend Technologies Ltd. (http://www.zend.com) | + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend license, | | that is bundled with this package in the file LICENSE, and is | @@ -33,6 +33,8 @@ #include "zend_generators.h" #include "zend_vm.h" #include "zend_float.h" +#include "zend_weakrefs.h" +#include "zend_inheritance.h" #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif @@ -130,8 +132,8 @@ void init_executor(void) /* {{{ */ original_sigsegv_handler = signal(SIGSEGV, zend_handle_sigsegv); #endif - EG(symtable_cache_ptr) = EG(symtable_cache) - 1; - EG(symtable_cache_limit) = EG(symtable_cache) + SYMTABLE_CACHE_SIZE - 1; + EG(symtable_cache_ptr) = EG(symtable_cache); + EG(symtable_cache_limit) = EG(symtable_cache) + SYMTABLE_CACHE_SIZE; EG(no_extensions) = 0; EG(function_table) = CG(function_table); @@ -183,6 +185,8 @@ void init_executor(void) /* {{{ */ EG(persistent_functions_count) = EG(function_table)->nNumUsed; EG(persistent_classes_count) = EG(class_table)->nNumUsed; + zend_weakrefs_init(); + EG(active) = 1; } /* }}} */ @@ -205,11 +209,11 @@ static void zend_unclean_zval_ptr_dtor(zval *zv) /* {{{ */ if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } - i_zval_ptr_dtor(zv ZEND_FILE_LINE_CC); + i_zval_ptr_dtor(zv); } /* }}} */ -static void zend_throw_or_error(int fetch_type, zend_class_entry *exception_ce, const char *format, ...) /* {{{ */ +static ZEND_COLD void zend_throw_or_error(int fetch_type, zend_class_entry *exception_ce, const char *format, ...) /* {{{ */ { va_list va; char *message = NULL; @@ -265,8 +269,20 @@ void shutdown_executor(void) /* {{{ */ zend_close_rsrc_list(&EG(regular_list)); } zend_end_try(); + if (!fast_shutdown) { + zend_hash_graceful_reverse_destroy(&EG(symbol_table)); + +#if ZEND_DEBUG + if (gc_enabled() && !CG(unclean_shutdown)) { + gc_collect_cycles(); + } +#endif + } + zend_objects_store_free_object_storage(&EG(objects_store), fast_shutdown); + zend_weakrefs_shutdown(); + /* All resources and objects are destroyed. */ /* No PHP callback functions may be called after this point. */ EG(active) = 0; @@ -286,14 +302,6 @@ void shutdown_executor(void) /* {{{ */ zend_hash_discard(EG(class_table), EG(persistent_classes_count)); zend_cleanup_internal_classes(); } else { - zend_hash_graceful_reverse_destroy(&EG(symbol_table)); - -#if ZEND_DEBUG - if (gc_enabled() && !CG(unclean_shutdown)) { - gc_collect_cycles(); - } -#endif - /* remove error handlers before destroying classes and functions, * so that if handler used some class, crash would not happen */ if (Z_TYPE(EG(user_error_handler)) != IS_UNDEF) { @@ -319,7 +327,7 @@ void shutdown_executor(void) /* {{{ */ } else { ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(zend_constants), key, zv) { zend_constant *c = Z_PTR_P(zv); - if (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) { + if (_idx == EG(persistent_constants_count)) { break; } zval_ptr_dtor_nogc(&c->value); @@ -329,30 +337,75 @@ void shutdown_executor(void) /* {{{ */ efree(c); zend_string_release_ex(key, 0); } ZEND_HASH_FOREACH_END_DEL(); + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(function_table), key, zv) { zend_function *func = Z_PTR_P(zv); - if (func->type == ZEND_INTERNAL_FUNCTION) { + if (_idx == EG(persistent_functions_count)) { break; } destroy_op_array(&func->op_array); zend_string_release_ex(key, 0); } ZEND_HASH_FOREACH_END_DEL(); + + /* Cleanup preloaded immutable functions */ + ZEND_HASH_REVERSE_FOREACH_VAL(EG(function_table), zv) { + zend_op_array *op_array = Z_PTR_P(zv); + if (op_array->type == ZEND_INTERNAL_FUNCTION) { + break; + } + ZEND_ASSERT(op_array->fn_flags & ZEND_ACC_IMMUTABLE); + if (op_array->static_variables) { + HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr); + if (ht) { + ZEND_ASSERT(GC_REFCOUNT(ht) == 1); + zend_array_destroy(ht); + ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL); + } + } + } ZEND_HASH_FOREACH_END(); + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(class_table), key, zv) { - zend_class_entry *ce = Z_PTR_P(zv); - if (ce->type == ZEND_INTERNAL_CLASS) { + if (_idx == EG(persistent_classes_count)) { break; } destroy_zend_class(zv); zend_string_release_ex(key, 0); } ZEND_HASH_FOREACH_END_DEL(); + + /* Cleanup preloaded immutable classes */ + ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { + zend_class_entry *ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + ZEND_ASSERT(ce->ce_flags & ZEND_ACC_IMMUTABLE); + if (ce->default_static_members_count) { + zend_cleanup_internal_class_data(ce); + } + if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) { + zend_op_array *op_array; + ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { + if (op_array->type == ZEND_USER_FUNCTION) { + if (op_array->static_variables) { + HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr); + if (ht) { + ZEND_ASSERT(GC_REFCOUNT(ht) == 1); + zend_array_destroy(ht); + ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL); + } + } + } + } ZEND_HASH_FOREACH_END(); + } + } ZEND_HASH_FOREACH_END(); } zend_cleanup_internal_classes(); - while (EG(symtable_cache_ptr)>=EG(symtable_cache)) { + while (EG(symtable_cache_ptr) > EG(symtable_cache)) { + EG(symtable_cache_ptr)--; zend_hash_destroy(*EG(symtable_cache_ptr)); FREE_HASHTABLE(*EG(symtable_cache_ptr)); - EG(symtable_cache_ptr)--; } zend_hash_destroy(&EG(included_files)); @@ -514,7 +567,7 @@ ZEND_API zend_bool zend_is_executing(void) /* {{{ */ } /* }}} */ -ZEND_API int zend_use_undefined_constant(zend_string *name, zend_ast_attr attr, zval *result) /* {{{ */ +ZEND_API ZEND_COLD int zend_use_undefined_constant(zend_string *name, zend_ast_attr attr, zval *result) /* {{{ */ { char *colon; @@ -605,6 +658,8 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) / zend_execute_data *call, dummy_execute_data; zend_fcall_info_cache fci_cache_local; zend_function *func; + uint32_t call_info; + void *object_or_called_scope; ZVAL_UNDEF(fci->retval); @@ -677,11 +732,18 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) / } func = fci_cache->function_handler; - fci->object = (func->common.fn_flags & ZEND_ACC_STATIC) ? - NULL : fci_cache->object; + if ((func->common.fn_flags & ZEND_ACC_STATIC) || !fci_cache->object) { + fci->object = NULL; + object_or_called_scope = fci_cache->called_scope; + call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC; + } else { + fci->object = fci_cache->object; + object_or_called_scope = fci->object; + call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_HAS_THIS; + } - call = zend_vm_stack_push_call_frame(ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC, - func, fci->param_count, fci_cache->called_scope, fci->object); + call = zend_vm_stack_push_call_frame(call_info, + func, fci->param_count, object_or_called_scope); if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_DEPRECATED)) { zend_error(E_DEPRECATED, "Function %s%s%s() is deprecated", @@ -763,7 +825,6 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) / int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0; ZVAL_NULL(fci->retval); call->prev_execute_data = EG(current_execute_data); - call->return_value = NULL; /* this is not a constructor call */ EG(current_execute_data) = call; if (EXPECTED(zend_execute_internal == NULL)) { /* saves one function call if zend_execute_internal is not used */ @@ -828,7 +889,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) / } /* }}} */ -ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, const zval *key, int use_autoload) /* {{{ */ +ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *key, uint32_t flags) /* {{{ */ { zend_class_entry *ce = NULL; zval args[1], *zv; @@ -838,7 +899,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, const zval *k zend_fcall_info_cache fcall_cache; if (key) { - lc_name = Z_STR_P(key); + lc_name = key; } else { if (name == NULL || !ZSTR_LEN(name)) { return NULL; @@ -857,13 +918,18 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, const zval *k if (!key) { zend_string_release_ex(lc_name, 0); } - return (zend_class_entry*)Z_PTR_P(zv); + ce = (zend_class_entry*)Z_PTR_P(zv); + if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED)) && + !(flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED)) { + return NULL; + } + return ce; } /* The compiler is not-reentrant. Make sure we __autoload() only during run-time * (doesn't impact functionality of __autoload() */ - if (!use_autoload || zend_is_compiling()) { + if ((flags & ZEND_FETCH_CLASS_NO_AUTOLOAD) || zend_is_compiling()) { if (!key) { zend_string_release_ex(lc_name, 0); } @@ -944,7 +1010,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, const zval *k ZEND_API zend_class_entry *zend_lookup_class(zend_string *name) /* {{{ */ { - return zend_lookup_class_ex(name, NULL, 1); + return zend_lookup_class_ex(name, NULL, 0); } /* }}} */ @@ -1073,7 +1139,7 @@ ZEND_API int zend_eval_string_ex(char *str, zval *retval_ptr, char *string_name, static void zend_set_timeout_ex(zend_long seconds, int reset_signals); -ZEND_API ZEND_NORETURN void zend_timeout(int dummy) /* {{{ */ +ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout(int dummy) /* {{{ */ { #if defined(PHP_WIN32) # ifndef ZTS @@ -1126,7 +1192,7 @@ static void zend_timeout_handler(int dummy) /* {{{ */ output_len = snprintf(log_buffer, sizeof(log_buffer), "\nFatal error: Maximum execution time of " ZEND_LONG_FMT "+" ZEND_LONG_FMT " seconds exceeded (terminated) in %s on line %d\n", EG(timeout_seconds), EG(hard_timeout), error_filename, error_lineno); if (output_len > 0) { - write(2, log_buffer, MIN(output_len, sizeof(log_buffer))); + zend_quiet_write(2, log_buffer, MIN(output_len, sizeof(log_buffer))); } _exit(124); } @@ -1193,7 +1259,7 @@ static void zend_set_timeout_ex(zend_long seconds, int reset_signals) /* {{{ */ timer, so we could end up with just an ignored timeout. Instead delete and recreate. */ if (NULL != tq_timer) { - if (!DeleteTimerQueueTimer(NULL, tq_timer, NULL)) { + if (!DeleteTimerQueueTimer(NULL, tq_timer, INVALID_HANDLE_VALUE)) { tq_timer = NULL; zend_error_noreturn(E_ERROR, "Could not delete queued timer"); return; @@ -1208,8 +1274,7 @@ static void zend_set_timeout_ex(zend_long seconds, int reset_signals) /* {{{ */ zend_error_noreturn(E_ERROR, "Could not queue new timer"); return; } -#else -# ifdef HAVE_SETITIMER +#elif defined(HAVE_SETITIMER) { struct itimerval t_r; /* timeout requested */ int signo; @@ -1218,39 +1283,38 @@ static void zend_set_timeout_ex(zend_long seconds, int reset_signals) /* {{{ */ t_r.it_value.tv_sec = seconds; t_r.it_value.tv_usec = t_r.it_interval.tv_sec = t_r.it_interval.tv_usec = 0; -# ifdef __CYGWIN__ +# ifdef __CYGWIN__ setitimer(ITIMER_REAL, &t_r, NULL); } signo = SIGALRM; -# else +# else setitimer(ITIMER_PROF, &t_r, NULL); } signo = SIGPROF; -# endif +# endif if (reset_signals) { -# ifdef ZEND_SIGNALS +# ifdef ZEND_SIGNALS zend_signal(signo, zend_timeout_handler); -# else +# else sigset_t sigset; -# ifdef HAVE_SIGACTION +# ifdef HAVE_SIGACTION struct sigaction act; act.sa_handler = zend_timeout_handler; sigemptyset(&act.sa_mask); act.sa_flags = SA_RESETHAND | SA_NODEFER; sigaction(signo, &act, NULL); -# else +# else signal(signo, zend_timeout_handler); -# endif /* HAVE_SIGACTION */ +# endif /* HAVE_SIGACTION */ sigemptyset(&sigset); sigaddset(&sigset, signo); sigprocmask(SIG_UNBLOCK, &sigset, NULL); -# endif /* ZEND_SIGNALS */ +# endif /* ZEND_SIGNALS */ } } -# endif /* HAVE_SETITIMER */ -#endif +#endif /* HAVE_SETITIMER */ } /* }}} */ @@ -1267,7 +1331,7 @@ void zend_unset_timeout(void) /* {{{ */ { #ifdef ZEND_WIN32 if (NULL != tq_timer) { - if (!DeleteTimerQueueTimer(NULL, tq_timer, NULL)) { + if (!DeleteTimerQueueTimer(NULL, tq_timer, INVALID_HANDLE_VALUE)) { EG(timed_out) = 0; tq_timer = NULL; zend_error_noreturn(E_ERROR, "Could not delete queued timer"); @@ -1275,23 +1339,20 @@ void zend_unset_timeout(void) /* {{{ */ } tq_timer = NULL; } - EG(timed_out) = 0; -#else -# ifdef HAVE_SETITIMER +#elif defined(HAVE_SETITIMER) if (EG(timeout_seconds)) { struct itimerval no_timeout; no_timeout.it_value.tv_sec = no_timeout.it_value.tv_usec = no_timeout.it_interval.tv_sec = no_timeout.it_interval.tv_usec = 0; -#ifdef __CYGWIN__ +# ifdef __CYGWIN__ setitimer(ITIMER_REAL, &no_timeout, NULL); -#else +# else setitimer(ITIMER_PROF, &no_timeout, NULL); -#endif +# endif } -# endif - EG(timed_out) = 0; #endif + EG(timed_out) = 0; } /* }}} */ @@ -1335,8 +1396,8 @@ check_fetch_type: } if (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) { - return zend_lookup_class_ex(class_name, NULL, 0); - } else if ((ce = zend_lookup_class_ex(class_name, NULL, 1)) == NULL) { + return zend_lookup_class_ex(class_name, NULL, fetch_type); + } else if ((ce = zend_lookup_class_ex(class_name, NULL, fetch_type)) == NULL) { if (!(fetch_type & ZEND_FETCH_CLASS_SILENT) && !EG(exception)) { if (fetch_sub_type == ZEND_FETCH_CLASS_INTERFACE) { zend_throw_or_error(fetch_type, NULL, "Interface '%s' not found", ZSTR_VAL(class_name)); @@ -1352,13 +1413,13 @@ check_fetch_type: } /* }}} */ -zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, const zval *key, int fetch_type) /* {{{ */ +zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *key, int fetch_type) /* {{{ */ { zend_class_entry *ce; if (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) { - return zend_lookup_class_ex(class_name, key, 0); - } else if ((ce = zend_lookup_class_ex(class_name, key, 1)) == NULL) { + return zend_lookup_class_ex(class_name, key, fetch_type); + } else if ((ce = zend_lookup_class_ex(class_name, key, fetch_type)) == NULL) { if (fetch_type & ZEND_FETCH_CLASS_SILENT) { return NULL; } @@ -1388,65 +1449,6 @@ zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, const zval * } /* }}} */ -#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] ? ZSTR_VAL(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; - int ctor; -} zend_abstract_info; - -static void zend_verify_abstract_class_function(zend_function *fn, zend_abstract_info *ai) /* {{{ */ -{ - if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { - if (ai->cnt < MAX_ABSTRACT_INFO_CNT) { - ai->afn[ai->cnt] = fn; - } - if (fn->common.fn_flags & ZEND_ACC_CTOR) { - if (!ai->ctor) { - ai->cnt++; - ai->ctor = 1; - } else { - ai->afn[ai->cnt] = NULL; - } - } else { - ai->cnt++; - } - } -} -/* }}} */ - -void zend_verify_abstract_class(zend_class_entry *ce) /* {{{ */ -{ - zend_function *func; - zend_abstract_info ai; - - if ((ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) && !(ce->ce_flags & (ZEND_ACC_TRAIT | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) { - memset(&ai, 0, sizeof(ai)); - - ZEND_HASH_FOREACH_PTR(&ce->function_table, func) { - zend_verify_abstract_class_function(func, &ai); - } ZEND_HASH_FOREACH_END(); - - if (ai.cnt) { - zend_error_noreturn(E_ERROR, "Class %s contains %d abstract method%s and must therefore be declared abstract or implement the remaining methods (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")", - ZSTR_VAL(ce->name), ai.cnt, - ai.cnt > 1 ? "s" : "", - DISPLAY_ABSTRACT_FN(0), - DISPLAY_ABSTRACT_FN(1), - DISPLAY_ABSTRACT_FN(2) - ); - } - } -} -/* }}} */ - ZEND_API int zend_delete_global_variable(zend_string *name) /* {{{ */ { return zend_hash_del_ind(&EG(symbol_table), name); @@ -1471,9 +1473,8 @@ ZEND_API zend_array *zend_rebuild_symbol_table(void) /* {{{ */ } ZEND_ADD_CALL_FLAG(ex, ZEND_CALL_HAS_SYMBOL_TABLE); - if (EG(symtable_cache_ptr) >= EG(symtable_cache)) { - /*printf("Cache hit! Reusing %x\n", symtable_cache[symtable_cache_ptr]);*/ - symbol_table = ex->symbol_table = *(EG(symtable_cache_ptr)--); + if (EG(symtable_cache_ptr) > EG(symtable_cache)) { + symbol_table = ex->symbol_table = *(--EG(symtable_cache_ptr)); if (!ex->func->op_array.last_var) { return symbol_table; } @@ -1647,27 +1648,3 @@ ZEND_API int zend_set_local_var_str(const char *name, size_t len, zval *value, i return FAILURE; } /* }}} */ - -ZEND_API int zend_forbid_dynamic_call(const char *func_name) /* {{{ */ -{ - zend_execute_data *ex = EG(current_execute_data); - ZEND_ASSERT(ex != NULL && ex->func != NULL); - - if (ZEND_CALL_INFO(ex) & ZEND_CALL_DYNAMIC) { - zend_error(E_WARNING, "Cannot call %s dynamically", func_name); - return FAILURE; - } - - return SUCCESS; -} -/* }}} */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - * vim600: sw=4 ts=4 fdm=marker - * vim<600: sw=4 ts=4 - */ |