TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
12 KiB
311 lines
12 KiB
<?php
|
|
|
|
namespace PhpAot\Tests;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use PhpAot\Php\CompilerTest;
|
|
use PhpAot\Php\CompilerBase;
|
|
use PhpAot\Php\ArgInfo;
|
|
|
|
class TraitsTest extends TestCase
|
|
{
|
|
private string $testDir;
|
|
private CompilerTest $compiler;
|
|
private \ReflectionClass $ref;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->testDir = sys_get_temp_dir() . '/traits_test_' . uniqid();
|
|
mkdir($this->testDir, 0777, true);
|
|
$this->compiler = CompilerTest::create($this->testDir);
|
|
$this->ref = new \ReflectionClass($this->compiler);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
parent::tearDown();
|
|
if (is_dir($this->testDir)) {
|
|
$this->removeDirectory($this->testDir);
|
|
}
|
|
}
|
|
|
|
private function removeDirectory(string $dir): void
|
|
{
|
|
if (!is_dir($dir)) {
|
|
return;
|
|
}
|
|
$files = array_diff(scandir($dir), ['.', '..']);
|
|
foreach ($files as $file) {
|
|
$path = $dir . DIRECTORY_SEPARATOR . $file;
|
|
is_dir($path) ? $this->removeDirectory($path) : unlink($path);
|
|
}
|
|
rmdir($dir);
|
|
}
|
|
|
|
private function invoke(string $method, ...$args): mixed
|
|
{
|
|
$m = $this->ref->getMethod($method);
|
|
$m->setAccessible(true);
|
|
return $m->invoke($this->compiler, ...$args);
|
|
}
|
|
|
|
// ========================================================================
|
|
// MagicMethodDetector::checkArgType
|
|
// ========================================================================
|
|
|
|
public function testCheckArgTypeExactMatch(): void
|
|
{
|
|
$this->assertTrue($this->invoke('checkArgType', 'php::Int', 'php::Int'));
|
|
$this->assertTrue($this->invoke('checkArgType', 'php::Str', 'php::Str'));
|
|
$this->assertTrue($this->invoke('checkArgType', 'php::Array', 'php::Array'));
|
|
}
|
|
|
|
public function testCheckArgTypeMismatch(): void
|
|
{
|
|
$this->assertFalse($this->invoke('checkArgType', 'php::Int', 'php::Str'));
|
|
$this->assertFalse($this->invoke('checkArgType', 'php::Float', 'php::Int'));
|
|
}
|
|
|
|
public function testCheckArgTypeVarMatchesAny(): void
|
|
{
|
|
// TYPE_VAR matches any expected type when canBeVar is true (default)
|
|
$this->assertTrue($this->invoke('checkArgType', 'php::Var', 'php::Int'));
|
|
$this->assertTrue($this->invoke('checkArgType', 'php::Var', 'php::Str'));
|
|
$this->assertTrue($this->invoke('checkArgType', 'php::Var', 'php::Array'));
|
|
}
|
|
|
|
public function testCheckArgTypeVarDoesNotMatchWhenCannotBeVar(): void
|
|
{
|
|
// TYPE_VAR does NOT match when canBeVar is false
|
|
$this->assertFalse($this->invoke('checkArgType', 'php::Var', 'php::Str', false));
|
|
$this->assertFalse($this->invoke('checkArgType', 'php::Var', 'php::Int', false));
|
|
}
|
|
|
|
public function testCheckArgTypeExactMatchWithCannotBeVar(): void
|
|
{
|
|
$this->assertTrue($this->invoke('checkArgType', 'php::Str', 'php::Str', false));
|
|
$this->assertTrue($this->invoke('checkArgType', 'php::Array', 'php::Array', false));
|
|
}
|
|
|
|
public function testCheckArgTypeVoid(): void
|
|
{
|
|
$this->assertTrue($this->invoke('checkArgType', 'void', 'void'));
|
|
$this->assertFalse($this->invoke('checkArgType', 'void', 'php::Str'));
|
|
}
|
|
|
|
// ========================================================================
|
|
// FuncCallOptimizer::isValidDefineName
|
|
// ========================================================================
|
|
|
|
public function testIsValidDefineNameValid(): void
|
|
{
|
|
$this->assertTrue($this->invoke('isValidDefineName', 'MY_CONSTANT'));
|
|
$this->assertTrue($this->invoke('isValidDefineName', 'APP_NAME'));
|
|
$this->assertTrue($this->invoke('isValidDefineName', '_PRIVATE'));
|
|
$this->assertTrue($this->invoke('isValidDefineName', 'camelCase'));
|
|
$this->assertTrue($this->invoke('isValidDefineName', 'Test123'));
|
|
$this->assertTrue($this->invoke('isValidDefineName', '_'));
|
|
}
|
|
|
|
public function testIsValidDefineNameInvalid(): void
|
|
{
|
|
$this->assertFalse($this->invoke('isValidDefineName', '123abc')); // starts with digit
|
|
$this->assertFalse($this->invoke('isValidDefineName', 'has space')); // contains space
|
|
$this->assertFalse($this->invoke('isValidDefineName', 'has-dash')); // contains dash
|
|
$this->assertFalse($this->invoke('isValidDefineName', '')); // empty
|
|
$this->assertFalse($this->invoke('isValidDefineName', '0abc')); // starts with zero
|
|
}
|
|
|
|
// ========================================================================
|
|
// StdContainerParser::isStdContainerType
|
|
// ========================================================================
|
|
|
|
public function testIsStdContainerTypeTrue(): void
|
|
{
|
|
$this->assertTrue($this->invoke('isStdContainerType', CompilerBase::TYPE_STD_ARRAY));
|
|
$this->assertTrue($this->invoke('isStdContainerType', CompilerBase::TYPE_STD_VECTOR));
|
|
$this->assertTrue($this->invoke('isStdContainerType', CompilerBase::TYPE_STD_MAP));
|
|
$this->assertTrue($this->invoke('isStdContainerType', CompilerBase::TYPE_STD_UNORDERED_MAP));
|
|
}
|
|
|
|
public function testIsStdContainerTypeFalse(): void
|
|
{
|
|
$this->assertFalse($this->invoke('isStdContainerType', CompilerBase::TYPE_ARRAY));
|
|
$this->assertFalse($this->invoke('isStdContainerType', CompilerBase::TYPE_INT));
|
|
$this->assertFalse($this->invoke('isStdContainerType', CompilerBase::TYPE_VAR));
|
|
$this->assertFalse($this->invoke('isStdContainerType', CompilerBase::TYPE_OBJECT));
|
|
}
|
|
|
|
// ========================================================================
|
|
// StdContainerParser::getStdTypeKey
|
|
// ========================================================================
|
|
|
|
public function testGetStdTypeKeyBasic(): void
|
|
{
|
|
$info = [
|
|
'kind' => 'vector',
|
|
'decl' => 'php::StdVector<php::Int>',
|
|
'type' => 'php::Int',
|
|
'class' => '',
|
|
];
|
|
$key = $this->invoke('getStdTypeKey', $info);
|
|
$this->assertStringContainsString('kind=vector', $key);
|
|
$this->assertStringContainsString('decl=php::StdVector<php::Int>', $key);
|
|
$this->assertStringContainsString('type=php::Int', $key);
|
|
$this->assertStringContainsString('class=', $key);
|
|
}
|
|
|
|
public function testGetStdTypeKeyWithClass(): void
|
|
{
|
|
$info = [
|
|
'kind' => 'vector',
|
|
'decl' => 'php::StdVector<php::Object>',
|
|
'type' => 'php::Object',
|
|
'class' => 'App\\Entity\\User',
|
|
];
|
|
$key = $this->invoke('getStdTypeKey', $info);
|
|
$this->assertStringContainsString('class=App\\Entity\\User', $key);
|
|
}
|
|
|
|
public function testGetStdTypeKeyWithKeyType(): void
|
|
{
|
|
$info = [
|
|
'kind' => 'map',
|
|
'decl' => 'php::StdMap<php::Str, php::Int>',
|
|
'type' => 'php::Int',
|
|
'class' => '',
|
|
'keyType' => 'php::Str',
|
|
];
|
|
$key = $this->invoke('getStdTypeKey', $info);
|
|
$this->assertStringContainsString('keyType=php::Str', $key);
|
|
}
|
|
|
|
public function testGetStdTypeKeyWithoutKeyType(): void
|
|
{
|
|
$info = [
|
|
'kind' => 'array',
|
|
'decl' => 'php::StdArray<php::Int, 10>',
|
|
'type' => 'php::Int',
|
|
'class' => '',
|
|
];
|
|
$key = $this->invoke('getStdTypeKey', $info);
|
|
$this->assertStringNotContainsString('keyType', $key);
|
|
}
|
|
|
|
public function testGetStdTypeKeyUnorderedMap(): void
|
|
{
|
|
$info = [
|
|
'kind' => 'unordered_map',
|
|
'decl' => 'php::StdUnorderedMap<php::Str, php::Int>',
|
|
'type' => 'php::Int',
|
|
'class' => '',
|
|
'keyType' => 'php::Str',
|
|
];
|
|
$key = $this->invoke('getStdTypeKey', $info);
|
|
$this->assertStringContainsString('kind=unordered_map', $key);
|
|
}
|
|
|
|
// ========================================================================
|
|
// PropertyPromotion::genPropertyPromotion
|
|
// ========================================================================
|
|
|
|
public function testGenPropertyPromotion(): void
|
|
{
|
|
// Need context initialized for genCharPtr
|
|
$this->invoke('resetFunction');
|
|
|
|
$argInfo = new ArgInfo();
|
|
$argInfo->name = 'title';
|
|
$argInfo->type = 'php::Str';
|
|
|
|
$result = $this->invoke('genPropertyPromotion', $argInfo);
|
|
$this->assertStringContainsString('this_.setProperty', $result);
|
|
$this->assertStringContainsString('title', $result);
|
|
}
|
|
|
|
public function testGenPropertyPromotionWithNumber(): void
|
|
{
|
|
$this->invoke('resetFunction');
|
|
|
|
$argInfo = new ArgInfo();
|
|
$argInfo->name = 'count';
|
|
$argInfo->type = 'php::Int';
|
|
|
|
$result = $this->invoke('genPropertyPromotion', $argInfo);
|
|
$this->assertStringContainsString('count', $result);
|
|
}
|
|
|
|
// ========================================================================
|
|
// ClosureGenerator::genScopeSwitchCode
|
|
// ========================================================================
|
|
|
|
public function testGenScopeSwitchCode(): void
|
|
{
|
|
$this->invoke('resetFunction');
|
|
|
|
$result = $this->invoke('genScopeSwitchCode');
|
|
$this->assertStringContainsString('php_switch_scope', $result);
|
|
$this->assertStringContainsString('ON_SCOPE_EXIT', $result);
|
|
$this->assertStringContainsString('php_restore_scope', $result);
|
|
}
|
|
|
|
public function testGenScopeSwitchCodeTemplate(): void
|
|
{
|
|
$this->invoke('resetFunction');
|
|
|
|
$result = $this->invoke('genScopeSwitchCode');
|
|
// Should have the pattern: auto tmp_var_X = php_switch_scope(this_);
|
|
$this->assertStringContainsString('auto', $result);
|
|
$this->assertStringContainsString('= php_switch_scope(this_)', $result);
|
|
}
|
|
|
|
// ========================================================================
|
|
// StdContainerParser::getStdArrayDecl
|
|
// ========================================================================
|
|
|
|
public function testGetStdArrayDecl(): void
|
|
{
|
|
$result = $this->invoke('getStdArrayDecl', 'php::Int', [10]);
|
|
$this->assertEquals('php::StdArray<php::Int, 10>', $result);
|
|
}
|
|
|
|
public function testGetStdArrayDeclNested(): void
|
|
{
|
|
$result = $this->invoke('getStdArrayDecl', 'php::Int', [3, 4]);
|
|
$this->assertEquals('php::StdArray<php::StdArray<php::Int, 4>, 3>', $result);
|
|
}
|
|
|
|
public function testGetStdArrayDeclFloat(): void
|
|
{
|
|
$result = $this->invoke('getStdArrayDecl', 'php::Float', [5]);
|
|
$this->assertEquals('php::StdArray<php::Float, 5>', $result);
|
|
}
|
|
|
|
// ========================================================================
|
|
// StdContainerParser::getStdMapDecl
|
|
// ========================================================================
|
|
|
|
public function testGetStdMapDecl(): void
|
|
{
|
|
$result = $this->invoke('getStdMapDecl', 'std::map', 'php::Str', 'php::Int');
|
|
$this->assertEquals('std::map<php::Str, php::Int>', $result);
|
|
}
|
|
|
|
public function testGetStdMapDeclUnordered(): void
|
|
{
|
|
$result = $this->invoke('getStdMapDecl', 'std::unordered_map', 'php::Int', 'php::Str');
|
|
$this->assertEquals('std::unordered_map<php::Int, php::Str>', $result);
|
|
}
|
|
|
|
// ========================================================================
|
|
// StdContainerParser::getStdValueTypeBytes
|
|
// ========================================================================
|
|
|
|
public function testGetStdValueTypeBytes(): void
|
|
{
|
|
$this->assertGreaterThan(0, $this->invoke('getStdValueTypeBytes', CompilerBase::TYPE_INT));
|
|
$this->assertGreaterThan(0, $this->invoke('getStdValueTypeBytes', CompilerBase::TYPE_FLOAT));
|
|
$this->assertGreaterThan(0, $this->invoke('getStdValueTypeBytes', CompilerBase::TYPE_BOOL));
|
|
}
|
|
}
|
|
|