parent
7cf265cbc7
commit
13344ace72
36 changed files with 897 additions and 80 deletions
@ -0,0 +1,14 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeGlobalContainerValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function native_global_container(): void |
||||||
|
{ |
||||||
|
global $values; |
||||||
|
$values = std::vector(NativeGlobalContainerValue::class); |
||||||
|
$values[] = new NativeGlobalContainerValue(); |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeMixedPropertyReference |
||||||
|
{ |
||||||
|
public mixed $value = null; |
||||||
|
} |
||||||
|
|
||||||
|
function native_mixed_property_reference(): void |
||||||
|
{ |
||||||
|
$object = new NativeMixedPropertyReference(); |
||||||
|
$reference =& $object->value; |
||||||
|
} |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativePrivateConstantOwner |
||||||
|
{ |
||||||
|
private const int VALUE = 1; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
var_dump(NativePrivateConstantOwner::VALUE); |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeStaticContainerValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function native_static_container(): void |
||||||
|
{ |
||||||
|
static $values = std::vector(NativeStaticContainerValue::class); |
||||||
|
$values[] = new NativeStaticContainerValue(); |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeArrowContainerValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function native_container_arrow(): Closure |
||||||
|
{ |
||||||
|
$values = std::vector(NativeArrowContainerValue::class); |
||||||
|
return static fn(): int => count($values); |
||||||
|
} |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeClosureContainerValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function native_container_closure(): Closure |
||||||
|
{ |
||||||
|
$values = std::vector(NativeClosureContainerValue::class); |
||||||
|
return static function () use ($values): int { |
||||||
|
return count($values); |
||||||
|
}; |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeConvertedContainerValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function native_container_conversion(): array |
||||||
|
{ |
||||||
|
$values = std::vector(NativeConvertedContainerValue::class); |
||||||
|
return $values->toArray(); |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeDestructuredContainerValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function native_container_destructure(): void |
||||||
|
{ |
||||||
|
$values = std::array(NativeDestructuredContainerValue::class, 1); |
||||||
|
[$first] = $values; |
||||||
|
} |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeAnyPropertyContainerValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeContainerAnyPropertyHolder |
||||||
|
{ |
||||||
|
public any $value; |
||||||
|
} |
||||||
|
|
||||||
|
function native_container_native_any_property(): void |
||||||
|
{ |
||||||
|
$values = std::vector(NativeAnyPropertyContainerValue::class); |
||||||
|
$holder = new NativeContainerAnyPropertyHolder(); |
||||||
|
$holder->value = $values; |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativePhpArrayContainerValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function native_container_php_array(): void |
||||||
|
{ |
||||||
|
$values = std::vector(NativePhpArrayContainerValue::class); |
||||||
|
$array = []; |
||||||
|
$array[] = $values; |
||||||
|
} |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativePhpPropertyContainerValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
class NativeContainerPhpPropertyHolder |
||||||
|
{ |
||||||
|
public mixed $value; |
||||||
|
} |
||||||
|
|
||||||
|
function native_container_php_property(): void |
||||||
|
{ |
||||||
|
$values = std::vector(NativePhpPropertyContainerValue::class); |
||||||
|
$holder = new NativeContainerPhpPropertyHolder(); |
||||||
|
$holder->value = $values; |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeReferencedContainerValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function native_container_reference(): void |
||||||
|
{ |
||||||
|
$values = std::vector(NativeReferencedContainerValue::class); |
||||||
|
$alias =& $values; |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeReturnedContainerValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function native_container_return(): array |
||||||
|
{ |
||||||
|
$values = std::vector(NativeReturnedContainerValue::class); |
||||||
|
$values[] = new NativeReturnedContainerValue(); |
||||||
|
return $values; |
||||||
|
} |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeStaticPropertyContainerValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
class NativeContainerStaticPropertyHolder |
||||||
|
{ |
||||||
|
public static mixed $value; |
||||||
|
} |
||||||
|
|
||||||
|
function native_container_static_property(): void |
||||||
|
{ |
||||||
|
$values = std::vector(NativeStaticPropertyContainerValue::class); |
||||||
|
NativeContainerStaticPropertyHolder::$value = $values; |
||||||
|
} |
||||||
@ -0,0 +1,64 @@ |
|||||||
|
--TEST-- |
||||||
|
Native class: constants resolve entirely at compile time across inheritance |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
namespace NativeConstantTest { |
||||||
|
|
||||||
|
#[\Native] |
||||||
|
class BaseConfig |
||||||
|
{ |
||||||
|
private const int PRIVATE_VALUE = 7; |
||||||
|
protected const string PREFIX = 'native'; |
||||||
|
public const array ITEMS = [1, 2, 3]; |
||||||
|
|
||||||
|
public function privateValue(int $value = self::PRIVATE_VALUE): int |
||||||
|
{ |
||||||
|
return $value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[\Native] |
||||||
|
class ChildConfig extends BaseConfig |
||||||
|
{ |
||||||
|
public const string CLASS_NAME = self::class; |
||||||
|
public const string PARENT_NAME = parent::class; |
||||||
|
public const string LABEL = parent::PREFIX; |
||||||
|
|
||||||
|
public function label(string $value = self::LABEL): string |
||||||
|
{ |
||||||
|
return $value; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
namespace { |
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$config = new \NativeConstantTest\ChildConfig(); |
||||||
|
var_dump( |
||||||
|
\NativeConstantTest\ChildConfig::CLASS_NAME, |
||||||
|
\NativeConstantTest\ChildConfig::PARENT_NAME, |
||||||
|
\NativeConstantTest\ChildConfig::LABEL, |
||||||
|
\NativeConstantTest\ChildConfig::ITEMS, |
||||||
|
$config->privateValue(), |
||||||
|
$config->label(), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
string(30) "NativeConstantTest\ChildConfig" |
||||||
|
string(29) "NativeConstantTest\BaseConfig" |
||||||
|
string(6) "native" |
||||||
|
array(3) { |
||||||
|
[0]=> |
||||||
|
int(1) |
||||||
|
[1]=> |
||||||
|
int(2) |
||||||
|
[2]=> |
||||||
|
int(3) |
||||||
|
} |
||||||
|
int(7) |
||||||
|
string(6) "native" |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
--TEST-- |
||||||
|
Native class: construction and clone hooks root objects across automatic collection |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeConstructionPressure |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function createPressure(): void |
||||||
|
{ |
||||||
|
for ($i = 0; $i < 300000; $i++) { |
||||||
|
$filler = new NativeConstructionPressure(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeRootedConstruction |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
|
||||||
|
public function __construct(int $value) |
||||||
|
{ |
||||||
|
createPressure(); |
||||||
|
$this->value = $value; |
||||||
|
} |
||||||
|
|
||||||
|
public function __clone(): void |
||||||
|
{ |
||||||
|
createPressure(); |
||||||
|
$this->value++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$object = new NativeRootedConstruction(41); |
||||||
|
var_dump($object->value); |
||||||
|
|
||||||
|
$copy = clone $object; |
||||||
|
var_dump($object->value, $copy->value); |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
int(41) |
||||||
|
int(41) |
||||||
|
int(42) |
||||||
@ -0,0 +1,52 @@ |
|||||||
|
--TEST-- |
||||||
|
Native class: objects allocated by a finalizer survive the active sweep and are finalized later |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeFinalizerNewborn |
||||||
|
{ |
||||||
|
public function __destruct() |
||||||
|
{ |
||||||
|
global $newbornFinalized; |
||||||
|
$newbornFinalized++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeFinalizerCreator |
||||||
|
{ |
||||||
|
public function __destruct() |
||||||
|
{ |
||||||
|
global $creatorFinalized; |
||||||
|
$creatorFinalized++; |
||||||
|
$newborn = new NativeFinalizerNewborn(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeFinalizerFiller |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
global $creatorFinalized, $newbornFinalized; |
||||||
|
$creatorFinalized = 0; |
||||||
|
$newbornFinalized = 0; |
||||||
|
|
||||||
|
$creator = new NativeFinalizerCreator(); |
||||||
|
$creator = null; |
||||||
|
for ($i = 0; $i < 400000; $i++) { |
||||||
|
$filler = new NativeFinalizerFiller(); |
||||||
|
} |
||||||
|
|
||||||
|
var_dump($creatorFinalized === 1); |
||||||
|
var_dump($newbornFinalized === 1); |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
@ -0,0 +1,65 @@ |
|||||||
|
--TEST-- |
||||||
|
Native class: a throwing derived finalizer does not skip base finalizers |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeThrowingFinalizerBase |
||||||
|
{ |
||||||
|
public function __destruct() |
||||||
|
{ |
||||||
|
global $events; |
||||||
|
$events[] = 'base'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeThrowingFinalizerChild extends NativeThrowingFinalizerBase |
||||||
|
{ |
||||||
|
public function __destruct() |
||||||
|
{ |
||||||
|
global $events; |
||||||
|
$events[] = 'child'; |
||||||
|
throw new RuntimeException('child finalizer failed'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeFinalizerPressureObject |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function collectThrowingFinalizer(): void |
||||||
|
{ |
||||||
|
for ($i = 0; $i < 400000; $i++) { |
||||||
|
try { |
||||||
|
$filler = new NativeFinalizerPressureObject(); |
||||||
|
} catch (RuntimeException $error) { |
||||||
|
echo $error->getMessage(), "\n"; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
echo "finalizer did not run\n"; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
global $events; |
||||||
|
$events = []; |
||||||
|
|
||||||
|
$object = new NativeThrowingFinalizerChild(); |
||||||
|
$object = null; |
||||||
|
collectThrowingFinalizer(); |
||||||
|
var_dump($events); |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
child finalizer failed |
||||||
|
array(2) { |
||||||
|
[0]=> |
||||||
|
string(5) "child" |
||||||
|
[1]=> |
||||||
|
string(4) "base" |
||||||
|
} |
||||||
@ -0,0 +1,132 @@ |
|||||||
|
--TEST-- |
||||||
|
Native class: constructor, clone and finalizer exceptions preserve heap invariants |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class NativeLifecycleProbe |
||||||
|
{ |
||||||
|
public function __construct(private string $name) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public function __destruct() |
||||||
|
{ |
||||||
|
global $releasedProbes; |
||||||
|
$releasedProbes[] = $this->name; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeConstructorFailure |
||||||
|
{ |
||||||
|
public object $probe; |
||||||
|
|
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$this->probe = new NativeLifecycleProbe('constructor'); |
||||||
|
throw new RuntimeException('constructor failed'); |
||||||
|
} |
||||||
|
|
||||||
|
public function __destruct() |
||||||
|
{ |
||||||
|
global $nativeFinalizers; |
||||||
|
$nativeFinalizers[] = 'constructor'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeCloneFailure |
||||||
|
{ |
||||||
|
public object $probe; |
||||||
|
|
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$this->probe = new NativeLifecycleProbe('source'); |
||||||
|
} |
||||||
|
|
||||||
|
public function __clone(): void |
||||||
|
{ |
||||||
|
$this->probe = new NativeLifecycleProbe('clone'); |
||||||
|
throw new RuntimeException('clone failed'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeThrowingFinalizer |
||||||
|
{ |
||||||
|
public function __destruct() |
||||||
|
{ |
||||||
|
global $nativeFinalizers; |
||||||
|
$nativeFinalizers[] = 'throwing'; |
||||||
|
throw new RuntimeException('finalizer failed'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeLifecycleFiller |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function allocateUntilFinalizerRuns(): void |
||||||
|
{ |
||||||
|
global $nativeFinalizers; |
||||||
|
for ($i = 0; $i < 400000; $i++) { |
||||||
|
try { |
||||||
|
$filler = new NativeLifecycleFiller(); |
||||||
|
} catch (RuntimeException $error) { |
||||||
|
echo $error->getMessage(), "\n"; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
echo "finalizer did not run\n"; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
global $releasedProbes, $nativeFinalizers; |
||||||
|
$releasedProbes = []; |
||||||
|
$nativeFinalizers = []; |
||||||
|
|
||||||
|
try { |
||||||
|
$failed = new NativeConstructorFailure(); |
||||||
|
} catch (RuntimeException $error) { |
||||||
|
echo $error->getMessage(), "\n"; |
||||||
|
} |
||||||
|
var_dump($releasedProbes, $nativeFinalizers); |
||||||
|
|
||||||
|
$source = new NativeCloneFailure(); |
||||||
|
try { |
||||||
|
$copy = clone $source; |
||||||
|
} catch (RuntimeException $error) { |
||||||
|
echo $error->getMessage(), "\n"; |
||||||
|
} |
||||||
|
var_dump($releasedProbes); |
||||||
|
|
||||||
|
$throwing = new NativeThrowingFinalizer(); |
||||||
|
$throwing = null; |
||||||
|
allocateUntilFinalizerRuns(); |
||||||
|
var_dump($nativeFinalizers); |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
constructor failed |
||||||
|
array(1) { |
||||||
|
[0]=> |
||||||
|
string(11) "constructor" |
||||||
|
} |
||||||
|
array(0) { |
||||||
|
} |
||||||
|
clone failed |
||||||
|
array(2) { |
||||||
|
[0]=> |
||||||
|
string(11) "constructor" |
||||||
|
[1]=> |
||||||
|
string(5) "clone" |
||||||
|
} |
||||||
|
finalizer failed |
||||||
|
array(1) { |
||||||
|
[0]=> |
||||||
|
string(8) "throwing" |
||||||
|
} |
||||||
Loading…
Reference in new issue