coerceList<T> static method

List<T> coerceList<T>(
  1. Object? arg,
  2. String paramName
)

Coerce a List from D4rt to a typed List.

D4rt creates List<Object?> when evaluating list literals, even if all elements are of the same type. This function coerces the list to the expected type by casting each element.

Implementation

static List<T> coerceList<T>(Object? arg, String paramName) {
  if (arg == null) {
    throw ArgumentD4rtException(
      'Invalid parameter "$paramName": expected List<$T>, got null',
    );
  }

  // Handle BridgedInstance wrapping
  final value = arg is BridgedInstance ? arg.nativeObject : arg;

  if (value is! List) {
    throw ArgumentD4rtException(
      'Invalid parameter "$paramName": expected List<$T>, got ${value.runtimeType}',
    );
  }

  // If already the correct type, return as-is
  if (value is List<T>) {
    return value;
  }

  // GEN-080: when T is non-nullable, drop null elements before coercion.
  // This matches Dart's collection-if semantics (`if (false) widget` adds
  // nothing to the resulting list) and prevents the interpreter — which
  // is more lenient than the analyzer about null leaking into typed
  // lists — from blowing up with `type 'Null' is not a subtype of type X`
  // on bridge constructors that take `List<T>` for non-nullable T.
  final iterable = (null is T) ? value : value.where((e) => e != null);

  // Coerce each element to the expected type
  try {
    return iterable.map<T>((e) {
      if (e is BridgedInstance) {
        final native = e.nativeObject;
        if (native is T) return native as T;
        // GEN-079: Try wrapper resolution before failing the cast.
        // e.g., TweenSequenceItem<dynamic> → $RelaxedTweenSequenceItem<double>
        final wrapped = _tryGenericWrapperResolution<T>(native);
        if (wrapped != null) return wrapped;
        return native as T;
      }
      if (e is BridgedEnumValue) {
        return e.nativeValue as T;
      }
      // RC-1: InterpretedInstance element unwrapping.
      // When a D4rt list contains interpreted widgets/objects that extend
      // a bridged type, unwrap via bridgedSuperObject.
      if (e is InterpretedInstance) {
        if (e.bridgedSuperObject is T) {
          return e.bridgedSuperObject as T;
        }
        // RC-1: Try interface proxy for elements that implement a bridged interface
        final effectiveVisitor = _activeVisitor;
        if (_interfaceProxies.isNotEmpty && effectiveVisitor != null) {
          final proxy = tryCreateInterfaceProxyWithVisitor<T>(
            e,
            effectiveVisitor,
          );
          if (proxy != null) return proxy;
        }
      }
      // INTER-003c: int→double element promotion in lists
      if (_isDoubleType<T>() && e is int) {
        return e.toDouble() as T;
      }
      // GEN-079: Generic wrapper resolution for list elements.
      // When T is a parameterized generic (e.g., TweenSequenceItem<double>)
      // and the element has the right base type but wrong type args
      // (e.g., TweenSequenceItem<dynamic>), use registered wrapper factories.
      final unwrappedElem = e is BridgedInstance ? e.nativeObject : e;
      final wrapped = _tryGenericWrapperResolution<T>(unwrappedElem);
      if (wrapped != null) return wrapped;
      return e as T;
    }).toList();
  } catch (e) {
    throw ArgumentD4rtException(
      'Invalid parameter "$paramName": cannot convert List to List<$T> - $e',
    );
  }
}