diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 5caa92e4..6f1ceeb6 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -78,13 +78,6 @@ class CompilerBase extends \PhpAot\Core\Translator public const string CLASS_MAP = 'class_map'; public const string FUNC_MAP = 'func_map'; public const string PROP_MAP = 'property_map'; - public const string EXPR_VARIABLE = 'Expr_Variable'; - - public const string EXPR_NEW = 'Expr_New'; - - public const string EXPR_ARRAY_DIM_FETCH = 'Expr_ArrayDimFetch'; - public const string EXPR_PROPERTY_FETCH = 'Expr_PropertyFetch'; - public const string NAMESPACE_SEPARATOR = '__'; public const string PREFIX = 'php_'; @@ -321,7 +314,7 @@ class CompilerBase extends \PhpAot\Core\Translator return $this->context->objects[$object] ?? 'stdClass'; } - public function parseExpr(NodeAbstract $expr) + public function parseExpr(NodeAbstract $expr): string { if ($expr->hasAttribute('replace')) { return $expr->getAttribute('replace'); @@ -416,9 +409,9 @@ class CompilerBase extends \PhpAot\Core\Translator return $this->parseBinaryOpMinus($expr); case 'Expr_Array': return $this->parseArray($expr); - case self::EXPR_ARRAY_DIM_FETCH: + case 'Expr_ArrayDimFetch': return $this->parseArrayDimFetch($expr, $this->context->inAssignExpr); - case self::EXPR_PROPERTY_FETCH: + case 'Expr_PropertyFetch': return $this->parsePropertyFetch($expr, $this->context->inAssignExpr); case 'Expr_NullsafePropertyFetch': return $this->parseNullsafePropertyFetch($expr); @@ -477,7 +470,7 @@ class CompilerBase extends \PhpAot\Core\Translator case 'Scalar_Int': case 'Scalar_Float': case 'Scalar_String': - case self::EXPR_VARIABLE: + case 'Expr_Variable': return $this->parseIdentifier($expr); case 'Scalar_MagicConst_File': case 'Scalar_MagicConst_Dir': @@ -979,7 +972,7 @@ class CompilerBase extends \PhpAot\Core\Translator { $type = $expr->getType(); switch ($type) { - case self::EXPR_VARIABLE: + case 'Expr_Variable': return $this->parseVariable($expr); case 'Name_FullyQualified': return '\\' . $expr->name; @@ -1306,6 +1299,9 @@ class CompilerBase extends \PhpAot\Core\Translator $this->fatalError($left, "Cannot re-assign typed object `\${$var}` from `{$leftClass}` to `{$rightClass}`"); } } else { + if ($this->getVarType($var) === self::TYPE_ARRAY) { + $this->fatalError($left, "Cannot re-assign `\${$var}` from object to array"); + } // TODO 右值是一个类型对象,但左值是一个 var ,许可,但无法标记对象类型 } } else { @@ -1344,6 +1340,8 @@ class CompilerBase extends \PhpAot\Core\Translator // 禁止字符串与数组互相转换,其他类型如对象可以使用 __toString() 协议转为字符串,整数和浮点型也可以转为字符串 or ($this->getVarType($var) === self::TYPE_STR and $type === self::TYPE_ARRAY) or ($this->getVarType($var) === self::TYPE_ARRAY and $type === self::TYPE_STR) + // 禁止对象转为数组 + or ($this->getVarType($var) === self::TYPE_ARRAY and $type === self::TYPE_OBJECT) ) { $this->fatalError($left, "Cannot re-assign variable `\${$var}` from " . $this->getVarType($var) . ' to ' . $type); } @@ -1904,7 +1902,7 @@ class CompilerBase extends \PhpAot\Core\Translator return self::TYPE_OBJECT; case 'Expr_Assign': return $this->detectVarType($expr->var); - case self::EXPR_VARIABLE: + case 'Expr_Variable': return $this->detectVarType($expr); case 'Expr_ConstFetch': return $this->detectConstType($expr); @@ -2145,7 +2143,6 @@ class CompilerBase extends \PhpAot\Core\Translator { $var = $this->parseIdentifier($node->var); $expr = $this->parseIdentifier($node->expr); - $leftExprType = $node->var->getType(); if ($this->isVarExpr($node->var)) { if (!$this->hasVar($var)) { @@ -2166,7 +2163,7 @@ class CompilerBase extends \PhpAot\Core\Translator return $var . ' ' . $op . ' ' . $rightExprStr; } - if ($leftExprType === self::EXPR_ARRAY_DIM_FETCH) { + if ($this->isArrayDimFetch($node->var)) { /** * $count[$r] -= 1; * 需要转为下面语句: @@ -2882,9 +2879,7 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parseBooleanNot(Expr\BooleanNot $expr): string { - $expr = $this->parseExpr($expr->expr); - - return '!(' . $expr . ')'; + return '!(' . $this->parseExpr($expr->expr) . ')'; } protected function parseWhile(Node\Stmt\While_ $v): string @@ -3093,7 +3088,13 @@ class CompilerBase extends \PhpAot\Core\Translator if ($className === 'static') { $cePtr = Symbol::getCalledCe(); } else { - $className = $this->getNamespacedClassName($className); + if ($className === 'self') { + $className = $this->getFullClassName(); + } elseif ($className === 'parent') { + $className = $this->classDef->extends; + } else { + $className = $this->getNamespacedClassName($className); + } if ($this->hasClass($className)) { $classDef = $this->getClass($className); if ($classDef->flags & Modifiers::ABSTRACT) { @@ -3370,23 +3371,22 @@ class CompilerBase extends \PhpAot\Core\Translator $vars = $node->vars; $lines = []; foreach ($vars as $var) { - $type = $var->getType(); - if ($type === self::EXPR_ARRAY_DIM_FETCH) { + if ($this->isArrayDimFetch($var)) { $array = $this->parseIdentifier($var->var); $dim = $this->parseIdentifier($var->dim); $lines[] = $array . '.offsetUnset(' . $dim . ');'; - } elseif ($type === 'Expr_PropertyFetch') { + } elseif ($this->isPropertyFetch($var)) { $object = $this->parseIdentifier($var->var); $propName = $this->identifierToStr($var->name, literal: true); $lines[] = $object . '.unsetProperty(' . $propName . ');'; - } elseif ($type === self::EXPR_VARIABLE) { + } elseif ($this->isVarExpr($var)) { $name = $this->parseIdentifier($var); if (!$this->hasVar($name)) { $this->errorUndefinedVariable($var); } $lines[] = "{$name}.unset();"; } else { - $this->fatalError($var, "Unsupported unset type `{$type}`"); + $this->fatalError($var, "Unsupported unset type `{$var->getType()}`"); } } @@ -3472,7 +3472,7 @@ class CompilerBase extends \PhpAot\Core\Translator $this->fatalError($node, 'Foreach by reference only supports variable as value'); } - if ($node->valueVar->getType() == self::EXPR_ARRAY_DIM_FETCH) { + if ($this->isArrayDimFetch($node->valueVar)) { $array = $this->parseIdentifier($node->valueVar->var); if (!$this->hasVar($array) or $node->valueVar->dim === null) { abort($node->valueVar); @@ -4057,10 +4057,6 @@ class CompilerBase extends \PhpAot\Core\Translator } return $id; } - /* - * 对 static 的支持存在问题,静态编译时无法获得实际运行时的子类名,所以只能使用 self - * self 是在编译期确定的,而 static 是运行时确定的,但使用 AOT 编译为可执行文件后,运行时类的名称是无法确定的 - */ if ($id === 'self') { $id = $this->getNamespacedClassName($this->class); } elseif ($id === 'static') { diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 0272797f..d78c16c0 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -184,7 +184,7 @@ class Translator extends Preprocessor $path = $realpath; if (is_dir($path)) { - $list = $this->getFilesFromDir($path); + $list = $this->getFilesFromDir($path); $targetName = basename($path); $this->setTargetName($targetName); $this->sourceDirs[] = $path; @@ -193,7 +193,7 @@ class Translator extends Preprocessor if ($ext === 'yml') { $list = $this->parseProjectYaml($path); } elseif ($ext === 'php') { - $list = [$path]; + $list = [$path]; $targetName = FileScanner::getFileName($path); $this->setTargetName($targetName); $this->sourceDirs[] = dirname($path); @@ -275,8 +275,8 @@ class Translator extends Preprocessor for ($i = 1; $i < count($argv); $i++) { $arg = $argv[$i]; if (preg_match('/^-([a-zA-Z])(.+)$/', $arg, $matches)) { - $option = $matches[1]; - $value = $matches[2]; + $option = $matches[1]; + $value = $matches[2]; $processed[] = "-{$option}"; $processed[] = $value; } elseif (preg_match('/^-([a-zA-Z]{2,})$/', $arg, $matches)) { @@ -368,14 +368,14 @@ class Translator extends Preprocessor $code .= 'uint32_t ' . self::PREFIX . self::PROP_MAP . '[' . count($this->propMap) . '];' . PHP_EOL; $code .= <<<'CODE' -zend_class_entry *php_get_class(int class_id, const php::String &class_name) { +zend_class_entry *php_get_class(int class_id, const php::Str &class_name) { if (UNEXPECTED(php_class_map[class_id] == nullptr)) { php_class_map[class_id] = php::getClassEntrySafe(class_name); } return php_class_map[class_id]; } -zend_function *php_get_func(int func_id, const php::String &func_name) { +zend_function *php_get_func(int func_id, const php::Str &func_name) { if (UNEXPECTED(php_func_map[func_id] == nullptr)) { php_func_map[func_id] = php::getFunction(func_name); } @@ -631,7 +631,7 @@ CODE; $this->genExternGlobalVars($this->getIncludeDir() . '/php_global_var_decl.h'); // 生成扩展模块的源文件 - $sourceFiles[] =$this->genExtension(); + $sourceFiles[] = $this->genExtension(); // embed 需要 main 函数,以及 cli 的内置函数定义 if ($this->getBuildMode() == 'bin') { @@ -751,7 +751,7 @@ CODE; $code .= 'extern ' . $func->returnType . ' ' . self::PREFIX . $name . '('; $list = []; if ($func->method) { - $list[] = 'php::Object &this_'; + $list[] = self::TYPE_OBJECT . ' &this_'; } $argInfoList = $func->argInfoList; if ($argInfoList) { @@ -804,7 +804,7 @@ CODE; public function genIncludeHeaderFiles(): string { $headers = array_merge($this->globalHeaders, $this->localHeaders); - $lines = []; + $lines = []; foreach ($headers as $header) { $lines[] = '#include <' . $header . '>'; } @@ -905,7 +905,7 @@ CODE; protected function getRegisterClassFunctionCeList(ClassDef|InterfaceDef $classDef): array { - $list = []; + $list = []; $parentCe = $this->getParentClassCe($classDef); if ($parentCe !== '') { $list = [$parentCe]; @@ -961,7 +961,7 @@ CODE; protected function parseProjectYaml(string $path): array { - $cfg = Yaml::parseFile($path); + $cfg = Yaml::parseFile($path); $projectDir = dirname($path); if (!empty($cfg['sources'])) { @@ -969,7 +969,7 @@ CODE; if (!is_array($sources)) { $this->error('`sources` must be array'); } - $list = []; + $list = []; foreach ($sources as $src) { $realPath = $this->getAbsolutePath($src, $projectDir); if (!$realPath) { @@ -1049,7 +1049,7 @@ CODE; { $this->climate->info('convert: ' . $this->getRelativePath($this->file)); - $ast = $this->parser->parse($phpCode); + $ast = $this->parser->parse($phpCode); $traverser = new NodeTraverser(); $traverser->addVisitor(new Visitor()); @@ -1126,8 +1126,8 @@ CODE; foreach ($this->interfaces as $interfaceDef) { $parent = $interfaceDef->extends; - $ce = $this->getClassCe($interfaceDef); - $deps = []; + $ce = $this->getClassCe($interfaceDef); + $deps = []; if ($parent) { // 不存在的接口,说明可能是内置接口 @@ -1139,17 +1139,17 @@ CODE; } $this->classCeInfo[$ce] = [ - 'deps' => $deps, - 'func' => $this->getRegisterClassFunction($interfaceDef->getNamespacedName()), - 'args' => $this->getRegisterClassFunctionArgs($interfaceDef), + 'deps' => $deps, + 'func' => $this->getRegisterClassFunction($interfaceDef->getNamespacedName()), + 'args' => $this->getRegisterClassFunctionArgs($interfaceDef), 'argDef' => $this->getRegisterClassFunctionArgDef($interfaceDef), ]; $sorter->add($ce, $deps); } foreach ($this->classes as $classDef) { - $ce = $this->getClassCe($classDef); - $deps = []; + $ce = $this->getClassCe($classDef); + $deps = []; $parent = $classDef->extends; if ($parent) { // 不存在的父类,说明可能是内置类 @@ -1173,9 +1173,9 @@ CODE; $this->classCeInfo[$ce] = [ 'classDef' => $classDef, - 'deps' => $deps, - 'func' => $this->getRegisterClassFunction($classDef->getNamespacedName()), - 'args' => $this->getRegisterClassFunctionArgs($classDef), + 'deps' => $deps, + 'func' => $this->getRegisterClassFunction($classDef->getNamespacedName()), + 'args' => $this->getRegisterClassFunctionArgs($classDef), 'argDef' => $this->getRegisterClassFunctionArgDef($classDef), ]; $sorter->add($ce, $deps); @@ -1191,7 +1191,7 @@ CODE; protected function parseNamespace(Node\Stmt\Namespace_ $node): string { - $ns = $node->name ? $this->parseIdentifier($node->name) : ''; + $ns = $node->name ? $this->parseIdentifier($node->name) : ''; $code = ''; $this->resetNamespace(); @@ -1200,7 +1200,7 @@ CODE; $this->resetFunction(); $this->namespace = $ns; - $ns_end = ''; + $ns_end = ''; foreach ($node->stmts as $v2) { $type2 = $v2->getType(); @@ -1422,7 +1422,7 @@ CODE; protected function genNativeMethod(array $methodCodes): string { - $code = ''; + $code = ''; $classDef = $this->classDef; foreach ($classDef->methods as $method) { $code .= $methodCodes[$method->name] . PHP_EOL; @@ -1434,7 +1434,7 @@ CODE; protected function genWrapperFunctionArgs(string $fn, FunctionDef $functionDef): string { - $cppCode = ''; + $cppCode = ''; $callParams = ''; foreach ($functionDef->argInfoList as $k => $argInfo) { $var = 'arg_' . $argInfo->name; @@ -1499,9 +1499,9 @@ CODE; protected function getClassRegisterCeFunc(ClassDef|InterfaceDef $classDef): string { $cppCode = ''; - $name = $classDef->getNamespacedName(); + $name = $classDef->getNamespacedName(); $argsDef = $this->getRegisterClassFunctionArgDef($classDef); - $param = $this->getRegisterClassFunctionArgs($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; @@ -1694,7 +1694,7 @@ CODE; protected function parseForeachObject(Foreach_ $node): string { - $obj = $this->parseIdentifier($node->expr); + $obj = $this->parseIdentifier($node->expr); $tmpVar = $this->genTmpVarName(); $this->addLocalVar($tmpVar, self::TYPE_OBJECT); @@ -1773,15 +1773,15 @@ CODE; { $code = 'class ' . $this->class . ' { '; - $publicMethods = []; - $protectedMethods = []; - $privateMethods = []; - $publicConstants = []; - $protectedConstants = []; - $privateConstants = []; - $publicProperties = []; + $publicMethods = []; + $protectedMethods = []; + $privateMethods = []; + $publicConstants = []; + $protectedConstants = []; + $privateConstants = []; + $publicProperties = []; $protectedProperties = []; - $privateProperties = []; + $privateProperties = []; foreach ($this->classDef->constants as $const) { if ($const->flags & Modifiers::PUBLIC) { diff --git a/tests/aot/self-class/001.phpt b/tests/aot/self-class/001.phpt new file mode 100644 index 00000000..f12b3577 --- /dev/null +++ b/tests/aot/self-class/001.phpt @@ -0,0 +1,24 @@ +--TEST-- +self class +--FILE-- +value = $value; + } + public static function null(): EvaluatedValue + { + return new self('null'); + } +} + +function main() { + $obj = EvaluatedValue::null(); + var_dump($obj->value); +} +?> +--EXPECT-- +string(4) "null" \ No newline at end of file