refactor(compiler): 替换 std::unsafe_cast 为对象方法调用并优化类型转换

- 将测试文件中的 std::unsafe_cast 调用替换为对应的 toStd* 方法
- 移除 std::unsafe_cast 函数实现,改为使用 $source->toStdMap 等方法
- 更新错误消息输出,移除函数名前缀
- 添加 TO_METHOD_TYPE_MAP 常量定义类型转换方法映射
- 修改变量赋值检查逻辑,支持新的 to* 转换方法
- 移除对 objval 函数的支持,统一使用 toObject 方法
- 新增 parseToStdAssign 方法处理标准容器转换分配
- 添加 genToObjectCall 和 genToConvertCall 代码生成方法
- 重构 UniversalMethodCall 类,移除重复的转换方法定义
- 优化类型检测逻辑,支持新的转换方法类型推断
pull/1/head
韩天峰 3 months ago
parent a4d28fbe6b
commit 3f9c76df5d
  1. 123
      src/Php/CompilerBase.php
  2. 5
      src/Php/FuncCallOptimizer.php
  3. 44
      src/Php/Parser/StdContainerParser.php
  4. 79
      src/Php/UniversalMethodCall.php
  5. 5
      src/polyfills.php
  6. 2
      tests/aot/std-array/007.phpt
  7. 4
      tests/aot/std-array/008.phpt
  8. 2
      tests/aot/std-map/005.phpt
  9. 4
      tests/aot/std-map/006.phpt
  10. 2
      tests/aot/std-unordered-map/005.phpt
  11. 4
      tests/aot/std-unordered-map/006.phpt
  12. 2
      tests/aot/std-vector/006.phpt
  13. 4
      tests/aot/std-vector/007.phpt
  14. 4
      tests/aot/std-vector/008.phpt
  15. 2
      tests/aot/std-vector/009.phpt

