visitFunctionExpressionInvocation method

  1. @override
Object? visitFunctionExpressionInvocation(
  1. SFunctionExpressionInvocation node
)
override

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}",
    );
  }
}