From aa6d81d2a7dab467c189ab05214a52e0de78e950 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 12 Feb 2026 12:54:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(compiler):=20=E6=B7=BB=E5=8A=A0=E5=8F=AF?= =?UTF-8?q?=E5=8F=98=E5=8F=82=E6=95=B0=E6=94=AF=E6=8C=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 ArgInfo 类中添加 variadic 属性用于标识可变参数 - 实现编译器对可变参数的验证逻辑,确保可变参数位于最后位置 - 修改参数类型处理逻辑,将可变参数转换为数组类型 - 更新函数调用参数解析,支持可变参数的展开和传递 - 在翻译器中实现可变参数的 C++ 代码生成逻辑 - 添加遍历可变参数的循环处理机制 - 新增可变参数功能的测试用例和示例文件 - 完善参数列表生成时对可变参数的特殊处理 --- examples/python/sys.php | 24 ++++++++++++++++++++++++ examples/vargs.php | 10 ++++++++++ src/Php/ArgInfo.php | 1 + src/Php/CompilerBase.php | 27 +++++++++++++++++++++++---- src/Php/Translator.php | 30 ++++++++++++++++++++++-------- tests/aot/variadic-args.phpt | 26 ++++++++++++++++++++++++++ 6 files changed, 106 insertions(+), 12 deletions(-) create mode 100644 examples/python/sys.php create mode 100644 examples/vargs.php create mode 100644 tests/aot/variadic-args.phpt diff --git a/examples/python/sys.php b/examples/python/sys.php new file mode 100644 index 00000000..0858cb00 --- /dev/null +++ b/examples/python/sys.php @@ -0,0 +1,24 @@ +get("PATH")); +} + + + +use numpy as np; + +function np() +{ + var_dump(np::$module); + var_dump(np::version->full_version); +} + + diff --git a/examples/vargs.php b/examples/vargs.php new file mode 100644 index 00000000..d0a744fe --- /dev/null +++ b/examples/vargs.php @@ -0,0 +1,10 @@ +argCountRequired = count($params); - foreach ($params as $param) { + $last = array_key_last($params); + foreach ($params as $i => $param) { // .stub 存根定义 C++ Native 函数,必须设置函数的参数类型 if ($this->stubFile and !$param->type) { throw new \RuntimeException('No type for ' . $this->parseIdentifier($param->var)); @@ -875,12 +876,20 @@ class CompilerBase extends \PhpAot\Core\Translator if ($param->byRef) { $this->fatalError($param, 'ByRef parameters are not supported'); } + if ($param->variadic and $i !== $last) { + $this->fatalError($param, 'Variadic parameters must be the last parameter'); + } $name = $this->parseIdentifier($param->var); $type = $this->parseParameterType($param, $name); - $list[] = $type . ' ' . $name; + if ($param->variadic) { + $list[] = self::TYPE_ARRAY . ' ' . $name; + } else { + $list[] = $type . ' ' . $name; + } $argInfo = new ArgInfo(); $argInfo->name = $name; $argInfo->type = $type; + $argInfo->variadic = $param->variadic; if (isset($param->default)) { $functionDef->argCountRequired = count($list) - 1; $argInfo->default = $this->parseIdentifier($param->default); @@ -1957,8 +1966,18 @@ class CompilerBase extends \PhpAot\Core\Translator if ($arg->name !== null) { $this->fatalError($arg, 'Named arguments are not supported'); } - $argInfo = $this->getArgInfo($arg, $nativeFunc, $i); - $list_args[] = $this->getTypeConvertedArg($arg, $argInfo); + $argInfo = $this->getArgInfo($arg, $nativeFunc, $i); + if ($argInfo->variadic) { + $vargs = array_slice($args, $i); + $list_vargs = []; + foreach ($vargs as $varg) { + $list_vargs[] = $this->getTypeConvertedArg($varg, $argInfo); + } + $list_args[] = '{' . implode(', ', $list_vargs) . '}'; + break; + } else { + $list_args[] = $this->getTypeConvertedArg($arg, $argInfo); + } } return implode(', ', $list_args); diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 9ebba593..8480dce3 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -300,9 +300,13 @@ class Translator extends Preprocessor $argInfoList = $func->argInfoList; if ($argInfoList) { foreach ($argInfoList as $argInfo) { - $arg = $argInfo->type . ' ' . $argInfo->name; - if ($argInfo->default) { - $arg .= ' = ' . $argInfo->default; + if ($argInfo->variadic) { + $arg = self::TYPE_ARRAY . ' ' . $argInfo->name . '()'; + } else { + $arg = $argInfo->type . ' ' . $argInfo->name; + if ($argInfo->default) { + $arg .= ' = ' . $argInfo->default; + } } $list[] = $arg; } @@ -734,13 +738,23 @@ class Translator extends Preprocessor $cppCode = ''; $callParams = ''; foreach ($functionDef->argInfoList as $k => $argInfo) { - if ($argInfo->default) { - $argExpr = 'php::getCallArg(' . $k . ', ' . $argInfo->default . ')'; + $var = 'arg_' . $argInfo->name; + if ($argInfo->variadic) { + $cppCode .= $this->getIndent() . self::TYPE_ARRAY . ' ' . $var . ';' . PHP_EOL; + $cppCode .= $this->getIndent() . 'for (uint32_t i = ' . $k . '; i < php::getCallArgNum(); i++) {' . PHP_EOL; + $this->indentLevel++; + $cppCode .= $this->getIndent() . $var . '.append(php::getCallArg(i));' . PHP_EOL; + $this->indentLevel--; + $cppCode .= '}' . PHP_EOL; } else { - $argExpr = 'php::getCallArg(' . $k . ')'; + if ($argInfo->default) { + $argExpr = 'php::getCallArg(' . $k . ', ' . $argInfo->default . ')'; + } else { + $argExpr = 'php::getCallArg(' . $k . ')'; + } + $expr = $this->convertExprFromType($argInfo->type, $argExpr); + $cppCode .= $this->getIndent() . $argInfo->type . ' ' . $var . ' = ' . $expr . ';' . PHP_EOL; } - $expr = $this->convertExprFromType($argInfo->type, $argExpr); - $cppCode .= $this->getIndent() . $argInfo->type . ' arg_' . $argInfo->name . ' = ' . $expr . ';' . PHP_EOL; $callParams .= 'arg_' . $argInfo->name . ','; } diff --git a/tests/aot/variadic-args.phpt b/tests/aot/variadic-args.phpt new file mode 100644 index 00000000..8c2711fd --- /dev/null +++ b/tests/aot/variadic-args.phpt @@ -0,0 +1,26 @@ +--TEST-- +variadic args +--FILE-- + +--EXPECT-- +int(1) +int(2) +array(3) { + [0]=> + int(3) + [1]=> + int(4) + [2]=> + int(5) +} +