visitFunctionDeclaration method
Visit a SFunctionDeclaration.
Implementation
@override
void visitFunctionDeclaration(SFunctionDeclaration node) {
final functionName = node.name?.name ?? '';
Logger.debug(
"[DeclarationVisitor.visitFunctionDeclaration] Processing function: $functionName");
if (environment.isDefinedLocally(functionName)) {
return;
}
// Handle type parameters for generic functions
Environment? tempEnvironment;
final typeParameters = node.functionExpression?.typeParameters;
if (typeParameters != null) {
Logger.debug(
"[DeclarationVisitor.visitFunctionDeclaration] Function '$functionName' has ${typeParameters.typeParameters.length} type parameters");
// Create a temporary environment for type resolution
tempEnvironment = Environment(enclosing: environment);
// Create temporary type parameter placeholders
for (final typeParam in typeParameters.typeParameters) {
final paramName = typeParam.name?.name ?? '';
// Create a simple TypeParameter placeholder in the temp environment
final typeParamPlaceholder = TypeParameter(paramName);
tempEnvironment.define(paramName, typeParamPlaceholder);
Logger.debug(
"[DeclarationVisitor.visitFunctionDeclaration] Defined type parameter '$paramName' in temp environment");
}
}
// Use the temp environment (if any) for type resolution, otherwise use the normal environment
final resolveEnvironment = tempEnvironment ?? environment;
// Now resolve the return type (which might reference type parameters)
final returnTypeNode = node.returnType;
RuntimeType declaredReturnType;
if (returnTypeNode is SNamedType) {
final typeName = returnTypeNode.name?.name ?? '';
Logger.debug(
"[DeclarationVisitor.visitFunctionDeclaration] Return type node name: $typeName");
try {
final resolvedType = resolveEnvironment.get(typeName);
Logger.debug(
"[DeclarationVisitor.visitFunctionDeclaration] environment.get('$typeName') resolved to: ${resolvedType?.runtimeType} with name: ${(resolvedType is RuntimeType ? resolvedType.name : 'N/A')}");
if (resolvedType is RuntimeType) {
declaredReturnType = resolvedType;
} else {
Logger.warn(
"[DeclarationVisitor.visitFunctionDeclaration] Type '$typeName' resolved to non-RuntimeType: $resolvedType. Using placeholder.");
declaredReturnType = BridgedClass(nativeType: Object, name: typeName);
}
} on RuntimeD4rtException catch (e) {
Logger.warn(
"[DeclarationVisitor.visitFunctionDeclaration] Type '$typeName' not found in environment (RuntimeError: ${e.message}). Using placeholder.");
declaredReturnType = BridgedClass(nativeType: Object, name: typeName);
}
} else if (returnTypeNode == null) {
declaredReturnType = BridgedClass(nativeType: Object, name: 'dynamic');
} else {
// For other TypeAnnotation types, use a generic placeholder
declaredReturnType =
BridgedClass(nativeType: Object, name: 'unknown_type_placeholder');
}
bool isNullable =
(returnTypeNode is SNamedType) && returnTypeNode.isNullable;
// For async functions with Future<T?> return type, extract isNullable from inner type T
final body0 = node.functionExpression?.body;
bool isAsync = (body0 is SBlockFunctionBody)
? body0.isAsync
: (body0 is SExpressionFunctionBody)
? body0.isAsync
: false;
if (isAsync &&
!isNullable &&
returnTypeNode is SNamedType &&
returnTypeNode.name?.name == 'Future') {
final typeArgs = returnTypeNode.typeArguments;
if (typeArgs != null && typeArgs.arguments.isNotEmpty) {
final innerType = typeArgs.arguments.first;
if (innerType is SNamedType) {
isNullable = innerType.isNullable;
}
}
}
final function = InterpretedFunction.declaration(
node,
environment, // The function captures the environment it's declared in
declaredReturnType,
isNullable);
Logger.debug(
"[DeclarationVisitor.visitFunctionDeclaration] Defining function '$functionName' with declaredReturnType: ${declaredReturnType.name} (Hash: ${declaredReturnType.hashCode})");
environment.define(functionName, function);
}