|
|
|
|
@ -3,12 +3,11 @@ |
|
|
|
|
namespace PhpAot\Php; |
|
|
|
|
|
|
|
|
|
use MJS\TopSort\Implementations\StringSort; |
|
|
|
|
use Symfony\Component\Yaml\Yaml; |
|
|
|
|
use PhpParser\Modifiers; |
|
|
|
|
use PhpParser\Node; |
|
|
|
|
use PhpParser\Node\Stmt\Foreach_; |
|
|
|
|
use PhpParser\NodeAbstract; |
|
|
|
|
use PhpParser\NodeTraverser; |
|
|
|
|
use Symfony\Component\Yaml\Yaml; |
|
|
|
|
|
|
|
|
|
class Translator extends Preprocessor |
|
|
|
|
{ |
|
|
|
|
@ -30,14 +29,14 @@ class Translator extends Preprocessor |
|
|
|
|
public function __construct(string $rootPath) |
|
|
|
|
{ |
|
|
|
|
parent::__construct($rootPath); |
|
|
|
|
$this->climate->arguments->add(require __DIR__ . '/../config/compiler_options.php'); |
|
|
|
|
$this->climate->arguments->add(require __DIR__.'/../config/compiler_options.php'); |
|
|
|
|
$this->preprocessArgvAdvanced(); |
|
|
|
|
$this->climate->arguments->parse(); |
|
|
|
|
|
|
|
|
|
$this->optimizeLevel = $this->climate->arguments->get('optimize'); |
|
|
|
|
$this->buildMode = $this->climate->arguments->get('mode'); |
|
|
|
|
$this->debugLine = intval($this->climate->arguments->get('debug-line')); |
|
|
|
|
// $this->noLiteralStrings = $this->climate->arguments->get('noLiteralStrings'); |
|
|
|
|
// $this->noLiteralStrings = $this->climate->arguments->get('noLiteralStrings'); |
|
|
|
|
$this->noLiteralStrings = true; |
|
|
|
|
$this->enableProfiler = $this->climate->arguments->defined('profile'); |
|
|
|
|
$this->internalFunctions = array_flip(get_defined_functions()['internal']); |
|
|
|
|
@ -84,7 +83,8 @@ class Translator extends Preprocessor |
|
|
|
|
public function convert(string $file): string |
|
|
|
|
{ |
|
|
|
|
if ($this->hasCppFileCache($file)) { |
|
|
|
|
$this->climate->darkGray('skip: ' . $file . ', cache exists'); |
|
|
|
|
$this->climate->darkGray('skip: '.$file.', cache exists'); |
|
|
|
|
|
|
|
|
|
return $this->getCppFile($file); |
|
|
|
|
} |
|
|
|
|
$phpCode = $this->loadFile($file); |
|
|
|
|
@ -96,6 +96,7 @@ class Translator extends Preprocessor |
|
|
|
|
$cppFile = $this->getCppFile($file); |
|
|
|
|
$this->save($cppCode, $cppFile); |
|
|
|
|
$this->phpSrcFiles[] = $file; |
|
|
|
|
|
|
|
|
|
return $cppFile; |
|
|
|
|
} catch (RedoException $e) { |
|
|
|
|
continue; |
|
|
|
|
@ -105,14 +106,14 @@ class Translator extends Preprocessor |
|
|
|
|
|
|
|
|
|
protected function getRegisterClassFunction(string $name): string |
|
|
|
|
{ |
|
|
|
|
return self::PREFIX . 'register_class_' . $name; |
|
|
|
|
return self::PREFIX.'register_class_'.$name; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected function getRegisterClassFunctionCeList(ClassDef|InterfaceDef $classDef): array |
|
|
|
|
{ |
|
|
|
|
$list = []; |
|
|
|
|
$parentCe = $this->getParentClassCe($classDef); |
|
|
|
|
if ($parentCe !== '') { |
|
|
|
|
if ('' !== $parentCe) { |
|
|
|
|
$list = [$parentCe]; |
|
|
|
|
} |
|
|
|
|
// interface 没有 implements |
|
|
|
|
@ -120,6 +121,7 @@ class Translator extends Preprocessor |
|
|
|
|
return $list; |
|
|
|
|
} |
|
|
|
|
$implements = $this->getImplementCe($classDef); |
|
|
|
|
|
|
|
|
|
return array_merge($list, $implements); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -134,12 +136,13 @@ class Translator extends Preprocessor |
|
|
|
|
if (empty($depsCeList)) { |
|
|
|
|
return ''; |
|
|
|
|
} |
|
|
|
|
return 'zend_class_entry *' . implode(', zend_class_entry *', $depsCeList); |
|
|
|
|
|
|
|
|
|
return 'zend_class_entry *'.implode(', zend_class_entry *', $depsCeList); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected function getClassCe(ClassLikeDef $classDef): string |
|
|
|
|
{ |
|
|
|
|
return self::PREFIX . 'class_entry_' . $classDef->getNamespacedName(); |
|
|
|
|
return self::PREFIX.'class_entry_'.$classDef->getNamespacedName(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function setTargetName(string $name): void |
|
|
|
|
@ -162,6 +165,7 @@ class Translator extends Preprocessor |
|
|
|
|
protected function getFilesFromDir(string $path): array |
|
|
|
|
{ |
|
|
|
|
$scanner = new FileScanner($path); |
|
|
|
|
|
|
|
|
|
return $scanner->scan(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -175,14 +179,14 @@ class Translator extends Preprocessor |
|
|
|
|
$list = []; |
|
|
|
|
foreach ($sources as $src) { |
|
|
|
|
$src = trim($src); |
|
|
|
|
if ($src[0] != '/') { |
|
|
|
|
$absPath = $projectDir . '/' . $src; |
|
|
|
|
if ('/' != $src[0]) { |
|
|
|
|
$absPath = $projectDir.'/'.$src; |
|
|
|
|
} else { |
|
|
|
|
$absPath = $src; |
|
|
|
|
} |
|
|
|
|
$realPath = realpath($absPath); |
|
|
|
|
if (!$realPath) { |
|
|
|
|
$this->error('Source file not exists: `' . $src . '`'); |
|
|
|
|
$this->error('Source file not exists: `'.$src.'`'); |
|
|
|
|
} |
|
|
|
|
if (is_file($realPath)) { |
|
|
|
|
$list[] = $realPath; |
|
|
|
|
@ -198,27 +202,28 @@ class Translator extends Preprocessor |
|
|
|
|
if (is_array($cfg['cxxflags'])) { |
|
|
|
|
$this->cxxflags = implode(' ', $cfg['cxxflags']); |
|
|
|
|
} else { |
|
|
|
|
$this->cxxflags = str_replace("\n", " ", $cfg['cxxflags']); |
|
|
|
|
$this->cxxflags = str_replace("\n", ' ', $cfg['cxxflags']); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!empty($cfg['ldflags'])) { |
|
|
|
|
if (is_array($cfg['ldflags'])) { |
|
|
|
|
$this->ldflags = implode(' ', $cfg['ldflags']); |
|
|
|
|
} else { |
|
|
|
|
$this->ldflags = str_replace("\n", " ", $cfg['ldflags']); |
|
|
|
|
$this->ldflags = str_replace("\n", ' ', $cfg['ldflags']); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!empty($cfg['name'])) { |
|
|
|
|
$this->setTargetName($cfg['name']); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $list; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function getFiles(string $path): array |
|
|
|
|
{ |
|
|
|
|
$realpath = realpath($path); |
|
|
|
|
if ($realpath === false) { |
|
|
|
|
die("path not exists: $path\n"); |
|
|
|
|
if (false === $realpath) { |
|
|
|
|
exit("path not exists: $path\n"); |
|
|
|
|
} |
|
|
|
|
$path = $realpath; |
|
|
|
|
|
|
|
|
|
@ -228,16 +233,17 @@ class Translator extends Preprocessor |
|
|
|
|
$this->setTargetName($targetName); |
|
|
|
|
} else { |
|
|
|
|
$ext = pathinfo($path, PATHINFO_EXTENSION); |
|
|
|
|
if ($ext === 'yml') { |
|
|
|
|
if ('yml' === $ext) { |
|
|
|
|
$list = $this->parseProjectYaml($path); |
|
|
|
|
} elseif ($ext === 'php') { |
|
|
|
|
} elseif ('php' === $ext) { |
|
|
|
|
$list = [$path]; |
|
|
|
|
$targetName = FileScanner::getFileName($path); |
|
|
|
|
$this->setTargetName($targetName); |
|
|
|
|
} else { |
|
|
|
|
$this->error('Unsupported file type: ' . $path); |
|
|
|
|
$this->error('Unsupported file type: '.$path); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $list; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -245,7 +251,7 @@ class Translator extends Preprocessor |
|
|
|
|
{ |
|
|
|
|
return [ |
|
|
|
|
'func' => 'php::getClassEntry', |
|
|
|
|
'args' => '"' . substr($ce, strlen(self::PREFIX . 'class_entry_')) . '"', |
|
|
|
|
'args' => '"'.substr($ce, strlen(self::PREFIX.'class_entry_')).'"', |
|
|
|
|
]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -254,21 +260,23 @@ class Translator extends Preprocessor |
|
|
|
|
if (!$classDef->extends) { |
|
|
|
|
return ''; |
|
|
|
|
} |
|
|
|
|
return self::PREFIX . 'class_entry_' . $classDef->extends; |
|
|
|
|
|
|
|
|
|
return self::PREFIX.'class_entry_'.$classDef->extends; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private function getImplementCe(ClassDef $classDef): array |
|
|
|
|
{ |
|
|
|
|
$list = []; |
|
|
|
|
foreach ($classDef->implements as $interface) { |
|
|
|
|
$list[] = self::PREFIX . 'class_entry_' . $interface; |
|
|
|
|
$list[] = self::PREFIX.'class_entry_'.$interface; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $list; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected function doConvert(string $phpCode): string |
|
|
|
|
{ |
|
|
|
|
$this->climate->info('convert: ' . $this->file); |
|
|
|
|
$this->climate->info('convert: '.$this->file); |
|
|
|
|
|
|
|
|
|
$ast = $this->parser->parse($phpCode); |
|
|
|
|
$traverser = new NodeTraverser(); |
|
|
|
|
@ -296,16 +304,16 @@ class Translator extends Preprocessor |
|
|
|
|
$cppCode .= $this->parseClass($v); |
|
|
|
|
break; |
|
|
|
|
case 'Stmt_Use': |
|
|
|
|
$cppCode .= $this->parseUse($v) . PHP_EOL; |
|
|
|
|
$cppCode .= $this->parseUse($v).PHP_EOL; |
|
|
|
|
break; |
|
|
|
|
case 'Stmt_Function': |
|
|
|
|
$cppCode .= $this->parseFunction($v) . PHP_EOL; |
|
|
|
|
$cppCode .= $this->parseFunction($v).PHP_EOL; |
|
|
|
|
break; |
|
|
|
|
case 'Stmt_Const': |
|
|
|
|
$this->parseConstDef($v) . PHP_EOL; |
|
|
|
|
$this->parseConstDef($v).PHP_EOL; |
|
|
|
|
break; |
|
|
|
|
case 'Stmt_Interface': |
|
|
|
|
$this->parseInterface($v) . PHP_EOL; |
|
|
|
|
$this->parseInterface($v).PHP_EOL; |
|
|
|
|
break; |
|
|
|
|
case 'Stmt_Nop': |
|
|
|
|
break; |
|
|
|
|
@ -326,7 +334,7 @@ class Translator extends Preprocessor |
|
|
|
|
$cppCode .= $this->genFunctionWrapper($functionDef); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $this->genIncludeHeaderFiles() . $cppCode; |
|
|
|
|
return $this->genIncludeHeaderFiles().$cppCode; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function preprocessArgvAdvanced(): void |
|
|
|
|
@ -334,7 +342,7 @@ class Translator extends Preprocessor |
|
|
|
|
global $argv; |
|
|
|
|
$processed = [$argv[0]]; |
|
|
|
|
|
|
|
|
|
for ($i = 1; $i < count($argv); $i++) { |
|
|
|
|
for ($i = 1; $i < count($argv); ++$i) { |
|
|
|
|
$arg = $argv[$i]; |
|
|
|
|
if (preg_match('/^-([a-zA-Z])(.+)$/', $arg, $matches)) { |
|
|
|
|
$option = $matches[1]; |
|
|
|
|
@ -358,26 +366,31 @@ class Translator extends Preprocessor |
|
|
|
|
$lines[] = '#include <phpx.h>'; |
|
|
|
|
$lines[] = PHP_EOL; |
|
|
|
|
foreach ($this->globalVars as $name => $type) { |
|
|
|
|
$lines[] = 'extern ' . self::TYPE_VAR . ' ' . $name . ';'; |
|
|
|
|
$lines[] = 'extern '.self::TYPE_VAR.' '.$name.';'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// property offset |
|
|
|
|
foreach ($this->classes as $classDef) { |
|
|
|
|
foreach ($classDef->properties as $propertyDef) { |
|
|
|
|
$lines[] = 'extern uint32_t ' . self::PREFIX . $this->getPropertyOffset($propertyDef->name, $classDef->name, $classDef->namespace) . ';'; |
|
|
|
|
$lines[] = 'extern uint32_t '.self::PREFIX.$this->getPropertyOffset($propertyDef->name, $classDef->name, $classDef->namespace).';'; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$literalStringsCount = count($this->literalStrings); |
|
|
|
|
$lines[] = 'extern php::Var ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '];' . PHP_EOL; |
|
|
|
|
$code = implode(PHP_EOL, $lines) . PHP_EOL . PHP_EOL; |
|
|
|
|
$lines[] = 'extern php::Var '.self::LITERAL_STRINGS.'['.$literalStringsCount.'];'.PHP_EOL; |
|
|
|
|
|
|
|
|
|
$classEntryCount = count($this->classMap); |
|
|
|
|
$lines[] = 'extern zend_class_entry *'.self::PREFIX.self::CLASS_ENTRY_MAP.'['.$classEntryCount.'];'.PHP_EOL; |
|
|
|
|
|
|
|
|
|
$code = implode(PHP_EOL, $lines).PHP_EOL.PHP_EOL; |
|
|
|
|
$this->writeFile($file, $code); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private function render(string $template): string |
|
|
|
|
{ |
|
|
|
|
ob_start(); |
|
|
|
|
include __DIR__ . '/../template/' . $template; |
|
|
|
|
include __DIR__.'/../template/'.$template; |
|
|
|
|
|
|
|
|
|
return ob_get_clean(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -428,7 +441,7 @@ class Translator extends Preprocessor |
|
|
|
|
$implements = $classDef->implements; |
|
|
|
|
if ($implements) { |
|
|
|
|
foreach ($implements as $interface) { |
|
|
|
|
$tmpCe = self::PREFIX . 'class_entry_' . $interface; |
|
|
|
|
$tmpCe = self::PREFIX.'class_entry_'.$interface; |
|
|
|
|
if (!isset($this->interfaces[$interface])) { |
|
|
|
|
$sorter->add($tmpCe); |
|
|
|
|
} |
|
|
|
|
@ -450,7 +463,7 @@ class Translator extends Preprocessor |
|
|
|
|
|
|
|
|
|
public function genExtension(string $file): void |
|
|
|
|
{ |
|
|
|
|
if ($this->buildMode == 'bin') { |
|
|
|
|
if ('bin' == $this->buildMode) { |
|
|
|
|
if (!isset($this->nativeFunctions['main'])) { |
|
|
|
|
$this->climate->red('When the build mode is a binary executable file, the `main()` function must be defined'); |
|
|
|
|
exit(1); |
|
|
|
|
@ -473,16 +486,18 @@ class Translator extends Preprocessor |
|
|
|
|
if (file_exists($objectFile) and filemtime($objectFile) > filemtime($cppFile)) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function compileFile(string $cppFile, string $objectFile): void |
|
|
|
|
{ |
|
|
|
|
if ($this->hasObjectFileCache($cppFile)) { |
|
|
|
|
$this->climate->darkGray('skip: ' . $cppFile . ', cache exists'); |
|
|
|
|
$this->climate->darkGray('skip: '.$cppFile.', cache exists'); |
|
|
|
|
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
$cmd = $this->cppCompiler . ' -c ' . $cppFile . ' -o ' . $objectFile; |
|
|
|
|
$cmd = $this->cppCompiler.' -c '.$cppFile.' -o '.$objectFile; |
|
|
|
|
$this->addCompilationOption($cmd, false); |
|
|
|
|
$this->climate->comment($cmd); |
|
|
|
|
shell_exec($cmd); |
|
|
|
|
@ -492,10 +507,10 @@ class Translator extends Preprocessor |
|
|
|
|
{ |
|
|
|
|
$objectList = implode(' ', $objectFiles); |
|
|
|
|
$targetFile = $this->targetName; |
|
|
|
|
if ($this->buildMode == 'ext' and !str_ends_with($targetFile, '.so')) { |
|
|
|
|
if ('ext' == $this->buildMode and !str_ends_with($targetFile, '.so')) { |
|
|
|
|
$targetFile .= '.so'; |
|
|
|
|
} |
|
|
|
|
$linkCmd = $this->cppCompiler . ' ' . $objectList . ' -o ' . $targetFile; |
|
|
|
|
$linkCmd = $this->cppCompiler.' '.$objectList.' -o '.$targetFile; |
|
|
|
|
$this->addCompilationOption($linkCmd, true); |
|
|
|
|
$this->climate->comment($linkCmd); |
|
|
|
|
shell_exec($linkCmd); |
|
|
|
|
@ -503,12 +518,12 @@ class Translator extends Preprocessor |
|
|
|
|
|
|
|
|
|
public function genFunctionDeclaration(string $file): void |
|
|
|
|
{ |
|
|
|
|
$code = '#include <phpx.h>' . PHP_EOL; |
|
|
|
|
$code = '#include <phpx.h>'.PHP_EOL; |
|
|
|
|
/** |
|
|
|
|
* @var FunctionDef $func |
|
|
|
|
*/ |
|
|
|
|
foreach ($this->nativeFunctions as $name => $func) { |
|
|
|
|
$code .= 'extern ' . $func->returnType . ' ' . self::PREFIX . $name . '('; |
|
|
|
|
$code .= 'extern '.$func->returnType.' '.self::PREFIX.$name.'('; |
|
|
|
|
$argInfoList = $func->argInfoList; |
|
|
|
|
if ($argInfoList) { |
|
|
|
|
$list = []; |
|
|
|
|
@ -516,22 +531,24 @@ class Translator extends Preprocessor |
|
|
|
|
$list[] = 'php::Object &this_'; |
|
|
|
|
} |
|
|
|
|
foreach ($argInfoList as $argInfo) { |
|
|
|
|
$arg = $argInfo->type . ' ' . $argInfo->name; |
|
|
|
|
$arg = $argInfo->type.' '.$argInfo->name; |
|
|
|
|
if ($argInfo->default) { |
|
|
|
|
$arg .= ' = ' . $argInfo->default; |
|
|
|
|
$arg .= ' = '.$argInfo->default; |
|
|
|
|
} |
|
|
|
|
$list[] = $arg; |
|
|
|
|
} |
|
|
|
|
$code .= implode(', ', $list); |
|
|
|
|
} |
|
|
|
|
$code .= ');' . PHP_EOL; |
|
|
|
|
$code .= ');'.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$code .= PHP_EOL; |
|
|
|
|
foreach ($this->nativeConstants as $name => $constant) { |
|
|
|
|
$code .= 'extern ' . $constant->type . ' ' . $name . ';' . PHP_EOL; |
|
|
|
|
$code .= 'extern '.$constant->type.' '.$name.';'.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$code .= 'extern zend_class_entry *php_get_class_entry(int class_id, const char *class_name);'.PHP_EOL; |
|
|
|
|
|
|
|
|
|
$this->writeFile($file, $code); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -555,10 +572,10 @@ class Translator extends Preprocessor |
|
|
|
|
if ($this->useCppNamespace) { |
|
|
|
|
$ns = explode('\\', $ns); |
|
|
|
|
$ns = array_filter($ns, function ($v) { |
|
|
|
|
return $v !== ''; |
|
|
|
|
return '' !== $v; |
|
|
|
|
}); |
|
|
|
|
foreach ($ns as $name) { |
|
|
|
|
$code .= 'namespace ' . $name . ' {' . PHP_EOL; |
|
|
|
|
$code .= 'namespace '.$name.' {'.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
$ns_end = str_repeat('}', count($ns)); |
|
|
|
|
$this->namespace = implode('::', $ns); |
|
|
|
|
@ -574,13 +591,13 @@ class Translator extends Preprocessor |
|
|
|
|
$code .= $this->parseClass($v2); |
|
|
|
|
break; |
|
|
|
|
case 'Stmt_Const': |
|
|
|
|
$this->parseConstDef($v2) . PHP_EOL; |
|
|
|
|
$this->parseConstDef($v2).PHP_EOL; |
|
|
|
|
break; |
|
|
|
|
case 'Stmt_Function': |
|
|
|
|
$code .= $this->parseFunction($v2) . PHP_EOL; |
|
|
|
|
$code .= $this->parseFunction($v2).PHP_EOL; |
|
|
|
|
break; |
|
|
|
|
case 'Stmt_Use': |
|
|
|
|
$code .= $this->parseUse($v2) . PHP_EOL; |
|
|
|
|
$code .= $this->parseUse($v2).PHP_EOL; |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
abort($v2); |
|
|
|
|
@ -588,18 +605,19 @@ class Translator extends Preprocessor |
|
|
|
|
} |
|
|
|
|
$code .= $ns_end; |
|
|
|
|
$this->resetNamespace(); |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected function genStubFile(string $file): void |
|
|
|
|
{ |
|
|
|
|
$genStubCmd = PHP_BINARY. ' ' . $this->rootPath . '/bin/gen_stub.php -f ' . $file; |
|
|
|
|
$genStubCmd = PHP_BINARY.' '.$this->rootPath.'/bin/gen_stub.php -f '.$file; |
|
|
|
|
$output = shell_exec($genStubCmd); |
|
|
|
|
$this->climate->info('generate stub file: ' . $file); |
|
|
|
|
$this->climate->info('generate stub file: '.$file); |
|
|
|
|
$this->climate->comment($genStubCmd); |
|
|
|
|
$stubFilenameWithoutExtension = str_replace([".stub.php", '.php'], "", $file); |
|
|
|
|
$stubFilenameWithoutExtension = str_replace(['.stub.php', '.php'], '', $file); |
|
|
|
|
$headerFile = $this->getArgInfoHeaderFile($stubFilenameWithoutExtension, true); |
|
|
|
|
if (!str_contains($output, "Saved")) { |
|
|
|
|
if (!str_contains($output, 'Saved')) { |
|
|
|
|
$this->error("failed to generate arginfo header file: `$headerFile`, output: $output"); |
|
|
|
|
} |
|
|
|
|
$this->argInfoHeaderFiles[] = $headerFile; |
|
|
|
|
@ -649,6 +667,7 @@ class Translator extends Preprocessor |
|
|
|
|
$this->fatalError($class, "Class `{$this->class}` uses a non-empty array as the default value for an property, and the constructor must be set."); |
|
|
|
|
} |
|
|
|
|
$this->resetClass(); |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -659,8 +678,8 @@ class Translator extends Preprocessor |
|
|
|
|
|
|
|
|
|
public function getArgInfoHeaderFile(string $stubFilenameWithoutExtension, bool $relative = false): string |
|
|
|
|
{ |
|
|
|
|
$basename = self::PREFIX . basename($stubFilenameWithoutExtension); |
|
|
|
|
$absPath = $this->getIncludeDir() . "/{$basename}_arginfo.h"; |
|
|
|
|
$basename = self::PREFIX.basename($stubFilenameWithoutExtension); |
|
|
|
|
$absPath = $this->getIncludeDir()."/{$basename}_arginfo.h"; |
|
|
|
|
if ($relative) { |
|
|
|
|
return ltrim($this->removeCommonPrefix($this->getIncludeDir(), $absPath), '/'); |
|
|
|
|
} else { |
|
|
|
|
@ -673,7 +692,7 @@ class Translator extends Preprocessor |
|
|
|
|
$code = ''; |
|
|
|
|
$classDef = $this->classDef; |
|
|
|
|
foreach ($classDef->methods as $method) { |
|
|
|
|
$code .= $methodCodes[$method->name] . PHP_EOL; |
|
|
|
|
$code .= $methodCodes[$method->name].PHP_EOL; |
|
|
|
|
} |
|
|
|
|
$code .= PHP_EOL; |
|
|
|
|
|
|
|
|
|
@ -686,28 +705,28 @@ class Translator extends Preprocessor |
|
|
|
|
$callParams = ''; |
|
|
|
|
foreach ($functionDef->argInfoList as $k => $argInfo) { |
|
|
|
|
if ($argInfo->default) { |
|
|
|
|
$argExpr = 'php::getCallArg(' . $k . ', ' . $argInfo->default . ')'; |
|
|
|
|
$argExpr = 'php::getCallArg('.$k.', '.$argInfo->default.')'; |
|
|
|
|
} else { |
|
|
|
|
$argExpr = 'php::getCallArg(' . $k . ')'; |
|
|
|
|
$argExpr = 'php::getCallArg('.$k.')'; |
|
|
|
|
} |
|
|
|
|
$expr = $this->convertExprFromType($argInfo->type, $argExpr); |
|
|
|
|
$cppCode .= $this->getIndent() . $argInfo->type . ' arg_' . $argInfo->name . ' = ' . $expr . ';' . PHP_EOL; |
|
|
|
|
$callParams .= 'arg_' . $argInfo->name . ','; |
|
|
|
|
$cppCode .= $this->getIndent().$argInfo->type.' arg_'.$argInfo->name.' = '.$expr.';'.PHP_EOL; |
|
|
|
|
$callParams .= 'arg_'.$argInfo->name.','; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ($functionDef->method) { |
|
|
|
|
$callParams = $functionDef->argInfoList ? 'this_, ' . rtrim($callParams, ',') : 'this_'; |
|
|
|
|
$callParams = $functionDef->argInfoList ? 'this_, '.rtrim($callParams, ',') : 'this_'; |
|
|
|
|
} else { |
|
|
|
|
$callParams = $functionDef->argInfoList ? rtrim($callParams, ',') : ''; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ($functionDef->returnType !== self::TYPE_VOID) { |
|
|
|
|
$cppCode .= $this->getIndent() . 'auto retval = ' . $fn . '(' . $callParams . ');' . PHP_EOL; |
|
|
|
|
$cppCode .= $this->getIndent() . 'php::move(retval, return_value);' . PHP_EOL; |
|
|
|
|
if (self::TYPE_VOID !== $functionDef->returnType) { |
|
|
|
|
$cppCode .= $this->getIndent().'auto retval = '.$fn.'('.$callParams.');'.PHP_EOL; |
|
|
|
|
$cppCode .= $this->getIndent().'php::move(retval, return_value);'.PHP_EOL; |
|
|
|
|
} else { |
|
|
|
|
$cppCode .= $this->getIndent() . $fn . '(' . $callParams . ');' . PHP_EOL; |
|
|
|
|
$cppCode .= $this->getIndent().$fn.'('.$callParams.');'.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
$cppCode .= '}' . PHP_EOL . PHP_EOL; |
|
|
|
|
$cppCode .= '}'.PHP_EOL.PHP_EOL; |
|
|
|
|
|
|
|
|
|
return $cppCode; |
|
|
|
|
} |
|
|
|
|
@ -715,27 +734,29 @@ class Translator extends Preprocessor |
|
|
|
|
protected function genMethodWrapper(ClassDef $classDef, MethodDef $methodDef): string |
|
|
|
|
{ |
|
|
|
|
$name = $classDef->getNamespacedName(); |
|
|
|
|
$cppCode = 'ZEND_METHOD(' . $name . ', ' . $methodDef->name . '){' . PHP_EOL; |
|
|
|
|
$cppCode .= $this->getIndent() . self::TYPE_OBJECT . ' this_(&execute_data->This);' . PHP_EOL; |
|
|
|
|
$cppCode = 'ZEND_METHOD('.$name.', '.$methodDef->name.'){'.PHP_EOL; |
|
|
|
|
$cppCode .= $this->getIndent().self::TYPE_OBJECT.' this_(&execute_data->This);'.PHP_EOL; |
|
|
|
|
|
|
|
|
|
foreach ($classDef->properties as $property) { |
|
|
|
|
if ($property->type === self::TYPE_ARRAY and $property->default and $property->default !== self::TYPE_ARRAY . '{}') { |
|
|
|
|
$propOffset = self::PREFIX . $this->getPropertyOffset($property->name, $classDef->name, $classDef->namespace); |
|
|
|
|
$cppCode .= $this->getIndent() . 'this_.getPropertyIndirect(' . $propOffset . ') = ' . $property->default . ';' . PHP_EOL; |
|
|
|
|
if (self::TYPE_ARRAY === $property->type and $property->default and $property->default !== self::TYPE_ARRAY.'{}') { |
|
|
|
|
$propOffset = self::PREFIX.$this->getPropertyOffset($property->name, $classDef->name, $classDef->namespace); |
|
|
|
|
$cppCode .= $this->getIndent().'this_.getPropertyIndirect('.$propOffset.') = '.$property->default.';'.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$fn = self::PREFIX . $this->getNativeMethodName($classDef, $methodDef); |
|
|
|
|
$fn = self::PREFIX.$this->getNativeMethodName($classDef, $methodDef); |
|
|
|
|
$cppCode .= $this->genWrapperFunctionArgs($fn, $methodDef->functionDef); |
|
|
|
|
|
|
|
|
|
return $cppCode; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private function genFunctionWrapper(FunctionDef $functionDef): string |
|
|
|
|
{ |
|
|
|
|
$name = $functionDef->name; |
|
|
|
|
$cppCode = 'ZEND_FUNCTION(' . $name . '){' . PHP_EOL; |
|
|
|
|
$fn = self::PREFIX . $this->getNativeName($functionDef->name); |
|
|
|
|
$cppCode = 'ZEND_FUNCTION('.$name.'){'.PHP_EOL; |
|
|
|
|
$fn = self::PREFIX.$this->getNativeName($functionDef->name); |
|
|
|
|
$cppCode .= $this->genWrapperFunctionArgs($fn, $functionDef); |
|
|
|
|
|
|
|
|
|
return $cppCode; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -745,12 +766,11 @@ class Translator extends Preprocessor |
|
|
|
|
$name = $classDef->getNamespacedName(); |
|
|
|
|
$argsDef = $this->getRegisterClassFunctionArgDef($classDef); |
|
|
|
|
$param = $this->getRegisterClassFunctionArgs($classDef); |
|
|
|
|
$cppCode .= 'zend_class_entry *' . $this->getRegisterClassFunction($name) . '(' . $argsDef . ') {' . PHP_EOL; |
|
|
|
|
$cppCode .= $this->getIndent() . 'return register_class_' . $name . '(' . $param . ');' . PHP_EOL; |
|
|
|
|
$cppCode .= '}' . PHP_EOL . PHP_EOL; |
|
|
|
|
$cppCode .= 'zend_class_entry *'.$this->getRegisterClassFunction($name).'('.$argsDef.') {'.PHP_EOL; |
|
|
|
|
$cppCode .= $this->getIndent().'return register_class_'.$name.'('.$param.');'.PHP_EOL; |
|
|
|
|
$cppCode .= '}'.PHP_EOL.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected function genClassWrapper(ClassDef|InterfaceDef $classDef): string |
|
|
|
|
{ |
|
|
|
|
$cppCode = ''; |
|
|
|
|
@ -768,7 +788,7 @@ class Translator extends Preprocessor |
|
|
|
|
|
|
|
|
|
private function genClassNative(): string |
|
|
|
|
{ |
|
|
|
|
$code = 'class ' . $this->class . ' { '; |
|
|
|
|
$code = 'class '.$this->class.' { '; |
|
|
|
|
|
|
|
|
|
$publicMethods = []; |
|
|
|
|
$protectedMethods = []; |
|
|
|
|
@ -815,36 +835,37 @@ class Translator extends Preprocessor |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ($privateConstants) { |
|
|
|
|
$code .= 'private:' . PHP_EOL; |
|
|
|
|
$code .= 'private:'.PHP_EOL; |
|
|
|
|
$code .= $this->genClassConstantList($privateConstants); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ($protectedConstants) { |
|
|
|
|
$code .= 'protected:' . PHP_EOL; |
|
|
|
|
$code .= 'protected:'.PHP_EOL; |
|
|
|
|
$code .= $this->genClassConstantList($protectedConstants); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ($publicConstants) { |
|
|
|
|
$code .= 'public:' . PHP_EOL; |
|
|
|
|
$code .= 'public:'.PHP_EOL; |
|
|
|
|
$code .= $this->genClassConstantList($publicConstants); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ($privateProperties) { |
|
|
|
|
$code .= 'private:' . PHP_EOL; |
|
|
|
|
$code .= 'private:'.PHP_EOL; |
|
|
|
|
$code .= $this->genClassPropertyList($privateProperties); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ($protectedProperties) { |
|
|
|
|
$code .= 'protected:' . PHP_EOL; |
|
|
|
|
$code .= 'protected:'.PHP_EOL; |
|
|
|
|
$code .= $this->genClassPropertyList($protectedProperties); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ($publicProperties) { |
|
|
|
|
$code .= 'public:' . PHP_EOL; |
|
|
|
|
$code .= 'public:'.PHP_EOL; |
|
|
|
|
$code .= $this->genClassPropertyList($publicProperties); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$code .= '};' . PHP_EOL . PHP_EOL; |
|
|
|
|
$code .= '};'.PHP_EOL.PHP_EOL; |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -853,9 +874,10 @@ class Translator extends Preprocessor |
|
|
|
|
$headers = array_merge($this->globalHeaders, $this->localHeaders); |
|
|
|
|
$lines = []; |
|
|
|
|
foreach ($headers as $header) { |
|
|
|
|
$lines[] = '#include <' . $header . '>'; |
|
|
|
|
$lines[] = '#include <'.$header.'>'; |
|
|
|
|
} |
|
|
|
|
return implode(PHP_EOL, $lines) . PHP_EOL . PHP_EOL; |
|
|
|
|
|
|
|
|
|
return implode(PHP_EOL, $lines).PHP_EOL.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -865,14 +887,15 @@ class Translator extends Preprocessor |
|
|
|
|
{ |
|
|
|
|
$code = ''; |
|
|
|
|
foreach ($list as $const) { |
|
|
|
|
$code .= $this->getIndent() . $this->genClassConstant($const); |
|
|
|
|
$code .= $this->getIndent().$this->genClassConstant($const); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected function genClassConstant(ConstantDef $const): string |
|
|
|
|
{ |
|
|
|
|
return 'static const ' . $const->type . ' ' . $const->name . ';' . PHP_EOL; |
|
|
|
|
return 'static const '.$const->type.' '.$const->name.';'.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -882,29 +905,32 @@ class Translator extends Preprocessor |
|
|
|
|
{ |
|
|
|
|
$code = ''; |
|
|
|
|
foreach ($list as $prop) { |
|
|
|
|
$code .= $this->getIndent() . $this->genClassProperty($prop); |
|
|
|
|
$code .= $this->getIndent().$this->genClassProperty($prop); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected function genClassProperty(PropertyDef $prop): string |
|
|
|
|
{ |
|
|
|
|
$code = $prop->type . ' ' . $prop->name; |
|
|
|
|
$code = $prop->type.' '.$prop->name; |
|
|
|
|
if ($prop->default) { |
|
|
|
|
$code .= ' = ' . $prop->default; |
|
|
|
|
$code .= ' = '.$prop->default; |
|
|
|
|
} |
|
|
|
|
return $code . ';' . PHP_EOL; |
|
|
|
|
|
|
|
|
|
return $code.';'.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected function genFunction(string $name, string $returnType, array $args = [], array $lines = []): string |
|
|
|
|
{ |
|
|
|
|
$_args = []; |
|
|
|
|
foreach ($args as $arg => $type) { |
|
|
|
|
$_args[] = $type . ' ' . $arg; |
|
|
|
|
$_args[] = $type.' '.$arg; |
|
|
|
|
} |
|
|
|
|
$code = $returnType . ' ' . $name . '(' . implode(', ', $_args) . ') {' . PHP_EOL; |
|
|
|
|
$code .= implode(PHP_EOL, $lines) . PHP_EOL; |
|
|
|
|
$code .= '}' . PHP_EOL; |
|
|
|
|
$code = $returnType.' '.$name.'('.implode(', ', $_args).') {'.PHP_EOL; |
|
|
|
|
$code .= implode(PHP_EOL, $lines).PHP_EOL; |
|
|
|
|
$code .= '}'.PHP_EOL; |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -915,7 +941,7 @@ class Translator extends Preprocessor |
|
|
|
|
foreach ($v->consts as $const) { |
|
|
|
|
$constName = $this->parseIdentifier($const->name); |
|
|
|
|
if (isset($this->classDef->constants[$constName])) { |
|
|
|
|
$this->fatalError($const, 'Cannot redefine class constant ' . $this->class . '::' . $constName); |
|
|
|
|
$this->fatalError($const, 'Cannot redefine class constant '.$this->class.'::'.$constName); |
|
|
|
|
} |
|
|
|
|
$constInfo = new ConstantDef($constName, $flags, $type, $this->parseIdentifier($const->value)); |
|
|
|
|
$this->classDef->constants[$constInfo->name] = $constInfo; |
|
|
|
|
@ -930,7 +956,7 @@ class Translator extends Preprocessor |
|
|
|
|
foreach ($v->props as $prop) { |
|
|
|
|
$propDef = new PropertyDef($this->parseIdentifier($prop->name), $flags, $type); |
|
|
|
|
if ($prop->default) { |
|
|
|
|
if ($prop->default->getType() == 'Expr_Array' and count($prop->default->items) > 0) { |
|
|
|
|
if ('Expr_Array' == $prop->default->getType() and count($prop->default->items) > 0) { |
|
|
|
|
$this->classDef->requireCtor = true; |
|
|
|
|
$propDef->type = self::TYPE_ARRAY; |
|
|
|
|
} |
|
|
|
|
@ -961,6 +987,7 @@ class Translator extends Preprocessor |
|
|
|
|
foreach ($implements as $implement) { |
|
|
|
|
$list[] = $this->parseIdentifier($implement); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $list; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -983,36 +1010,36 @@ class Translator extends Preprocessor |
|
|
|
|
$tmpArrayVar = $this->genTmpVarName(); |
|
|
|
|
$this->addLocalVar($tmpArrayVar, self::TYPE_ARRAY); |
|
|
|
|
|
|
|
|
|
$code = 'if (' . $obj . '.instanceOf("IteratorAggregate")) {' . PHP_EOL; |
|
|
|
|
$code .= $this->getIndent() . $tmpVar . ' = ' . $obj . '.exec("getIterator");' . PHP_EOL . '}' . PHP_EOL; |
|
|
|
|
$code .= 'else if (' . $obj . '.instanceOf("Iterator")) {' . PHP_EOL; |
|
|
|
|
$code .= $this->getIndent() . $tmpVar . ' = ' . $obj . ';' . PHP_EOL . '}'. PHP_EOL; |
|
|
|
|
$code = 'if ('.$obj.'.instanceOf("IteratorAggregate")) {'.PHP_EOL; |
|
|
|
|
$code .= $this->getIndent().$tmpVar.' = '.$obj.'.exec("getIterator");'.PHP_EOL.'}'.PHP_EOL; |
|
|
|
|
$code .= 'else if ('.$obj.'.instanceOf("Iterator")) {'.PHP_EOL; |
|
|
|
|
$code .= $this->getIndent().$tmpVar.' = '.$obj.';'.PHP_EOL.'}'.PHP_EOL; |
|
|
|
|
|
|
|
|
|
$code .= 'if (' . $tmpVar . ') {'. PHP_EOL; |
|
|
|
|
$code .= 'if ('.$tmpVar.') {'.PHP_EOL; |
|
|
|
|
|
|
|
|
|
$this->indentLevel++; |
|
|
|
|
$code .= $this->getIndent() . $tmpVar . '.exec("rewind");' . PHP_EOL; |
|
|
|
|
$code .= $this->getIndent() . 'for (;' . $tmpVar . '.exec("valid"); ' . $tmpVar . '.exec("next")) {' . PHP_EOL; |
|
|
|
|
$this->indentLevel++; |
|
|
|
|
++$this->indentLevel; |
|
|
|
|
$code .= $this->getIndent().$tmpVar.'.exec("rewind");'.PHP_EOL; |
|
|
|
|
$code .= $this->getIndent().'for (;'.$tmpVar.'.exec("valid"); '.$tmpVar.'.exec("next")) {'.PHP_EOL; |
|
|
|
|
++$this->indentLevel; |
|
|
|
|
|
|
|
|
|
$valueVar = $this->parseIdentifier($node->valueVar); |
|
|
|
|
$this->checkVar($node, $valueVar); |
|
|
|
|
|
|
|
|
|
$code .= $this->getIndent() . ' ' . $valueVar . ' = ' . $tmpVar . '.exec("current");' . PHP_EOL; |
|
|
|
|
$code .= $this->getIndent().' '.$valueVar.' = '.$tmpVar.'.exec("current");'.PHP_EOL; |
|
|
|
|
if ($node->keyVar) { |
|
|
|
|
$keyVar = $this->parseIdentifier($node->keyVar); |
|
|
|
|
$this->checkVar($node, $keyVar); |
|
|
|
|
$code .= $this->getIndent() . ' ' . $keyVar . ' = ' . $tmpVar . '.exec("key");' . PHP_EOL; |
|
|
|
|
$code .= $this->getIndent().' '.$keyVar.' = '.$tmpVar.'.exec("key");'.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
$code .= $this->parseStmts($node->stmts); |
|
|
|
|
$code .= '}' . PHP_EOL; |
|
|
|
|
$this->indentLevel--; |
|
|
|
|
$code .= $this->getIndent() . '} else {' . PHP_EOL; |
|
|
|
|
$code .= $this->getIndent() . $tmpArrayVar . ' = php::call("get_object_vars", {' . $obj . '});' . PHP_EOL; |
|
|
|
|
$code .= '}'.PHP_EOL; |
|
|
|
|
--$this->indentLevel; |
|
|
|
|
$code .= $this->getIndent().'} else {'.PHP_EOL; |
|
|
|
|
$code .= $this->getIndent().$tmpArrayVar.' = php::call("get_object_vars", {'.$obj.'});'.PHP_EOL; |
|
|
|
|
$code .= $this->parseForeachArray($node, $tmpArrayVar); |
|
|
|
|
$this->indentLevel--; |
|
|
|
|
$code .= '}' . PHP_EOL; |
|
|
|
|
--$this->indentLevel; |
|
|
|
|
$code .= '}'.PHP_EOL; |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|