From 4411216354c824d818df9c1ca3f7d93ab4e25bff Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 12 May 2026 15:37:41 +0800 Subject: [PATCH] =?UTF-8?q?refactor(aot):=20=E7=AE=80=E5=8C=96=20unsafe=5F?= =?UTF-8?q?ptr=20=E4=BD=BF=E7=94=A8=E5=B9=B6=E6=94=B9=E8=BF=9B=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除手动创建 unsafe_ptr 的调用,直接传递容器变量 - 添加对 UnsafePtr 参数重新赋值的错误检查 - 验证 UnsafePtr 参数必须是标准容器变量 - 更新 unsafe_cast 错误消息以反映参数要求 - 移除不再需要的 unsafe_ptr 解析方法 - 添加新的测试用例验证各种错误场景 --- ...afe-cast-rejects-unsafe-ptr-local-copy.php | 7 ++ ...unsafe-ptr-argument-requires-container.php | 11 +++ ...afe-ptr-parameter-cannot-be-reassigned.php | 6 ++ phpunit/src/AssignTest.php | 26 ++++++- src/Php/CompilerBase.php | 74 ++++++++++++++----- src/Php/Context/FunctionContext.php | 5 -- src/Php/Parser/StdContainerParser.php | 26 +------ tests/aot/std-array/007.phpt | 3 +- tests/aot/std-array/008.phpt | 4 +- tests/aot/std-array/009.phpt | 24 ++++++ tests/aot/std-map/005.phpt | 3 +- tests/aot/std-map/006.phpt | 4 +- tests/aot/std-map/007.phpt | 21 ++++++ tests/aot/std-unordered-map/005.phpt | 3 +- tests/aot/std-unordered-map/006.phpt | 4 +- tests/aot/std-unordered-map/007.phpt | 21 ++++++ tests/aot/std-vector/006.phpt | 3 +- tests/aot/std-vector/007.phpt | 4 +- tests/aot/std-vector/008.phpt | 4 +- tests/aot/std-vector/009.phpt | 3 +- tests/aot/std-vector/010.phpt | 21 ++++++ 21 files changed, 202 insertions(+), 75 deletions(-) create mode 100644 phpunit/code/std-unsafe-cast-rejects-unsafe-ptr-local-copy.php create mode 100644 phpunit/code/std-unsafe-ptr-argument-requires-container.php create mode 100644 phpunit/code/std-unsafe-ptr-parameter-cannot-be-reassigned.php create mode 100644 tests/aot/std-array/009.phpt create mode 100644 tests/aot/std-map/007.phpt create mode 100644 tests/aot/std-unordered-map/007.phpt create mode 100644 tests/aot/std-vector/010.phpt diff --git a/phpunit/code/std-unsafe-cast-rejects-unsafe-ptr-local-copy.php b/phpunit/code/std-unsafe-cast-rejects-unsafe-ptr-local-copy.php new file mode 100644 index 00000000..fd511a21 --- /dev/null +++ b/phpunit/code/std-unsafe-cast-rejects-unsafe-ptr-local-copy.php @@ -0,0 +1,7 @@ +exec( - 'std::unsafe_cast() expects second argument to be declared as UnsafePtr', + 'std::unsafe_cast() expects second argument to be an UnsafePtr parameter', 'std-unsafe-cast-requires-unsafe-ptr.php' ); } + + public function testStdUnsafeCastRejectsUnsafePtrLocalCopy() + { + $this->exec( + 'std::unsafe_cast() expects second argument to be an UnsafePtr parameter', + 'std-unsafe-cast-rejects-unsafe-ptr-local-copy.php' + ); + } + + public function testStdUnsafePtrParameterCannotBeReassigned() + { + $this->exec( + 'Cannot re-assign UnsafePtr parameter `$unsafePtr`', + 'std-unsafe-ptr-parameter-cannot-be-reassigned.php' + ); + } + + public function testStdUnsafePtrArgumentRequiresContainer() + { + $this->exec( + 'Argument `unsafePtr` must be a std container variable for UnsafePtr parameter', + 'std-unsafe-ptr-argument-requires-container.php' + ); + } } diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 445374ea..644c25c3 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -1050,9 +1050,6 @@ class CompilerBase extends \PhpAot\Core\Translator } foreach ($this->functionDef->argInfoList as $argInfo) { $this->addArgument($argInfo->name, $argInfo->type); - if ($argInfo->unsafePtr) { - $this->addUnsafePtr($argInfo->name); - } if ($argInfo->class and !$this->isAbstractClass($argInfo->class) and !$this->hasInterface($argInfo->class) @@ -1499,6 +1496,9 @@ class CompilerBase extends \PhpAot\Core\Translator if ($var === 'this_') { $this->fatalError($left, 'Cannot re-assign $this'); } + if ($this->isVarExpr($left) and $this->isUnsafePtrParameter($var)) { + $this->fatalError($left, "Cannot re-assign UnsafePtr parameter `\${$var}`"); + } $type = $this->detectTypeOfExpr($right); @@ -1575,9 +1575,6 @@ class CompilerBase extends \PhpAot\Core\Translator if (!$this->hasVar($var)) { $this->addLocalVar($var, $right->getAttribute('nativeType')); } - if ($right->getAttribute('unsafePtr')) { - $this->addUnsafePtr($var); - } return $var . ' = ' . $valueExpr; } } @@ -1609,10 +1606,22 @@ class CompilerBase extends \PhpAot\Core\Translator return $this->parseAssignArrayDim($left, $right); } - $rightExpr = $this->parseExpr($right); + $rightExpr = $this->parseAssignRightExpr($right); return $var . ' = ' . $this->convertExprType($rightExpr, $this->detectTypeOfExpr($left), $this->detectTypeOfExpr($right)); } + protected function parseAssignRightExpr(Expr $right): string + { + $rightExpr = $this->parseExpr($right); + if ($this->isVarExpr($right)) { + $rightVar = $this->parseIdentifier($right); + if ($this->isStdContainer($rightVar)) { + return $this->convertArrayExpr($rightExpr); + } + } + return $rightExpr; + } + protected function parseEcho(mixed $v): string { $lines = []; @@ -1896,11 +1905,6 @@ class CompilerBase extends \PhpAot\Core\Translator $this->context->localVars[$name] = $type; } - protected function addUnsafePtr(string $name): void - { - $this->context->unsafePtrs[$name] = true; - } - protected function registerStdType(string $key): int { if (isset($this->stdTypeMap[$key])) { @@ -1911,11 +1915,6 @@ class CompilerBase extends \PhpAot\Core\Translator return $typeId; } - protected function isUnsafePtr(string $name): bool - { - return isset($this->context->unsafePtrs[$name]); - } - protected function addTmpVar(string $type): string { $var = $this->genTmpVarName(); @@ -3061,6 +3060,10 @@ class CompilerBase extends \PhpAot\Core\Translator foreach ($args as $i => $arg) { $argInfo = $this->getArgInfo($arg, $nativeFunc, $i); + if ($argInfo->unsafePtr) { + $argList[] = $this->getUnsafePtrConvertedArg($arg, $argInfo); + continue; + } if ($argInfo->variadic) { $argsSlice = array_slice($args, $i); if (count($argsSlice) === 1 and $argsSlice[0]->unpack) { @@ -3966,6 +3969,24 @@ class CompilerBase extends \PhpAot\Core\Translator return $this->convertExprType($expr, $argInfo->type, $type); } + protected function getUnsafePtrConvertedArg(Node\Arg $arg, ArgInfo $argInfo): string + { + if (!$this->isVarExpr($arg->value)) { + $this->fatalError($arg, "Argument `{$argInfo->name}` must be a std container variable for UnsafePtr parameter"); + } + + $var = $this->parseVariable($arg->value); + if (!$this->hasVar($var)) { + $this->fatalError($arg, 'Undefined variable `$' . $var . '`'); + } + if (!$this->isStdContainer($var)) { + $this->fatalError($arg, "Argument `{$argInfo->name}` must be a std container variable for UnsafePtr parameter"); + } + + $info = $this->getStdContainerVarInfo($var); + return 'php_create_unsafe_ptr(&' . $var . ', ' . $info['typeId'] . ')'; + } + protected function convertExprType(string $expr, $leftType, $rightType): string { if ($leftType === self::TYPE_FLOAT or $rightType === self::TYPE_FLOAT) { @@ -4634,6 +4655,9 @@ class CompilerBase extends \PhpAot\Core\Translator $this->context->inAssignExpr = false; if ($this->isVarExpr($expr->var)) { + if ($this->isUnsafePtrParameter($left)) { + $this->fatalError($expr->var, "Cannot re-assign UnsafePtr parameter `\${$left}`"); + } if (!$this->hasVar($left)) { $this->addLocalVar($left, self::TYPE_REF); } else { @@ -5424,9 +5448,6 @@ class CompilerBase extends \PhpAot\Core\Translator $expr->setAttribute('nativeType', $type); $valueExpr = $this->parseExpr($expr->args[0]->value); return $this->convertExprFromType($type, $valueExpr); - } elseif ($func === 'unsafe_ptr') { - $expr->setAttribute('nativeType', self::TYPE_VAR); - return $this->parseStdUnsafePtr($expr); } else { $this->fatalError($expr, 'Unknown std method: ' . $func); } @@ -5440,6 +5461,19 @@ class CompilerBase extends \PhpAot\Core\Translator and $this->parseIdentifier($type) === 'UnsafePtr'; } + protected function isUnsafePtrParameter(string $name): bool + { + if (!$this->functionDef) { + return false; + } + foreach ($this->functionDef->argInfoList as $argInfo) { + if ($argInfo->name === $name) { + return $argInfo->unsafePtr; + } + } + return false; + } + protected function parseParentMethodCall(Expr\StaticCall $expr): string { $methodStr = $this->classDef->name . '::' . $this->parseIdentifier($expr->name); diff --git a/src/Php/Context/FunctionContext.php b/src/Php/Context/FunctionContext.php index 3fce125c..d5554821 100644 --- a/src/Php/Context/FunctionContext.php +++ b/src/Php/Context/FunctionContext.php @@ -24,10 +24,6 @@ class FunctionContext * @var array */ public array $stdContainers = []; - /** - * @var array - */ - public array $unsafePtrs = []; public array $localVars = []; public array $staticVars = []; public array $globalVars = []; @@ -62,7 +58,6 @@ class FunctionContext $this->objects = []; $this->stdArrays = []; $this->stdContainers = []; - $this->unsafePtrs = []; $this->objectProps = []; $this->ceWrappers = []; $this->tmpVarIndex = 0; diff --git a/src/Php/Parser/StdContainerParser.php b/src/Php/Parser/StdContainerParser.php index 6fa54879..d354ad76 100644 --- a/src/Php/Parser/StdContainerParser.php +++ b/src/Php/Parser/StdContainerParser.php @@ -372,28 +372,6 @@ trait StdContainerParser return 'php::toObject(' . $valueExpr . ', ' . $this->getClassEntryPtr($class) . ', true)'; } - protected function parseStdUnsafePtr(Expr\StaticCall $expr): string - { - if (count($expr->args) !== 1) { - $this->fatalError($expr, 'std::unsafe_ptr() expects one argument'); - } - $arg = $expr->args[0]->value; - if (!$this->isVarExpr($arg)) { - $this->fatalError($expr, 'std::unsafe_ptr() expects a std container variable'); - } - $container = $this->parseVariable($arg); - if (!$this->isStdContainer($container)) { - $this->fatalError($expr, 'std::unsafe_ptr() only supports std container variables'); - } - - $tmpVar = $this->addTmpVar(self::TYPE_VAR); - $this->addUnsafePtr($tmpVar); - $expr->setAttribute('unsafePtr', true); - $info = $this->getStdContainerVarInfo($container); - $this->context->beforeStmtLines[] = $tmpVar . ' = php_create_unsafe_ptr(&' . $container . ', ' . $info['typeId'] . ');'; - return $tmpVar; - } - protected function parseStdUnsafeCastAssign(string $var, Expr\StaticCall $expr): string { if (count($expr->args) !== 2) { @@ -414,8 +392,8 @@ trait StdContainerParser if (!$this->hasVar($unsafePtr)) { $this->fatalError($expr->args[1]->value, 'Undefined variable `$' . $unsafePtr . '`'); } - if (!$this->isUnsafePtr($unsafePtr)) { - $this->fatalError($expr->args[1]->value, 'std::unsafe_cast() expects second argument to be declared as UnsafePtr'); + if (!$this->isUnsafePtrParameter($unsafePtr)) { + $this->fatalError($expr->args[1]->value, 'std::unsafe_cast() expects second argument to be an UnsafePtr parameter'); } if ($containerType === 'array') { diff --git a/tests/aot/std-array/007.phpt b/tests/aot/std-array/007.phpt index b38bc562..3da3a20d 100644 --- a/tests/aot/std-array/007.phpt +++ b/tests/aot/std-array/007.phpt @@ -15,8 +15,7 @@ function main() { $array[1] = 7; $array[2] = 3; - $ptr = std::unsafe_ptr($array); - std_array_unsafe_ptr_update($ptr); + std_array_unsafe_ptr_update($array); var_dump($array[2]); } ?> diff --git a/tests/aot/std-array/008.phpt b/tests/aot/std-array/008.phpt index d95dc6c9..a41e0940 100644 --- a/tests/aot/std-array/008.phpt +++ b/tests/aot/std-array/008.phpt @@ -9,10 +9,8 @@ function std_array_unsafe_ptr_type_mismatch(UnsafePtr $unsafePtr): void function main() { $array = std::array(native_types::type_int, 3); - $ptr = std::unsafe_ptr($array); - try { - std_array_unsafe_ptr_type_mismatch($ptr); + std_array_unsafe_ptr_type_mismatch($array); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } diff --git a/tests/aot/std-array/009.phpt b/tests/aot/std-array/009.phpt new file mode 100644 index 00000000..6e4e5a8a --- /dev/null +++ b/tests/aot/std-array/009.phpt @@ -0,0 +1,24 @@ +--TEST-- +std array: assign to PHP array +--FILE-- + +--EXPECT-- +bool(true) +int(3) +int(10) +int(20) +int(30) diff --git a/tests/aot/std-map/005.phpt b/tests/aot/std-map/005.phpt index 3fca4008..9a1bd3cb 100644 --- a/tests/aot/std-map/005.phpt +++ b/tests/aot/std-map/005.phpt @@ -23,8 +23,7 @@ function main() { $map["b"] = 7; $map["c"] = 3; - $ptr = std::unsafe_ptr($map); - std_map_unsafe_ptr_update($ptr); + std_map_unsafe_ptr_update($map); var_dump($map["c"]); } ?> diff --git a/tests/aot/std-map/006.phpt b/tests/aot/std-map/006.phpt index 8a21f7c4..902b8a64 100644 --- a/tests/aot/std-map/006.phpt +++ b/tests/aot/std-map/006.phpt @@ -9,10 +9,8 @@ function std_map_unsafe_ptr_type_mismatch(UnsafePtr $unsafePtr): void function main() { $map = std::map(complex_types::type_str, native_types::type_int); - $ptr = std::unsafe_ptr($map); - try { - std_map_unsafe_ptr_type_mismatch($ptr); + std_map_unsafe_ptr_type_mismatch($map); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } diff --git a/tests/aot/std-map/007.phpt b/tests/aot/std-map/007.phpt new file mode 100644 index 00000000..de7c047f --- /dev/null +++ b/tests/aot/std-map/007.phpt @@ -0,0 +1,21 @@ +--TEST-- +std map: assign to PHP array +--FILE-- + +--EXPECT-- +bool(true) +int(2) +int(100) +int(200) diff --git a/tests/aot/std-unordered-map/005.phpt b/tests/aot/std-unordered-map/005.phpt index 244b312e..1a823521 100644 --- a/tests/aot/std-unordered-map/005.phpt +++ b/tests/aot/std-unordered-map/005.phpt @@ -15,8 +15,7 @@ function main() { $map[2] = 7; $map[3] = 3; - $ptr = std::unsafe_ptr($map); - std_unordered_map_unsafe_ptr_update($ptr); + std_unordered_map_unsafe_ptr_update($map); var_dump($map[3]); } ?> diff --git a/tests/aot/std-unordered-map/006.phpt b/tests/aot/std-unordered-map/006.phpt index b85ae61e..1d818cc7 100644 --- a/tests/aot/std-unordered-map/006.phpt +++ b/tests/aot/std-unordered-map/006.phpt @@ -9,10 +9,8 @@ function std_unordered_map_unsafe_ptr_type_mismatch(UnsafePtr $unsafePtr): void function main() { $map = std::unordered_map(native_types::type_int, native_types::type_int); - $ptr = std::unsafe_ptr($map); - try { - std_unordered_map_unsafe_ptr_type_mismatch($ptr); + std_unordered_map_unsafe_ptr_type_mismatch($map); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } diff --git a/tests/aot/std-unordered-map/007.phpt b/tests/aot/std-unordered-map/007.phpt new file mode 100644 index 00000000..fb2c6f7b --- /dev/null +++ b/tests/aot/std-unordered-map/007.phpt @@ -0,0 +1,21 @@ +--TEST-- +std unordered map: assign to PHP array +--FILE-- + +--EXPECT-- +bool(true) +int(2) +int(100) +int(200) diff --git a/tests/aot/std-vector/006.phpt b/tests/aot/std-vector/006.phpt index aa7a9c0c..fadbe97c 100644 --- a/tests/aot/std-vector/006.phpt +++ b/tests/aot/std-vector/006.phpt @@ -15,8 +15,7 @@ function main() { $vector[1] = 7; $vector[2] = 3; - $ptr = std::unsafe_ptr($vector); - std_vector_unsafe_ptr_update($ptr); + std_vector_unsafe_ptr_update($vector); var_dump($vector[2]); } ?> diff --git a/tests/aot/std-vector/007.phpt b/tests/aot/std-vector/007.phpt index 5855c0da..755861ef 100644 --- a/tests/aot/std-vector/007.phpt +++ b/tests/aot/std-vector/007.phpt @@ -9,10 +9,8 @@ function std_vector_unsafe_ptr_type_mismatch(UnsafePtr $unsafePtr): void function main() { $vector = std::vector(native_types::type_int, 3); - $ptr = std::unsafe_ptr($vector); - try { - std_vector_unsafe_ptr_type_mismatch($ptr); + std_vector_unsafe_ptr_type_mismatch($vector); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } diff --git a/tests/aot/std-vector/008.phpt b/tests/aot/std-vector/008.phpt index 95d2e843..f7ebcece 100644 --- a/tests/aot/std-vector/008.phpt +++ b/tests/aot/std-vector/008.phpt @@ -17,10 +17,8 @@ function std_vector_unsafe_ptr_class_type_mismatch(UnsafePtr $unsafePtr): void function main() { $vector = std::vector(StdVectorUnsafeCastBase::class); - $ptr = std::unsafe_ptr($vector); - try { - std_vector_unsafe_ptr_class_type_mismatch($ptr); + std_vector_unsafe_ptr_class_type_mismatch($vector); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } diff --git a/tests/aot/std-vector/009.phpt b/tests/aot/std-vector/009.phpt index 8c3c3feb..ccba9a41 100644 --- a/tests/aot/std-vector/009.phpt +++ b/tests/aot/std-vector/009.phpt @@ -14,8 +14,7 @@ namespace StdVectorUnsafeCastNs { public static function run(): void { $vector = std::vector(self::class); - $ptr = std::unsafe_ptr($vector); - self::update($ptr); + self::update($vector); } } } diff --git a/tests/aot/std-vector/010.phpt b/tests/aot/std-vector/010.phpt new file mode 100644 index 00000000..7b628334 --- /dev/null +++ b/tests/aot/std-vector/010.phpt @@ -0,0 +1,21 @@ +--TEST-- +std vector: assign to PHP array +--FILE-- + +--EXPECT-- +bool(true) +int(2) +int(10) +int(20)