call method

  1. @override
Object? call(
  1. InterpreterVisitor visitor,
  2. List<Object?> positionalArguments, [
  3. Map<String, Object?> namedArguments = const {},
  4. List<RuntimeType>? typeArguments,
])
override

Implementation

@override
Object? call(InterpreterVisitor visitor, List<Object?> positionalArguments,
    [Map<String, Object?> namedArguments = const {},
    List<RuntimeType>? typeArguments]) {
  // OPEN B.1 — if the unnamed constructor is a factory, dispatch to it
  // directly. A factory must not pre-create an instance of (possibly
  // abstract) this class; it creates and returns its own instance, which is
  // how redirecting factories (`factory X() = Y`) reach the subclass.
  final unnamedConstructor = findConstructor('');
  if (unnamedConstructor != null && unnamedConstructor.isFactory) {
    return unnamedConstructor.call(
        visitor, positionalArguments, namedArguments, typeArguments);
  }

  // 1. Create and initialize instance using the new helper
  final instance = createAndInitializeInstance(visitor, typeArguments);

  // 2. Find the UNNAMED constructor
  var constructor = unnamedConstructor; // Look for the default constructor

  // RC-4: If no unnamed constructor found but the class has exactly one
  // constructor (named), use it as fallback. This handles cases like
  // private classes with only a named const constructor (e.g.,
  // _ThemePreset({required this.id, ...})) that are called dynamically.
  if (constructor == null && constructors.length == 1) {
    constructor = constructors.values.first;
    Logger.debug(
        "[Instance Init] No unnamed constructor for '$name', falling back to sole constructor '${constructors.keys.first}'");
  }

  // 3. Call constructor if found, binding 'this' (which is the instance)
  if (constructor != null) {
    try {
      // The constructor function already has the class closure.
      // Binding it adds 'this' to a new environment enclosing the class closure.
      // Parameter evaluation and body execution will happen in environments
      // enclosing this bound environment.
      final boundConstructor = constructor.bind(instance);
      // Pass initializers to call?
      boundConstructor.call(
          visitor, positionalArguments, namedArguments, typeArguments);
    } on RuntimeD4rtException catch (e) {
      throw RuntimeD4rtException(
          "Error during constructor execution for class '$name': ${e.message}");
    }
  } else {
    // No explicit constructor found. Check arity for default constructor.
    if (positionalArguments.isNotEmpty || (namedArguments.isNotEmpty)) {
      throw RuntimeD4rtException(
          "Class '$name' does not have an unnamed constructor that accepts arguments.");
    }
    // If no constructor and no args passed, it's okay (implicit default constructor).
    // However, we STILL need to call the superclass's default constructor if one exists.
    if (superclass != null) {
      final defaultSuperConstructor = superclass!.findConstructor('');
      if (defaultSuperConstructor != null) {
        Logger.debug(
            "[Instance Init] Calling implicit super() for class '$name' with no explicit constructor");
        // Call the default super constructor, bound to the instance
        defaultSuperConstructor.bind(instance).call(visitor, [], {});
      }
      // If superclass has no explicit constructor either, that's fine -
      // the superclass's createAndInitializeInstance will handle field initialization.
      // No error needed here.
    }

    // RC-5 Path B: Also check for bridged superclass when no explicit constructor.
    // When an interpreted class extends a bridged class (e.g., CounterNotifier
    // extends ChangeNotifier) and has no explicit constructor, the bridged
    // superclass's default constructor must still be called to initialize
    // the native super object (bridgedSuperObject).
    if (bridgedSuperclass != null && instance.bridgedSuperObject == null) {
      final defaultBridgedCtor =
          bridgedSuperclass!.findConstructorAdapter('');
      if (defaultBridgedCtor != null) {
        Logger.debug(
            "[Instance Init] Calling implicit bridged super() for class '$name' (bridged super: ${bridgedSuperclass!.name})");
        try {
          final nativeSuperObject = defaultBridgedCtor(visitor, [], {});
          instance.bridgedSuperObject = nativeSuperObject;
        } catch (e) {
          Logger.error(
              "[Instance Init] Error during implicit bridged super() for '$name': $e");
        }
      }
    }
  }

  // Field initializers from constructor list (e.g., : this.x = y) need separate handling.
  // This should happen *after* field initializers above but *before* constructor body.
  // This logic needs to be added to InterpretedFunction.call for constructors.

  // 4. Return the initialized instance
  return instance;
}