visitSetOrMapLiteral method

  1. @override
Object? visitSetOrMapLiteral(
  1. SSetOrMapLiteral node
)
override

Visit a SSetOrMapLiteral.

Implementation

@override
Object? visitSetOrMapLiteral(SSetOrMapLiteral node) {
  // T3 (perf, F4): a const set/map literal is canonical — return the cached
  // instance instead of re-inferring the type, re-evaluating elements and
  // re-allocating an unmodifiable view on every visit.
  if (node.isConst) {
    final cached = _constCollectionCache[node];
    if (cached != null) return cached;
  }

  bool isMap;
  bool typeExplicit = false;

  // Check explicit type arguments first
  if (node.typeArguments != null &&
      node.typeArguments!.arguments.isNotEmpty) {
    isMap = node.typeArguments!.arguments.length == 2;
    typeExplicit = true;
    Logger.debug(
      "[SSetOrMapLiteral] Determined type via explicit args: isMap = $isMap",
    );
  } else {
    // No explicit types, infer from content
    isMap = false; // Default to Set if unsure initially
    bool onlySpreads = true;
    SAstNode? firstEffectiveElement;

    for (final element in node.elements) {
      // Check if element (or its inner body) is a SMapLiteralEntry
      if (_isMapLiteralElement(element)) {
        isMap = true;
        onlySpreads = false;
        firstEffectiveElement = element;
        Logger.debug(
          "[SSetOrMapLiteral] Determined isMap = true (found SMapLiteralEntry or element containing one).",
        );
        break; // Found a map entry, definitely a map
      }
      if (element is! SSpreadElement && firstEffectiveElement == null) {
        firstEffectiveElement = element;
        onlySpreads = false;
        // If it's an Expression, isMap remains false (it's a Set)
        Logger.debug(
          "[SSetOrMapLiteral] Determined isMap = false (found first non-spread Expression).",
        );
      }
      if (element is! SSpreadElement) {
        onlySpreads = false;
      }
    }

    // Handle empty literal or spread-only literal
    if (!typeExplicit) {
      // Re-evaluate if type wasn't explicit
      if (node.elements.isEmpty) {
        isMap = true; // Empty literal defaults to Map
        Logger.debug(
          "[SSetOrMapLiteral] Determined isMap = true (empty literal).",
        );
      } else if (onlySpreads) {
        // Only spread elements, no explicit type args. Infer from first spread.
        Logger.debug(
          "[SSetOrMapLiteral] Only spreads found. Inferring type from first spread element.",
        );
        final firstSpread = node.elements.first as SSpreadElement;
        final spreadValue = firstSpread.expression!.accept<Object?>(this);
        // Check if the evaluated spread value is a Map or looks like one
        final bridgedInstance = toBridgedInstance(spreadValue);
        if (spreadValue is Map ||
            (bridgedInstance.$2 && bridgedInstance.$1?.nativeObject is Map)) {
          isMap = true;
          Logger.debug(
            "[SSetOrMapLiteral]   First spread evaluated to Map. Setting isMap = true.",
          );
        } else {
          isMap = false; // Assume Set otherwise
          Logger.debug(
            "[SSetOrMapLiteral]   First spread did not evaluate to Map. Setting isMap = false.",
          );
        }
      } else if (firstEffectiveElement != null &&
          _isMapLiteralElement(firstEffectiveElement)) {
        isMap =
            true; // Confirmation if first non-spread was entry (or contains one)
      } else {
        isMap = false; // If first non-spread was Expression, it's a Set
      }
    }
  }

  // Create and populate the collection
  final Object collection = isMap ? <Object?, Object?>{} : <Object?>{};

  for (final element in node.elements) {
    try {
      _processCollectionElement(element, collection, isMap: isMap);
    } on RuntimeD4rtException catch (e) {
      final literalType = isMap ? "Map" : "Set";
      // Check if error already contains context to avoid duplication
      if (!e.message.contains('in $literalType literal')) {
        throw RuntimeD4rtException("${e.message} (in $literalType literal)");
      } else {
        rethrow; // Rethrow original error with context
      }
    }
  }

  // If this is a const collection, return an unmodifiable version and cache
  // it (T3): the same literal node must canonicalize to one instance.
  if (node.isConst) {
    final Object canonical = isMap
        ? Map.unmodifiable(collection as Map<Object?, Object?>)
        : Set.unmodifiable(collection as Set<Object?>);
    _constCollectionCache[node] = canonical;
    return canonical;
  }

  return collection;
}