1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Arginfo: reuse zend_string objects for initializing attribute values (#19241)

Avoid initializing the same string content multiple times and make use of the
fact that the strings created to initialize attribute values are not freed by
simply making use of an existing zend_string with the same content if one is
available.
This commit is contained in:
Daniel Scherzer
2025-07-27 17:27:22 -07:00
committed by GitHub
parent 068aaed196
commit ff810d5e36
11 changed files with 113 additions and 91 deletions

View File

@@ -3319,8 +3319,12 @@ class AttributeInfo {
$this->args = $args;
}
/** @param array<string, ConstInfo> $allConstInfos */
public function generateCode(string $invocation, string $nameSuffix, array $allConstInfos, ?int $phpVersionIdMinimumCompatibility): string {
/**
* @param array<string, ConstInfo> $allConstInfos
* @param array<string, string> &$declaredStrings Map of string content to
* the name of a zend_string already created with that content
*/
public function generateCode(string $invocation, string $nameSuffix, array $allConstInfos, ?int $phpVersionIdMinimumCompatibility, array &$declaredStrings = []): string {
$escapedAttributeName = strtr($this->class, '\\', '_');
[$stringInit, $nameCode, $stringRelease] = StringBuilder::getString(
"attribute_name_{$escapedAttributeName}_$nameSuffix",
@@ -3344,6 +3348,9 @@ class AttributeInfo {
);
if ($strInit === '') {
$initValue = "\tZVAL_STR(&attribute_{$escapedAttributeName}_{$nameSuffix}->args[$i].value, $strUse);\n";
} elseif (isset($declaredStrings[$strVal])) {
$strUse = $declaredStrings[$strVal];
$initValue = "\tZVAL_STR_COPY(&attribute_{$escapedAttributeName}_{$nameSuffix}->args[$i].value, $strUse);\n";
}
}
if ($initValue === '') {
@@ -3353,6 +3360,9 @@ class AttributeInfo {
true,
"attribute_{$escapedAttributeName}_{$nameSuffix}_arg{$i}_str"
);
if ($arg->value instanceof Node\Scalar\String_) {
$declaredStrings[$arg->value->value] = "attribute_{$escapedAttributeName}_{$nameSuffix}_arg{$i}_str";
}
} else {
$code .= $initValue;
}
@@ -3603,6 +3613,8 @@ class ClassInfo {
$php80CondEnd = "#endif\n";
}
$declaredStrings = [];
if (!empty($this->attributes)) {
$code .= $php80CondStart;
@@ -3611,26 +3623,27 @@ class ClassInfo {
"zend_add_class_attribute(class_entry",
"class_{$escapedName}_$key",
$allConstInfos,
$this->phpVersionIdMinimumCompatibility
$this->phpVersionIdMinimumCompatibility,
$declaredStrings
);
}
$code .= $php80CondEnd;
}
if ($attributeInitializationCode = generateConstantAttributeInitialization($this->constInfos, $allConstInfos, $this->phpVersionIdMinimumCompatibility, $this->cond)) {
if ($attributeInitializationCode = generateConstantAttributeInitialization($this->constInfos, $allConstInfos, $this->phpVersionIdMinimumCompatibility, $this->cond, $declaredStrings)) {
$code .= $php80CondStart;
$code .= "\n" . $attributeInitializationCode;
$code .= $php80CondEnd;
}
if ($attributeInitializationCode = generatePropertyAttributeInitialization($this->propertyInfos, $allConstInfos, $this->phpVersionIdMinimumCompatibility)) {
if ($attributeInitializationCode = generatePropertyAttributeInitialization($this->propertyInfos, $allConstInfos, $this->phpVersionIdMinimumCompatibility, $declaredStrings)) {
$code .= $php80CondStart;
$code .= "\n" . $attributeInitializationCode;
$code .= $php80CondEnd;
}
if ($attributeInitializationCode = generateFunctionAttributeInitialization($this->funcInfos, $allConstInfos, $this->phpVersionIdMinimumCompatibility, $this->cond)) {
if ($attributeInitializationCode = generateFunctionAttributeInitialization($this->funcInfos, $allConstInfos, $this->phpVersionIdMinimumCompatibility, $this->cond, $declaredStrings)) {
$code .= $php80CondStart;
$code .= "\n" . $attributeInitializationCode;
$code .= $php80CondEnd;
@@ -5185,8 +5198,9 @@ function generateArgInfoCode(
$php80MinimumCompatibility = $fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_80_VERSION_ID;
if ($fileInfo->generateClassEntries) {
$attributeInitializationCode = generateFunctionAttributeInitialization($fileInfo->funcInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null);
$attributeInitializationCode .= generateGlobalConstantAttributeInitialization($fileInfo->constInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null);
$declaredStrings = [];
$attributeInitializationCode = generateFunctionAttributeInitialization($fileInfo->funcInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null, $declaredStrings);
$attributeInitializationCode .= generateGlobalConstantAttributeInitialization($fileInfo->constInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null, $declaredStrings);
if ($attributeInitializationCode) {
if (!$php80MinimumCompatibility) {
$attributeInitializationCode = "\n#if (PHP_VERSION_ID >= " . PHP_80_VERSION_ID . ")" . $attributeInitializationCode . "#endif\n";
@@ -5244,12 +5258,16 @@ function generateFunctionEntries(?Name $className, array $funcInfos, ?string $co
return $code;
}
/** @param iterable<FuncInfo> $funcInfos */
function generateFunctionAttributeInitialization(iterable $funcInfos, array $allConstInfos, ?int $phpVersionIdMinimumCompatibility, ?string $parentCond = null): string {
/**
* @param iterable<FuncInfo> $funcInfos
* @param array<string, string> &$declaredStrings Map of string content to
* the name of a zend_string already created with that content
*/
function generateFunctionAttributeInitialization(iterable $funcInfos, array $allConstInfos, ?int $phpVersionIdMinimumCompatibility, ?string $parentCond = null, array &$declaredStrings = []): string {
return generateCodeWithConditions(
$funcInfos,
"",
static function (FuncInfo $funcInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility) {
static function (FuncInfo $funcInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility, &$declaredStrings) {
$code = null;
if ($funcInfo->name instanceof MethodName) {
@@ -5258,12 +5276,22 @@ function generateFunctionAttributeInitialization(iterable $funcInfos, array $all
$functionTable = "CG(function_table)";
}
// Make sure we don't try and use strings that might only be
// conditionally available; string reuse is only among declarations
// that are always there
if ($funcInfo->cond) {
$useDeclared = [];
} else {
$useDeclared = &$declaredStrings;
}
foreach ($funcInfo->attributes as $key => $attribute) {
$code .= $attribute->generateCode(
"zend_add_function_attribute(zend_hash_str_find_ptr($functionTable, \"" . $funcInfo->name->getNameForAttributes() . "\", sizeof(\"" . $funcInfo->name->getNameForAttributes() . "\") - 1)",
"func_" . $funcInfo->name->getNameForAttributes() . "_$key",
$allConstInfos,
$phpVersionIdMinimumCompatibility
$phpVersionIdMinimumCompatibility,
$useDeclared
);
}
@@ -5273,7 +5301,8 @@ function generateFunctionAttributeInitialization(iterable $funcInfos, array $all
"zend_add_parameter_attribute(zend_hash_str_find_ptr($functionTable, \"" . $funcInfo->name->getNameForAttributes() . "\", sizeof(\"" . $funcInfo->name->getNameForAttributes() . "\") - 1), $index",
"func_{$funcInfo->name->getNameForAttributes()}_arg{$index}_$key",
$allConstInfos,
$phpVersionIdMinimumCompatibility
$phpVersionIdMinimumCompatibility,
$useDeclared
);
}
}
@@ -5287,12 +5316,15 @@ function generateFunctionAttributeInitialization(iterable $funcInfos, array $all
/**
* @param iterable<ConstInfo> $constInfos
* @param array<string, ConstInfo> $allConstInfos
* @param array<string, string> &$declaredStrings Map of string content to
* the name of a zend_string already created with that content
*/
function generateGlobalConstantAttributeInitialization(
iterable $constInfos,
array $allConstInfos,
?int $phpVersionIdMinimumCompatibility,
?string $parentCond = null
?string $parentCond = null,
array &$declaredStrings = []
): string {
$isConditional = false;
if ($phpVersionIdMinimumCompatibility !== null && $phpVersionIdMinimumCompatibility < PHP_85_VERSION_ID) {
@@ -5301,12 +5333,20 @@ function generateGlobalConstantAttributeInitialization(
$code = generateCodeWithConditions(
$constInfos,
"",
static function (ConstInfo $constInfo) use ($allConstInfos, $isConditional) {
static function (ConstInfo $constInfo) use ($allConstInfos, $isConditional, &$declaredStrings) {
$code = "";
if ($constInfo->attributes === []) {
return null;
}
// Make sure we don't try and use strings that might only be
// conditionally available; string reuse is only among declarations
// that are always there
if ($constInfo->cond) {
$useDeclared = [];
} else {
$useDeclared = &$declaredStrings;
}
$constName = str_replace('\\', '\\\\', $constInfo->name->__toString());
$constVarName = 'const_' . $constName;
@@ -5321,7 +5361,8 @@ function generateGlobalConstantAttributeInitialization(
"zend_add_global_constant_attribute($constVarName",
$constVarName . "_$key",
$allConstInfos,
PHP_85_VERSION_ID
PHP_85_VERSION_ID,
$useDeclared
);
}
@@ -5338,25 +5379,37 @@ function generateGlobalConstantAttributeInitialization(
/**
* @param iterable<ConstInfo> $constInfos
* @param array<string, ConstInfo> $allConstInfos
* @param array<string, string> &$declaredStrings Map of string content to
* the name of a zend_string already created with that content
*/
function generateConstantAttributeInitialization(
iterable $constInfos,
array $allConstInfos,
?int $phpVersionIdMinimumCompatibility,
?string $parentCond = null
?string $parentCond = null,
array &$declaredStrings = []
): string {
return generateCodeWithConditions(
$constInfos,
"",
static function (ConstInfo $constInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility) {
static function (ConstInfo $constInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility, &$declaredStrings) {
$code = null;
// Make sure we don't try and use strings that might only be
// conditionally available; string reuse is only among declarations
// that are always there
if ($constInfo->cond) {
$useDeclared = [];
} else {
$useDeclared = &$declaredStrings;
}
foreach ($constInfo->attributes as $key => $attribute) {
$code .= $attribute->generateCode(
"zend_add_class_constant_attribute(class_entry, const_" . $constInfo->name->getDeclarationName(),
"const_" . $constInfo->name->getDeclarationName() . "_$key",
$allConstInfos,
$phpVersionIdMinimumCompatibility
$phpVersionIdMinimumCompatibility,
$useDeclared
);
}
@@ -5369,11 +5422,14 @@ function generateConstantAttributeInitialization(
/**
* @param iterable<PropertyInfo> $propertyInfos
* @param array<string, ConstInfo> $allConstInfos
* @param array<string, string> &$declaredStrings Map of string content to
* the name of a zend_string already created with that content
*/
function generatePropertyAttributeInitialization(
iterable $propertyInfos,
array $allConstInfos,
?int $phpVersionIdMinimumCompatibility
?int $phpVersionIdMinimumCompatibility,
array &$declaredStrings
): string {
$code = "";
foreach ($propertyInfos as $propertyInfo) {
@@ -5382,7 +5438,8 @@ function generatePropertyAttributeInitialization(
"zend_add_property_attribute(class_entry, property_" . $propertyInfo->name->getDeclarationName(),
"property_" . $propertyInfo->name->getDeclarationName() . "_" . $key,
$allConstInfos,
$phpVersionIdMinimumCompatibility
$phpVersionIdMinimumCompatibility,
$declaredStrings
);
}
}