summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2015-02-05 11:01:07 +0300
committerDmitry Stogov <dmitry@zend.com>2015-02-05 11:01:07 +0300
commitf5a9cfc33ab86e343b5cbf0d0a39a62037c32975 (patch)
tree05433d4070b6396fdaa3d65117fa61c29d16d348
parent067902daee9b293eec5b319c466d3f12fa643a27 (diff)
parent601fcc31af502fcf7aa36b8de53dfd4004fda613 (diff)
downloadphp-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.phpt15
-rw-r--r--Zend/tests/return_types/internal_functions002.phpt16
-rw-r--r--Zend/zend_API.c19
-rw-r--r--Zend/zend_API.h7
-rw-r--r--Zend/zend_builtin_functions.c19
-rw-r--r--Zend/zend_execute.c43
-rw-r--r--Zend/zend_vm_def.h4
-rw-r--r--Zend/zend_vm_execute.h4
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));
}