Support single class unions in gen stubs

This commit is contained in:
Nikita Popov 2019-11-15 12:50:44 +01:00
parent 468ecf295d
commit 0cec268d15
7 changed files with 112 additions and 57 deletions

View file

@ -128,6 +128,10 @@ typedef struct _zend_fcall_info_cache {
static const zend_internal_arg_info name[] = { \
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_MASK(type | _ZEND_ARG_INFO_FLAGS(return_reference, 0)) },
#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(name, return_reference, required_num_args, class_name, type) \
static const zend_internal_arg_info name[] = { \
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CLASS_CONST_MASK(#class_name, type | _ZEND_ARG_INFO_FLAGS(return_reference, 0)) },
#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_INIT_CODE(type, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0)) },

View file

@ -12,6 +12,9 @@ Class Closure
function call(object $newthis, ...$parameters) {}
/** @return Closure */
function fromCallable(callable $callable) {}
/**
* @param callable $callable Not a proper type annotation due to bug #78770
* @return Closure
*/
function fromCallable($callable) {}
}

View file

@ -269,6 +269,9 @@ typedef struct {
{ (void *) (ptr), \
(type_kind) | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0) | (extra_flags) }
#define ZEND_TYPE_INIT_PTR_MASK(ptr, type_mask) \
{ (void *) (ptr), (type_mask) }
#define ZEND_TYPE_INIT_CE(_ce, allow_null, extra_flags) \
ZEND_TYPE_INIT_PTR(_ce, _ZEND_TYPE_CE_BIT, allow_null, extra_flags)
@ -278,6 +281,9 @@ typedef struct {
#define ZEND_TYPE_INIT_CLASS_CONST(class_name, allow_null, extra_flags) \
ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_NAME_BIT, allow_null, extra_flags)
#define ZEND_TYPE_INIT_CLASS_CONST_MASK(class_name, type_mask) \
ZEND_TYPE_INIT_PTR_MASK(class_name, _ZEND_TYPE_NAME_BIT | (type_mask))
typedef union _zend_value {
zend_long lval; /* long value */
double dval; /* double value */

View file

@ -93,9 +93,9 @@ static const zend_function_entry date_functions[] = {
/* Advanced Interface */
PHP_FE(date_create, arginfo_date_create)
PHP_FE(date_create_immutable, arginfo_date_create)
PHP_FE(date_create_immutable, arginfo_date_create_immutable)
PHP_FE(date_create_from_format, arginfo_date_create_from_format)
PHP_FE(date_create_immutable_from_format, arginfo_date_create_from_format)
PHP_FE(date_create_immutable_from_format, arginfo_date_create_immutable_from_format)
PHP_FE(date_parse, arginfo_date_parse)
PHP_FE(date_parse_from_format, arginfo_date_parse_from_format)
PHP_FE(date_get_last_errors, arginfo_date_get_last_errors)

View file

@ -31,18 +31,16 @@ function localtime(int $timestamp = UNKNOWN, bool $associative = false): array {
function getdate(int $timestamp = UNKNOWN): array {}
/** @return DateTime|false */
function date_create(string $time = "now", ?DateTimeZone $timezone = null) {}
function date_create(string $time = "now", ?DateTimeZone $timezone = null): DateTime|false {}
/** @return DateTime|false */
function date_create_immutable(string $time = "now", ?DateTimeZone $timezone = null) {}
function date_create_immutable(
string $time = "now", ?DateTimeZone $timezone = null): DateTimeImmutable|false {}
/** @return DateTime|false */
function date_create_from_format(string $format, string $time, ?DateTimeZone $timezone = null) {}
function date_create_from_format(
string $format, string $time, ?DateTimeZone $timezone = null): DateTime|false {}
/** @return DateTimeImmutable|false */
function date_create_immutable_from_format(
string $format, string $time, ?DateTimeZone $timezone = null) {}
string $format, string $time, ?DateTimeZone $timezone = null): DateTimeImmutable|false {}
function date_parse(string $date): array {}
@ -52,15 +50,13 @@ function date_get_last_errors(): array|false {}
function date_format(DateTimeInterface $object, string $format): string {}
/** @return DateTime|false */
function date_modify(DateTime $object, string $modify) {}
function date_modify(DateTime $object, string $modify): DateTime|false {}
function date_add(DateTime $object, DateInterval $interval): DateTime {}
function date_sub(DateTime $object, DateInterval $interval): DateTime {}
/** @return DateTimeZone|false */
function date_timezone_get(DateTimeInterface $object) {}
function date_timezone_get(DateTimeInterface $object): DateTimeZone|false {}
function date_timezone_set(DateTimeInterface $object, DateTimeZone $timezone): DateTime {}
@ -80,8 +76,7 @@ function date_timestamp_set(DateTime $object, int $timestamp): DateTime {}
function date_timestamp_get(DateTimeInterface $object): int|false {}
/** @return DateTimeZone|false */
function timezone_open(string $timezone) {}
function timezone_open(string $timezone): DateTimeZone|false {}
function timezone_name_get(DateTimeZone $object): string {}
@ -100,8 +95,7 @@ function timezone_abbreviations_list(): array {}
function timezone_version_get(): string {}
/** @return DateInterval|false */
function date_interval_create_from_date_string(string $time) {}
function date_interval_create_from_date_string(string $time): DateInterval|false {}
function date_interval_format(DateInterval $object, string $format): string {}

View file

@ -53,20 +53,27 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_getdate, 0, 0, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_date_create, 0, 0, 0)
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_date_create, 0, 0, DateTime, MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, time, IS_STRING, 0)
ZEND_ARG_OBJ_INFO(0, timezone, DateTimeZone, 1)
ZEND_END_ARG_INFO()
#define arginfo_date_create_immutable arginfo_date_create
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_date_create_immutable, 0, 0, DateTimeImmutable, MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, time, IS_STRING, 0)
ZEND_ARG_OBJ_INFO(0, timezone, DateTimeZone, 1)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_date_create_from_format, 0, 0, 2)
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_date_create_from_format, 0, 2, DateTime, MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, time, IS_STRING, 0)
ZEND_ARG_OBJ_INFO(0, timezone, DateTimeZone, 1)
ZEND_END_ARG_INFO()
#define arginfo_date_create_immutable_from_format arginfo_date_create_from_format
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_date_create_immutable_from_format, 0, 2, DateTimeImmutable, MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, time, IS_STRING, 0)
ZEND_ARG_OBJ_INFO(0, timezone, DateTimeZone, 1)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_date_parse, 0, 1, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, date, IS_STRING, 0)
@ -85,7 +92,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_date_format, 0, 2, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_date_modify, 0, 0, 2)
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_date_modify, 0, 2, DateTime, MAY_BE_FALSE)
ZEND_ARG_OBJ_INFO(0, object, DateTime, 0)
ZEND_ARG_TYPE_INFO(0, modify, IS_STRING, 0)
ZEND_END_ARG_INFO()
@ -97,7 +104,7 @@ ZEND_END_ARG_INFO()
#define arginfo_date_sub arginfo_date_add
ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timezone_get, 0, 0, 1)
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_date_timezone_get, 0, 1, DateTimeZone, MAY_BE_FALSE)
ZEND_ARG_OBJ_INFO(0, object, DateTimeInterface, 0)
ZEND_END_ARG_INFO()
@ -147,7 +154,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_date_timestamp_get, 0, 1, MAY_BE
ZEND_ARG_OBJ_INFO(0, object, DateTimeInterface, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_open, 0, 0, 1)
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_timezone_open, 0, 1, DateTimeZone, MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, timezone, IS_STRING, 0)
ZEND_END_ARG_INFO()
@ -187,7 +194,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_timezone_version_get, 0, 0, IS_STRING, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_date_interval_create_from_date_string, 0, 0, 1)
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_date_interval_create_from_date_string, 0, 1, DateInterval, MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, time, IS_STRING, 0)
ZEND_END_ARG_INFO()
@ -237,7 +244,10 @@ ZEND_END_ARG_INFO()
#define arginfo_class_DateTimeInterface___wakeup arginfo_class_DateTimeInterface_getTimezone
#define arginfo_class_DateTime___construct arginfo_date_create
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime___construct, 0, 0, 0)
ZEND_ARG_TYPE_INFO(0, time, IS_STRING, 0)
ZEND_ARG_OBJ_INFO(0, timezone, DateTimeZone, 1)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime___set_state, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)
@ -247,7 +257,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_createFromImmutable, 0, 0, 1)
ZEND_ARG_OBJ_INFO(0, object, DateTimeImmutable, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_DateTime_createFromFormat arginfo_date_create_from_format
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_createFromFormat, 0, 0, 2)
ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, time, IS_STRING, 0)
ZEND_ARG_OBJ_INFO(0, timezone, DateTimeZone, 1)
ZEND_END_ARG_INFO()
#define arginfo_class_DateTime_getLastErrors arginfo_class_DateTimeInterface_getTimezone
@ -288,7 +302,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_setTimestamp, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_DateTimeImmutable___construct arginfo_date_create
#define arginfo_class_DateTimeImmutable___construct arginfo_class_DateTime___construct
#define arginfo_class_DateTimeImmutable___set_state arginfo_class_DateTime___set_state
@ -296,7 +310,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeImmutable_createFromMutable, 0, 0,
ZEND_ARG_OBJ_INFO(0, object, DateTime, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_DateTimeImmutable_createFromFormat arginfo_date_create_from_format
#define arginfo_class_DateTimeImmutable_createFromFormat arginfo_class_DateTime_createFromFormat
#define arginfo_class_DateTimeImmutable_getLastErrors arginfo_class_DateTimeInterface_getTimezone
@ -316,7 +330,9 @@ ZEND_END_ARG_INFO()
#define arginfo_class_DateTimeImmutable_setTimestamp arginfo_class_DateTime_setTimestamp
#define arginfo_class_DateTimeZone___construct arginfo_timezone_open
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeZone___construct, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, timezone, IS_STRING, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_DateTimeZone_getName arginfo_class_DateTimeInterface_getTimezone
@ -346,7 +362,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateInterval___construct, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, interval_spec, IS_STRING, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_DateInterval_createFromDateString arginfo_date_interval_create_from_date_string
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateInterval_createFromDateString, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, time, IS_STRING, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_DateInterval_format arginfo_class_DateTimeInterface_format

View file

@ -131,6 +131,10 @@ class SimpleType {
}
}
public function toEscapedName(): string {
return str_replace('\\', '\\\\', $this->name);
}
public function equals(SimpleType $other) {
return $this->name === $other->name
&& $this->isBuiltin === $other->isBuiltin;
@ -167,15 +171,6 @@ class Type {
return false;
}
public function isBuiltinOnly(): bool {
foreach ($this->types as $type) {
if (!$type->isBuiltin) {
return false;
}
}
return true;
}
public function getWithoutNull(): Type {
return new Type(array_filter($this->types, function(SimpleType $type) {
return !$type->isNull();
@ -190,10 +185,20 @@ class Type {
return null;
}
public function toTypeMask(): string {
return implode('|', array_map(function(SimpleType $type) {
return $type->toTypeMask();
}, $this->types));
public function tryToRepresentableType(): ?RepresentableType {
$classType = null;
$builtinTypes = [];
foreach ($this->types as $type) {
if ($type->isBuiltin) {
$builtinTypes[] = $type;
} else if ($classType === null) {
$classType = $type;
} else {
// We can only represent a single class type.
return false;
}
}
return new RepresentableType($classType, $builtinTypes);
}
public static function equals(?Type $a, ?Type $b): bool {
@ -215,6 +220,24 @@ class Type {
}
}
class RepresentableType {
/** @var ?SimpleType $classType */
public $classType;
/** @var SimpleType[] $builtinTypes */
public $builtinTypes;
public function __construct(?SimpleType $classType, array $builtinTypes) {
$this->classType = $classType;
$this->builtinTypes = $builtinTypes;
}
public function toTypeMask(): string {
return implode('|', array_map(function(SimpleType $type) {
return $type->toTypeMask();
}, $this->builtinTypes));
}
}
class ArgInfo {
const SEND_BY_VAL = 0;
const SEND_BY_REF = 1;
@ -467,8 +490,7 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
$code = '';
$returnType = $funcInfo->return->type;
if ($returnType !== null) {
$simpleReturnType = $returnType->tryToSimpleType();
if ($simpleReturnType !== null) {
if (null !== $simpleReturnType = $returnType->tryToSimpleType()) {
if ($simpleReturnType->isBuiltin) {
$code .= sprintf(
"ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_%s, %d, %d, %s, %d)\n",
@ -479,15 +501,23 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
$code .= sprintf(
"ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_%s, %d, %d, %s, %d)\n",
$funcInfo->name, $funcInfo->return->byRef, $funcInfo->numRequiredArgs,
str_replace('\\', '\\\\', $simpleReturnType->name), $returnType->isNullable()
$simpleReturnType->toEscapedName(), $returnType->isNullable()
);
}
} else if ($returnType->isBuiltinOnly()) {
} else if (null !== $representableType = $returnType->tryToRepresentableType()) {
if ($representableType->classType !== null) {
$code .= sprintf(
"ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_%s, %d, %d, %s, %s)\n",
$funcInfo->name, $funcInfo->return->byRef, $funcInfo->numRequiredArgs,
$representableType->classType->toEscapedName(), $representableType->toTypeMask()
);
} else {
$code .= sprintf(
"ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_%s, %d, %d, %s)\n",
$funcInfo->name, $funcInfo->return->byRef, $funcInfo->numRequiredArgs,
$returnType->toTypeMask()
$representableType->toTypeMask()
);
}
} else {
throw new Exception('Unimplemented');
}
@ -514,7 +544,7 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
$code .= sprintf(
"\tZEND_%s_OBJ_INFO(%s, %s, %s, %d)\n",
$argKind, $argInfo->getSendByString(), $argInfo->name,
str_replace('\\', '\\\\', $simpleArgType->name), $argType->isNullable()
$simpleArgType->toEscapedName(), $argType->isNullable()
);
}
} else {