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.
240 lines
11 KiB
240 lines
11 KiB
<?php
|
|
|
|
use TypePhp\Analysis\CompilationStatistics;
|
|
use TypePhp\Build\NanoExtensionSelector;
|
|
use TypePhp\Build\NanoSourceComposer;
|
|
use TypePhp\CompilerTest;
|
|
use TypePhp\Type;
|
|
|
|
final class CompilationStatisticsTest extends BaseTest
|
|
{
|
|
public function testConvertCollectsFunctionAndEmittedTypeUsage(): void
|
|
{
|
|
global $translator;
|
|
$compiler = CompilerTest::create(TYPEPHP_ROOT_PATH);
|
|
$translator = $compiler;
|
|
$file = dirname(__DIR__) . '/code/compilation_statistics.php';
|
|
$compiler->addFiles([$file]);
|
|
$compiler->prepareFile($file);
|
|
$compiler->convertFile($file);
|
|
|
|
$statistics = $compiler->getCompilationStatistics();
|
|
self::assertTrue($statistics->has(CompilationStatistics::FUNCTIONS, 'json_encode'));
|
|
self::assertTrue($statistics->has(CompilationStatistics::FUNCTIONS, 'time'));
|
|
self::assertTrue($statistics->has(CompilationStatistics::DIRECT_FUNCTIONS, 'json_encode'));
|
|
self::assertTrue($statistics->has(CompilationStatistics::DIRECT_FUNCTIONS, 'time'));
|
|
self::assertTrue($statistics->has(CompilationStatistics::TYPES, Type::STR));
|
|
|
|
$selection = (new NanoExtensionSelector())->select($statistics, ['date', 'json']);
|
|
self::assertSame(['date', 'json'], $selection->extensions);
|
|
self::assertSame([], $selection->features);
|
|
}
|
|
|
|
public function testStatisticsAreReusableByCategory(): void
|
|
{
|
|
$statistics = new CompilationStatistics();
|
|
$statistics->begin();
|
|
$statistics->record(CompilationStatistics::FUNCTIONS, 'json_encode');
|
|
$statistics->record(CompilationStatistics::FUNCTIONS, 'json_encode');
|
|
$statistics->record('future-module-data', 'example');
|
|
$statistics->finish();
|
|
|
|
self::assertSame(['json_encode' => 2], $statistics->get(CompilationStatistics::FUNCTIONS));
|
|
self::assertSame(['example' => 1], $statistics->get('future-module-data'));
|
|
}
|
|
|
|
public function testNanoSelectorUsesFunctionsClassesTypesAndDependencies(): void
|
|
{
|
|
$statistics = new CompilationStatistics();
|
|
$statistics->begin();
|
|
$statistics->record(CompilationStatistics::FUNCTIONS, 'preg_match');
|
|
$statistics->record(CompilationStatistics::RUNTIME_FUNCTIONS, 'preg_match');
|
|
$statistics->record(CompilationStatistics::CLASSES, 'ArrayObject');
|
|
$statistics->record(CompilationStatistics::TYPES, Type::BIGINT);
|
|
$statistics->finish();
|
|
|
|
$selection = (new NanoExtensionSelector())->select(
|
|
$statistics,
|
|
['standard', 'date', 'hash', 'json', 'pcre', 'random', 'reflection', 'spl', 'filter', 'bcmath'],
|
|
);
|
|
|
|
self::assertFalse($selection->completeFallback);
|
|
self::assertSame(['bcmath', 'json', 'pcre', 'spl'], $selection->extensions);
|
|
self::assertSame([], $selection->features);
|
|
}
|
|
|
|
public function testDynamicFunctionCallFallsBackToBuiltInExtensionsOnly(): void
|
|
{
|
|
$statistics = new CompilationStatistics();
|
|
$statistics->begin();
|
|
$statistics->record(CompilationStatistics::DYNAMIC_CAPABILITIES, 'function-call');
|
|
$statistics->finish();
|
|
|
|
$selection = (new NanoExtensionSelector())->select(
|
|
$statistics,
|
|
['curl', 'json', 'openssl', 'standard'],
|
|
);
|
|
|
|
self::assertTrue($selection->completeFallback);
|
|
self::assertSame(['json', 'standard'], $selection->extensions);
|
|
|
|
$composition = (new NanoSourceComposer(TYPEPHP_ROOT_PATH))->compose(
|
|
sys_get_temp_dir() . '/typephp-nano-dynamic-fallback-test',
|
|
'dynamic_fallback_test',
|
|
false,
|
|
$statistics,
|
|
);
|
|
self::assertFalse($this->containsSource($composition['packageSources'], 'main/network.c'));
|
|
self::assertFalse($this->containsSource($composition['packageSources'], 'main/streams/xp_socket.c'));
|
|
self::assertFalse($this->containsSource($composition['packageSources'], 'ext/curl/interface.c'));
|
|
self::assertFalse($this->containsSource($composition['packageSources'], 'ext/openssl/openssl.c'));
|
|
}
|
|
|
|
public function testDateRemainsWholeWhileStandardUsesFineGrainedFeatures(): void
|
|
{
|
|
$statistics = new CompilationStatistics();
|
|
$statistics->begin();
|
|
foreach (['date', 'gmdate', 'array_keys', 'trim'] as $function) {
|
|
$statistics->record(CompilationStatistics::FUNCTIONS, $function);
|
|
$statistics->record(CompilationStatistics::DIRECT_FUNCTIONS, $function);
|
|
}
|
|
$statistics->finish();
|
|
|
|
$selection = (new NanoExtensionSelector())->select($statistics, ['date', 'standard']);
|
|
|
|
self::assertSame(['date'], $selection->extensions);
|
|
self::assertSame(
|
|
['standard.array', 'standard.string'],
|
|
$selection->features,
|
|
);
|
|
}
|
|
|
|
public function testZendDispatchedDateFunctionSelectsTheFullExtension(): void
|
|
{
|
|
$statistics = new CompilationStatistics();
|
|
$statistics->begin();
|
|
$statistics->record(CompilationStatistics::FUNCTIONS, 'date_create');
|
|
$statistics->record(CompilationStatistics::RUNTIME_FUNCTIONS, 'date_create');
|
|
$statistics->finish();
|
|
|
|
$selection = (new NanoExtensionSelector())->select($statistics, ['date']);
|
|
|
|
self::assertSame(['date'], $selection->extensions);
|
|
self::assertSame([], $selection->features);
|
|
|
|
$composition = (new NanoSourceComposer(TYPEPHP_ROOT_PATH))->compose(
|
|
sys_get_temp_dir() . '/typephp-nano-date-component-test',
|
|
'date_component_test',
|
|
false,
|
|
$statistics,
|
|
);
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/date/php_date.c'));
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/date/lib/timelib.c'));
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'src/std/datetime.cc'));
|
|
self::assertStringContainsString('date_module_entry', file_get_contents($composition['registry']));
|
|
}
|
|
|
|
public function testDirectWrapperStillSelectsItsProvidingExtension(): void
|
|
{
|
|
$statistics = new CompilationStatistics();
|
|
$statistics->begin();
|
|
$statistics->record(CompilationStatistics::FUNCTIONS, 'json_encode');
|
|
$statistics->record(CompilationStatistics::DIRECT_FUNCTIONS, 'json_encode');
|
|
$statistics->finish();
|
|
|
|
$selection = (new NanoExtensionSelector())->select($statistics, ['json']);
|
|
|
|
self::assertSame(['json'], $selection->extensions);
|
|
self::assertSame([], $selection->features);
|
|
}
|
|
|
|
public function testNanoComposerAppliesStandardComponentSourcesAndDefines(): void
|
|
{
|
|
$statistics = new CompilationStatistics();
|
|
$statistics->begin();
|
|
$statistics->record(CompilationStatistics::FUNCTIONS, 'array_keys');
|
|
$statistics->record(CompilationStatistics::DIRECT_FUNCTIONS, 'array_keys');
|
|
$statistics->finish();
|
|
|
|
$composition = (new NanoSourceComposer(TYPEPHP_ROOT_PATH))->compose(
|
|
sys_get_temp_dir() . '/typephp-nano-component-test',
|
|
'component_test',
|
|
false,
|
|
$statistics,
|
|
);
|
|
|
|
self::assertContains('PHP_NANO_SELECTIVE=1', $composition['defines']);
|
|
self::assertContains('PHP_NANO_STANDARD_ARRAY=1', $composition['defines']);
|
|
self::assertContains('PHP_NANO_STANDARD_CORE=1', $composition['defines']);
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/standard/array.c'));
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/standard/math.c'));
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/standard/string.c'));
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/standard/html.c'));
|
|
self::assertFalse($this->containsSource($composition['packageSources'], 'ext/standard/base64.c'));
|
|
self::assertFalse($this->containsSource($composition['packageSources'], 'ext/date/php_date.c'));
|
|
$registry = file_get_contents($composition['registry']);
|
|
self::assertStringContainsString('basic_functions_module', $registry);
|
|
self::assertStringContainsString('random_module_entry', $registry);
|
|
self::assertStringNotContainsString('date_module_entry', $registry);
|
|
}
|
|
|
|
public function testNanoSplCompositionIncludesItsNativeLinkDependencies(): void
|
|
{
|
|
$statistics = new CompilationStatistics();
|
|
$statistics->begin();
|
|
$statistics->record(CompilationStatistics::CLASSES, 'ArrayObject');
|
|
$statistics->finish();
|
|
|
|
$composition = (new NanoSourceComposer(TYPEPHP_ROOT_PATH))->compose(
|
|
sys_get_temp_dir() . '/typephp-nano-spl-dependencies-test',
|
|
'spl_dependencies_test',
|
|
false,
|
|
$statistics,
|
|
);
|
|
|
|
self::assertSame(['json', 'spl'], $composition['selection']->extensions);
|
|
self::assertContains('PHP_NANO_STANDARD_VAR=1', $composition['defines']);
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/pcre/php_pcre.c'));
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/standard/var.c'));
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/standard/var_unserializer.c'));
|
|
$registry = file_get_contents($composition['registry']);
|
|
self::assertStringContainsString('pcre_module_entry', $registry);
|
|
self::assertStringContainsString('spl_module_entry', $registry);
|
|
}
|
|
|
|
public function testNanoComposerAlwaysIncludesSharedStandardSources(): void
|
|
{
|
|
$statistics = new CompilationStatistics();
|
|
$statistics->begin();
|
|
$statistics->record(CompilationStatistics::FUNCTIONS, 'json_encode');
|
|
$statistics->record(CompilationStatistics::DIRECT_FUNCTIONS, 'json_encode');
|
|
$statistics->finish();
|
|
|
|
$composition = (new NanoSourceComposer(TYPEPHP_ROOT_PATH))->compose(
|
|
sys_get_temp_dir() . '/typephp-nano-shared-standard-test',
|
|
'shared_standard_test',
|
|
false,
|
|
$statistics,
|
|
);
|
|
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/standard/array.c'));
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/standard/string.c'));
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/standard/html.c'));
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/standard/url.c'));
|
|
self::assertTrue($this->containsSource($composition['packageSources'], 'ext/standard/scanf.c'));
|
|
self::assertNotContains('PHP_NANO_STANDARD_ARRAY=1', $composition['defines']);
|
|
self::assertNotContains('PHP_NANO_STANDARD_STRING=1', $composition['defines']);
|
|
self::assertNotContains('PHP_NANO_STANDARD_ENCODING=1', $composition['defines']);
|
|
}
|
|
|
|
/** @param list<string> $sources */
|
|
private function containsSource(array $sources, string $suffix): bool
|
|
{
|
|
foreach ($sources as $source) {
|
|
if (str_ends_with(str_replace('\\', '/', $source), $suffix)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|