diff options
Diffstat (limited to 'Zend/zend_execute.c')
-rw-r--r-- | Zend/zend_execute.c | 320 |
1 files changed, 192 insertions, 128 deletions
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index caaeb6a5fd..0fe2a26e97 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -598,7 +598,7 @@ ZEND_API char * zend_verify_arg_class_kind(const zend_arg_info *cur_arg_info, ch } } -ZEND_API void zend_verify_arg_error(int error_type, 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) +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) { zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data; const char *fname = zf->common.function_name->val; @@ -621,16 +621,18 @@ ZEND_API void zend_verify_arg_error(int error_type, const zend_function *zf, uin } if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) { - zend_error(error_type, "Argument %d passed to %s%s%s() must %s%s, %s%s given, called in %s on line %d and defined", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind, ptr->func->op_array.filename->val, ptr->opline->lineno); + zend_type_error("Argument %d passed to %s%s%s() must %s%s, %s%s given, called in %s on line %d", + arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind, + ptr->func->op_array.filename->val, ptr->opline->lineno); } else { - zend_error(error_type, "Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); + zend_type_error("Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); } if (arg) { ZVAL_COPY_VALUE(arg, &old_arg); } } else { - zend_error(error_type, "Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); + zend_type_error("Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); } } @@ -649,10 +651,71 @@ static int is_null_constant(zval *default_value) return 0; } +static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *arg) +{ + switch (type_hint) { + case _IS_BOOL: { + zend_bool dest; + + if (!zend_parse_arg_bool_weak(arg, &dest)) { + return 0; + } + zval_ptr_dtor(arg); + ZVAL_BOOL(arg, dest); + return 1; + } + case IS_LONG: { + zend_long dest; + + if (!zend_parse_arg_long_weak(arg, &dest)) { + return 0; + } + zval_ptr_dtor(arg); + ZVAL_LONG(arg, dest); + return 1; + } + case IS_DOUBLE: { + double dest; + + if (!zend_parse_arg_double_weak(arg, &dest)) { + return 0; + } + zval_ptr_dtor(arg); + ZVAL_DOUBLE(arg, dest); + return 1; + } + case IS_STRING: { + zend_string *dest; + + /* on success "arg" is converted to IS_STRING */ + if (!zend_parse_arg_str_weak(arg, &dest)) { + return 0; + } + return 1; + } + default: + return 0; + } +} + +static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, zend_bool strict) +{ + if (UNEXPECTED(strict)) { + /* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */ + if (type_hint != IS_DOUBLE || Z_TYPE_P(arg) != IS_LONG) { + return 0; + } + } else if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL)) { + /* NULL may be accepted only by nullable hints (this is already checked) */ + return 0; + } + 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) { zend_internal_arg_info *cur_arg_info; - char *need_msg; + char *need_msg, *class_name; zend_class_entry *ce; if (EXPECTED(arg_num <= zf->internal_function.num_args)) { @@ -663,33 +726,29 @@ static void zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, z return; } - if (cur_arg_info->class_name) { - char *class_name; - + if (cur_arg_info->type_hint) { ZVAL_DEREF(arg); - if (Z_TYPE_P(arg) == IS_OBJECT) { - 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(E_EXCEPTION | E_ERROR, zf, arg_num, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name->val, arg); + if (EXPECTED(cur_arg_info->type_hint == Z_TYPE_P(arg))) { + 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 ", Z_OBJCE_P(arg)->name->val, arg); + } } } else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) { - need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce); - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "", arg); - } - } else if (cur_arg_info->type_hint) { - if (cur_arg_info->type_hint == IS_ARRAY) { - ZVAL_DEREF(arg); - if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, "be of the type array", "", zend_zval_type_name(arg), "", arg); - } - } else if (cur_arg_info->type_hint == IS_CALLABLE) { - if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL) && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, "be callable", "", zend_zval_type_name(arg), "", arg); + 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); + } 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); + } + } 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); } -#if ZEND_DEBUG - } else { - zend_error(E_ERROR, "Unknown typehint"); -#endif } } } @@ -697,7 +756,7 @@ static void zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, z static void zend_verify_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value) { zend_arg_info *cur_arg_info; - char *need_msg; + char *need_msg, *class_name; zend_class_entry *ce; if (EXPECTED(arg_num <= zf->common.num_args)) { @@ -708,33 +767,29 @@ static void zend_verify_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, return; } - if (cur_arg_info->class_name) { - char *class_name; - + if (cur_arg_info->type_hint) { ZVAL_DEREF(arg); - if (Z_TYPE_P(arg) == IS_OBJECT) { - need_msg = zend_verify_arg_class_kind(cur_arg_info, &class_name, &ce); - if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce)) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name->val, arg); + if (EXPECTED(cur_arg_info->type_hint == Z_TYPE_P(arg))) { + if (cur_arg_info->class_name) { + need_msg = zend_verify_arg_class_kind(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 ", Z_OBJCE_P(arg)->name->val, arg); + } } } else if (Z_TYPE_P(arg) != IS_NULL || !(cur_arg_info->allow_null || (default_value && is_null_constant(default_value)))) { - need_msg = zend_verify_arg_class_kind(cur_arg_info, &class_name, &ce); - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "", arg); - } - } else if (cur_arg_info->type_hint) { - if (cur_arg_info->type_hint == IS_ARRAY) { - ZVAL_DEREF(arg); - if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL || !(cur_arg_info->allow_null || (default_value && is_null_constant(default_value))))) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, "be of the type array", "", zend_zval_type_name(arg), "", arg); - } - } else if (cur_arg_info->type_hint == IS_CALLABLE) { - if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL) && (Z_TYPE_P(arg) != IS_NULL || !(cur_arg_info->allow_null || (default_value && is_null_constant(default_value))))) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, "be callable", "", zend_zval_type_name(arg), "", arg); + if (cur_arg_info->class_name) { + need_msg = zend_verify_arg_class_kind(cur_arg_info, &class_name, &ce); + zend_verify_arg_error(zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "", arg); + } 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); + } + } 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); } -#if ZEND_DEBUG - } else { - zend_error(E_ERROR, "Unknown typehint"); -#endif } } } @@ -753,21 +808,17 @@ static inline int zend_verify_missing_arg_type(zend_function *zf, uint32_t arg_n return 1; } - if (cur_arg_info->class_name) { - char *class_name; + if (cur_arg_info->type_hint) { + if (cur_arg_info->class_name) { + char *class_name; - need_msg = zend_verify_arg_class_kind(cur_arg_info, &class_name, &ce); - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, need_msg, class_name, "none", "", NULL); - return 0; - } else if (cur_arg_info->type_hint) { - if (cur_arg_info->type_hint == IS_ARRAY) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, "be of the type array", "", "none", "", NULL); + need_msg = zend_verify_arg_class_kind(cur_arg_info, &class_name, &ce); + zend_verify_arg_error(zf, arg_num, need_msg, class_name, "none", "", NULL); + return 0; } else if (cur_arg_info->type_hint == IS_CALLABLE) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, "be callable", "", "none", "", NULL); -#if ZEND_DEBUG + zend_verify_arg_error(zf, arg_num, "be callable", "", "none", "", NULL); } else { - zend_error(E_ERROR, "Unknown typehint"); -#endif + zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), "none", "", NULL); } return 0; } @@ -791,7 +842,7 @@ static void zend_verify_missing_arg(zend_execute_data *execute_data, uint32_t ar } } -ZEND_API void zend_verify_return_error(int error_type, const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind) +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) { const char *fname = zf->common.function_name->val; const char *fsep; @@ -805,8 +856,31 @@ ZEND_API void zend_verify_return_error(int error_type, const zend_function *zf, fclass = ""; } - zend_error(error_type, - "Return value of %s%s%s() must %s%s, %s%s returned", + 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, + zf->op_array.filename->val, 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_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) +{ + const char *fname = zf->common.function_name->val; + const char *fsep; + const char *fclass; + + if (zf->common.scope) { + fsep = "::"; + fclass = zf->common.scope->name->val; + } else { + fsep = ""; + fclass = ""; + } + + zend_error(E_CORE_ERROR, "Return value of %s%s%s() must %s%s, %s%s returned", fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind); } @@ -814,39 +888,36 @@ ZEND_API void zend_verify_return_error(int error_type, const zend_function *zf, static int zend_verify_internal_return_type(zend_function *zf, zval *ret) { zend_arg_info *ret_info = zf->common.arg_info - 1; - char *need_msg; + char *need_msg, *class_name; zend_class_entry *ce; - if (ret_info->class_name) { - char *class_name; - if (Z_TYPE_P(ret) == IS_OBJECT) { - need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info *)ret_info, &class_name, &ce); - if (!ce || !instanceof_function(Z_OBJCE_P(ret), ce)) { - zend_verify_return_error(E_CORE_ERROR, zf, need_msg, class_name, "instance of ", Z_OBJCE_P(ret)->name->val); - return 0; + if (ret_info->type_hint) { + if (EXPECTED(ret_info->type_hint == Z_TYPE_P(ret))) { + if (ret_info->class_name) { + need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info *)ret_info, &class_name, &ce); + if (!ce || !instanceof_function(Z_OBJCE_P(ret), ce)) { + zend_verify_internal_return_error(zf, need_msg, class_name, "instance of ", Z_OBJCE_P(ret)->name->val); + return 0; + } } } else if (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null) { - need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info *)ret_info, &class_name, &ce); - zend_verify_return_error(E_CORE_ERROR, zf, need_msg, class_name, zend_zval_type_name(ret), ""); - return 0; - } - } else if (ret_info->type_hint) { - if (ret_info->type_hint == IS_ARRAY) { - if (Z_TYPE_P(ret) != IS_ARRAY && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) { - zend_verify_return_error(E_CORE_ERROR, zf, "be of the type array", "", zend_zval_type_name(ret), ""); - return 0; - } - } else if (ret_info->type_hint == IS_CALLABLE) { - if (!zend_is_callable(ret, IS_CALLABLE_CHECK_SILENT, NULL) && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) { - zend_verify_return_error(E_CORE_ERROR, zf, "be callable", "", zend_zval_type_name(ret), ""); + if (ret_info->class_name) { + need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info *)ret_info, &class_name, &ce); + zend_verify_internal_return_error(zf, need_msg, class_name, zend_zval_type_name(ret), ""); + } else if (ret_info->type_hint == IS_CALLABLE) { + if (!zend_is_callable(ret, IS_CALLABLE_CHECK_SILENT, NULL) && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) { + zend_verify_internal_return_error(zf, "be callable", "", zend_zval_type_name(ret), ""); + return 0; + } + } else if (ret_info->type_hint == _IS_BOOL && + EXPECTED(Z_TYPE_P(ret) == IS_FALSE || Z_TYPE_P(ret) == IS_TRUE)) { + /* pass */ + } 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), ""); return 0; } -#if ZEND_DEBUG - } else { - zend_error(E_CORE_ERROR, "Unknown typehint"); - return 0; -#endif } } return 1; @@ -856,34 +927,31 @@ static int zend_verify_internal_return_type(zend_function *zf, zval *ret) static void zend_verify_return_type(zend_function *zf, zval *ret) { zend_arg_info *ret_info = zf->common.arg_info - 1; - char *need_msg; + char *need_msg, *class_name; zend_class_entry *ce; - if (ret_info->class_name) { - char *class_name; - - if (Z_TYPE_P(ret) == IS_OBJECT) { - need_msg = zend_verify_arg_class_kind(ret_info, &class_name, &ce); - if (!ce || !instanceof_function(Z_OBJCE_P(ret), ce)) { - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, need_msg, class_name, "instance of ", Z_OBJCE_P(ret)->name->val); + if (ret_info->type_hint) { + if (EXPECTED(ret_info->type_hint == Z_TYPE_P(ret))) { + if (ret_info->class_name) { + need_msg = zend_verify_arg_class_kind(ret_info, &class_name, &ce); + if (!ce || !instanceof_function(Z_OBJCE_P(ret), ce)) { + zend_verify_return_error(zf, need_msg, class_name, "instance of ", Z_OBJCE_P(ret)->name->val); + } } } else if (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null) { - need_msg = zend_verify_arg_class_kind(ret_info, &class_name, &ce); - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, need_msg, class_name, zend_zval_type_name(ret), ""); - } - } else if (ret_info->type_hint) { - if (ret_info->type_hint == IS_ARRAY) { - if (Z_TYPE_P(ret) != IS_ARRAY && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) { - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, "be of the type array", "", zend_zval_type_name(ret), ""); - } - } else if (ret_info->type_hint == IS_CALLABLE) { - if (!zend_is_callable(ret, IS_CALLABLE_CHECK_SILENT, NULL) && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) { - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, "be callable", "", zend_zval_type_name(ret), ""); + if (ret_info->class_name) { + need_msg = zend_verify_arg_class_kind(ret_info, &class_name, &ce); + zend_verify_return_error(zf, need_msg, class_name, zend_zval_type_name(ret), ""); + } else if (ret_info->type_hint == IS_CALLABLE) { + if (!zend_is_callable(ret, IS_CALLABLE_CHECK_SILENT, NULL)) { + zend_verify_return_error(zf, "be callable", "", zend_zval_type_name(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 (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), ""); } -#if ZEND_DEBUG - } else { - zend_error(E_ERROR, "Unknown typehint"); -#endif } } } @@ -894,21 +962,17 @@ static inline int zend_verify_missing_return_type(zend_function *zf) char *need_msg; zend_class_entry *ce; - if (ret_info->class_name) { - char *class_name; + if (ret_info->type_hint) { + if (ret_info->class_name) { + char *class_name; - need_msg = zend_verify_arg_class_kind(ret_info, &class_name, &ce); - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, need_msg, class_name, "none", ""); - return 0; - } else if (ret_info->type_hint) { - if (ret_info->type_hint == IS_ARRAY) { - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, "be of the type array", "", "none", ""); + need_msg = zend_verify_arg_class_kind(ret_info, &class_name, &ce); + zend_verify_return_error(zf, need_msg, class_name, "none", ""); + return 0; } else if (ret_info->type_hint == IS_CALLABLE) { - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, "be callable", "", "none", ""); -#if ZEND_DEBUG + zend_verify_return_error(zf, "be callable", "", "none", ""); } else { - zend_error(E_ERROR, "Unknown typehint"); -#endif + zend_verify_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), "none", ""); } return 0; } |