visitAsExpression method
Visit a SAsExpression.
Implementation
@override
Object? visitAsExpression(SAsExpression node) {
final value = node.expression!.accept<Object?>(this);
final typeNode = node.type;
if (typeNode is SNamedType) {
final rawTypeName = typeNode.name!.name;
// GEN-100c: Handle import-prefixed cast types (e.g., value as ui.Color)
final typeName = typeNode.importPrefix != null
? '${typeNode.importPrefix!.name}.$rawTypeName'
: rawTypeName;
// G-DOV2-1 FIX: Handle nullable types (e.g., String?, int?)
// If the type is nullable (has a '?' suffix), then null is always allowed
final isNullable = typeNode.isNullable;
if (isNullable && value == null) {
return value; // Null is valid for any nullable type
}
switch (typeName) {
case 'int':
if (value is int) return value;
break;
case 'double':
if (value is double) return value;
// GEN-094: int→double promotion for `x as double` mirrors the
// INTER-003 promotion already done by extractBridgedArg and
// matches the semantics the analyzer applies to `<double>[0,
// 25, 50]` literals — the D4rt interpreter stores those
// elements as int, so `list[0] as double` would otherwise
// fail even though the same cast succeeds in native Dart.
if (value is int) return value.toDouble();
break;
case 'num':
if (value is num) return value;
break;
case 'String':
if (value is String) return value;
break;
case 'bool':
if (value is bool) return value;
break;
case 'List':
if (value is List) return value;
break;
case 'Null':
if (value == null) return value;
break;
case 'Object':
// G-DOV2-1 FIX: For Object?, null is valid (handled above)
// For Object, any non-null value is valid
if (value != null || isNullable) return value;
break;
case 'dynamic':
return value;
default:
// C21: If the value is a native interface-proxy that wraps an
// InterpretedInstance and the cast target names a class in the
// wrapped instance's class chain, unwrap to the InterpretedInstance
// so subsequent property/method access dispatches against the
// scripted class (which knows about user-defined fields and
// methods that the bridged proxy alone cannot serve).
if (value is D4InterpretedProxy) {
final inner = value.d4rtInstance;
if (inner is InterpretedInstance &&
_interpretedClassChainHasName(inner.klass, typeName)) {
return inner;
}
}
// For custom/interpreted types, we can add logic here
// For now, we accept all (permissive behavior)
return value;
}
}
// GEN-094: Improve error message: toString()-based error was always
// "Instance of 'SNamedType'" because SNamedType doesn't override
// toString. Include the actual type name and value type so the
// failure is actionable.
final typeDesc = typeNode is SNamedType
? (typeNode.importPrefix != null
? '${typeNode.importPrefix!.name}.${typeNode.name?.name ?? '?'}'
: typeNode.name?.name ?? '?')
: typeNode.runtimeType.toString();
final valueDesc = value?.runtimeType.toString() ?? 'Null';
throw RuntimeD4rtException(
"Cast failed with 'as' : value of type $valueDesc cannot be cast to $typeDesc",
);
}