call method
Object?
call(
- InterpreterVisitor visitor,
- List<
Object?> positionalArguments, [ - Map<
String, Object?> namedArguments = const {}, - 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;
}