refactor(aot): 移除UnsafePtr类型支持并优化std容器实现

- 移除UnsafePtr类型声明及相关参数标记功能
- 修改std容器变量访问方式,统一使用_ref后缀引用
- 更新unsafe_cast函数参数验证逻辑,移除UnsafePtr参数检查
- 重构std容器在C++代码中的声明和初始化方式
- 删除相关测试用例及单元测试中的UnsafePtr验证逻辑
- 统一std容器赋值、遍历、删除操作的引用访问模式
pull/1/head
韩天峰 3 months ago
parent 000b6a221d
commit 11acfdf10f
  1. 7
      phpunit/code/std-unsafe-cast-rejects-unsafe-ptr-local-copy.php
  2. 6
      phpunit/code/std-unsafe-cast-requires-unsafe-ptr.php
  3. 11
      phpunit/code/std-unsafe-ptr-argument-requires-container.php
  4. 6
      phpunit/code/std-unsafe-ptr-parameter-cannot-be-reassigned.php
  5. 10
      phpunit/src/ArgInfoTest.php
  6. 32
      phpunit/src/AssignTest.php
  7. 1
      phpunit/src/CompilerBaseApiTest.php
  8. 3
      phpunit/src/PreprocessorTest.php
  9. 1
      src/Php/ArgInfo.php
  10. 110
      src/Php/CompilerBase.php
  11. 38
      src/Php/Parser/StdContainerParser.php
  12. 6
      tests/aot/std-array/007.phpt
  13. 6
      tests/aot/std-array/008.phpt
  14. 12
      tests/aot/std-map/005.phpt
  15. 6
      tests/aot/std-map/006.phpt
  16. 6
      tests/aot/std-unordered-map/005.phpt
  17. 6
      tests/aot/std-unordered-map/006.phpt
  18. 6
      tests/aot/std-vector/006.phpt
  19. 6
      tests/aot/std-vector/007.phpt
  20. 6
      tests/aot/std-vector/008.phpt
  21. 4
      tests/aot/std-vector/009.phpt

@ -1,7 +0,0 @@
<?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);
}

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

@ -1,11 +0,0 @@
<?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);
}

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

@ -22,7 +22,6 @@ class ArgInfoTest extends TestCase
$this->assertFalse($arg->variadic); $this->assertFalse($arg->variadic);
$this->assertFalse($arg->nullable); $this->assertFalse($arg->nullable);
$this->assertFalse($arg->property); $this->assertFalse($arg->property);
$this->assertFalse($arg->unsafePtr);
} }
public function testNameAndType(): void public function testNameAndType(): void
@ -87,15 +86,6 @@ class ArgInfoTest extends TestCase
$this->assertTrue($arg->property); $this->assertTrue($arg->property);
} }
public function testUnsafePtr(): void
{
$arg = new ArgInfo();
$this->assertFalse($arg->unsafePtr);
$arg->unsafePtr = true;
$this->assertTrue($arg->unsafePtr);
}
public function testAllFlagsCombined(): void public function testAllFlagsCombined(): void
{ {
$arg = new ArgInfo(); $arg = new ArgInfo();

@ -22,38 +22,6 @@ class AssignTest extends \BaseTest
); );
} }
public function testStdUnsafeCastRequiresUnsafePtr()
{
$this->exec(
'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'
);
}
// === Object value assigned to non-object variable (right side is New_ expr) === // === Object value assigned to non-object variable (right side is New_ expr) ===
public function testObjectToInt() public function testObjectToInt()

@ -82,7 +82,6 @@ class CompilerBaseApiTest extends TestCase
$this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('null')); $this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('null'));
$this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('callable')); $this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('callable'));
$this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('iterable')); $this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('iterable'));
$this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('UnsafePtr'));
} }
public function testGetTypeFromZendTypeUnknown(): void public function testGetTypeFromZendTypeUnknown(): void

@ -88,12 +88,11 @@ class PreprocessorTest extends TestCase
$this->assertEquals('php::Object obj', $result); $this->assertEquals('php::Object obj', $result);
} }
public function testGenArgumentDeclarationUnsafePtr(): void public function testGenArgumentDeclarationVar(): void
{ {
$arg = new ArgInfo(); $arg = new ArgInfo();
$arg->name = 'container'; $arg->name = 'container';
$arg->type = 'php::Var'; $arg->type = 'php::Var';
$arg->unsafePtr = true;
$result = $this->invokeMethod('genArgumentDeclaration', $arg); $result = $this->invokeMethod('genArgumentDeclaration', $arg);
$this->assertEquals('php::Var container', $result); $this->assertEquals('php::Var container', $result);
} }

