visitIndexExpression method

  1. @override
Object? visitIndexExpression(
  1. SIndexExpression node
)
override

Visit a SIndexExpression.

Implementation

@override
Object? visitIndexExpression(SIndexExpression node) {
  final target = node.target;
  final index = node.index;
  final targetValue = target?.accept<Object?>(this);

  // Handle null-aware indexing: x?[i] returns null if x is null
  if (node.isNullAware && targetValue == null) {
    return null;
  }

  // C21 — Dart null-shorting: when an inner selector in this chain
  // uses `?.`/`?[…]` (e.g. `a?.b[i]` where `a == null`), the outer
  // index access must short-circuit to null instead of throwing.
  if (targetValue == null && _chainHasNullAwareSelector(node.target)) {
    return null;
  }

  final indexValue = index!.accept<Object?>(this);

  if (targetValue is AsyncSuspensionRequest) return targetValue;
  if (indexValue is AsyncSuspensionRequest) return indexValue;

  if (targetValue is Map) {
    // Unwrap BridgedEnumValue keys to nativeValue so lookups work
    // regardless of whether the map was built with native or wrapped keys.
    final key = indexValue is BridgedEnumValue
        ? indexValue.nativeValue
        : indexValue;
    return targetValue[key];
  }
  if (targetValue is String && indexValue is int) {
    return targetValue[indexValue];
  } else if (targetValue is List) {
    if (indexValue is int) {
      if (indexValue < 0 || indexValue >= targetValue.length) {
        throw RuntimeD4rtException('Index out of range: $indexValue');
      }
      return targetValue[indexValue];
    } else {
      throw RuntimeD4rtException('List index must be an integer');
    }
  } else if (targetValue is InterpretedInstance) {
    // Check for class operator [] method
    final operatorMethod = targetValue.findOperator('[]');
    if (operatorMethod != null) {
      Logger.debug(
        "[visitIndexExpression] Found class operator '[]' on ${targetValue.klass.name}. Calling...",
      );
      try {
        return operatorMethod.bind(targetValue).call(this, [indexValue], {});
      } on ReturnException catch (e) {
        return e.value;
      } catch (e) {
        throw RuntimeD4rtException("Error executing class operator '[]': $e");
      }
    }
  } else if (toBridgedInstance(targetValue).$2) {
    final bridgedInstance = toBridgedInstance(targetValue).$1!;
    final bridgedClass = bridgedInstance.bridgedClass;
    final operatorName = '[]';

    final methodAdapter = bridgedClass.findInstanceMethodAdapter(
      operatorName,
    );

    if (methodAdapter != null) {
      Logger.debug(
        "[visitIndexExpression] Found bridged operator '$operatorName' for ${bridgedClass.name}. Calling adapter...",
      );
      try {
        return methodAdapter(
          this,
          bridgedInstance.nativeObject,
          [indexValue],
          {},
          null,
        );
      } catch (e, s) {
        Logger.error(
          "[visitIndexExpression] Native exception during bridged operator '$operatorName' on ${bridgedClass.name}: $e\\n$s",
        );
        throw RuntimeD4rtException(
          "Native error during bridged operator '$operatorName' on ${bridgedClass.name}: $e",
          originalException: e,
        );
      }
    }
    Logger.debug(
      "[visitIndexExpression] Bridged operator '$operatorName' not found directly for ${bridgedClass.name}. Trying extensions.",
    );
  }

  const operatorNameForExtension = '[]';
  try {
    final extensionOperator = environment.findExtensionMember(
      targetValue,
      operatorNameForExtension,
    );

    if (extensionOperator is ExtensionMemberCallable &&
        extensionOperator.isOperator) {
      Logger.debug(
        "[SIndexExpression] Found extension operator '[]' for type ${targetValue?.runtimeType}. Calling...",
      );
      final extensionPositionalArgs = [targetValue, indexValue];
      try {
        return extensionOperator.call(this, extensionPositionalArgs, {});
      } on ReturnException catch (e) {
        return e.value;
      } catch (e) {
        throw RuntimeD4rtException(
          "Error executing extension operator '[]': $e",
        );
      }
    }
    Logger.debug(
      "[SIndexExpression] No suitable extension operator '[]' found for type ${targetValue?.runtimeType}.",
    );
  } on RuntimeD4rtException catch (findError) {
    Logger.debug(
      "[SIndexExpression] No extension member '[]' found for type ${targetValue?.runtimeType}. Error: ${findError.message}",
    );
  }

  throw RuntimeD4rtException(
    'Unsupported target for indexing: ${targetValue?.runtimeType}',
  );
}