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