visitVariableDeclarationList method

  1. @override
Object? visitVariableDeclarationList(
  1. SVariableDeclarationList node
)
override

Implementation

@override
Object? visitVariableDeclarationList(SVariableDeclarationList node) {
  // We need to ensure variables are defined in the current environment.
  // visitVariableDeclaration is NOT automatically called for node.variables
  // by the generalizing visitor when visiting the list itself.
  for (final variable in node.variables) {
    if (variable.name!.name == '_') {
      // Evaluate initializer for potential side effects, but don't define
      variable.initializer?.accept<Object?>(this);
    } else {
      // Check if this is a late variable
      final isLate = node.isLate;
      final isFinal = node.isFinal;
      final variableName = variable.name!.name;

      // S3c (plan_3 §4.6 / §9.3): a slotted local dual-writes into BOTH the
      // frame's slot array and the name map [_values]. Resolved reads go
      // through [Environment.getSlot] (see [visitSimpleIdentifier]) — the hot
      // path that previously paid the per-level name hash — while name-based
      // resolution paths the interpreter still uses for the SAME local (the
      // function-invocation callee lookup `environment.get(name)` for
      // tear-offs / bridged-static / extension-call values) keep working
      // against [_values]. Full demotion out of [_values] is deferred until
      // every name-read site is audited (plan_3 §11). The slot index lives on
      // the mirror node itself ([SVariableDeclaration.declSlot]) — no Expando,
      // so it survives serialization.
      final declSlot = variable.declSlot;
      void def(Object? v) {
        environment.define(variableName, v);
        if (declSlot != null) {
          environment.defineSlot(declSlot, variableName, v);
        }
      }

      if (isLate) {
        // Handle late variable
        if (variable.initializer != null) {
          // Late variable with lazy initializer
          final lateVar = LateVariable(variableName, () {
            // Create a closure that will evaluate the initializer when accessed
            return variable.initializer!.accept<Object?>(this);
          }, isFinal: isFinal);
          def(lateVar);
          Logger.debug(
            "[VariableDeclList] Defined late variable '$variableName' with lazy initializer.",
          );
        } else {
          // Late variable without initializer
          final lateVar = LateVariable(variableName, null, isFinal: isFinal);
          def(lateVar);
          Logger.debug(
            "[VariableDeclList] Defined late variable '$variableName' without initializer.",
          );
        }
      } else {
        // Regular (non-late) variable handling
        Object? initValue;
        Object? result; // Value returned by accept() (could be suspension)

        if (variable.initializer != null) {
          result = variable.initializer!.accept<Object?>(this);
          if (result is AsyncSuspensionRequest) {
            // Async initializer: Define as null for now, result holds suspension
            Logger.debug(
              "[VariableDeclList] Async init for '$variableName'. Defined as null.",
            );
            def(null);
            // Propagate the suspension request.
            // If there are multiple async inits, the LAST suspension request wins.
          } else {
            // Sync initializer: Use the computed value
            initValue = result;
            if (Logger.isDebug) {
              Logger.debug(
                "[VariableDeclList] Sync init for '$variableName'. Defined as $initValue.",
              );
            }
            def(initValue);
          }
        } else {
          // No initializer: Define as null
          Logger.debug(
            "[VariableDeclList] No init for '$variableName'. Defined as null.",
          );
          def(null);
          result = null; // No suspension
        }

        // If the result of the variable's init was a suspension, we need to return it
        // to signal the state machine.
        if (result is AsyncSuspensionRequest) {
          // If any variable initialization caused suspension, return that suspension immediately.
          // The state machine needs to handle this before processing subsequent variables.
          Logger.debug(
            "[VariableDeclList] Propagating suspension from initializer of '$variableName'.",
          );
          return result;
        }
      }
    }
  }
  // If no suspension occurred (or only sync initializers for all variables)
  return null; // Original behavior
}