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