callInterpreterCallback static method

Object? callInterpreterCallback(
  1. InterpreterVisitor visitor,
  2. Object? callback,
  3. List<Object?> args, [
  4. Map<String, Object?> namedArgs = const {},
])

Call an interpreter callback that may be either an InterpretedFunction or a NativeFunction.

G-DCLI-07 FIX: When bridge code needs to invoke a callback parameter (e.g., forEach(print)), the callback may be a NativeFunction (like print) or an InterpretedFunction (user-defined lambda). This method handles both.

The result is automatically unwrapped: BridgedInstance returns its BridgedInstance.nativeObject, BridgedEnumValue returns its BridgedEnumValue.nativeValue, and all other values pass through. This ensures generated bridge code casts (e.g., as Widget) succeed when the interpreter returns wrapped bridge values.

Returns the unwrapped native result of calling the callback.

Implementation

static Object? callInterpreterCallback(
  InterpreterVisitor visitor,
  Object? callback,
  List<Object?> args, [
  Map<String, Object?> namedArgs = const {},
]) {
  final Object? result;
  if (callback is InterpretedFunction) {
    result = callback.call(visitor, args, namedArgs);
  } else if (callback is NativeFunction) {
    result = callback.call(visitor, args, namedArgs);
  } else if (callback is Callable) {
    result = callback.call(visitor, args, namedArgs);
  } else if (callback is Function) {
    // 1401-TODO #8 (F5/F6): plain native Dart function. Happens when
    // the framework hands a typed callback (e.g. `TickerCallback =
    // void Function(Duration)`) to a script method via the bridge,
    // and the script forwards it unchanged to another bridge
    // constructor (e.g. `Ticker(onTick)`). The Ticker bridge wraps
    // it in `callInterpreterCallback`, which at tick time receives
    // the original native Dart closure — not wrapped as
    // InterpretedFunction/NativeFunction/Callable. `Function.apply`
    // dispatches by Dart Function signature; named args are
    // re-keyed to Symbols (Dart's `Function.apply` requires
    // `Map<Symbol, dynamic>?`).
    final Map<Symbol, dynamic>? symbolNamedArgs;
    if (namedArgs.isEmpty) {
      symbolNamedArgs = null;
    } else {
      symbolNamedArgs = {
        for (final entry in namedArgs.entries) Symbol(entry.key): entry.value,
      };
    }
    result = Function.apply(callback, args, symbolNamedArgs);
  } else {
    throw ArgumentD4rtException(
      'Expected a callable function, got ${callback?.runtimeType}',
    );
  }
  return unwrapInterpreterValue(result);
}