- Add function call cache infrastructure with typephp_call_cached wrapper - Implement method call caching using typephp_call_method_cached for named methods - Add scoped method call caching via typephp_call_method_scoped_cached for dynamic scopes - Create late-static-bound class entry tracking with getCalledCeExpr and getCalledClassExpr - Implement lazy static property slot resolution with typephp_get_static_property_cached - Add benchmark cases for dynamic static method calls and scoped method invocations - Update call cache test expectations to reflect new cached call implementations - Integrate call caching into function and method call compilation paths - Add nullsafe method call caching support in nullsafe access trait - Implement static property slot registration and resolution mechanisms - Update native property handling to use cached static property access - Add comprehensive test coverage for various dynamic call scenariosmaster
parent
02a98ffc61
commit
87c7eda964
29 changed files with 738 additions and 81 deletions
@ -0,0 +1,27 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
class StaticPropertyFunctionLocalCache |
||||||
|
{ |
||||||
|
public static mixed $value = 1; |
||||||
|
|
||||||
|
public static function noStaticProperty(): int |
||||||
|
{ |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
public static function repeatedSelf(): array |
||||||
|
{ |
||||||
|
self::$value = null; |
||||||
|
return [self::$value, self::$value]; |
||||||
|
} |
||||||
|
|
||||||
|
public static function repeatedStatic(): array |
||||||
|
{ |
||||||
|
static::$value = null; |
||||||
|
return [static::$value, static::$value, static::class, static::class]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
} |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use TypePhp\CompilerTest; |
||||||
|
use TypePhp\Exception\TestError; |
||||||
|
|
||||||
|
class StaticPropertyFunctionLocalCacheTest extends BaseTest |
||||||
|
{ |
||||||
|
public function testSlotsAndCalledScopeAreGeneratedOnlyForFunctionsThatUseThem(): void |
||||||
|
{ |
||||||
|
global $translator; |
||||||
|
|
||||||
|
$compiler = CompilerTest::create(TYPEPHP_ROOT_PATH); |
||||||
|
$translator = $compiler; |
||||||
|
$source = TYPEPHP_ROOT_PATH . '/phpunit/code/static-property-function-local-cache.php'; |
||||||
|
$compiler->addFiles([$source]); |
||||||
|
$compiler->prepareFile($source); |
||||||
|
try { |
||||||
|
$output = $compiler->convertFile($source); |
||||||
|
} catch (TestError $error) { |
||||||
|
self::fail($error->getMessage()); |
||||||
|
} |
||||||
|
$code = file_get_contents($output); |
||||||
|
self::assertIsString($code); |
||||||
|
|
||||||
|
self::assertMatchesRegularExpression( |
||||||
|
'/php_staticpropertyfunctionlocalcache__nostaticproperty\(.*?return php::toInt\(1L\);\n}/s', |
||||||
|
$code, |
||||||
|
); |
||||||
|
preg_match( |
||||||
|
'/php_staticpropertyfunctionlocalcache__nostaticproperty\(.*?\n}/s', |
||||||
|
$code, |
||||||
|
$noStatic, |
||||||
|
); |
||||||
|
self::assertStringNotContainsString('_typephp_static_property_slot_', $noStatic[0]); |
||||||
|
self::assertStringNotContainsString('_typephp_called_ce', $noStatic[0]); |
||||||
|
|
||||||
|
preg_match('/php_staticpropertyfunctionlocalcache__repeatedself\(.*?\n}/s', $code, $selfMethod); |
||||||
|
self::assertSame(1, substr_count($selfMethod[0], 'zval *_typephp_static_property_slot_0 = nullptr;')); |
||||||
|
self::assertSame(1, substr_count($selfMethod[0], 'const auto _typephp_static_property_0')); |
||||||
|
self::assertSame(1, substr_count($selfMethod[0], 'typephp_get_static_property_cached(')); |
||||||
|
self::assertSame(3, substr_count($selfMethod[0], '_typephp_static_property_0()')); |
||||||
|
self::assertSame(1, substr_count($selfMethod[0], 'typephp_get_static_property_slot(get_persistent_class(')); |
||||||
|
self::assertStringNotContainsString('_typephp_called_ce', $selfMethod[0]); |
||||||
|
|
||||||
|
preg_match('/php_staticpropertyfunctionlocalcache__repeatedstatic\(.*?\n}/s', $code, $staticMethod); |
||||||
|
self::assertSame(1, substr_count($staticMethod[0], 'typephp_get_called_ce(this_)')); |
||||||
|
self::assertSame(1, substr_count($staticMethod[0], 'typephp_get_called_class(_typephp_called_ce)')); |
||||||
|
self::assertSame(1, substr_count($staticMethod[0], 'zval *_typephp_static_property_slot_0 = nullptr;')); |
||||||
|
self::assertSame(1, substr_count($staticMethod[0], 'const auto _typephp_static_property_0')); |
||||||
|
self::assertSame(1, substr_count($staticMethod[0], 'typephp_get_static_property_cached(')); |
||||||
|
self::assertSame(3, substr_count($staticMethod[0], '_typephp_static_property_0()')); |
||||||
|
self::assertSame(1, substr_count($staticMethod[0], 'typephp_get_static_property_slot(_typephp_called_ce')); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,89 @@ |
|||||||
|
--TEST-- |
||||||
|
Static-property slot caches retain live values, references, inheritance and late static binding |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class StaticSlotBase |
||||||
|
{ |
||||||
|
public static mixed $shared = 'base'; |
||||||
|
public static mixed $separate = 'base'; |
||||||
|
|
||||||
|
public static function mutateShared(): array |
||||||
|
{ |
||||||
|
$before = self::$shared; |
||||||
|
self::$shared = null; |
||||||
|
$afterNull = self::$shared; |
||||||
|
$reference = &self::$shared; |
||||||
|
$reference = 'reference'; |
||||||
|
return [$before, $afterNull, self::$shared]; |
||||||
|
} |
||||||
|
|
||||||
|
public static function mutateLate(string $value): array |
||||||
|
{ |
||||||
|
$before = static::$separate; |
||||||
|
static::$separate = null; |
||||||
|
$afterNull = static::$separate; |
||||||
|
static::$separate = $value; |
||||||
|
return [static::class, $before, $afterNull, static::$separate]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class StaticSlotInherited extends StaticSlotBase |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
class StaticSlotChildA extends StaticSlotBase |
||||||
|
{ |
||||||
|
public static mixed $separate = 'a'; |
||||||
|
} |
||||||
|
|
||||||
|
class StaticSlotChildB extends StaticSlotBase |
||||||
|
{ |
||||||
|
public static mixed $separate = 'b'; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
var_dump(StaticSlotBase::mutateShared()); |
||||||
|
var_dump(StaticSlotInherited::$shared); |
||||||
|
StaticSlotInherited::$shared = 'child-write'; |
||||||
|
var_dump(StaticSlotBase::$shared); |
||||||
|
|
||||||
|
var_dump(StaticSlotChildA::mutateLate('A')); |
||||||
|
var_dump(StaticSlotChildB::mutateLate('B')); |
||||||
|
var_dump(StaticSlotChildA::$separate, StaticSlotChildB::$separate); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
array(3) { |
||||||
|
[0]=> |
||||||
|
string(4) "base" |
||||||
|
[1]=> |
||||||
|
NULL |
||||||
|
[2]=> |
||||||
|
&string(9) "reference" |
||||||
|
} |
||||||
|
string(9) "reference" |
||||||
|
string(11) "child-write" |
||||||
|
array(4) { |
||||||
|
[0]=> |
||||||
|
string(16) "StaticSlotChildA" |
||||||
|
[1]=> |
||||||
|
string(1) "a" |
||||||
|
[2]=> |
||||||
|
NULL |
||||||
|
[3]=> |
||||||
|
string(1) "A" |
||||||
|
} |
||||||
|
array(4) { |
||||||
|
[0]=> |
||||||
|
string(16) "StaticSlotChildB" |
||||||
|
[1]=> |
||||||
|
string(1) "b" |
||||||
|
[2]=> |
||||||
|
NULL |
||||||
|
[3]=> |
||||||
|
string(1) "B" |
||||||
|
} |
||||||
|
string(1) "A" |
||||||
|
string(1) "B" |
||||||
Loading…
Reference in new issue