@ -75,6 +75,21 @@ class CompilerBase extends \PhpAot\Core\Translator
public const string TYPE_BIGFLOAT = 'php::BigFloat';
public const string TYPE_BOX = 'php::Box';
/**
* to* conversion methods are keywords with mandated return types. */
public const array TO_METHOD_TYPE_MAP = [
'toInt' => self::TYPE_INT,
'toFloat' => self::TYPE_FLOAT,
'toString' => self::TYPE_STR,
'toBool' => self::TYPE_BOOL,
'toArray' => self::TYPE_ARRAY,
'toStream' => self::TYPE_STREAM,
'toBigInt' => self::TYPE_BIGINT,
'toBigFloat' => self::TYPE_BIGFLOAT,
'toDecimal' => self::TYPE_DECIMAL,
'toObject' => self::TYPE_OBJECT,
];
private const array STREAM_FUNCTIONS = [
'fopen',
'tmpfile',
@ -1652,7 +1667,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->addObject($var, $rightClass);
} elseif ($this->isTypedObject($var)) {
$leftClass = $this->getObjectType($var);
// 对象的类不一致,不能互相赋值,必须使用 objval() 对齐类型
// 对象的类不一致,不能互相赋值,必须使用 toObject() 对齐类型
// 注意这里必须使用绝对相等比较,即使存在继承关系,类的方法也可能不一致
if ($leftClass !== $rightClass) {
$this->fatalError($left, "Cannot re-assign typed object `\${$var}` from `{$leftClass}` to `{$rightClass}`");
@ -1661,6 +1676,18 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->checkVarAssignExpr($left, $this->getVarType($var), self::TYPE_OBJECT);
}
} else {
if ($this->isMethodCall($right) and $this->isNamedMethod($right->name)) {
$methodName = $right->name->toString();
if (in_array($methodName, ['toStdArray', 'toStdVector', 'toStdMap', 'toStdUnorderedMap'], true)) {
if ($this->hasVar($var)) {
$this->fatalError($left, "Cannot re-assign `\${$var}` to {$methodName}()");
}
if ($this->context->scopeLevel > 1) {
$this->fatalError($left, "Must use {$methodName}() in the top-level scope of the function");
}
return $this->parseToStdAssign($var, $right);
}
}
if ($this->isFuncCallExpr($right) and $this->isNameExpr($right->name)) {
$fn = $this->parseIdentifier($right->name);
if (count($right->args) === 1 and $fn === 'any') {
@ -1676,15 +1703,7 @@ class CompilerBase extends \PhpAot\Core\Translator
} elseif ($this->isStaticCall($right) and $this->isNameExpr($right->class) and $this->isIdExpr($right->name)) {
$class = $this->parseIdentifier($right->class);
if ($class === 'std') {
if ($right->name->toString() === 'unsafe_cast') {
if ($this->hasVar($var)) {
$this->fatalError($left, "Cannot re-assign `\${$var}` to std::unsafe_cast()");
}
if ($this->context->scopeLevel > 1) {
$this->fatalError($left, 'Must use std::unsafe_cast() in the top-level scope of the function');
}
return $this->parseStdUnsafeCastAssign($var, $right);
} elseif (in_array($right->name->toString(), ['array', 'vector', 'map', 'unordered_map'], true)) {
if (in_array($right->name->toString(), ['array', 'vector', 'map', 'unordered_map'], true)) {
if ($this->hasVar($var)) {
$this->fatalError($left, "Cannot re-assign `\${$var}` to std::{$right->name->toString()}");
}
@ -2035,22 +2054,6 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->getObjectType($object);
}
}
if ($this->isFuncCallExpr($expr) and $this->isNameExpr($expr->name)) {
$fn = $this->parseIdentifier($expr->name);
if (count($expr->args) === 2 and $fn === 'objval') {
$argClass = $expr->args[1]->value;
if ($this->isScalarString($argClass)) {
return $this->getNamespacedClassName($argClass->value);
}
if ($this->isClassConstFetch($argClass)) {
if ($this->isNameExpr($argClass->class) and $this->isIdExpr($argClass->name) and $this->parseIdentifier($argClass->name) === 'class') {
return $this->getNamespacedClassName($this->parseIdentifier($argClass->class));
}
} else {
$this->fatalError($expr, 'The second parameter of objval() function only supports string literals or `ClassName::class` constant');
}
}
}
if ($this->isArrayDimFetch($expr) and $this->isStdContainerExpr($expr)) {
if ($this->isStdArrayExpr($expr)) {
if (!$expr->hasAttribute('stdArrayDimFetch')) {
@ -2068,14 +2071,28 @@ class CompilerBase extends \PhpAot\Core\Translator
$attr = $expr->getAttribute('stdContainerDimFetch');
return $this->context->stdContainers[$attr['var']]['class'] ?? '';
}
if ($this->isMethodCall($expr) and $this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) {
$object = $this->parseVariable($expr->var);
try {
$nativeFunc = $this->findNativeMethod($expr, $object, $this->parseIdentifier($expr->name));
if ($nativeFunc) {
return $this->getFunction($nativeFunc)->returnClass;
if ($this->isMethodCall($expr) and $this->isNamedMethod($expr->name)) {
$method = $this->parseIdentifier($expr->name);
if ($method === 'toObject' and !empty($expr->args)) {
$argClass = $expr->args[0]->value;
if ($this->isScalarString($argClass)) {
return $this->getNamespacedClassName($argClass->value);
}
if ($this->isClassConstFetch($argClass)) {
if ($this->isNameExpr($argClass->class) and $this->isIdExpr($argClass->name) and $this->parseIdentifier($argClass->name) === 'class') {
return $this->getNamespacedClassName($this->parseIdentifier($argClass->class));
}
}
}
if ($this->isVarExpr($expr->var)) {
$object = $this->parseVariable($expr->var);
try {
$nativeFunc = $this->findNativeMethod($expr, $object, $method);
if ($nativeFunc) {
return $this->getFunction($nativeFunc)->returnClass;
}
} catch (DynamicCall) {
}
} catch (DynamicCall) {
}
}
if ($this->isStaticCall($expr) and $this->isNameExpr($expr->class) and $this->isNamedMethod($expr->name)) {
@ -2502,6 +2519,10 @@ class CompilerBase extends \PhpAot\Core\Translator
case 'Expr_MethodCall':
if ($this->isNamedMethod($expr->name)) {
$method = $this->parseIdentifier($expr->name);
// to* methods are keywords — their return type is mandated regardless of receiver
if (isset(self::TO_METHOD_TYPE_MAP[$method])) {
return self::TO_METHOD_TYPE_MAP[$method];
}
// Class definition resolution (handles this_, typed VarExpr)
$classDef = $this->resolveObjectClassDef($expr->var);
if ($classDef !== null && $classDef->hasMethod($method)) {
@ -5376,6 +5397,26 @@ class CompilerBase extends \PhpAot\Core\Translator
return $left . ' = &' . $tmpVar;
}
protected function genToObjectCall(Expr\MethodCall $expr, string $receiver): string
{
if (empty($expr->args)) {
return 'php::toObject(' . $receiver . ')';
}
$argClass = $expr->args[0]->value;
if ($this->isScalarString($argClass)) {
$className = $this->getNamespacedClassName($argClass->value);
} elseif ($this->isClassConstFetch($argClass)) {
if ($this->isNameExpr($argClass->class) and $this->isIdExpr($argClass->name) and $this->parseIdentifier($argClass->name) === 'class') {
$className = $this->getNamespacedClassName($this->parseIdentifier($argClass->class));
} else {
$this->fatalError($expr, 'The first parameter of toObject() only supports string literals or `ClassName::class` constant');
}
} else {
$this->fatalError($expr, 'The first parameter of toObject() only supports string literals or `ClassName::class` constant');
}
return 'php::toObject(' . $receiver . ', ' . $this->getClassEntryPtr($className) . ', true)';
}
protected function parseMethodCall(Expr\MethodCall $expr): string
{
$class = '';
@ -5392,6 +5433,22 @@ class CompilerBase extends \PhpAot\Core\Translator
$magicMethod = false;
$method = $this->identifierToStr($expr->name, literal: true);
// to* conversion methods are language keywords — always dispatched directly
if ($this->isNamedMethod($expr->name)) {
$methodName = $expr->name->toString();
if (isset(self::TO_METHOD_TYPE_MAP[$methodName])) {
if ($this->isVarExpr($expr->var)) {
$receiverType = $this->getVarType($object);
} else {
$receiverType = $this->detectTypeOfExpr($expr->var);
}
if ($methodName === 'toObject') {
return $this->genToObjectCall($expr, $object);
}
return $this->genToConvertCall($object, $methodName, $receiverType);
}
}
// 可转为原生调用的 MethodCall
if ($this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) {
$type = $this->getVarType($object);

@ -87,11 +87,6 @@ trait FuncCallOptimizer
}
} elseif (count($expr->args) == 2) {
switch ($name) {
case 'objval':
$arg1 = $expr->args[0]->value;
$arg2 = $expr->args[1]->value;
return $this->convertObjectExpr($this->parseExpr($arg1), $this->parseExpr($arg2));
case 'define':
$arg1 = $expr->args[0]->value;
if ($this->isScalarString($arg1) and !$this->isValidDefineName($arg1->value)) {

@ -10,6 +10,9 @@ namespace PhpAot\Php\Parser;
use PhpAot\Php\Symbol;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\NodeAbstract;
@ -589,43 +592,44 @@ trait StdContainerParser
return $valueExpr;
}
protected function parseStdUnsafeCastAssign(string $var, Expr\StaticCall $expr): string
protected function parseToStdAssign(string $var, Expr\MethodCall $expr): string
{
if (count($expr->args) !== 2) {
$this->fatalError($expr, 'std::unsafe_cast() expects two arguments');
}
$typeExpr = $expr->args[0]->value;
if (!$this->isStaticCall($typeExpr) || !$this->isNameExpr($typeExpr->class) || !$this->isIdExpr($typeExpr->name) || $typeExpr->class->toString() !== 'std') {
$this->fatalError($expr->args[0]->value, 'std::unsafe_cast() expects first argument to be a std container type expression');
}
$containerType = $typeExpr->name->toString();
if (!in_array($containerType, ['array', 'vector', 'map', 'unordered_map'], true)) {
$this->fatalError($expr->args[0]->value, 'std::unsafe_cast() expects first argument to be a std container type expression');
}
if (!$this->isVarExpr($expr->args[1]->value)) {
$this->fatalError($expr->args[1]->value, 'std::unsafe_cast() expects second argument to be a variable');
$methodName = $expr->name->toString();
$containerType = match ($methodName) {
'toStdArray' => 'array',
'toStdVector' => 'vector',
'toStdMap' => 'map',
'toStdUnorderedMap' => 'unordered_map',
};
if (!$this->isVarExpr($expr->var)) {
$this->fatalError($expr->var, "{$methodName}() must be called on a variable");
}
$sourceVar = $this->parseVariable($expr->args[1]->value);
$sourceVar = $this->parseVariable($expr->var);
if (!$this->hasVar($sourceVar)) {
$this->fatalError($expr->args[1]->value, 'Undefined variable `$' . $sourceVar . '`');
$this->fatalError($expr->var, 'Undefined variable `$' . $sourceVar . '`');
}
$name = new Name('std');
$method = new Identifier($containerType);
$fakeCall = new StaticCall($name, $method, $expr->args);
if ($containerType === 'array') {
$this->addLocalVar($var, self::TYPE_STD_ARRAY);
$this->parseStdArray($var, $typeExpr);
$this->parseStdArray($var, $fakeCall);
$this->context->stdArrays[$var]['unsafePtr'] = $sourceVar;
return '// php_unsafe_cast<' . $this->context->stdArrays[$var]['decl'] . '>(' . $sourceVar . ')';
}
if ($containerType === 'vector') {
$this->addLocalVar($var, self::TYPE_STD_VECTOR);
$this->parseStdVector($var, $typeExpr);
$this->parseStdVector($var, $fakeCall);
} elseif ($containerType === 'map') {
$this->addLocalVar($var, self::TYPE_STD_MAP);
$this->parseStdMap($var, $typeExpr);
$this->parseStdMap($var, $fakeCall);
} else {
$this->addLocalVar($var, self::TYPE_STD_UNORDERED_MAP);
$this->parseStdUnorderedMap($var, $typeExpr);
$this->parseStdUnorderedMap($var, $fakeCall);
}
$this->context->stdContainers[$var]['unsafePtr'] = $sourceVar;
return '// php_unsafe_cast<' . $this->context->stdContainers[$var]['decl'] . '>(' . $sourceVar . ')';

@ -17,9 +17,6 @@ trait UniversalMethodCall
'mod' => ['handler' => 'calc_op', 'op' => '%', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1],
'inc' => ['handler' => 'calc_inc', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'dec' => ['handler' => 'calc_dec', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'toFloat' => ['handler' => 'convert_fn', 'fn' => 'toFloat', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
'toString' => ['handler' => 'convert_fn', 'fn' => 'toString', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'toBool' => ['handler' => 'convert_fn', 'fn' => 'toBool', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0],
// math
'abs' => ['handler' => 'php_fn', 'fn' => 'abs', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'ceil' => ['handler' => 'php_fn', 'fn' => 'ceil', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
@ -49,9 +46,6 @@ trait UniversalMethodCall
'div' => ['handler' => 'calc_op', 'op' => '/', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1],
'inc' => ['handler' => 'calc_inc', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
'dec' => ['handler' => 'calc_dec', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
'toInt' => ['handler' => 'convert_fn', 'fn' => 'toInt', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'toString' => ['handler' => 'convert_fn', 'fn' => 'toString', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'toBool' => ['handler' => 'convert_fn', 'fn' => 'toBool', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0],
// math
'abs' => ['handler' => 'php_fn', 'fn' => 'abs', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
'ceil' => ['handler' => 'php_fn', 'fn' => 'ceil', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
@ -75,8 +69,6 @@ trait UniversalMethodCall
'min' => ['handler' => 'php_fn', 'fn' => 'min', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1],
],
CompilerBase::TYPE_BOOL => [
'toInt' => ['handler' => 'convert_fn', 'fn' => 'toInt', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'toString' => ['handler' => 'convert_fn', 'fn' => 'toString', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
],
CompilerBase::TYPE_STR => [
// --- stdext string_methods (all use PHP standard functions) ---
@ -173,10 +165,6 @@ trait UniversalMethodCall
'jsonDecodeToObject' => ['handler' => 'php_fn', 'fn' => 'json_decode', 'return_type' => CompilerBase::TYPE_OBJECT, 'min_args' => 0, 'max_args' => 2, 'const_args' => [1 => 'false']],
// phpx C++ methods (no PHP function equivalent)
'equals' => ['handler' => 'direct_method', 'method' => 'equals', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2],
// conversions
'toInt' => ['handler' => 'convert_fn', 'fn' => 'toInt', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'toFloat' => ['handler' => 'convert_fn', 'fn' => 'toFloat', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
'toBool' => ['handler' => 'convert_fn', 'fn' => 'toBool', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0],
],
CompilerBase::TYPE_ARRAY => [
// --- stdext array_methods (all use PHP standard functions) ---
@ -240,11 +228,6 @@ trait UniversalMethodCall
'get' => ['handler' => 'direct_method', 'method' => 'get', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 1],
'del' => ['handler' => 'direct_method_mutate', 'method' => 'del', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 1],
'clean' => ['handler' => 'direct_method_mutate', 'method' => 'clean', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0],
// conversions
'toInt' => ['handler' => 'convert_fn', 'fn' => 'toInt', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'toFloat' => ['handler' => 'convert_fn', 'fn' => 'toFloat', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
'toBool' => ['handler' => 'convert_fn', 'fn' => 'toBool', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0],
'toString' => ['handler' => 'convert_fn', 'fn' => 'toString', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
],
CompilerBase::TYPE_STREAM => [
// --- stdext stream_methods ---
@ -299,9 +282,6 @@ trait UniversalMethodCall
'divmod' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::divmod', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 1],
'powmod' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::powmod', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 2, 'max_args' => 2],
'sqrt' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::sqrt', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 0, 'max_args' => 0],
'toString' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::toString', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'toInt' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::toInt', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'toFloat' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::toFloat', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
'bitAnd' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitAnd', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1],
'bitOr' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitOr', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1],
'bitXor' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitXor', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1],
@ -327,9 +307,6 @@ trait UniversalMethodCall
'floor' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::floor', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 0, 'max_args' => 0],
'ceil' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::ceil', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 0, 'max_args' => 0],
'round' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::round', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 0, 'max_args' => 1],
'toString' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::toString', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'toInt' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::toInt', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'toFloat' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::toFloat', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
],
CompilerBase::TYPE_BIGFLOAT => [
'add' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::add', 'return_type' => CompilerBase::TYPE_BIGFLOAT, 'min_args' => 1, 'max_args' => 1],
@ -339,9 +316,6 @@ trait UniversalMethodCall
'neg' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::neg', 'return_type' => CompilerBase::TYPE_BIGFLOAT, 'min_args' => 0, 'max_args' => 0],
'cmp' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::cmp', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1],
'abs' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::abs', 'return_type' => CompilerBase::TYPE_BIGFLOAT, 'min_args' => 0, 'max_args' => 0],
'toString' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::toString', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'toInt' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::toInt', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'toFloat' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::toFloat', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
],
];
@ -351,19 +325,6 @@ trait UniversalMethodCall
protected function detectUniversalMethodReturnType(string $type, string $method): ?string
{
if ($type === CompilerBase::TYPE_VAR) {
foreach (self::TYPE_SEARCH_ORDER as $searchType) {
$def = static::UNIVERSAL_METHODS[$searchType][$method] ?? null;
if ($def !== null) {
return $def['return_type'];
}
$ext = $this->findExtensionMethod($searchType, $method);
if ($ext !== null) {
return $ext['return_type'];
}
}
return null;
}
$builtin = self::UNIVERSAL_METHODS[$type][$method]['return_type'] ?? null;
if ($builtin !== null) {
return $builtin;
@ -390,24 +351,36 @@ trait UniversalMethodCall
return strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name));
}
private const array TO_CONVERT_FN = [
CompilerBase::TYPE_BIGINT => ['toInt' => 'php::BigInt::toInt', 'toFloat' => 'php::BigInt::toFloat', 'toString' => 'php::BigInt::toString'],
CompilerBase::TYPE_BIGFLOAT => ['toInt' => 'php::BigFloat::toInt', 'toFloat' => 'php::BigFloat::toFloat', 'toString' => 'php::BigFloat::toString'],
CompilerBase::TYPE_DECIMAL => ['toInt' => 'php::Decimal::toInt', 'toFloat' => 'php::Decimal::toFloat', 'toString' => 'php::Decimal::toString'],
];
/**
* Search all type tables for a method. Returns [type, def] or null.
* Generate C++ code for a to* keyword conversion call.
*/
protected function genToConvertCall(string $receiver, string $method, string $receiverType = ''): string
{
if ($receiverType !== '' && isset(self::TO_CONVERT_FN[$receiverType][$method])) {
return self::TO_CONVERT_FN[$receiverType][$method] . '(' . $receiver . ')';
}
return match ($method) {
'toInt' => 'php::toInt(' . $receiver . ')',
'toFloat' => 'php::toFloat(' . $receiver . ')',
'toString' => 'php::toString(' . $receiver . ')',
'toBool' => 'php::toBool(' . $receiver . ')',
'toArray' => 'php::toArray(' . $receiver . ')',
'toStream' => 'php::toStream(' . $receiver . ')',
'toBigInt' => 'php::BigInt::newInstance(' . $receiver . ')',
'toBigFloat' => 'php::BigFloat::newInstance(' . $receiver . ')',
'toDecimal' => 'php::Decimal::newInstance(' . $receiver . ')',
default => $receiver,
};
}
protected function findUniversalMethodAnyType(string $type, string $method): ?array
{
if ($type === CompilerBase::TYPE_VAR) {
foreach (self::TYPE_SEARCH_ORDER as $searchType) {
$def = self::UNIVERSAL_METHODS[$searchType][$method] ?? null;
if ($def !== null) {
return $def;
}
$ext = $this->findExtensionMethod($searchType, $method);
if ($ext !== null) {
return $ext;
}
}
return null;
}
$builtin = self::UNIVERSAL_METHODS[$type][$method] ?? null;
if ($builtin !== null) {
return $builtin;

@ -70,11 +70,6 @@ class std
return null;
}
public static function unsafe_cast(mixed $type, mixed $ptr): mixed
{
return $ptr;
}
public static function fill(array $array, mixed $value): void
{
for ($i = 0; $i < count($array); $i++) {

@ -4,7 +4,7 @@ std array: unsafe_cast
<?php
function std_array_unsafe_ptr_update($source): void
{
$array = std::unsafe_cast(std::array(native_types::type_int, 3), $source);
$array = $source->toStdArray(native_types::type_int, 3);
var_dump($array[1]);
$array[2] = 9;
}

@ -4,7 +4,7 @@ std array: unsafe_cast type mismatch
<?php
function std_array_unsafe_ptr_type_mismatch($source): void
{
$array = std::unsafe_cast(std::array(native_types::type_float, 3), $source);
$array = $source->toStdArray(native_types::type_float, 3);
}
function main() {
@ -17,4 +17,4 @@ function main() {
}
?>
--EXPECT--
std::unsafe_cast(): std container type mismatch
std container type mismatch

@ -4,7 +4,7 @@ std map: unsafe_cast
<?php
function std_map_unsafe_ptr_update($source): void
{
$map = std::unsafe_cast(std::map(complex_types::type_str, native_types::type_int), $source);
$map = $source->toStdMap(complex_types::type_str, native_types::type_int);
var_dump($map["b"]);
$map["c"] = 9;

@ -4,7 +4,7 @@ std map: unsafe_cast type mismatch
<?php
function std_map_unsafe_ptr_type_mismatch($source): void
{
$map = std::unsafe_cast(std::map(complex_types::type_str, native_types::type_float), $source);
$map = $source->toStdMap(complex_types::type_str, native_types::type_float);
}
function main() {
@ -17,4 +17,4 @@ function main() {
}
?>
--EXPECT--
std::unsafe_cast(): std container type mismatch
std container type mismatch

@ -4,7 +4,7 @@ std unordered map: unsafe_cast
<?php
function std_unordered_map_unsafe_ptr_update($source): void
{
$map = std::unsafe_cast(std::unordered_map(native_types::type_int, native_types::type_int), $source);
$map = $source->toStdUnorderedMap(native_types::type_int, native_types::type_int);
var_dump($map[2]);
$map[3] = 9;
}

@ -4,7 +4,7 @@ std unordered map: unsafe_cast type mismatch
<?php
function std_unordered_map_unsafe_ptr_type_mismatch($source): void
{
$map = std::unsafe_cast(std::unordered_map(native_types::type_int, native_types::type_float), $source);
$map = $source->toStdUnorderedMap(native_types::type_int, native_types::type_float);
}
function main() {
@ -17,4 +17,4 @@ function main() {
}
?>
--EXPECT--
std::unsafe_cast(): std container type mismatch
std container type mismatch

@ -4,7 +4,7 @@ std vector: unsafe_cast
<?php
function std_vector_unsafe_ptr_update($source): void
{
$vector = std::unsafe_cast(std::vector(native_types::type_int), $source);
$vector = $source->toStdVector(native_types::type_int);
var_dump($vector[1]);
$vector[2] = 9;
}

@ -4,7 +4,7 @@ std vector: unsafe_cast type mismatch
<?php
function std_vector_unsafe_ptr_type_mismatch($source): void
{
$vector = std::unsafe_cast(std::vector(native_types::type_float), $source);
$vector = $source->toStdVector(native_types::type_float);
}
function main() {
@ -17,4 +17,4 @@ function main() {
}
?>
--EXPECT--
std::unsafe_cast(): std container type mismatch
std container type mismatch

@ -12,7 +12,7 @@ class StdVectorUnsafeCastOther
function std_vector_unsafe_ptr_class_type_mismatch($source): void
{
$vector = std::unsafe_cast(std::vector(StdVectorUnsafeCastOther::class), $source);
$vector = $source->toStdVector(StdVectorUnsafeCastOther::class);
}
function main() {
@ -25,4 +25,4 @@ function main() {
}
?>
--EXPECT--
std::unsafe_cast(): std container type mismatch
std container type mismatch

@ -7,7 +7,7 @@ namespace StdVectorUnsafeCastNs {
{
public static function update($source): void
{
$vector = std::unsafe_cast(std::vector(self::class), $source);
$vector = $source->toStdVector(self::class);
echo "ok\n";
}

Loading…
Cancel
Save