From d37c97903196f36e9b6e4190d970ac0b98bb57b4 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 9 Apr 2026 15:44:50 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=EF=BC=9A=E5=9C=A8=E9=A2=84?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=98=B6=E6=AE=B5=E8=8E=B7=E5=8F=96=E6=89=80?= =?UTF-8?q?=E6=9C=89=E7=B1=BB=E3=80=81=E5=87=BD=E6=95=B0=E3=80=81=E5=B8=B8?= =?UTF-8?q?=E9=87=8F=E3=80=81=E5=B1=9E=E6=80=A7=E5=AE=9A=E4=B9=89=EF=BC=8C?= =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=BE=AA=E7=8E=AF=E4=BE=9D=E8=B5=96=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/extends/A.php | 8 + examples/extends/B.php | 21 +++ examples/extends/C.php | 8 + examples/extends/Foo.php | 4 + examples/extends/test.php | 32 +--- src/Php/CompilerBase.php | 221 +++++------------------ src/Php/Entity/ConstantDef.php | 6 +- src/Php/Entity/FunctionDef.php | 5 +- src/Php/Entity/PropertyDef.php | 1 + src/Php/Preprocessor.php | 311 +++++++++++++++++++++++++++++++-- src/Php/Translator.php | 158 ++--------------- 11 files changed, 418 insertions(+), 357 deletions(-) create mode 100644 examples/extends/A.php create mode 100644 examples/extends/B.php create mode 100644 examples/extends/C.php create mode 100644 examples/extends/Foo.php diff --git a/examples/extends/A.php b/examples/extends/A.php new file mode 100644 index 00000000..91fdd331 --- /dev/null +++ b/examples/extends/A.php @@ -0,0 +1,8 @@ +bar2(); + } + + function bar2() + { + var_dump(__METHOD__); + } +} \ No newline at end of file diff --git a/examples/extends/C.php b/examples/extends/C.php new file mode 100644 index 00000000..24be5ca9 --- /dev/null +++ b/examples/extends/C.php @@ -0,0 +1,8 @@ +foo(); @@ -37,4 +21,6 @@ function main() $c->offsetSet(1, 2); var_dump($c); var_dump($c->foo()); + + foo2(); } diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 3eb0d155..4e96c857 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -764,11 +764,11 @@ class CompilerBase extends \PhpAot\Core\Translator return 'php_get_prop(' . $funcId . ', ' . $this->getLiteralString($prop) . ', ' . $classId . ', ' . $this->getLiteralString($class) . ')'; } - protected function parseTypeDecl(?NodeAbstract $type, int $what): string + protected function parseTypeDecl(?NodeAbstract $type, int $what, string &$class): string { - // 未定义类型,属性、参数默认是 var (mixed, any) ,返回值是 void + // 未定义类型视为 var (mixed, any) if ($type === null) { - return $what === self::DECL_TYPE_OF_RETURN ? self::TYPE_VOID : self::TYPE_VAR; + return self::TYPE_VAR; } if ($type instanceof UnionType or $type instanceof NullableType) { // 联合类型暂时不支持,使用 var 类型代替 @@ -781,84 +781,29 @@ class CompilerBase extends \PhpAot\Core\Translator } elseif (isset($this->zendTypeMap[$typeName])) { return $this->getTypeFromZendType($typeName); } else { + $class = $this->getNamespacedClassName($typeName); return self::TYPE_OBJECT; } } } - protected function parseFunctionDecl(Node\Stmt\Function_|Node\Stmt\ClassMethod $v): FunctionDef - { - // .stub 存根定义 C++ Native 函数,必须设置返回值类型 - if ($this->stubFile and !$v->returnType) { - // 以下魔术方法都不能声明返回值类型 __construct()/__destruct()/__clone() - if (($this->method and !in_array($this->method, ['__construct', '__destruct', '__clone'])) or !$this->method) { - $name = $this->class ? $this->class . '::' . $v->name : $v->name; - $this->fatalError($v, 'The return type of the function `' . $name . '` must be specified'); - } - } - // 返回值不能是引用类型 - if ($v->byRef) { - $this->fatalError($v, 'The return type of the function `' . $v->name . '` cannot be a reference type'); - } - - $fnName = $this->parseIdentifier($v->name); - $returnType = $this->parseTypeDecl($v->returnType, self::DECL_TYPE_OF_RETURN); - $functionDef = new FunctionDef($fnName, $returnType, $this->namespace, $this->stubFile); - // 函数返回值精确类型 - if ($v->returnType != null and !($v->returnType instanceof UnionType or $v->returnType instanceof NullableType)) { - $functionDef->exactReturnType = true; - $functionDef->returnClass = $v->returnType; - } - $this->functionDef = $functionDef; - - $this->parseParams($v->params, $functionDef); - - // main 函数,返回值必须为 void 类型,参数必须为空或者 argc, argv 两个参数 - if (!$this->class and !$this->namespace and $fnName === 'main') { - if (count($v->params) > 0) { - if (count($v->params) != 2) { - $this->fatalError($v, 'The parameters of the main function must be `(int $argc, array $argv)`.'); - } - if ($returnType !== self::TYPE_VOID) { - $this->fatalError($v, 'main function must return void'); - } - if (!$this->checkArgType($functionDef->argInfoList[0]->type, self::TYPE_INT)) { - $this->fatalError($v, 'The first parameter of the main function must be of type `int`.'); - } - if (!$this->checkArgType($functionDef->argInfoList[1]->type, self::TYPE_ARRAY)) { - $this->fatalError($v, 'The second parameter of the main function must be of type `array`.'); - } - } - } - - return $functionDef; - } - /** * @throws \Exception */ protected function parseFunction(Node\Stmt\Function_|Node\Stmt\ClassMethod $v): string { $this->resetFunction(); - $this->function = $this->parseIdentifier($v->name); $name = $this->getFunctionName($v); - if ($this->hasFunction($name)) { - $this->functionDef = $this->getFunction($name); - } else { - $this->addFunction($name, $this->parseFunctionDecl($v)); - if (isset($this->redoAfterDeclare[$name])) { - unset($this->redoAfterDeclare[$name]); - $this->climate->cyan('Received redo request, retrying...'); - throw new Redo(); - } + $this->function = $this->parseIdentifier($v->name); + + if (!$this->hasFunction($name)) { + $this->fatalError($v, 'Function `' . $name . '` not found'); } + $this->functionDef = $this->getFunction($name); // 类方法不要保存到 functions 中 - if ($this->class) { - if ($this->methodDef) { - $this->functionDef->method = true; - $this->methodDef->functionDef = $this->functionDef; - } + if ($this->methodDef) { + $this->methodDef->functionDef = $this->functionDef; } else { $this->functionDefineInFile[$name] = $this->functionDef; } @@ -884,7 +829,6 @@ class CompilerBase extends \PhpAot\Core\Translator if (!$this->isReturnStmtInLastLine($v->stmts)) { $stmts .= $this->genReturnCode(); } - $this->functionDef->completed = true; } catch (Skip) { $this->climate->cyan('Skip function ' . $name); } @@ -980,6 +924,15 @@ class CompilerBase extends \PhpAot\Core\Translator return $this->escapeVarName($expr->name); } + protected function parseImplements(array $implements): array + { + $list = []; + foreach ($implements as $implement) { + $list[] = $this->getNamespacedClassName($implement); + } + return $list; + } + protected function parseIdentifier(NodeAbstract $expr): string { $type = $expr->getType(); @@ -1023,95 +976,6 @@ class CompilerBase extends \PhpAot\Core\Translator } } - /** - * @param $params array - */ - protected function parseParams(array $params, FunctionDef $functionDef): void - { - $list = []; - $functionDef->argCountRequired = count($params); - $defaultValueCount = 0; - $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)); - } - // 构造方法属性定义语法(Constructor Property Promotion) - if ($param->isPromoted()) { - if (!$this->classDef or !$this->methodDef or $this->methodDef->name !== '__construct') { - $this->fatalError($param, 'Promoted properties are not supported'); - } - $name = $this->parseIdentifier($param->var); - if ($param->type instanceof NullableType) { - $type = $param->type->type; - $nullable = true; - } elseif ($param->type instanceof UnionType) { - $type = 'mixed'; - $nullable = false; - } else { - $type = $param->type === null ? '' : $param->type; - $nullable = false; - } - $default = $this->parseParamDefaultValue($param->default); - $propertyDef = new PropertyDef($name, $param->flags, $type, $default, $nullable); - $this->classDef->properties[$name] = $propertyDef; - } - if ($param->variadic) { - if ($i !== $last) { - $this->fatalError($param, 'Variadic parameters must be the last parameter'); - } elseif ($param->byRef) { - $this->fatalError($param, 'Variadic parameters cannot be passed by reference'); - } - } - $name = $this->parseIdentifier($param->var); - if ($this->method and $name == 'this_') { - $this->fatalError($param, 'Cannot use `$this` as parameter of class method'); - } - $argInfo = new ArgInfo(); - $type = $this->parseParameterType($param, $argInfo, $name); - if ($param->variadic) { - $list[] = self::TYPE_ARRAY . ' ' . $name; - } else { - $list[] = $type . ' ' . $name; - } - $argInfo->name = $name; - $argInfo->type = $type; - $argInfo->byRef = $param->byRef; - $argInfo->variadic = $param->variadic; - $argInfo->property = $param->isPromoted(); - if ($param->type and $param->type instanceof NullableType) { - $argInfo->nullable = true; - } - if ($param->default) { - if ($param->byRef) { - if ($this->isEmptyArray($param->default)) { - $argInfo->default = 'php::getEmptyArrayRef()'; - $argInfo->defaultValue = null; - } elseif ($this->isNull($param->default)) { - $argInfo->default = 'nullptr'; - $argInfo->defaultValue = null; - } else { - $this->fatalError($param, 'Only null and empty array can be used as default value for reference parameter'); - } - } else { - $argInfo->default = $this->parseParamDefaultValue($param->default); - $argInfo->defaultValue = $param->default; - } - $defaultValueCount++; - } elseif ($param->variadic) { - // 变长参数可以视为空数组默认值 - $defaultValueCount++; - $argInfo->default = '{}'; - $argInfo->defaultValue = new Expr\Array_(); - } - $functionDef->argInfoList[] = $argInfo; - } - $functionDef->params = implode(', ', $list); - $functionDef->argCountRequired -= $defaultValueCount; - } - protected function getComment(Node\Stmt $v, string $class): string { if ($class == 'Stmt_Expression') { @@ -1589,17 +1453,13 @@ class CompilerBase extends \PhpAot\Core\Translator // 实际函数的返回值 $type = $this->detectExprType($v->expr); $expr = $this->parseExpr($v->expr); + $returnType = $this->getReturnType(); // 匿名函数的返回值一定是 var if (!$this->context->inClosure) { - // 函数定义时没有声明返回值,但函数体中有返回值,修改为实际的返回值类型 - if ($this->getReturnType() === 'void') { - $this->resetReturnType($v, $type); - } elseif ($this->isNativeType($type) and $this->getReturnType() !== self::TYPE_VAR and $this->getReturnType() !== $type) { - // 返回值类型不一致,说明存在多种类型的返回值,修改为 var 表示 any - $this->resetReturnType($v, self::TYPE_VAR); - } - $returnType = $this->getReturnType(); + if ($returnType === 'void') { + $this->fatalError($v, 'The return type is void, cannot return any value'); + } } else { $returnType = self::TYPE_VAR; } @@ -1908,10 +1768,7 @@ class CompilerBase extends \PhpAot\Core\Translator $nativeFunc = $this->findNativeMethod($expr, $object, $method); if ($nativeFunc) { $funcDef = $this->functions[$nativeFunc]; - // 用户未定义类型,但函数已编译完成,通过分析代码获得了返回值类型 - if ($funcDef->completed or $funcDef->exactReturnType) { - return $funcDef->returnType; - } + return $funcDef->returnType; } elseif ($this->isTypedObject($object)) { return $this->detectMethodCallReturnType($this->getObjectType($object), $method); } @@ -4365,6 +4222,14 @@ class CompilerBase extends \PhpAot\Core\Translator return $v->name->name . ':'; } + protected function parseModifiers(int $flags): int + { + if (!($flags & Modifiers::PRIVATE) and !($flags & Modifiers::PROTECTED)) { + $flags |= Modifiers::PUBLIC; + } + return $flags; + } + protected function parseConstDef(mixed $v2): string { foreach ($v2->consts as $const) { @@ -4567,8 +4432,10 @@ class CompilerBase extends \PhpAot\Core\Translator $class = $this->context->objects[$object]; $nativeFunc = $this->getNativeMethod($expr, $class, $method); // 存在 Native 类,但是没有找到方法,可能是动态调用 - if (!$nativeFunc and $this->hasClass($class) and $this->getNativeMethod($expr, $class, '__call', false)) { - throw new DynamicCall(); + if (!$nativeFunc) { + if ($this->hasClass($class) and $this->getNativeMethod($expr, $class, '__call', false)) { + throw new DynamicCall(); + } } } if ($classDef) { @@ -4619,14 +4486,20 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parseParentMethodCall(Expr\StaticCall $expr): string { + $methodStr = $this->classDef->name . '::' . $this->parseIdentifier($expr->name); if (!$this->classDef->extends) { - $this->fatalError($expr, 'Cannot call parent method `' . $this->classDef->name . '::' . $this->parseIdentifier($expr->name) . '()` because class `' . $this->classDef->name . '` does not extend any class'); + $this->fatalError($expr, 'Cannot call parent method `' . $methodStr . '()` because class `' . $this->classDef->name . '` does not extend any class'); + } + if (!$this->isIdExpr($expr->name)) { + $this->fatalError($expr, 'Cannot call parent method `' . $methodStr . '()` because method name is not a literal'); } - $method = $this->identifierToStr($expr->name); + $parentClass = $this->classDef->extends; + $method = $this->parseIdentifier($expr->name); + // TODO 是否转为 native 调用 if (empty($expr->args)) { - return 'this_.callParentMethod(' . $method . ')'; + return 'this_.call(' . $this->getMethodPtr($parentClass, $method) . ')'; } - return 'this_.callParentMethod(' . $method . ', ' . $this->parseCallArgs($expr->args) . ')'; + return 'this_.call(' . $this->getMethodPtr($parentClass, $method) . ', ' . $this->parseCallArgs($expr->args) . ')'; } protected function genDebugInfo(?NodeAbstract $stmt = null): string diff --git a/src/Php/Entity/ConstantDef.php b/src/Php/Entity/ConstantDef.php index 8e91e5cc..550a1cab 100644 --- a/src/Php/Entity/ConstantDef.php +++ b/src/Php/Entity/ConstantDef.php @@ -14,14 +14,14 @@ class ConstantDef public string $type; public int $flags; public string $value; - public string $arrayExpr; + public string $arrayExpr = ''; + public string $class = ''; - public function __construct(string $name, int $flags, string $type, string $value, string $arrayExpr = '') + public function __construct(string $name, int $flags, string $type, string $value) { $this->name = $name; $this->type = $type; $this->flags = $flags; $this->value = $value; - $this->arrayExpr = $arrayExpr; } } diff --git a/src/Php/Entity/FunctionDef.php b/src/Php/Entity/FunctionDef.php index 5594b527..139944f3 100644 --- a/src/Php/Entity/FunctionDef.php +++ b/src/Php/Entity/FunctionDef.php @@ -24,16 +24,13 @@ class FunctionDef public string $namespace; public bool $method = false; public bool $stub = false; - public bool $completed = false; - public bool $exactReturnType = false; public string $returnClass = ''; - public function __construct(string $name, string $returnType, string $namespace, bool $stub) + public function __construct(string $name, string $returnType, string $namespace) { $this->name = $name; $this->returnType = $returnType; $this->namespace = $namespace; - $this->stub = $stub; } public function getNamespacedName(): string diff --git a/src/Php/Entity/PropertyDef.php b/src/Php/Entity/PropertyDef.php index f3fb4505..2cbf356c 100644 --- a/src/Php/Entity/PropertyDef.php +++ b/src/Php/Entity/PropertyDef.php @@ -17,6 +17,7 @@ class PropertyDef public int $flags; public ?string $default = null; public bool $nullable = false; + public string $class = ''; public function __construct(string $name, int $flags, string $type, ?string $default = null, bool $nullable = false) { diff --git a/src/Php/Preprocessor.php b/src/Php/Preprocessor.php index dbc1f121..7d773dcf 100644 --- a/src/Php/Preprocessor.php +++ b/src/Php/Preprocessor.php @@ -11,10 +11,17 @@ namespace PhpAot\Php; use MJS\TopSort\CircularDependencyException; use MJS\TopSort\ElementNotFoundException; use MJS\TopSort\Implementations\StringSort; +use PhpAot\Php\Entity\ClassDef; +use PhpAot\Php\Entity\ConstantDef; +use PhpAot\Php\Entity\FunctionDef; +use PhpAot\Php\Entity\MethodDef; +use PhpAot\Php\Entity\PropertyDef; use PhpAot\Php\Exception\SyntaxError; use PhpAot\Php\Exception\Unsupported; use PhpParser\Modifiers; use PhpParser\Node; +use PhpParser\Node\NullableType; +use PhpParser\Node\UnionType; use PhpParser\NodeAbstract; use PhpParser\NodeFinder; use PhpParser\NodeTraverser; @@ -161,6 +168,7 @@ class Preprocessor extends CompilerBase $this->symbolCallInFile[$this->file] = []; $this->resetFile(); $this->resetFunction(); + $this->resetMethod(); $this->resetClass(); $this->resetNamespace(); @@ -208,6 +216,10 @@ class Preprocessor extends CompilerBase } } + } + + protected function findSymbolUsing(NodeAbstract $ast) + { $nodeFinder = new NodeFinder(); $functionCalls = $nodeFinder->findInstanceOf($ast, Node\Expr\FuncCall::class); @@ -222,8 +234,8 @@ class Preprocessor extends CompilerBase } $depClasses = []; -// $depClasses = array_merge($depClasses, $nodeFinder->findInstanceOf($ast, Node\Expr\StaticCall::class)); -// $depClasses = array_merge($depClasses, $nodeFinder->findInstanceOf($ast, Node\Expr\StaticPropertyFetch::class)); + $depClasses = array_merge($depClasses, $nodeFinder->findInstanceOf($ast, Node\Expr\StaticCall::class)); + $depClasses = array_merge($depClasses, $nodeFinder->findInstanceOf($ast, Node\Expr\StaticPropertyFetch::class)); $depClasses = array_merge($depClasses, $nodeFinder->findInstanceOf($ast, Node\Expr\ClassConstFetch::class)); $depClasses = array_merge($depClasses, $nodeFinder->findInstanceOf($ast, Node\Expr\New_::class)); foreach ($depClasses as $call) { @@ -242,13 +254,18 @@ class Preprocessor extends CompilerBase protected function prepareNamespace(Node\Stmt\Namespace_ $node): void { + $this->resetClass(); + $this->resetMethod(); + $this->resetFunction(); $this->resetNamespace(); + $this->namespace = $node->name ? $this->parseIdentifier($node->name) : ''; foreach ($node->stmts as $v2) { $type2 = $v2->getType(); switch ($type2) { case 'Stmt_Class': case 'Stmt_Enum': + case 'Stmt_Trait': $this->prepareClass($v2); break; case 'Stmt_Function': @@ -269,17 +286,153 @@ class Preprocessor extends CompilerBase } } + /** + * @param $params array + */ + protected function parseParams(array $params, FunctionDef $functionDef): void + { + $list = []; + $functionDef->argCountRequired = count($params); + $defaultValueCount = 0; + $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)); + } + // 构造方法属性定义语法(Constructor Property Promotion) + if ($param->isPromoted()) { + if (!$this->classDef or !$this->methodDef or $this->methodDef->name !== '__construct') { + $this->fatalError($param, 'Promoted properties are not supported'); + } + $name = $this->parseIdentifier($param->var); + if ($param->type instanceof NullableType) { + $type = $param->type->type; + $nullable = true; + } elseif ($param->type instanceof UnionType) { + $type = 'mixed'; + $nullable = false; + } else { + $type = $param->type === null ? '' : $param->type; + $nullable = false; + } + $default = $this->parseParamDefaultValue($param->default); + $propertyDef = new PropertyDef($name, $param->flags, $type, $default, $nullable); + $this->classDef->properties[$name] = $propertyDef; + } + if ($param->variadic) { + if ($i !== $last) { + $this->fatalError($param, 'Variadic parameters must be the last parameter'); + } elseif ($param->byRef) { + $this->fatalError($param, 'Variadic parameters cannot be passed by reference'); + } + } + $name = $this->parseIdentifier($param->var); + if ($this->method and $name == 'this_') { + $this->fatalError($param, 'Cannot use `$this` as parameter of class method'); + } + $argInfo = new ArgInfo(); + $type = $this->parseParameterType($param, $argInfo, $name); + if ($param->variadic) { + $list[] = self::TYPE_ARRAY . ' ' . $name; + } else { + $list[] = $type . ' ' . $name; + } + $argInfo->name = $name; + $argInfo->type = $type; + $argInfo->byRef = $param->byRef; + $argInfo->variadic = $param->variadic; + $argInfo->property = $param->isPromoted(); + if ($param->type and $param->type instanceof NullableType) { + $argInfo->nullable = true; + } + if ($param->default) { + if ($param->byRef) { + if ($this->isEmptyArray($param->default)) { + $argInfo->default = 'php::getEmptyArrayRef()'; + $argInfo->defaultValue = null; + } elseif ($this->isNull($param->default)) { + $argInfo->default = 'nullptr'; + $argInfo->defaultValue = null; + } else { + $this->fatalError($param, 'Only null and empty array can be used as default value for reference parameter'); + } + } else { + $argInfo->default = $this->parseParamDefaultValue($param->default); + $argInfo->defaultValue = $param->default; + } + $defaultValueCount++; + } elseif ($param->variadic) { + // 变长参数可以视为空数组默认值 + $defaultValueCount++; + $argInfo->default = '{}'; + $argInfo->defaultValue = new Node\Expr\Array_(); + } + $functionDef->argInfoList[] = $argInfo; + } + $functionDef->params = implode(', ', $list); + $functionDef->argCountRequired -= $defaultValueCount; + } + + protected function parseFunctionDecl(Node\Stmt\Function_|Node\Stmt\ClassMethod $v): FunctionDef + { + // .stub 存根定义 C++ Native 函数,必须设置返回值类型 + if ($this->stubFile and !$v->returnType) { + // 以下魔术方法都不能声明返回值类型 __construct()/__destruct()/__clone() + if (($this->method and !in_array($this->method, ['__construct', '__destruct', '__clone'])) or !$this->method) { + $name = $this->class ? $this->class . '::' . $v->name : $v->name; + $this->fatalError($v, 'The return type of the function `' . $name . '` must be specified'); + } + } + // 返回值不能是引用类型 + if ($v->byRef) { + $this->fatalError($v, 'The return type of the function `' . $v->name . '` cannot be a reference type'); + } + + $fnName = $this->parseIdentifier($v->name); + $class = ''; + $returnType = $this->parseTypeDecl($v->returnType, self::DECL_TYPE_OF_RETURN, $class); + $functionDef = new FunctionDef($fnName, $returnType, $this->namespace); + $functionDef->returnClass = $class; + $functionDef->stub = $this->stubFile; + + $this->parseParams($v->params, $functionDef); + + // main 函数,返回值必须为 void 类型,参数必须为空或者 argc, argv 两个参数 + if (!$this->class and !$this->namespace and $fnName === 'main') { + if (count($v->params) > 0) { + if (count($v->params) != 2) { + $this->fatalError($v, 'The parameters of the main function must be `(int $argc, array $argv)`.'); + } + if ($returnType !== self::TYPE_VOID) { + $this->fatalError($v, 'main function must return void'); + } + if (!$this->checkArgType($functionDef->argInfoList[0]->type, self::TYPE_INT)) { + $this->fatalError($v, 'The first parameter of the main function must be of type `int`.'); + } + if (!$this->checkArgType($functionDef->argInfoList[1]->type, self::TYPE_ARRAY)) { + $this->fatalError($v, 'The second parameter of the main function must be of type `array`.'); + } + } + } + + return $functionDef; + } + protected function prepareFunction(Node\Stmt\ClassMethod|Node\Stmt\Function_ $v): void { + $this->resetFunction(); + $this->function = $this->parseIdentifier($v->name); $name = $this->getFunctionName($v); - if ($this->stubFile) { - $this->addFunction($name, $this->parseFunctionDecl($v)); - } else { - $functionNameLower = strtolower($name); - if (isset($this->symbolDeclInFile[$functionNameLower])) { - $this->fatalError($v, "Duplicate function `{$functionNameLower}`"); - } - $this->symbolDeclInFile[$functionNameLower] = $this->file; + if ($this->hasFunction($name)) { + $this->fatalError($v, "Duplicate function `{$name}`"); + } + $functionDef = $this->parseFunctionDecl($v); + $this->addFunction($name, $functionDef); + if ($this->methodDef) { + $functionDef->method = true; + $this->methodDef->functionDef = $functionDef; } } @@ -298,6 +451,17 @@ class Preprocessor extends CompilerBase $fullClassName = $this->getFullClassName(); $fullClassNameLower = strtolower($fullClassName); + if ($class instanceof Node\Stmt\Enum_) { + $flags = Modifiers::PUBLIC; + } else if (!$class instanceof Node\Stmt\Trait_) { + $flags = $class->flags; + } else { + $flags = Modifiers::PUBLIC; + } + + $this->classDef = new ClassDef($this->class, $flags, $this->namespace); + $this->addClass($fullClassName, $this->classDef); + if (!empty($class->extends)) { $this->parentClass = $this->getParentClass($class->extends); $parentClassLower = strtolower($this->parentClass); @@ -308,8 +472,17 @@ class Preprocessor extends CompilerBase if (!$this->isInternalClass($parentClassLower)) { $this->symbolCallInFile[$this->file][] = $parentClassLower; } + $this->classDef->extends = $this->parentClass; + // 是否继承了内置类 + $this->classDef->inheritedFromInternalClass = $this->isInternalClass($parentClassLower); } + if ($class instanceof Node\Stmt\Enum_) { + $this->classDef->enum = true; + } + if (!$class instanceof Node\Stmt\Trait_) { + $this->classDef->implements = $this->parseImplements($class->implements); + } if (isset($this->symbolDeclInFile[$fullClassNameLower])) { $this->fatalError($class, "Duplicate class `{$fullClassName}`"); } @@ -321,13 +494,17 @@ class Preprocessor extends CompilerBase $type = $v->getType(); switch ($type) { case 'Stmt_ClassConst': + $this->parseClassConstDef($v); + break; case 'Stmt_Property': + $this->parseClassPropertyDef($v); + break; case 'Stmt_Nop': case 'Stmt_TraitUse': case 'Stmt_EnumCase': break; case 'Stmt_ClassMethod': - $this->prepareMethod($v, $class); + $this->prepareClassMethod($v, $class); break; case 'Stmt_Expression': $this->foundStrayCode($v); @@ -342,12 +519,118 @@ class Preprocessor extends CompilerBase return $code; } - protected function prepareMethod(Node\Stmt\ClassMethod $v, Node\Stmt\Class_|Node\Stmt\Trait_|Node\Stmt\Enum_ $class): void + protected function getMethodName(Node\Stmt\ClassMethod $v): string { - $this->method = $v->name; - $abstract = $v->flags & Modifiers::ABSTRACT; + return $this->parseIdentifier($v->name); + } + + /** + * 检查父类方法是否可以被重写,私有方法不能被重写 + */ + protected function checkParentMethodCanBeOverridden(Node\Stmt\ClassMethod $v, string $name): void + { + $classDef = $this->classDef; + while (true) { + $extends = $classDef->extends; + if (!$extends) { + break; + } + // 父类是内置类 + if ($classDef->inheritedFromInternalClass) { + if (Reflection::getClassMethodModifiers($extends, $name) & \ReflectionMethod::IS_PRIVATE) { + goto _error; + } + break; + } + $classDef = $this->getClass($extends); + if ($classDef->hasMethod($this->method)) { + $methodDef = $classDef->getMethod($this->method); + if ($methodDef->flags & Modifiers::PRIVATE) { + _error: + $this->fatalError($v, + 'Cannot override private method `' . + $classDef->getNamespacedName(false) . '::' . $this->method . '()`'); + } + } + } + } + + protected function parseClassConstDef(Node\Stmt\ClassConst $v): void + { + $this->resetFunction(); + $flags = $this->parseModifiers($v->flags); + $class = ''; + + if ($v->type) { + $type = $this->parseTypeDecl($v->type, self::DECL_TYPE_OF_CONST, $class); + } else { + $type = null; + } + + foreach ($v->consts as $const) { + if ($type === null) { + $type = match ($const->value->getType()) { + 'Expr_Array' => self::TYPE_ARRAY, + 'Scalar_String' => self::TYPE_STR, + default => self::TYPE_VAR, + }; + } + $constName = $this->parseIdentifier($const->name); + $constValue = $this->parseIdentifier($const->value); + + $constInfo = new ConstantDef($constName, $flags, $type, $constValue); + + if ($this->context->beforeStmtLines) { + $arrayExpr = ''; + if ($this->context->localVars) { + $arrayExpr .= $this->genScopeVarDecl(); + } + $arrayExpr .= $this->parseBeforeStmtLines(); + $constInfo->arrayExpr = $arrayExpr; + } + $constInfo->class = $class; + $this->classDef->constants[$constInfo->name] = $constInfo; + } + } + + protected function parseClassPropertyDef(Node\Stmt\Property $v): void + { + $oriCtx = $this->context; + $this->context = $this->classDef->propertyContext; + $flags = $this->parseModifiers($v->flags); + $class = ''; + $type = $this->parseTypeDecl($v->type, self::DECL_TYPE_OF_PROPERTY, $class); + + foreach ($v->props as $prop) { + $propDef = new PropertyDef($this->parseIdentifier($prop->name), $flags, $type); + if ($prop->default) { + $propDef->default = $this->parseIdentifier($prop->default); + if ($prop->default->getType() == 'Expr_Array') { + $propDef->type = self::TYPE_ARRAY; + } + } + $propDef->class = $class; + $this->classDef->properties[$propDef->name] = $propDef; + } + + $this->context = $oriCtx; + } + + protected function prepareClassMethod(Node\Stmt\ClassMethod $v, Node\Stmt\Class_|Node\Stmt\Trait_|Node\Stmt\Enum_ $class): void + { + $name = $this->getMethodName($v); + $this->method = $name; + $flags = $this->parseModifiers($v->flags); + $abstract = $flags & Modifiers::ABSTRACT; + if (!$abstract) { + $this->methodDef = new MethodDef($flags, $name); + if ($this->classDef->hasMethod($name)) { + $this->fatalError($v, "Duplicate method `{$this->method}`"); + } $this->prepareFunction($v) . PHP_EOL; + $this->checkRequiredArgNum($name, $this->methodDef, $v); + $this->classDef->addMethod($this->methodDef); } else { if (!($class->flags & Modifiers::ABSTRACT)) { $this->fatalError($v, "Class {$this->class} cannot override non-abstract method {$v->name}"); diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 6c555d0f..e04e8e02 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -671,9 +671,10 @@ class Translator extends Preprocessor $stmts = $traverser->traverse($ast); $this->resetFile(); - $this->resetFunction(); - $this->resetClass(); $this->resetNamespace(); + $this->resetClass(); + $this->resetMethod(); + $this->resetFunction(); $cppCode = ''; foreach ($stmts as $v) { @@ -798,11 +799,6 @@ class Translator extends Preprocessor $this->classCeList = $sorter->sort(); } - protected function getMethodName(Node\Stmt\ClassMethod $v): string - { - return $this->parseIdentifier($v->name); - } - protected function getNativeMethodName(ClassDef $classDef, MethodDef $methodDef): string { return $this->getNativeName($methodDef->name, $classDef->namespace, $classDef->name); @@ -814,6 +810,9 @@ class Translator extends Preprocessor $code = ''; $this->resetNamespace(); + $this->resetClass(); + $this->resetMethod(); + $this->resetFunction(); $this->namespace = $ns; $ns_end = ''; @@ -876,45 +875,26 @@ class Translator extends Preprocessor protected function parseClass(Node\Stmt\Class_|Node\Stmt\Trait_|Node\Stmt\Enum_ $class): string { $this->class = $this->parseIdentifier($class->name); - if ($class instanceof Node\Stmt\Enum_) { - $flags = Modifiers::PUBLIC; - $extends = ''; - } else { - $flags = $class->flags; - $extends = $class->extends; - } - $fullName = $this->getFullClassName(); - if ($this->hasClass($fullName)) { - $this->classDef = $this->getClass($fullName); - } else { - $this->classDef = new ClassDef($this->class, $flags, $this->namespace); - $this->addClass($fullName, $this->classDef); - } - - if ($class instanceof Node\Stmt\Enum_) { - $this->classDef->enum = true; + if (!$this->hasClass($fullName)) { + $this->fatalError($class, "class {$fullName} not found"); } + $this->classDef = $this->getClass($fullName); - if ($extends) { + // 如果不是继承自内置类,需要检查父类是否存在,在预处理阶段只需检查了是否继承内置类 + // 目前不允许继承自动态加载的自定义类 + if ($this->classDef->extends and !$this->classDef->inheritedFromInternalClass) { $parentClass = $this->getParentClass($class->extends); if ($this->hasClass($parentClass)) { $parent = $this->getClass($parentClass); + // 父类是 final 无法继承 if ($parent->flags & Modifiers::FINAL) { $this->fatalError($class, "Class `{$this->class}` cannot extend final class `{$parentClass}`"); } - $this->classDef->extends = $parentClass; - $this->classDef->inheritedFromInternalClass = false; } else { - if (Reflection::isInternalClass($parentClass)) { - $this->classDef->extends = $parentClass; - $this->classDef->inheritedFromInternalClass = true; - } else { - $this->fatalError($class, "Class `{$this->class}` inherits from a non-existent class `{$parentClass}`"); - } + $this->fatalError($class, "Class `{$this->class}` inherits from a non-existent class `{$parentClass}`"); } } - $this->classDef->implements = $this->parseIdentifierList($class->implements); $className = $this->classDef->getNamespacedName(); $this->classesDefineInFile[$className] = $this->classDef; @@ -925,17 +905,13 @@ class Translator extends Preprocessor $type = $v->getType(); switch ($type) { case 'Stmt_ClassConst': - $this->parseClassConstDef($v); - break; case 'Stmt_Property': - $this->parsePropertyDef($v); + case 'Stmt_Nop': + case 'Stmt_EnumCase': break; case 'Stmt_ClassMethod': $this->parseClassMethod($v, $methodCodes); break; - case 'Stmt_EnumCase': - case 'Stmt_Nop': - break; default: abort($v); } @@ -1129,118 +1105,22 @@ class Translator extends Preprocessor return $code; } - protected function parseClassConstDef(Node\Stmt\ClassConst $v): void - { - $this->resetFunction(); - $flags = $this->parseModifiers($v->flags); - if ($v->type) { - $type = $this->parseTypeDecl($v->type, self::DECL_TYPE_OF_CONST); - } else { - $type = null; - } - foreach ($v->consts as $const) { - if ($type === null) { - $type = match ($const->value->getType()) { - 'Expr_Array' => self::TYPE_ARRAY, - 'Scalar_String' => self::TYPE_STR, - default => self::TYPE_VAR, - }; - } - $constName = $this->parseIdentifier($const->name); - $constValue = $this->parseIdentifier($const->value); - $arrayExpr = ''; - if ($this->context->beforeStmtLines) { - if ($this->context->localVars) { - $arrayExpr .= $this->genScopeVarDecl(); - } - $arrayExpr .= $this->parseBeforeStmtLines(); - } - $constInfo = new ConstantDef($constName, $flags, $type, $constValue, $arrayExpr); - $this->classDef->constants[$constInfo->name] = $constInfo; - } - } - - protected function parsePropertyDef(Node\Stmt\Property $v): void - { - $oriCtx = $this->context; - $this->context = $this->classDef->propertyContext; - $flags = $this->parseModifiers($v->flags); - $type = $this->parseTypeDecl($v->type, self::DECL_TYPE_OF_PROPERTY); - - foreach ($v->props as $prop) { - $propDef = new PropertyDef($this->parseIdentifier($prop->name), $flags, $type); - if ($prop->default) { - $propDef->default = $this->parseIdentifier($prop->default); - if ($prop->default->getType() == 'Expr_Array') { - $propDef->type = self::TYPE_ARRAY; - } - } - $this->classDef->properties[$propDef->name] = $propDef; - } - - $this->context = $oriCtx; - } - - protected function parseModifiers(int $flags): int - { - if (!($flags & Modifiers::PRIVATE) and !($flags & Modifiers::PROTECTED)) { - $flags |= Modifiers::PUBLIC; - } - return $flags; - } - protected function parseClassMethod(Node\Stmt\ClassMethod $v, array &$methodCodes): void { $name = $this->getMethodName($v); $this->method = $name; $flags = $this->parseModifiers($v->flags); - $classDef = $this->classDef; - while (true) { - $extends = $classDef->extends; - if (!$extends) { - break; - } - // 父类是内置类 - if ($classDef->inheritedFromInternalClass) { - if (Reflection::getClassMethodModifiers($extends, $name) & \ReflectionMethod::IS_PRIVATE) { - goto _error; - } - break; - } - $classDef = $this->getClass($extends); - if ($classDef->hasMethod($this->method)) { - $methodDef = $classDef->getMethod($this->method); - if ($methodDef->flags & Modifiers::PRIVATE) { - _error: - $this->fatalError($v, - 'Cannot override private method `' . - $classDef->getNamespacedName(false) . '::' . $this->method . '()`'); - } - } - } - if (!($flags & Modifiers::ABSTRACT)) { - $this->methodDef = new MethodDef($flags, $name); + $this->methodDef = $this->classDef->getMethod($name); + // 预处理阶段没有父类的信息,只能在实现阶段检查 + $this->checkParentMethodCanBeOverridden($v, $name); $methodCodes[$name] = $this->parseFunction($v); - - $this->checkRequiredArgNum($name, $this->methodDef, $v); - $this->classDef->addMethod($this->methodDef); } $this->resetMethod(); } - protected function parseIdentifierList(array $implements): array - { - $list = []; - foreach ($implements as $implement) { - $list[] = $this->getNamespacedClassName($implement); - } - - return $list; - } - protected function parseInterface(Node\Stmt\Interface_ $v): void { $name = $this->parseIdentifier($v->name);