From 7f4dee40c06f3bccc8e65b0f449295fd567ddf78 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 12 May 2026 14:32:01 +0800 Subject: [PATCH] =?UTF-8?q?fix(aot):=20=E8=A7=A3=E5=86=B3std=E5=AE=B9?= =?UTF-8?q?=E5=99=A8unsafe=5Fcast=E7=B1=BB=E5=9E=8B=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加了stdTypeMap用于存储标准类型映射关系 - 实现了registerStdType方法用于注册标准类型ID - 为std数组、向量和映射容器添加了类型ID分配逻辑 - 在unsafe_cast操作中增加了类型ID验证机制 - 添加了类型不匹配时的异常抛出功能 - 修复了self::class和parent::class的命名空间解析问题 - 更新了unsafe_ptr创建时的类型ID传递逻辑 - 添加了多个测试用例验证类型不匹配场景 --- .../std-container-static-class-mismatch.php | 15 ++++++ .../std-unsafe-cast-requires-unsafe-ptr.php | 6 +++ src/Php/CompilerBase.php | 38 ++++++++++++-- src/Php/Parser/StdContainerParser.php | 52 +++++++++++++++---- tests/aot/std-vector/007.phpt | 22 ++++++++ tests/aot/std-vector/008.phpt | 30 +++++++++++ tests/aot/std-vector/009.phpt | 30 +++++++++++ 7 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 phpunit/code/std-container-static-class-mismatch.php create mode 100644 phpunit/code/std-unsafe-cast-requires-unsafe-ptr.php create mode 100644 tests/aot/std-vector/007.phpt create mode 100644 tests/aot/std-vector/008.phpt create mode 100644 tests/aot/std-vector/009.phpt diff --git a/phpunit/code/std-container-static-class-mismatch.php b/phpunit/code/std-container-static-class-mismatch.php new file mode 100644 index 00000000..f3af208e --- /dev/null +++ b/phpunit/code/std-container-static-class-mismatch.php @@ -0,0 +1,15 @@ + */ protected array $classMap = []; + /** + * @var array + */ + 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_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_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_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 . '{};'; } diff --git a/src/Php/Parser/StdContainerParser.php b/src/Php/Parser/StdContainerParser.php index 69486f36..04d126ac 100644 --- a/src/Php/Parser/StdContainerParser.php +++ b/src/Php/Parser/StdContainerParser.php @@ -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; } } diff --git a/tests/aot/std-vector/007.phpt b/tests/aot/std-vector/007.phpt new file mode 100644 index 00000000..1aa8d0d1 --- /dev/null +++ b/tests/aot/std-vector/007.phpt @@ -0,0 +1,22 @@ +--TEST-- +std vector: unsafe_cast type mismatch +--FILE-- +getMessage(), "\n"; + } +} +?> +--EXPECT-- +std::unsafe_cast(): UnsafePtr type mismatch diff --git a/tests/aot/std-vector/008.phpt b/tests/aot/std-vector/008.phpt new file mode 100644 index 00000000..a2cc2fb5 --- /dev/null +++ b/tests/aot/std-vector/008.phpt @@ -0,0 +1,30 @@ +--TEST-- +std vector: unsafe_cast class value type mismatch +--FILE-- +getMessage(), "\n"; + } +} +?> +--EXPECT-- +std::unsafe_cast(): UnsafePtr type mismatch diff --git a/tests/aot/std-vector/009.phpt b/tests/aot/std-vector/009.phpt new file mode 100644 index 00000000..8c3c3feb --- /dev/null +++ b/tests/aot/std-vector/009.phpt @@ -0,0 +1,30 @@ +--TEST-- +std vector: namespaced self class value unsafe_cast +--FILE-- + +--EXPECT-- +ok