diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 0c4386a1..aef6483b 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -4041,6 +4041,10 @@ class CompilerBase extends \PhpAot\Core\Translator $this->fatalError($var, 'Cannot use [] for array unset'); } $array = $this->parseIdentifier($var->var); + if (($this->isStdMap($array) or $this->isStdUnorderedMap($array)) + and !empty($this->context->stdContainers[$array]['locking'])) { + $this->fatalError($var, 'Cannot delete element in std container in foreach loop'); + } $dim = $this->parseIdentifier($var->dim); $lines[] = $array . '.offsetUnset(' . $dim . ');'; } elseif ($this->isPropertyFetch($var)) { @@ -4229,6 +4233,8 @@ class CompilerBase extends \PhpAot\Core\Translator $this->fatalError($node, 'Cannot use & with foreach'); } return $this->parseForeachObject($node); + } elseif ($this->isStdContainerType($type)) { + return $this->parseForeachStdContainer($node); } } } @@ -5313,14 +5319,18 @@ class CompilerBase extends \PhpAot\Core\Translator return $code; } - protected function checkVar(NodeAbstract $node, string $name): void + protected function checkVar(NodeAbstract $node, string $name, string $defaultType = self::TYPE_VAR): void { if (!$this->hasVar($name)) { - $this->addLocalVar($name, self::TYPE_VAR); - } else { + $this->addLocalVar($name, $defaultType); + } elseif ($defaultType === self::TYPE_VAR) { if ($this->getVarType($name) !== self::TYPE_VAR) { $this->fatalError($node, 'Cannot assign value to variable $' . $name . ' of type ' . $this->getVarType($name)); } + } else { + if ($this->getVarType($name) !== $defaultType) { + $this->fatalError($node, 'Cannot assign value to variable $' . $name . ' of type ' . $this->getVarType($name) . ' with type ' . $defaultType); + } } } diff --git a/src/Php/Parser/StdContainerParser.php b/src/Php/Parser/StdContainerParser.php index e2aa8008..07fb217c 100644 --- a/src/Php/Parser/StdContainerParser.php +++ b/src/Php/Parser/StdContainerParser.php @@ -10,18 +10,24 @@ namespace PhpAot\Php\Parser; use PhpAot\Php\Symbol; use PhpParser\Node\Expr; +use PhpParser\Node\Stmt\Foreach_; use PhpParser\NodeAbstract; trait StdContainerParser { protected function isStdContainer(string $var): bool { - return $this->hasLocalVar($var) and in_array($this->getVarType($var), [ - self::TYPE_STD_ARRAY, - self::TYPE_STD_VECTOR, - self::TYPE_STD_MAP, - self::TYPE_STD_UNORDERED_MAP, - ], true); + return $this->hasLocalVar($var) and $this->isStdContainerType($this->getVarType($var)); + } + + protected function isStdContainerType(string $type): bool + { + return in_array($type, [ + self::TYPE_STD_ARRAY, + self::TYPE_STD_VECTOR, + self::TYPE_STD_MAP, + self::TYPE_STD_UNORDERED_MAP, + ], true); } protected function isStdArray(string $var): bool @@ -72,6 +78,28 @@ trait StdContainerParser return $this->context->stdContainers[$var]; } + protected function getStdContainerKeyType(string $var): string + { + if ($this->isStdVector($var) or $this->isStdArray($var)) { + return self::TYPE_INT; + } + return $this->getStdContainerVarInfo($var)['keyType']; + } + + protected function getStdContainerValueType(string $var, string $valueVar): string + { + $info = $this->getStdContainerVarInfo($var); + if ($this->isStdArray($var)) { + return count($info['sizes']) > 1 ? self::TYPE_ARRAY : $info['type']; + } + if ($info['type'] === self::TYPE_OBJECT and $info['class']) { + $this->addObject($valueVar, $info['class']); + } else { + unset($this->context->objects[$valueVar]); + } + return $info['type']; + } + protected function getStdArrayDecl(string $type, array $sizes): string { $decl = str_repeat(self::TYPE_STD_ARRAY . '<', count($sizes)); @@ -315,6 +343,56 @@ trait StdContainerParser return implode('', $nesting); } + protected function parseForeachStdContainer(Foreach_ $node): string + { + $container = $this->parseIdentifier($node->expr); + if ($this->isStdMap($container) or $this->isStdUnorderedMap($container)) { + $this->context->stdContainers[$container]['locking'] = true; + } + $iterator = $this->genTmpVarName(); + $code = "for (auto $iterator = $container.begin(); $iterator != $container.end(); ++$iterator) {" . PHP_EOL; + $this->indentLevel++; + if ($node->keyVar) { + $keyVar = $this->parseIdentifier($node->keyVar); + $this->checkVar($node, $keyVar, $this->getStdContainerKeyType($container)); + if ($this->isStdVector($container) or $this->isStdArray($container)) { + $code .= $this->getIndent() . "$keyVar = $iterator - $container.begin();" . PHP_EOL; + } else { + $code .= $this->getIndent() . "$keyVar = {$iterator}->first;" . PHP_EOL; + } + } + + if ($node->byRef) { + $this->fatalError($node, 'Cannot use & with std container foreach'); + } + + if (!$this->isVarExpr($node->valueVar)) { + $this->fatalError($node, 'Cannot assign value to std container foreach'); + } + + $valueVar = $this->parseIdentifier($node->valueVar); + $this->checkVar($node, $valueVar, $this->getStdContainerValueType($container, $valueVar)); + + if ($this->isStdVector($container) or $this->isStdArray($container)) { + $code .= $this->getIndent() . "$valueVar = *$iterator;" . PHP_EOL; + } else { + $code .= $this->getIndent() . "$valueVar = {$iterator}->second;" . PHP_EOL; + } + + $body = $this->parseStmts($node->stmts); + $this->indentLevel--; + + $code .= $this->parseBeforeStmtLines() . PHP_EOL; + $code .= $body . PHP_EOL; + + $code .= $this->getIndent() . '}'; + unset($this->context->objects[$valueVar]); + if ($this->isStdMap($container) or $this->isStdUnorderedMap($container)) { + $this->context->stdContainers[$container]['locking'] = false; + } + return $code; + } + protected function parseStdContainerDimFetch(Expr\ArrayDimFetch $expr): string { if ($this->isStdArrayExpr($expr)) { @@ -532,11 +610,7 @@ trait StdContainerParser protected function getStdMapDecl(string $containerType, string $keyType, string $valueType): string { - $decl = $containerType . '<' . $valueType; - if ($keyType === self::TYPE_STR) { - $decl .= ', ' . self::TYPE_STR; - } - return $decl . '>'; + return $containerType . '<' . $keyType . ', ' . $valueType . '>'; } protected function parseStdArray(string $var, Expr\StaticCall $expr): string diff --git a/tests/aot/std-array/012.phpt b/tests/aot/std-array/012.phpt new file mode 100644 index 00000000..29c2eec1 --- /dev/null +++ b/tests/aot/std-array/012.phpt @@ -0,0 +1,15 @@ +--TEST-- +std array: foreach +--FILE-- + $item) { + $a[$k] = random_int(1, 1000); + } + $count = array_sum($a); + var_dump($count >= 5); +} +?> +--EXPECT-- +bool(true) \ No newline at end of file diff --git a/tests/aot/std-map/010.phpt b/tests/aot/std-map/010.phpt new file mode 100644 index 00000000..ffd491cb --- /dev/null +++ b/tests/aot/std-map/010.phpt @@ -0,0 +1,32 @@ +--TEST-- +std map: unset +--FILE-- + $v) { + var_dump($k, $v); + } + unset($map["beta"]); + echo "unset-------------\n"; + foreach ($map as $k => $v) { + var_dump($k, $v); + } +} +?> +--EXPECT-- +string(5) "alpha" +int(10) +string(4) "beta" +int(20) +string(5) "gamma" +int(30) +unset------------- +string(5) "alpha" +int(10) +string(5) "gamma" +int(30) \ No newline at end of file diff --git a/tests/aot/std-unordered-map/010.phpt b/tests/aot/std-unordered-map/010.phpt new file mode 100644 index 00000000..725a6434 --- /dev/null +++ b/tests/aot/std-unordered-map/010.phpt @@ -0,0 +1,32 @@ +--TEST-- +std map: unset +--FILE-- + $v) { + var_dump($k, $v); + } + unset($map["beta"]); + echo "unset-------------\n"; + foreach ($map as $k => $v) { + var_dump($k, $v); + } +} +?> +--EXPECT-- +string(5) "gamma" +int(30) +string(4) "beta" +int(20) +string(5) "alpha" +int(10) +unset------------- +string(5) "gamma" +int(30) +string(5) "alpha" +int(10) \ No newline at end of file diff --git a/tests/aot/std-vector/013.phpt b/tests/aot/std-vector/013.phpt new file mode 100644 index 00000000..11ae13b1 --- /dev/null +++ b/tests/aot/std-vector/013.phpt @@ -0,0 +1,23 @@ +--TEST-- +std vector: foreach +--FILE-- += 5); + $total = 0; + foreach($a as $v){ + $total += $v; + } + var_dump($total == $count); +} +?> +--EXPECT-- +bool(true) +bool(true)