refactor(aot): 简化 unsafe_ptr 使用并改进错误处理

- 移除手动创建 unsafe_ptr 的调用,直接传递容器变量
- 添加对 UnsafePtr 参数重新赋值的错误检查
- 验证 UnsafePtr 参数必须是标准容器变量
- 更新 unsafe_cast 错误消息以反映参数要求
- 移除不再需要的 unsafe_ptr 解析方法
- 添加新的测试用例验证各种错误场景
pull/1/head
韩天峰 4 months ago
parent 61dba85b12
commit 4411216354
  1. 7
      phpunit/code/std-unsafe-cast-rejects-unsafe-ptr-local-copy.php
  2. 11
      phpunit/code/std-unsafe-ptr-argument-requires-container.php
  3. 6
      phpunit/code/std-unsafe-ptr-parameter-cannot-be-reassigned.php
  4. 26
      phpunit/src/AssignTest.php
  5. 74
      src/Php/CompilerBase.php
  6. 5
      src/Php/Context/FunctionContext.php
  7. 26
      src/Php/Parser/StdContainerParser.php
  8. 3
      tests/aot/std-array/007.phpt
  9. 4
      tests/aot/std-array/008.phpt
  10. 24
      tests/aot/std-array/009.phpt
  11. 3
      tests/aot/std-map/005.phpt
  12. 4
      tests/aot/std-map/006.phpt
  13. 21
      tests/aot/std-map/007.phpt
  14. 3
      tests/aot/std-unordered-map/005.phpt
  15. 4
      tests/aot/std-unordered-map/006.phpt
  16. 21
      tests/aot/std-unordered-map/007.phpt
  17. 3
      tests/aot/std-vector/006.phpt
  18. 4
      tests/aot/std-vector/007.phpt
  19. 4
      tests/aot/std-vector/008.phpt
  20. 3
      tests/aot/std-vector/009.phpt
  21. 21
      tests/aot/std-vector/010.phpt

@ -0,0 +1,7 @@
<?php
function std_unsafe_cast_rejects_unsafe_ptr_local_copy(UnsafePtr $unsafePtr): void
{
$ptr = $unsafePtr;
$array = std::unsafe_cast(std::array(native_types::type_int, 3), $ptr);
}

@ -0,0 +1,11 @@
<?php
function std_unsafe_ptr_accepts_container_only(UnsafePtr $unsafePtr): void
{
}
function test_std_unsafe_ptr_argument_requires_container(): void
{
$value = 1;
std_unsafe_ptr_accepts_container_only($value);
}

@ -0,0 +1,6 @@
<?php
function std_unsafe_ptr_parameter_cannot_be_reassigned(UnsafePtr $unsafePtr): void
{
$unsafePtr = null;
}

@ -23,8 +23,32 @@ class AssignTest extends \BaseTest
public function testStdUnsafeCastRequiresUnsafePtr()
{
$this->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'
);
}
}

@ -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);

@ -24,10 +24,6 @@ class FunctionContext
* @var array<string, array>
*/
public array $stdContainers = [];
/**
* @var array<string, bool>
*/
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;

@ -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') {

@ -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]);
}
?>

@ -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";
}

@ -0,0 +1,24 @@
--TEST--
std array: assign to PHP array
--FILE--
<?php
function main() {
$array = std::array(native_types::type_int, 3);
$array[0] = 10;
$array[1] = 20;
$array[2] = 30;
$copy = $array;
var_dump(is_array($copy));
var_dump(count($copy));
var_dump($copy[0]);
var_dump($copy[1]);
var_dump($copy[2]);
}
?>
--EXPECT--
bool(true)
int(3)
int(10)
int(20)
int(30)

@ -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"]);
}
?>

@ -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";
}

@ -0,0 +1,21 @@
--TEST--
std map: assign to PHP array
--FILE--
<?php
function main() {
$map = std::map(native_types::type_int, native_types::type_int);
$map[10] = 100;
$map[20] = 200;
$copy = $map;
var_dump(is_array($copy));
var_dump(count($copy));
var_dump($copy[10]);
var_dump($copy[20]);
}
?>
--EXPECT--
bool(true)
int(2)
int(100)
int(200)

@ -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]);
}
?>

@ -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";
}

@ -0,0 +1,21 @@
--TEST--
std unordered map: assign to PHP array
--FILE--
<?php
function main() {
$map = std::unordered_map(native_types::type_int, native_types::type_int);
$map[10] = 100;
$map[20] = 200;
$copy = $map;
var_dump(is_array($copy));
var_dump(count($copy));
var_dump($copy[10]);
var_dump($copy[20]);
}
?>
--EXPECT--
bool(true)
int(2)
int(100)
int(200)

@ -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]);
}
?>

@ -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";
}

@ -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";
}

@ -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);
}
}
}

@ -0,0 +1,21 @@
--TEST--
std vector: assign to PHP array
--FILE--
<?php
function main() {
$vector = std::vector(native_types::type_int);
$vector[] = 10;
$vector[] = 20;
$copy = $vector;
var_dump(is_array($copy));
var_dump(count($copy));
var_dump($copy[0]);
var_dump($copy[1]);
}
?>
--EXPECT--
bool(true)
int(2)
int(10)
int(20)
Loading…
Cancel
Save