feat(translator): implement argument consumption optimization for wrapper functions

- Add canConsumeWrapperArgument method to determine when arguments can be consumed
- Modify argument passing logic to use php::takeValue for consumable arguments
- Support variadic arguments and specific types (mixed, string, array, object) for consumption
- Preserve reference arguments without consumption to maintain memory safety
- Add comprehensive test coverage for argument move semantics
- Create test files to verify correct argument handling in wrapper generation
pull/20/head
韩天峰 1 month ago
parent c75fb14be1
commit 91c8dc9544
  1. 19
      phpunit/code/wrapper-argument-move.php
  2. 33
      phpunit/src/WrapperArgumentMoveTest.php
  3. 23
      src/Translator.php
  4. 47
      tests/compiler/functions/wrapper-argument-move.phpt

@ -0,0 +1,19 @@
<?php
function phpunit_wrapper_argument_move(
mixed $value,
string $text,
array $items,
object $object,
int $count,
&$reference,
...$rest,
): void {
$reference = $text;
}
class PhpunitWrapperArgumentMoveTarget
{
public function consume(string $value): void
{
}
}

@ -0,0 +1,33 @@
<?php
use PHPUnit\Framework\TestCase;
use TypePhp\CompilerTest;
final class WrapperArgumentMoveTest extends TestCase
{
public function testWrapperConsumesOnlyDeadByValueWrappers(): void
{
global $translator;
$compiler = CompilerTest::create(ROOT_PATH);
$translator = $compiler;
$file = __DIR__ . '/../code/wrapper-argument-move.php';
$compiler->addFiles([$file]);
$compiler->prepareFile($file);
$cppFile = $compiler->convertFile($file);
$code = file_get_contents($cppFile);
$this->assertStringContainsString(
'php_phpunit_wrapper_argument_move(php::takeValue(arg_value),php::takeValue(arg_text),'
. 'php::takeValue(arg_items),php::takeValue(arg_object),arg_count,arg_reference,'
. 'php::takeValue(arg_rest));',
$code,
);
$this->assertStringNotContainsString('php::takeValue(arg_count)', $code);
$this->assertStringNotContainsString('php::takeValue(arg_reference)', $code);
$this->assertStringContainsString(
'php_phpunitwrapperargumentmovetarget__consume(this_, php::takeValue(arg_value));',
$code,
);
$this->assertStringNotContainsString('php::takeValue(this_)', $code);
}
}

@ -2712,7 +2712,11 @@ CODE;
}
$cppCode .= $this->getIndent() . $cppType . ' ' . $var . ' = ' . $expr . ';' . PHP_EOL;
}
$callParams .= 'arg_' . $argInfo->name . ',';
$callParam = $var;
if ($this->canConsumeWrapperArgument($argInfo)) {
$callParam = 'php::takeValue(' . $var . ')';
}
$callParams .= $callParam . ',';
}
if ($functionDef->method) {
@ -2735,6 +2739,23 @@ CODE;
return $cppCode;
}
private function canConsumeWrapperArgument(ArgInfo $argInfo): bool
{
if ($argInfo->byRef) {
return false;
}
if ($argInfo->variadic) {
return true;
}
return in_array($this->getDefaultArgumentType($argInfo), [
Type::VAR,
Type::STR,
Type::ARRAY,
Type::OBJECT,
], true);
}
private function genWrapperRequiredArgCountCheck(FunctionDef $functionDef, string $displayName): string
{
$required = $functionDef->argCountRequired;

@ -0,0 +1,47 @@
--TEST--
Zend wrappers safely consume dead by-value arguments
--FILE--
<?php
function wrapper_argument_move(
mixed $value,
string $text,
array $items,
object $object,
int $count,
&$reference,
...$rest,
): array {
$reference = 'updated';
$result = [$value, $text, $items[0], $object->name, $count, $rest];
return $result;
}
function main(): void
{
$reference = 'before';
$object = (object) ['name' => 'object'];
$result = wrapper_argument_move('mixed', 'text', [10], $object, 5, $reference, 'x', 'y');
var_dump($reference, $result);
}
?>
--EXPECT--
string(7) "updated"
array(6) {
[0]=>
string(5) "mixed"
[1]=>
string(4) "text"
[2]=>
int(10)
[3]=>
string(6) "object"
[4]=>
int(5)
[5]=>
array(2) {
[0]=>
string(1) "x"
[1]=>
string(1) "y"
}
}
Loading…
Cancel
Save