feat(compiler): add multi-return array adapter with safe parameter consumption

- Implement multi-return forward args functionality for by-value parameters
- Add support for parameter forwarding with php::takeValue wrapper
- Create test cases for multi-return array adapter behavior
- Update translator to handle forwarded arguments correctly
- Add phpunit test coverage for multi-return tuple functionality
- Implement proper argument consumption logic for multi-return scenarios
pull/20/head
韩天峰 1 month ago
parent 91c8dc9544
commit 682a9be61b
  1. 20
      phpunit/code/multi-return-tuple.php
  2. 13
      phpunit/src/MultiReturnTest.php
  3. 8
      src/Translator.php
  4. 99
      tests/compiler/array/multi-return-forward-args.phpt

@ -27,6 +27,26 @@ function phpunit_multi_repeated_value(): array
return [$repeated, $repeated, $tail]; return [$repeated, $repeated, $tail];
} }
function phpunit_multi_forward_args(
mixed $value,
string $text,
array $items,
object $object,
int $count,
&$reference,
...$rest,
): array {
return [$value, $text];
}
function phpunit_multi_forward_defaults(
string $text = 'default',
array $items = [],
...$rest,
): array {
return [$text, $items];
}
function phpunit_multi_side_effect(): array function phpunit_multi_side_effect(): array
{ {
return [time(), 2]; return [time(), 2];

@ -46,6 +46,19 @@ final class MultiReturnTest extends TestCase
'array = php_phpunit_multi_values()', 'array = php_phpunit_multi_values()',
$code, $code,
); );
$this->assertStringContainsString(
'return php::Array(typephp::detail::php_phpunit_multi_forward_args('
. 'php::takeValue(value), php::takeValue(text), php::takeValue(items), '
. 'php::takeValue(object), count, reference, php::takeValue(rest)));',
$code,
);
$this->assertStringContainsString(
'return php::Array(typephp::detail::php_phpunit_multi_forward_defaults('
. 'php::takeValue(text), php::takeValue(items), php::takeValue(rest)));',
$code,
);
$this->assertStringNotContainsString('php::takeValue(reference)', $code);
$this->assertStringNotContainsString('php::takeValue(count)', $code);
$this->assertStringContainsString( $this->assertStringContainsString(
'std::tie(partialFirst, partialSecond, std::ignore) = typephp::detail::php_phpunit_multi_three_values()', 'std::tie(partialFirst, partialSecond, std::ignore) = typephp::detail::php_phpunit_multi_three_values()',
$code, $code,

@ -2713,7 +2713,7 @@ CODE;
$cppCode .= $this->getIndent() . $cppType . ' ' . $var . ' = ' . $expr . ';' . PHP_EOL; $cppCode .= $this->getIndent() . $cppType . ' ' . $var . ' = ' . $expr . ';' . PHP_EOL;
} }
$callParam = $var; $callParam = $var;
if ($this->canConsumeWrapperArgument($argInfo)) { if ($this->canConsumeForwardedArgument($argInfo)) {
$callParam = 'php::takeValue(' . $var . ')'; $callParam = 'php::takeValue(' . $var . ')';
} }
$callParams .= $callParam . ','; $callParams .= $callParam . ',';
@ -2739,7 +2739,7 @@ CODE;
return $cppCode; return $cppCode;
} }
private function canConsumeWrapperArgument(ArgInfo $argInfo): bool private function canConsumeForwardedArgument(ArgInfo $argInfo): bool
{ {
if ($argInfo->byRef) { if ($argInfo->byRef) {
return false; return false;
@ -3023,7 +3023,9 @@ CODE;
if ($multiReturn) { if ($multiReturn) {
$forwardArgs = implode(', ', array_map( $forwardArgs = implode(', ', array_map(
static fn($argInfo) => $argInfo->name, fn($argInfo) => $this->canConsumeForwardedArgument($argInfo)
? 'php::takeValue(' . $argInfo->name . ')'
: $argInfo->name,
$this->functionDef->argInfoList, $this->functionDef->argInfoList,
)); ));
$code .= Type::ARRAY . ' ' . $nativeName . '(' . $this->functionDef->params . ') {' . PHP_EOL; $code .= Type::ARRAY . ' ' . $nativeName . '(' . $this->functionDef->params . ') {' . PHP_EOL;

@ -0,0 +1,99 @@
--TEST--
Multi-return Array adapter safely consumes its by-value parameters
--FILE--
<?php
function multi_return_forward_args(
mixed $value,
string $text,
array $items,
object $object,
int $count,
&$reference,
...$rest,
): array {
$reference = 'updated';
return [$value, $text, $items, $object, $count, $reference, $rest];
}
function multi_return_forward_defaults(
string $text = 'default',
array $items = [],
...$rest,
): array {
return [$text, $items, $rest];
}
function main(): void
{
$object = (object) ['name' => 'direct'];
$directReference = 'before';
[$value, $text, $items, $directObject, $count, $returnedReference, $rest]
= multi_return_forward_args('value', 'text', [1], $object, 2, $directReference, 'x');
var_dump(
$value,
$text,
$items,
$directObject->name,
$count,
$directReference,
$returnedReference,
$rest,
);
$arrayReference = 'before';
$array = multi_return_forward_args('array', 'adapter', [3], $object, 4, $arrayReference, 'y', 'z');
var_dump($arrayReference, $array[0], $array[1], $array[2], $array[3]->name, $array[4], $array[5], $array[6]);
$defaults = multi_return_forward_defaults();
[$defaultText, $defaultItems, $defaultRest] = multi_return_forward_defaults(rest: 'named');
var_dump($defaults, $defaultText, $defaultItems, $defaultRest);
}
?>
--EXPECT--
string(5) "value"
string(4) "text"
array(1) {
[0]=>
int(1)
}
string(6) "direct"
int(2)
string(7) "updated"
string(7) "updated"
array(1) {
[0]=>
string(1) "x"
}
string(7) "updated"
string(5) "array"
string(7) "adapter"
array(1) {
[0]=>
int(3)
}
string(6) "direct"
int(4)
string(7) "updated"
array(2) {
[0]=>
string(1) "y"
[1]=>
string(1) "z"
}
array(3) {
[0]=>
string(7) "default"
[1]=>
array(0) {
}
[2]=>
array(0) {
}
}
string(7) "default"
array(0) {
}
array(1) {
["rest"]=>
string(5) "named"
}
Loading…
Cancel
Save