- 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