visitYieldStatement method

  1. @override
Object? visitYieldStatement(
  1. SYieldStatement node
)
override

Visit a SYieldStatement.

Implementation

@override
Object? visitYieldStatement(SYieldStatement node) {
  final value = node.expression!.accept<Object?>(this);
  Logger.debug(
    "[SYieldStatement] Yielding value: $value (star: ${node.isStar})",
  );

  // If we're in an async* generator (with real async state), create a suspension
  if (currentAsyncState?.isGenerator == true) {
    final controller = currentAsyncState!.generatorStreamController!;

    final gate = currentAsyncState!.generatorGate;

    // SCF4: a subscription cancelled while the body was elsewhere ends it at
    // this yield, before the value is added.
    if (gate != null && gate.cancelled) {
      return AsyncSuspensionRequest(
        Future<Object?>.error(const GeneratorCancelledSignal()),
        currentAsyncState!,
        isYieldSuspension: true,
      );
    }

    if (node.isStar) {
      // yield* - handle asynchronously
      return AsyncSuspensionRequest(
        _handleYieldStarAsync(value, controller, gate),
        currentAsyncState!,
        isYieldSuspension: true,
      );
    } else {
      controller.add(value);
      // SCF4: wait for the listener. The suspension used to be on an
      // already-completed future, so the body ran on regardless of pauses
      // and of cancellation.
      return AsyncSuspensionRequest(
        gate?.afterYield(controller) ?? Future<void>.value(),
        currentAsyncState!,
        isYieldSuspension: true,
      );
    }
  }

  // If we're in a sync* generator context, add directly to the values list
  if (syncGeneratorValues != null) {
    if (node.isStar) {
      // yield* - expand iterable
      if (value is Iterable) {
        syncGeneratorValues!.addAll(value);
      } else {
        throw RuntimeD4rtException(
          "yield* expression must be an Iterable, got ${value.runtimeType}",
        );
      }
    } else {
      syncGeneratorValues!.add(value);
    }
    return null; // Continue execution
  }

  // Lim-4/Bug-43 FIX: If we're in a lazy sync* context, throw suspension to pause
  if (isLazySyncGeneratorContext) {
    throw SyncGeneratorYieldSuspension(value, isYieldStar: node.isStar);
  }

  // Fallback for other contexts
  return YieldValue(value, isYieldStar: node.isStar);
}