visitIndexExpression method
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}',
);
}