mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Merge branch 'PHP-8.0'
* PHP-8.0: Add support for union types for internal functions
This commit is contained in:
commit
45a4d07dd0
5 changed files with 97 additions and 54 deletions
|
@ -2566,10 +2566,40 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
|
||||||
for (i = 0; i < num_args; i++) {
|
for (i = 0; i < num_args; i++) {
|
||||||
if (ZEND_TYPE_HAS_CLASS(new_arg_info[i].type)) {
|
if (ZEND_TYPE_HAS_CLASS(new_arg_info[i].type)) {
|
||||||
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(new_arg_info[i].type)
|
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(new_arg_info[i].type)
|
||||||
&& "Only simple classes are currently supported");
|
&& "Should be stored as simple name");
|
||||||
const char *class_name = ZEND_TYPE_LITERAL_NAME(new_arg_info[i].type);
|
const char *class_name = ZEND_TYPE_LITERAL_NAME(new_arg_info[i].type);
|
||||||
ZEND_TYPE_SET_PTR(new_arg_info[i].type,
|
|
||||||
zend_string_init_interned(class_name, strlen(class_name), 1));
|
size_t num_types = 1;
|
||||||
|
const char *p = class_name;
|
||||||
|
while ((p = strchr(p, '|'))) {
|
||||||
|
num_types++;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_types == 1) {
|
||||||
|
/* Simple class type */
|
||||||
|
ZEND_TYPE_SET_PTR(new_arg_info[i].type,
|
||||||
|
zend_string_init_interned(class_name, strlen(class_name), 1));
|
||||||
|
} else {
|
||||||
|
/* Union type */
|
||||||
|
zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types));
|
||||||
|
list->num_types = num_types;
|
||||||
|
ZEND_TYPE_SET_LIST(new_arg_info[i].type, list);
|
||||||
|
|
||||||
|
const char *start = class_name;
|
||||||
|
uint32_t j = 0;
|
||||||
|
while (true) {
|
||||||
|
const char *end = strchr(start, '|');
|
||||||
|
zend_string *str =
|
||||||
|
zend_string_init(start, end ? end - start : strlen(start), 1);
|
||||||
|
list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0);
|
||||||
|
if (!end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
start = end + 1;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -951,6 +951,13 @@ ZEND_API zend_bool zend_value_instanceof_static(zval *zv) {
|
||||||
return instanceof_function(Z_OBJCE_P(zv), called_scope);
|
return instanceof_function(Z_OBJCE_P(zv), called_scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The cache_slot may only be NULL in debug builds, where arginfo verification of
|
||||||
|
* internal functions is enabled. Avoid unnecessary checks in release builds. */
|
||||||
|
#if ZEND_DEBUG
|
||||||
|
# define HAVE_CACHE_SLOT (cache_slot != NULL)
|
||||||
|
#else
|
||||||
|
# define HAVE_CACHE_SLOT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
static zend_always_inline zend_bool zend_check_type_slow(
|
static zend_always_inline zend_bool zend_check_type_slow(
|
||||||
zend_type type, zval *arg, zend_reference *ref, void **cache_slot, zend_class_entry *scope,
|
zend_type type, zval *arg, zend_reference *ref, void **cache_slot, zend_class_entry *scope,
|
||||||
|
@ -962,31 +969,39 @@ static zend_always_inline zend_bool zend_check_type_slow(
|
||||||
if (ZEND_TYPE_HAS_LIST(type)) {
|
if (ZEND_TYPE_HAS_LIST(type)) {
|
||||||
zend_type *list_type;
|
zend_type *list_type;
|
||||||
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
|
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
|
||||||
if (*cache_slot) {
|
if (HAVE_CACHE_SLOT && *cache_slot) {
|
||||||
ce = *cache_slot;
|
ce = *cache_slot;
|
||||||
} else {
|
} else {
|
||||||
ce = zend_fetch_class(ZEND_TYPE_NAME(*list_type),
|
ce = zend_fetch_class(ZEND_TYPE_NAME(*list_type),
|
||||||
(ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
|
(ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
|
||||||
if (!ce) {
|
if (!ce) {
|
||||||
cache_slot++;
|
if (HAVE_CACHE_SLOT) {
|
||||||
|
cache_slot++;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
*cache_slot = ce;
|
if (HAVE_CACHE_SLOT) {
|
||||||
|
*cache_slot = ce;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (instanceof_function(Z_OBJCE_P(arg), ce)) {
|
if (instanceof_function(Z_OBJCE_P(arg), ce)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
cache_slot++;
|
if (HAVE_CACHE_SLOT) {
|
||||||
|
cache_slot++;
|
||||||
|
}
|
||||||
} ZEND_TYPE_LIST_FOREACH_END();
|
} ZEND_TYPE_LIST_FOREACH_END();
|
||||||
} else {
|
} else {
|
||||||
if (EXPECTED(*cache_slot)) {
|
if (EXPECTED(HAVE_CACHE_SLOT && *cache_slot)) {
|
||||||
ce = (zend_class_entry *) *cache_slot;
|
ce = (zend_class_entry *) *cache_slot;
|
||||||
} else {
|
} else {
|
||||||
ce = zend_fetch_class(ZEND_TYPE_NAME(type), (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
|
ce = zend_fetch_class(ZEND_TYPE_NAME(type), (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
|
||||||
if (UNEXPECTED(!ce)) {
|
if (UNEXPECTED(!ce)) {
|
||||||
goto builtin_types;
|
goto builtin_types;
|
||||||
}
|
}
|
||||||
*cache_slot = (void *) ce;
|
if (HAVE_CACHE_SLOT) {
|
||||||
|
*cache_slot = (void *) ce;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (instanceof_function(Z_OBJCE_P(arg), ce)) {
|
if (instanceof_function(Z_OBJCE_P(arg), ce)) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1079,8 +1094,6 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ
|
||||||
|
|
||||||
for (i = 0; i < num_args; ++i) {
|
for (i = 0; i < num_args; ++i) {
|
||||||
zend_arg_info *cur_arg_info;
|
zend_arg_info *cur_arg_info;
|
||||||
void *dummy_cache_slot = NULL;
|
|
||||||
|
|
||||||
if (EXPECTED(i < fbc->common.num_args)) {
|
if (EXPECTED(i < fbc->common.num_args)) {
|
||||||
cur_arg_info = &fbc->common.arg_info[i];
|
cur_arg_info = &fbc->common.arg_info[i];
|
||||||
} else if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_VARIADIC)) {
|
} else if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_VARIADIC)) {
|
||||||
|
@ -1090,7 +1103,7 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ZEND_TYPE_IS_SET(cur_arg_info->type)
|
if (ZEND_TYPE_IS_SET(cur_arg_info->type)
|
||||||
&& UNEXPECTED(!zend_check_type(cur_arg_info->type, arg, &dummy_cache_slot, fbc->common.scope, 0, /* is_internal */ 1))) {
|
&& UNEXPECTED(!zend_check_type(cur_arg_info->type, arg, /* cache_slot */ NULL, fbc->common.scope, 0, /* is_internal */ 1))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
arg++;
|
arg++;
|
||||||
|
@ -1215,8 +1228,6 @@ static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, con
|
||||||
static bool zend_verify_internal_return_type(zend_function *zf, zval *ret)
|
static bool zend_verify_internal_return_type(zend_function *zf, zval *ret)
|
||||||
{
|
{
|
||||||
zend_internal_arg_info *ret_info = zf->internal_function.arg_info - 1;
|
zend_internal_arg_info *ret_info = zf->internal_function.arg_info - 1;
|
||||||
void *dummy_cache_slot = NULL;
|
|
||||||
|
|
||||||
if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_VOID) {
|
if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_VOID) {
|
||||||
if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) {
|
if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) {
|
||||||
zend_verify_void_return_error(zf, zend_zval_type_name(ret), "");
|
zend_verify_void_return_error(zf, zend_zval_type_name(ret), "");
|
||||||
|
@ -1225,7 +1236,7 @@ static bool zend_verify_internal_return_type(zend_function *zf, zval *ret)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UNEXPECTED(!zend_check_type(ret_info->type, ret, &dummy_cache_slot, NULL, 1, /* is_internal */ 1))) {
|
if (UNEXPECTED(!zend_check_type(ret_info->type, ret, /* cache_slot */ NULL, NULL, 1, /* is_internal */ 1))) {
|
||||||
zend_verify_internal_return_error(zf, ret);
|
zend_verify_internal_return_error(zf, ret);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -293,20 +293,17 @@ class Type {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tryToRepresentableType(): ?RepresentableType {
|
public function toArginfoType(): ?ArginfoType {
|
||||||
$classType = null;
|
$classTypes = [];
|
||||||
$builtinTypes = [];
|
$builtinTypes = [];
|
||||||
foreach ($this->types as $type) {
|
foreach ($this->types as $type) {
|
||||||
if ($type->isBuiltin) {
|
if ($type->isBuiltin) {
|
||||||
$builtinTypes[] = $type;
|
$builtinTypes[] = $type;
|
||||||
} else if ($classType === null) {
|
|
||||||
$classType = $type;
|
|
||||||
} else {
|
} else {
|
||||||
// We can only represent a single class type.
|
$classTypes[] = $type;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new RepresentableType($classType, $builtinTypes);
|
return new ArginfoType($classTypes, $builtinTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function equals(?Type $a, ?Type $b): bool {
|
public static function equals(?Type $a, ?Type $b): bool {
|
||||||
|
@ -339,18 +336,32 @@ class Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RepresentableType {
|
class ArginfoType {
|
||||||
/** @var ?SimpleType $classType */
|
/** @var ClassType[] $classTypes */
|
||||||
public $classType;
|
public $classTypes;
|
||||||
/** @var SimpleType[] $builtinTypes */
|
|
||||||
public $builtinTypes;
|
|
||||||
|
|
||||||
public function __construct(?SimpleType $classType, array $builtinTypes) {
|
/** @var SimpleType[] $builtinTypes */
|
||||||
$this->classType = $classType;
|
private $builtinTypes;
|
||||||
|
|
||||||
|
public function __construct(array $classTypes, array $builtinTypes) {
|
||||||
|
$this->classTypes = $classTypes;
|
||||||
$this->builtinTypes = $builtinTypes;
|
$this->builtinTypes = $builtinTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasClassType(): bool {
|
||||||
|
return !empty($this->classTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toClassTypeString(): string {
|
||||||
|
return implode('|', array_map(function(SimpleType $type) {
|
||||||
|
return $type->toEscapedName();
|
||||||
|
}, $this->classTypes));
|
||||||
|
}
|
||||||
|
|
||||||
public function toTypeMask(): string {
|
public function toTypeMask(): string {
|
||||||
|
if (empty($this->builtinTypes)) {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
return implode('|', array_map(function(SimpleType $type) {
|
return implode('|', array_map(function(SimpleType $type) {
|
||||||
return $type->toTypeMask();
|
return $type->toTypeMask();
|
||||||
}, $this->builtinTypes));
|
}, $this->builtinTypes));
|
||||||
|
@ -1362,24 +1373,23 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
|
||||||
$simpleReturnType->toEscapedName(), $returnType->isNullable()
|
$simpleReturnType->toEscapedName(), $returnType->isNullable()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (null !== $representableType = $returnType->tryToRepresentableType()) {
|
} else {
|
||||||
if ($representableType->classType !== null) {
|
$arginfoType = $returnType->toArginfoType();
|
||||||
|
if ($arginfoType->hasClassType()) {
|
||||||
$code .= sprintf(
|
$code .= sprintf(
|
||||||
"ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(%s, %d, %d, %s, %s)\n",
|
"ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(%s, %d, %d, %s, %s)\n",
|
||||||
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
|
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
|
||||||
$funcInfo->numRequiredArgs,
|
$funcInfo->numRequiredArgs,
|
||||||
$representableType->classType->toEscapedName(), $representableType->toTypeMask()
|
$arginfoType->toClassTypeString(), $arginfoType->toTypeMask()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$code .= sprintf(
|
$code .= sprintf(
|
||||||
"ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(%s, %d, %d, %s)\n",
|
"ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(%s, %d, %d, %s)\n",
|
||||||
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
|
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
|
||||||
$funcInfo->numRequiredArgs,
|
$funcInfo->numRequiredArgs,
|
||||||
$representableType->toTypeMask()
|
$arginfoType->toTypeMask()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
throw new Exception('Unimplemented');
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$code .= sprintf(
|
$code .= sprintf(
|
||||||
|
@ -1409,25 +1419,23 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
|
||||||
$argInfo->hasProperDefaultValue() ? ", " . $argInfo->getDefaultValueAsArginfoString() : ""
|
$argInfo->hasProperDefaultValue() ? ", " . $argInfo->getDefaultValueAsArginfoString() : ""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (null !== $representableType = $argType->tryToRepresentableType()) {
|
} else {
|
||||||
if ($representableType->classType !== null) {
|
$arginfoType = $argType->toArginfoType();
|
||||||
|
if ($arginfoType->hasClassType()) {
|
||||||
$code .= sprintf(
|
$code .= sprintf(
|
||||||
"\tZEND_%s_OBJ_TYPE_MASK(%s, %s, %s, %s, %s)\n",
|
"\tZEND_%s_OBJ_TYPE_MASK(%s, %s, %s, %s, %s)\n",
|
||||||
$argKind, $argInfo->getSendByString(), $argInfo->name,
|
$argKind, $argInfo->getSendByString(), $argInfo->name,
|
||||||
$representableType->classType->toEscapedName(),
|
$arginfoType->toClassTypeString(), $arginfoType->toTypeMask(),
|
||||||
$representableType->toTypeMask(),
|
|
||||||
$argInfo->getDefaultValueAsArginfoString()
|
$argInfo->getDefaultValueAsArginfoString()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$code .= sprintf(
|
$code .= sprintf(
|
||||||
"\tZEND_%s_TYPE_MASK(%s, %s, %s, %s)\n",
|
"\tZEND_%s_TYPE_MASK(%s, %s, %s, %s)\n",
|
||||||
$argKind, $argInfo->getSendByString(), $argInfo->name,
|
$argKind, $argInfo->getSendByString(), $argInfo->name,
|
||||||
$representableType->toTypeMask(),
|
$arginfoType->toTypeMask(),
|
||||||
$argInfo->getDefaultValueAsArginfoString()
|
$argInfo->getDefaultValueAsArginfoString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
throw new Exception('Unimplemented');
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$code .= sprintf(
|
$code .= sprintf(
|
||||||
|
|
|
@ -33,17 +33,11 @@ final class FFI
|
||||||
/** @prefer-ref $ptr */
|
/** @prefer-ref $ptr */
|
||||||
public static function addr(FFI\CData $ptr): FFI\CData {}
|
public static function addr(FFI\CData $ptr): FFI\CData {}
|
||||||
|
|
||||||
/**
|
/** @prefer-ref $ptr */
|
||||||
* @param FFI\CData|FFI\CType $ptr
|
public static function sizeof(FFI\CData|FFI\CType $ptr): int {}
|
||||||
* @prefer-ref $ptr
|
|
||||||
*/
|
|
||||||
public static function sizeof($ptr): int {}
|
|
||||||
|
|
||||||
/**
|
/** @prefer-ref $ptr */
|
||||||
* @param FFI\CData|FFI\CType $ptr
|
public static function alignof(FFI\CData|FFI\CType $ptr): int {}
|
||||||
* @prefer-ref $ptr
|
|
||||||
*/
|
|
||||||
public static function alignof($ptr): int {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param FFI\CData|string $from
|
* @param FFI\CData|string $from
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* This is a generated file, edit the .stub.php file instead.
|
/* This is a generated file, edit the .stub.php file instead.
|
||||||
* Stub hash: 0b4215e4686f4184b2eef0de7d60e01855725924 */
|
* Stub hash: 5aeec68fea7a94cd643464acfb10bf4cfcc863da */
|
||||||
|
|
||||||
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_FFI_cdef, 0, 0, FFI, 0)
|
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_FFI_cdef, 0, 0, FFI, 0)
|
||||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, code, IS_STRING, 0, "\"\"")
|
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, code, IS_STRING, 0, "\"\"")
|
||||||
|
@ -47,7 +47,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_FFI_addr, 0, 1, FFI\\CData,
|
||||||
ZEND_END_ARG_INFO()
|
ZEND_END_ARG_INFO()
|
||||||
|
|
||||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_FFI_sizeof, 0, 1, IS_LONG, 0)
|
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_FFI_sizeof, 0, 1, IS_LONG, 0)
|
||||||
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
|
ZEND_ARG_OBJ_TYPE_MASK(ZEND_SEND_PREFER_REF, ptr, FFI\\CData|FFI\\CType, 0, NULL)
|
||||||
ZEND_END_ARG_INFO()
|
ZEND_END_ARG_INFO()
|
||||||
|
|
||||||
#define arginfo_class_FFI_alignof arginfo_class_FFI_sizeof
|
#define arginfo_class_FFI_alignof arginfo_class_FFI_sizeof
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue