visitFunctionDeclaration method

  1. @override
void visitFunctionDeclaration(
  1. SFunctionDeclaration node
)
override

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);
}