gen_stub: add ReturnInfo::beginArgInfo()

The vast majority of the decisions about the use of `ZEND_BEGIN_ARG_INFO_EX` or
one of its variations are based on the return information of the function - is
the type builtin, is the return information tentative, does it include an
object mask, etc. Accordingly, move the logic into the `ReturnInfo` class.

The logic is actually moved into two methods, `ReturnInfo::beginArgInfo()`,
which needs to handle the case of tentative returns being used when PHP < 8.1
is supported, and `::beginArgInfoCompatible()`, which can assume that PHP 8.1+
is supported and thus make use of early returns and guard clauses. Further
improvements to the logic will be made in a subsequent commit.

In the process, make `ReturnInfo::$byRef` private.
This commit is contained in:
Daniel Scherzer 2025-03-16 22:44:45 -07:00 committed by DanielEScherzer
parent 24b7c7a365
commit b5361d75e0

View file

@ -1146,7 +1146,7 @@ class ReturnInfo {
self::REFCOUNT_N,
];
public /* readonly */ bool $byRef;
private /* readonly */ bool $byRef;
// NOT readonly - gets removed when discarding info for older PHP versions
public ?Type $type;
public /* readonly */ ?Type $phpDocType;
@ -1193,6 +1193,69 @@ class ReturnInfo {
$this->refcount = $refcount;
}
public function beginArgInfo(string $funcInfoName, int $minArgs, bool $php81MinimumCompatibility): string {
$code = $this->beginArgInfoCompatible($funcInfoName, $minArgs);
if ($this->type !== null && $this->tentativeReturnType && !$php81MinimumCompatibility) {
$realCode = "#if (PHP_VERSION_ID >= " . PHP_81_VERSION_ID . ")\n";
$realCode .= $code;
$realCode .= sprintf(
"#else\nZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n#endif\n",
$funcInfoName, $this->byRef, $minArgs
);
return $realCode;
}
return $code;
}
/**
* Assumes PHP 8.1 compatibility, if that is not the case the caller is
* responsible for making the use of a tentative return type conditional
* based on the PHP version. Separate to allow using early returns
*/
private function beginArgInfoCompatible(string $funcInfoName, int $minArgs): string {
if ($this->type !== null) {
if (null !== $simpleReturnType = $this->type->tryToSimpleType()) {
if ($simpleReturnType->isBuiltin) {
return sprintf(
"%s(%s, %d, %d, %s, %d)\n",
$this->tentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX",
$funcInfoName, $this->byRef,
$minArgs,
$simpleReturnType->toTypeCode(), $this->type->isNullable()
);
}
return sprintf(
"%s(%s, %d, %d, %s, %d)\n",
$this->tentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX",
$funcInfoName, $this->byRef,
$minArgs,
$simpleReturnType->toEscapedName(), $this->type->isNullable()
);
}
$arginfoType = $this->type->toArginfoType();
if ($arginfoType->hasClassType()) {
return sprintf(
"%s(%s, %d, %d, %s, %s)\n",
$this->tentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX",
$funcInfoName, $this->byRef,
$minArgs,
$arginfoType->toClassTypeString(), $arginfoType->toTypeMask()
);
}
return sprintf(
"%s(%s, %d, %d, %s)\n",
$this->tentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX",
$funcInfoName, $this->byRef,
$minArgs,
$arginfoType->toTypeMask()
);
}
return sprintf(
"ZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n",
$funcInfoName, $this->byRef, $minArgs
);
}
}
class FuncInfo {
@ -5012,65 +5075,11 @@ function parseStubFile(string $code): FileInfo {
}
function funcInfoToCode(FileInfo $fileInfo, FuncInfo $funcInfo): string {
$code = '';
$returnType = $funcInfo->return->type;
$isTentativeReturnType = $funcInfo->return->tentativeReturnType;
$php81MinimumCompatibility = $fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_81_VERSION_ID;
if ($returnType !== null) {
if ($isTentativeReturnType && !$php81MinimumCompatibility) {
$code .= "#if (PHP_VERSION_ID >= " . PHP_81_VERSION_ID . ")\n";
}
if (null !== $simpleReturnType = $returnType->tryToSimpleType()) {
if ($simpleReturnType->isBuiltin) {
$code .= sprintf(
"%s(%s, %d, %d, %s, %d)\n",
$isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX",
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
$funcInfo->numRequiredArgs,
$simpleReturnType->toTypeCode(), $returnType->isNullable()
);
} else {
$code .= sprintf(
"%s(%s, %d, %d, %s, %d)\n",
$isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX",
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
$funcInfo->numRequiredArgs,
$simpleReturnType->toEscapedName(), $returnType->isNullable()
);
}
} else {
$arginfoType = $returnType->toArginfoType();
if ($arginfoType->hasClassType()) {
$code .= sprintf(
"%s(%s, %d, %d, %s, %s)\n",
$isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX",
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
$funcInfo->numRequiredArgs,
$arginfoType->toClassTypeString(), $arginfoType->toTypeMask()
);
} else {
$code .= sprintf(
"%s(%s, %d, %d, %s)\n",
$isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX",
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
$funcInfo->numRequiredArgs,
$arginfoType->toTypeMask()
);
}
}
if ($isTentativeReturnType && !$php81MinimumCompatibility) {
$code .= sprintf(
"#else\nZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n#endif\n",
$funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs
);
}
} else {
$code .= sprintf(
"ZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n",
$funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs
);
}
$code = $funcInfo->return->beginArgInfo(
$funcInfo->getArgInfoName(),
$funcInfo->numRequiredArgs,
$fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_81_VERSION_ID
);
foreach ($funcInfo->args as $argInfo) {
$code .= $argInfo->toZendInfo();