|
|
|
|
@ -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 |
|
|
|
|
{ |
|
|
|
|
@ -85,6 +84,7 @@ class Translator extends Preprocessor |
|
|
|
|
{ |
|
|
|
|
if ($this->hasCppFileCache($file)) { |
|
|
|
|
$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; |
|
|
|
|
@ -112,7 +113,7 @@ class Translator extends Preprocessor |
|
|
|
|
{ |
|
|
|
|
$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,6 +136,7 @@ class Translator extends Preprocessor |
|
|
|
|
if (empty($depsCeList)) { |
|
|
|
|
return ''; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 'zend_class_entry *'.implode(', zend_class_entry *', $depsCeList); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -162,6 +165,7 @@ class Translator extends Preprocessor |
|
|
|
|
protected function getFilesFromDir(string $path): array |
|
|
|
|
{ |
|
|
|
|
$scanner = new FileScanner($path); |
|
|
|
|
|
|
|
|
|
return $scanner->scan(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -175,7 +179,7 @@ class Translator extends Preprocessor |
|
|
|
|
$list = []; |
|
|
|
|
foreach ($sources as $src) { |
|
|
|
|
$src = trim($src); |
|
|
|
|
if ($src[0] != '/') { |
|
|
|
|
if ('/' != $src[0]) { |
|
|
|
|
$absPath = $projectDir.'/'.$src; |
|
|
|
|
} else { |
|
|
|
|
$absPath = $src; |
|
|
|
|
@ -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,9 +233,9 @@ 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); |
|
|
|
|
@ -238,6 +243,7 @@ class Translator extends Preprocessor |
|
|
|
|
$this->error('Unsupported file type: '.$path); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $list; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -254,6 +260,7 @@ class Translator extends Preprocessor |
|
|
|
|
if (!$classDef->extends) { |
|
|
|
|
return ''; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return self::PREFIX.'class_entry_'.$classDef->extends; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -263,6 +270,7 @@ class Translator extends Preprocessor |
|
|
|
|
foreach ($classDef->implements as $interface) { |
|
|
|
|
$list[] = self::PREFIX.'class_entry_'.$interface; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $list; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -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]; |
|
|
|
|
@ -370,6 +378,10 @@ class Translator extends Preprocessor |
|
|
|
|
|
|
|
|
|
$literalStringsCount = count($this->literalStrings); |
|
|
|
|
$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); |
|
|
|
|
} |
|
|
|
|
@ -378,6 +390,7 @@ class Translator extends Preprocessor |
|
|
|
|
{ |
|
|
|
|
ob_start(); |
|
|
|
|
include __DIR__.'/../template/'.$template; |
|
|
|
|
|
|
|
|
|
return ob_get_clean(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -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,6 +486,7 @@ class Translator extends Preprocessor |
|
|
|
|
if (file_exists($objectFile) and filemtime($objectFile) > filemtime($cppFile)) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -480,6 +494,7 @@ class Translator extends Preprocessor |
|
|
|
|
{ |
|
|
|
|
if ($this->hasObjectFileCache($cppFile)) { |
|
|
|
|
$this->climate->darkGray('skip: '.$cppFile.', cache exists'); |
|
|
|
|
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
$cmd = $this->cppCompiler.' -c '.$cppFile.' -o '.$objectFile; |
|
|
|
|
@ -492,7 +507,7 @@ 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; |
|
|
|
|
@ -532,6 +547,8 @@ class Translator extends Preprocessor |
|
|
|
|
$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,7 +572,7 @@ 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; |
|
|
|
|
@ -588,6 +605,7 @@ class Translator extends Preprocessor |
|
|
|
|
} |
|
|
|
|
$code .= $ns_end; |
|
|
|
|
$this->resetNamespace(); |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -597,9 +615,9 @@ class Translator extends Preprocessor |
|
|
|
|
$output = shell_exec($genStubCmd); |
|
|
|
|
$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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -701,7 +720,7 @@ class Translator extends Preprocessor |
|
|
|
|
$callParams = $functionDef->argInfoList ? rtrim($callParams, ',') : ''; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ($functionDef->returnType !== self::TYPE_VOID) { |
|
|
|
|
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 { |
|
|
|
|
@ -719,7 +738,7 @@ class Translator extends Preprocessor |
|
|
|
|
$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 . '{}') { |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
@ -727,6 +746,7 @@ class Translator extends Preprocessor |
|
|
|
|
|
|
|
|
|
$fn = self::PREFIX.$this->getNativeMethodName($classDef, $methodDef); |
|
|
|
|
$cppCode .= $this->genWrapperFunctionArgs($fn, $methodDef->functionDef); |
|
|
|
|
|
|
|
|
|
return $cppCode; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -736,6 +756,7 @@ class Translator extends Preprocessor |
|
|
|
|
$cppCode = 'ZEND_FUNCTION('.$name.'){'.PHP_EOL; |
|
|
|
|
$fn = self::PREFIX.$this->getNativeName($functionDef->name); |
|
|
|
|
$cppCode .= $this->genWrapperFunctionArgs($fn, $functionDef); |
|
|
|
|
|
|
|
|
|
return $cppCode; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -750,7 +771,6 @@ class Translator extends Preprocessor |
|
|
|
|
$cppCode .= '}'.PHP_EOL.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected function genClassWrapper(ClassDef|InterfaceDef $classDef): string |
|
|
|
|
{ |
|
|
|
|
$cppCode = ''; |
|
|
|
|
@ -845,6 +865,7 @@ class Translator extends Preprocessor |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$code .= '};'.PHP_EOL.PHP_EOL; |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -855,6 +876,7 @@ class Translator extends Preprocessor |
|
|
|
|
foreach ($headers as $header) { |
|
|
|
|
$lines[] = '#include <'.$header.'>'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return implode(PHP_EOL, $lines).PHP_EOL.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -867,6 +889,7 @@ class Translator extends Preprocessor |
|
|
|
|
foreach ($list as $const) { |
|
|
|
|
$code .= $this->getIndent().$this->genClassConstant($const); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -884,6 +907,7 @@ class Translator extends Preprocessor |
|
|
|
|
foreach ($list as $prop) { |
|
|
|
|
$code .= $this->getIndent().$this->genClassProperty($prop); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -893,6 +917,7 @@ class Translator extends Preprocessor |
|
|
|
|
if ($prop->default) { |
|
|
|
|
$code .= ' = '.$prop->default; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $code.';'.PHP_EOL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -905,6 +930,7 @@ class Translator extends Preprocessor |
|
|
|
|
$code = $returnType.' '.$name.'('.implode(', ', $_args).') {'.PHP_EOL; |
|
|
|
|
$code .= implode(PHP_EOL, $lines).PHP_EOL; |
|
|
|
|
$code .= '}'.PHP_EOL; |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -990,10 +1017,10 @@ class Translator extends Preprocessor |
|
|
|
|
|
|
|
|
|
$code .= 'if ('.$tmpVar.') {'.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++; |
|
|
|
|
++$this->indentLevel; |
|
|
|
|
|
|
|
|
|
$valueVar = $this->parseIdentifier($node->valueVar); |
|
|
|
|
$this->checkVar($node, $valueVar); |
|
|
|
|
@ -1006,11 +1033,11 @@ class Translator extends Preprocessor |
|
|
|
|
} |
|
|
|
|
$code .= $this->parseStmts($node->stmts); |
|
|
|
|
$code .= '}'.PHP_EOL; |
|
|
|
|
$this->indentLevel--; |
|
|
|
|
--$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--; |
|
|
|
|
--$this->indentLevel; |
|
|
|
|
$code .= '}'.PHP_EOL; |
|
|
|
|
|
|
|
|
|
return $code; |
|
|
|
|
|