visitFunctionExpressionInvocation method
Visit a SFunctionExpressionInvocation.
Implementation
@override
Object? visitFunctionExpressionInvocation(
SFunctionExpressionInvocation node,
) {
// 1. Evaluate the function expression itself.
// This should result in a Callable (like InterpretedFunction or NativeFunction).
final calleeValue = node.function!.accept<Object?>(this);
// 2. Evaluate arguments (shared logic).
final (positionalArgs, namedArgs) = _evaluateArguments(node.argumentList);
// 3. Evaluate type arguments (shared logic).
List<RuntimeType>? evaluatedTypeArguments;
final typeArgsNode = node.typeArguments;
if (typeArgsNode != null) {
evaluatedTypeArguments = typeArgsNode.arguments
.map((typeNode) => _resolveTypeAnnotation(typeNode))
.toList();
}
// 4. Check if it's actually callable (standard callable).
if (calleeValue is Callable) {
// 5. Perform the standard call.
try {
return calleeValue.call(
this,
positionalArgs,
namedArgs,
evaluatedTypeArguments,
);
} on ReturnException catch (e) {
return e.value;
}
}
// INTER-001 FIX: Check if it's a BridgedInstance with a call() method
final bridgedResult = toBridgedInstance(calleeValue);
if (bridgedResult.$2) {
final bridgedInstance = bridgedResult.$1!;
final callMethodAdapter = bridgedInstance.bridgedClass
.findInstanceMethodAdapter('call');
if (callMethodAdapter != null) {
Logger.debug(
"[FuncExprInvoke] Found 'call' method on BridgedInstance (${bridgedInstance.bridgedClass.name}). Invoking...",
);
try {
return callMethodAdapter(
this,
bridgedInstance.nativeObject,
positionalArgs,
namedArgs,
evaluatedTypeArguments,
);
} on ReturnException catch (e) {
return e.value;
}
}
}
// Try Extension 'call' Method
{
const methodName = 'call';
try {
final extensionMethod = environment.findExtensionMember(
calleeValue,
methodName,
);
if (extensionMethod is ExtensionMemberCallable &&
!extensionMethod
.isOperator && // Ensure it's a regular method named 'call'
!extensionMethod.isGetter &&
!extensionMethod.isSetter) {
Logger.debug(
"[FuncExprInvoke] Found extension method 'call' for type ${calleeValue?.runtimeType}. Calling...",
);
// Prepare arguments for extension method:
// First arg is the receiver (the object being called)
final extensionPositionalArgs = [calleeValue, ...positionalArgs];
try {
// Call the extension method
return extensionMethod.call(
this,
extensionPositionalArgs,
namedArgs,
evaluatedTypeArguments,
);
} on ReturnException catch (e) {
return e.value;
} catch (e) {
throw RuntimeD4rtException(
"Error executing extension method 'call': $e",
);
}
}
Logger.debug(
"[FuncExprInvoke] No suitable extension method 'call' found for type ${calleeValue?.runtimeType}.",
);
} on RuntimeD4rtException catch (findError) {
Logger.debug(
"[FuncExprInvoke] No extension member 'call' found for type ${calleeValue?.runtimeType}. Error: ${findError.message}",
);
// Fall through to the final standard error below.
}
// GEN-083: Allow invoking native Dart Function values (e.g. callbacks
// that were stored on a bridged target via a typed-wrapper setter and
// then read back through the getter).
//
// GEN-110 extension: Callable positional/named args (interpreted
// closures) are wrapped via `D4.coerceCallableToFunction` so they
// satisfy the native function's typed parameters.
if (calleeValue is Function) {
final wrappedPositional = positionalArgs
.map((a) => D4.coerceCallableToFunction(this, a))
.toList();
final wrappedNamed = namedArgs.map(
(k, v) => MapEntry(k, D4.coerceCallableToFunction(this, v)),
);
final symbolNamed = wrappedNamed.isEmpty
? const <Symbol, Object?>{}
: wrappedNamed
.map<Symbol, Object?>((k, v) => MapEntry(Symbol(k), v));
try {
return Function.apply(calleeValue, wrappedPositional, symbolNamed);
} on ReturnException catch (e) {
return e.value;
}
}
// Original Error: The expression evaluated did not yield a callable function or an object with a callable 'call' extension.
throw RuntimeD4rtException(
"Attempted to call something that is not a function and has no 'call' extension method. Got type: ${calleeValue?.runtimeType}",
);
}
}