toBridgedClass method

BridgedClass toBridgedClass(
  1. Type nativeType
)

Implementation

BridgedClass toBridgedClass(Type nativeType) {
  // Resolution is split into TWO chain walks so that a PRECISE match in a
  // far (enclosing) frame always beats a FUZZY name-prefix match in a near
  // frame. Before this split the loop tried every strategy — including the
  // loose G-DCLI-05 `startsWith` prefix fallback — within a single frame and
  // returned on the first hit, so a nearer frame's fuzzy match short-circuited
  // a more-correct precise match still waiting in an enclosing frame.
  //
  // Concrete failure (lazy-bridge substrate, step #17/#19): a
  // `MappedListIterable<…>` returned by `List.map(...).toList()` has its
  // precise `nativeNames` entry on the stdlib `Iterable` bridge, which (under
  // the warm-parent / per-module split) lives in an *enclosing* frame. A
  // nearer module frame carries the `Map` bridge but not `Iterable`, and
  // `"MappedListIterable".startsWith("Map")` made the fuzzy fallback wrap it
  // as `Map` — so `.toList()` failed. Walking all frames for precise matches
  // first, then all frames for the fuzzy fallback, resolves it to `Iterable`.
  // Mirror of tom_d4rt/lib/src/environment.dart.
  final String nativeTypeNameFull = nativeType.toString();

  // PASS A — precise matching across the whole scope chain: exact Type
  // lookup, `_FooImpl → Foo` canonicalization, generic-base `name`/
  // `nativeNames` match, suffix match, name-exact, and longest-nativeName
  // prefix. Every strategy here is anchored on a declared bridge identity.
  Environment? current = this;
  while (current != null) {
    BridgedClass? bridgedClass =
        current._bridgedClassesLookupByType[nativeType];

    String nativeTypeName = nativeTypeNameFull;

    if (bridgedClass == null && (nativeTypeName.substring(0, 1) == '_')) {
      if (nativeTypeName.endsWith('Impl')) {
        nativeTypeName = nativeTypeName.substringBeforeLast('Impl');
      }
      // Cluster HASHSET FIX: when matching nativeNames by prefix, choose the
      // LONGEST matching nativeName so a more-specific bridge (e.g. Iterator
      // with `_HashSetIterator` in nativeNames) wins over a less-specific
      // bridge whose nativeName is a shorter prefix (e.g. Set's `_HashSet`).
      // Mirror of tom_d4rt/lib/src/environment.dart.
      final cleanedName = nativeTypeName.substring(1).substringBefore('<');
      bridgedClass = current._bridgedClassesLookupByType.entries
          .firstWhereOrNull((e) => e.value.name == cleanedName)
          ?.value;
      bridgedClass ??= _longestNativeNamePrefixMatch(current, nativeTypeName);
    } else if (bridgedClass == null && nativeTypeName.contains('<')) {
      // Extract the base type name before '<' for accurate matching.
      // Using contains() was too broad — e.g., 'ListMapView<int>'.contains('View<')
      // would falsely match the Flutter View widget bridge.
      final baseTypeName = nativeTypeName.substring(
        0,
        nativeTypeName.indexOf('<'),
      );
      bridgedClass = current._bridgedClassesLookupByType.entries
          .firstWhereOrNull(
            (e) =>
                baseTypeName == e.value.name ||
                (e.value.nativeNames?.contains(baseTypeName) ?? false),
          )
          ?.value;
    }
    bridgedClass ??= current._bridgedClassesLookupByType.entries
        .firstWhereOrNull((e) => e.value.name == nativeTypeName)
        ?.value;
    // Cluster HASHSET FIX: pick the LONGEST nativeName prefix so a
    // specific bridge wins over a less-specific bridge whose nativeName
    // is a shorter prefix.
    bridgedClass ??= _longestNativeNamePrefixMatch(current, nativeTypeName);

    if (bridgedClass != null) {
      return bridgedClass;
    }

    current = current._enclosing;
  }

  // PASS A2 — the SUFFIX fallback, across the whole chain, AFTER every
  // precise strategy has been tried in every frame.
  //
  // SCE162/SCF26: this used to run inside the loop above, so a FUZZY suffix
  // match in a NEARER frame beat a PRECISE `nativeNames` match in an
  // enclosing one. Under the lazy-bridge substrate the Flutter bridges sit in
  // the child frame and the stdlib bridges in the warm parent, so
  // `UnmodifiableSetView<String>` — named outright by the stdlib `Set`
  // bridge's `nativeNames` — resolved to Flutter's `View` WIDGET, because
  // `View` is the longest bridge name that is a suffix of
  // `UnmodifiableSetView` and it was reached one frame sooner. A script
  // passing `const <String>{'shiftLeft'}` to a `Set<String>` parameter was
  // then refused with `type 'View<String>' is not a subtype of type
  // 'Set<String>'`.
  //
  // THIS IS THE SAME LESSON AS THE PREFIX CASE, arriving at the strategy it
  // missed. `MappedListIterable → Map` was fixed by splitting resolution into
  // a precise chain walk and a fuzzy one; the suffix match is equally fuzzy —
  // it is anchored on the BRIDGE's name appearing inside the native type's,
  // not on any declared relationship — and it stayed frame-local, so
  // proximity kept beating precision for it alone.
  //
  // It can only move a resolution from a fuzzy answer to a precise one: every
  // candidate this walk can find was already reachable, just later.
  if (nativeTypeNameFull.contains('<')) {
    final suffixBase = nativeTypeNameFull.substring(
      0,
      nativeTypeNameFull.indexOf('<'),
    );
    current = this;
    while (current != null) {
      final match = _longestNameSuffixMatch(current, suffixBase);
      if (match != null) return match;
      current = current._enclosing;
    }
  }

  // PASS B — prefix fallback across the whole scope chain, CORROBORATED.
  //
  // SCD132. This used to match any registered bridge whose name was a
  // >=3-character prefix of the native type name, with nothing else asked.
  // Two of the three false positives this method's header documents are that
  // rule firing: `MappedListIterable` claimed by `Map`, and `TextDirection`
  // claimed by `Text` — and each was repaired by routing ONE caller around
  // PASS B rather than by narrowing it, so the next name-shaped coincidence
  // was always going to be claimed just as silently.
  //
  // A NAME PREFIX IS A COINCIDENCE, NOT A RELATIONSHIP. The prefix is still
  // how a candidate is FOUND — it is cheap and it is what the G-DCLI-05 case
  // (`ProgressBothImpl` → `Progress`) looks like — but the bridge must also
  // DECLARE the connection, one of two ways:
  //
  //   * `nativeNames` names this type, so the bridge says it speaks for it;
  //   * the supertype registry relates the two names, so a hierarchy
  //     somebody registered says they are related.
  //
  // `isAssignable` is the obvious third corroboration and is NOT reachable
  // here: it takes a VALUE and this method is given only a `Type`. Callers
  // that hold the value already consult it (GEN-075 in
  // `BridgedClass.isSubtypeOf`); this is the path that cannot.
  //
  // MEASURED BEFORE NARROWING. A probe on every PASS B match across both
  // trees' full suites fired 12 times: 11 for one test's deliberately
  // prefix-named proxy, and once for `TextDirection` → `Text`, the known
  // false positive. G-DCLI-05's own case never reached PASS B at all — PASS A
  // resolves it today. So the rule this narrows had no measured legitimate
  // user, and the test that did rely on it now declares `nativeNames`, which
  // is the realignment rather than a workaround.
  //
  // AND MEASURED AGAINST THE FLUTTER CORPUS (SCE149), which is where this
  // rule's heaviest use lives and which the unit suites cannot speak for:
  // the twins resolve the interpreter from pub.flutter-io.cn (DGUC6), so no corpus run
  // had ever exercised the narrowing. Both twins' base corpus, run serially
  // 2026-09-22 through SCD66's pre-publish path resolution at tom_d4rt
  // 1.175.0 / tom_d4rt_ast 0.157.0: **926 / 1 / 1 each**, against the
  // 927/1/0 hosted baseline, and the single failure is the same script in
  // both — scf26's `const <String>{}` resolving to Flutter's `View` widget,
  // which is the SUFFIX pass (`_longestNameSuffixMatch`) and not this one.
  //
  // THE EXPECTED FINDING DID NOT MATERIALISE, and that is the result. The
  // todo predicted Flutter bridges would need `nativeNames` entries once the
  // prefix coincidence stopped covering for them. NONE did: no corpus
  // resolution was reaching PASS B uncorroborated. The two false positives
  // this rule is infamous for (`MappedListIterable` → `Map`,
  // `TextDirection` → `Text`) were both found BY the corpus, so the corpus
  // was the right place to ask — it simply answered no.
  //
  // When nothing corroborates, falling through to the throw below is more
  // honest than returning a wrong bridge: every caller already handles it,
  // and a wrong bridge is a silently wrong dispatch.
  //
  // HOW MUCH MORE HONEST, MEASURED (SCE153). F-SCD133-4 in both Flutter
  // twins ablates the enum branch of `getRuntimeType` and asks what THIS
  // path alone would return for every bridged enum in the live Flutter
  // registry. Under the published interpreter 104 of 213 came back as a
  // DIFFERENT bridge on the AST line, and 82 of 151 on the source line.
  // Under the narrowed rule, measured 2026-09-22 via SCD66's pre-publish
  // path resolution: zero on both lines — all 213 and all 151 throw. The
  // wrong-answer outcome is not reduced by the narrowing, it is gone. That
  // is the sentence above, priced over a registry rather than argued.
  current = this;
  while (current != null) {
    final bridgedClass = current._bridgedClassesLookupByType.entries
        .firstWhereOrNull((e) {
          final bridgeName = e.value.name;
          // The bridge name must be a substantial prefix of the native type
          // name — and that is only the candidate test, not the verdict.
          if (bridgeName.length < 3 ||
              !nativeTypeNameFull.startsWith(bridgeName) ||
              nativeTypeNameFull.length <= bridgeName.length) {
            return false;
          }
          return _prefixMatchIsCorroborated(e.value, nativeTypeNameFull);
        })
        ?.value;
    if (bridgedClass != null) {
      Logger.debug(
        "[Environment] Matched native type '$nativeTypeNameFull' to bridge '${bridgedClass.name}' via corroborated prefix matching",
      );
      return bridgedClass;
    }
    current = current._enclosing;
  }

  throw RuntimeD4rtException(
    'Cannot bridge native object: No registered bridged class found for native type $nativeType.',
  );
}