TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
510 lines
23 KiB
510 lines
23 KiB
<?php
|
|
|
|
namespace TypePhp\Parser;
|
|
|
|
use PhpParser\Node;
|
|
use PhpParser\Node\Expr;
|
|
use PhpParser\Node\Stmt\Foreach_;
|
|
use PhpParser\NodeAbstract;
|
|
use TypePhp\Entity\ArgInfo;
|
|
use TypePhp\Entity\TypedArrayPropertyWritePlan;
|
|
use TypePhp\Transform\CompileTimeAttribute;
|
|
use TypePhp\Type;
|
|
|
|
/** Compiler-enforced PHP-array contracts. Storage and COW remain php::Array's. */
|
|
trait TypedArrayTrait
|
|
{
|
|
protected function parseTypedArrayPropertyDefinition(Node\Stmt\Property $property): ?array
|
|
{
|
|
foreach (['StdList' => 'list', 'StdDict' => 'dict'] as $name => $kind) {
|
|
$attribute = CompileTimeAttribute::find($property, $name);
|
|
if ($attribute !== null) {
|
|
$this->validateStdAttributeType($property, $name, 'array');
|
|
return $this->parseTypedArrayDefinition($kind, $attribute->args, $attribute);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected function prepareTypedArrayPropertyDirectWrite(
|
|
Expr\ArrayDimFetch $left,
|
|
Expr $right,
|
|
string $value,
|
|
): ?TypedArrayPropertyWritePlan {
|
|
$definition = $this->getNativePropertyDef($left->var)?->typedArray;
|
|
if ($definition === null) {
|
|
return null;
|
|
}
|
|
$value = $this->guardTypedArrayValue($definition, $right, $value);
|
|
if ($left->dim === null) {
|
|
if ($definition['kind'] !== 'list') {
|
|
$this->fatalError($left, 'StdDict properties do not support append writes');
|
|
}
|
|
return new TypedArrayPropertyWritePlan(true, null, $value);
|
|
}
|
|
$key = $this->guardTypedArrayValue($definition, $left->dim, $this->parseExprAsValue($left->dim), true);
|
|
return new TypedArrayPropertyWritePlan(false, $key, $value);
|
|
}
|
|
|
|
protected function parseTypedArrayDefinition(string $kind, array $args, NodeAbstract $owner): array
|
|
{
|
|
$count = $kind === 'list' ? 1 : 2;
|
|
if (count($args) !== $count) {
|
|
$this->fatalError($owner, "Std{$kind} expects {$count} type argument(s)");
|
|
}
|
|
foreach ($args as $arg) {
|
|
if ($arg->name !== null || $arg->unpack || $arg->byRef) {
|
|
$this->fatalError($arg, 'Typed arrays require positional type arguments');
|
|
}
|
|
}
|
|
$key = $kind === 'list' ? Type::INT : $this->parseStdMapKeyType($args[0]->value, 'std::dict');
|
|
$value = $this->parseStdValueTypeInfo($args[$count - 1]->value, 'std::' . $kind);
|
|
if (!in_array($value['type'], [Type::INT, Type::FLOAT, Type::BOOL, Type::STR, Type::ARRAY, Type::OBJECT, Type::VAR], true)) {
|
|
$this->fatalError($owner, 'Typed PHP arrays only support PHP value types or ClassName::class');
|
|
}
|
|
if ($this->isNativeObjectClass($value['class'] ?? '')) {
|
|
$this->fatalError($owner, 'Typed PHP arrays cannot hold Native objects');
|
|
}
|
|
// Integer-key lists/dicts share PHP storage; only list grants append.
|
|
return ['kind' => $kind, 'keyType' => $key, 'type' => $value['type'], 'class' => $value['class']];
|
|
}
|
|
|
|
protected function parseTypedArrayFactoryDefinition(string $kind, Expr\StaticCall $call): array
|
|
{
|
|
$initializer = $this->getStdValueInitializer($call);
|
|
if ($initializer === null) {
|
|
return $this->parseTypedArrayDefinition($kind, $call->args, $call);
|
|
}
|
|
|
|
$inferred = $this->inferStdFlatInitializer(
|
|
$initializer,
|
|
'std::' . $kind,
|
|
$kind === 'list' ? 'integer' : 'map',
|
|
);
|
|
if (!in_array($inferred['type'], [
|
|
Type::INT,
|
|
Type::FLOAT,
|
|
Type::BOOL,
|
|
Type::STR,
|
|
Type::ARRAY,
|
|
Type::OBJECT,
|
|
], true)) {
|
|
$this->fatalError($call, 'Typed PHP array value initialization only supports concrete PHP value types');
|
|
}
|
|
if ($this->isNativeObjectClass($inferred['class'] ?? '')) {
|
|
$this->fatalError($call, 'Typed PHP arrays cannot hold Native objects');
|
|
}
|
|
return [
|
|
'kind' => $kind,
|
|
'keyType' => $kind === 'list' ? Type::INT : $inferred['keyType'],
|
|
'type' => $inferred['type'],
|
|
'class' => $inferred['class'],
|
|
];
|
|
}
|
|
|
|
protected function parseTypedArrayParameterDefinition(Node\Param $param): ?array
|
|
{
|
|
foreach (['StdList' => 'list', 'StdDict' => 'dict'] as $name => $kind) {
|
|
$attribute = CompileTimeAttribute::find($param, $name);
|
|
if ($attribute === null) {
|
|
continue;
|
|
}
|
|
$this->validateStdAttributeType($param, $name, 'array');
|
|
if ($param->variadic || $param->default !== null || $param->isPromoted()) {
|
|
$this->fatalError($param, $name . ' does not support variadic, defaulted or promoted parameters');
|
|
}
|
|
return $this->parseTypedArrayDefinition($kind, $attribute->args, $attribute);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected function getTypedArrayDefinition(NodeAbstract $expr): ?array
|
|
{
|
|
if ($expr instanceof Expr\ErrorSuppress) {
|
|
return $this->getTypedArrayDefinition($expr->expr);
|
|
}
|
|
if ($expr instanceof Expr\Assign || $expr instanceof Expr\AssignRef) {
|
|
return $this->getTypedArrayDefinition($expr->expr);
|
|
}
|
|
if ($expr instanceof Expr\MethodCall && $expr->name instanceof Node\Identifier) {
|
|
$kind = match ($expr->name->name) {
|
|
'toStdList' => 'list',
|
|
'toStdDict' => 'dict',
|
|
default => null,
|
|
};
|
|
if ($kind !== null) {
|
|
return $this->parseTypedArrayDefinition($kind, $expr->args, $expr);
|
|
}
|
|
}
|
|
if ($expr instanceof Expr\StaticCall && $expr->class instanceof Node\Name
|
|
&& $expr->name instanceof Node\Identifier && $this->isStdClassExpr($expr->class)
|
|
&& in_array(strtolower($expr->name->name), ['list', 'dict'], true)) {
|
|
return $this->parseTypedArrayFactoryDefinition(strtolower($expr->name->name), $expr);
|
|
}
|
|
if ($this->isVarExpr($expr)) {
|
|
return $this->context->typedArrays[$this->parseIdentifier($expr)] ?? null;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected function parseTypedArrayConversionCall(Expr\MethodCall $call): string
|
|
{
|
|
$kind = $call->name->toString() === 'toStdList' ? 'list' : 'dict';
|
|
$definition = $this->parseTypedArrayDefinition($kind, $call->args, $call);
|
|
if ($this->isVarExpr($call->var)) {
|
|
$this->assertStdContainerDoesNotEscapeNativeObjects($call, $this->parseIdentifier($call->var));
|
|
}
|
|
// A matching typed array already satisfies the contract. Preserve
|
|
// ordinary PHP array assignment and its copy-on-write behavior.
|
|
if ($this->getTypedArrayDefinition($call->var) === $definition) {
|
|
return $this->parseExprAsValue($call->var);
|
|
}
|
|
|
|
$stdContainer = $this->isVarExpr($call->var)
|
|
&& $this->isStdContainer($this->parseIdentifier($call->var));
|
|
if ($this->detectTypeOfExpr($call->var) === Type::ARRAY && !$stdContainer) {
|
|
$source = $this->parseExprAsValue($call->var);
|
|
} else {
|
|
$class = $this->detectClassOfExpr($call->var);
|
|
if ($this->isNativeObjectClass($class)) {
|
|
// Native objects use their declared toArray() method; their
|
|
// pointer cannot be passed to PHPX's dynamic conversion.
|
|
$source = $this->parseExprAsValue(new Expr\MethodCall($call->var, new Node\Identifier('toArray')));
|
|
} else {
|
|
$source = 'php::toArray(' . $this->parseExprAsValue($call->var) . ')';
|
|
}
|
|
}
|
|
$valueType = match ($definition['type']) {
|
|
Type::INT => 'Int',
|
|
Type::FLOAT => 'Float',
|
|
Type::BOOL => 'Bool',
|
|
Type::STR => 'String',
|
|
Type::ARRAY => 'Array',
|
|
Type::OBJECT => 'Object',
|
|
Type::VAR => 'Any',
|
|
};
|
|
$valueClass = $definition['class'] !== null && $definition['class'] !== ''
|
|
? $this->getClassEntryPtr($definition['class'])
|
|
: 'nullptr';
|
|
return 'php::toTypedArray(' . $source . ', '
|
|
. ($definition['keyType'] === Type::STR ? 'true' : 'false') . ', '
|
|
. 'php::TypedArrayValueType::' . $valueType . ', ' . $valueClass . ')';
|
|
}
|
|
|
|
protected function getTypedArrayAccessDefinition(NodeAbstract $expr): ?array
|
|
{
|
|
return $expr instanceof Expr\ArrayDimFetch ? $this->getTypedArrayDefinition($expr->var) : null;
|
|
}
|
|
|
|
protected function assertTypedArrayReferenceForbidden(NodeAbstract $expr): void
|
|
{
|
|
while ($expr instanceof Expr\ArrayDimFetch) {
|
|
$expr = $expr->var;
|
|
}
|
|
if ($this->getTypedArrayDefinition($expr) !== null) {
|
|
$this->fatalError($expr, 'Typed arrays cannot escape through std::ref(), toRef(), or element references');
|
|
}
|
|
}
|
|
|
|
protected function parseTypedArrayAssignment(Expr $left, Expr $right): ?string
|
|
{
|
|
if ($this->getTypedArrayAccessDefinition($left) !== null) {
|
|
return $this->parseTypedArrayWrite($left, $right);
|
|
}
|
|
// Nested writes would bypass the declared outer element type.
|
|
if ($left instanceof Expr\ArrayDimFetch && $left->var instanceof Expr\ArrayDimFetch
|
|
&& $this->getTypedArrayAccessDefinition($left->var) !== null) {
|
|
$this->fatalError($left, 'Nested typed-array writes must replace a checked element');
|
|
}
|
|
if (!$this->isVarExpr($left)) {
|
|
return null;
|
|
}
|
|
$name = $this->parseWritableIdentifier($left);
|
|
$factory = $right instanceof Expr\StaticCall && $right->class instanceof Node\Name
|
|
&& $right->name instanceof Node\Identifier && $this->isStdClassExpr($right->class)
|
|
&& in_array(strtolower($right->name->name), ['list', 'dict'], true);
|
|
$definition = $factory
|
|
? $this->parseTypedArrayFactoryDefinition(strtolower($right->name->name), $right)
|
|
: $this->getTypedArrayDefinition($right);
|
|
$existing = $this->context->typedArrays[$name] ?? null;
|
|
if ($definition === null && $existing === null) {
|
|
return null;
|
|
}
|
|
if ($existing !== null && $definition !== $existing) {
|
|
$this->fatalError($right, 'Typed array assignment requires an identical list/dict contract');
|
|
}
|
|
if ($this->hasScopeGlobalVar($name) || $this->hasStaticVar($name)
|
|
|| isset($this->context->varTypeDegradations[$name])) {
|
|
$this->fatalError($left, 'Typed arrays require fixed function-local storage and cannot be captured by reference');
|
|
}
|
|
if ($factory && $this->hasVar($name)) {
|
|
$this->fatalError($left, 'Typed array factories require a new variable');
|
|
}
|
|
if ($existing === null) {
|
|
if ($this->hasVar($name) && $this->getRawVarType($name) !== Type::ARRAY) {
|
|
$this->fatalError($left, 'Cannot erase a typed array into dynamic or reference storage');
|
|
}
|
|
// Do not retrofit a contract onto an already-live ordinary array:
|
|
// existing aliases or previous paths may still write untyped values.
|
|
if ($this->hasVar($name)) {
|
|
$this->fatalError($left, 'Typed array propagation requires a new variable');
|
|
}
|
|
$this->addLocalVar($name, Type::ARRAY);
|
|
$this->context->typedArrays[$name] = $definition;
|
|
}
|
|
if ($factory) {
|
|
$initializer = $this->getStdValueInitializer($right);
|
|
return $name . ' = ' . ($initializer === null ? 'php::Array{}' : $this->parseArray($initializer));
|
|
}
|
|
return $name . ' = ' . $this->parseExprAsValue($right);
|
|
}
|
|
|
|
protected function guardTypedArrayValue(array $def, Expr $expr, string $code, bool $key = false): string
|
|
{
|
|
$this->assertExprCanBeUsedAsValue($expr, 'typed array element');
|
|
$expected = $key ? $def['keyType'] : $def['type'];
|
|
$class = $key ? '' : ($def['class'] ?? '');
|
|
$actual = Type::getReferencedType($this->detectTypeOfExpr($expr));
|
|
if ($this->isNativeObjectClass($this->detectClassOfExpr($expr))) {
|
|
$this->fatalError($expr, 'Native objects cannot be stored in typed PHP arrays');
|
|
}
|
|
if ($key && $actual === Type::VAR) {
|
|
// Dynamic keys need a strict check, not a coercing conversion.
|
|
// These are internal PHPX helpers, not user keyword methods.
|
|
return ($expected === Type::INT ? 'php::toIntExact(' : 'php::toStringExact(') . $code . ')';
|
|
}
|
|
if (!$key && $expected === Type::VAR) {
|
|
return $code;
|
|
}
|
|
if ($class !== '') {
|
|
$actualClass = $this->detectClassOfExpr($expr);
|
|
if ($actualClass === '' || !$this->isObjectClassStaticallyAssignableTo($actualClass, $class)) {
|
|
$this->fatalError($expr, 'Typed array value must be an instance of ' . $class);
|
|
}
|
|
if ($actual !== Type::OBJECT) {
|
|
$this->fatalError($expr, 'Typed array value must be an instance of ' . $class);
|
|
}
|
|
return $code;
|
|
}
|
|
if ($actual !== $expected) {
|
|
$this->fatalError($expr, 'Typed array ' . ($key ? 'key' : 'value') . ' must have type ' . $expected);
|
|
}
|
|
if ($expected === Type::INT && $this->varIntTypes && $this->exprCanOverflowInt($expr)) {
|
|
$this->fatalError($expr, 'Typed array integer expressions that may widen require an explicit toInt() or native integer conversion');
|
|
}
|
|
return $code;
|
|
}
|
|
|
|
protected function convertTypedArrayRead(array $def, string $code): string
|
|
{
|
|
// Recover the statically proven type; no Exact checks or element scans.
|
|
return $this->convertExprFromType($def['type'], $code);
|
|
}
|
|
|
|
protected function parseTypedArrayKey(Expr\ArrayDimFetch $expr, bool $write = false): string
|
|
{
|
|
$def = $this->getTypedArrayAccessDefinition($expr);
|
|
if ($expr->dim === null) {
|
|
if (!$write || $def['kind'] !== 'list') {
|
|
$this->fatalError($expr, 'Only typed lists support append writes');
|
|
}
|
|
return '';
|
|
}
|
|
$key = $this->guardTypedArrayValue($def, $expr->dim, $this->parseExprAsValue($expr->dim), true);
|
|
return $key;
|
|
}
|
|
|
|
protected function parseTypedArrayRead(Expr\ArrayDimFetch $expr): string
|
|
{
|
|
$def = $this->getTypedArrayAccessDefinition($expr);
|
|
return $this->convertTypedArrayRead($def, $this->parseTypedArrayRawRead($expr));
|
|
}
|
|
|
|
protected function parseTypedArrayRawRead(Expr\ArrayDimFetch $expr): string
|
|
{
|
|
$array = $this->parseIdentifier($expr->var);
|
|
$key = $this->parseTypedArrayKey($expr);
|
|
return $array . '.offsetGet(' . $key . ')';
|
|
}
|
|
|
|
protected function parseTypedArrayPresence(Expr\ArrayDimFetch $expr, string $op, bool $getValue): string
|
|
{
|
|
$raw = $this->parseTypedArrayRawRead($expr);
|
|
if ($getValue) {
|
|
$result = $this->addTmpVar(Type::VAR);
|
|
$expr->setAttribute('chainOpResult', $result);
|
|
$raw = '(' . $result . ' = ' . $raw . ')';
|
|
}
|
|
return match ($op) {
|
|
self::OP_ISSET => 'php::exists(' . $raw . ')',
|
|
self::OP_EMPTY => 'php::empty(' . $raw . ')',
|
|
self::OP_NOT_EMPTY => 'php::notEmpty(' . $raw . ')',
|
|
default => $this->getChainedFunc($op) . '(' . $raw . ')',
|
|
};
|
|
}
|
|
|
|
protected function parseTypedArrayKeyExistsCall(Expr\FuncCall $call): ?string
|
|
{
|
|
if (count($call->args) !== 2) {
|
|
return null;
|
|
}
|
|
$arguments = [];
|
|
foreach ($call->args as $index => $argument) {
|
|
if ($argument->unpack || $argument->byRef) {
|
|
return null;
|
|
}
|
|
$name = $argument->name?->name ?? ($index === 0 ? 'key' : 'array');
|
|
if (!in_array($name, ['key', 'array'], true) || isset($arguments[$name])) {
|
|
return null;
|
|
}
|
|
$arguments[$name] = $argument->value;
|
|
}
|
|
if (!isset($arguments['key'], $arguments['array'])
|
|
|| $this->getTypedArrayDefinition($arguments['array']) === null) {
|
|
return null;
|
|
}
|
|
$definition = $this->getTypedArrayDefinition($arguments['array']);
|
|
// Snapshot source arguments in order, including named arguments.
|
|
$values = [];
|
|
foreach ($arguments as $name => $argument) {
|
|
if ($name === 'key') {
|
|
$key = $this->guardTypedArrayValue($definition, $argument, $this->parseExprAsValue($argument), true);
|
|
$values[$name] = $this->addTmpVar($definition['keyType']);
|
|
$this->context->beforeStmtLines[] = $values[$name] . ' = ' . $key . ';';
|
|
} else {
|
|
$values[$name] = $this->parseOrderedOperand($argument, false, true);
|
|
}
|
|
}
|
|
// Existing PHPX lookup retains PHP numeric-string normalization.
|
|
return $values['array'] . '.exists(' . $values['key'] . ')';
|
|
}
|
|
|
|
protected function parseTypedArrayForeach(Foreach_ $node, array $def): string
|
|
{
|
|
if ($node->byRef) {
|
|
$this->fatalError($node, 'Typed array elements cannot escape through foreach references');
|
|
}
|
|
$array = $this->parseIdentifier($node->expr);
|
|
$iterator = $this->genTmpVarName();
|
|
$assignments = '';
|
|
foreach ([[$node->keyVar, $def['keyType'], '', 'key'],
|
|
[$node->valueVar, $def['type'], $def['class'] ?? '', 'value']] as [$target, $type, $class, $part]) {
|
|
if ($target === null) {
|
|
continue;
|
|
}
|
|
if (!$this->isVarExpr($target)) {
|
|
$this->fatalError($target, 'Typed array foreach requires simple typed key/value variables');
|
|
}
|
|
$this->assertImmutableMutationTarget($target);
|
|
$name = $this->parseWritableIdentifier($target);
|
|
if (isset($this->context->varTypeDegradations[$name])) {
|
|
$this->fatalError($target, 'Typed foreach variables cannot be captured by reference');
|
|
}
|
|
if ($this->hasVar($name) && ($this->getRawVarType($name) !== $type
|
|
|| ($class !== '' && $this->getDeclaredObjectType($name) !== $class))) {
|
|
$this->fatalError($target, 'Typed array foreach variable must have the declared key/value type');
|
|
}
|
|
if (!$this->hasVar($name)) {
|
|
$this->addLocalVar($name, $type);
|
|
if ($class !== '') {
|
|
$this->addObject($name, $class);
|
|
}
|
|
}
|
|
// PHP normalizes numeric string keys to integers. Recover the
|
|
// declared key type here, without changing PHPX or array storage.
|
|
$assignments .= $this->getIndent() . ' ' . $name . ' = '
|
|
. $this->convertExprFromType($type, $iterator . '.' . $part . '()') . ';' . PHP_EOL;
|
|
}
|
|
$scope = $this->class ? $this->getLocalClassEntryPtr($this->getFullClassName()) : 'nullptr';
|
|
$code = '{' . PHP_EOL . $this->getIndent() . 'php::ForeachIterator ' . $iterator
|
|
. '{' . $array . ', false, ' . $scope . '};' . PHP_EOL
|
|
. $this->getIndent() . 'while (' . $iterator . '.next()) {' . PHP_EOL . $assignments;
|
|
$this->indentLevel++;
|
|
$body = $this->parseForeachBody($node);
|
|
$this->indentLevel--;
|
|
return $code . $this->parseBeforeStmtLines() . $body . $this->getIndent() . '}' . PHP_EOL
|
|
. $this->getIndent() . '}' . PHP_EOL;
|
|
}
|
|
|
|
protected function parseTypedArrayWrite(Expr\ArrayDimFetch $left, Expr $right): string
|
|
{
|
|
$def = $this->getTypedArrayAccessDefinition($left);
|
|
$array = $this->parseWritableIdentifier($left->var);
|
|
$key = $this->parseTypedArrayKey($left, true);
|
|
// Snapshot a key before lowering a RHS that might hoist side effects.
|
|
if ($key !== '') {
|
|
$keyVar = $this->addTmpVar($def['keyType']);
|
|
$this->context->beforeStmtLines[] = $keyVar . ' = ' . $key . ';';
|
|
$key = $keyVar;
|
|
}
|
|
$value = $this->guardTypedArrayValue($def, $right, $this->parseExprAsValue($right));
|
|
$result = $this->addTmpVar(Type::VAR);
|
|
// An assignment expression yields the assigned value, not void.
|
|
return '[&]() -> php::Var { ' . $result . ' = ' . $value . '; '
|
|
. $this->genTypedArrayStore($array, $key, $result) . '; return ' . $result . '; }()';
|
|
}
|
|
|
|
private function genTypedArrayStore(string $array, string $key, string $value): string
|
|
{
|
|
if ($key === '') {
|
|
return $array . '.appendValue(' . $value . ')';
|
|
}
|
|
return $array . '.offsetSet(' . $key . ', ' . $value . ')';
|
|
}
|
|
|
|
protected function parseTypedArrayUnset(Expr\ArrayDimFetch $expr): string
|
|
{
|
|
$array = $this->parseWritableIdentifier($expr->var);
|
|
$key = $this->parseTypedArrayKey($expr);
|
|
return $array . '.offsetUnset(' . $key . ')';
|
|
}
|
|
|
|
protected function assertTypedArrayArgument(Node\Arg $arg, ?ArgInfo $parameter, bool $byRef, bool $project): void
|
|
{
|
|
$expr = $arg->value;
|
|
if ($this->isReferenceWrapperCall($expr)) {
|
|
$expr = $this->unwrapReferenceWrapperCall($expr, $arg);
|
|
$this->assertTypedArrayReferenceForbidden($expr);
|
|
}
|
|
$def = $this->getTypedArrayDefinition($expr);
|
|
$expected = $parameter?->typedArray;
|
|
if ($expected !== null && $def !== $expected) {
|
|
$this->fatalError($arg, 'Typed array parameter requires an identical list/dict contract');
|
|
}
|
|
if ($def !== null && ($arg->unpack || ($project && $expected === null) || ($byRef && $expected === null))) {
|
|
$this->fatalError($arg, 'Typed arrays require matching annotated TypePHP parameters or read-only PHP calls');
|
|
}
|
|
if ($byRef && $expected === null) {
|
|
$this->assertTypedArrayReferenceForbidden($expr);
|
|
}
|
|
}
|
|
|
|
protected function validateTypedArrayDynamicArgument(Node\Arg $arg, string $function, string $class, int $index): void
|
|
{
|
|
$parameter = $arg->name !== null
|
|
? $this->getAotCallArgInfoByName($function, $class, $arg->name->name)
|
|
: $this->getAotCallArgInfo($function, $class, $index);
|
|
if ($parameter?->typedArray !== null) {
|
|
$this->fatalError($arg, 'Typed array parameters require a statically resolved native TypePHP call, not Zend dispatch');
|
|
}
|
|
$byRef = $function !== '' && ($arg->name !== null
|
|
? $this->isReferenceNamedArgument($function, $class, $arg->name->name)
|
|
: $this->isReferenceArgument($function, $class, $index));
|
|
$this->assertTypedArrayArgument($arg, $parameter, $byRef, $parameter !== null);
|
|
}
|
|
|
|
protected function functionUsesTypedArray(\TypePhp\Entity\FunctionDef $function): bool
|
|
{
|
|
foreach ($function->argInfoList as $parameter) {
|
|
if ($parameter->typedArray !== null) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected function functionRequiresNativeAbi(\TypePhp\Entity\FunctionDef $function): bool
|
|
{
|
|
return $this->functionUsesNativeObject($function) || $this->functionUsesTypedArray($function);
|
|
}
|
|
}
|
|
|