parent
821c686ab0
commit
b3898c329d
17 changed files with 324 additions and 9 deletions
@ -0,0 +1,7 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeUint64Property |
||||
{ |
||||
public uint64 $value; |
||||
} |
||||
@ -0,0 +1,119 @@ |
||||
<?php |
||||
/** |
||||
* This file is part of TypePHP. |
||||
* |
||||
* @link https://www.swoole.com/ |
||||
* @contact service@swoole.com |
||||
*/ |
||||
|
||||
namespace TypePhp\Transform; |
||||
|
||||
use PhpParser\Node; |
||||
use PhpParser\Node\Stmt; |
||||
use PhpParser\NodeAbstract; |
||||
use TypePhp\Exception\SyntaxError; |
||||
|
||||
/** |
||||
* Preserve PHP's scalar type system while selecting a narrower C++ field for |
||||
* explicitly sized Native Class properties. |
||||
*/ |
||||
final class NativePropertyTypeLowering |
||||
{ |
||||
public const string STORAGE_TYPE_ATTRIBUTE = 'typephpNativePropertyStorageType'; |
||||
|
||||
/** @var array<string, array{php: string, cpp: string}> */ |
||||
private const array TYPES = [ |
||||
'int8' => ['php' => 'int', 'cpp' => 'int8_t'], |
||||
'int16' => ['php' => 'int', 'cpp' => 'int16_t'], |
||||
'int32' => ['php' => 'int', 'cpp' => 'int32_t'], |
||||
'uint8' => ['php' => 'int', 'cpp' => 'uint8_t'], |
||||
'uint16' => ['php' => 'int', 'cpp' => 'uint16_t'], |
||||
'uint32' => ['php' => 'int', 'cpp' => 'uint32_t'], |
||||
'float32' => ['php' => 'float', 'cpp' => 'float'], |
||||
]; |
||||
|
||||
private const array UNSUPPORTED_TYPES = [ |
||||
'uint64' => 'it cannot be represented by php::Int', |
||||
]; |
||||
|
||||
public static function lowerClass(Stmt\Class_ $class): void |
||||
{ |
||||
foreach ($class->stmts as $stmt) { |
||||
if ($stmt instanceof Stmt\Property) { |
||||
$stmt->type = self::lowerDeclaration($stmt, $stmt->type); |
||||
continue; |
||||
} |
||||
if (!$stmt instanceof Stmt\ClassMethod || $stmt->name->toLowerString() !== '__construct') { |
||||
continue; |
||||
} |
||||
foreach ($stmt->params as $param) { |
||||
if ($param->isPromoted()) { |
||||
$param->type = self::lowerDeclaration($param, $param->type); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static function getStorageType(Node $declaration): string |
||||
{ |
||||
$type = $declaration->getAttribute(self::STORAGE_TYPE_ATTRIBUTE, ''); |
||||
return is_string($type) ? $type : ''; |
||||
} |
||||
|
||||
private static function lowerDeclaration( |
||||
Node $declaration, |
||||
Node\ComplexType|Node\Identifier|Node\Name|null $type, |
||||
): Node\ComplexType|Node\Identifier|Node\Name|null { |
||||
$unsupportedType = self::findType($type, self::UNSUPPORTED_TYPES); |
||||
if ($unsupportedType !== null) { |
||||
throw new SyntaxError( |
||||
"Native property type `{$unsupportedType}` is not supported because " |
||||
. self::UNSUPPORTED_TYPES[$unsupportedType], |
||||
); |
||||
} |
||||
$fixedType = self::findFixedType($type); |
||||
if ($fixedType === null) { |
||||
return $type; |
||||
} |
||||
if (!$type instanceof Node\Name && !$type instanceof Node\Identifier) { |
||||
throw new SyntaxError( |
||||
"Native property type `{$fixedType}` must be declared directly and cannot be nullable or composite", |
||||
); |
||||
} |
||||
|
||||
$mapping = self::TYPES[$fixedType]; |
||||
$declaration->setAttribute(self::STORAGE_TYPE_ATTRIBUTE, $mapping['cpp']); |
||||
return new Node\Identifier($mapping['php'], $type->getAttributes()); |
||||
} |
||||
|
||||
private static function findFixedType(?NodeAbstract $type): ?string |
||||
{ |
||||
return self::findType($type, self::TYPES); |
||||
} |
||||
|
||||
/** @param array<string, mixed> $types */ |
||||
private static function findType(?NodeAbstract $type, array $types): ?string |
||||
{ |
||||
if ($type === null) { |
||||
return null; |
||||
} |
||||
if ($type instanceof Node\NullableType) { |
||||
return self::findType($type->type, $types); |
||||
} |
||||
if ($type instanceof Node\UnionType || $type instanceof Node\IntersectionType) { |
||||
foreach ($type->types as $member) { |
||||
$matchedType = self::findType($member, $types); |
||||
if ($matchedType !== null) { |
||||
return $matchedType; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
if (!$type instanceof Node\Name && !$type instanceof Node\Identifier) { |
||||
return null; |
||||
} |
||||
|
||||
$name = strtolower($type->toString()); |
||||
return isset($types[$name]) ? $name : null; |
||||
} |
||||
} |
||||
@ -0,0 +1,99 @@ |
||||
--TEST-- |
||||
Native class: fixed-width scalar fields retain PHP scalar expression types |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
final class CompactScalars |
||||
{ |
||||
public int8 $i8 = -8; |
||||
public int16 $i16 = -1600; |
||||
public int32 $i32 = -320000; |
||||
public uint8 $u8 = 8; |
||||
public uint16 $u16 = 1600; |
||||
public uint32 $u32 = 4294967295; |
||||
public float32 $f32 = 16777216.0; |
||||
public float32 $f32unit = 1.0; |
||||
public int32 $negative = -1; |
||||
public int32 $one = 1; |
||||
|
||||
public function update(): void |
||||
{ |
||||
$this->i8 += 2; |
||||
$this->u16 *= 2; |
||||
} |
||||
|
||||
public function intSum(): int |
||||
{ |
||||
return $this->u32 + $this->one; |
||||
} |
||||
|
||||
public function intCompare(): bool |
||||
{ |
||||
return $this->u32 > $this->negative; |
||||
} |
||||
|
||||
public function floatSum(): float |
||||
{ |
||||
return $this->f32 + $this->f32unit; |
||||
} |
||||
} |
||||
|
||||
#[Native] |
||||
final class MixedScalars |
||||
{ |
||||
public int $integer = 3; |
||||
public float $floating = 0.5; |
||||
public int8 $smallInteger = 2; |
||||
public float32 $smallFloat = 0.25; |
||||
|
||||
public function nativeSum(): float |
||||
{ |
||||
return $this->integer + $this->floating; |
||||
} |
||||
|
||||
public function fixedIntSum(): float |
||||
{ |
||||
return $this->smallInteger + $this->floating; |
||||
} |
||||
|
||||
public function fixedFloatSum(): float |
||||
{ |
||||
return $this->integer + $this->smallFloat; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new CompactScalars(); |
||||
$value->update(); |
||||
|
||||
var_dump( |
||||
$value->i8, |
||||
$value->i16, |
||||
$value->i32, |
||||
$value->u8, |
||||
$value->u16, |
||||
$value->u32, |
||||
$value->intSum(), |
||||
$value->intCompare(), |
||||
$value->floatSum(), |
||||
); |
||||
|
||||
$mixed = new MixedScalars(); |
||||
var_dump($mixed->nativeSum(), $mixed->fixedIntSum(), $mixed->fixedFloatSum()); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
int(-6) |
||||
int(-1600) |
||||
int(-320000) |
||||
int(8) |
||||
int(3200) |
||||
int(4294967295) |
||||
int(4294967296) |
||||
bool(true) |
||||
float(16777217) |
||||
float(3.5) |
||||
float(2.5) |
||||
float(3.25) |
||||
Loading…
Reference in new issue