parent
13344ace72
commit
7d1a90a340
24 changed files with 784 additions and 17 deletions
@ -0,0 +1,8 @@ |
||||
<?php |
||||
|
||||
function native_anonymous_class(): void |
||||
{ |
||||
$value = new #[Native] class { |
||||
public int $value = 1; |
||||
}; |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativePrivateMethodAccess |
||||
{ |
||||
private function hidden(): void {} |
||||
} |
||||
|
||||
function native_private_method_access(): void |
||||
{ |
||||
$value = new NativePrivateMethodAccess(); |
||||
$value->hidden(); |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeProtectedMethodAccess |
||||
{ |
||||
protected function hidden(): void {} |
||||
} |
||||
|
||||
function native_protected_method_access(): void |
||||
{ |
||||
$value = new NativeProtectedMethodAccess(); |
||||
$value->hidden(); |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeSerializeBoundary {} |
||||
|
||||
function native_serialize_boundary(): void |
||||
{ |
||||
$value = new NativeSerializeBoundary(); |
||||
serialize($value); |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeStaticSignature |
||||
{ |
||||
public function identity(): static |
||||
{ |
||||
return $this; |
||||
} |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
<?php |
||||
|
||||
trait NativeTraitStaticSignatureProvider |
||||
{ |
||||
public function identity(): static |
||||
{ |
||||
return $this; |
||||
} |
||||
} |
||||
|
||||
#[Native] |
||||
class NativeTraitStaticSignature |
||||
{ |
||||
use NativeTraitStaticSignatureProvider; |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeWeakReferenceBoundary {} |
||||
|
||||
function native_weak_reference_boundary(): void |
||||
{ |
||||
$value = new NativeWeakReferenceBoundary(); |
||||
WeakReference::create($value); |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
<?php |
||||
|
||||
final class GenStubVersionFlagsTest extends BaseTest |
||||
{ |
||||
public function testFlagIntroducedBeforeMinimumSupportedVersionRemainsEnabled(): void |
||||
{ |
||||
$flags = new VersionFlags(['ZEND_ACC_PRIVATE']); |
||||
$flags->addForVersionsAbove('ZEND_ACC_STATIC', PHP_70_VERSION_ID); |
||||
|
||||
self::assertSame( |
||||
'ZEND_ACC_PRIVATE|ZEND_ACC_STATIC', |
||||
$flags->generateVersionDependentFlagCode('%s', null), |
||||
); |
||||
} |
||||
} |
||||
@ -0,0 +1,62 @@ |
||||
--TEST-- |
||||
Native class: constructor arguments are evaluated left-to-right and rooted |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeConstructorArgument |
||||
{ |
||||
public string $name; |
||||
|
||||
public function __construct(string $name) |
||||
{ |
||||
$this->name = $name; |
||||
} |
||||
} |
||||
|
||||
#[Native] |
||||
class NativeConstructorPressure |
||||
{ |
||||
public int $value; |
||||
} |
||||
|
||||
#[Native] |
||||
class NativeConstructorPair |
||||
{ |
||||
public NativeConstructorArgument $first; |
||||
public NativeConstructorArgument $second; |
||||
|
||||
public function __construct( |
||||
NativeConstructorArgument $first, |
||||
NativeConstructorArgument $second, |
||||
) { |
||||
$this->first = $first; |
||||
$this->second = $second; |
||||
} |
||||
} |
||||
|
||||
function makeNativeConstructorArgument(string $name): NativeConstructorArgument |
||||
{ |
||||
echo 'make:', $name, PHP_EOL; |
||||
if ($name === 'B') { |
||||
for ($i = 0; $i < 300000; $i++) { |
||||
$filler = new NativeConstructorPressure(); |
||||
} |
||||
} |
||||
return new NativeConstructorArgument($name); |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$pair = new NativeConstructorPair( |
||||
makeNativeConstructorArgument('A'), |
||||
makeNativeConstructorArgument('B'), |
||||
); |
||||
echo $pair->first->name, ':', $pair->second->name, PHP_EOL; |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
make:A |
||||
make:B |
||||
A:B |
||||
@ -0,0 +1,150 @@ |
||||
--TEST-- |
||||
Native class: objects published before constructor or clone failure never become dangling |
||||
--FILE-- |
||||
<?php |
||||
|
||||
class NativeFailedLifecycleProbe |
||||
{ |
||||
public function __construct(public string $name) |
||||
{ |
||||
} |
||||
|
||||
public function __destruct() |
||||
{ |
||||
global $released; |
||||
$released[] = $this->name; |
||||
} |
||||
} |
||||
|
||||
#[Native] |
||||
class NativeFailedConstructionEscape |
||||
{ |
||||
public object $probe; |
||||
public int $value = 42; |
||||
|
||||
public function __construct() |
||||
{ |
||||
global $failedConstruction; |
||||
$this->probe = new NativeFailedLifecycleProbe('constructor field'); |
||||
$failedConstruction = $this; |
||||
throw new RuntimeException('constructor failed'); |
||||
} |
||||
|
||||
public function __destruct() |
||||
{ |
||||
global $finalized; |
||||
$finalized[] = 'constructor object'; |
||||
} |
||||
} |
||||
|
||||
#[Native] |
||||
class NativeFailedCloneEscape |
||||
{ |
||||
public object $probe; |
||||
public string $kind = 'source'; |
||||
|
||||
public function __construct() |
||||
{ |
||||
$this->probe = new NativeFailedLifecycleProbe('source field'); |
||||
} |
||||
|
||||
public function __clone(): void |
||||
{ |
||||
global $failedClone; |
||||
$this->kind = 'clone'; |
||||
$this->probe = new NativeFailedLifecycleProbe('clone field'); |
||||
$failedClone = $this; |
||||
throw new RuntimeException('clone failed'); |
||||
} |
||||
|
||||
public function __destruct() |
||||
{ |
||||
global $finalized; |
||||
$finalized[] = $this->kind; |
||||
} |
||||
} |
||||
|
||||
#[Native] |
||||
class NativeFailedLifecyclePressure |
||||
{ |
||||
public int $value; |
||||
} |
||||
|
||||
function forceNativeCollection(): void |
||||
{ |
||||
for ($i = 0; $i < 400000; $i++) { |
||||
$filler = new NativeFailedLifecyclePressure(); |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
global $failedConstruction, $failedClone, $released, $finalized; |
||||
$released = []; |
||||
$finalized = []; |
||||
|
||||
try { |
||||
$unused = new NativeFailedConstructionEscape(); |
||||
} catch (RuntimeException $error) { |
||||
echo $error->getMessage(), "\n"; |
||||
} |
||||
var_dump($failedConstruction->value, $failedConstruction->probe->name); |
||||
$failedConstruction = null; |
||||
forceNativeCollection(); |
||||
var_dump($released, $finalized); |
||||
|
||||
$source = new NativeFailedCloneEscape(); |
||||
try { |
||||
$unusedClone = clone $source; |
||||
} catch (RuntimeException $error) { |
||||
echo $error->getMessage(), "\n"; |
||||
} |
||||
var_dump($failedClone->kind, $failedClone === $source, $failedClone->probe->name); |
||||
$failedClone = null; |
||||
forceNativeCollection(); |
||||
var_dump($released, $finalized); |
||||
|
||||
$source = null; |
||||
forceNativeCollection(); |
||||
var_dump($released, $finalized); |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
constructor failed |
||||
int(42) |
||||
string(17) "constructor field" |
||||
array(1) { |
||||
[0]=> |
||||
string(17) "constructor field" |
||||
} |
||||
array(0) { |
||||
} |
||||
clone failed |
||||
string(5) "clone" |
||||
bool(false) |
||||
string(11) "clone field" |
||||
array(2) { |
||||
[0]=> |
||||
string(17) "constructor field" |
||||
[1]=> |
||||
string(11) "clone field" |
||||
} |
||||
array(1) { |
||||
[0]=> |
||||
string(5) "clone" |
||||
} |
||||
array(3) { |
||||
[0]=> |
||||
string(17) "constructor field" |
||||
[1]=> |
||||
string(11) "clone field" |
||||
[2]=> |
||||
string(12) "source field" |
||||
} |
||||
array(2) { |
||||
[0]=> |
||||
string(5) "clone" |
||||
[1]=> |
||||
string(6) "source" |
||||
} |
||||
@ -0,0 +1,50 @@ |
||||
--TEST-- |
||||
Native class: BigInt, BigFloat and Decimal properties retain typed value semantics |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeHighPrecisionValues |
||||
{ |
||||
public BigInt $integer; |
||||
public BigFloat $floating; |
||||
public Decimal $decimal; |
||||
} |
||||
|
||||
#[Native] |
||||
class NativeHighPrecisionPressure |
||||
{ |
||||
public int $value; |
||||
} |
||||
|
||||
function createNativePressure(): void |
||||
{ |
||||
for ($i = 0; $i < 300000; $i++) { |
||||
$filler = new NativeHighPrecisionPressure(); |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$values = new NativeHighPrecisionValues(); |
||||
var_dump($values->integer, $values->floating, $values->decimal); |
||||
|
||||
$values->integer = std::bigInt('123456789012345678901234567890'); |
||||
$values->floating = std::bigFloat('3.141592653589793238462643383279'); |
||||
$values->decimal = std::decimal('99.125'); |
||||
|
||||
createNativePressure(); |
||||
|
||||
echo ($values->integer + 10)->toString(), "\n"; |
||||
echo $values->floating->toString(), "\n"; |
||||
echo ($values->decimal * 2)->toString(), "\n"; |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
NULL |
||||
NULL |
||||
NULL |
||||
123456789012345678901234567900 |
||||
3.141592653589793238462643383279 |
||||
198.250 |
||||
@ -0,0 +1,56 @@ |
||||
--TEST-- |
||||
Native class: method visibility is resolved at compile time across inheritance |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeVisibilityBase |
||||
{ |
||||
private function baseLabel(): string |
||||
{ |
||||
return 'base-private'; |
||||
} |
||||
|
||||
protected function protectedLabel(): string |
||||
{ |
||||
return 'base-protected'; |
||||
} |
||||
|
||||
public function callBasePrivate(): string |
||||
{ |
||||
return $this->baseLabel(); |
||||
} |
||||
} |
||||
|
||||
#[Native] |
||||
class NativeVisibilityChild extends NativeVisibilityBase |
||||
{ |
||||
private function childLabel(): string |
||||
{ |
||||
return 'child-private'; |
||||
} |
||||
|
||||
public function callChildPrivate(): string |
||||
{ |
||||
return $this->childLabel(); |
||||
} |
||||
|
||||
public function callProtected(): string |
||||
{ |
||||
return $this->protectedLabel(); |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeVisibilityChild(); |
||||
echo $value->callBasePrivate(), PHP_EOL; |
||||
echo $value->callChildPrivate(), PHP_EOL; |
||||
echo $value->callProtected(), PHP_EOL; |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
base-private |
||||
child-private |
||||
base-protected |
||||
@ -0,0 +1,68 @@ |
||||
--TEST-- |
||||
Native class: property hooks preserve native pointer types and temporary roots |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeHookObjectValue |
||||
{ |
||||
public string $name; |
||||
|
||||
public function __construct(string $name) |
||||
{ |
||||
$this->name = $name; |
||||
} |
||||
} |
||||
|
||||
#[Native] |
||||
class NativeHookObjectPressure |
||||
{ |
||||
public int $value; |
||||
} |
||||
|
||||
#[Native] |
||||
class NativeHookObjectOwner |
||||
{ |
||||
private ?NativeHookObjectValue $stored; |
||||
|
||||
public ?NativeHookObjectValue $value { |
||||
get { |
||||
return $this->stored; |
||||
} |
||||
set(?NativeHookObjectValue $value) { |
||||
$this->stored = $value; |
||||
} |
||||
} |
||||
} |
||||
|
||||
function makeNativeHookValueAfterPressure(string $name): NativeHookObjectValue |
||||
{ |
||||
for ($i = 0; $i < 300000; $i++) { |
||||
$filler = new NativeHookObjectPressure(); |
||||
} |
||||
return new NativeHookObjectValue($name); |
||||
} |
||||
|
||||
function consumeNativeHookValues( |
||||
NativeHookObjectValue $first, |
||||
NativeHookObjectValue $second, |
||||
): void { |
||||
echo $first->name, ':', $second->name, PHP_EOL; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$owner = new NativeHookObjectOwner(); |
||||
$owner->value = new NativeHookObjectValue('first'); |
||||
consumeNativeHookValues( |
||||
$owner->value, |
||||
makeNativeHookValueAfterPressure('second'), |
||||
); |
||||
$owner->value = null; |
||||
echo $owner->value === null ? "NULL\n" : "not-null\n"; |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
first:second |
||||
NULL |
||||
@ -0,0 +1,62 @@ |
||||
--TEST-- |
||||
Native class: self and parent signatures preserve typed pointer semantics |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeSignatureBase |
||||
{ |
||||
public ?self $peer; |
||||
|
||||
public function identity(self $value): self |
||||
{ |
||||
return $value; |
||||
} |
||||
|
||||
public function nullable(?self $value): ?self |
||||
{ |
||||
return $value; |
||||
} |
||||
} |
||||
|
||||
#[Native] |
||||
class NativeSignatureChild extends NativeSignatureBase |
||||
{ |
||||
public ?parent $parentPeer; |
||||
|
||||
public function childIdentity(self $value): self |
||||
{ |
||||
return $value; |
||||
} |
||||
|
||||
public function parentIdentity(parent $value): parent |
||||
{ |
||||
return $value; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$base = new NativeSignatureBase(); |
||||
$child = new NativeSignatureChild(); |
||||
$base->peer = $base; |
||||
$child->parentPeer = $child; |
||||
|
||||
var_dump( |
||||
$base->identity($base) === $base, |
||||
$base->nullable(null) === null, |
||||
$child->childIdentity($child) === $child, |
||||
$child->parentIdentity($child) === $child, |
||||
$base->peer === $base, |
||||
$child->parentPeer === $child, |
||||
); |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
bool(true) |
||||
bool(true) |
||||
bool(true) |
||||
bool(true) |
||||
bool(true) |
||||
bool(true) |
||||
@ -0,0 +1,54 @@ |
||||
--TEST-- |
||||
Native class: $this is a native object pointer in value contexts |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeThisValue |
||||
{ |
||||
public int $value; |
||||
|
||||
public function __construct(int $value) |
||||
{ |
||||
$this->value = $value; |
||||
} |
||||
|
||||
public function identity(): self |
||||
{ |
||||
return $this; |
||||
} |
||||
|
||||
public function sameAs(NativeThisValue $other): bool |
||||
{ |
||||
$alias = $this; |
||||
return $alias === $other; |
||||
} |
||||
|
||||
public function passToFunction(): int |
||||
{ |
||||
return readNativeThisValue($this); |
||||
} |
||||
} |
||||
|
||||
function readNativeThisValue(NativeThisValue $value): int |
||||
{ |
||||
return $value->value; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeThisValue(42); |
||||
$other = new NativeThisValue(42); |
||||
|
||||
var_dump($value->identity() === $value); |
||||
var_dump($value->sameAs($value)); |
||||
var_dump($value->sameAs($other)); |
||||
var_dump($value->passToFunction()); |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
bool(true) |
||||
bool(true) |
||||
bool(false) |
||||
int(42) |
||||
Loading…
Reference in new issue