summaryrefslogtreecommitdiff
path: root/Zend/zend_execute.c
diff options
context:
space:
mode:
authorAaron Piotrowski <aaron@trowski.com>2016-06-10 22:02:23 -0500
committerAaron Piotrowski <aaron@trowski.com>2016-06-10 22:02:23 -0500
commite3c681aa5cc71122a8d2fae42e6513fc413ccac8 (patch)
tree5f1df62f7b666028edb0ee1adf083a52d63df45a /Zend/zend_execute.c
parentfb4e3085cbaa76eb8f28eebf848a81d1c0190067 (diff)
parent792e89385ca6fc722a03590722eb7745a2374720 (diff)
downloadphp-git-e3c681aa5cc71122a8d2fae42e6513fc413ccac8.tar.gz
Merge branch 'master' into throw-error-in-extensions
Diffstat (limited to 'Zend/zend_execute.c')
-rw-r--r--Zend/zend_execute.c1697
1 files changed, 1067 insertions, 630 deletions
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index bbe1537b88..b2769ad6fb 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) 1998-2015 Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright (c) 1998-2016 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 |
@@ -14,6 +14,7 @@
+----------------------------------------------------------------------+
| Authors: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
+ | Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
@@ -39,6 +40,7 @@
#include "zend_vm.h"
#include "zend_dtrace.h"
#include "zend_inheritance.h"
+#include "zend_type_info.h"
/* Virtual current working directory support */
#include "zend_virtual_cwd.h"
@@ -53,6 +55,8 @@ typedef int (ZEND_FASTCALL *incdec_t)(zval *);
#define get_zval_ptr(op_type, node, ex, should_free, type) _get_zval_ptr(op_type, node, ex, should_free, type)
#define get_zval_ptr_deref(op_type, node, ex, should_free, type) _get_zval_ptr_deref(op_type, node, ex, should_free, type)
+#define get_zval_ptr_r(op_type, node, ex, should_free) _get_zval_ptr_r(op_type, node, ex, should_free)
+#define get_zval_ptr_r_deref(op_type, node, ex, should_free) _get_zval_ptr_r_deref(op_type, node, ex, should_free)
#define get_zval_ptr_undef(op_type, node, ex, should_free, type) _get_zval_ptr_undef(op_type, node, ex, should_free, type)
#define get_zval_ptr_ptr(op_type, node, ex, should_free, type) _get_zval_ptr_ptr(op_type, node, ex, should_free, type)
#define get_zval_ptr_ptr_undef(op_type, node, ex, should_free, type) _get_zval_ptr_ptr(op_type, node, ex, should_free, type)
@@ -61,17 +65,17 @@ typedef int (ZEND_FASTCALL *incdec_t)(zval *);
#define get_obj_zval_ptr_ptr(op_type, node, ex, should_free, type) _get_obj_zval_ptr_ptr(op_type, node, ex, should_free, type)
/* Prototypes */
-static void zend_extension_statement_handler(const zend_extension *extension, zend_op_array *op_array);
-static void zend_extension_fcall_begin_handler(const zend_extension *extension, zend_op_array *op_array);
-static void zend_extension_fcall_end_handler(const zend_extension *extension, zend_op_array *op_array);
+static void zend_extension_statement_handler(const zend_extension *extension, zend_execute_data *frame);
+static void zend_extension_fcall_begin_handler(const zend_extension *extension, zend_execute_data *frame);
+static void zend_extension_fcall_end_handler(const zend_extension *extension, zend_execute_data *frame);
-#define RETURN_VALUE_USED(opline) (!((opline)->result_type & EXT_TYPE_UNUSED))
+#define RETURN_VALUE_USED(opline) ((opline)->result_type != IS_UNUSED)
static ZEND_FUNCTION(pass)
{
}
-static const zend_internal_function zend_pass_function = {
+ZEND_API const zend_internal_function zend_pass_function = {
ZEND_INTERNAL_FUNCTION, /* type */
{0, 0, 0}, /* arg_flags */
0, /* fn_flags */
@@ -82,24 +86,22 @@ static const zend_internal_function zend_pass_function = {
0, /* required_num_args */
NULL, /* arg_info */
ZEND_FN(pass), /* handler */
- NULL /* module */
+ NULL, /* module */
+ {NULL,NULL,NULL,NULL} /* reserved */
};
#undef zval_ptr_dtor
#define zval_ptr_dtor(zv) i_zval_ptr_dtor(zv ZEND_FILE_LINE_CC)
-#define PZVAL_LOCK(z) if (Z_REFCOUNTED_P(z)) Z_ADDREF_P((z))
-#define SELECTIVE_PZVAL_LOCK(pzv, opline) if (RETURN_VALUE_USED(opline)) { PZVAL_LOCK(pzv); }
-
#define READY_TO_DESTROY(zv) \
- (zv && Z_REFCOUNTED_P(zv) && Z_REFCOUNT_P(zv) == 1)
+ (UNEXPECTED(zv) && Z_REFCOUNTED_P(zv) && Z_REFCOUNT_P(zv) == 1)
-#define EXTRACT_ZVAL_PTR(zv) do { \
- zval *__zv = (zv); \
- if (Z_TYPE_P(__zv) == IS_INDIRECT) { \
- ZVAL_COPY(__zv, Z_INDIRECT_P(__zv)); \
- } \
- } while (0)
+#define EXTRACT_ZVAL_PTR(zv) do { \
+ zval *__zv = (zv); \
+ if (EXPECTED(Z_TYPE_P(__zv) == IS_INDIRECT)) { \
+ ZVAL_COPY(__zv, Z_INDIRECT_P(__zv)); \
+ } \
+} while (0)
#define FREE_OP(should_free) \
if (should_free) { \
@@ -116,21 +118,8 @@ static const zend_internal_function zend_pass_function = {
zval_ptr_dtor_nogc(should_free); \
}
-/* End of zend_execute_locks.h */
-
#define CV_DEF_OF(i) (EX(func)->op_array.vars[i])
-#define CTOR_CALL_BIT 0x1
-#define CTOR_USED_BIT 0x2
-
-#define IS_CTOR_CALL(ce) (((zend_uintptr_t)(ce)) & CTOR_CALL_BIT)
-#define IS_CTOR_USED(ce) (((zend_uintptr_t)(ce)) & CTOR_USED_BIT)
-
-#define ENCODE_CTOR(ce, used) \
- ((zend_class_entry*)(((zend_uintptr_t)(ce)) | CTOR_CALL_BIT | ((used) ? CTOR_USED_BIT : 0)))
-#define DECODE_CTOR(ce) \
- ((zend_class_entry*)(((zend_uintptr_t)(ce)) & ~(CTOR_CALL_BIT|CTOR_USED_BIT)))
-
#define ZEND_VM_MAIN_STACK_PAGE_SLOTS (16 * 1024) /* should be a power of 2 */
#define ZEND_VM_GENERATOR_STACK_PAGE_SLOTS (256)
@@ -147,7 +136,7 @@ static const zend_internal_function zend_pass_function = {
static zend_always_inline zend_vm_stack zend_vm_stack_new_page(size_t size, zend_vm_stack prev) {
zend_vm_stack page = (zend_vm_stack)emalloc(size);
- page->top = ZEND_VM_STACK_ELEMETS(page);
+ page->top = ZEND_VM_STACK_ELEMENTS(page);
page->end = (zval*)((char*)page + size);
page->prev = prev;
return page;
@@ -221,22 +210,25 @@ static zend_always_inline zval *_get_zval_ptr_var_deref(uint32_t var, const zend
return ret;
}
-static zend_never_inline zval *_get_zval_cv_lookup(zval *ptr, uint32_t var, int type, const zend_execute_data *execute_data)
+static zend_never_inline ZEND_COLD void zval_undefined_cv(uint32_t var, const zend_execute_data *execute_data)
{
- zend_string *cv;
+ zend_string *cv = CV_DEF_OF(EX_VAR_TO_NUM(var));
+
+ zend_error(E_NOTICE, "Undefined variable: %s", ZSTR_VAL(cv));
+}
+static zend_never_inline zval *_get_zval_cv_lookup(zval *ptr, uint32_t var, int type, const zend_execute_data *execute_data)
+{
switch (type) {
case BP_VAR_R:
case BP_VAR_UNSET:
- cv = CV_DEF_OF(EX_VAR_TO_NUM(var));
- zend_error(E_NOTICE, "Undefined variable: %s", ZSTR_VAL(cv));
+ zval_undefined_cv(var, execute_data);
/* break missing intentionally */
case BP_VAR_IS:
ptr = &EG(uninitialized_zval);
break;
case BP_VAR_RW:
- cv = CV_DEF_OF(EX_VAR_TO_NUM(var));
- zend_error(E_NOTICE, "Undefined variable: %s", ZSTR_VAL(cv));
+ zval_undefined_cv(var, execute_data);
/* break missing intentionally */
case BP_VAR_W:
ZVAL_NULL(ptr);
@@ -247,26 +239,20 @@ static zend_never_inline zval *_get_zval_cv_lookup(zval *ptr, uint32_t var, int
static zend_always_inline zval *_get_zval_cv_lookup_BP_VAR_R(zval *ptr, uint32_t var, const zend_execute_data *execute_data)
{
- zend_string *cv = CV_DEF_OF(EX_VAR_TO_NUM(var));
-
- zend_error(E_NOTICE, "Undefined variable: %s", ZSTR_VAL(cv));
+ zval_undefined_cv(var, execute_data);
return &EG(uninitialized_zval);
}
static zend_always_inline zval *_get_zval_cv_lookup_BP_VAR_UNSET(zval *ptr, uint32_t var, const zend_execute_data *execute_data)
{
- zend_string *cv = CV_DEF_OF(EX_VAR_TO_NUM(var));
-
- zend_error(E_NOTICE, "Undefined variable: %s", ZSTR_VAL(cv));
+ zval_undefined_cv(var, execute_data);
return &EG(uninitialized_zval);
}
static zend_always_inline zval *_get_zval_cv_lookup_BP_VAR_RW(zval *ptr, uint32_t var, const zend_execute_data *execute_data)
{
- zend_string *cv = CV_DEF_OF(EX_VAR_TO_NUM(var));
-
ZVAL_NULL(ptr);
- zend_error(E_NOTICE, "Undefined variable: %s", ZSTR_VAL(cv));
+ zval_undefined_cv(var, execute_data);
return ptr;
}
@@ -400,6 +386,11 @@ static zend_always_inline zval *_get_zval_ptr_cv_undef_BP_VAR_RW(const zend_exec
return EX_VAR(var);
}
+static zend_always_inline zval *_get_zval_ptr_cv_undef_BP_VAR_UNSET(const zend_execute_data *execute_data, uint32_t var)
+{
+ return EX_VAR(var);
+}
+
static zend_always_inline zval *_get_zval_ptr_cv_deref_BP_VAR_W(const zend_execute_data *execute_data, uint32_t var)
{
zval *ret = EX_VAR(var);
@@ -432,6 +423,27 @@ static zend_always_inline zval *_get_zval_ptr(int op_type, znode_op node, const
}
}
+static zend_always_inline zval *_get_zval_ptr_r(int op_type, znode_op node, const zend_execute_data *execute_data, zend_free_op *should_free)
+{
+ if (op_type & (IS_TMP_VAR|IS_VAR)) {
+ if (op_type == IS_TMP_VAR) {
+ return _get_zval_ptr_tmp(node.var, execute_data, should_free);
+ } else {
+ ZEND_ASSERT(op_type == IS_VAR);
+ return _get_zval_ptr_var(node.var, execute_data, should_free);
+ }
+ } else {
+ *should_free = NULL;
+ if (op_type == IS_CONST) {
+ return EX_CONSTANT(node);
+ } else if (op_type == IS_CV) {
+ return _get_zval_ptr_cv_BP_VAR_R(execute_data, node.var);
+ } else {
+ return NULL;
+ }
+ }
+}
+
static zend_always_inline zval *_get_zval_ptr_deref(int op_type, znode_op node, const zend_execute_data *execute_data, zend_free_op *should_free, int type)
{
if (op_type & (IS_TMP_VAR|IS_VAR)) {
@@ -453,6 +465,27 @@ static zend_always_inline zval *_get_zval_ptr_deref(int op_type, znode_op node,
}
}
+static zend_always_inline zval *_get_zval_ptr_r_deref(int op_type, znode_op node, const zend_execute_data *execute_data, zend_free_op *should_free)
+{
+ if (op_type & (IS_TMP_VAR|IS_VAR)) {
+ if (op_type == IS_TMP_VAR) {
+ return _get_zval_ptr_tmp(node.var, execute_data, should_free);
+ } else {
+ ZEND_ASSERT(op_type == IS_VAR);
+ return _get_zval_ptr_var_deref(node.var, execute_data, should_free);
+ }
+ } else {
+ *should_free = NULL;
+ if (op_type == IS_CONST) {
+ return EX_CONSTANT(node);
+ } else if (op_type == IS_CV) {
+ return _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, node.var);
+ } else {
+ return NULL;
+ }
+ }
+}
+
static zend_always_inline zval *_get_zval_ptr_undef(int op_type, znode_op node, const zend_execute_data *execute_data, zend_free_op *should_free, int type)
{
if (op_type & (IS_TMP_VAR|IS_VAR)) {
@@ -481,13 +514,8 @@ static zend_always_inline zval *_get_zval_ptr_ptr_var(uint32_t var, const zend_e
if (EXPECTED(Z_TYPE_P(ret) == IS_INDIRECT)) {
*should_free = NULL;
ret = Z_INDIRECT_P(ret);
- } else if (!Z_REFCOUNTED_P(ret)) {
- *should_free = ret; /* immutable array may be converted to regular */
- } else if (Z_REFCOUNT_P(ret) == 1) {
- *should_free = ret;
} else {
- *should_free = NULL;
- Z_DELREF_P(ret);
+ *should_free = ret;
}
return ret;
}
@@ -568,7 +596,7 @@ static inline int make_real_object(zval *object)
return 1;
}
-ZEND_API char * zend_verify_internal_arg_class_kind(const zend_internal_arg_info *cur_arg_info, char **class_name, zend_class_entry **pce)
+static char * zend_verify_internal_arg_class_kind(const zend_internal_arg_info *cur_arg_info, char **class_name, zend_class_entry **pce)
{
zend_string *key;
ALLOCA_FLAG(use_heap);
@@ -590,7 +618,7 @@ static zend_always_inline zend_class_entry* zend_verify_arg_class_kind(const zen
return zend_fetch_class(cur_arg_info->class_name, (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
}
-ZEND_API void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, const char *need_msg, const char *need_kind, const char *given_msg, const char *given_kind, zval *arg)
+static ZEND_COLD void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, const char *need_msg, const char *need_kind, const char *given_msg, const char *given_kind)
{
zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data;
const char *fname = ZSTR_VAL(zf->common.function_name);
@@ -618,19 +646,19 @@ ZEND_API void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, c
}
}
-static int is_null_constant(zval *default_value)
+static int is_null_constant(zend_class_entry *scope, zval *default_value)
{
if (Z_CONSTANT_P(default_value)) {
zval constant;
- ZVAL_COPY_VALUE(&constant, default_value);
- if (UNEXPECTED(zval_update_constant_ex(&constant, 0, NULL) != SUCCESS)) {
+ ZVAL_COPY(&constant, default_value);
+ if (UNEXPECTED(zval_update_constant_ex(&constant, scope) != SUCCESS)) {
return 0;
}
if (Z_TYPE(constant) == IS_NULL) {
return 1;
}
- zval_dtor(&constant);
+ zval_ptr_dtor(&constant);
}
return 0;
}
@@ -696,7 +724,7 @@ static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, z
return zend_verify_weak_scalar_type_hint(type_hint, arg);
}
-static void zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zval *arg)
+static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zval *arg)
{
zend_internal_arg_info *cur_arg_info;
char *need_msg, *class_name;
@@ -707,7 +735,7 @@ static void zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, z
} else if (zf->internal_function.fn_flags & ZEND_ACC_VARIADIC) {
cur_arg_info = &zf->internal_function.arg_info[zf->internal_function.num_args];
} else {
- return;
+ return 1;
}
if (cur_arg_info->type_hint) {
@@ -716,25 +744,47 @@ static void zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, z
if (cur_arg_info->class_name) {
need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce);
if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce)) {
- zend_verify_arg_error(zf, arg_num, need_msg, class_name, "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name), arg);
+ zend_verify_arg_error(zf, arg_num, need_msg, class_name, "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name));
+ return 0;
}
}
} else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) {
if (cur_arg_info->class_name) {
need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce);
- zend_verify_arg_error(zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "", arg);
+ zend_verify_arg_error(zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "");
+ return 0;
} else if (cur_arg_info->type_hint == IS_CALLABLE) {
if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) {
- zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), "", arg);
+ zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), "");
+ return 0;
}
} else if (cur_arg_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) {
/* pass */
} else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
- zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "", arg);
+ zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "");
+ return 0;
}
}
}
+ return 1;
+}
+
+static zend_never_inline int zend_verify_internal_arg_types(zend_function *fbc, zend_execute_data *call)
+{
+ uint32_t i;
+ uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
+ zval *p = ZEND_CALL_ARG(call, 1);
+
+ for (i = 0; i < num_args; ++i) {
+ if (UNEXPECTED(!zend_verify_internal_arg_type(fbc, i + 1, p))) {
+ EG(current_execute_data) = call->prev_execute_data;
+ zend_vm_stack_free_args(call);
+ return 0;
+ }
+ p++;
+ }
+ return 1;
}
static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot)
@@ -760,7 +810,7 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
} else {
ce = zend_verify_arg_class_kind(cur_arg_info);
if (UNEXPECTED(!ce)) {
- zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name), arg);
+ zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name));
return 0;
}
*cache_slot = (void*)ce;
@@ -769,11 +819,11 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
need_msg =
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
"implement interface " : "be an instance of ";
- zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name), arg);
+ zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name));
return 0;
}
}
- } else if (Z_TYPE_P(arg) != IS_NULL || !(cur_arg_info->allow_null || (default_value && is_null_constant(default_value)))) {
+ } else if (Z_TYPE_P(arg) != IS_NULL || !(cur_arg_info->allow_null || (default_value && is_null_constant(zf->common.scope, default_value)))) {
if (cur_arg_info->class_name) {
if (EXPECTED(*cache_slot)) {
ce = (zend_class_entry*)*cache_slot;
@@ -781,9 +831,9 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
ce = zend_verify_arg_class_kind(cur_arg_info);
if (UNEXPECTED(!ce)) {
if (Z_TYPE_P(arg) == IS_OBJECT) {
- zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name), arg);
+ zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name));
} else {
- zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "", zend_zval_type_name(arg), arg);
+ zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "", zend_zval_type_name(arg));
}
return 0;
}
@@ -792,18 +842,18 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
need_msg =
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
"implement interface " : "be an instance of ";
- zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), zend_zval_type_name(arg), "", arg);
+ zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), zend_zval_type_name(arg), "");
return 0;
} else if (cur_arg_info->type_hint == IS_CALLABLE) {
if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) {
- zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), "", arg);
+ zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), "");
return 0;
}
} else if (cur_arg_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) {
/* pass */
} else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_ARG_USES_STRICT_TYPES()))) {
- zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "", arg);
+ zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "");
return 0;
}
}
@@ -832,7 +882,7 @@ static zend_always_inline int zend_verify_missing_arg_type(zend_function *zf, ui
} else {
ce = zend_verify_arg_class_kind(cur_arg_info);
if (UNEXPECTED(!ce)) {
- zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "none", "", NULL);
+ zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "none", "");
return 0;
}
*cache_slot = (void*)ce;
@@ -840,21 +890,21 @@ static zend_always_inline int zend_verify_missing_arg_type(zend_function *zf, ui
need_msg =
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
"implement interface " : "be an instance of ";
- zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "none", "", NULL);
+ zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "none", "");
} else if (cur_arg_info->type_hint == IS_CALLABLE) {
- zend_verify_arg_error(zf, arg_num, "be callable", "", "none", "", NULL);
+ zend_verify_arg_error(zf, arg_num, "be callable", "", "none", "");
} else {
- zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), "none", "", NULL);
+ zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), "none", "");
}
return 0;
}
return 1;
}
-static zend_always_inline int zend_verify_missing_arg(zend_execute_data *execute_data, uint32_t arg_num, void **cache_slot)
+static ZEND_COLD void zend_verify_missing_arg(zend_execute_data *execute_data, uint32_t arg_num, void **cache_slot)
{
if (EXPECTED(!(EX(func)->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) ||
- zend_verify_missing_arg_type(EX(func), arg_num, cache_slot)) {
+ UNEXPECTED(zend_verify_missing_arg_type(EX(func), arg_num, cache_slot))) {
const char *class_name = EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "";
const char *space = EX(func)->common.scope ? "::" : "";
const char *func_name = EX(func)->common.function_name ? ZSTR_VAL(EX(func)->common.function_name) : "main";
@@ -865,12 +915,10 @@ static zend_always_inline int zend_verify_missing_arg(zend_execute_data *execute
} else {
zend_error(E_WARNING, "Missing argument %u for %s%s%s()", arg_num, class_name, space, func_name);
}
- return 1;
}
- return 0;
}
-ZEND_API void zend_verify_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind)
+static ZEND_COLD void zend_verify_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind)
{
const char *fname = ZSTR_VAL(zf->common.function_name);
const char *fsep;
@@ -884,17 +932,12 @@ ZEND_API void zend_verify_return_error(const zend_function *zf, const char *need
fclass = "";
}
- if (zf->common.type == ZEND_USER_FUNCTION) {
- zend_type_error("Return value of %s%s%s() must %s%s, %s%s returned in %s on line %d",
- fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind,
- ZSTR_VAL(zf->op_array.filename), EG(current_execute_data)->opline->lineno);
- } else {
- zend_type_error("Return value of %s%s%s() must %s%s, %s%s returned",
- fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind);
- }
+ zend_type_error("Return value of %s%s%s() must %s%s, %s%s returned",
+ fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind);
}
-ZEND_API void zend_verify_internal_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind)
+#if ZEND_DEBUG
+static ZEND_COLD void zend_verify_internal_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind)
{
const char *fname = ZSTR_VAL(zf->common.function_name);
const char *fsep;
@@ -912,7 +955,24 @@ ZEND_API void zend_verify_internal_return_error(const zend_function *zf, const c
fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind);
}
-#if ZEND_DEBUG
+static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, const char *returned_msg, const char *returned_kind)
+{
+ const char *fname = ZSTR_VAL(zf->common.function_name);
+ const char *fsep;
+ const char *fclass;
+
+ if (zf->common.scope) {
+ fsep = "::";
+ fclass = ZSTR_VAL(zf->common.scope->name);
+ } else {
+ fsep = "";
+ fclass = "";
+ }
+
+ zend_type_error("%s%s%s() must not return a value, %s%s returned",
+ fclass, fsep, fname, returned_msg, returned_kind);
+}
+
static int zend_verify_internal_return_type(zend_function *zf, zval *ret)
{
zend_arg_info *ret_info = zf->common.arg_info - 1;
@@ -941,6 +1001,8 @@ static int zend_verify_internal_return_type(zend_function *zf, zval *ret)
} else if (ret_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(ret) == IS_FALSE || Z_TYPE_P(ret) == IS_TRUE)) {
/* pass */
+ } else if (ret_info->type_hint == IS_VOID) {
+ zend_verify_void_return_error(zf, zend_zval_type_name(ret), "");
} else {
/* Use strict check to verify return value of internal function */
zend_verify_internal_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), zend_zval_type_name(ret), "");
@@ -1001,6 +1063,12 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *
} else if (ret_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(ret) == IS_FALSE || Z_TYPE_P(ret) == IS_TRUE)) {
/* pass */
+ /* There would be a check here for the IS_VOID type hint, which
+ * would trigger an error because a value had been returned.
+ * However, zend_compile.c already does a compile-time check
+ * that bans `return ...;` within a void function. Thus we can skip
+ * this part of the runtime check for non-internal functions.
+ */
} else if (UNEXPECTED(!zend_verify_scalar_type_hint(ret_info->type_hint, ret, ZEND_RET_USES_STRICT_TYPES()))) {
zend_verify_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), zend_zval_type_name(ret), "");
}
@@ -1008,13 +1076,13 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *
}
}
-static zend_always_inline int zend_verify_missing_return_type(zend_function *zf, void **cache_slot)
+static ZEND_COLD int zend_verify_missing_return_type(zend_function *zf, void **cache_slot)
{
zend_arg_info *ret_info = zf->common.arg_info - 1;
char *need_msg;
zend_class_entry *ce;
- if (ret_info->type_hint) {
+ if (ret_info->type_hint && EXPECTED(ret_info->type_hint != IS_VOID)) {
if (ret_info->class_name) {
if (EXPECTED(*cache_slot)) {
ce = (zend_class_entry*)*cache_slot;
@@ -1041,190 +1109,14 @@ static zend_always_inline int zend_verify_missing_return_type(zend_function *zf,
return 1;
}
-static zend_always_inline void zend_assign_to_object(zval *retval, zval *object, uint32_t object_op_type, zval *property_name, uint32_t property_op_type, int value_type, znode_op value_op, const zend_execute_data *execute_data, void **cache_slot)
-{
- zend_free_op free_value;
- zval *value = get_zval_ptr(value_type, value_op, execute_data, &free_value, BP_VAR_R);
- zval tmp;
-
- if (object_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
- do {
- if (object_op_type == IS_VAR && UNEXPECTED(object == &EG(error_zval))) {
- if (retval) {
- ZVAL_NULL(retval);
- }
- FREE_OP(free_value);
- return;
- }
- if (Z_ISREF_P(object)) {
- object = Z_REFVAL_P(object);
- if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
- break;
- }
- }
- if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
- (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
- zend_object *obj;
-
- zval_ptr_dtor(object);
- object_init(object);
- Z_ADDREF_P(object);
- obj = Z_OBJ_P(object);
- zend_error(E_WARNING, "Creating default object from empty value");
- if (GC_REFCOUNT(obj) == 1) {
- /* the enclosing container was deleted, obj is unreferenced */
- if (retval) {
- ZVAL_NULL(retval);
- }
- FREE_OP(free_value);
- OBJ_RELEASE(obj);
- return;
- }
- Z_DELREF_P(object);
- } else {
- zend_error(E_WARNING, "Attempt to assign property of non-object");
- if (retval) {
- ZVAL_NULL(retval);
- }
- FREE_OP(free_value);
- return;
- }
- } while (0);
- }
-
- if (property_op_type == IS_CONST &&
- EXPECTED(Z_OBJCE_P(object) == CACHED_PTR_EX(cache_slot))) {
- uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1);
- zend_object *zobj = Z_OBJ_P(object);
- zval *property;
-
- if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
- property = OBJ_PROP(zobj, prop_offset);
- if (Z_TYPE_P(property) != IS_UNDEF) {
-fast_assign:
- value = zend_assign_to_variable(property, value, value_type);
- if (retval && EXPECTED(!EG(exception))) {
- ZVAL_COPY(retval, value);
- }
- return;
- }
- } else {
- if (EXPECTED(zobj->properties != NULL)) {
- property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
- if (property) {
- goto fast_assign;
- }
- }
-
- if (!zobj->ce->__set) {
-
- if (EXPECTED(zobj->properties == NULL)) {
- rebuild_object_properties(zobj);
- }
- /* separate our value if necessary */
- if (value_type == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- ZVAL_COPY_VALUE(&tmp, value);
- zval_copy_ctor_func(&tmp);
- value = &tmp;
- }
- } else if (value_type != IS_TMP_VAR) {
- if (Z_ISREF_P(value)) {
- if (value_type == IS_VAR) {
- zend_reference *ref = Z_REF_P(value);
- if (--(GC_REFCOUNT(ref)) == 0) {
- ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
- efree_size(ref, sizeof(zend_reference));
- value = &tmp;
- } else {
- value = Z_REFVAL_P(value);
- if (Z_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
- }
- }
- } else {
- value = Z_REFVAL_P(value);
- if (Z_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
- }
- }
- } else if (value_type == IS_CV && Z_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
- }
- }
- zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
- if (retval) {
- ZVAL_COPY(retval, value);
- }
- return;
- }
- }
- }
-
- if (!Z_OBJ_HT_P(object)->write_property) {
- zend_error(E_WARNING, "Attempt to assign property of non-object");
- if (retval) {
- ZVAL_NULL(retval);
- }
- FREE_OP(free_value);
- return;
- }
-
- /* separate our value if necessary */
- if (value_type == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- ZVAL_COPY_VALUE(&tmp, value);
- zval_copy_ctor_func(&tmp);
- value = &tmp;
- }
- } else if (value_type != IS_TMP_VAR) {
- ZVAL_DEREF(value);
- }
-
- Z_OBJ_HT_P(object)->write_property(object, property_name, value, cache_slot);
-
- if (retval && EXPECTED(!EG(exception))) {
- ZVAL_COPY(retval, value);
- }
- if (value_type == IS_CONST) {
- zval_ptr_dtor_nogc(value);
- } else {
- FREE_OP(free_value);
- }
-}
-
-static zend_never_inline void zend_assign_to_object_dim(zval *retval, zval *object, zval *property_name, int value_type, znode_op value_op, const zend_execute_data *execute_data)
+static zend_never_inline void zend_assign_to_object_dim(zval *object, zval *dim, zval *value)
{
- zend_free_op free_value;
- zval *value = get_zval_ptr_deref(value_type, value_op, execute_data, &free_value, BP_VAR_R);
- zval tmp;
-
- /* Note: property_name in this case is really the array index! */
- if (!Z_OBJ_HT_P(object)->write_dimension) {
- zend_throw_error(zend_ce_error, "Cannot use object as array");
- FREE_OP(free_value);
+ if (UNEXPECTED(!Z_OBJ_HT_P(object)->write_dimension)) {
+ zend_throw_error(NULL, "Cannot use object as array");
return;
}
- /* separate our value if necessary */
- if (value_type == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- ZVAL_COPY_VALUE(&tmp, value);
- zval_copy_ctor_func(&tmp);
- value = &tmp;
- }
- }
-
- Z_OBJ_HT_P(object)->write_dimension(object, property_name, value);
-
- if (retval && EXPECTED(!EG(exception))) {
- ZVAL_COPY(retval, value);
- }
- if (value_type == IS_CONST) {
- zval_ptr_dtor_nogc(value);
- } else {
- FREE_OP(free_value);
- }
+ Z_OBJ_HT_P(object)->write_dimension(object, dim, value);
}
static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval *property, zval *value, zval *retval, binary_op_type binary_op)
@@ -1261,48 +1153,221 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval *
}
}
-static void zend_assign_to_string_offset(zval *str, zend_long offset, zval *value, zval *result)
+static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type)
+{
+ zend_long offset;
+
+try_again:
+ if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
+ switch(Z_TYPE_P(dim)) {
+ case IS_STRING:
+ if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, -1)) {
+ break;
+ }
+ if (type != BP_VAR_UNSET) {
+ zend_error(E_WARNING, "Illegal string offset '%s'", Z_STRVAL_P(dim));
+ }
+ break;
+ case IS_UNDEF:
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
+ case IS_DOUBLE:
+ case IS_NULL:
+ case IS_FALSE:
+ case IS_TRUE:
+ zend_error(E_NOTICE, "String offset cast occurred");
+ break;
+ case IS_REFERENCE:
+ dim = Z_REFVAL_P(dim);
+ goto try_again;
+ default:
+ zend_error(E_WARNING, "Illegal offset type");
+ break;
+ }
+
+ offset = _zval_get_long_func(dim);
+ } else {
+ offset = Z_LVAL_P(dim);
+ }
+
+ return offset;
+}
+
+static zend_never_inline ZEND_COLD void zend_wrong_string_offset(void)
+{
+ const char *msg = NULL;
+ const zend_op *opline = EG(current_execute_data)->opline;
+ const zend_op *end;
+ uint32_t var;
+
+ switch (opline->opcode) {
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_CONCAT:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_ASSIGN_POW:
+ msg = "Cannot use assign-op operators with string offsets";
+ break;
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ /* TODO: Encode the "reason" into opline->extended_value??? */
+ var = opline->result.var;
+ opline++;
+ end = EG(current_execute_data)->func->op_array.opcodes +
+ EG(current_execute_data)->func->op_array.last;
+ while (opline < end) {
+ if (opline->op1_type == IS_VAR && opline->op1.var == var) {
+ switch (opline->opcode) {
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_CONCAT:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_ASSIGN_POW:
+ if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+ msg = "Cannot use string offset as an object";
+ } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
+ msg = "Cannot use string offset as an array";
+ } else {
+ msg = "Cannot use assign-op operators with string offsets";
+ }
+ break;
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ msg = "Cannot increment/decrement string offsets";
+ break;
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_ASSIGN_DIM:
+ msg = "Cannot use string offset as an array";
+ break;
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_ASSIGN_OBJ:
+ msg = "Cannot use string offset as an object";
+ break;
+ case ZEND_ASSIGN_REF:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_INIT_ARRAY:
+ msg = "Cannot create references to/from string offsets";
+ break;
+ case ZEND_RETURN_BY_REF:
+ msg = "Cannot return string offsets by reference";
+ break;
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ msg = "Cannot unset string offsets";
+ break;
+ case ZEND_YIELD:
+ msg = "Cannot yield string offsets by reference";
+ break;
+ case ZEND_SEND_REF:
+ case ZEND_SEND_VAR_EX:
+ msg = "Only variables can be passed by reference";
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE();
+ }
+ break;
+ }
+ if (opline->op2_type == IS_VAR && opline->op2.var == var) {
+ ZEND_ASSERT(opline->opcode == ZEND_ASSIGN_REF);
+ msg = "Cannot create references to/from string offsets";
+ break;
+ }
+ }
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE();
+ }
+ ZEND_ASSERT(msg != NULL);
+ zend_throw_error(NULL, msg);
+}
+
+static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value, zval *result)
{
zend_string *old_str;
+ zend_uchar c;
+ size_t string_len;
+ zend_long offset;
- if (offset < 0) {
+ offset = zend_check_string_offset(dim, BP_VAR_W);
+ if (offset < (zend_long)(-Z_STRLEN_P(str))) {
+ /* Error on negative offset */
zend_error(E_WARNING, "Illegal string offset: " ZEND_LONG_FMT, offset);
- zend_string_release(Z_STR_P(str));
if (result) {
ZVAL_NULL(result);
}
return;
}
- old_str = Z_STR_P(str);
+ if (Z_TYPE_P(value) != IS_STRING) {
+ /* Convert to string, just the time to pick the 1st byte */
+ zend_string *tmp = zval_get_string(value);
+
+ string_len = ZSTR_LEN(tmp);
+ c = (zend_uchar)ZSTR_VAL(tmp)[0];
+ zend_string_release(tmp);
+ } else {
+ string_len = Z_STRLEN_P(value);
+ c = (zend_uchar)Z_STRVAL_P(value)[0];
+ }
+
+ if (string_len == 0) {
+ /* Error on empty input string */
+ zend_error(E_WARNING, "Cannot assign an empty string to a string offset");
+ if (result) {
+ ZVAL_NULL(result);
+ }
+ return;
+ }
+
+ if (offset < 0) { /* Handle negative offset */
+ offset += (zend_long)Z_STRLEN_P(str);
+ }
+
if ((size_t)offset >= Z_STRLEN_P(str)) {
+ /* Extend string if needed */
zend_long old_len = Z_STRLEN_P(str);
Z_STR_P(str) = zend_string_extend(Z_STR_P(str), offset + 1, 0);
Z_TYPE_INFO_P(str) = IS_STRING_EX;
memset(Z_STRVAL_P(str) + old_len, ' ', offset - old_len);
Z_STRVAL_P(str)[offset+1] = 0;
} else if (!Z_REFCOUNTED_P(str)) {
+ old_str = Z_STR_P(str);
Z_STR_P(str) = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0);
Z_TYPE_INFO_P(str) = IS_STRING_EX;
- }
-
- if (Z_TYPE_P(value) != IS_STRING) {
- zend_string *tmp = zval_get_string(value);
-
- Z_STRVAL_P(str)[offset] = ZSTR_VAL(tmp)[0];
- zend_string_release(tmp);
+ zend_string_release(old_str);
} else {
- Z_STRVAL_P(str)[offset] = Z_STRVAL_P(value)[0];
+ SEPARATE_STRING(str);
}
- /*
- * the value of an assignment to a string offset is undefined
- T(result->u.var).var = &T->str_offset.str;
- */
- zend_string_release(old_str);
- if (result) {
- zend_uchar c = (zend_uchar)Z_STRVAL_P(str)[offset];
+ Z_STRVAL_P(str)[offset] = c;
+ if (result) {
+ /* Return the new character */
if (CG(one_char_string)[c]) {
ZVAL_INTERNED_STR(result, CG(one_char_string)[c]);
} else {
@@ -1334,14 +1399,18 @@ static zend_never_inline void zend_post_incdec_overloaded_property(zval *object,
}
ZVAL_COPY_VALUE(z, value);
}
- ZVAL_COPY(result, z);
- ZVAL_DUP(&z_copy, z);
+
+ if (UNEXPECTED(Z_TYPE_P(z) == IS_REFERENCE)) {
+ ZVAL_COPY(result, Z_REFVAL_P(z));
+ } else {
+ ZVAL_COPY(result, z);
+ }
+ ZVAL_DUP(&z_copy, result);
if (inc) {
increment_function(&z_copy);
} else {
decrement_function(&z_copy);
}
- if (Z_REFCOUNTED_P(z)) Z_ADDREF_P(z);
Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, cache_slot);
OBJ_RELEASE(Z_OBJ(obj));
zval_ptr_dtor(&z_copy);
@@ -1405,8 +1474,8 @@ static zend_never_inline void zend_assign_op_overloaded_property(zval *object, z
ZVAL_OBJ(&obj, Z_OBJ_P(object));
Z_ADDREF(obj);
- if (Z_OBJ_HT(obj)->read_property &&
- (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv)) != NULL) {
+ if (EXPECTED(Z_OBJ_HT(obj)->read_property)) {
+ z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv);
if (UNEXPECTED(EG(exception))) {
OBJ_RELEASE(Z_OBJ(obj));
return;
@@ -1439,26 +1508,26 @@ static zend_never_inline void zend_assign_op_overloaded_property(zval *object, z
}
/* Utility Functions for Extensions */
-static void zend_extension_statement_handler(const zend_extension *extension, zend_op_array *op_array)
+static void zend_extension_statement_handler(const zend_extension *extension, zend_execute_data *frame)
{
if (extension->statement_handler) {
- extension->statement_handler(op_array);
+ extension->statement_handler(frame);
}
}
-static void zend_extension_fcall_begin_handler(const zend_extension *extension, zend_op_array *op_array)
+static void zend_extension_fcall_begin_handler(const zend_extension *extension, zend_execute_data *frame)
{
if (extension->fcall_begin_handler) {
- extension->fcall_begin_handler(op_array);
+ extension->fcall_begin_handler(frame);
}
}
-static void zend_extension_fcall_end_handler(const zend_extension *extension, zend_op_array *op_array)
+static void zend_extension_fcall_end_handler(const zend_extension *extension, zend_execute_data *frame)
{
if (extension->fcall_end_handler) {
- extension->fcall_end_handler(op_array);
+ extension->fcall_end_handler(frame);
}
}
@@ -1470,18 +1539,9 @@ static zend_always_inline HashTable *zend_get_target_symbol_table(zend_execute_d
if (EXPECTED(fetch_type == ZEND_FETCH_GLOBAL_LOCK) ||
EXPECTED(fetch_type == ZEND_FETCH_GLOBAL)) {
ht = &EG(symbol_table);
- } else if (EXPECTED(fetch_type == ZEND_FETCH_STATIC)) {
- ZEND_ASSERT(EX(func)->op_array.static_variables != NULL);
- ht = EX(func)->op_array.static_variables;
- if (GC_REFCOUNT(ht) > 1) {
- if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
- GC_REFCOUNT(ht)--;
- }
- EX(func)->op_array.static_variables = ht = zend_array_dup(ht);
- }
} else {
ZEND_ASSERT(fetch_type == ZEND_FETCH_LOCAL);
- if (!EX(symbol_table)) {
+ if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) {
zend_rebuild_symbol_table();
}
ht = EX(symbol_table);
@@ -1499,23 +1559,24 @@ try_again:
if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
hval = Z_LVAL_P(dim);
num_index:
- retval = zend_hash_index_find(ht, hval);
- if (retval == NULL) {
- switch (type) {
- case BP_VAR_R:
- zend_error(E_NOTICE,"Undefined offset: " ZEND_LONG_FMT, hval);
- /* break missing intentionally */
- case BP_VAR_UNSET:
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined offset: " ZEND_LONG_FMT, hval);
- /* break missing intentionally */
- case BP_VAR_W:
- retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval));
- break;
- }
+ ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
+ return retval;
+num_undef:
+ switch (type) {
+ case BP_VAR_R:
+ zend_error(E_NOTICE,"Undefined offset: " ZEND_LONG_FMT, hval);
+ /* break missing intentionally */
+ case BP_VAR_UNSET:
+ case BP_VAR_IS:
+ retval = &EG(uninitialized_zval);
+ break;
+ case BP_VAR_RW:
+ zend_error(E_NOTICE,"Undefined offset: " ZEND_LONG_FMT, hval);
+ retval = zend_hash_index_update(ht, hval, &EG(uninitialized_zval));
+ break;
+ case BP_VAR_W:
+ retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval));
+ break;
}
} else if (EXPECTED(Z_TYPE_P(dim) == IS_STRING)) {
offset_key = Z_STR_P(dim);
@@ -1559,7 +1620,8 @@ str_index:
break;
case BP_VAR_RW:
zend_error(E_NOTICE,"Undefined index: %s", ZSTR_VAL(offset_key));
- /* break missing intentionally */
+ retval = zend_hash_update(ht, offset_key, &EG(uninitialized_zval));
+ break;
case BP_VAR_W:
retval = zend_hash_add_new(ht, offset_key, &EG(uninitialized_zval));
break;
@@ -1567,6 +1629,9 @@ str_index:
}
} else {
switch (Z_TYPE_P(dim)) {
+ case IS_UNDEF:
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
+ /* break missing intentionally */
case IS_NULL:
offset_key = ZSTR_EMPTY_ALLOC();
goto str_index;
@@ -1589,61 +1654,30 @@ str_index:
default:
zend_error(E_WARNING, "Illegal offset type");
retval = (type == BP_VAR_W || type == BP_VAR_RW) ?
- &EG(error_zval) : &EG(uninitialized_zval);
+ NULL : &EG(uninitialized_zval);
}
}
return retval;
}
-static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type)
+static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_W(HashTable *ht, const zval *dim)
{
- zend_long offset;
-
-try_again:
- if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
- switch(Z_TYPE_P(dim)) {
- case IS_STRING:
- if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, -1)) {
- break;
- }
- if (type != BP_VAR_UNSET) {
- zend_error(E_WARNING, "Illegal string offset '%s'", Z_STRVAL_P(dim));
- }
- break;
- case IS_DOUBLE:
- case IS_NULL:
- case IS_FALSE:
- case IS_TRUE:
- zend_error(E_NOTICE, "String offset cast occurred");
- break;
- case IS_REFERENCE:
- dim = Z_REFVAL_P(dim);
- goto try_again;
- default:
- zend_error(E_WARNING, "Illegal offset type");
- break;
- }
-
- offset = zval_get_long(dim);
- } else {
- offset = Z_LVAL_P(dim);
- }
+ return zend_fetch_dimension_address_inner(ht, dim, IS_TMP_VAR, BP_VAR_W);
+}
- return offset;
+static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_W_CONST(HashTable *ht, const zval *dim)
+{
+ return zend_fetch_dimension_address_inner(ht, dim, IS_CONST, BP_VAR_W);
}
-static zend_always_inline zend_long zend_fetch_string_offset(zval *container, zval *dim, int type)
+static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_RW(HashTable *ht, const zval *dim)
{
- zend_long offset = zend_check_string_offset(dim, type);
+ return zend_fetch_dimension_address_inner(ht, dim, IS_TMP_VAR, BP_VAR_RW);
+}
- if (Z_REFCOUNTED_P(container)) {
- if (Z_REFCOUNT_P(container) > 1) {
- Z_DELREF_P(container);
- zval_copy_ctor_func(container);
- }
- Z_ADDREF_P(container);
- }
- return offset;
+static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_RW_CONST(HashTable *ht, const zval *dim)
+{
+ return zend_fetch_dimension_address_inner(ht, dim, IS_CONST, BP_VAR_RW);
}
static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *container, zval *dim, int dim_type, int type)
@@ -1658,10 +1692,15 @@ fetch_from_array:
retval = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
if (UNEXPECTED(retval == NULL)) {
zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
- retval = &EG(error_zval);
+ ZVAL_ERROR(result);
+ return;
}
} else {
retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type);
+ if (UNEXPECTED(!retval)) {
+ ZVAL_ERROR(result);
+ return;
+ }
}
ZVAL_INDIRECT(result, retval);
return;
@@ -1671,26 +1710,22 @@ fetch_from_array:
goto try_array;
}
}
- if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) {
- if (type != BP_VAR_UNSET && UNEXPECTED(Z_STRLEN_P(container) == 0)) {
- zval_ptr_dtor_nogc(container);
-convert_to_array:
- ZVAL_NEW_ARR(container);
- zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
- goto fetch_from_array;
- }
-
+ if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
if (dim == NULL) {
- zend_throw_error(zend_ce_error, "[] operator not supported for strings");
- ZVAL_NULL(result);
+ zend_throw_error(NULL, "[] operator not supported for strings");
} else {
zend_check_string_offset(dim, type);
- ZVAL_INDIRECT(result, NULL); /* wrong string offset */
+ zend_wrong_string_offset();
}
+ ZVAL_ERROR(result);
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ if (/*dim_type == IS_CV &&*/ dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
+ dim = &EG(uninitialized_zval);
+ }
if (!Z_OBJ_HT_P(container)->read_dimension) {
- zend_throw_error(zend_ce_error, "Cannot use object as array");
- retval = &EG(error_zval);
+ zend_throw_error(NULL, "Cannot use object as array");
+ ZVAL_ERROR(result);
} else {
retval = Z_OBJ_HT_P(container)->read_dimension(container, dim, type, result);
@@ -1708,7 +1743,7 @@ convert_to_array:
ZVAL_DUP(result, retval);
retval = result;
} else {
- ZVAL_COPY(result, retval);
+ ZVAL_COPY_VALUE(result, retval);
retval = result;
}
}
@@ -1716,30 +1751,42 @@ convert_to_array:
zend_class_entry *ce = Z_OBJCE_P(container);
zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
}
+ } else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) {
+ ZVAL_UNREF(retval);
}
if (result != retval) {
ZVAL_INDIRECT(result, retval);
}
} else {
- ZVAL_INDIRECT(result, &EG(error_zval));
+ ZVAL_ERROR(result);
}
}
- } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
- if (UNEXPECTED(container == &EG(error_zval))) {
- ZVAL_INDIRECT(result, &EG(error_zval));
- } else if (type != BP_VAR_UNSET) {
- goto convert_to_array;
- } else {
- /* for read-mode only */
- ZVAL_NULL(result);
- }
} else {
- if (type == BP_VAR_UNSET) {
- zend_error(E_WARNING, "Cannot unset offset in a non-array variable");
- ZVAL_NULL(result);
+ if (type != BP_VAR_W && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
+ zval_undefined_cv(EG(current_execute_data)->opline->op1.var, EG(current_execute_data));
+ }
+ if (/*dim_type == IS_CV &&*/ dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
+ }
+ if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
+ if (type != BP_VAR_UNSET) {
+ ZVAL_NEW_ARR(container);
+ zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
+ goto fetch_from_array;
+ } else {
+ /* for read-mode only */
+ ZVAL_NULL(result);
+ }
+ } else if (EXPECTED(Z_ISERROR_P(container))) {
+ ZVAL_ERROR(result);
} else {
- zend_error(E_WARNING, "Cannot use a scalar value as an array");
- ZVAL_INDIRECT(result, &EG(error_zval));
+ if (type == BP_VAR_UNSET) {
+ zend_error(E_WARNING, "Cannot unset offset in a non-array variable");
+ ZVAL_NULL(result);
+ } else {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ ZVAL_ERROR(result);
+ }
}
}
}
@@ -1759,27 +1806,29 @@ static zend_never_inline void zend_fetch_dimension_address_UNSET(zval *result, z
zend_fetch_dimension_address(result, container_ptr, dim, dim_type, BP_VAR_UNSET);
}
-static zend_always_inline void zend_fetch_dimension_address_read(zval *result, zval *container, zval *dim, int dim_type, int type)
+static zend_always_inline void zend_fetch_dimension_address_read(zval *result, zval *container, zval *dim, int dim_type, int type, int support_strings, int slow)
{
zval *retval;
- if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
-try_array:
- retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type);
- ZVAL_COPY(result, retval);
- return;
- } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
- container = Z_REFVAL_P(container);
+ if (!slow) {
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
- goto try_array;
+try_array:
+ retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type);
+ ZVAL_COPY(result, retval);
+ return;
+ } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto try_array;
+ }
}
}
- if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) {
+ if (support_strings && EXPECTED(Z_TYPE_P(container) == IS_STRING)) {
zend_long offset;
try_string_offset:
if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
- switch(Z_TYPE_P(dim)) {
+ switch (Z_TYPE_P(dim)) {
/* case IS_LONG: */
case IS_STRING:
if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, -1)) {
@@ -1791,6 +1840,8 @@ try_string_offset:
}
zend_error(E_WARNING, "Illegal string offset '%s'", Z_STRVAL_P(dim));
break;
+ case IS_UNDEF:
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
case IS_DOUBLE:
case IS_NULL:
case IS_FALSE:
@@ -1807,12 +1858,12 @@ try_string_offset:
break;
}
- offset = zval_get_long(dim);
+ offset = _zval_get_long_func(dim);
} else {
offset = Z_LVAL_P(dim);
}
- if (UNEXPECTED(offset < 0) || UNEXPECTED(Z_STRLEN_P(container) <= (size_t)offset)) {
+ if (UNEXPECTED(Z_STRLEN_P(container) < (size_t)((offset < 0) ? -offset : (offset + 1)))) {
if (type != BP_VAR_IS) {
zend_error(E_NOTICE, "Uninitialized string offset: %pd", offset);
ZVAL_EMPTY_STRING(result);
@@ -1820,17 +1871,26 @@ try_string_offset:
ZVAL_NULL(result);
}
} else {
- zend_uchar c = (zend_uchar)Z_STRVAL_P(container)[offset];
+ zend_uchar c;
+ zend_long real_offset;
+
+ real_offset = (UNEXPECTED(offset < 0)) /* Handle negative offset */
+ ? (zend_long)Z_STRLEN_P(container) + offset : offset;
+ c = (zend_uchar)Z_STRVAL_P(container)[real_offset];
if (CG(one_char_string)[c]) {
ZVAL_INTERNED_STR(result, CG(one_char_string)[c]);
} else {
- ZVAL_NEW_STR(result, zend_string_init(Z_STRVAL_P(container) + offset, 1, 0));
+ ZVAL_NEW_STR(result, zend_string_init(Z_STRVAL_P(container) + real_offset, 1, 0));
}
}
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ if (/*dim_type == IS_CV &&*/ UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
+ dim = &EG(uninitialized_zval);
+ }
if (!Z_OBJ_HT_P(container)->read_dimension) {
- zend_throw_error(zend_ce_error, "Cannot use object as array");
+ zend_throw_error(NULL, "Cannot use object as array");
ZVAL_NULL(result);
} else {
retval = Z_OBJ_HT_P(container)->read_dimension(container, dim, type, result);
@@ -1845,18 +1905,34 @@ try_string_offset:
}
}
} else {
+ if (type != BP_VAR_IS && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
+ zval_undefined_cv(EG(current_execute_data)->opline->op1.var, EG(current_execute_data));
+ }
+ if (/*dim_type == IS_CV &&*/ UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
+ }
ZVAL_NULL(result);
}
}
static zend_never_inline void zend_fetch_dimension_address_read_R(zval *result, zval *container, zval *dim, int dim_type)
{
- zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_R);
+ zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_R, 1, 0);
+}
+
+static zend_never_inline void zend_fetch_dimension_address_read_R_slow(zval *result, zval *container, zval *dim)
+{
+ zend_fetch_dimension_address_read(result, container, dim, IS_CV, BP_VAR_R, 1, 1);
}
static zend_never_inline void zend_fetch_dimension_address_read_IS(zval *result, zval *container, zval *dim, int dim_type)
{
- zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS);
+ zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS, 1, 0);
+}
+
+static zend_never_inline void zend_fetch_dimension_address_read_LIST(zval *result, zval *container, zval *dim)
+{
+ zend_fetch_dimension_address_read(result, container, dim, IS_TMP_VAR, BP_VAR_R, 0, 0);
}
ZEND_API void zend_fetch_dimension_by_zval(zval *result, zval *container, zval *dim)
@@ -1864,15 +1940,16 @@ ZEND_API void zend_fetch_dimension_by_zval(zval *result, zval *container, zval *
zend_fetch_dimension_address_read_R(result, container, dim, IS_TMP_VAR);
}
+ZEND_API void zend_fetch_dimension_by_zval_is(zval *result, zval *container, zval *dim, int dim_type)
+{
+ zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS, 1, 0);
+}
+
+
static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type)
{
if (container_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) {
do {
- if (container_op_type == IS_VAR && UNEXPECTED(container == &EG(error_zval))) {
- ZVAL_INDIRECT(result, &EG(error_zval));
- return;
- }
-
if (Z_ISREF_P(container)) {
container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
@@ -1887,8 +1964,10 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
zval_ptr_dtor_nogc(container);
object_init(container);
} else {
- zend_error(E_WARNING, "Attempt to modify property of non-object");
- ZVAL_INDIRECT(result, &EG(error_zval));
+ if (container_op_type != IS_VAR || EXPECTED(!Z_ISERROR_P(container))) {
+ zend_error(E_WARNING, "Attempt to modify property of non-object");
+ }
+ ZVAL_ERROR(result);
return;
}
} while (0);
@@ -1906,6 +1985,12 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
return;
}
} else if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
retval = zend_hash_find(zobj->properties, Z_STR_P(prop_ptr));
if (EXPECTED(retval)) {
ZVAL_INDIRECT(result, retval);
@@ -1916,54 +2001,29 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
if (EXPECTED(Z_OBJ_HT_P(container)->get_property_ptr_ptr)) {
zval *ptr = Z_OBJ_HT_P(container)->get_property_ptr_ptr(container, prop_ptr, type, cache_slot);
if (NULL == ptr) {
- if (Z_OBJ_HT_P(container)->read_property &&
- (ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, cache_slot, result)) != NULL) {
+ if (EXPECTED(Z_OBJ_HT_P(container)->read_property)) {
+use_read_property:
+ ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, cache_slot, result);
if (ptr != result) {
ZVAL_INDIRECT(result, ptr);
+ } else if (UNEXPECTED(Z_ISREF_P(ptr) && Z_REFCOUNT_P(ptr) == 1)) {
+ ZVAL_UNREF(ptr);
}
} else {
- zend_throw_error(zend_ce_error, "Cannot access undefined property for object with overloaded property access");
- ZVAL_INDIRECT(result, &EG(error_zval));
+ zend_throw_error(NULL, "Cannot access undefined property for object with overloaded property access");
+ ZVAL_ERROR(result);
}
} else {
ZVAL_INDIRECT(result, ptr);
}
} else if (EXPECTED(Z_OBJ_HT_P(container)->read_property)) {
- zval *ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, cache_slot, result);
- if (ptr != result) {
- ZVAL_INDIRECT(result, ptr);
- }
+ goto use_read_property;
} else {
zend_error(E_WARNING, "This object doesn't support property references");
- ZVAL_INDIRECT(result, &EG(error_zval));
+ ZVAL_ERROR(result);
}
}
-static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_offset, const zend_op_array *op_array, const zend_execute_data *execute_data)
-{
- zend_brk_cont_element *jmp_to;
-
- do {
- ZEND_ASSERT(array_offset != -1);
- jmp_to = &op_array->brk_cont_array[array_offset];
- if (nest_levels > 1 && jmp_to->start >= 0) {
- zend_op *brk_opline = &op_array->opcodes[jmp_to->brk];
-
- if (brk_opline->opcode == ZEND_FREE) {
- zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
- } else if (brk_opline->opcode == ZEND_FE_FREE) {
- zval *var = EX_VAR(brk_opline->op1.var);
- if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
- zend_hash_iterator_del(Z_FE_ITER_P(var));
- }
- zval_ptr_dtor_nogc(var);
- }
- }
- array_offset = jmp_to->parent;
- } while (--nest_levels > 0);
- return jmp_to;
-}
-
#if ZEND_INTENSIVE_DEBUGGING
#define CHECK_SYMBOL_TABLES() \
@@ -1980,7 +2040,7 @@ static int zend_check_symbol(zval *pz)
if (Z_TYPE_P(pz) > 10) {
fprintf(stderr, "Warning! %x has invalid type!\n", *pz);
/* See http://support.microsoft.com/kb/190351 */
-#ifdef PHP_WIN32
+#ifdef ZEND_WIN32
fflush(stderr);
#endif
} else if (Z_TYPE_P(pz) == IS_ARRAY) {
@@ -2025,7 +2085,7 @@ static zend_always_inline void i_free_compiled_variables(zend_execute_data *exec
if (!Z_DELREF_P(cv)) {
zend_refcounted *r = Z_COUNTED_P(cv);
ZVAL_NULL(cv);
- zval_dtor_func_for_ptr(r);
+ zval_dtor_func(r);
} else {
GC_ZVAL_CHECK_POSSIBLE_ROOT(cv);
}
@@ -2041,16 +2101,17 @@ void zend_free_compiled_variables(zend_execute_data *execute_data) /* {{{ */
}
/* }}} */
-#ifdef ZEND_WIN32
-# define ZEND_VM_INTERRUPT_CHECK() do { \
- if (EG(timed_out)) { \
- zend_timeout(0); \
+static zend_never_inline ZEND_COLD ZEND_NORETURN void ZEND_FASTCALL zend_interrupt(void) /* {{{ */
+{
+ zend_timeout(0);
+}
+/* }}} */
+
+#define ZEND_VM_INTERRUPT_CHECK() do { \
+ if (UNEXPECTED(EG(timed_out))) { \
+ zend_interrupt(); \
} \
} while (0)
-#else
-# define ZEND_VM_INTERRUPT_CHECK() do { \
- } while (0)
-#endif
/*
* Stack Frame Layout (the whole stack frame is allocated at once)
@@ -2085,33 +2146,35 @@ static zend_always_inline void i_init_func_execute_data(zend_execute_data *execu
first_extra_arg = op_array->num_args;
num_args = EX_NUM_ARGS();
if (UNEXPECTED(num_args > first_extra_arg)) {
- zval *end, *src, *dst;
- uint32_t type_flags = 0;
+ if (EXPECTED(!(op_array->fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))) {
+ zval *end, *src, *dst;
+ uint32_t type_flags = 0;
- if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) {
- /* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */
- EX(opline) += first_extra_arg;
- }
+ if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) {
+ /* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */
+ EX(opline) += first_extra_arg;
+ }
- /* move extra args into separate array after all CV and TMP vars */
- end = EX_VAR_NUM(first_extra_arg - 1);
- src = end + (num_args - first_extra_arg);
- dst = src + (op_array->last_var + op_array->T - first_extra_arg);
- if (EXPECTED(src != dst)) {
- do {
- type_flags |= Z_TYPE_INFO_P(src);
- ZVAL_COPY_VALUE(dst, src);
- ZVAL_UNDEF(src);
- src--;
- dst--;
- } while (src != end);
- } else {
- do {
- type_flags |= Z_TYPE_INFO_P(src);
- src--;
- } while (src != end);
+ /* move extra args into separate array after all CV and TMP vars */
+ end = EX_VAR_NUM(first_extra_arg - 1);
+ src = end + (num_args - first_extra_arg);
+ dst = src + (op_array->last_var + op_array->T - first_extra_arg);
+ if (EXPECTED(src != dst)) {
+ do {
+ type_flags |= Z_TYPE_INFO_P(src);
+ ZVAL_COPY_VALUE(dst, src);
+ ZVAL_UNDEF(src);
+ src--;
+ dst--;
+ } while (src != end);
+ } else {
+ do {
+ type_flags |= Z_TYPE_INFO_P(src);
+ src--;
+ } while (src != end);
+ }
+ ZEND_ADD_CALL_FLAG(execute_data, ((type_flags >> Z_TYPE_FLAGS_SHIFT) & IS_TYPE_REFCOUNTED));
}
- ZEND_ADD_CALL_FLAG(execute_data, ((type_flags >> Z_TYPE_FLAGS_SHIFT) & IS_TYPE_REFCOUNTED));
} else if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) {
/* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */
EX(opline) += num_args;
@@ -2128,20 +2191,23 @@ static zend_always_inline void i_init_func_execute_data(zend_execute_data *execu
} while (var != end);
}
- if (check_this && op_array->this_var != (uint32_t)-1 && EXPECTED(Z_OBJ(EX(This)))) {
+ if (check_this && op_array->this_var != (uint32_t)-1 && EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
ZVAL_OBJ(EX_VAR(op_array->this_var), Z_OBJ(EX(This)));
GC_REFCOUNT(Z_OBJ(EX(This)))++;
}
- if (UNEXPECTED(!op_array->run_time_cache)) {
- op_array->run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size);
- memset(op_array->run_time_cache, 0, op_array->cache_size);
- }
EX_LOAD_RUN_TIME_CACHE(op_array);
EX_LOAD_LITERALS(op_array);
EG(current_execute_data) = execute_data;
- ZEND_VM_INTERRUPT_CHECK();
+}
+/* }}} */
+
+static zend_never_inline void ZEND_FASTCALL init_func_run_time_cache(zend_op_array *op_array) /* {{{ */
+{
+ ZEND_ASSERT(op_array->run_time_cache == NULL);
+ op_array->run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size);
+ memset(op_array->run_time_cache, 0, op_array->cache_size);
}
/* }}} */
@@ -2153,13 +2219,15 @@ static zend_always_inline void i_init_code_execute_data(zend_execute_data *execu
EX(call) = NULL;
EX(return_value) = return_value;
- zend_attach_symbol_table(execute_data);
-
- if (op_array->this_var != (uint32_t)-1 && EXPECTED(Z_OBJ(EX(This)))) {
- ZVAL_OBJ(EX_VAR(op_array->this_var), Z_OBJ(EX(This)));
+ if (UNEXPECTED(op_array->this_var != (uint32_t)-1) && EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
GC_REFCOUNT(Z_OBJ(EX(This)))++;
+ if (!zend_hash_add(EX(symbol_table), CG(known_strings)[ZEND_STR_THIS], &EX(This))) {
+ GC_REFCOUNT(Z_OBJ(EX(This)))--;
+ }
}
+ zend_attach_symbol_table(execute_data);
+
if (!op_array->run_time_cache) {
op_array->run_time_cache = emalloc(op_array->cache_size);
memset(op_array->run_time_cache, 0, op_array->cache_size);
@@ -2168,7 +2236,6 @@ static zend_always_inline void i_init_code_execute_data(zend_execute_data *execu
EX_LOAD_LITERALS(op_array);
EG(current_execute_data) = execute_data;
- ZEND_VM_INTERRUPT_CHECK();
}
/* }}} */
@@ -2180,7 +2247,14 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da
EX(call) = NULL;
EX(return_value) = return_value;
- if (UNEXPECTED(EX(symbol_table) != NULL)) {
+ if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
+ if (UNEXPECTED(op_array->this_var != (uint32_t)-1) && EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ GC_REFCOUNT(Z_OBJ(EX(This)))++;
+ if (!zend_hash_add(EX(symbol_table), CG(known_strings)[ZEND_STR_THIS], &EX(This))) {
+ GC_REFCOUNT(Z_OBJ(EX(This)))--;
+ }
+ }
+
zend_attach_symbol_table(execute_data);
} else {
uint32_t first_extra_arg, num_args;
@@ -2189,33 +2263,35 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da
first_extra_arg = op_array->num_args;
num_args = EX_NUM_ARGS();
if (UNEXPECTED(num_args > first_extra_arg)) {
- zval *end, *src, *dst;
- uint32_t type_flags = 0;
+ if (EXPECTED(!(op_array->fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))) {
+ zval *end, *src, *dst;
+ uint32_t type_flags = 0;
- if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) {
- /* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */
- EX(opline) += first_extra_arg;
- }
+ if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) {
+ /* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */
+ EX(opline) += first_extra_arg;
+ }
- /* move extra args into separate array after all CV and TMP vars */
- end = EX_VAR_NUM(first_extra_arg - 1);
- src = end + (num_args - first_extra_arg);
- dst = src + (op_array->last_var + op_array->T - first_extra_arg);
- if (EXPECTED(src != dst)) {
- do {
- type_flags |= Z_TYPE_INFO_P(src);
- ZVAL_COPY_VALUE(dst, src);
- ZVAL_UNDEF(src);
- src--;
- dst--;
- } while (src != end);
- } else {
- do {
- type_flags |= Z_TYPE_INFO_P(src);
- src--;
- } while (src != end);
+ /* move extra args into separate array after all CV and TMP vars */
+ end = EX_VAR_NUM(first_extra_arg - 1);
+ src = end + (num_args - first_extra_arg);
+ dst = src + (op_array->last_var + op_array->T - first_extra_arg);
+ if (EXPECTED(src != dst)) {
+ do {
+ type_flags |= Z_TYPE_INFO_P(src);
+ ZVAL_COPY_VALUE(dst, src);
+ ZVAL_UNDEF(src);
+ src--;
+ dst--;
+ } while (src != end);
+ } else {
+ do {
+ type_flags |= Z_TYPE_INFO_P(src);
+ src--;
+ } while (src != end);
+ }
+ ZEND_ADD_CALL_FLAG(execute_data, ((type_flags >> Z_TYPE_FLAGS_SHIFT) & IS_TYPE_REFCOUNTED));
}
- ZEND_ADD_CALL_FLAG(execute_data, ((type_flags >> Z_TYPE_FLAGS_SHIFT) & IS_TYPE_REFCOUNTED));
} else if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) {
/* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */
EX(opline) += num_args;
@@ -2231,11 +2307,11 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da
var++;
} while (var != end);
}
- }
- if (op_array->this_var != (uint32_t)-1 && EXPECTED(Z_OBJ(EX(This)))) {
- ZVAL_OBJ(EX_VAR(op_array->this_var), Z_OBJ(EX(This)));
- GC_REFCOUNT(Z_OBJ(EX(This)))++;
+ if (op_array->this_var != (uint32_t)-1 && EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ ZVAL_OBJ(EX_VAR(op_array->this_var), Z_OBJ(EX(This)));
+ GC_REFCOUNT(Z_OBJ(EX(This)))++;
+ }
}
if (!op_array->run_time_cache) {
@@ -2250,65 +2326,6 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da
EX_LOAD_LITERALS(op_array);
EG(current_execute_data) = execute_data;
- ZEND_VM_INTERRUPT_CHECK();
-}
-/* }}} */
-
-ZEND_API zend_execute_data *zend_create_generator_execute_data(zend_execute_data *call, zend_op_array *op_array, zval *return_value) /* {{{ */
-{
- /*
- * Normally the execute_data is allocated on the VM stack (because it does
- * not actually do any allocation and thus is faster). For generators
- * though this behavior would be suboptimal, because the (rather large)
- * structure would have to be copied back and forth every time execution is
- * suspended or resumed. That's why for generators the execution context
- * is allocated using a separate VM stack, thus allowing to save and
- * restore it simply by replacing a pointer.
- */
- zend_execute_data *execute_data;
- uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
- size_t stack_size = (ZEND_CALL_FRAME_SLOT + MAX(op_array->last_var + op_array->T, num_args)) * sizeof(zval);
- uint32_t call_info;
-
- EG(vm_stack) = zend_vm_stack_new_page(
- EXPECTED(stack_size < ZEND_VM_STACK_FREE_PAGE_SIZE(1)) ?
- ZEND_VM_STACK_PAGE_SIZE(1) :
- ZEND_VM_STACK_PAGE_ALIGNED_SIZE(1, stack_size),
- NULL);
- EG(vm_stack_top) = EG(vm_stack)->top;
- EG(vm_stack_end) = EG(vm_stack)->end;
-
- call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_ALLOCATED | (ZEND_CALL_INFO(call) & (ZEND_CALL_CLOSURE|ZEND_CALL_RELEASE_THIS));
- if (Z_OBJ(call->This)) {
- call_info |= ZEND_CALL_RELEASE_THIS;
- }
- execute_data = zend_vm_stack_push_call_frame(
- call_info,
- (zend_function*)op_array,
- num_args,
- call->called_scope,
- Z_OBJ(call->This));
- EX(prev_execute_data) = NULL;
- EX_NUM_ARGS() = num_args;
-
- /* copy arguments */
- if (num_args > 0) {
- zval *arg_src = ZEND_CALL_ARG(call, 1);
- zval *arg_dst = ZEND_CALL_ARG(execute_data, 1);
- zval *end = arg_src + num_args;
-
- do {
- ZVAL_COPY_VALUE(arg_dst, arg_src);
- arg_src++;
- arg_dst++;
- } while (arg_src != end);
- }
-
- EX(symbol_table) = NULL;
-
- i_init_func_execute_data(execute_data, op_array, return_value, 1);
-
- return execute_data;
}
/* }}} */
@@ -2322,6 +2339,10 @@ ZEND_API void zend_init_execute_data(zend_execute_data *execute_data, zend_op_ar
static zend_always_inline zend_bool zend_is_by_ref_func_arg_fetch(const zend_op *opline, zend_execute_data *call) /* {{{ */
{
uint32_t arg_num = opline->extended_value & ZEND_FETCH_ARG_MASK;
+
+ if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
+ return QUICK_ARG_SHOULD_BE_SENT_BY_REF(call->func, arg_num);
+ }
return ARG_SHOULD_BE_SENT_BY_REF(call->func, arg_num);
}
/* }}} */
@@ -2334,7 +2355,7 @@ static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call,
/* copy call frame into new stack segment */
new_call = zend_vm_stack_extend(used_stack * sizeof(zval));
*new_call = *call;
- ZEND_SET_CALL_INFO(new_call, ZEND_CALL_INFO(new_call) | ZEND_CALL_ALLOCATED);
+ ZEND_ADD_CALL_FLAG(new_call, ZEND_CALL_ALLOCATED);
if (passed_args) {
zval *src = ZEND_CALL_ARG(call, 1);
@@ -2351,7 +2372,7 @@ static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call,
EG(vm_stack)->prev->top = (zval*)call;
/* delete previous stack segment if it becames empty */
- if (UNEXPECTED(EG(vm_stack)->prev->top == ZEND_VM_STACK_ELEMETS(EG(vm_stack)->prev))) {
+ if (UNEXPECTED(EG(vm_stack)->prev->top == ZEND_VM_STACK_ELEMENTS(EG(vm_stack)->prev))) {
zend_vm_stack r = EG(vm_stack)->prev;
EG(vm_stack)->prev = r->prev;
@@ -2382,14 +2403,22 @@ static zend_always_inline zend_generator *zend_get_running_generator(zend_execut
}
/* }}} */
-static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) /* {{{ */
+static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t op_num) /* {{{ */
{
- int i;
if (UNEXPECTED(EX(call))) {
zend_execute_data *call = EX(call);
zend_op *opline = EX(func)->op_array.opcodes + op_num;
int level;
int do_exit;
+
+ if (UNEXPECTED(opline->opcode == ZEND_INIT_FCALL ||
+ opline->opcode == ZEND_INIT_FCALL_BY_NAME ||
+ opline->opcode == ZEND_INIT_DYNAMIC_CALL ||
+ opline->opcode == ZEND_INIT_METHOD_CALL ||
+ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL)) {
+ ZEND_ASSERT(op_num);
+ opline--;
+ }
do {
/* If the exception was thrown during a function call there might be
@@ -2426,6 +2455,7 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data
case ZEND_SEND_VAR_EX:
case ZEND_SEND_REF:
case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
case ZEND_SEND_USER:
if (level == 0) {
ZEND_CALL_NUM_ARGS(call) = opline->op2.num;
@@ -2477,9 +2507,7 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
if (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR) {
- if (!(ZEND_CALL_INFO(call) & ZEND_CALL_CTOR_RESULT_UNUSED)) {
- GC_REFCOUNT(Z_OBJ(call->This))--;
- }
+ GC_REFCOUNT(Z_OBJ(call->This))--;
if (GC_REFCOUNT(Z_OBJ(call->This)) == 1) {
zend_object_store_ctor_failed(Z_OBJ(call->This));
}
@@ -2488,8 +2516,7 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data
}
if (call->func->common.fn_flags & ZEND_ACC_CLOSURE) {
zend_object_release((zend_object *) call->func->common.prototype);
- }
- if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
+ } else if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
zend_string_release(call->func->common.function_name);
zend_free_trampoline(call->func);
}
@@ -2499,30 +2526,36 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data
call = EX(call);
} while (call);
}
+}
+/* }}} */
+
+static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) /* {{{ */
+{
+ int i;
- for (i = 0; i < EX(func)->op_array.last_brk_cont; i++) {
- const zend_brk_cont_element *brk_cont = &EX(func)->op_array.brk_cont_array[i];
- if (brk_cont->start < 0) {
- continue;
- } else if (brk_cont->start > op_num) {
+ for (i = 0; i < EX(func)->op_array.last_live_range; i++) {
+ const zend_live_range *range = &EX(func)->op_array.live_range[i];
+ if (range->start > op_num) {
/* further blocks will not be relevant... */
break;
- } else if (op_num < brk_cont->brk) {
- if (!catch_op_num || catch_op_num >= brk_cont->brk) {
- zend_op *brk_opline = &EX(func)->op_array.opcodes[brk_cont->brk];
-
- if (brk_opline->opcode == ZEND_FREE) {
- zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
- } else if (brk_opline->opcode == ZEND_FE_FREE) {
- zval *var = EX_VAR(brk_opline->op1.var);
+ } else if (op_num < range->end) {
+ if (!catch_op_num || catch_op_num >= range->end) {
+ uint32_t kind = range->var & ZEND_LIVE_MASK;
+ uint32_t var_num = range->var & ~ZEND_LIVE_MASK;
+ zval *var = EX_VAR(var_num);
+
+ if (kind == ZEND_LIVE_TMPVAR) {
+ zval_ptr_dtor_nogc(var);
+ } else if (kind == ZEND_LIVE_LOOP) {
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
- } else if (brk_opline->opcode == ZEND_ROPE_END) {
- zend_string **rope = (zend_string **) EX_VAR(brk_opline->op1.var);
+ } else if (kind == ZEND_LIVE_ROPE) {
+ zend_string **rope = (zend_string **)var;
zend_op *last = EX(func)->op_array.opcodes + op_num;
- while (last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT) {
+ while ((last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT)
+ || last->result.var != var_num) {
ZEND_ASSERT(last >= EX(func)->op_array.opcodes);
last--;
}
@@ -2534,10 +2567,10 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data
zend_string_release(rope[j]);
} while (j--);
}
- } else if (brk_opline->opcode == ZEND_END_SILENCE) {
+ } else if (kind == ZEND_LIVE_SILENCE) {
/* restore previous error_reporting value */
- if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) {
- EG(error_reporting) = Z_LVAL_P(EX_VAR(brk_opline->op1.var));
+ if (!EG(error_reporting) && Z_LVAL_P(var) != 0) {
+ EG(error_reporting) = Z_LVAL_P(var);
}
}
}
@@ -2547,8 +2580,359 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data
/* }}} */
void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) {
- i_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
+ cleanup_unfinished_calls(execute_data, op_num);
+ cleanup_live_vars(execute_data, op_num, catch_op_num);
+}
+
+static void zend_swap_operands(zend_op *op) /* {{{ */
+{
+ znode_op tmp;
+ zend_uchar tmp_type;
+
+ tmp = op->op1;
+ tmp_type = op->op1_type;
+ op->op1 = op->op2;
+ op->op1_type = op->op2_type;
+ op->op2 = tmp;
+ op->op2_type = tmp_type;
}
+/* }}} */
+
+static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_string *function, uint32_t num_args) /* {{{ */
+{
+ zend_function *fbc;
+ zval *func;
+ zend_class_entry *called_scope;
+ zend_string *lcname;
+ const char *colon;
+
+ if ((colon = zend_memrchr(ZSTR_VAL(function), ':', ZSTR_LEN(function))) != NULL &&
+ colon > ZSTR_VAL(function) &&
+ *(colon-1) == ':'
+ ) {
+ zend_string *mname;
+ size_t cname_length = colon - ZSTR_VAL(function) - 1;
+ size_t mname_length = ZSTR_LEN(function) - cname_length - (sizeof("::") - 1);
+
+ lcname = zend_string_init(ZSTR_VAL(function), cname_length, 0);
+
+ called_scope = zend_fetch_class_by_name(lcname, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(called_scope == NULL)) {
+ zend_string_release(lcname);
+ return NULL;
+ }
+
+ mname = zend_string_init(ZSTR_VAL(function) + (cname_length + sizeof("::") - 1), mname_length, 0);
+
+ if (called_scope->get_static_method) {
+ fbc = called_scope->get_static_method(called_scope, mname);
+ } else {
+ fbc = zend_std_get_static_method(called_scope, mname, NULL);
+ }
+ if (UNEXPECTED(fbc == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), ZSTR_VAL(mname));
+ }
+ zend_string_release(lcname);
+ zend_string_release(mname);
+ return NULL;
+ }
+
+ zend_string_release(lcname);
+ zend_string_release(mname);
+
+ if (UNEXPECTED(!(fbc->common.fn_flags & ZEND_ACC_STATIC))) {
+ if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
+ zend_error(E_DEPRECATED,
+ "Non-static method %s::%s() should not be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ return NULL;
+ }
+ } else {
+ zend_throw_error(
+ zend_ce_error,
+ "Non-static method %s::%s() cannot be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ return NULL;
+ }
+ }
+ } else {
+ if (ZSTR_VAL(function)[0] == '\\') {
+ lcname = zend_string_alloc(ZSTR_LEN(function) - 1, 0);
+ zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(function) + 1, ZSTR_LEN(function) - 1);
+ } else {
+ lcname = zend_string_tolower(function);
+ }
+ if (UNEXPECTED((func = zend_hash_find(EG(function_table), lcname)) == NULL)) {
+ zend_throw_error(NULL, "Call to undefined function %s()", ZSTR_VAL(function));
+ zend_string_release(lcname);
+ return NULL;
+ }
+ zend_string_release(lcname);
+
+ fbc = Z_FUNC_P(func);
+ called_scope = NULL;
+ }
+
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+
+ return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC,
+ fbc, num_args, called_scope, NULL);
+}
+/* }}} */
+
+static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zval *function, uint32_t num_args) /* {{{ */
+{
+ zend_function *fbc;
+ zend_class_entry *called_scope;
+ zend_object *object;
+ uint32_t call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC;
+
+ if (EXPECTED(Z_OBJ_HANDLER_P(function, get_closure)) &&
+ EXPECTED(Z_OBJ_HANDLER_P(function, get_closure)(function, &called_scope, &fbc, &object) == SUCCESS)) {
+
+ if (fbc->common.fn_flags & ZEND_ACC_CLOSURE) {
+ /* Delay closure destruction until its invocation */
+ ZEND_ASSERT(GC_TYPE((zend_object*)fbc->common.prototype) == IS_OBJECT);
+ GC_REFCOUNT((zend_object*)fbc->common.prototype)++;
+ call_info |= ZEND_CALL_CLOSURE;
+ } else if (object) {
+ call_info |= ZEND_CALL_RELEASE_THIS;
+ GC_REFCOUNT(object)++; /* For $this pointer */
+ }
+ } else {
+ zend_throw_error(NULL, "Function name must be a string");
+ return NULL;
+ }
+
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+
+ return zend_vm_stack_push_call_frame(call_info,
+ fbc, num_args, called_scope, object);
+}
+/* }}} */
+
+static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_array *function, uint32_t num_args) /* {{{ */
+{
+ zend_function *fbc;
+ zend_class_entry *called_scope;
+ zend_object *object;
+ uint32_t call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC;
+
+ if (zend_hash_num_elements(function) == 2) {
+ zval *obj;
+ zval *method;
+ obj = zend_hash_index_find(function, 0);
+ method = zend_hash_index_find(function, 1);
+
+ if (UNEXPECTED(!obj) || UNEXPECTED(!method)) {
+ zend_throw_error(NULL, "Array callback has to contain indices 0 and 1");
+ return NULL;
+ }
+
+ ZVAL_DEREF(obj);
+ if (UNEXPECTED(Z_TYPE_P(obj) != IS_STRING) && UNEXPECTED(Z_TYPE_P(obj) != IS_OBJECT)) {
+ zend_throw_error(NULL, "First array member is not a valid class name or object");
+ return NULL;
+ }
+
+ ZVAL_DEREF(method);
+ if (UNEXPECTED(Z_TYPE_P(method) != IS_STRING)) {
+ zend_throw_error(NULL, "Second array member is not a valid method");
+ return NULL;
+ }
+
+ if (Z_TYPE_P(obj) == IS_STRING) {
+ object = NULL;
+ called_scope = zend_fetch_class_by_name(Z_STR_P(obj), NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(called_scope == NULL)) {
+ return NULL;
+ }
+
+ if (called_scope->get_static_method) {
+ fbc = called_scope->get_static_method(called_scope, Z_STR_P(method));
+ } else {
+ fbc = zend_std_get_static_method(called_scope, Z_STR_P(method), NULL);
+ }
+ if (UNEXPECTED(fbc == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), Z_STRVAL_P(method));
+ }
+ return NULL;
+ }
+ if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
+ if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
+ zend_error(E_DEPRECATED,
+ "Non-static method %s::%s() should not be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ return NULL;
+ }
+ } else {
+ zend_throw_error(
+ zend_ce_error,
+ "Non-static method %s::%s() cannot be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ return NULL;
+ }
+ }
+ } else {
+ called_scope = Z_OBJCE_P(obj);
+ object = Z_OBJ_P(obj);
+
+ fbc = Z_OBJ_HT_P(obj)->get_method(&object, Z_STR_P(method), NULL);
+ if (UNEXPECTED(fbc == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(object->ce->name), Z_STRVAL_P(method));
+ }
+ return NULL;
+ }
+
+ if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) {
+ object = NULL;
+ } else {
+ call_info |= ZEND_CALL_RELEASE_THIS;
+ GC_REFCOUNT(object)++; /* For $this pointer */
+ }
+ }
+ } else {
+ zend_throw_error(NULL, "Function name must be a string");
+ return NULL;
+ }
+
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+
+ return zend_vm_stack_push_call_frame(call_info,
+ fbc, num_args, called_scope, object);
+}
+/* }}} */
+
+#define ZEND_FAKE_OP_ARRAY ((zend_op_array*)(zend_intptr_t)-1)
+
+static zend_never_inline zend_op_array* ZEND_FASTCALL zend_include_or_eval(zval *inc_filename, int type) /* {{{ */
+{
+ zend_op_array *new_op_array = NULL;
+ zval tmp_inc_filename;
+
+ ZVAL_UNDEF(&tmp_inc_filename);
+ if (Z_TYPE_P(inc_filename) != IS_STRING) {
+ ZVAL_STR(&tmp_inc_filename, zval_get_string(inc_filename));
+ inc_filename = &tmp_inc_filename;
+ }
+
+ if (type != ZEND_EVAL && strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename)) {
+ if (type == ZEND_INCLUDE_ONCE || type == ZEND_INCLUDE) {
+ zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename));
+ } else {
+ zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename));
+ }
+ } else {
+ switch (type) {
+ case ZEND_INCLUDE_ONCE:
+ case ZEND_REQUIRE_ONCE: {
+ zend_file_handle file_handle;
+ zend_string *resolved_path;
+
+ resolved_path = zend_resolve_path(Z_STRVAL_P(inc_filename), (int)Z_STRLEN_P(inc_filename));
+ if (resolved_path) {
+ if (zend_hash_exists(&EG(included_files), resolved_path)) {
+ goto already_compiled;
+ }
+ } else {
+ resolved_path = zend_string_copy(Z_STR_P(inc_filename));
+ }
+
+ if (SUCCESS == zend_stream_open(ZSTR_VAL(resolved_path), &file_handle)) {
+
+ if (!file_handle.opened_path) {
+ file_handle.opened_path = zend_string_copy(resolved_path);
+ }
+
+ if (zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path)) {
+ zend_op_array *op_array = zend_compile_file(&file_handle, (type==ZEND_INCLUDE_ONCE?ZEND_INCLUDE:ZEND_REQUIRE));
+ zend_destroy_file_handle(&file_handle);
+ zend_string_release(resolved_path);
+ if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp_inc_filename));
+ }
+ return op_array;
+ } else {
+ zend_file_handle_dtor(&file_handle);
+already_compiled:
+ new_op_array = ZEND_FAKE_OP_ARRAY;
+ }
+ } else {
+ if (type == ZEND_INCLUDE_ONCE) {
+ zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename));
+ } else {
+ zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename));
+ }
+ }
+ zend_string_release(resolved_path);
+ }
+ break;
+ case ZEND_INCLUDE:
+ case ZEND_REQUIRE:
+ new_op_array = compile_filename(type, inc_filename);
+ break;
+ case ZEND_EVAL: {
+ char *eval_desc = zend_make_compiled_string_description("eval()'d code");
+ new_op_array = zend_compile_string(inc_filename, eval_desc);
+ efree(eval_desc);
+ }
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ }
+ if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp_inc_filename));
+ }
+ return new_op_array;
+}
+/* }}} */
+
+static zend_never_inline int zend_do_fcall_overloaded(zend_function *fbc, zend_execute_data *call, zval *ret) /* {{{ */
+{
+ zend_object *object;
+
+ /* Not sure what should be done here if it's a static method */
+ if (UNEXPECTED(Z_TYPE(call->This) != IS_OBJECT)) {
+ zend_vm_stack_free_args(call);
+ if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
+ zend_string_release(fbc->common.function_name);
+ }
+ efree(fbc);
+ zend_vm_stack_free_call_frame(call);
+
+ zend_throw_error(NULL, "Cannot call overloaded function for non-object");
+ return 0;
+ }
+
+ object = Z_OBJ(call->This);
+
+ ZVAL_NULL(ret);
+
+ EG(current_execute_data) = call;
+ object->handlers->call_method(fbc->common.function_name, object, call, ret);
+ EG(current_execute_data) = call->prev_execute_data;
+
+ zend_vm_stack_free_args(call);
+
+ if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
+ zend_string_release(fbc->common.function_name);
+ }
+ efree(fbc);
+
+ return 1;
+}
+/* }}} */
#ifdef HAVE_GCC_GLOBAL_REGS
# if defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(i386)
@@ -2560,14 +2944,27 @@ void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t
# elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__powerpc64__)
# define ZEND_VM_FP_GLOBAL_REG "r28"
# define ZEND_VM_IP_GLOBAL_REG "r29"
+# elif defined(__IBMC__) && ZEND_GCC_VERSION >= 4002 && defined(__powerpc64__)
+# define ZEND_VM_FP_GLOBAL_REG "r28"
+# define ZEND_VM_IP_GLOBAL_REG "r29"
# endif
#endif
-#define ZEND_VM_NEXT_OPCODE() \
+#define ZEND_VM_NEXT_OPCODE_EX(check_exception, skip) \
CHECK_SYMBOL_TABLES() \
- ZEND_VM_INC_OPCODE(); \
+ if (check_exception) { \
+ OPLINE = EX(opline) + (skip); \
+ } else { \
+ OPLINE = opline + (skip); \
+ } \
ZEND_VM_CONTINUE()
+#define ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION() \
+ ZEND_VM_NEXT_OPCODE_EX(1, 1)
+
+#define ZEND_VM_NEXT_OPCODE() \
+ ZEND_VM_NEXT_OPCODE_EX(0, 1)
+
#define ZEND_VM_SET_NEXT_OPCODE(new_op) \
CHECK_SYMBOL_TABLES() \
OPLINE = new_op
@@ -2622,10 +3019,34 @@ void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t
} \
ZEND_VM_CONTINUE(); \
} while (0)
+# define ZEND_VM_SMART_BRANCH_JMPZ(_result, _check) do { \
+ if ((_check) && UNEXPECTED(EG(exception))) { \
+ HANDLE_EXCEPTION(); \
+ } \
+ if (_result) { \
+ ZEND_VM_SET_NEXT_OPCODE(opline + 2); \
+ } else { \
+ ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline + 1, (opline+1)->op2)); \
+ } \
+ ZEND_VM_CONTINUE(); \
+ } while (0)
+# define ZEND_VM_SMART_BRANCH_JMPNZ(_result, _check) do { \
+ if ((_check) && UNEXPECTED(EG(exception))) { \
+ HANDLE_EXCEPTION(); \
+ } \
+ if (!(_result)) { \
+ ZEND_VM_SET_NEXT_OPCODE(opline + 2); \
+ } else { \
+ ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline + 1, (opline+1)->op2)); \
+ } \
+ ZEND_VM_CONTINUE(); \
+ } while (0)
#else
# define ZEND_VM_REPEATABLE_OPCODE
# define ZEND_VM_REPEAT_OPCODE(_opcode)
# define ZEND_VM_SMART_BRANCH(_result, _check)
+# define ZEND_VM_SMART_BRANCH_JMPZ(_result, _check)
+# define ZEND_VM_SMART_BRANCH_JMPNZ(_result, _check)
#endif
#ifdef __GNUC__
@@ -2661,10 +3082,26 @@ ZEND_API user_opcode_handler_t zend_get_user_opcode_handler(zend_uchar opcode)
return zend_user_opcode_handlers[opcode];
}
-ZEND_API zval *zend_get_zval_ptr(int op_type, const znode_op *node, const zend_execute_data *execute_data, zend_free_op *should_free, int type) {
+ZEND_API zval *zend_get_zval_ptr(int op_type, const znode_op *node, const zend_execute_data *execute_data, zend_free_op *should_free, int type)
+{
return get_zval_ptr(op_type, *node, execute_data, should_free, type);
}
+ZEND_API void ZEND_FASTCALL zend_check_internal_arg_type(zend_function *zf, uint32_t arg_num, zval *arg)
+{
+ zend_verify_internal_arg_type(zf, arg_num, arg);
+}
+
+ZEND_API int ZEND_FASTCALL zend_check_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot)
+{
+ return zend_verify_arg_type(zf, arg_num, arg, default_value, cache_slot);
+}
+
+ZEND_API void ZEND_FASTCALL zend_check_missing_arg(zend_execute_data *execute_data, uint32_t arg_num, void **cache_slot)
+{
+ zend_verify_missing_arg(execute_data, arg_num, cache_slot);
+}
+
/*
* Local variables:
* tab-width: 4