diff options
| author | Dmitry Stogov <dmitry@zend.com> | 2015-02-05 11:01:07 +0300 |
|---|---|---|
| committer | Dmitry Stogov <dmitry@zend.com> | 2015-02-05 11:01:07 +0300 |
| commit | f5a9cfc33ab86e343b5cbf0d0a39a62037c32975 (patch) | |
| tree | 05433d4070b6396fdaa3d65117fa61c29d16d348 | |
| parent | 067902daee9b293eec5b319c466d3f12fa643a27 (diff) | |
| parent | 601fcc31af502fcf7aa36b8de53dfd4004fda613 (diff) | |
| download | php-git-f5a9cfc33ab86e343b5cbf0d0a39a62037c32975.tar.gz | |
Merge branch 'internal-function-return-types' of github.com:reeze/php-src into test
* 'internal-function-return-types' of github.com:reeze/php-src:
Add load time return type checking to match user land logic
Add test function arguments
Implemented internal function return types
| -rw-r--r-- | Zend/tests/return_types/internal_functions001.phpt | 15 | ||||
| -rw-r--r-- | Zend/tests/return_types/internal_functions002.phpt | 16 | ||||
| -rw-r--r-- | Zend/zend_API.c | 19 | ||||
| -rw-r--r-- | Zend/zend_API.h | 7 | ||||
| -rw-r--r-- | Zend/zend_builtin_functions.c | 19 | ||||
| -rw-r--r-- | Zend/zend_execute.c | 43 | ||||
| -rw-r--r-- | Zend/zend_vm_def.h | 4 | ||||
| -rw-r--r-- | Zend/zend_vm_execute.h | 4 |
8 files changed, 126 insertions, 1 deletions
diff --git a/Zend/tests/return_types/internal_functions001.phpt b/Zend/tests/return_types/internal_functions001.phpt new file mode 100644 index 0000000000..66c7123a4d --- /dev/null +++ b/Zend/tests/return_types/internal_functions001.phpt @@ -0,0 +1,15 @@ +--TEST-- +Return type hinting for internal functions + +--SKIPIF-- +<?php +if (!function_exists('zend_test_func')) { + print 'skip'; +} + +--FILE-- +<?php +zend_test_func(); +?> +--EXPECTF-- +Fatal error: Return value of zend_test_func() must be of the type array, null returned in %s on line %d diff --git a/Zend/tests/return_types/internal_functions002.phpt b/Zend/tests/return_types/internal_functions002.phpt new file mode 100644 index 0000000000..65838a1a67 --- /dev/null +++ b/Zend/tests/return_types/internal_functions002.phpt @@ -0,0 +1,16 @@ +--TEST-- +Return type hinting for internal functions 2 + +--SKIPIF-- +<?php +if (!function_exists('zend_test_func2')) { + print 'skip'; +} + +--FILE-- +<?php +zend_test_func2(); +echo "==DONE==\n" +?> +--EXPECTF-- +==DONE== diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 7abbf31cb2..69327778e3 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2002,6 +2002,13 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio internal_function->num_args--; } if (info->type_hint) { + if (info->class_name) { + ZEND_ASSERT(info->type_hint == IS_OBJECT); + if (!strcasecmp(info->class_name, "self") && !scope) { + zend_error(E_CORE_ERROR, "Cannot declare a return type of self outside of a class scope"); + } + } + internal_function->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE; } } else { @@ -2206,6 +2213,18 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio zend_error(error_type, "Method %s::%s() cannot be static", scope->name->val, __debugInfo->common.function_name->val); } } + + if (ctor && ctor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && ctor->common.fn_flags & ZEND_ACC_CTOR) { + zend_error(E_CORE_ERROR, "Constructor %s::%s() cannot declare a return type", scope->name->val, ctor->common.function_name->val); + } + + if (dtor && dtor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && dtor->common.fn_flags & ZEND_ACC_DTOR) { + zend_error(E_CORE_ERROR, "Destructor %s::%s() cannot declare a return type", scope->name->val, dtor->common.function_name->val); + } + + if (clone && clone->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && dtor->common.fn_flags & ZEND_ACC_DTOR) { + zend_error(E_CORE_ERROR, "%s::%s() cannot declare a return type", scope->name->val, clone->common.function_name->val); + } efree((char*)lc_class_name); } return SUCCESS; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index e3be8d7138..746021e367 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -105,6 +105,13 @@ typedef struct _zend_fcall_info_cache { #define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, NULL, type_hint, pass_by_ref, allow_null, 0 }, #define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, NULL, 0, pass_by_ref, 0, 1 }, + +#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, class_name, allow_null) \ + static const zend_internal_arg_info name[] = { \ + { (const char*)(zend_uintptr_t)(required_num_args), class_name, type, return_reference, allow_null, 0 }, +#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(name, type, class_name, allow_null) \ + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, 0, -1, type, class_name, allow_null) + #define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) \ static const zend_internal_arg_info name[] = { \ { (const char*)(zend_uintptr_t)(required_num_args), NULL, 0, return_reference, 0, 0 }, diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 2fce4b7964..fe7c8f7556 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -87,6 +87,7 @@ static ZEND_FUNCTION(debug_backtrace); static ZEND_FUNCTION(debug_print_backtrace); #if ZEND_DEBUG static ZEND_FUNCTION(zend_test_func); +static ZEND_FUNCTION(zend_test_func2); #ifdef ZTS static ZEND_FUNCTION(zend_thread_id); #endif @@ -243,6 +244,14 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_extension_loaded, 0, 0, 1) ZEND_ARG_INFO(0, extension_name) ZEND_END_ARG_INFO() + +#ifdef ZEND_DEBUG +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_func, IS_ARRAY, NULL, 0) +ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_func2, IS_ARRAY, NULL, 1) +ZEND_END_ARG_INFO() +#endif + /* }}} */ static const zend_function_entry builtin_functions[] = { /* {{{ */ @@ -304,7 +313,8 @@ static const zend_function_entry builtin_functions[] = { /* {{{ */ ZEND_FE(debug_backtrace, arginfo_debug_backtrace) ZEND_FE(debug_print_backtrace, arginfo_debug_print_backtrace) #if ZEND_DEBUG - ZEND_FE(zend_test_func, NULL) + ZEND_FE(zend_test_func, arginfo_zend_test_func) + ZEND_FE(zend_test_func2, arginfo_zend_test_func2) #ifdef ZTS ZEND_FE(zend_thread_id, NULL) #endif @@ -1954,6 +1964,13 @@ ZEND_FUNCTION(zend_test_func) zend_get_parameters(ZEND_NUM_ARGS(), 2, &arg1, &arg2); } +ZEND_FUNCTION(zend_test_func2) +{ + zval *arg1, *arg2; + + zend_get_parameters(ZEND_NUM_ARGS(), 2, &arg1, &arg2); +} + #ifdef ZTS ZEND_FUNCTION(zend_thread_id) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 2c457c4533..62064dcd5f 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -771,6 +771,49 @@ ZEND_API void zend_verify_return_error(int error_type, const zend_function *zf, fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind); } +#if ZEND_DEBUG +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; + 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; + } + } 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), ""); + return 0; + } +#if ZEND_DEBUG + } else { + zend_error(E_CORE_ERROR, "Unknown typehint"); + return 0; +#endif + } + } + return 1; +} +#endif + static void zend_verify_return_type(zend_function *zf, zval *ret) { zend_arg_info *ret_info = zf->common.arg_info - 1; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 484ee44bd8..8a1bda8cd7 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2875,6 +2875,10 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY) zend_vm_stack_free_args(call); zend_vm_stack_free_call_frame(call); + ZEND_ASSERT( + !(fbc->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || + zend_verify_internal_return_type(fbc, EX_VAR(opline->result.var))); + if (!RETURN_VALUE_USED(opline)) { zval_ptr_dtor(EX_VAR(opline->result.var)); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 80a31331e5..19e1a741b5 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -601,6 +601,10 @@ static int ZEND_FASTCALL ZEND_DO_FCALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zend_vm_stack_free_args(call); zend_vm_stack_free_call_frame(call); + ZEND_ASSERT( + !(fbc->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || + zend_verify_internal_return_type(fbc, EX_VAR(opline->result.var))); + if (!RETURN_VALUE_USED(opline)) { zval_ptr_dtor(EX_VAR(opline->result.var)); } |
