-
Notifications
You must be signed in to change notification settings - Fork 197
Closed
Labels
Description
Labels
bugprotobufnull-safety
Description
The deepEquals() function in protobuf 5.0.0 crashes when comparing GeneratedMessage objects that contain map fields with different keys, throwing the error:
type 'Null' is not a subtype of type 'Object'
This is a regression from protobuf 4.x where such comparisons worked correctly.
Environment
- Package:
protobuf - Version:
5.0.0 - Dart SDK:
>=3.5.0 <4.0.0 - Platform: All platforms
Steps to Reproduce
- Create a protobuf message with a
map<K, V>field - Create two instances with maps that have the same size but different keys
- Compare the messages using
==operator
Minimal Example
import 'package:protobuf/protobuf.dart';
// Using a protobuf message with: map<uint32, bool> properties = 1;
void main() {
final msg1 = TestMessage()
..properties[1] = true
..properties[2] = false;
final msg2 = TestMessage()
..properties[1] = true
..properties[3] = true; // Different key: 3 instead of 2
// This crashes:
print(msg1 == msg2);
}Expected Behavior
The comparison should return false (maps are different) without crashing.
Actual Behavior
The code crashes with:
type 'Null' is not a subtype of type 'Object'
#0 areMapsEqual.<anonymous closure> (package:protobuf/src/protobuf/utils.dart:27:58)
#1 Iterable.every (dart:core/iterable.dart:446:16)
#2 areMapsEqual (package:protobuf/src/protobuf/utils.dart:27:19)
#3 deepEquals (package:protobuf/src/protobuf/utils.dart:13:44)
#4 FieldSet._equalFieldValues (package:protobuf/src/protobuf/field_set.dart:559:47)
#5 FieldSet._equals (package:protobuf/src/protobuf/field_set.dart:517:12)
#6 GeneratedMessage.== (package:protobuf/src/protobuf/generated_message.dart:133:51)
Root Cause
In protobuf 5.0.0, the deepEquals() signature changed from dynamic to non-nullable:
// protobuf 4.x
bool _deepEquals(lhs, rhs) { ... }
// protobuf 5.0.0
bool deepEquals(Object lhs, Object rhs) { ... } // Non-nullable!However, areMapsEqual() can pass null when accessing a key that doesn't exist:
bool areMapsEqual(Map lhs, Map rhs) {
if (lhs.length != rhs.length) return false;
return lhs.keys.every((key) => deepEquals(lhs[key], rhs[key]));
// ^^^^^^^^ ^^^^^^^^
// Can be null!
}Proposed Solution
Make deepEquals accept nullable parameters:
bool deepEquals(Object? lhs, Object? rhs) {
// Handle null cases first
if (lhs == null || rhs == null) return lhs == rhs;
// ... rest of the function unchanged
}Files to Modify
protobuf/lib/src/protobuf/utils.dart(line 8)
Impact
This affects:
- Any code comparing protobuf messages with
map<K, V>fields - Production applications using protobuf 5.0.0
- Migration from protobuf 4.x to 5.x