summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2017-01-13 11:37:46 +0300
committerDmitry Stogov <dmitry@zend.com>2017-01-13 11:37:46 +0300
commit141d1ba9801f742dc5d9ccd06e02b94284c4deb7 (patch)
treecc9d1730b73a2b147d14857bb967e6d55df5af90
parent28391c30ca008013267592ab2a2eebce3da3f3b0 (diff)
downloadphp-git-141d1ba9801f742dc5d9ccd06e02b94284c4deb7.tar.gz
Introduced "zend_type" - an abstraction for type-hinting representation.
-rw-r--r--Zend/zend_API.c47
-rw-r--r--Zend/zend_API.h30
-rw-r--r--Zend/zend_builtin_functions.c4
-rw-r--r--Zend/zend_compile.c103
-rw-r--r--Zend/zend_compile.h12
-rw-r--r--Zend/zend_execute.c242
-rw-r--r--Zend/zend_inheritance.c69
-rw-r--r--Zend/zend_opcode.c21
-rw-r--r--Zend/zend_types.h58
-rw-r--r--Zend/zend_vm_def.h8
-rw-r--r--Zend/zend_vm_execute.h40
-rw-r--r--ext/com_dotnet/com_handlers.c2
-rw-r--r--ext/opcache/Optimizer/dfa_pass.c4
-rw-r--r--ext/opcache/Optimizer/zend_inference.c28
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.c6
-rw-r--r--ext/opcache/ZendAccelerator.c16
-rw-r--r--ext/opcache/zend_file_cache.c22
-rw-r--r--ext/opcache/zend_persist.c8
-rw-r--r--ext/opcache/zend_persist_calc.c8
-rw-r--r--ext/reflection/php_reflection.c88
20 files changed, 413 insertions, 403 deletions
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index e0c7396cdc..bbea611697 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -2187,11 +2187,15 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
/* Don't count the variadic argument */
internal_function->num_args--;
}
- if (info->type_hint) {
- if (info->class_name) {
- ZEND_ASSERT(info->type_hint == IS_OBJECT);
- if (!scope && (!strcasecmp(info->class_name, "self") || !strcasecmp(info->class_name, "parent"))) {
- zend_error_noreturn(E_CORE_ERROR, "Cannot declare a return type of %s outside of a class scope", info->class_name);
+ if (ZEND_TYPE_IS_SET(info->type)) {
+ if (ZEND_TYPE_IS_CLASS(info->type)) {
+ const char *type_name = (const char*)info->type;
+
+ if (type_name[0] == '?') {
+ type_name++;
+ }
+ if (!scope && (!strcasecmp(type_name, "self") || !strcasecmp(type_name, "parent"))) {
+ zend_error_noreturn(E_CORE_ERROR, "Cannot declare a return type of %s outside of a class scope", type_name);
}
}
@@ -2248,14 +2252,43 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
if (reg_function->common.arg_info && reg_function->common.num_args) {
uint32_t i;
for (i = 0; i < reg_function->common.num_args; i++) {
- if (reg_function->common.arg_info[i].class_name ||
- reg_function->common.arg_info[i].type_hint) {
+ if (ZEND_TYPE_IS_SET(reg_function->common.arg_info[i].type)) {
reg_function->common.fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
break;
}
}
}
+ if (reg_function->common.arg_info &&
+ (reg_function->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) {
+ /* convert "const char*" class type names into "zend_string*" */
+ uint32_t i;
+ uint32_t num_args = reg_function->common.num_args + 1;
+ zend_arg_info *arg_info = reg_function->common.arg_info - 1;
+ zend_arg_info *new_arg_info;
+
+ if (reg_function->common.fn_flags & ZEND_ACC_VARIADIC) {
+ num_args++;
+ }
+ new_arg_info = malloc(sizeof(zend_arg_info) * num_args);
+ memcpy(new_arg_info, arg_info, sizeof(zend_arg_info) * num_args);
+ reg_function->common.arg_info = new_arg_info + 1;
+ for (i = 0; i < num_args; i++) {
+ if (ZEND_TYPE_IS_CLASS(new_arg_info[i].type)) {
+ const char *class_name = (const char*)new_arg_info[i].type;
+ zend_bool allow_null = 0;
+ zend_string *str;
+
+ if (class_name[0] == '?') {
+ class_name++;
+ allow_null = 1;
+ }
+ str = zend_new_interned_string(zend_string_init(class_name, strlen(class_name), 1));
+ new_arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(str, allow_null);
+ }
+ }
+ }
+
if (scope) {
/* Look for ctor, dtor, clone
* If it's an old-style constructor, store it only if we don't have
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index 704779346a..55f5e55d62 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -97,24 +97,30 @@ typedef struct _zend_fcall_info_cache {
#define ZEND_FE_END { NULL, NULL, NULL, 0, 0 }
-#define ZEND_ARG_INFO(pass_by_ref, name) { #name, NULL, 0, pass_by_ref, 0, 0 },
-#define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, NULL, 0, pass_by_ref, 0, 0 },
-#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, #classname, IS_OBJECT, pass_by_ref, allow_null, 0 },
-#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, NULL, IS_ARRAY, pass_by_ref, allow_null, 0 },
-#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) { #name, NULL, IS_CALLABLE, pass_by_ref, allow_null, 0 },
-#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_ARG_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 0},
+#define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, 0, pass_by_ref, 0},
+#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), pass_by_ref, 0 },
+#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE(IS_ARRAY, allow_null), pass_by_ref, 0 },
+#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE(IS_CALLABLE, allow_null), pass_by_ref, 0 },
+#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE(type_hint, allow_null), pass_by_ref, 0 },
+#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 1 },
-#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, class_name, allow_null) \
+#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, classname, 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)
+ { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), return_reference, 0 },
+#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO(name, class_name, allow_null) \
+ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, 0, -1, class_name, allow_null)
+
+#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \
+ static const zend_internal_arg_info name[] = { \
+ { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_ENCODE(type, allow_null), return_reference, 0 },
+#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(name, type, allow_null) \
+ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, 0, -1, type, 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 },
+ { (const char*)(zend_uintptr_t)(required_num_args), 0, return_reference, 0 },
#define ZEND_BEGIN_ARG_INFO(name, _unused) \
ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1)
#define ZEND_END_ARG_INFO() };
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index f94467239c..40ab240a8c 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -256,9 +256,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_extension_loaded, 0, 0, 1)
ZEND_END_ARG_INFO()
#if ZEND_DEBUG
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_func, IS_ARRAY, NULL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_func, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_func2, IS_ARRAY, NULL, 1)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_func2, IS_ARRAY, 1)
ZEND_END_ARG_INFO()
#endif
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 488920b006..c289fefb9e 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -1274,17 +1274,17 @@ static void zend_mark_function_as_generator() /* {{{ */
if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
zend_arg_info return_info = CG(active_op_array)->arg_info[-1];
- if (return_info.type_hint != IS_ITERABLE) {
+ if (ZEND_TYPE_CODE(return_info.type) != IS_ITERABLE) {
const char *msg = "Generators may only declare a return type of Generator, Iterator, Traversable, or iterable, %s is not permitted";
- if (!return_info.class_name) {
- zend_error_noreturn(E_COMPILE_ERROR, msg, zend_get_type_by_const(return_info.type_hint));
+ if (!ZEND_TYPE_IS_CLASS(return_info.type)) {
+ zend_error_noreturn(E_COMPILE_ERROR, msg, zend_get_type_by_const(ZEND_TYPE_CODE(return_info.type)));
}
- if (!zend_string_equals_literal_ci(return_info.class_name, "Traversable")
- && !zend_string_equals_literal_ci(return_info.class_name, "Iterator")
- && !zend_string_equals_literal_ci(return_info.class_name, "Generator")) {
- zend_error_noreturn(E_COMPILE_ERROR, msg, ZSTR_VAL(return_info.class_name));
+ if (!zend_string_equals_literal_ci(ZEND_TYPE_NAME(return_info.type), "Traversable")
+ && !zend_string_equals_literal_ci(ZEND_TYPE_NAME(return_info.type), "Iterator")
+ && !zend_string_equals_literal_ci(ZEND_TYPE_NAME(return_info.type), "Generator")) {
+ zend_error_noreturn(E_COMPILE_ERROR, msg, ZSTR_VAL(ZEND_TYPE_NAME(return_info.type)));
}
}
}
@@ -2323,26 +2323,26 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
static void zend_emit_return_type_check(
znode *expr, zend_arg_info *return_info, zend_bool implicit) /* {{{ */
{
- /* `return ...;` is illegal in a void function (but `return;` isn't) */
- if (return_info->type_hint == IS_VOID) {
- if (expr) {
- if (expr->op_type == IS_CONST && Z_TYPE(expr->u.constant) == IS_NULL) {
- zend_error_noreturn(E_COMPILE_ERROR,
- "A void function must not return a value "
- "(did you mean \"return;\" instead of \"return null;\"?)");
- } else {
- zend_error_noreturn(E_COMPILE_ERROR, "A void function must not return a value");
+ if (ZEND_TYPE_IS_SET(return_info->type)) {
+ zend_op *opline;
+
+ /* `return ...;` is illegal in a void function (but `return;` isn't) */
+ if (ZEND_TYPE_CODE(return_info->type) == IS_VOID) {
+ if (expr) {
+ if (expr->op_type == IS_CONST && Z_TYPE(expr->u.constant) == IS_NULL) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "A void function must not return a value "
+ "(did you mean \"return;\" instead of \"return null;\"?)");
+ } else {
+ zend_error_noreturn(E_COMPILE_ERROR, "A void function must not return a value");
+ }
}
+ /* we don't need run-time check */
+ return;
}
- /* we don't need run-time check */
- return;
- }
-
- if (return_info->type_hint != IS_UNDEF) {
- zend_op *opline;
if (!expr && !implicit) {
- if (return_info->allow_null) {
+ if (ZEND_TYPE_ALLOW_NULL(return_info->type)) {
zend_error_noreturn(E_COMPILE_ERROR,
"A function with return type must return a value "
"(did you mean \"return null;\" instead of \"return;\"?)");
@@ -2353,11 +2353,11 @@ static void zend_emit_return_type_check(
}
if (expr && expr->op_type == IS_CONST) {
- if ((return_info->type_hint == Z_TYPE(expr->u.constant))
- ||((return_info->type_hint == _IS_BOOL)
+ if ((ZEND_TYPE_CODE(return_info->type) == Z_TYPE(expr->u.constant))
+ ||((ZEND_TYPE_CODE(return_info->type) == _IS_BOOL)
&& (Z_TYPE(expr->u.constant) == IS_FALSE
|| Z_TYPE(expr->u.constant) == IS_TRUE))
- || (return_info->allow_null
+ || (ZEND_TYPE_ALLOW_NULL(return_info->type)
&& Z_TYPE(expr->u.constant) == IS_NULL)) {
/* we don't need run-time check */
return;
@@ -2369,7 +2369,7 @@ static void zend_emit_return_type_check(
opline->result_type = expr->op_type = IS_TMP_VAR;
opline->result.var = expr->u.op.var = get_temporary_variable(CG(active_op_array));
}
- if (return_info->class_name) {
+ if (ZEND_TYPE_IS_CLASS(return_info->type)) {
opline->op2.num = CG(active_op_array)->cache_size;
CG(active_op_array)->cache_size += sizeof(void*);
} else {
@@ -5038,10 +5038,10 @@ ZEND_API void zend_set_function_arg_flags(zend_function *func) /* {{{ */
}
/* }}} */
-static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info) /* {{{ */
+static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info, zend_bool allow_null) /* {{{ */
{
if (ast->kind == ZEND_AST_TYPE) {
- arg_info->type_hint = ast->attr;
+ arg_info->type = ZEND_TYPE_ENCODE(ast->attr, allow_null);
} else {
zend_string *class_name = zend_ast_get_str(ast);
zend_uchar type = zend_lookup_builtin_type_by_name(class_name);
@@ -5052,7 +5052,7 @@ static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info) /* {{{
"Scalar type declaration '%s' must be unqualified",
ZSTR_VAL(zend_string_tolower(class_name)));
}
- arg_info->type_hint = type;
+ arg_info->type = ZEND_TYPE_ENCODE(type, allow_null);
} else {
uint32_t fetch_type = zend_get_class_fetch_type_ast(ast);
if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
@@ -5063,8 +5063,7 @@ static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info) /* {{{
zend_string_addref(class_name);
}
- arg_info->type_hint = IS_OBJECT;
- arg_info->class_name = class_name;
+ arg_info->type = ZEND_TYPE_ENCODE_CLASS(class_name, allow_null);
}
}
}
@@ -5078,23 +5077,23 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
zend_arg_info *arg_infos;
if (return_type_ast) {
+ zend_bool allow_null = 0;
+
/* Use op_array->arg_info[-1] for return type */
arg_infos = safe_emalloc(sizeof(zend_arg_info), list->children + 1, 0);
arg_infos->name = NULL;
arg_infos->pass_by_reference = (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
arg_infos->is_variadic = 0;
- arg_infos->type_hint = 0;
- arg_infos->allow_null = 0;
- arg_infos->class_name = NULL;
+ arg_infos->type = 0;
if (return_type_ast->attr & ZEND_TYPE_NULLABLE) {
- arg_infos->allow_null = 1;
+ allow_null = 1;
return_type_ast->attr &= ~ZEND_TYPE_NULLABLE;
}
- zend_compile_typename(return_type_ast, arg_infos);
+ zend_compile_typename(return_type_ast, arg_infos, allow_null);
- if (arg_infos->type_hint == IS_VOID && arg_infos->allow_null) {
+ if (ZEND_TYPE_CODE(arg_infos->type) == IS_VOID && ZEND_TYPE_ALLOW_NULL(arg_infos->type)) {
zend_error_noreturn(E_COMPILE_ERROR, "Void type cannot be nullable");
}
@@ -5171,11 +5170,11 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
arg_info->name = zend_string_copy(name);
arg_info->pass_by_reference = is_ref;
arg_info->is_variadic = is_variadic;
- arg_info->type_hint = 0;
- arg_info->allow_null = 1;
- arg_info->class_name = NULL;
+ /* TODO: Keep compatibility, but may be better reset "allow_null" ??? */
+ arg_info->type = ZEND_TYPE_ENCODE(0, 1);
if (type_ast) {
+ zend_bool allow_null;
zend_bool has_null_default = default_ast
&& (Z_TYPE(default_node.u.constant) == IS_NULL
|| (Z_TYPE(default_node.u.constant) == IS_CONSTANT
@@ -5183,17 +5182,17 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
zend_bool is_explicitly_nullable = (type_ast->attr & ZEND_TYPE_NULLABLE) == ZEND_TYPE_NULLABLE;
op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
- arg_info->allow_null = has_null_default || is_explicitly_nullable;
+ allow_null = has_null_default || is_explicitly_nullable;
type_ast->attr &= ~ZEND_TYPE_NULLABLE;
- zend_compile_typename(type_ast, arg_info);
+ zend_compile_typename(type_ast, arg_info, allow_null);
- if (arg_info->type_hint == IS_VOID) {
+ if (ZEND_TYPE_CODE(arg_info->type) == IS_VOID) {
zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type");
}
if (type_ast->kind == ZEND_AST_TYPE) {
- if (arg_info->type_hint == IS_ARRAY) {
+ if (ZEND_TYPE_CODE(arg_info->type) == IS_ARRAY) {
if (default_ast && !has_null_default
&& Z_TYPE(default_node.u.constant) != IS_ARRAY
&& !Z_CONSTANT(default_node.u.constant)
@@ -5201,7 +5200,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
"with array type can only be an array or NULL");
}
- } else if (arg_info->type_hint == IS_CALLABLE && default_ast) {
+ } else if (ZEND_TYPE_CODE(arg_info->type) == IS_CALLABLE && default_ast) {
if (!has_null_default && !Z_CONSTANT(default_node.u.constant)) {
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
"with callable type can only be NULL");
@@ -5209,10 +5208,10 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
}
} else {
if (default_ast && !has_null_default && !Z_CONSTANT(default_node.u.constant)) {
- if (arg_info->class_name) {
+ if (ZEND_TYPE_IS_CLASS(arg_info->type)) {
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
"with a class type can only be NULL");
- } else switch (arg_info->type_hint) {
+ } else switch (ZEND_TYPE_CODE(arg_info->type)) {
case IS_DOUBLE:
if (Z_TYPE(default_node.u.constant) != IS_DOUBLE && Z_TYPE(default_node.u.constant) != IS_LONG) {
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
@@ -5228,10 +5227,10 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
break;
default:
- if (!ZEND_SAME_FAKE_TYPE(arg_info->type_hint, Z_TYPE(default_node.u.constant))) {
+ if (!ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(arg_info->type), Z_TYPE(default_node.u.constant))) {
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
"with a %s type can only be %s or NULL",
- zend_get_type_by_const(arg_info->type_hint), zend_get_type_by_const(arg_info->type_hint));
+ zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type)), zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type)));
}
break;
}
@@ -5240,13 +5239,13 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
/* Allocate cache slot to speed-up run-time class resolution */
if (opline->opcode == ZEND_RECV_INIT) {
- if (arg_info->class_name) {
+ if (ZEND_TYPE_IS_CLASS(arg_info->type)) {
zend_alloc_cache_slot(opline->op2.constant);
} else {
Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = -1;
}
} else {
- if (arg_info->class_name) {
+ if (ZEND_TYPE_IS_CLASS(arg_info->type)) {
opline->op2.num = op_array->cache_size;
op_array->cache_size += sizeof(void*);
} else {
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index d848a2a35e..5018602a1f 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -322,20 +322,16 @@ typedef struct _zend_class_constant {
/* arg_info for internal functions */
typedef struct _zend_internal_arg_info {
const char *name;
- const char *class_name;
- zend_uchar type_hint;
+ zend_type type;
zend_uchar pass_by_reference;
- zend_bool allow_null;
zend_bool is_variadic;
} zend_internal_arg_info;
/* arg_info for user functions */
typedef struct _zend_arg_info {
zend_string *name;
- zend_string *class_name;
- zend_uchar type_hint;
+ zend_type type;
zend_uchar pass_by_reference;
- zend_bool allow_null;
zend_bool is_variadic;
} zend_arg_info;
@@ -346,10 +342,8 @@ typedef struct _zend_arg_info {
*/
typedef struct _zend_internal_function_info {
zend_uintptr_t required_num_args;
- const char *class_name;
- zend_uchar type_hint;
+ zend_type type;
zend_bool return_reference;
- zend_bool allow_null;
zend_bool _is_variadic;
} zend_internal_function_info;
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 3ad663445b..ad788aff44 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -607,25 +607,6 @@ static inline int make_real_object(zval *object)
return 1;
}
-static zend_class_entry *zend_verify_internal_arg_class_kind(
- const zend_internal_arg_info *cur_arg_info)
-{
- zend_string *key;
- zend_class_entry *ce;
- ALLOCA_FLAG(use_heap);
-
- ZSTR_ALLOCA_INIT(key, cur_arg_info->class_name, strlen(cur_arg_info->class_name), use_heap);
- ce = zend_fetch_class(key, (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
- ZSTR_ALLOCA_FREE(key, use_heap);
-
- return ce;
-}
-
-static zend_always_inline zend_class_entry* zend_verify_arg_class_kind(const zend_arg_info *cur_arg_info)
-{
- return zend_fetch_class(cur_arg_info->class_name, (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
-}
-
static ZEND_COLD void zend_verify_type_error_common(
const zend_function *zf, const zend_arg_info *arg_info,
const zend_class_entry *ce, zval *value,
@@ -644,46 +625,46 @@ static ZEND_COLD void zend_verify_type_error_common(
*fclass = "";
}
- switch (arg_info->type_hint) {
- case IS_OBJECT:
- if (ce) {
- if (ce->ce_flags & ZEND_ACC_INTERFACE) {
- *need_msg = "implement interface ";
- is_interface = 1;
- } else {
- *need_msg = "be an instance of ";
- }
- *need_kind = ZSTR_VAL(ce->name);
+ if (ZEND_TYPE_IS_CLASS(arg_info->type)) {
+ if (ce) {
+ if (ce->ce_flags & ZEND_ACC_INTERFACE) {
+ *need_msg = "implement interface ";
+ is_interface = 1;
} else {
- /* We don't know whether it's a class or interface, assume it's a class */
*need_msg = "be an instance of ";
- *need_kind = zf->common.type == ZEND_INTERNAL_FUNCTION
- ? ((zend_internal_arg_info *) arg_info)->class_name
- : ZSTR_VAL(arg_info->class_name);
}
- break;
- case IS_CALLABLE:
- *need_msg = "be callable";
- *need_kind = "";
- break;
- case IS_ITERABLE:
- *need_msg = "be iterable";
- *need_kind = "";
- break;
- default:
- *need_msg = "be of the type ";
- *need_kind = zend_get_type_by_const(arg_info->type_hint);
- break;
+ *need_kind = ZSTR_VAL(ce->name);
+ } else {
+ /* We don't know whether it's a class or interface, assume it's a class */
+
+ *need_msg = "be an instance of ";
+ *need_kind = ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type));
+ }
+ } else {
+ switch (ZEND_TYPE_CODE(arg_info->type)) {
+ case IS_CALLABLE:
+ *need_msg = "be callable";
+ *need_kind = "";
+ break;
+ case IS_ITERABLE:
+ *need_msg = "be iterable";
+ *need_kind = "";
+ break;
+ default:
+ *need_msg = "be of the type ";
+ *need_kind = zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type));
+ break;
+ }
}
- if (arg_info->allow_null) {
+ if (ZEND_TYPE_ALLOW_NULL(arg_info->type)) {
*need_or_null = is_interface ? " or be null" : " or null";
} else {
*need_or_null = "";
}
if (value) {
- if (arg_info->type_hint == IS_OBJECT && Z_TYPE_P(value) == IS_OBJECT) {
+ if (ZEND_TYPE_IS_CLASS(arg_info->type) && Z_TYPE_P(value) == IS_OBJECT) {
*given_msg = "instance of ";
*given_kind = ZSTR_VAL(Z_OBJCE_P(value)->name);
} else {
@@ -803,137 +784,48 @@ 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 zend_always_inline zend_bool zend_check_internal_type(
- const zend_function *zf, const zend_internal_arg_info *arg_info,
- zval *arg, zend_class_entry **ce, zend_bool is_return_type)
-{
- if (!arg_info->type_hint) {
- return 1;
- }
-
- ZVAL_DEREF(arg);
- if (EXPECTED(arg_info->type_hint == Z_TYPE_P(arg))) {
- if (arg_info->class_name) {
- *ce = zend_verify_internal_arg_class_kind(arg_info);
- return *ce && instanceof_function(Z_OBJCE_P(arg), *ce);
- }
- return 1;
- }
-
- if (Z_TYPE_P(arg) == IS_NULL && arg_info->allow_null) {
- return 1;
- }
-
- if (arg_info->class_name) {
- *ce = zend_verify_internal_arg_class_kind(arg_info);
- return 0;
- } else if (arg_info->type_hint == IS_CALLABLE) {
- return zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL);
- } else if (arg_info->type_hint == IS_ITERABLE) {
- return zend_is_iterable(arg);
- } else if (arg_info->type_hint == _IS_BOOL &&
- EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) {
- return 1;
- } else if (is_return_type) {
- /* Internal return types are always strict */
- return 0;
- } else {
- /* Internal parameter types honor strict_types */
- return zend_verify_scalar_type_hint(arg_info->type_hint, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)));
- }
-}
-
-static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zval *arg)
-{
- const zend_internal_arg_info *cur_arg_info;
- zend_class_entry *ce = NULL;
-
- if (EXPECTED(arg_num <= zf->internal_function.num_args)) {
- cur_arg_info = &zf->internal_function.arg_info[arg_num-1];
- } 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 1;
- }
-
- if (UNEXPECTED(!zend_check_internal_type(zf, cur_arg_info, arg, &ce, 0))) {
- zend_verify_arg_error(zf, (const zend_arg_info *) cur_arg_info, arg_num, ce, 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 zend_bool zend_check_type(
- const zend_function *zf, const zend_arg_info *arg_info,
+ zend_type type,
zval *arg, zend_class_entry **ce, void **cache_slot,
- zval *default_value, zend_bool is_return_type)
+ zval *default_value, zend_class_entry *scope,
+ zend_bool is_return_type)
{
- if (!arg_info->type_hint) {
+ if (!ZEND_TYPE_IS_SET(type)) {
return 1;
}
ZVAL_DEREF(arg);
- if (EXPECTED(arg_info->type_hint == Z_TYPE_P(arg))) {
- if (arg_info->class_name) {
- if (EXPECTED(*cache_slot)) {
- *ce = (zend_class_entry *) *cache_slot;
- } else {
- *ce = zend_verify_arg_class_kind(arg_info);
- if (UNEXPECTED(!*ce)) {
- return 0;
- }
- *cache_slot = (void *) *ce;
- }
- if (UNEXPECTED(!instanceof_function(Z_OBJCE_P(arg), *ce))) {
+ if (ZEND_TYPE_IS_CLASS(type)) {
+ if (EXPECTED(*cache_slot)) {
+ *ce = (zend_class_entry *) *cache_slot;
+ } else {
+ *ce = zend_fetch_class(ZEND_TYPE_NAME(type), (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
+ if (UNEXPECTED(!*ce)) {
return 0;
}
+ *cache_slot = (void *) *ce;
+ }
+ if (EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
+ return instanceof_function(Z_OBJCE_P(arg), *ce);
}
+ } else if (EXPECTED(ZEND_TYPE_CODE(type) == Z_TYPE_P(arg))) {
return 1;
}
- if (Z_TYPE_P(arg) == IS_NULL && (arg_info->allow_null || (default_value && is_null_constant(zf->common.scope, default_value)))) {
+ if (Z_TYPE_P(arg) == IS_NULL && (ZEND_TYPE_ALLOW_NULL(type) || (default_value && is_null_constant(scope, default_value)))) {
/* Null passed to nullable type */
return 1;
}
- if (UNEXPECTED(arg_info->class_name)) {
- /* This is always an error - we fetch the class name for the error message here */
- if (EXPECTED(*cache_slot)) {
- *ce = (zend_class_entry *) *cache_slot;
- } else {
- *ce = zend_verify_arg_class_kind(arg_info);
- if (*ce) {
- *cache_slot = (void *) *ce;
- }
- }
- return 0;
- } else if (arg_info->type_hint == IS_CALLABLE) {
+ if (ZEND_TYPE_CODE(type) == IS_CALLABLE) {
return zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL);
- } else if (arg_info->type_hint == IS_ITERABLE) {
+ } else if (ZEND_TYPE_CODE(type) == IS_ITERABLE) {
return zend_is_iterable(arg);
- } else if (arg_info->type_hint == _IS_BOOL &&
+ } else if (ZEND_TYPE_CODE(type) == _IS_BOOL &&
EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) {
return 1;
} else {
- return zend_verify_scalar_type_hint(arg_info->type_hint, arg,
+ return zend_verify_scalar_type_hint(ZEND_TYPE_CODE(type), arg,
is_return_type ? ZEND_RET_USES_STRICT_TYPES() : ZEND_ARG_USES_STRICT_TYPES());
}
@@ -954,7 +846,7 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
return 1;
}
- if (UNEXPECTED(!zend_check_type(zf, cur_arg_info, arg, &ce, cache_slot, default_value, 0))) {
+ if (UNEXPECTED(!zend_check_type(cur_arg_info->type, arg, &ce, cache_slot, default_value, zf->common.scope, 0))) {
zend_verify_arg_error(zf, cur_arg_info, arg_num, ce, arg);
return 0;
}
@@ -962,6 +854,25 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
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);
+ void *dummy_cache_slot;
+
+ for (i = 0; i < num_args; ++i) {
+ dummy_cache_slot = NULL;
+ if (UNEXPECTED(!zend_verify_arg_type(fbc, i + 1, p, NULL, &dummy_cache_slot))) {
+ EG(current_execute_data) = call->prev_execute_data;
+ zend_vm_stack_free_args(call);
+ return 0;
+ }
+ p++;
+ }
+ return 1;
+}
+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data)
{
zend_execute_data *ptr = EX(prev_execute_data);
@@ -1040,13 +951,14 @@ static int zend_verify_internal_return_type(zend_function *zf, zval *ret)
{
zend_internal_arg_info *ret_info = zf->internal_function.arg_info - 1;
zend_class_entry *ce = NULL;
+ void *dummy_cache_slot = NULL;
- if (UNEXPECTED(ret_info->type_hint == IS_VOID && Z_TYPE_P(ret) != IS_NULL)) {
+ if (UNEXPECTED(ZEND_TYPE_CODE(ret_info->type) == IS_VOID && Z_TYPE_P(ret) != IS_NULL)) {
zend_verify_void_return_error(zf, zend_zval_type_name(ret), "");
return 0;
}
- if (UNEXPECTED(!zend_check_internal_type(zf, ret_info, ret, &ce, 1))) {
+ if (UNEXPECTED(!zend_check_type(ret_info->type, ret, &ce, &dummy_cache_slot, NULL, NULL, 1))) {
zend_verify_internal_return_error(zf, ce, ret);
return 0;
}
@@ -1060,7 +972,7 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *
zend_arg_info *ret_info = zf->common.arg_info - 1;
zend_class_entry *ce = NULL;
- if (UNEXPECTED(!zend_check_type(zf, ret_info, ret, &ce, cache_slot, NULL, 1))) {
+ if (UNEXPECTED(!zend_check_type(ret_info->type, ret, &ce, cache_slot, NULL, NULL, 1))) {
zend_verify_return_error(zf, ce, ret);
}
}
@@ -1069,13 +981,13 @@ static ZEND_COLD int zend_verify_missing_return_type(zend_function *zf, void **c
{
zend_arg_info *ret_info = zf->common.arg_info - 1;
- if (ret_info->type_hint && UNEXPECTED(ret_info->type_hint != IS_VOID)) {
+ if (ZEND_TYPE_IS_SET(ret_info->type) && UNEXPECTED(ZEND_TYPE_CODE(ret_info->type) != IS_VOID)) {
zend_class_entry *ce = NULL;
- if (ret_info->class_name) {
+ if (ZEND_TYPE_IS_CLASS(ret_info->type)) {
if (EXPECTED(*cache_slot)) {
ce = (zend_class_entry*) *cache_slot;
} else {
- ce = zend_verify_arg_class_kind(ret_info);
+ ce = zend_fetch_class(ZEND_TYPE_NAME(ret_info->type), (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
if (ce) {
*cache_slot = (void*)ce;
}
@@ -3072,7 +2984,7 @@ ZEND_API zval *zend_get_zval_ptr(int op_type, const znode_op *node, const zend_e
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_verify_arg_type(zf, arg_num, arg, NULL, NULL);
}
ZEND_API int ZEND_FASTCALL zend_check_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot)
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index fd1345f844..9f64162e81 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -169,11 +169,11 @@ char *zend_visibility_string(uint32_t fn_flags) /* {{{ */
static zend_always_inline zend_bool zend_iterable_compatibility_check(zend_arg_info *arg_info) /* {{{ */
{
- if (arg_info->type_hint == IS_ARRAY) {
+ if (ZEND_TYPE_CODE(arg_info->type) == IS_ARRAY) {
return 1;
}
- if (arg_info->class_name && zend_string_equals_literal_ci(arg_info->class_name, "Traversable")) {
+ if (ZEND_TYPE_IS_CLASS(arg_info->type) && zend_string_equals_literal_ci(ZEND_TYPE_NAME(arg_info->type), "Traversable")) {
return 1;
}
@@ -183,47 +183,33 @@ static zend_always_inline zend_bool zend_iterable_compatibility_check(zend_arg_i
static int zend_do_perform_type_hint_check(const zend_function *fe, zend_arg_info *fe_arg_info, const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */
{
- if (ZEND_LOG_XOR(fe_arg_info->class_name, proto_arg_info->class_name)) {
+ if (ZEND_LOG_XOR(ZEND_TYPE_IS_CLASS(fe_arg_info->type), ZEND_TYPE_IS_CLASS(proto_arg_info->type))) {
/* Only one has a type declaration and the other one doesn't */
return 0;
}
- if (fe_arg_info->class_name) {
+ if (ZEND_TYPE_IS_CLASS(fe_arg_info->type)) {
zend_string *fe_class_name, *proto_class_name;
const char *class_name;
- if (fe->type == ZEND_INTERNAL_FUNCTION) {
- fe_class_name = NULL;
- class_name = ((zend_internal_arg_info*)fe_arg_info)->class_name;
- } else {
- fe_class_name = fe_arg_info->class_name;
- class_name = ZSTR_VAL(fe_arg_info->class_name);
- }
+ fe_class_name = ZEND_TYPE_NAME(fe_arg_info->type);
+ class_name = ZSTR_VAL(fe_class_name);
if (!strcasecmp(class_name, "parent") && proto->common.scope) {
fe_class_name = zend_string_copy(proto->common.scope->name);
} else if (!strcasecmp(class_name, "self") && fe->common.scope) {
fe_class_name = zend_string_copy(fe->common.scope->name);
- } else if (fe_class_name) {
- zend_string_addref(fe_class_name);
} else {
- fe_class_name = zend_string_init(class_name, strlen(class_name), 0);
+ zend_string_addref(fe_class_name);
}
- if (proto->type == ZEND_INTERNAL_FUNCTION) {
- proto_class_name = NULL;
- class_name = ((zend_internal_arg_info*)proto_arg_info)->class_name;
- } else {
- proto_class_name = proto_arg_info->class_name;
- class_name = ZSTR_VAL(proto_arg_info->class_name);
- }
+ proto_class_name = ZEND_TYPE_NAME(proto_arg_info->type);
+ class_name = ZSTR_VAL(proto_class_name);
if (!strcasecmp(class_name, "parent") && proto->common.scope && proto->common.scope->parent) {
proto_class_name = zend_string_copy(proto->common.scope->parent->name);
} else if (!strcasecmp(class_name, "self") && proto->common.scope) {
proto_class_name = zend_string_copy(proto->common.scope->name);
- } else if (proto_class_name) {
- zend_string_addref(proto_class_name);
} else {
- proto_class_name = zend_string_init(class_name, strlen(class_name), 0);
+ zend_string_addref(proto_class_name);
}
if (strcasecmp(ZSTR_VAL(fe_class_name), ZSTR_VAL(proto_class_name)) != 0) {
@@ -250,9 +236,7 @@ static int zend_do_perform_type_hint_check(const zend_function *fe, zend_arg_inf
}
zend_string_release(proto_class_name);
zend_string_release(fe_class_name);
- }
-
- if (fe_arg_info->type_hint != proto_arg_info->type_hint) {
+ } else if (ZEND_TYPE_CODE(fe_arg_info->type) != ZEND_TYPE_CODE(proto_arg_info->type)) {
/* Incompatible type */
return 0;
}
@@ -330,7 +314,7 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
}
if (!zend_do_perform_type_hint_check(fe, fe_arg_info, proto, proto_arg_info)) {
- switch (fe_arg_info->type_hint) {
+ switch (ZEND_TYPE_CODE(fe_arg_info->type)) {
case IS_ITERABLE:
if (!zend_iterable_compatibility_check(proto_arg_info)) {
return 0;
@@ -343,7 +327,7 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
}
// This introduces BC break described at https://bugs.php.net/bug.php?id=72119
- if (proto_arg_info->type_hint && proto_arg_info->allow_null && !fe_arg_info->allow_null) {
+ if (ZEND_TYPE_IS_SET(proto_arg_info->type) && ZEND_TYPE_ALLOW_NULL(proto_arg_info->type) && !ZEND_TYPE_ALLOW_NULL(fe_arg_info->type)) {
/* incompatible nullability */
return 0;
}
@@ -363,7 +347,7 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
}
if (!zend_do_perform_type_hint_check(fe, fe->common.arg_info - 1, proto, proto->common.arg_info - 1)) {
- switch (proto->common.arg_info[-1].type_hint) {
+ switch (ZEND_TYPE_CODE(proto->common.arg_info[-1].type)) {
case IS_ITERABLE:
if (!zend_iterable_compatibility_check(fe->common.arg_info - 1)) {
return 0;
@@ -375,7 +359,7 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
}
}
- if (fe->common.arg_info[-1].allow_null && !proto->common.arg_info[-1].allow_null) {
+ if (ZEND_TYPE_ALLOW_NULL(fe->common.arg_info[-1].type) && !ZEND_TYPE_ALLOW_NULL(proto->common.arg_info[-1].type)) {
return 0;
}
}
@@ -386,21 +370,16 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
static ZEND_COLD void zend_append_type_hint(smart_str *str, const zend_function *fptr, zend_arg_info *arg_info, int return_hint) /* {{{ */
{
- if (arg_info->type_hint != IS_UNDEF && arg_info->allow_null) {
+ if (ZEND_TYPE_IS_SET(arg_info->type) && ZEND_TYPE_ALLOW_NULL(arg_info->type)) {
smart_str_appendc(str, '?');
}
- if (arg_info->class_name) {
+ if (ZEND_TYPE_IS_CLASS(arg_info->type)) {
const char *class_name;
size_t class_name_len;
- if (fptr->type == ZEND_INTERNAL_FUNCTION) {
- class_name = ((zend_internal_arg_info*)arg_info)->class_name;
- class_name_len = strlen(class_name);
- } else {
- class_name = ZSTR_VAL(arg_info->class_name);
- class_name_len = ZSTR_LEN(arg_info->class_name);
- }
+ class_name = ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type));
+ class_name_len = ZSTR_LEN(ZEND_TYPE_NAME(arg_info->type));
if (!strcasecmp(class_name, "self") && fptr->common.scope) {
class_name = ZSTR_VAL(fptr->common.scope->name);
@@ -414,13 +393,13 @@ static ZEND_COLD void zend_append_type_hint(smart_str *str, const zend_function
if (!return_hint) {
smart_str_appendc(str, ' ');
}
- } else if (arg_info->type_hint) {
- if (arg_info->type_hint == IS_LONG) {
+ } else if (ZEND_TYPE_IS_CODE(arg_info->type)) {
+ if (ZEND_TYPE_CODE(arg_info->type) == IS_LONG) {
smart_str_appendl(str, "int", 3);
- } else if (arg_info->type_hint == _IS_BOOL) {
+ } else if (ZEND_TYPE_CODE(arg_info->type) == _IS_BOOL) {
smart_str_appendl(str, "bool", 4);
} else {
- const char *type_name = zend_get_type_by_const(arg_info->type_hint);
+ const char *type_name = zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type));
smart_str_appends(str, type_name);
}
if (!return_hint) {
@@ -628,7 +607,7 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
} else if ((parent->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) &&
(!(child->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
!zend_do_perform_type_hint_check(child, child->common.arg_info - 1, parent, parent->common.arg_info - 1) ||
- (child->common.arg_info[-1].allow_null && !parent->common.arg_info[-1].allow_null))) {
+ (ZEND_TYPE_ALLOW_NULL(child->common.arg_info[-1].type) && !ZEND_TYPE_ALLOW_NULL(parent->common.arg_info[-1].type)))) {
error_level = E_COMPILE_ERROR;
error_verb = "must";
} else {
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index 1719094e9a..438feb4c91 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -110,6 +110,23 @@ ZEND_API void destroy_zend_function(zend_function *function)
ZEND_ASSERT(function->type == ZEND_INTERNAL_FUNCTION);
ZEND_ASSERT(function->common.function_name);
zend_string_release(function->common.function_name);
+
+ if (function->common.arg_info &&
+ (function->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) {
+ uint32_t i;
+ uint32_t num_args = function->common.num_args + 1;
+ zend_arg_info *arg_info = function->common.arg_info - 1;
+
+ if (function->common.fn_flags & ZEND_ACC_VARIADIC) {
+ num_args++;
+ }
+ for (i = 0 ; i < num_args; i++) {
+ if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) {
+ zend_string_release(ZEND_TYPE_NAME(arg_info[i].type));
+ }
+ }
+ free(arg_info);
+ }
}
}
@@ -433,8 +450,8 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
if (arg_info[i].name) {
zend_string_release(arg_info[i].name);
}
- if (arg_info[i].class_name) {
- zend_string_release(arg_info[i].class_name);
+ if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) {
+ zend_string_release(ZEND_TYPE_NAME(arg_info[i].type));
}
}
efree(arg_info);
diff --git a/Zend/zend_types.h b/Zend/zend_types.h
index 1b52d150d3..76bb594671 100644
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@ -98,6 +98,64 @@ typedef void (*sort_func_t)(void *, size_t, size_t, compare_func_t, swap_func_t)
typedef void (*dtor_func_t)(zval *pDest);
typedef void (*copy_ctor_func_t)(zval *pElement);
+/*
+ * zend_type - is an abstraction layer to represent information about type hint.
+ * It shouldn't be used directly. Only through ZEND_TYPE_* macros.
+ *
+ * ZEND_TYPE_IS_SET() - checks if type-hint exists
+ * ZEND_TYPE_IS_CODE() - checks if type-hint refer to standard type
+ * ZEND_TYPE_IS_CLASS() - checks if type-hint refer to some class
+ *
+ * ZEND_TYPE_NAME() - returns referenced class name
+ * ZEND_TYPE_CE() - returns referenced class entry
+ * ZEND_TYPE_CODE() - returns standard type code (e.g. IS_LONG, _IS_BOOL)
+ *
+ * ZEND_TYPE_ALLOW_NULL() - checks if NULL is allowed
+ *
+ * ZEND_TYPE_ENCODE() and ZEND_TYPE_ENCODE_CLASS() should be used for
+ * construction.
+ */
+
+typedef uintptr_t zend_type;
+
+#define ZEND_TYPE_IS_SET(t) \
+ ((t) > Z_L(1))
+
+#define ZEND_TYPE_IS_CODE(t) \
+ (((t) > Z_L(1)) && ((t) <= Z_L(0x1ff)))
+
+#define ZEND_TYPE_IS_CLASS(t) \
+ ((t) > Z_L(0x1ff))
+
+#define ZEND_TYPE_NAME(t) \
+ ((zend_string*)((t) & ~Z_L(0x3)))
+
+#define ZEND_TYPE_CE(t) \
+ ((zend_class_entry*)((t) & ~Z_L(0x3)))
+
+#define ZEND_TYPE_CODE(t) \
+ ((t) >> Z_L(1))
+
+#define ZEND_TYPE_ALLOW_NULL(t) \
+ (((t) & Z_L(0x1)) != 0)
+
+#define ZEND_TYPE_ENCODE(code, allow_null) \
+ (((code) << Z_L(1)) | ((allow_null) ? Z_L(0x1) : Z_L(0x0)))
+
+#define ZEND_TYPE_ENCODE_CLASS(class_name, allow_null) \
+ (((uintptr_t)(class_name)) | ((allow_null) ? Z_L(0x1) : Z_L(0)))
+
+#define ZEND_TYPE_ENCODE_CLASS_CONST_0(class_name) \
+ ((zend_type) class_name)
+#define ZEND_TYPE_ENCODE_CLASS_CONST_1(class_name) \
+ ((zend_type) "?" class_name)
+#define ZEND_TYPE_ENCODE_CLASS_CONST_Q2(macro, class_name) \
+ macro(class_name)
+#define ZEND_TYPE_ENCODE_CLASS_CONST_Q1(allow_null, class_name) \
+ ZEND_TYPE_ENCODE_CLASS_CONST_Q2(ZEND_TYPE_ENCODE_CLASS_CONST_ ##allow_null, class_name)
+#define ZEND_TYPE_ENCODE_CLASS_CONST(class_name, allow_null) \
+ ZEND_TYPE_ENCODE_CLASS_CONST_Q1(allow_null, class_name)
+
typedef union _zend_value {
zend_long lval; /* long value */
double dval; /* double value */
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 930fffb04e..569e1c0717 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -3796,10 +3796,10 @@ ZEND_VM_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
ZVAL_DEREF(retval_ptr);
}
- if (UNEXPECTED(!ret_info->class_name
- && ret_info->type_hint != IS_CALLABLE
- && ret_info->type_hint != IS_ITERABLE
- && !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(retval_ptr))
+ if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type)
+ && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE
+ && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE
+ && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr))
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
&& retval_ref != retval_ptr)
) {
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index a02f8be0dc..f6ebd5f297 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -7686,10 +7686,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CONST_
ZVAL_DEREF(retval_ptr);
}
- if (UNEXPECTED(!ret_info->class_name
- && ret_info->type_hint != IS_CALLABLE
- && ret_info->type_hint != IS_ITERABLE
- && !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(retval_ptr))
+ if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type)
+ && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE
+ && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE
+ && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr))
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
&& retval_ref != retval_ptr)
) {
@@ -14410,10 +14410,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
ZVAL_DEREF(retval_ptr);
}
- if (UNEXPECTED(!ret_info->class_name
- && ret_info->type_hint != IS_CALLABLE
- && ret_info->type_hint != IS_ITERABLE
- && !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(retval_ptr))
+ if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type)
+ && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE
+ && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE
+ && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr))
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
&& retval_ref != retval_ptr)
) {
@@ -21425,10 +21425,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
ZVAL_DEREF(retval_ptr);
}
- if (UNEXPECTED(!ret_info->class_name
- && ret_info->type_hint != IS_CALLABLE
- && ret_info->type_hint != IS_ITERABLE
- && !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(retval_ptr))
+ if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type)
+ && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE
+ && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE
+ && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr))
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
&& retval_ref != retval_ptr)
) {
@@ -29118,10 +29118,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
ZVAL_DEREF(retval_ptr);
}
- if (UNEXPECTED(!ret_info->class_name
- && ret_info->type_hint != IS_CALLABLE
- && ret_info->type_hint != IS_ITERABLE
- && !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(retval_ptr))
+ if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type)
+ && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE
+ && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE
+ && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr))
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
&& retval_ref != retval_ptr)
) {
@@ -40446,10 +40446,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
ZVAL_DEREF(retval_ptr);
}
- if (UNEXPECTED(!ret_info->class_name
- && ret_info->type_hint != IS_CALLABLE
- && ret_info->type_hint != IS_ITERABLE
- && !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(retval_ptr))
+ if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type)
+ && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE
+ && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE
+ && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr))
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
&& retval_ref != retval_ptr)
) {
diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c
index 9597ce1007..3eebad0ef1 100644
--- a/ext/com_dotnet/com_handlers.c
+++ b/ext/com_dotnet/com_handlers.c
@@ -302,7 +302,7 @@ static union _zend_function *com_method_get(zend_object **object_ptr, zend_strin
f.arg_info = ecalloc(bindptr.lpfuncdesc->cParams, sizeof(zend_arg_info));
for (i = 0; i < bindptr.lpfuncdesc->cParams; i++) {
- f.arg_info[i].allow_null = 1;
+ f.arg_info[i].type = ZEND_TYPE_ENCODE(0,1);;
if (bindptr.lpfuncdesc->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FOUT) {
f.arg_info[i].pass_by_reference = ZEND_SEND_BY_REF;
}
diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c
index 55915e70a9..63f7fafa93 100644
--- a/ext/opcache/Optimizer/dfa_pass.c
+++ b/ext/opcache/Optimizer/dfa_pass.c
@@ -321,11 +321,11 @@ static inline zend_bool can_elide_return_type_check(
return 0;
}
- if (info->type_hint == IS_CALLABLE) {
+ if (ZEND_TYPE_CODE(info->type) == IS_CALLABLE) {
return 0;
}
- if (info->class_name) {
+ if (ZEND_TYPE_IS_CLASS(info->type)) {
if (!use_info->ce || !def_info->ce || !instanceof_function(use_info->ce, def_info->ce)) {
return 0;
}
diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c
index 9ecf71ea83..069074d3bf 100644
--- a/ext/opcache/Optimizer/zend_inference.c
+++ b/ext/opcache/Optimizer/zend_inference.c
@@ -1323,13 +1323,13 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int
return 1;
} else if (op_array->arg_info &&
opline->op1.num <= op_array->num_args) {
- if (op_array->arg_info[opline->op1.num-1].type_hint == IS_LONG) {
+ if (ZEND_TYPE_CODE(op_array->arg_info[opline->op1.num-1].type) == IS_LONG) {
tmp->underflow = 0;
tmp->min = ZEND_LONG_MIN;
tmp->max = ZEND_LONG_MAX;
tmp->overflow = 0;
return 1;
- } else if (op_array->arg_info[opline->op1.num-1].type_hint == _IS_BOOL) {
+ } else if (ZEND_TYPE_CODE(op_array->arg_info[opline->op1.num-1].type) == _IS_BOOL) {
tmp->underflow = 0;
tmp->min = 0;
tmp->max = 1;
@@ -2087,31 +2087,33 @@ static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *ar
uint32_t tmp = 0;
*pce = NULL;
- if (arg_info->class_name) {
+ if (ZEND_TYPE_IS_CLASS(arg_info->type)) {
// class type hinting...
- zend_string *lcname = zend_string_tolower(arg_info->class_name);
+ zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(arg_info->type));
tmp |= MAY_BE_OBJECT;
*pce = get_class_entry(script, lcname);
zend_string_release(lcname);
- } else if (arg_info->type_hint != IS_UNDEF) {
- if (arg_info->type_hint == IS_VOID) {
+ } else if (ZEND_TYPE_IS_CODE(arg_info->type)) {
+ zend_uchar type_hint = ZEND_TYPE_CODE(arg_info->type);
+
+ if (type_hint == IS_VOID) {
tmp |= MAY_BE_NULL;
- } else if (arg_info->type_hint == IS_CALLABLE) {
+ } else if (type_hint == IS_CALLABLE) {
tmp |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- } else if (arg_info->type_hint == IS_ITERABLE) {
+ } else if (type_hint == IS_ITERABLE) {
tmp |= MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- } else if (arg_info->type_hint == IS_ARRAY) {
+ } else if (type_hint == IS_ARRAY) {
tmp |= MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- } else if (arg_info->type_hint == _IS_BOOL) {
+ } else if (type_hint == _IS_BOOL) {
tmp |= MAY_BE_TRUE|MAY_BE_FALSE;
} else {
- ZEND_ASSERT(arg_info->type_hint < IS_REFERENCE);
- tmp |= 1 << arg_info->type_hint;
+ ZEND_ASSERT(type_hint < IS_REFERENCE);
+ tmp |= 1 << type_hint;
}
} else {
tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
}
- if (arg_info->allow_null) {
+ if (ZEND_TYPE_ALLOW_NULL(arg_info->type)) {
tmp |= MAY_BE_NULL;
}
return tmp;
diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c
index a30f18479a..ee659fa2f2 100644
--- a/ext/opcache/Optimizer/zend_optimizer.c
+++ b/ext/opcache/Optimizer/zend_optimizer.c
@@ -506,9 +506,9 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
}
case ZEND_VERIFY_RETURN_TYPE: {
zend_arg_info *ret_info = op_array->arg_info - 1;
- if (ret_info->class_name
- || ret_info->type_hint == IS_CALLABLE
- || !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(val))
+ if (ZEND_TYPE_IS_CLASS(ret_info->type)
+ || ZEND_TYPE_CODE(ret_info->type) == IS_CALLABLE
+ || !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(val))
|| (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
zval_dtor(val);
return 0;
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index 8be645be65..634e1151aa 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -537,6 +537,22 @@ static void accel_use_shm_interned_strings(void)
if (Z_FUNC(p->val)->common.function_name) {
Z_FUNC(p->val)->common.function_name = accel_new_interned_string(Z_FUNC(p->val)->common.function_name);
}
+ if (Z_FUNC(p->val)->common.arg_info &&
+ (Z_FUNC(p->val)->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) {
+ uint32_t i;
+ uint32_t num_args = Z_FUNC(p->val)->common.num_args + 1;
+ zend_arg_info *arg_info = Z_FUNC(p->val)->common.arg_info - 1;
+
+ if (Z_FUNC(p->val)->common.fn_flags & ZEND_ACC_VARIADIC) {
+ num_args++;
+ }
+ for (i = 0 ; i < num_args; i++) {
+ if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) {
+ zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(arg_info[i].type);
+ arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(accel_new_interned_string(ZEND_TYPE_NAME(arg_info[i].type)), allow_null);
+ }
+ }
+ }
}
/* class table hash keys, class names, properties, methods, constants, etc */
diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c
index c9ae0dd66e..9e99dd6221 100644
--- a/ext/opcache/zend_file_cache.c
+++ b/ext/opcache/zend_file_cache.c
@@ -436,8 +436,15 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
if (!IS_SERIALIZED(p->name)) {
SERIALIZE_STR(p->name);
}
- if (!IS_SERIALIZED(p->class_name)) {
- SERIALIZE_STR(p->class_name);
+ if (ZEND_TYPE_IS_CLASS(p->type)) {
+ zend_string *type_name = ZEND_TYPE_NAME(p->type);
+
+ if (!IS_SERIALIZED(type_name)) {
+ zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(p->type);
+
+ SERIALIZE_STR(type_name);
+ p->type = ZEND_TYPE_ENCODE_CLASS(type_name, allow_null);
+ }
}
p++;
}
@@ -1011,8 +1018,15 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
if (!IS_UNSERIALIZED(p->name)) {
UNSERIALIZE_STR(p->name);
}
- if (!IS_UNSERIALIZED(p->class_name)) {
- UNSERIALIZE_STR(p->class_name);
+ if (ZEND_TYPE_IS_CLASS(p->type)) {
+ zend_string *type_name = ZEND_TYPE_NAME(p->type);
+
+ if (!IS_UNSERIALIZED(type_name)) {
+ zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(p->type);
+
+ UNSERIALIZE_STR(type_name);
+ p->type = ZEND_TYPE_ENCODE_CLASS(type_name, allow_null);
+ }
}
p++;
}
diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c
index 5eeea6c056..e67184f757 100644
--- a/ext/opcache/zend_persist.c
+++ b/ext/opcache/zend_persist.c
@@ -492,8 +492,12 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
if (arg_info[i].name) {
zend_accel_store_interned_string(arg_info[i].name);
}
- if (arg_info[i].class_name) {
- zend_accel_store_interned_string(arg_info[i].class_name);
+ if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) {
+ zend_string *type_name = ZEND_TYPE_NAME(arg_info[i].type);
+ zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(arg_info[i].type);
+
+ zend_accel_store_interned_string(type_name);
+ arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(type_name, allow_null);
}
}
}
diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c
index e597373edd..f169dffad1 100644
--- a/ext/opcache/zend_persist_calc.c
+++ b/ext/opcache/zend_persist_calc.c
@@ -230,8 +230,12 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
if (arg_info[i].name) {
ADD_INTERNED_STRING(arg_info[i].name, 1);
}
- if (arg_info[i].class_name) {
- ADD_INTERNED_STRING(arg_info[i].class_name, 1);
+ if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) {
+ zend_string *type_name = ZEND_TYPE_NAME(arg_info[i].type);
+ zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(arg_info[i].type);
+
+ ADD_INTERNED_STRING(type_name, 1);
+ arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(type_name, allow_null);
}
}
}
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 0757e75f29..9c465d78d4 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -603,18 +603,15 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_
} else {
smart_str_append_printf(str, "<required> ");
}
- if (arg_info->class_name) {
+ if (ZEND_TYPE_IS_CLASS(arg_info->type)) {
smart_str_append_printf(str, "%s ",
- (fptr->type == ZEND_INTERNAL_FUNCTION &&
- !(fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) ?
- ((zend_internal_arg_info*)arg_info)->class_name :
- ZSTR_VAL(arg_info->class_name));
- if (arg_info->allow_null) {
+ ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type)));
+ if (ZEND_TYPE_ALLOW_NULL(arg_info->type)) {
smart_str_append_printf(str, "or NULL ");
}
- } else if (arg_info->type_hint) {
- smart_str_append_printf(str, "%s ", zend_get_type_by_const(arg_info->type_hint));
- if (arg_info->allow_null) {
+ } else if (ZEND_TYPE_IS_CODE(arg_info->type)) {
+ smart_str_append_printf(str, "%s ", zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type)));
+ if (ZEND_TYPE_ALLOW_NULL(arg_info->type)) {
smart_str_append_printf(str, "or NULL ");
}
}
@@ -825,18 +822,15 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent
smart_str_free(&param_indent);
if (fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
smart_str_append_printf(str, " %s- Return [ ", indent);
- if (fptr->common.arg_info[-1].class_name) {
+ if (ZEND_TYPE_IS_CLASS(fptr->common.arg_info[-1].type)) {
smart_str_append_printf(str, "%s ",
- (fptr->type == ZEND_INTERNAL_FUNCTION &&
- !(fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) ?
- ((zend_internal_arg_info*)(fptr->common.arg_info - 1))->class_name :
- ZSTR_VAL(fptr->common.arg_info[-1].class_name));
- if (fptr->common.arg_info[-1].allow_null) {
+ ZSTR_VAL(ZEND_TYPE_NAME(fptr->common.arg_info[-1].type)));
+ if (ZEND_TYPE_ALLOW_NULL(fptr->common.arg_info[-1].type)) {
smart_str_appends(str, "or NULL ");
}
- } else if (fptr->common.arg_info[-1].type_hint) {
- smart_str_append_printf(str, "%s ", zend_get_type_by_const(fptr->common.arg_info[-1].type_hint));
- if (fptr->common.arg_info[-1].allow_null) {
+ } else if (ZEND_TYPE_IS_CODE(fptr->common.arg_info[-1].type)) {
+ smart_str_append_printf(str, "%s ", zend_get_type_by_const(ZEND_TYPE_CODE(fptr->common.arg_info[-1].type)));
+ if (ZEND_TYPE_ALLOW_NULL(fptr->common.arg_info[-1].type)) {
smart_str_appends(str, "or NULL ");
}
}
@@ -2551,7 +2545,7 @@ ZEND_METHOD(reflection_parameter, getClass)
}
GET_REFLECTION_OBJECT_PTR(param);
- if (param->arg_info->class_name) {
+ if (ZEND_TYPE_IS_CLASS(param->arg_info->type)) {
/* Class name is stored as a string, we might also get "self" or "parent"
* - For "self", simply use the function scope. If scope is NULL then
* the function is global and thus self does not make any sense
@@ -2564,25 +2558,17 @@ ZEND_METHOD(reflection_parameter, getClass)
* TODO: Think about moving these checks to the compiler or some sort of
* lint-mode.
*/
- const char *class_name;
- size_t class_name_len;
+ zend_string *class_name;
- if (param->fptr->type == ZEND_INTERNAL_FUNCTION &&
- !(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
- class_name = ((zend_internal_arg_info*)param->arg_info)->class_name;
- class_name_len = strlen(class_name);
- } else {
- class_name = ZSTR_VAL(param->arg_info->class_name);
- class_name_len = ZSTR_LEN(param->arg_info->class_name);
- }
- if (0 == zend_binary_strcasecmp(class_name, class_name_len, "self", sizeof("self")- 1)) {
+ class_name = ZEND_TYPE_NAME(param->arg_info->type);
+ if (0 == zend_binary_strcasecmp(ZSTR_VAL(class_name), ZSTR_LEN(class_name), "self", sizeof("self")- 1)) {
ce = param->fptr->common.scope;
if (!ce) {
zend_throw_exception_ex(reflection_exception_ptr, 0,
"Parameter uses 'self' as type hint but function is not a class member!");
return;
}
- } else if (0 == zend_binary_strcasecmp(class_name, class_name_len, "parent", sizeof("parent")- 1)) {
+ } else if (0 == zend_binary_strcasecmp(ZSTR_VAL(class_name), ZSTR_LEN(class_name), "parent", sizeof("parent")- 1)) {
ce = param->fptr->common.scope;
if (!ce) {
zend_throw_exception_ex(reflection_exception_ptr, 0,
@@ -2596,17 +2582,10 @@ ZEND_METHOD(reflection_parameter, getClass)
}
ce = ce->parent;
} else {
- if (param->fptr->type == ZEND_INTERNAL_FUNCTION &&
- !(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
- zend_string *name = zend_string_init(class_name, class_name_len, 0);
- ce = zend_lookup_class(name);
- zend_string_release(name);
- } else {
- ce = zend_lookup_class(param->arg_info->class_name);
- }
+ ce = zend_lookup_class(class_name);
if (!ce) {
zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Class %s does not exist", class_name);
+ "Class %s does not exist", ZSTR_VAL(class_name));
return;
}
}
@@ -2627,7 +2606,7 @@ ZEND_METHOD(reflection_parameter, hasType)
}
GET_REFLECTION_OBJECT_PTR(param);
- RETVAL_BOOL(param->arg_info->type_hint != 0);
+ RETVAL_BOOL(ZEND_TYPE_IS_SET(param->arg_info->type));
}
/* }}} */
@@ -2643,11 +2622,7 @@ ZEND_METHOD(reflection_parameter, getType)
}
GET_REFLECTION_OBJECT_PTR(param);
- if (((param->fptr->type == ZEND_INTERNAL_FUNCTION &&
- !(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) ?
- ((zend_internal_arg_info*)param->arg_info)->type_hint :
- param->arg_info->type_hint) == 0)
- {
+ if (!ZEND_TYPE_IS_SET(param->arg_info->type)) {
RETURN_NULL();
}
reflection_type_factory(_copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, param->arg_info, return_value);
@@ -2666,7 +2641,7 @@ ZEND_METHOD(reflection_parameter, isArray)
}
GET_REFLECTION_OBJECT_PTR(param);
- RETVAL_BOOL(param->arg_info->type_hint == IS_ARRAY);
+ RETVAL_BOOL(ZEND_TYPE_CODE(param->arg_info->type) == IS_ARRAY);
}
/* }}} */
@@ -2682,7 +2657,7 @@ ZEND_METHOD(reflection_parameter, isCallable)
}
GET_REFLECTION_OBJECT_PTR(param);
- RETVAL_BOOL(param->arg_info->type_hint == IS_CALLABLE);
+ RETVAL_BOOL(ZEND_TYPE_CODE(param->arg_info->type) == IS_CALLABLE);
}
/* }}} */
@@ -2698,7 +2673,7 @@ ZEND_METHOD(reflection_parameter, allowsNull)
}
GET_REFLECTION_OBJECT_PTR(param);
- RETVAL_BOOL(param->arg_info->allow_null);
+ RETVAL_BOOL(ZEND_TYPE_ALLOW_NULL(param->arg_info->type));
}
/* }}} */
@@ -2897,7 +2872,7 @@ ZEND_METHOD(reflection_type, allowsNull)
}
GET_REFLECTION_OBJECT_PTR(param);
- RETVAL_BOOL(param->arg_info->allow_null);
+ RETVAL_BOOL(ZEND_TYPE_ALLOW_NULL(param->arg_info->type));
}
/* }}} */
@@ -2913,21 +2888,18 @@ ZEND_METHOD(reflection_type, isBuiltin)
}
GET_REFLECTION_OBJECT_PTR(param);
- RETVAL_BOOL(param->arg_info->type_hint != IS_OBJECT);
+ RETVAL_BOOL(ZEND_TYPE_IS_CODE(param->arg_info->type));
}
/* }}} */
/* {{{ reflection_type_name */
static zend_string *reflection_type_name(type_reference *param) {
- switch (param->arg_info->type_hint) {
+ if (ZEND_TYPE_IS_CLASS(param->arg_info->type)) {
+ return zend_string_copy(ZEND_TYPE_NAME(param->arg_info->type));
+ }
+ switch (ZEND_TYPE_CODE(param->arg_info->type)) {
case IS_ARRAY: return zend_string_init("array", sizeof("array") - 1, 0);
case IS_CALLABLE: return zend_string_init("callable", sizeof("callable") - 1, 0);
- case IS_OBJECT:
- if (param->fptr->type == ZEND_INTERNAL_FUNCTION &&
- !(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
- return zend_string_init(((zend_internal_arg_info*)param->arg_info)->class_name, strlen(((zend_internal_arg_info*)param->arg_info)->class_name), 0);
- }
- return zend_string_copy(param->arg_info->class_name);
case IS_STRING: return zend_string_init("string", sizeof("string") - 1, 0);
case _IS_BOOL: return zend_string_init("bool", sizeof("bool") - 1, 0);
case IS_LONG: return zend_string_init("int", sizeof("int") - 1, 0);