visitAsExpression method

  1. @override
Object? visitAsExpression(
  1. SAsExpression node
)
override

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",
  );
}