fix(aot): 解决std容器unsafe_cast类型检查问题

- 添加了stdTypeMap用于存储标准类型映射关系
- 实现了registerStdType方法用于注册标准类型ID
- 为std数组、向量和映射容器添加了类型ID分配逻辑
- 在unsafe_cast操作中增加了类型ID验证机制
- 添加了类型不匹配时的异常抛出功能
- 修复了self::class和parent::class的命名空间解析问题
- 更新了unsafe_ptr创建时的类型ID传递逻辑
- 添加了多个测试用例验证类型不匹配场景
pull/1/head
韩天峰 4 months ago
parent 79bc9d4e69
commit 7f4dee40c0
  1. 15
      phpunit/code/std-container-static-class-mismatch.php
  2. 6
      phpunit/code/std-unsafe-cast-requires-unsafe-ptr.php
  3. 38
      src/Php/CompilerBase.php
  4. 52
      src/Php/Parser/StdContainerParser.php
  5. 22
      tests/aot/std-vector/007.phpt
  6. 30
      tests/aot/std-vector/008.phpt
  7. 30
      tests/aot/std-vector/009.phpt

@ -0,0 +1,15 @@
<?php
class StdContainerStaticBase
{
}
class StdContainerStaticChild extends StdContainerStaticBase
{
}
function test_std_container_static_class_mismatch(): void
{
$vector = std::vector(StdContainerStaticBase::class);
$vector[] = new StdContainerStaticChild();
}

@ -0,0 +1,6 @@
<?php
function std_unsafe_cast_requires_unsafe_ptr(mixed $ptr): void
{
$array = std::unsafe_cast(std::array(native_types::type_int, 3), $ptr);
}

