From 57e1790639609b69be91506ecc5527b1c78403fb Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 30 Jan 2026 19:52:34 +0800 Subject: [PATCH] =?UTF-8?q?feat(compiler):=20=E5=AE=9E=E7=8E=B0=E5=AF=B9?= =?UTF-8?q?=E9=9D=99=E6=80=81=E5=B1=9E=E6=80=A7=E5=92=8C=E5=B8=B8=E9=87=8F?= =?UTF-8?q?=E7=9A=84=E5=8E=9F=E7=94=9F=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 hasConstant、getMethod 和 getConstant 方法到 ClassDef 类 - 在 CompilerBase 中实现静态属性和常量的原生解析功能 - 添加 parseNativeStaticPropertyFetch 和 findNativeStaticProperty 方法 - 修改 getFuncPtr 方法以支持宏参数 - 实现对类常量和静态属性的直接访问优化 - 添加对后置操作符中属性获取的支持 - 更新 examples/micro_bench.php 测试用例以验证功能 - 在 PropertyDef 中添加 isStatic 方法判断静态属性 - 重构预处理器对常量定义的处理逻辑 --- examples/micro_bench.php | 92 +++++++++++----------- src/Php/ClassDef.php | 16 ++++ src/Php/CompilerBase.php | 151 ++++++++++++++++++++++++++++-------- src/Php/Preprocessor.php | 4 +- src/Php/PropertyDef.php | 5 ++ tests/aot/prop-dec-inc.phpt | 28 +++++++ 6 files changed, 218 insertions(+), 78 deletions(-) create mode 100644 tests/aot/prop-dec-inc.phpt diff --git a/examples/micro_bench.php b/examples/micro_bench.php index a35288d7..1ff5a409 100644 --- a/examples/micro_bench.php +++ b/examples/micro_bench.php @@ -25,7 +25,7 @@ function simpleicall($n) class Foo { static $a = 0; public $b = 0; - const TEST = 0; + const TEST = 23; static function read_static($n) { for ($i = 0; $i < $n; ++$i) { @@ -169,7 +169,8 @@ function create_object($n) { function read_const($n) { for ($i = 0; $i < $n; ++$i) { - $x = TEST; +// $x = TEST; + $x = TEST_CONST_2; } } @@ -283,6 +284,7 @@ const TEST = null; function main() { global $total, $last_time, $g_var; + define('TEST_CONST_2', 1999); $g_var = 0; $t0 = $t = start_test(); empty_loop(N); @@ -296,51 +298,51 @@ function main() $t = end_test($t, 'int_func()', $overhead); Foo::read_static(N); $t = end_test($t, '$x = self::$x', $overhead); -// Foo::write_static(N); -// $t = end_test($t, 'self::$x = 0', $overhead); -// Foo::isset_static(N); -// $t = end_test($t, 'isset(self::$x)', $overhead); -// Foo::empty_static(N); -// $t = end_test($t, 'empty(self::$x)', $overhead); -// read_static(N); -// $t = end_test($t, '$x = Foo::$x', $overhead); -// write_static(N); -// $t = end_test($t, 'Foo::$x = 0', $overhead); -// isset_static(N); -// $t = end_test($t, 'isset(Foo::$x)', $overhead); -// empty_static(N); -// $t = end_test($t, 'empty(Foo::$x)', $overhead); -// Foo::call_static(N); -// $t = end_test($t, 'self::f()', $overhead); -// call_static(N); -// $t = end_test($t, 'Foo::f()', $overhead); -// $x = new Foo(); -// $x->read_prop(N); -// $t = end_test($t, '$x = $this->x', $overhead); -// $x->write_prop(N); -// $t = end_test($t, '$this->x = 0', $overhead); -// $x->assign_add_prop(N); -// $t = end_test($t, '$this->x += 2', $overhead); -// $x->pre_inc_prop(N); -// $t = end_test($t, '++$this->x', $overhead); -// $x->pre_dec_prop(N); -// $t = end_test($t, '--$this->x', $overhead); -// $x->post_inc_prop(N); -// $t = end_test($t, '$this->x++', $overhead); -// $x->post_dec_prop(N); -// $t = end_test($t, '$this->x--', $overhead); -// $x->isset_prop(N); -// $t = end_test($t, 'isset($this->x)', $overhead); -// $x->empty_prop(N); -// $t = end_test($t, 'empty($this->x)', $overhead); -// $x->call(N); -// $t = end_test($t, '$this->f()', $overhead); -// $x->read_const(N); -// $t = end_test($t, '$x = Foo::TEST', $overhead); + Foo::write_static(N); + $t = end_test($t, 'self::$x = 0', $overhead); + Foo::isset_static(N); + $t = end_test($t, 'isset(self::$x)', $overhead); + Foo::empty_static(N); + $t = end_test($t, 'empty(self::$x)', $overhead); + read_static(N); + $t = end_test($t, '$x = Foo::$x', $overhead); + write_static(N); + $t = end_test($t, 'Foo::$x = 0', $overhead); + isset_static(N); + $t = end_test($t, 'isset(Foo::$x)', $overhead); + empty_static(N); + $t = end_test($t, 'empty(Foo::$x)', $overhead); + Foo::call_static(N); + $t = end_test($t, 'self::f()', $overhead); + call_static(N); + $t = end_test($t, 'Foo::f()', $overhead); + $x = new Foo(); + $x->read_prop(N); + $t = end_test($t, '$x = $this->x', $overhead); + $x->write_prop(N); + $t = end_test($t, '$this->x = 0', $overhead); + $x->assign_add_prop(N); + $t = end_test($t, '$this->x += 2', $overhead); + $x->pre_inc_prop(N); + $t = end_test($t, '++$this->x', $overhead); + $x->pre_dec_prop(N); + $t = end_test($t, '--$this->x', $overhead); + $x->post_inc_prop(N); + $t = end_test($t, '$this->x++', $overhead); + $x->post_dec_prop(N); + $t = end_test($t, '$this->x--', $overhead); + $x->isset_prop(N); + $t = end_test($t, 'isset($this->x)', $overhead); + $x->empty_prop(N); + $t = end_test($t, 'empty($this->x)', $overhead); + $x->call(N); + $t = end_test($t, '$this->f()', $overhead); + $x->read_const(N); + $t = end_test($t, '$x = Foo::TEST', $overhead); // create_object(N); // $t = end_test($t, 'new Foo()', $overhead); -// read_const(N); -// $t = end_test($t, '$x = TEST', $overhead); + read_const(N); + $t = end_test($t, '$x = TEST', $overhead); // read_auto_global(N); // $t = end_test($t, '$x = $_GET', $overhead); // read_global_var(N); diff --git a/src/Php/ClassDef.php b/src/Php/ClassDef.php index 610c2eba..9854a796 100644 --- a/src/Php/ClassDef.php +++ b/src/Php/ClassDef.php @@ -45,8 +45,24 @@ class ClassDef extends ClassLikeDef return isset($this->properties[$property]); } + public function hasConstant(string $name): bool + { + return isset($this->constants[$name]); + } + + public function getProperty($property): PropertyDef { return $this->properties[$property]; } + + public function getMethod($method): MethodDef + { + return $this->methods[$method]; + } + + public function getConstant($name): ConstantDef + { + return $this->constants[$name]; + } } diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 4c386efa..b028cf63 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -121,6 +121,7 @@ class CompilerBase extends \PhpAot\Core\Translator protected string $interface = ''; /** + * key 类名,包含命名空间 * @var array */ protected array $classes = []; @@ -614,7 +615,7 @@ class CompilerBase extends \PhpAot\Core\Translator return 'php_get_class(' . $id . ', ' . $this->getLiteralString($className) . ')'; } - protected function getFuncPtr(string $funcName): string + protected function getFuncPtr(string $funcName, bool $macro = true): string { if (isset($this->funcMap[$funcName])) { $id = $this->funcMap[$funcName]; @@ -622,7 +623,11 @@ class CompilerBase extends \PhpAot\Core\Translator $id = $this->funcIndex++; $this->funcMap[$funcName] = $id; } - return $id . ', ' . $this->getLiteralString($funcName); + if ($macro) { + return $id . ', ' . $this->getLiteralString($funcName); + } else { + return 'php_get_func(' . $id . ', ' . $this->getLiteralString($funcName) . ')'; + } } protected function parseFunctionDeclaration(Node\Stmt\Function_|Node\Stmt\ClassMethod $v): FunctionDef @@ -999,10 +1004,13 @@ class CompilerBase extends \PhpAot\Core\Translator return $this->parseAssignArrayDim($left, $right); } if ($left->getType() === 'Expr_StaticPropertyFetch') { + $value = $this->trimBrackets($this->parseExpr($right)); + $native = $this->parseNativeStaticPropertyFetch($left); + if ($native) { + return $native . ' = ' . $value; + } $class = $this->identifierToStr($left->class); $propName = $this->identifierToStr($left->name); - $value = $this->trimBrackets($this->parseExpr($right)); - return "php::setStaticProperty({$class}, {$propName}, {$value})"; } @@ -1239,6 +1247,18 @@ class CompilerBase extends \PhpAot\Core\Translator return isset($this->localVars[$name]); } + protected function hasNativeClass(string $name): bool + { + $name = trim($name, '\\'); + return array_key_exists($name, $this->classes); + } + + protected function getClassDef(string $name): ClassDef + { + $name = trim($name, '\\'); + return $this->classes[$name]; + } + protected function resetReturnType(string $type): void { $this->functionDef->returnType = $type; @@ -1751,6 +1771,7 @@ class CompilerBase extends \PhpAot\Core\Translator return $code; } $fn = $this->getFuncPtr($name); + $this->beforeStmtLines[] = "// Func Call: " . $name; $call = $silent ? 'CALL_SILENT' : 'CALL'; } else { $tmpVar = $this->genTmpVarName(); @@ -1827,20 +1848,15 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parsePostOp($expr, string $op): string { - if ($this->isVarExpr($expr->var)) { + if ($this->isVarExpr($expr->var) or $this->isPropertyFetch($expr->var)) { return $this->parseIdentifier($expr->var) . str_repeat($op, 2); } - if ($this->isPropertyFetch($expr->var)) { - $obj = $this->parseIdentifier($expr->var->var); - $prop = $this->identifierToStr($expr->var->name); - $tmpVar = $this->genTmpVarName(); - $this->addLocalVar($tmpVar, self::TYPE_VAR); - $this->beforeStmtLines[] = $tmpVar . ' = ' . $obj . '.getProperty(' . $prop . ');'; - $this->afterStmtLines[] = $obj . '.setProperty(' . $prop . ', ' . $tmpVar . ' ' . $op . ' 1);'; - - return $tmpVar; - } if ($this->isStaticPropertyFetch($expr->var)) { + $native = $this->parseNativeStaticPropertyFetch($expr->var); + if ($native) { + return $native . str_repeat($op, 2); + } + $class = $this->identifierToStr($expr->var->class); $prop = $this->identifierToStr($expr->var->name); $tmpVar = $this->genTmpVarName(); @@ -2171,7 +2187,7 @@ class CompilerBase extends \PhpAot\Core\Translator abort($expr); } $name = $this->parseIdentifier($expr->name); - if ($this->hasConstant($name)) { + if ($this->isNameExpr($expr->name) and $this->hasConstant($name)) { return $this->getConstant($name); } if ($name === 'null') { @@ -2186,7 +2202,15 @@ class CompilerBase extends \PhpAot\Core\Translator if ($name === 'PHP_EOL') { return '"' . $this->escapeString(PHP_EOL) . '"'; } - + if ($this->isNameExpr($expr->name)) { + if (str_contains($name, '::')) { + $ns = explode('::', $name)[0]; + $ce = $this->getClassEntryPtr($ns[0]); + return 'php::constant(' . $ce . ', ' . $this->getLiteralString($ns[1]) . ')'; + } else { + return 'php::constant(nullptr, ' . $this->getLiteralString($name) . ')'; + } + } return 'php::constant("' . $this->escapeString($name) . '")'; } @@ -2654,13 +2678,19 @@ class CompilerBase extends \PhpAot\Core\Translator return $this->parseIdentifier($var->var) . '.offsetExists(' . $this->parseIdentifier($var->dim) . ')'; } if ($var instanceof Node\Expr\StaticPropertyFetch) { + $nativeProp = $this->findNativeStaticProperty($var, $class, $namespace); + if ($nativeProp) { + return 'true'; + } return 'php::hasStaticProperty(' . $this->identifierToStr($var->class) . ', ' . $this->identifierToStr($var->name) . ')'; } if ($var instanceof Node\Expr\PropertyFetch) { - $object = $var->var; - $prop = $var->name; - - return $this->parseIdentifier($object) . '.propertyExists(' . $this->identifierToStr($prop) . ')'; + $prop = $var->name; + $object = $this->parseIdentifier($var->var); + if ($object === 'this_' and $this->isIdExpr($prop)) { + return $this->escapeBool($this->classDef->hasProperty($this->parseIdentifier($prop))); + } + return $object . '.propertyExists(' . $this->identifierToStr($prop) . ')'; } abort($var); } @@ -2819,30 +2849,87 @@ class CompilerBase extends \PhpAot\Core\Translator return $this->parseParentMethodCall($expr); } $method = $this->parseIdentifier($expr->name); - $fn = '"' . $class . '::' . $method . '"'; + $ce = $this->getClassEntryPtr($class); + $fn = $ce . ', ' . $this->getFuncPtr($class . '::' . $method, false); } + $call = 'php::call'; if (empty($expr->args)) { - return 'php::call(' . $fn . ')'; + return $call . '(' . $fn . ')'; + } + return $call . '(' . $fn . ', {' . $this->parseCallArgs($expr->args) . '})'; + } + + protected function findNativeStaticProperty(Node\Expr\StaticPropertyFetch $expr, ?string &$class, ?string &$namespace): ?PropertyDef + { + if ($this->isNameExpr($expr->class) and $this->isIdExpr($expr->name)) { + $class = $this->parseIdentifier($expr->class); + $prop = $this->parseIdentifier($expr->name); + if ($class === 'self') { + $classDef = $this->classDef; + $class = $this->class; + $namespace = $this->namespace; + } else { + $classDef = $this->classes[$class]; + $namespace = $classDef->namespace; + } + if ($classDef->hasProperty($prop)) { + $propDef = $classDef->getProperty($prop); + if ($propDef->isStatic()) { + return $propDef; + } + } } - return 'php::call(' . $fn . ', {' . $this->parseCallArgs($expr->args) . '})'; + return null; } - protected function parseStaticPropertyFetch(Node $expr): string + protected function parseNativeStaticPropertyFetch(Node\Expr\StaticPropertyFetch $expr): string|bool { + $nativeProp = $this->findNativeStaticProperty($expr, $class, $namespace); + if ($nativeProp) { + $classPtr = $this->getClassEntryPtr($class); + $propOffset = self::PREFIX . $this->getPropertyOffset($nativeProp->name, $class, $namespace); + return 'php::getStaticProperty(' . $classPtr . ', ' . $propOffset . ')'; + } + return false; + } + + protected function parseStaticPropertyFetch(Node\Expr\StaticPropertyFetch $expr): string + { + $native = $this->parseNativeStaticPropertyFetch($expr); + if ($native) { + return $native; + } return 'php::getStaticProperty(' . $this->identifierToStr($expr->class) . ', ' . $this->identifierToStr($expr->name) . ')'; } protected function parseClassConstFetch(Node\Expr\ClassConstFetch $expr): string { $class = $this->parseIdentifier($expr->class); - $class = ($class === 'self' or $class === 'this_') ? $this->class : $class; + $self = false; + if ($class === 'self' or $class === 'this_') { + $self = true; + $class = $this->class; + } + $const = $this->escapeString($this->parseIdentifier($expr->name)); - $class = $this->escapeString($this->getNamespacedClassName($class)); + $class = $this->getNamespacedClassName($class); if ($const === 'class') { - return '"' . $class . '"'; + return '"' . $this->escapeString($class) . '"'; + } + if (($self or $this->isNameExpr($expr->class)) and $this->isIdExpr($expr->name)) { + if ($this->hasNativeClass($class)) { + $classDef = $this->getClassDef($class); + if ($classDef->hasConstant($const)) { + return $classDef->getConstant($const)->value; + } + } + $ce = $this->getClassEntryPtr($class); + return 'php::constant(' . $ce . ', ' . $this->getLiteralString($const) . ')'; + } else { + $name = $class . '::' . $const; + $name = $this->getLiteralString($name); + return 'php::constant(' . $name . ')'; } - - return 'php::constant("' . $class . '::' . $const . '")'; } protected function parseThrow(mixed $expr): string @@ -3109,11 +3196,11 @@ class CompilerBase extends \PhpAot\Core\Translator $nativeFunc = $this->getNativeName($method, $this->namespace, $this->class); } elseif (isset($this->objects[$object])) { $class = $this->objects[$object]; - if (!isset($this->classes[$class])) { + if (!$this->hasNativeClass($class)) { return false; } $classDef = $this->classes[$class]; - if (!isset($classDef->methods[$method])) { + if (!$classDef->hasMethod($method)) { return false; } $methodDef = $classDef->methods[$method]; diff --git a/src/Php/Preprocessor.php b/src/Php/Preprocessor.php index cd1ed02f..1f183f78 100644 --- a/src/Php/Preprocessor.php +++ b/src/Php/Preprocessor.php @@ -98,10 +98,12 @@ class Preprocessor extends CompilerBase break; case 'Stmt_Declare': case 'Stmt_Use': - case 'Stmt_Const': case 'Stmt_Interface': case 'Stmt_Nop': break; + case 'Stmt_Const': + $this->parseConstDef($v); + break; default: $this->fatalError($v, 'Unsupported statement: ' . $type); break; diff --git a/src/Php/PropertyDef.php b/src/Php/PropertyDef.php index 6a4c1605..614ae7d4 100644 --- a/src/Php/PropertyDef.php +++ b/src/Php/PropertyDef.php @@ -39,4 +39,9 @@ class PropertyDef { return !$this->isPrivate() && !$this->isProtected(); } + + public function isStatic(): bool + { + return $this->flags & Modifiers::STATIC; + } } diff --git a/tests/aot/prop-dec-inc.phpt b/tests/aot/prop-dec-inc.phpt new file mode 100644 index 00000000..19e1fb39 --- /dev/null +++ b/tests/aot/prop-dec-inc.phpt @@ -0,0 +1,28 @@ +--TEST-- +object property inc/dec +--FILE-- +a++; + var_dump($o->a); + $o->a--; + var_dump($o->a); + + TestObj::$b++; + var_dump(TestObj::$b); + TestObj::$b--; + var_dump(TestObj::$b); +} +?> +--EXPECT-- +int(1000) +int(999) +int(101) +int(100)