toBridgedInstance method

BridgedInstance<Object>? toBridgedInstance(
  1. Object? nativeObject
)

Converts a native object to a bridged instance if a bridge exists.

nativeObject The native object to convert.

Returns a BridgedInstance if a bridge is found for the object's type, otherwise throws a D4rtException (caller should handle).

Resolution order:

  1. Direct lookup by Type (most specific — bridges with explicit BridgedClass.nativeType).
  2. BridgedClass.isAssignable iteration — keeps the LAST match across all enclosing environments. Bridges register general→specific, so the last match is the most specific (e.g., CupertinoTextThemeData wins over Diagnosticable). Required to disambiguate native types whose name happens to be a prefix of another bridge's name — e.g., StringCharacters implements Characters: without this step the name-prefix fallback in toBridgedClass would wrap it as String. Bucket #11 fix.
  3. toBridgedClass name-based fallbacks (private impl types, generic suffix matching, *Impl prefix matching). Used only when neither direct type lookup nor isAssignable found a bridge.

Implementation

BridgedInstance? toBridgedInstance(Object? nativeObject) {
  if (nativeObject == null) {
    return null;
  }
  final runtimeType = nativeObject.runtimeType;

  // 1) Direct type lookup.
  Environment? current = this;
  while (current != null) {
    final direct = current._bridgedClassesLookupByType[runtimeType];
    if (direct != null) {
      return BridgedInstance(direct, nativeObject);
    }
    current = current._enclosing;
  }

  // 1b) Resolution cache. GEN-115 Phase 2: a previous call already paid
  //     the full step-2 / step-3 cost for this runtimeType; reuse it.
  //     Walks the env chain so a hit in any enclosing scope short-circuits.
  current = this;
  while (current != null) {
    final cached = current._resolvedTypeCache[runtimeType];
    if (cached != null) {
      return BridgedInstance(cached, nativeObject);
    }
    // T4 (perf, F3): negative-cache hit — a previous call already proved this
    // runtimeType has no bridge. Re-throw the same miss without re-walking the
    // chain, iterating every bridge, or running the name-fallback toString.
    if (current._unbridgedTypeCache.contains(runtimeType)) {
      throw RuntimeD4rtException(
          'Cannot bridge native object: No registered bridged class found '
          'for native type $runtimeType.');
    }
    current = current._enclosing;
  }

  // 2) isAssignable iteration. Bridges may register in any order, so we
  //    collect ALL matches and then drop those that are supertypes of
  //    another match using [BridgedClass.transitiveSupertypeNames]. The
  //    remaining set is "leaf" matches; we pick the last one (preserves
  //    legacy LAST-wins behaviour when the registry doesn't disambiguate).
  //
  //    D2 fix: A native object whose runtimeType is a private impl of
  //    BoxConstraints (e.g. `_BodyBoxConstraints`) was wrapped as
  //    `Constraints` because the LAST-match-wins iteration picked the
  //    abstract base. With `BoxConstraints: [Constraints, ...]` registered
  //    in the supertype registry, the filter drops `Constraints` and
  //    keeps `BoxConstraints`, so `.maxWidth` resolves correctly.
  final allMatches = <BridgedClass>[];
  current = this;
  while (current != null) {
    for (final entry in current._bridgedClassesLookupByType.entries) {
      final bridge = entry.value;
      if (bridge.isAssignable != null && bridge.isAssignable!(nativeObject)) {
        allMatches.add(bridge);
      }
    }
    current = current._enclosing;
  }
  if (allMatches.isNotEmpty) {
    final filtered = _filterToMostSpecific(allMatches);
    final picked = filtered.isNotEmpty ? filtered.last : allMatches.last;
    _resolvedTypeCacheOrNew[runtimeType] = picked;
    return BridgedInstance(picked, nativeObject);
  }

  // 3) Name-based fallbacks (private impl, generic suffix, *Impl prefix).
  //    [toBridgedClass] will throw if no bridge matches — propagate, but
  //    negative-cache the miss first so repeats short-circuit (T4, F3).
  final BridgedClass bridgedClass;
  try {
    bridgedClass = toBridgedClass(runtimeType);
  } on RuntimeD4rtException {
    _unbridgedTypeCacheOrNew.add(runtimeType);
    rethrow;
  }
  _resolvedTypeCacheOrNew[runtimeType] = bridgedClass;
  return BridgedInstance(bridgedClass, nativeObject);
}