operator == method
SCC32: value equality, delegated to the wrapped native.
Without this the wrapper compared by identity, so two separately
constructed wrappers around equal natives were different keys. The
interpreted a == b expression looked right only because
visitBinaryExpression unwraps both operands before comparing — the
wrapper itself was never consulted. So a script got true from == and a
miss from every hash-based collection, with no error either way. List
membership was wrong too ([Duration(seconds: 1)].contains(Duration( seconds: 1)) was false), which no amount of hashing would explain and
which is why hashCode alone was not the fix.
Equality with the raw native is deliberate, and it is the same choice BridgedEnumValue already made for the same reason.
SCD98 removed the inconsistency this was written to survive, and this
operator stays anyway. When this comment was written a constructor call
yielded a wrapper while every bridged method return yielded a bare
native, so DateTime(2021).difference(x) and Duration(seconds: 1) were
the same value in two representations that routinely met in one
collection. The constructor now yields the native too, so nothing the
interpreter PRODUCES is a wrapper and that particular collision cannot
arise. What remains is the boundary this type exists for:
Environment.toBridgedInstance still hands a wrapper to bridge dispatch,
and an embedder can put one into a collection itself. So the operator is
no longer a workaround for an internal inconsistency — it is what it
reads as, a courtesy to a wrapper that arrives from outside.
This is asymmetric, which is normally a hazard — raw == wrapper stays
false because a native's == rejects a foreign type, and nothing here can
change that. It is safe because Dart's hash lookup calls
lookupKey == storedKey, making the lookup key the receiver: whenever the
wrapper is the value being looked up, this operator runs and the
comparison succeeds regardless of how the key was stored. The opposite
direction — a raw native looking up a stored wrapper — would fall on the
unfixable side, which is why hash keys are additionally normalized to the
native at storage time (see _unwrapHashKey in the interpreter). Between
the two, no stored key is ever a wrapper and every lookup resolves.
Implementation
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other is BridgedInstance) {
return nativeObject == other.nativeObject;
}
return nativeObject == other;
}