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]) {
// 1. Extract the target instance (first argument)
if (positionalArguments.isEmpty) {
throw RuntimeD4rtException(
"Internal error: Extension method '${declaration.name!.name}' called without target instance ('this').");
}
final targetInstance =
positionalArguments.removeAt(0); // Consomme le premier argument
// 2. Create the execution environment
final executionEnvironment = Environment(enclosing: closure);
// Define 'this' in this environment
executionEnvironment.define('this', targetInstance);
Logger.debug(
"[InterpretedExtensionMethod.call] Created execution env (${executionEnvironment.hashCode}) for '${declaration.name!.name}', defining 'this'=${targetInstance?.runtimeType}");
// 3. Bind the declared parameters (explicit arguments)
final params = declaration.parameters?.parameters;
int positionalArgIndex = 0;
final providedNamedArgs = namedArguments;
final processedParamNames = <String>{};
if (params != null) {
for (final param in params) {
String? paramName;
SAstNode? defaultValueExpr;
bool isRequired = false;
bool isOptionalPositional = false;
bool isNamed = false;
bool isRequiredNamed = false;
// Determine the parameter info (copied from InterpretedFunction)
SAstNode actualParam = param;
if (param is SDefaultFormalParameter) {
defaultValueExpr = param.defaultValue;
actualParam = param.parameter ?? param;
}
if (actualParam is SSimpleFormalParameter) {
paramName = actualParam.name?.name;
isRequired = actualParam.isPositional && actualParam.isRequired;
isOptionalPositional =
actualParam.isPositional && !actualParam.isRequired;
isNamed = actualParam.isNamed;
isRequiredNamed = actualParam.isNamed && actualParam.isRequired;
} else {
throw UnimplementedD4rtException(
"Unsupported parameter kind in extension method: ${actualParam.runtimeType}");
}
if (paramName == null) {
throw StateD4rtException("Extension parameter missing name");
}
processedParamNames.add(paramName);
// Find the corresponding argument and value
Object? valueToDefine;
bool argumentProvided = false;
if (isOptionalPositional || isRequired) {
if (positionalArgIndex < positionalArguments.length) {
valueToDefine = positionalArguments[positionalArgIndex++];
argumentProvided = true;
}
} else if (isNamed) {
if (providedNamedArgs.containsKey(paramName)) {
valueToDefine = providedNamedArgs[paramName];
argumentProvided = true;
}
}
// Handle default values and required checks
if (!argumentProvided) {
if (defaultValueExpr != null) {
final previousVisitorEnv = visitor.environment;
try {
visitor.environment =
closure; // Defaults evaluated in declaration scope
valueToDefine = defaultValueExpr.accept<Object?>(visitor);
} finally {
visitor.environment = previousVisitorEnv;
}
} else if (isRequired || isRequiredNamed) {
throw RuntimeD4rtException(
"Missing required ${isNamed ? 'named' : ''} argument for '$paramName' in extension method '${declaration.name!.name}'.");
} else {
valueToDefine = null;
}
}
// Define the variable in the execution environment
executionEnvironment.define(paramName, valueToDefine);
Logger.debug(
" [InterpretedExtensionMethod.call] Bound param '$paramName' = $valueToDefine");
}
// Final argument checks (copied from InterpretedFunction)
final int totalPositionalDeclared = params
.where((p) =>
(p is SSimpleFormalParameter && p.isPositional) ||
(p is SDefaultFormalParameter && p.isPositional))
.length;
if (positionalArgIndex < positionalArguments.length) {
throw RuntimeD4rtException(
"Too many positional arguments for extension method '${declaration.name!.name}'. Expected at most $totalPositionalDeclared, got ${positionalArguments.length}.");
}
for (final providedName in providedNamedArgs.keys) {
if (!processedParamNames.contains(providedName)) {
throw RuntimeD4rtException(
"Extension method '${declaration.name!.name}' does not have a parameter named '$providedName'.");
}
}
} else if (positionalArguments.isNotEmpty || providedNamedArgs.isNotEmpty) {
throw RuntimeD4rtException(
"Extension method '${declaration.name!.name}' takes no arguments (besides 'this'), but arguments were provided.");
}
// 4. Execute the body in the new environment
final previousEnvironment = visitor.environment;
final previousFunction = visitor.currentFunction;
// G-DOV2-7 FIX: install an InterpretedFunction.method wrapper so that
// `visitReturnStatement` reads *this* extension method's declared return
// type (and matching name) instead of falling back to the caller's
// function (typically `main` declared `void`). Without this, returning a
// value from an extension getter on an enum incorrectly tripped the
// String-vs-void check on `main`.
visitor.currentFunction =
InterpretedFunction.method(declaration, closure, onType);
visitor.environment = executionEnvironment; // USE THE NEW ENVIRONMENT
Logger.debug(
"[InterpretedExtensionMethod.call] Set visitor environment to executionEnvironment (${executionEnvironment.hashCode}) before executing body.");
try {
final body = declaration.body;
if (body is SBlockFunctionBody) {
// executeBlock already handles ReturnException correctly
return visitor.executeBlock(
body.block!.statements, executionEnvironment);
} else if (body is SExpressionFunctionBody) {
// For an expression body, evaluate and return the value
final result = body.expression!.accept<Object?>(visitor);
// No need to raise ReturnException here, we return directly
return result;
} else if (body is SEmptyFunctionBody) {
throw RuntimeD4rtException(
"Cannot execute empty body for extension method '${declaration.name!.name}'.");
} else {
throw UnimplementedD4rtException(
'Function body type not handled in extension method: ${body.runtimeType}');
}
} on ReturnException catch (e) {
// Catch in case executeBlock (or other) still raises ReturnException
Logger.debug(
" [InterpretedExtensionMethod.call] Caught ReturnException, returning value: ${e.value}");
return e.value;
} finally {
Logger.debug(
" [InterpretedExtensionMethod.call] Restoring visitor environment from (${visitor.environment.hashCode}) to (${previousEnvironment.hashCode}).");
visitor.environment = previousEnvironment;
visitor.currentFunction =
previousFunction; // Restaurer aussi currentFunction
}
}