Hide PbList and PbMap constructors (#1072)
`PbList` and `PbMap` can't be constructed by the users properly, because the
constructor arguments are private:
- `PbList` element check function
- `PbMap` key and value field type arguments
Internally we had just one `PbMap` constructor call (with random field type
arguments) which we replaced with `{}`, and a few `PbList` constructor calls
(without a check function) which we replaced with `[]`.
To prevent confusion and incorrect uses of these types, hide the constructors.
Some of the `PbList` element validation tests are removed: they would need to
be moved to `protoc_plugin/test` as we no longer have access to `PbList`
constructors (but we can get `PbList`s from messages), but the tests are also
duplicates of the tests in `protoc_plugin/test/validate_fail_test.dart`. So
removed them instead.diff --git a/protobuf/CHANGELOG.md b/protobuf/CHANGELOG.md
index d2f12ba..64dc4c3 100644
--- a/protobuf/CHANGELOG.md
+++ b/protobuf/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 5.2.0
+## 6.0.0
* New `GeneratedMessage` extension methods `toTextFormat` and `writeTextFormat`
added to convert the message into the [official protocol buffers text
@@ -7,11 +7,19 @@
* Add [well-known proto types][wkts] as libraries. This change is required for
protoc_plugin-25.0.0. ([#1081])
+* **Breaking:** Hide `PbList` and `PbMap` constructors. It is not possible to
+ construct these values correctly in user code, so the constructors are now
+ private. Existing uses of `PbList` can be replaced by `List` and `PbMap` can
+ be replaced by `Map`.
+
+ For immutable lists and maps, you can use `built_value`. ([#1072])
+
[text format]: https://protobuf.dev/reference/protobuf/textformat-spec/
[#1080]: https://github.com/google/protobuf.dart/pull/1080
[#125]: https://github.com/google/protobuf.dart/issues/125
[wkts]: https://protobuf.dev/reference/protobuf/google.protobuf
[#1081]: https://github.com/google/protobuf.dart/pull/1081
+[#1072]: https://github.com/google/protobuf.dart/pull/1072
## 5.1.0
diff --git a/protobuf/lib/src/protobuf/field_info.dart b/protobuf/lib/src/protobuf/field_info.dart
index 775b3c0..59c709f 100644
--- a/protobuf/lib/src/protobuf/field_info.dart
+++ b/protobuf/lib/src/protobuf/field_info.dart
@@ -144,7 +144,7 @@
this.enumValues,
this.defaultEnumValue,
String? protoName,
- }) : makeDefault = (() => PbList<T>(check: check)),
+ }) : makeDefault = (() => newPbList<T>(check: check)),
_protoName = protoName,
assert(PbFieldType.isRepeated(type)),
assert(!PbFieldType.isEnum(type) || valueOf != null);
@@ -169,7 +169,7 @@
/// [GeneratedMessage.getField], doesn't create a repeated field.
dynamic get readonlyDefault {
if (isRepeated) {
- return _emptyList ??= PbList.unmodifiable();
+ return _emptyList ??= newUnmodifiablePbList();
}
return makeDefault!();
}
@@ -232,13 +232,13 @@
/// Creates a repeated field.
PbList<T> _createRepeatedField() {
assert(isRepeated);
- return PbList<T>(check: check);
+ return newPbList<T>(check: check);
}
/// Same as above, but allow a tighter typed [PbList] to be created.
PbList<S> _createRepeatedFieldWithType<S extends T>() {
assert(isRepeated);
- return PbList<S>(check: check);
+ return newPbList<S>(check: check);
}
/// Convenience method to thread this FieldInfo's reified type parameter to
@@ -303,7 +303,7 @@
tagNumber,
index,
type,
- defaultOrMaker: () => PbMap<K, V>(keyFieldType, valueFieldType),
+ defaultOrMaker: () => newPbMap<K, V>(keyFieldType, valueFieldType),
defaultEnumValue: defaultEnumValue,
protoName: protoName,
) {
@@ -319,7 +319,7 @@
PbMap<K, V> _createMapField() {
assert(isMapField);
- return PbMap<K, V>(keyFieldType, valueFieldType);
+ return newPbMap<K, V>(keyFieldType, valueFieldType);
}
}
diff --git a/protobuf/lib/src/protobuf/field_set.dart b/protobuf/lib/src/protobuf/field_set.dart
index 6677da0..89bae3d 100644
--- a/protobuf/lib/src/protobuf/field_set.dart
+++ b/protobuf/lib/src/protobuf/field_set.dart
@@ -402,7 +402,7 @@
assert(fi.isMapField);
if (_isReadOnly) {
- return PbMap<K, V>.unmodifiable(fi.keyFieldType, fi.valueFieldType);
+ return newUnmodifiablePbMap<K, V>(fi.keyFieldType, fi.valueFieldType);
}
final map = fi._createMapField();
diff --git a/protobuf/lib/src/protobuf/pb_list.dart b/protobuf/lib/src/protobuf/pb_list.dart
index eed80e2..137ad55 100644
--- a/protobuf/lib/src/protobuf/pb_list.dart
+++ b/protobuf/lib/src/protobuf/pb_list.dart
@@ -13,6 +13,17 @@
/// Throws [ArgumentError] or [RangeError] when the item is not valid.
typedef CheckFunc<E> = void Function(E? x);
+@pragma('dart2js:tryInline')
+@pragma('vm:prefer-inline')
+@pragma('wasm:prefer-inline')
+PbList<E> newPbList<E>({CheckFunc<E>? check}) => PbList._(check: check);
+
+@pragma('dart2js:tryInline')
+@pragma('vm:prefer-inline')
+@pragma('wasm:prefer-inline')
+PbList<E> newUnmodifiablePbList<E>({CheckFunc<E>? check}) =>
+ PbList._unmodifiable();
+
/// A [ListBase] implementation used for protobuf `repeated` fields.
class PbList<E> extends ListBase<E> {
/// The actual list storing the elements.
@@ -35,17 +46,13 @@
bool get isFrozen => _isReadOnly;
- PbList({CheckFunc<E>? check}) : _wrappedList = <E>[], _check = check;
+ PbList._({CheckFunc<E>? check}) : _wrappedList = <E>[], _check = check;
- PbList.unmodifiable()
+ PbList._unmodifiable()
: _wrappedList = _emptyList,
_check = checkNotNull,
_isReadOnly = true;
- PbList.from(Iterable<E> from)
- : _wrappedList = List<E>.of(from),
- _check = checkNotNull;
-
@override
@pragma('dart2js:never-inline')
void add(E element) {
@@ -288,7 +295,7 @@
}
PbList<E> _deepCopy() {
- final newList = PbList<E>(check: _check);
+ final newList = PbList<E>._(check: _check);
final wrappedList = _wrappedList;
final newWrappedList = newList._wrappedList;
if (wrappedList.isNotEmpty) {
diff --git a/protobuf/lib/src/protobuf/pb_map.dart b/protobuf/lib/src/protobuf/pb_map.dart
index 0051333..0200f4a 100644
--- a/protobuf/lib/src/protobuf/pb_map.dart
+++ b/protobuf/lib/src/protobuf/pb_map.dart
@@ -10,6 +10,18 @@
const mapKeyFieldNumber = 1;
const mapValueFieldNumber = 2;
+@pragma('dart2js:tryInline')
+@pragma('vm:prefer-inline')
+@pragma('wasm:prefer-inline')
+PbMap<K, V> newPbMap<K, V>(int keyFieldType, int valueFieldType) =>
+ PbMap<K, V>._(keyFieldType, valueFieldType);
+
+@pragma('dart2js:tryInline')
+@pragma('vm:prefer-inline')
+@pragma('wasm:prefer-inline')
+PbMap<K, V> newUnmodifiablePbMap<K, V>(int keyFieldType, int valueFieldType) =>
+ PbMap<K, V>._unmodifiable(keyFieldType, valueFieldType);
+
/// A [MapBase] implementation used for protobuf `map` fields.
class PbMap<K, V> extends MapBase<K, V> {
/// Key type of the map. Per proto2 and proto3 specs, this needs to be an
@@ -34,9 +46,9 @@
bool _isReadOnly = false;
- PbMap(this.keyFieldType, this.valueFieldType) : _wrappedMap = <K, V>{};
+ PbMap._(this.keyFieldType, this.valueFieldType) : _wrappedMap = <K, V>{};
- PbMap.unmodifiable(this.keyFieldType, this.valueFieldType)
+ PbMap._unmodifiable(this.keyFieldType, this.valueFieldType)
: _wrappedMap = <K, V>{},
_isReadOnly = true;
@@ -114,7 +126,7 @@
}
PbMap<K, V> _deepCopy() {
- final newMap = PbMap<K, V>(keyFieldType, valueFieldType);
+ final newMap = PbMap<K, V>._(keyFieldType, valueFieldType);
final wrappedMap = _wrappedMap;
final newWrappedMap = newMap._wrappedMap;
if (PbFieldType.isGroupOrMessage(valueFieldType)) {
diff --git a/protobuf/pubspec.yaml b/protobuf/pubspec.yaml
index e508c44..b2fa5d5 100644
--- a/protobuf/pubspec.yaml
+++ b/protobuf/pubspec.yaml
@@ -1,5 +1,5 @@
name: protobuf
-version: 5.2.0
+version: 6.0.0
description: >-
Runtime library for protocol buffers support. Use with package:protoc_plugin
to generate Dart code for your '.proto' files.
diff --git a/protobuf/test/list_test.dart b/protobuf/test/list_test.dart
index 23e9951..ea95cc8 100644
--- a/protobuf/test/list_test.dart
+++ b/protobuf/test/list_test.dart
@@ -2,22 +2,13 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:fixnum/fixnum.dart';
-import 'package:protobuf/protobuf.dart';
import 'package:test/test.dart';
-// [ArgumentError] in production mode, [TypeError] in checked.
-final invalidArgumentException = predicate(
- (e) => e is ArgumentError || e is TypeError,
-);
-final badArgument = throwsA(invalidArgumentException);
-
-// Suppress an analyzer warning for a deliberate type mismatch.
-T cast<T>(Object? x) => x as T;
+import 'mock_util.dart';
void main() {
test('testPbList handles basic operations', () {
- final lb1 = PbList<int>();
+ final lb1 = T().int32s;
expect(lb1, []);
lb1.add(1);
@@ -63,7 +54,7 @@
});
test('PbList handles range operations', () {
- final lb2 = PbList<int>();
+ final lb2 = T().int32s;
lb2.addAll([1, 2, 3, 4, 5, 6, 7, 8, 9]);
expect(lb2.sublist(3, 7), [4, 5, 6, 7]);
@@ -89,168 +80,4 @@
lb2.setRange(5, 6, [88, 99].take(2), 1);
expect(lb2, [1, 2, 3, 9, 8, 99]);
});
-
- test('PbList validates items', () {
- expect(() {
- // ignore: unnecessary_cast
- (PbList<int>() as List).add('hello');
- }, throwsA(TypeMatcher<TypeError>()));
- });
-
- test('PbList for signed int32 validates items', () {
- final List<int> list = PbList(check: getCheckFunction(PbFieldType.P3));
-
- expect(() {
- list.add(-2147483649);
- }, throwsArgumentError);
-
- expect(
- () {
- list.add(-2147483648);
- },
- returnsNormally,
- reason: 'could not add min signed int32 to a PbList',
- );
-
- expect(() {
- list.add(2147483648);
- }, throwsArgumentError);
-
- expect(
- () {
- list.add(2147483647);
- },
- returnsNormally,
- reason: 'could not add max signed int32 to a PbList',
- );
- });
-
- test('PBList for unsigned int32 validates items', () {
- final List<int> list = PbList(check: getCheckFunction(PbFieldType.PU3));
-
- expect(() {
- list.add(-1);
- }, throwsArgumentError);
-
- expect(
- () {
- list.add(0);
- },
- returnsNormally,
- reason: 'could not add zero to a PbList',
- );
-
- expect(() {
- list.add(4294967296);
- }, throwsArgumentError);
-
- expect(
- () {
- list.add(4294967295);
- },
- returnsNormally,
- reason: 'could not add max unsigned int32 to a PbList',
- );
- });
-
- test('PbList for float validates items', () {
- final List<double> list = PbList(check: getCheckFunction(PbFieldType.PF));
-
- expect(() {
- list.add(3.4028234663852886E39);
- }, throwsArgumentError);
-
- expect(() {
- list.add(-3.4028234663852886E39);
- }, throwsArgumentError);
-
- expect(
- () {
- list.add(3.4028234663852886E38);
- },
- returnsNormally,
- reason: 'could not add max float to a PbList',
- );
-
- expect(
- () {
- list.add(-3.4028234663852886E38);
- },
- returnsNormally,
- reason: 'could not add min float to a PbList',
- );
- });
-
- test('PbList for signed Int64 validates items', () {
- final List<Int64> list = PbList();
- expect(() {
- list.add(cast(0)); // not an Int64
- }, badArgument);
-
- expect(
- () {
- list.add(Int64(0));
- },
- returnsNormally,
- reason: 'could not add Int64(0) to a PbList',
- );
-
- expect(
- () {
- list.add(Int64.MAX_VALUE);
- },
- returnsNormally,
- reason: 'could not add max Int64 to a PbList',
- );
-
- expect(
- () {
- list.add(Int64.MIN_VALUE);
- },
- returnsNormally,
- reason: 'could not add min Int64 to PbList',
- );
- });
-
- test('PbList for unsigned Int64 validates items', () {
- final List<Int64> list = PbList();
- expect(() {
- list.add(cast(0)); // not an Int64
- }, badArgument);
-
- expect(
- () {
- list.add(Int64(0));
- },
- returnsNormally,
- reason: 'could not add Int64(0) to a PbList',
- );
-
- // Adding -1 should work because we are storing the bits as-is.
- // (It will be interpreted as a positive number.)
- // See: https://github.com/google/protobuf.dart/issues/44
- expect(
- () {
- list.add(Int64(-1));
- },
- returnsNormally,
- reason: 'could not add Int64(-1) to a PbList',
- );
-
- expect(
- () {
- list.add(Int64.MAX_VALUE);
- },
- returnsNormally,
- reason: 'could not add max Int64 to a PbList',
- );
-
- expect(
- () {
- list.add(Int64.MIN_VALUE);
- },
- returnsNormally,
- reason: 'could not add min Int64 to a PbList',
- );
- });
}
diff --git a/protoc_plugin/pubspec.yaml b/protoc_plugin/pubspec.yaml
index 6e55b50..662d385 100644
--- a/protoc_plugin/pubspec.yaml
+++ b/protoc_plugin/pubspec.yaml
@@ -14,7 +14,7 @@
dart_style: ^3.0.0
fixnum: ^1.0.0
path: ^1.8.0
- protobuf: ^5.2.0
+ protobuf: ^6.0.0
pub_semver: ^2.2.0
dev_dependencies: