diff --git a/bin/compiler.php b/bin/compiler.php index 08c3f00a..2d829cd6 100755 --- a/bin/compiler.php +++ b/bin/compiler.php @@ -1,6 +1,7 @@ #!/usr/bin/env php removeCommonPrefix($cwd, $path), '/'); diff --git a/src/Php/Entity/ConstantDef.php b/src/Php/Entity/ConstantDef.php index 550a1cab..cdbbfedf 100644 --- a/src/Php/Entity/ConstantDef.php +++ b/src/Php/Entity/ConstantDef.php @@ -8,6 +8,8 @@ namespace PhpAot\Php\Entity; +use PhpParser\NodeAbstract; + class ConstantDef { public string $name; @@ -16,6 +18,7 @@ class ConstantDef public string $value; public string $arrayExpr = ''; public string $class = ''; + public ?NodeAbstract $valueExpr = null; public function __construct(string $name, int $flags, string $type, string $value) { diff --git a/src/Php/Generator/Utils.php b/src/Php/Generator/Utils.php index 88e40035..13c72fa0 100644 --- a/src/Php/Generator/Utils.php +++ b/src/Php/Generator/Utils.php @@ -13,6 +13,17 @@ use PhpAot\Php\Constants; trait Utils { + protected function genCValue(mixed $value): mixed + { + if (is_int($value) or is_float($value)) { + return $value; + } elseif (is_string($value)) { + return $this->genCharPtr($value); + } else { + $this->error('Unsupported constant type: ' . gettype($value)); + } + } + protected function genCharPtr(string $str, bool $escape = false): string { return '"' . ($escape ? $this->escapeString($str) : $str) . '"'; diff --git a/src/Php/Preprocessor.php b/src/Php/Preprocessor.php index 0a5b9841..1032a45d 100644 --- a/src/Php/Preprocessor.php +++ b/src/Php/Preprocessor.php @@ -518,6 +518,7 @@ class Preprocessor extends CompilerBase $constValue = $this->parseIdentifier($const->value); $constInfo = new ConstantDef($constName, $flags, $type, $constValue); + $constInfo->valueExpr = $const->value; if ($this->context->beforeStmtLines) { $arrayExpr = ''; diff --git a/src/Php/Translator.php b/src/Php/Translator.php index b196a6cb..e123428d 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -21,6 +21,7 @@ use PhpAot\Php\Exception\Unsupported; use PhpParser\Modifiers; use PhpParser\Node; use PhpParser\Node\Stmt\Foreach_; +use PhpParser\NodeAbstract; use PhpParser\NodeTraverser; use Symfony\Component\Yaml\Yaml; @@ -106,7 +107,6 @@ class Translator extends Preprocessor return $this->getCppFile($file); } $phpCode = $this->loadFile($file); - $this->genStubFile($this->file); $this->localHeaders = []; while (true) { try { @@ -114,7 +114,8 @@ class Translator extends Preprocessor $cppFile = $this->getCppFile($file); $this->save($cppCode, $cppFile); $this->phpSrcFiles[] = $file; - + // 生成 stub 文件,依赖 convert 阶段的 use 等信息 + $this->genStubFile($this->file); return $cppFile; } catch (Redo $e) { continue; @@ -646,6 +647,11 @@ class Translator extends Preprocessor return $list; } + public function getDefinedConstants(): array + { + return $this->internalConstants; + } + protected function getInternalCeInfo(string $ce): array { return [ @@ -856,14 +862,13 @@ class Translator extends Preprocessor $stubFilenameWithoutExtension = str_replace(['.stub.php', '.php'], '', $file); $headerFile = $this->getArgInfoHeaderFile($stubFilenameWithoutExtension, true); - $genStubCmd = PHP_BINARY . ' ' . $this->rootPath . '/bin/gen_stub.php -f -o ' . $this->getIncludeDir() . '/' . $headerFile . ' ' . $file; - $output = shell_exec($genStubCmd); - $this->climate->info('generate stub file: ' . $this->getRelativePath($file)); - $this->climate->comment($genStubCmd); - - if (!str_contains($output, 'Saved')) { - $this->error("failed to generate arginfo header file: `{$headerFile}`, output: {$output}"); + try { + generateStubFile($file, $this->getIncludeDir() . '/' . $headerFile, true); + } catch (\Throwable $e) { + $this->error("failed to generate arginfo header file: `{$headerFile}`, Error: {$e->getMessage()}"); } + + $this->climate->info('generate stub file: ' . $this->getRelativePath($file)); if ($this->useRegisterSymbolsFn) { preg_match('/php_(.*)_arginfo.h/', $headerFile, $matches); $registerSymbolFn = 'register_' . $matches[1] . '_symbols'; @@ -1301,4 +1306,48 @@ class Translator extends Preprocessor return $code; } + + /** + * 仅用于 gen_stub 脚本 + * @param NodeAbstract $expr + * @param string $class + * @param string $name + * @return mixed + * @throws \Exception + */ + public function getClassConstValue(NodeAbstract $expr, string $class, string $name): mixed + { + $class = $this->getNamespacedClassName($class); + $nativeConst = $this->findNativeClassConst($expr, $class, $name); + if ($nativeConst and $expr->hasAttribute('nativeConst')) { + $constDef = $expr->getAttribute('nativeConst'); + return $this->genCValue($constDef->valueExpr->value); + } + throw new \Exception("Class constant `$class::$name` not found"); + } + + public function getConstValue(string $name): mixed + { + if ($this->isInternalConstant($name)) { + $value = $this->internalConstants[$name]; + if (is_int($value)) { + $expr = strval($value); + if ($value === PHP_INT_MIN) { + $expr = 'LONG_MIN'; + } elseif ($value === PHP_INT_MAX) { + $expr = 'LONG_MAX'; + } else { + $expr = $expr . 'L'; + } + } elseif (is_float($value)) { + return $value; + } elseif (is_string($value)) { + return $this->genCharPtr($value); + } else { + $this->error('Unsupported constant type: ' . gettype($value)); + } + return $expr; + } + throw new \Exception('Constant ' . $name . ' not found'); + } } diff --git a/bin/gen_stub.php b/src/gen_stub.php similarity index 99% rename from bin/gen_stub.php rename to src/gen_stub.php index 0364cde8..87b817ea 100755 --- a/bin/gen_stub.php +++ b/src/gen_stub.php @@ -1,5 +1,5 @@ -#!/usr/bin/env php -writeFile($filename, $content); - echo "Saved $filename\n"; + getTranslator()->writeFile($filename, $content); } /** @@ -149,8 +147,7 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly = return $fileInfo; } catch (Exception $e) { - echo "In $stubFile:\n{$e->getMessage()}\n"; - exit(1); + throw new RuntimeException("In " . getTranslator()->getRelativePath($stubFile) . ": {$e->getMessage()}"); } } @@ -2309,7 +2306,7 @@ class EvaluatedValue if (isset($allConstInfos[$constName])) { return $allConstInfos[$constName]->getValue($allConstInfos)->value; } else { - throw new Exception("Class constant `$constName` not found"); + return getTranslator()->getClassConstValue($expr, ClassInfo::$currentClass->name->toString(), $expr->name->toString()); } } elseif ($expr->name->__toString() === 'class') { return $class; @@ -2337,6 +2334,7 @@ class EvaluatedValue } elseif ($constType->isFloat()) { return M_PI; } elseif ($constType->isString()) { + var_dump($const); return $const->name; } elseif ($constType->isArray()) { return []; @@ -2346,7 +2344,7 @@ class EvaluatedValue return null; } - global $definedConstants; + $definedConstants = getTranslator()->getDefinedConstants(); if (isset($definedConstants[$constName])) { $constValue = $definedConstants[$constName]; if (is_scalar($constValue)) { @@ -2448,28 +2446,17 @@ class EvaluatedValue $this->expr->name->__toString() === 'class') { $expr = '"' . addcslashes($this->expr->class->name, '\\') . '"'; } else { - var_dump($this->value); + return $this->value; } } elseif (!($this->expr instanceof String_)) { + if ($this->expr instanceof Expr\ConstFetch) { + return getTranslator()->getConstValue($this->expr->name->toString()); + } throw new Exception("Expression at line " . $this->expr->getStartLine() . " must be a scalar string"); } $expr = preg_replace("/(^'|'$)/", '"', $expr); - } elseif ($this->type->isInt() or $this->type->isFloat()) { + } elseif ($this->type->isInt() or $this->type->isFloat() or $this->expr instanceof Expr\ConstFetch) { return strval($this->value); - } else { - if ($this->expr instanceof Expr\ConstFetch) { - $value = constant($this->expr->name->__toString()); - if (is_int($value)) { - $expr = strval($value); - if ($value === PHP_INT_MIN) { - $expr = 'LONG_MIN'; - } elseif ($value === PHP_INT_MAX) { - $expr = 'LONG_MAX'; - } else { - $expr = $expr . 'L'; - } - } - } } return $expr[0] == '"' ? $expr : preg_replace('(\bnull\b)', 'NULL', str_replace('\\', '', $expr)); } @@ -6172,20 +6159,17 @@ function initPhpParser() { $isInitialized = true; } -function main() +function getTranslator(): Translator { - global $argv, $argc, $translator, $definedConstants; - - error_reporting(E_ALL & ~E_DEPRECATED); - ini_set("precision", "-1"); - require __DIR__ . '/bootstrap.php'; - - $translator = new PhpAot\Php\Translator(ROOT_PATH); - $translator->setIndent("\t"); - $translator->setIndentLevel(1); - - $definedConstants = get_defined_constants(); + global $translator; + return $translator; +} +/** + * @throws Exception + */ +function generateStubFile(string $stubFile, string $objectFile, bool $forceRegeneration): void +{ $opt_index = 0; $options = getopt( "fho:", @@ -6208,15 +6192,15 @@ function main() $replaceMethodSynopses = isset($options["replace-methodsynopses"]); $generateOptimizerInfo = isset($options["generate-optimizer-info"]); - $context->forceRegeneration = isset($options["f"]) || isset($options["force-regeneration"]); - $context->objectFile = $options["o"] ?? ''; + $context->forceRegeneration = $forceRegeneration; + $context->objectFile = $objectFile; $context->forceParse = $context->forceRegeneration || $printParameterStats || $verify || $verifyManual || $replacePredefinedConstants || $generateClassSynopses || $generateOptimizerInfo || $replaceClassSynopses || $generateMethodSynopses || $replaceMethodSynopses; if (isset($options["h"]) || isset($options["help"])) { die("\nUsage: gen_stub.php [ -f | --force-regeneration ] [ --replace-predefined-constants ] [ --generate-classsynopses ] [ --replace-classsynopses ] [ --generate-methodsynopses ] [ --replace-methodsynopses ] [ --parameter-stats ] [ --verify ] [ --verify-manual ] [ --generate-optimizer-info ] [ -h | --help ] [ name.stub.php | directory ] [ directory ]\n\n"); } - $locations = array_slice($argv, $opt_index); + $locations = [$stubFile]; $locationCount = count($locations); if ($replacePredefinedConstants && $locationCount < 2) { die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --replace-predefined-constants ./ ../doc-en/\n"); @@ -6388,10 +6372,8 @@ function main() } } - echo implode("\n", $errors); if (!empty($errors)) { - echo "\n"; - exit(1); + throw new Exception("Errors found: " . implode("\n", $errors)); } } @@ -6492,5 +6474,3 @@ function main() } } } - -main(); \ No newline at end of file