summaryrefslogtreecommitdiff
path: root/Zend/zend_execute_API.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_execute_API.c')
-rw-r--r--Zend/zend_execute_API.c269
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
- */