@ -118,6 +118,10 @@ class CompilerBase extends \PhpAot\Core\Translator
* @var array<string, int>
*/
protected array $classMap = [];
/**
* @var array<string, int>
*/
protected array $stdTypeMap = [];
protected int $funcIndex = 0;
/**
@ -1897,6 +1901,16 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->context->unsafePtrs[$name] = true;
}
protected function registerStdType(string $key): int
{
if (isset($this->stdTypeMap[$key])) {
return $this->stdTypeMap[$key];
}
$typeId = count($this->stdTypeMap) + 1;
$this->stdTypeMap[$key] = $typeId;
return $typeId;
}
protected function isUnsafePtr(string $name): bool
{
return isset($this->context->unsafePtrs[$name]);
@ -5468,7 +5482,13 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($type === self::TYPE_STD_ARRAY) {
$info = $this->context->stdArrays[$name];
if (isset($info['unsafePtr'])) {
$code .= 'auto &' . $name . ' = *reinterpret_cast<' . $info['decl'] . '*>(Z_PTR_P(' . $info['unsafePtr'] . '.ptr()));';
$unsafePtrBox = $this->genTmpVarName();
$code .= 'auto *' . $unsafePtrBox . ' = ' . $info['unsafePtr'] . '.toBox<php::UnsafePtr>();' . PHP_EOL;
$code .= $this->getIndent() . 'if (UNEXPECTED(' . $unsafePtrBox . '->type_id != ' . $info['typeId'] . ')) {' . PHP_EOL;
$code .= $this->getIndent() . ' php::throwException("RuntimeException", "std::unsafe_cast(): UnsafePtr type mismatch");' . PHP_EOL;
$code .= $this->getIndent() . '}' . PHP_EOL;
$code .= $this->getIndent();
$code .= 'auto &' . $name . ' = *reinterpret_cast<' . $info['decl'] . '*>(' . $unsafePtrBox . '->ptr);';
} elseif ($info['bytes'] > self::MAX_BYTES_IN_STACK) {
$code .= "auto {$name}_unique_ptr = std::make_unique<{$info['decl']}>();\n";
$code .= $this->getIndent() . ' auto &' . $name . ' = *' . $name . '_unique_ptr;';
@ -5478,7 +5498,13 @@ class CompilerBase extends \PhpAot\Core\Translator
} elseif ($type === self::TYPE_STD_VECTOR) {
$info = $this->context->stdContainers[$name];
if (isset($info['unsafePtr'])) {
$code .= 'auto &' . $name . ' = *reinterpret_cast<' . $info['decl'] . '*>(Z_PTR_P(' . $info['unsafePtr'] . '.ptr()));';
$unsafePtrBox = $this->genTmpVarName();
$code .= 'auto *' . $unsafePtrBox . ' = ' . $info['unsafePtr'] . '.toBox<php::UnsafePtr>();' . PHP_EOL;
$code .= $this->getIndent() . 'if (UNEXPECTED(' . $unsafePtrBox . '->type_id != ' . $info['typeId'] . ')) {' . PHP_EOL;
$code .= $this->getIndent() . ' php::throwException("RuntimeException", "std::unsafe_cast(): UnsafePtr type mismatch");' . PHP_EOL;
$code .= $this->getIndent() . '}' . PHP_EOL;
$code .= $this->getIndent();
$code .= 'auto &' . $name . ' = *reinterpret_cast<' . $info['decl'] . '*>(' . $unsafePtrBox . '->ptr);';
} else {
$code .= $info['decl'] . ' ' . $name;
if ($info['size'] !== null) {
@ -5491,7 +5517,13 @@ class CompilerBase extends \PhpAot\Core\Translator
} elseif ($type === self::TYPE_STD_MAP || $type === self::TYPE_STD_UNORDERED_MAP) {
$info = $this->context->stdContainers[$name];
if (isset($info['unsafePtr'])) {
$code .= 'auto &' . $name . ' = *reinterpret_cast<' . $info['decl'] . '*>(Z_PTR_P(' . $info['unsafePtr'] . '.ptr()));';
$unsafePtrBox = $this->genTmpVarName();
$code .= 'auto *' . $unsafePtrBox . ' = ' . $info['unsafePtr'] . '.toBox<php::UnsafePtr>();' . PHP_EOL;
$code .= $this->getIndent() . 'if (UNEXPECTED(' . $unsafePtrBox . '->type_id != ' . $info['typeId'] . ')) {' . PHP_EOL;
$code .= $this->getIndent() . ' php::throwException("RuntimeException", "std::unsafe_cast(): UnsafePtr type mismatch");' . PHP_EOL;
$code .= $this->getIndent() . '}' . PHP_EOL;
$code .= $this->getIndent();
$code .= 'auto &' . $name . ' = *reinterpret_cast<' . $info['decl'] . '*>(' . $unsafePtrBox . '->ptr);';
} else {
$code .= $info['decl'] . ' ' . $name . '{};';
}

@ -44,6 +44,34 @@ trait StdContainerParser
return $this->hasLocalVar($var) and $this->getVarType($var) === self::TYPE_STD_UNORDERED_MAP;
}
protected function getStdTypeKey(array $info): string
{
$parts = [
'kind=' . $info['kind'],
'decl=' . $info['decl'],
'type=' . $info['type'],
'class=' . ($info['class'] ?? ''),
];
if (isset($info['keyType'])) {
$parts[] = 'keyType=' . $info['keyType'];
}
return implode(';', $parts);
}
protected function addStdTypeId(array $info): array
{
$info['typeId'] = $this->registerStdType($this->getStdTypeKey($info));
return $info;
}
protected function getStdContainerVarInfo(string $var): array
{
if ($this->isStdArray($var)) {
return $this->context->stdArrays[$var];
}
return $this->context->stdContainers[$var];
}
protected function isStdArrayExpr(Expr\ArrayDimFetch $expr): bool
{
$info = $this->getStdArrayInfo($expr);
@ -309,12 +337,12 @@ trait StdContainerParser
if (!$this->classDef) {
$this->fatalError($expr, "{$owner} class value cannot use self::class outside class scope");
}
$class = $this->class;
$class = $this->getNamespacedClassName($this->class);
} elseif ($class === 'parent') {
if (!$this->classDef || !$this->classDef->extends) {
$this->fatalError($expr, "{$owner} class value cannot use parent::class because current class does not extend any class");
}
$class = $this->classDef->extends;
$class = $this->getNamespacedClassName('\\' . $this->classDef->extends);
} else {
$class = $this->getNamespacedClassName($class);
}
@ -361,7 +389,8 @@ trait StdContainerParser
$tmpVar = $this->addTmpVar(self::TYPE_VAR);
$this->addUnsafePtr($tmpVar);
$expr->setAttribute('unsafePtr', true);
$this->context->beforeStmtLines[] = 'Z_PTR_P(' . $tmpVar . '.ptr()) = &' . $container . ';';
$info = $this->getStdContainerVarInfo($container);
$this->context->beforeStmtLines[] = $tmpVar . ' = php_create_unsafe_ptr(&' . $container . ', ' . $info['typeId'] . ');';
return $tmpVar;
}
@ -476,13 +505,14 @@ trait StdContainerParser
for ($i = count($nesting) - 1; $i >= 0; $i--) {
$decl .= ', ' . $nesting[$i] . '>';
}
$this->context->stdArrays[$var] = [
$this->context->stdArrays[$var] = $this->addStdTypeId([
'kind' => 'array',
'decl' => $decl,
'type' => $type,
'class' => $typeInfo['class'],
'sizes' => array_reverse($nesting),
'bytes' => $totalBytes,
];
]);
return '// ' . $decl;
}
@ -501,13 +531,13 @@ trait StdContainerParser
$size = $expr->args[1]->value->value;
}
$decl = self::TYPE_STD_VECTOR . '<' . $type . '>';
$this->context->stdContainers[$var] = [
$this->context->stdContainers[$var] = $this->addStdTypeId([
'kind' => 'vector',
'decl' => $decl,
'type' => $type,
'class' => $typeInfo['class'],
'size' => $size,
];
]);
return '// ' . $decl;
}
@ -520,13 +550,13 @@ trait StdContainerParser
$valueTypeInfo = $this->parseStdValueTypeInfo($expr->args[1]->value, 'std::map');
$valueType = $valueTypeInfo['type'];
$decl = $this->getStdMapDecl(self::TYPE_STD_MAP, $keyType, $valueType);
$this->context->stdContainers[$var] = [
$this->context->stdContainers[$var] = $this->addStdTypeId([
'kind' => 'map',
'decl' => $decl,
'type' => $valueType,
'class' => $valueTypeInfo['class'],
'keyType' => $keyType,
];
]);
return '// ' . $decl;
}
@ -539,13 +569,13 @@ trait StdContainerParser
$valueTypeInfo = $this->parseStdValueTypeInfo($expr->args[1]->value, 'std::unordered_map');
$valueType = $valueTypeInfo['type'];
$decl = $this->getStdMapDecl(self::TYPE_STD_UNORDERED_MAP, $keyType, $valueType);
$this->context->stdContainers[$var] = [
$this->context->stdContainers[$var] = $this->addStdTypeId([
'kind' => 'unordered_map',
'decl' => $decl,
'type' => $valueType,
'class' => $valueTypeInfo['class'],
'keyType' => $keyType,
];
]);
return '// ' . $decl;
}
}

@ -0,0 +1,22 @@
--TEST--
std vector: unsafe_cast type mismatch
--FILE--
<?php
function std_vector_unsafe_ptr_type_mismatch(UnsafePtr $unsafePtr): void
{
$vector = std::unsafe_cast(std::vector(native_types::type_float), $unsafePtr);
}
function main() {
$vector = std::vector(native_types::type_int, 3);
$ptr = std::unsafe_ptr($vector);
try {
std_vector_unsafe_ptr_type_mismatch($ptr);
} catch (RuntimeException $e) {
echo $e->getMessage(), "\n";
}
}
?>
--EXPECT--
std::unsafe_cast(): UnsafePtr type mismatch

@ -0,0 +1,30 @@
--TEST--
std vector: unsafe_cast class value type mismatch
--FILE--
<?php
class StdVectorUnsafeCastBase
{
}
class StdVectorUnsafeCastOther
{
}
function std_vector_unsafe_ptr_class_type_mismatch(UnsafePtr $unsafePtr): void
{
$vector = std::unsafe_cast(std::vector(StdVectorUnsafeCastOther::class), $unsafePtr);
}
function main() {
$vector = std::vector(StdVectorUnsafeCastBase::class);
$ptr = std::unsafe_ptr($vector);
try {
std_vector_unsafe_ptr_class_type_mismatch($ptr);
} catch (RuntimeException $e) {
echo $e->getMessage(), "\n";
}
}
?>
--EXPECT--
std::unsafe_cast(): UnsafePtr type mismatch

@ -0,0 +1,30 @@
--TEST--
std vector: namespaced self class value unsafe_cast
--FILE--
<?php
namespace StdVectorUnsafeCastNs {
class Holder
{
public static function update(UnsafePtr $unsafePtr): void
{
$vector = std::unsafe_cast(std::vector(self::class), $unsafePtr);
echo "ok\n";
}
public static function run(): void
{
$vector = std::vector(self::class);
$ptr = std::unsafe_ptr($vector);
self::update($ptr);
}
}
}
namespace {
function main() {
StdVectorUnsafeCastNs\Holder::run();
}
}
?>
--EXPECT--
ok
Loading…
Cancel
Save