diff --git a/docs/REFACTORING_PLAN.md b/docs/REFACTORING_PLAN.md index 5e20b703..9ec1a32e 100644 --- a/docs/REFACTORING_PLAN.md +++ b/docs/REFACTORING_PLAN.md @@ -242,6 +242,7 @@ - `CompilerBase::isSameClassName()`、`isSameOrSubclassOf()`、`canAccessProtectedProperty()` 已委托 resolver,避免规则继续扩散。 - 已添加 `prepare/convert/idle` 编译阶段状态;`PropertyAccessResolver` 只能在 convert 阶段创建和使用,避免预处理阶段误用不完整的类表状态。 - `PropertyAccessResolver` 已改为依赖 `PropertyAccessContext` 只读接口,而不是完整依赖 `CompilerBase` 大类。 +- 已建立 `PropertyAssignTypeInfo`,抽离 typed property 写入的纯 metadata 计算,包括固定类型属性判断、默认值、runtime typecheck 列表和类型字符串。 - 当前迁移保持生成代码不变,后续阶段再统一 read/write emitter。 验证: diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 9d0fc935..dc466fd9 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -46,6 +46,7 @@ use PhpAot\Php\Platform\Windows; use PhpAot\Php\Resolver\PropertyAccessContext; use PhpAot\Php\Resolver\PropertyAccessResult; use PhpAot\Php\Resolver\PropertyAccessResolver; +use PhpAot\Php\Resolver\PropertyAssignTypeInfo; use PhpParser\Modifiers; use PhpParser\Node; use PhpParser\Node\ArrayItem; @@ -99,6 +100,9 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont public const string TYPE_BIGFLOAT = 'php::BigFloat'; public const string TYPE_BOX = 'php::Box'; + protected const string NATIVE_PROPERTY_VALUE_VAR = 'var'; + protected const string NATIVE_PROPERTY_VALUE_DYNAMIC = 'dynamic'; + /** * Keyword methods (to* builtins) with mandated return types. * Use findKeywordMethod() for unified lookup including keyword extension methods. @@ -2416,13 +2420,12 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont break; case 'Expr_StaticPropertyFetch': if ($this->isIdExpr($expr->name)) { - if (!$expr->hasAttribute('nativePropertyDef')) { + if (!$this->getNativePropertyDef($expr)) { $class = null; $this->findNativeStaticProperty($expr, $class); } - if ($expr->hasAttribute('nativePropertyDef')) { - /** @var PropertyDef $def */ - $def = $expr->getAttribute('nativePropertyDef'); + $def = $this->getNativePropertyDef($expr); + if ($def) { return $def->type; } } @@ -4650,25 +4653,12 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont protected function getFixedObjectPropDefaultValue(PropertyDef $def): ?string { - return match ($def->type) { - self::TYPE_INT => $def->default ?? '0', - self::TYPE_FLOAT => $def->default ?? '0.0', - self::TYPE_BOOL => $def->default ?? 'false', - self::TYPE_STR => $def->default ?? self::TYPE_STR . '()', - self::TYPE_ARRAY => $def->default ?? self::TYPE_ARRAY . '{}', - default => null, - }; + return (new PropertyAssignTypeInfo())->getFixedDefaultValue($def); } protected function isFixedObjectProp(PropertyDef $def): bool { - return in_array($def->type, [ - self::TYPE_INT, - self::TYPE_FLOAT, - self::TYPE_BOOL, - self::TYPE_STR, - self::TYPE_ARRAY, - ], true) && !$def->nullable; + return (new PropertyAssignTypeInfo())->isFixed($def); } protected function assertCanAssignObjectProp(Expr\PropertyFetch $left, Expr $right): void @@ -4683,12 +4673,11 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont private function assertCanAssignObjectProperty(NodeAbstract $left, Expr $right, string $label): void { - if (!$left->hasAttribute('nativePropertyDef')) { + $def = $this->getNativePropertyDef($left); + if (!$def) { return; } - /** @var PropertyDef $def */ - $def = $left->getAttribute('nativePropertyDef'); $propName = $this->parseIdentifier($left->name); if ($this->isNull($right)) { @@ -4732,12 +4721,11 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont protected function wrapObjectPropertyAssignTypeCheck(NodeAbstract $left, Expr $right, string $rightExpr): string { - if (!$left->hasAttribute('nativePropertyDef')) { + $def = $this->getNativePropertyDef($left); + if (!$def) { return $rightExpr; } - /** @var PropertyDef $def */ - $def = $left->getAttribute('nativePropertyDef'); $typeCheck = $this->getObjectPropertyAssignTypeCheck($def); if (empty($typeCheck)) { return $rightExpr; @@ -4776,26 +4764,15 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont private function getObjectPropertyAssignTypeCheck(PropertyDef $def): array { - if (!empty($def->typeCheck)) { - return $def->typeCheck; - } - if ($def->type !== self::TYPE_OBJECT || $def->class === '') { - return []; - } - - $check = []; - if ($def->nullable) { - $check[] = ['kind' => 'isNull']; - } - $check[] = ['kind' => 'instanceof', 'class' => $def->class]; - return $check; + return (new PropertyAssignTypeInfo())->getRuntimeTypeCheck($def); } private function getObjectPropertyTypeCheckDisplayName(NodeAbstract $left): string { $propName = $this->parseIdentifier($left->name); - if ($left->hasAttribute('nativeClassDef')) { - $class = $left->getAttribute('nativeClassDef')->getNamespacedName(false); + $classDef = $this->getNativePropertyClassDef($left); + if ($classDef) { + $class = $classDef->getNamespacedName(false); return $class . '::$' . $propName; } @@ -4808,13 +4785,7 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont private function getObjectPropertyTypeCheckTypeString(PropertyDef $def): string { - if ($def->typeStr !== '') { - return $def->typeStr; - } - if ($def->class !== '') { - return ($def->nullable ? '?' : '') . $def->class; - } - return $def->type; + return (new PropertyAssignTypeInfo())->getTypeString($def); } protected function parseUnset(Node\Stmt\Unset_ $node): string @@ -4843,9 +4814,8 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont $restoreDefault = null; if ($this->isIdExpr($var->name)) { $propertyId = $this->getPropertyIdentifier($var, $var->var, $var->name); - if ($var->hasAttribute('nativePropertyDef')) { - /** @var PropertyDef $def */ - $def = $var->getAttribute('nativePropertyDef'); + $def = $this->getNativePropertyDef($var); + if ($def) { if ($this->isFixedObjectProp($def)) { $restoreDefault = $this->getFixedObjectPropDefaultValue($def); if ($restoreDefault === null) { @@ -4922,11 +4892,8 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont } $objectVar = $objectName; $getProperty = $objectVar . '.attr(' . $id . ', ' . $this->escapeBool($update) . ')'; - if ($expr->hasAttribute('nativePropertyDef') and $this->nativeTypes) { - /** - * @var PropertyDef $def - */ - $def = $expr->getAttribute('nativePropertyDef'); + $def = $this->getNativePropertyDef($expr); + if ($def and $this->nativeTypes) { $propName = $this->parseIdentifier($property); $propVar = $this->getObjectPropVarName($objectVar, $propName); if ($objectVar === 'this_') { @@ -4940,15 +4907,20 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont ]; } $expr->setAttribute('nativePropertyVar', $propVar); + $this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR); return $propVar; } } elseif ($this->canHoistStableObjectProp($objectVar, $propName)) { // SSA-stable object: lazily create reference at first access point $result = $this->hoistStableObjectProp($objectVar, $propName, $id, $def->type); $expr->setAttribute('nativePropertyVar', $result); + $this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR); return $result; } } + if ($def) { + $this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_DYNAMIC); + } return $getProperty; } @@ -5884,14 +5856,33 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont return $expr->hasAttribute('nativeProperty'); } + protected function getNativePropertyDef(NodeAbstract $expr): ?PropertyDef + { + return $expr->hasAttribute('nativePropertyDef') ? $expr->getAttribute('nativePropertyDef') : null; + } + + protected function getNativePropertyClassDef(NodeAbstract $expr): ?ClassDef + { + return $expr->hasAttribute('nativeClassDef') ? $expr->getAttribute('nativeClassDef') : null; + } + + protected function setNativePropertyValueSource(NodeAbstract $expr, string $source): void + { + $expr->setAttribute('nativePropertyValueSource', $source); + } + + protected function isNativePropertyTypedValue(NodeAbstract $expr): bool + { + return $expr->getAttribute('nativePropertyValueSource') === self::NATIVE_PROPERTY_VALUE_VAR; + } + protected function parseNativeStaticPropertyFetch(Expr\StaticPropertyFetch $expr): string|bool { $class = null; $nativeProp = $this->findNativeStaticProperty($expr, $class); if ($nativeProp) { - if ($this->nativeTypes && $expr->hasAttribute('nativePropertyDef')) { - /** @var PropertyDef $def */ - $def = $expr->getAttribute('nativePropertyDef'); + $def = $this->getNativePropertyDef($expr); + if ($this->nativeTypes && $def) { $info = $this->getHoistedObjectPropInfo($def->type); $propName = $this->parseIdentifier($expr->name); $refVar = '_static_' . str_replace('\\', '_', $class) . '_' . $propName; @@ -5907,6 +5898,7 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont ]; } $helper = $def->type === self::TYPE_FLOAT ? 'php_aot_static_float_ref' : 'php_aot_static_int_ref'; + $this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR); return $helper . '(' . $refVar . ')'; } @@ -5919,13 +5911,16 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont 'kind' => $info['kind'], ]; } + $this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR); return $refVar; } if ($this->isNativePropertyAccess($expr)) { $classPtr = $this->getClassEntryPtr($class); + $this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_DYNAMIC); return Symbol::getStaticProperty() . '(' . $classPtr . ', ' . $nativeProp . ')'; } else { + $this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_DYNAMIC); return $nativeProp; } } diff --git a/src/Php/Parser/BinaryOpTrait.php b/src/Php/Parser/BinaryOpTrait.php index 66112bd0..28d3dc4c 100644 --- a/src/Php/Parser/BinaryOpTrait.php +++ b/src/Php/Parser/BinaryOpTrait.php @@ -209,16 +209,18 @@ trait BinaryOpTrait if (isset($this->context->objectProps[$nativePropertyVar])) { return $this->context->objectProps[$nativePropertyVar]['type']; } - if (!str_contains($nativePropertyVar, '.attr(') && $expr->hasAttribute('nativePropertyDef')) { - return $expr->getAttribute('nativePropertyDef')->type; + $def = $this->getNativePropertyDef($expr); + if ($def && $this->isNativePropertyTypedValue($expr)) { + return $def->type; } } return self::TYPE_VAR; } if ($expr instanceof Expr\StaticPropertyFetch) { - if ($expr->hasAttribute('nativePropertyDef') && !str_contains($value, 'getStaticProperty')) { - return $expr->getAttribute('nativePropertyDef')->type; + $def = $this->getNativePropertyDef($expr); + if ($def && $this->isNativePropertyTypedValue($expr)) { + return $def->type; } return self::TYPE_VAR; }