diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index bd59fa7f..044eaf14 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -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); diff --git a/src/Php/FuncCallOptimizer.php b/src/Php/FuncCallOptimizer.php index 278c22c0..54328e9d 100644 --- a/src/Php/FuncCallOptimizer.php +++ b/src/Php/FuncCallOptimizer.php @@ -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)) { diff --git a/src/Php/Parser/StdContainerParser.php b/src/Php/Parser/StdContainerParser.php index 47cb92d6..52ba7318 100644 --- a/src/Php/Parser/StdContainerParser.php +++ b/src/Php/Parser/StdContainerParser.php @@ -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 . ')'; diff --git a/src/Php/UniversalMethodCall.php b/src/Php/UniversalMethodCall.php index 1938eb42..0b31c9d5 100644 --- a/src/Php/UniversalMethodCall.php +++ b/src/Php/UniversalMethodCall.php @@ -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; diff --git a/src/polyfills.php b/src/polyfills.php index 4a37bacd..aa515931 100644 --- a/src/polyfills.php +++ b/src/polyfills.php @@ -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++) { diff --git a/tests/aot/std-array/007.phpt b/tests/aot/std-array/007.phpt index 4714af38..bfa936f4 100644 --- a/tests/aot/std-array/007.phpt +++ b/tests/aot/std-array/007.phpt @@ -4,7 +4,7 @@ std array: unsafe_cast toStdArray(native_types::type_int, 3); var_dump($array[1]); $array[2] = 9; } diff --git a/tests/aot/std-array/008.phpt b/tests/aot/std-array/008.phpt index d307d1f3..3484f110 100644 --- a/tests/aot/std-array/008.phpt +++ b/tests/aot/std-array/008.phpt @@ -4,7 +4,7 @@ std array: unsafe_cast type mismatch 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 diff --git a/tests/aot/std-map/005.phpt b/tests/aot/std-map/005.phpt index cea9f498..c99ba9fe 100644 --- a/tests/aot/std-map/005.phpt +++ b/tests/aot/std-map/005.phpt @@ -4,7 +4,7 @@ std map: unsafe_cast toStdMap(complex_types::type_str, native_types::type_int); var_dump($map["b"]); $map["c"] = 9; diff --git a/tests/aot/std-map/006.phpt b/tests/aot/std-map/006.phpt index b6316045..20c16643 100644 --- a/tests/aot/std-map/006.phpt +++ b/tests/aot/std-map/006.phpt @@ -4,7 +4,7 @@ std map: unsafe_cast type mismatch 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 diff --git a/tests/aot/std-unordered-map/005.phpt b/tests/aot/std-unordered-map/005.phpt index 45b90d79..69ce71e7 100644 --- a/tests/aot/std-unordered-map/005.phpt +++ b/tests/aot/std-unordered-map/005.phpt @@ -4,7 +4,7 @@ std unordered map: unsafe_cast toStdUnorderedMap(native_types::type_int, native_types::type_int); var_dump($map[2]); $map[3] = 9; } diff --git a/tests/aot/std-unordered-map/006.phpt b/tests/aot/std-unordered-map/006.phpt index c67afc66..f679936c 100644 --- a/tests/aot/std-unordered-map/006.phpt +++ b/tests/aot/std-unordered-map/006.phpt @@ -4,7 +4,7 @@ std unordered map: unsafe_cast type mismatch 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 diff --git a/tests/aot/std-vector/006.phpt b/tests/aot/std-vector/006.phpt index 615e940d..4483f6f1 100644 --- a/tests/aot/std-vector/006.phpt +++ b/tests/aot/std-vector/006.phpt @@ -4,7 +4,7 @@ std vector: unsafe_cast toStdVector(native_types::type_int); var_dump($vector[1]); $vector[2] = 9; } diff --git a/tests/aot/std-vector/007.phpt b/tests/aot/std-vector/007.phpt index 7974d730..e6be5234 100644 --- a/tests/aot/std-vector/007.phpt +++ b/tests/aot/std-vector/007.phpt @@ -4,7 +4,7 @@ std vector: unsafe_cast type mismatch 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 diff --git a/tests/aot/std-vector/008.phpt b/tests/aot/std-vector/008.phpt index f93e8be5..e6c0be63 100644 --- a/tests/aot/std-vector/008.phpt +++ b/tests/aot/std-vector/008.phpt @@ -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 diff --git a/tests/aot/std-vector/009.phpt b/tests/aot/std-vector/009.phpt index aaef3ce8..fe72c9ae 100644 --- a/tests/aot/std-vector/009.phpt +++ b/tests/aot/std-vector/009.phpt @@ -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"; }