@ -21,5 +21,4 @@ class ArgInfo
public bool $variadic = false; public bool $variadic = false;
public bool $nullable = false; public bool $nullable = false;
public bool $property = false; public bool $property = false;
public bool $unsafePtr = false;
} }

@ -168,8 +168,6 @@ class CompilerBase extends \PhpAot\Core\Translator
'callable' => self::TYPE_VAR, 'callable' => self::TYPE_VAR,
// iterable 类型,可以是数组或者对象 // iterable 类型,可以是数组或者对象
'iterable' => self::TYPE_VAR, 'iterable' => self::TYPE_VAR,
// 编译器符号类型,C++ 层仍使用 php::Var 承载 null zval 中的 value.ptr
'UnsafePtr' => self::TYPE_VAR,
'stream' => self::TYPE_STREAM, 'stream' => self::TYPE_STREAM,
]; ];
protected array $globalHeaders = [ protected array $globalHeaders = [
@ -692,7 +690,11 @@ class CompilerBase extends \PhpAot\Core\Translator
case 'Scalar_Float': case 'Scalar_Float':
case 'Scalar_String': case 'Scalar_String':
case 'Expr_Variable': case 'Expr_Variable':
return $this->parseIdentifier($expr); $varName = $this->parseIdentifier($expr);
if ($this->isStdContainer($varName)) {
return $varName . '_ref';
}
return $varName;
case 'Scalar_MagicConst_File': case 'Scalar_MagicConst_File':
case 'Scalar_MagicConst_Dir': case 'Scalar_MagicConst_Dir':
case 'Scalar_MagicConst_Line': case 'Scalar_MagicConst_Line':
@ -798,8 +800,8 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($this->hasLocalVar($name)) { if ($this->hasLocalVar($name)) {
return $this->context->localVars[$name]; return $this->context->localVars[$name];
} }
if ($this->hasLocalVar($name)) { if ($this->hasScopeGlobalVar($name)) {
return $this->globalVars[$name]; return $this->context->globalVars[$name];
} }
return self::TYPE_VAR; return self::TYPE_VAR;
@ -1634,10 +1636,6 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($var === 'this_') { if ($var === 'this_') {
$this->fatalError($left, 'Cannot re-assign $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); $type = $this->detectTypeOfExpr($right);
if ($this->isVarExpr($left)) { if ($this->isVarExpr($left)) {
@ -1761,7 +1759,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->fatalError($right, 'Cannot copy std container with different type'); $this->fatalError($right, 'Cannot copy std container with different type');
} }
return $leftVar . ' = ' . $this->parseStdContainerCopyExpr($right); return $leftVar . '_ref = ' . $this->parseStdContainerCopyExpr($right);
} }
protected function parseAssignRightExpr(Expr $right): string protected function parseAssignRightExpr(Expr $right): string
@ -2665,10 +2663,6 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($param->byRef) { if ($param->byRef) {
return self::TYPE_REF; return self::TYPE_REF;
} }
if ($this->isUnsafePtrTypeDecl($param->type)) {
$argInfo->unsafePtr = true;
return self::TYPE_VAR;
}
$class = ''; $class = '';
$type = $this->parseTypeDecl($param->type, self::DECL_TYPE_OF_PARAM, $class); $type = $this->parseTypeDecl($param->type, self::DECL_TYPE_OF_PARAM, $class);
// stream 是伪类型,实际运行时依然作为 var 处理 // stream 是伪类型,实际运行时依然作为 var 处理
@ -3496,10 +3490,6 @@ class CompilerBase extends \PhpAot\Core\Translator
foreach ($args as $i => $arg) { foreach ($args as $i => $arg) {
$argInfo = $this->getArgInfo($arg, $nativeFunc, $i); $argInfo = $this->getArgInfo($arg, $nativeFunc, $i);
if ($argInfo->unsafePtr) {
$argList[] = $this->getUnsafePtrConvertedArg($arg, $argInfo);
continue;
}
if ($argInfo->variadic) { if ($argInfo->variadic) {
$argsSlice = array_slice($args, $i); $argsSlice = array_slice($args, $i);
if (count($argsSlice) === 1 and $argsSlice[0]->unpack) { if (count($argsSlice) === 1 and $argsSlice[0]->unpack) {
@ -3712,7 +3702,7 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
$expr = $this->parseIdentifier($arg->value); $expr = $this->parseIdentifier($arg->value);
if ($this->isVarExpr($arg->value) and $this->isStdContainer($arg->value->name)) { if ($this->isVarExpr($arg->value) and $this->isStdContainer($arg->value->name)) {
return $this->convertArrayExpr($expr); return $this->convertArrayExpr($expr . '_ref');
} }
return $expr; return $expr;
} }
@ -4583,6 +4573,13 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->checkVarAssignExpr($arg, $argInfo->type, $type); $this->checkVarAssignExpr($arg, $argInfo->type, $type);
if ($argInfo->type === self::TYPE_VAR && $this->isVarExpr($arg->value)) {
$varName = $this->parseIdentifier($arg->value);
if ($this->isStdContainer($varName)) {
return $varName;
}
}
if ($argInfo->type === self::TYPE_OBJECT) { if ($argInfo->type === self::TYPE_OBJECT) {
if ($this->isVarExpr($arg->value)) { if ($this->isVarExpr($arg->value)) {
$object = $this->parseVariable($arg->value); $object = $this->parseVariable($arg->value);
@ -4599,24 +4596,6 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->convertExprType($expr, $argInfo->type, $type); 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 protected function convertExprType(string $expr, $leftType, $rightType): string
{ {
if ($leftType === self::TYPE_FLOAT or $rightType === self::TYPE_FLOAT) { if ($leftType === self::TYPE_FLOAT or $rightType === self::TYPE_FLOAT) {
@ -4655,7 +4634,11 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->fatalError($var, 'Cannot delete element in std container in foreach loop'); $this->fatalError($var, 'Cannot delete element in std container in foreach loop');
} }
$dim = $this->parseIdentifier($var->dim); $dim = $this->parseIdentifier($var->dim);
$lines[] = $array . '.offsetUnset(' . $dim . ');'; if ($this->isStdContainer($array)) {
$lines[] = $array . '_ref.offsetUnset(' . $dim . ');';
} else {
$lines[] = $array . '.offsetUnset(' . $dim . ');';
}
} elseif ($this->isPropertyFetch($var)) { } elseif ($this->isPropertyFetch($var)) {
$object = $this->parseIdentifier($var->var); $object = $this->parseIdentifier($var->var);
$lines[] = $object . '.unsetProperty(' . $this->identifierToStr($var->name, literal: true) . ');'; $lines[] = $object . '.unsetProperty(' . $this->identifierToStr($var->name, literal: true) . ');';
@ -5211,7 +5194,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseCastArray(Expr\Cast\Array_ $expr): string protected function parseCastArray(Expr\Cast\Array_ $expr): string
{ {
return $this->convertArrayExpr($this->parseIdentifier($expr->expr)); return $this->convertArrayExpr($this->parseExpr($expr->expr));
} }
protected function hasGlobalVar(string $name): bool protected function hasGlobalVar(string $name): bool
@ -5306,9 +5289,6 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->context->inAssignExpr = false; $this->context->inAssignExpr = false;
if ($this->isVarExpr($expr->var)) { if ($this->isVarExpr($expr->var)) {
if ($this->isUnsafePtrParameter($left)) {
$this->fatalError($expr->var, "Cannot re-assign UnsafePtr parameter `\${$left}`");
}
if (!$this->hasVar($left)) { if (!$this->hasVar($left)) {
$this->addLocalVar($left, self::TYPE_REF); $this->addLocalVar($left, self::TYPE_REF);
} else { } else {
@ -6210,26 +6190,6 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
} }
protected function isUnsafePtrTypeDecl(?NodeAbstract $type): bool
{
return $type !== null
and !$type instanceof UnionType
and !$type instanceof NullableType
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 protected function parseParentMethodCall(Expr\StaticCall $expr): string
{ {
@ -6277,32 +6237,34 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($type === self::TYPE_STD_ARRAY) { if ($type === self::TYPE_STD_ARRAY) {
$info = $this->context->stdArrays[$name]; $info = $this->context->stdArrays[$name];
if (isset($info['unsafePtr'])) { if (isset($info['unsafePtr'])) {
$code .= 'auto &' . $name . ' = php_unsafe_cast<' . $info['decl'] . '>(' . $info['unsafePtr'] . ', ' . $info['typeId'] . ');'; $code .= 'auto &' . $name . '_ref = php_unsafe_cast<' . $info['decl'] . '>(' . $info['unsafePtr'] . ', ' . $info['typeId'] . ');';
} 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;';
} else { } else {
$code .= $info['decl'] . ' ' . $name . '{};'; $containerType = 'php::StdContainerBox<' . $info['decl'] . '>';
$code .= 'php::Var ' . $name . ' = php::Var(new ' . $containerType . '(' . $info['typeId'] . '));' . PHP_EOL;
$code .= $this->getIndent() . 'auto &' . $name . '_ref = ' . $name . '.toBox<' . $containerType . '>()->container;';
} }
} elseif ($type === self::TYPE_STD_VECTOR) { } elseif ($type === self::TYPE_STD_VECTOR) {
$info = $this->context->stdContainers[$name]; $info = $this->context->stdContainers[$name];
if (isset($info['unsafePtr'])) { if (isset($info['unsafePtr'])) {
$code .= 'auto &' . $name . ' = php_unsafe_cast<' . $info['decl'] . '>(' . $info['unsafePtr'] . ', ' . $info['typeId'] . ');'; $code .= 'auto &' . $name . '_ref = php_unsafe_cast<' . $info['decl'] . '>(' . $info['unsafePtr'] . ', ' . $info['typeId'] . ');';
} else { } else {
$code .= $info['decl'] . ' ' . $name; $containerType = 'php::StdContainerBox<' . $info['decl'] . '>';
if ($info['size'] !== null) { if ($info['size'] !== null) {
$code .= '(' . $info['size'] . ')'; $boxCtor = 'new ' . $containerType . '(' . $info['typeId'] . ', ' . $info['size'] . ')';
} else { } else {
$code .= '{}'; $boxCtor = 'new ' . $containerType . '(' . $info['typeId'] . ')';
} }
$code .= ';'; $code .= 'php::Var ' . $name . ' = php::Var(' . $boxCtor . ');' . PHP_EOL;
$code .= $this->getIndent() . 'auto &' . $name . '_ref = ' . $name . '.toBox<' . $containerType . '>()->container;';
} }
} elseif ($type === self::TYPE_STD_MAP || $type === self::TYPE_STD_UNORDERED_MAP) { } elseif ($type === self::TYPE_STD_MAP || $type === self::TYPE_STD_UNORDERED_MAP) {
$info = $this->context->stdContainers[$name]; $info = $this->context->stdContainers[$name];
if (isset($info['unsafePtr'])) { if (isset($info['unsafePtr'])) {
$code .= 'auto &' . $name . ' = php_unsafe_cast<' . $info['decl'] . '>(' . $info['unsafePtr'] . ', ' . $info['typeId'] . ');'; $code .= 'auto &' . $name . '_ref = php_unsafe_cast<' . $info['decl'] . '>(' . $info['unsafePtr'] . ', ' . $info['typeId'] . ');';
} else { } else {
$code .= $info['decl'] . ' ' . $name . '{};'; $containerType = 'php::StdContainerBox<' . $info['decl'] . '>';
$code .= 'php::Var ' . $name . ' = php::Var(new ' . $containerType . '(' . $info['typeId'] . '));' . PHP_EOL;
$code .= $this->getIndent() . 'auto &' . $name . '_ref = ' . $name . '.toBox<' . $containerType . '>()->container;';
} }
} elseif ($type === self::TYPE_STREAM || $type === self::TYPE_BIGINT || $type === self::TYPE_DECIMAL || $type === self::TYPE_BIGFLOAT) { } elseif ($type === self::TYPE_STREAM || $type === self::TYPE_BIGINT || $type === self::TYPE_DECIMAL || $type === self::TYPE_BIGFLOAT) {
$code .= self::TYPE_VAR . ' ' . $name . ';'; $code .= self::TYPE_VAR . ' ' . $name . ';';

@ -169,7 +169,7 @@ trait StdContainerParser
protected function parseStdContainerCopyExpr(NodeAbstract $expr): string protected function parseStdContainerCopyExpr(NodeAbstract $expr): string
{ {
if ($this->isVarExpr($expr)) { if ($this->isVarExpr($expr)) {
return $this->parseVariable($expr); return $this->parseVariable($expr) . '_ref';
} }
if ($this->isArrayDimFetch($expr) and $this->isStdArrayExpr($expr)) { if ($this->isArrayDimFetch($expr) and $this->isStdArrayExpr($expr)) {
return $this->parseStdArrayDimFetch($expr); return $this->parseStdArrayDimFetch($expr);
@ -196,7 +196,7 @@ trait StdContainerParser
$array = $this->parseIdentifier($expr->args[0]->value); $array = $this->parseIdentifier($expr->args[0]->value);
$info = $this->context->stdArrays[$array]; $info = $this->context->stdArrays[$array];
$value = $this->convertStdValueExpr($info, $expr->args[1]->value); $value = $this->convertStdValueExpr($info, $expr->args[1]->value);
return "{$array}.fill({$value})"; return "{$array}_ref.fill({$value})";
} }
protected function getStdArrayInfo(Expr\ArrayDimFetch $expr): ?array protected function getStdArrayInfo(Expr\ArrayDimFetch $expr): ?array
@ -259,7 +259,7 @@ trait StdContainerParser
$this->fatalError($left, 'std::vector append only supports a vector variable'); $this->fatalError($left, 'std::vector append only supports a vector variable');
} }
$vector = $this->parseVariable($left->var); $vector = $this->parseVariable($left->var);
return $vector . '.push_back(' . $this->convertStdValueExpr($info, $right) . ')'; return $vector . '_ref.push_back(' . $this->convertStdValueExpr($info, $right) . ')';
} }
if ($left->dim === null) { if ($left->dim === null) {
$this->fatalError($left, 'std map expects a key'); $this->fatalError($left, 'std map expects a key');
@ -327,7 +327,8 @@ trait StdContainerParser
$this->fatalError($expr, 'std::array access level exceeds array dimensions'); $this->fatalError($expr, 'std::array access level exceeds array dimensions');
} }
$nesting = [$this->parseVariable($tmp)]; $baseVar = $this->parseVariable($tmp);
$nesting = [$baseVar . '_ref'];
foreach ($dims as $level => $dim) { foreach ($dims as $level => $dim) {
$size = $sizes[$level]; $size = $sizes[$level];
if ($this->isScalarInt($dim)) { if ($this->isScalarInt($dim)) {
@ -338,7 +339,7 @@ trait StdContainerParser
$index = $this->parseExpr($dim); $index = $this->parseExpr($dim);
$nesting[] = '[' . Symbol::safeIndex($this->convertIntExpr($index), $size) . ']'; $nesting[] = '[' . Symbol::safeIndex($this->convertIntExpr($index), $size) . ']';
} }
$expr->setAttribute('stdArrayDimFetch', ['var' => $nesting[0], 'accessLevel' => count($dims), 'totalLevel' => count($sizes)]); $expr->setAttribute('stdArrayDimFetch', ['var' => $baseVar, 'accessLevel' => count($dims), 'totalLevel' => count($sizes)]);
return implode('', $nesting); return implode('', $nesting);
} }
@ -350,13 +351,13 @@ trait StdContainerParser
$this->context->stdContainers[$container]['locking'] = true; $this->context->stdContainers[$container]['locking'] = true;
} }
$iterator = $this->genTmpVarName(); $iterator = $this->genTmpVarName();
$code = "for (auto $iterator = $container.begin(); $iterator != $container.end(); ++$iterator) {" . PHP_EOL; $code = "for (auto $iterator = {$container}_ref.begin(); $iterator != {$container}_ref.end(); ++$iterator) {" . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
if ($node->keyVar) { if ($node->keyVar) {
$keyVar = $this->parseIdentifier($node->keyVar); $keyVar = $this->parseIdentifier($node->keyVar);
$this->checkVar($node, $keyVar, $this->getStdContainerKeyType($container)); $this->checkVar($node, $keyVar, $this->getStdContainerKeyType($container));
if ($this->isStdVector($container) or $this->isStdArray($container)) { if ($this->isStdVector($container) or $this->isStdArray($container)) {
$code .= $this->getIndent() . "$keyVar = $iterator - $container.begin();" . PHP_EOL; $code .= $this->getIndent() . "$keyVar = $iterator - {$container}_ref.begin();" . PHP_EOL;
} else { } else {
$code .= $this->getIndent() . "$keyVar = {$iterator}->first;" . PHP_EOL; $code .= $this->getIndent() . "$keyVar = {$iterator}->first;" . PHP_EOL;
} }
@ -424,7 +425,7 @@ trait StdContainerParser
$container = $this->parseVariable($tmp); $container = $this->parseVariable($tmp);
$index = $this->parseExpr($dim); $index = $this->parseExpr($dim);
$key = $info['kind'] === 'vector' ? $this->convertIntExpr($index) : $this->convertStdContainerKey($info, $index); $key = $info['kind'] === 'vector' ? $this->convertIntExpr($index) : $this->convertStdContainerKey($info, $index);
$access = $container . '.offsetGet(' . $key . ')'; $access = $container . '_ref.offsetGet(' . $key . ')';
$expr->setAttribute('stdContainerDimFetch', ['var' => $container, 'accessLevel' => 1, 'totalLevel' => 1]); $expr->setAttribute('stdContainerDimFetch', ['var' => $container, 'accessLevel' => 1, 'totalLevel' => 1]);
return $access; return $access;
@ -442,7 +443,7 @@ trait StdContainerParser
$container = $this->parseVariable($expr->var); $container = $this->parseVariable($expr->var);
$indexExpr = $this->parseExpr($expr->dim); $indexExpr = $this->parseExpr($expr->dim);
$index = $info['kind'] === 'vector' ? $this->convertIntExpr($indexExpr) : $this->convertStdContainerKey($info, $indexExpr); $index = $info['kind'] === 'vector' ? $this->convertIntExpr($indexExpr) : $this->convertStdContainerKey($info, $indexExpr);
return $container . '.offsetSet(' . $index . ', ' . $value . ')'; return $container . '_ref.offsetSet(' . $index . ', ' . $value . ')';
} }
protected function convertStdContainerKey(array $info, string $index): string protected function convertStdContainerKey(array $info, string $index): string
@ -563,21 +564,18 @@ trait StdContainerParser
$this->fatalError($expr->args[0]->value, 'std::unsafe_cast() expects first argument to be a std container type expression'); $this->fatalError($expr->args[0]->value, 'std::unsafe_cast() expects first argument to be a std container type expression');
} }
if (!$this->isVarExpr($expr->args[1]->value)) { if (!$this->isVarExpr($expr->args[1]->value)) {
$this->fatalError($expr->args[1]->value, 'std::unsafe_cast() expects second argument to be an UnsafePtr variable'); $this->fatalError($expr->args[1]->value, 'std::unsafe_cast() expects second argument to be a variable');
} }
$unsafePtr = $this->parseVariable($expr->args[1]->value); $sourceVar = $this->parseVariable($expr->args[1]->value);
if (!$this->hasVar($unsafePtr)) { if (!$this->hasVar($sourceVar)) {
$this->fatalError($expr->args[1]->value, 'Undefined variable `$' . $unsafePtr . '`'); $this->fatalError($expr->args[1]->value, 'Undefined variable `$' . $sourceVar . '`');
}
if (!$this->isUnsafePtrParameter($unsafePtr)) {
$this->fatalError($expr->args[1]->value, 'std::unsafe_cast() expects second argument to be an UnsafePtr parameter');
} }
if ($containerType === 'array') { if ($containerType === 'array') {
$this->addLocalVar($var, self::TYPE_STD_ARRAY); $this->addLocalVar($var, self::TYPE_STD_ARRAY);
$this->parseStdArray($var, $typeExpr); $this->parseStdArray($var, $typeExpr);
$this->context->stdArrays[$var]['unsafePtr'] = $unsafePtr; $this->context->stdArrays[$var]['unsafePtr'] = $sourceVar;
return '// php_unsafe_cast<' . $this->context->stdArrays[$var]['decl'] . '>(' . $unsafePtr . ')'; return '// php_unsafe_cast<' . $this->context->stdArrays[$var]['decl'] . '>(' . $sourceVar . ')';
} }
if ($containerType === 'vector') { if ($containerType === 'vector') {
@ -590,8 +588,8 @@ trait StdContainerParser
$this->addLocalVar($var, self::TYPE_STD_UNORDERED_MAP); $this->addLocalVar($var, self::TYPE_STD_UNORDERED_MAP);
$this->parseStdUnorderedMap($var, $typeExpr); $this->parseStdUnorderedMap($var, $typeExpr);
} }
$this->context->stdContainers[$var]['unsafePtr'] = $unsafePtr; $this->context->stdContainers[$var]['unsafePtr'] = $sourceVar;
return '// php_unsafe_cast<' . $this->context->stdContainers[$var]['decl'] . '>(' . $unsafePtr . ')'; return '// php_unsafe_cast<' . $this->context->stdContainers[$var]['decl'] . '>(' . $sourceVar . ')';
} }
protected function parseStdMapKeyType(NodeAbstract $expr, string $owner): string protected function parseStdMapKeyType(NodeAbstract $expr, string $owner): string

@ -1,10 +1,10 @@
--TEST-- --TEST--
std array: UnsafePtr unsafe_cast std array: unsafe_cast
--FILE-- --FILE--
<?php <?php
function std_array_unsafe_ptr_update(UnsafePtr $unsafePtr): void function std_array_unsafe_ptr_update($source): void
{ {
$array = std::unsafe_cast(std::array(native_types::type_int, 3), $unsafePtr); $array = std::unsafe_cast(std::array(native_types::type_int, 3), $source);
var_dump($array[1]); var_dump($array[1]);
$array[2] = 9; $array[2] = 9;
} }

@ -2,9 +2,9 @@
std array: unsafe_cast type mismatch std array: unsafe_cast type mismatch
--FILE-- --FILE--
<?php <?php
function std_array_unsafe_ptr_type_mismatch(UnsafePtr $unsafePtr): void function std_array_unsafe_ptr_type_mismatch($source): void
{ {
$array = std::unsafe_cast(std::array(native_types::type_float, 3), $unsafePtr); $array = std::unsafe_cast(std::array(native_types::type_float, 3), $source);
} }
function main() { function main() {
@ -17,4 +17,4 @@ function main() {
} }
?> ?>
--EXPECT-- --EXPECT--
std::unsafe_cast(): UnsafePtr type mismatch std::unsafe_cast(): std container type mismatch

@ -1,17 +1,14 @@
--TEST-- --TEST--
std map: UnsafePtr unsafe_cast std map: unsafe_cast
--FILE-- --FILE--
<?php <?php
function std_map_unsafe_ptr_update(UnsafePtr $unsafePtr): void function std_map_unsafe_ptr_update($source): void
{ {
$map = std::unsafe_cast(std::map(complex_types::type_str, native_types::type_int), $unsafePtr); $map = std::unsafe_cast(std::map(complex_types::type_str, native_types::type_int), $source);
var_dump($map["b"]); var_dump($map["b"]);
$map["c"] = 9; $map["c"] = 9;
$GLOBALS['unsafe_ptr'] = $unsafePtr; unset($source);
var_dump($GLOBALS['unsafe_ptr']);
unset($unsafePtr);
$array = (array)$map; $array = (array)$map;
var_dump(count($array)); var_dump(count($array));
@ -29,6 +26,5 @@ function main() {
?> ?>
--EXPECT-- --EXPECT--
int(7) int(7)
NULL
int(3) int(3)
int(9) int(9)

@ -2,9 +2,9 @@
std map: unsafe_cast type mismatch std map: unsafe_cast type mismatch
--FILE-- --FILE--
<?php <?php
function std_map_unsafe_ptr_type_mismatch(UnsafePtr $unsafePtr): void function std_map_unsafe_ptr_type_mismatch($source): void
{ {
$map = std::unsafe_cast(std::map(complex_types::type_str, native_types::type_float), $unsafePtr); $map = std::unsafe_cast(std::map(complex_types::type_str, native_types::type_float), $source);
} }
function main() { function main() {
@ -17,4 +17,4 @@ function main() {
} }
?> ?>
--EXPECT-- --EXPECT--
std::unsafe_cast(): UnsafePtr type mismatch std::unsafe_cast(): std container type mismatch

@ -1,10 +1,10 @@
--TEST-- --TEST--
std unordered map: UnsafePtr unsafe_cast std unordered map: unsafe_cast
--FILE-- --FILE--
<?php <?php
function std_unordered_map_unsafe_ptr_update(UnsafePtr $unsafePtr): void function std_unordered_map_unsafe_ptr_update($source): void
{ {
$map = std::unsafe_cast(std::unordered_map(native_types::type_int, native_types::type_int), $unsafePtr); $map = std::unsafe_cast(std::unordered_map(native_types::type_int, native_types::type_int), $source);
var_dump($map[2]); var_dump($map[2]);
$map[3] = 9; $map[3] = 9;
} }

@ -2,9 +2,9 @@
std unordered map: unsafe_cast type mismatch std unordered map: unsafe_cast type mismatch
--FILE-- --FILE--
<?php <?php
function std_unordered_map_unsafe_ptr_type_mismatch(UnsafePtr $unsafePtr): void function std_unordered_map_unsafe_ptr_type_mismatch($source): void
{ {
$map = std::unsafe_cast(std::unordered_map(native_types::type_int, native_types::type_float), $unsafePtr); $map = std::unsafe_cast(std::unordered_map(native_types::type_int, native_types::type_float), $source);
} }
function main() { function main() {
@ -17,4 +17,4 @@ function main() {
} }
?> ?>
--EXPECT-- --EXPECT--
std::unsafe_cast(): UnsafePtr type mismatch std::unsafe_cast(): std container type mismatch

@ -1,10 +1,10 @@
--TEST-- --TEST--
std vector: UnsafePtr unsafe_cast std vector: unsafe_cast
--FILE-- --FILE--
<?php <?php
function std_vector_unsafe_ptr_update(UnsafePtr $unsafePtr): void function std_vector_unsafe_ptr_update($source): void
{ {
$vector = std::unsafe_cast(std::vector(native_types::type_int), $unsafePtr); $vector = std::unsafe_cast(std::vector(native_types::type_int), $source);
var_dump($vector[1]); var_dump($vector[1]);
$vector[2] = 9; $vector[2] = 9;
} }

@ -2,9 +2,9 @@
std vector: unsafe_cast type mismatch std vector: unsafe_cast type mismatch
--FILE-- --FILE--
<?php <?php
function std_vector_unsafe_ptr_type_mismatch(UnsafePtr $unsafePtr): void function std_vector_unsafe_ptr_type_mismatch($source): void
{ {
$vector = std::unsafe_cast(std::vector(native_types::type_float), $unsafePtr); $vector = std::unsafe_cast(std::vector(native_types::type_float), $source);
} }
function main() { function main() {
@ -17,4 +17,4 @@ function main() {
} }
?> ?>
--EXPECT-- --EXPECT--
std::unsafe_cast(): UnsafePtr type mismatch std::unsafe_cast(): std container type mismatch

@ -10,9 +10,9 @@ class StdVectorUnsafeCastOther
{ {
} }
function std_vector_unsafe_ptr_class_type_mismatch(UnsafePtr $unsafePtr): void function std_vector_unsafe_ptr_class_type_mismatch($source): void
{ {
$vector = std::unsafe_cast(std::vector(StdVectorUnsafeCastOther::class), $unsafePtr); $vector = std::unsafe_cast(std::vector(StdVectorUnsafeCastOther::class), $source);
} }
function main() { function main() {
@ -25,4 +25,4 @@ function main() {
} }
?> ?>
--EXPECT-- --EXPECT--
std::unsafe_cast(): UnsafePtr type mismatch std::unsafe_cast(): std container type mismatch

@ -5,9 +5,9 @@ std vector: namespaced self class value unsafe_cast
namespace StdVectorUnsafeCastNs { namespace StdVectorUnsafeCastNs {
class Holder class Holder
{ {
public static function update(UnsafePtr $unsafePtr): void public static function update($source): void
{ {
$vector = std::unsafe_cast(std::vector(self::class), $unsafePtr); $vector = std::unsafe_cast(std::vector(self::class), $source);
echo "ok\n"; echo "ok\n";
} }

Loading…
Cancel
Save