Avoid runtime function type check in lazily created singleton creator functions (#574)
diff --git a/protobuf/lib/src/protobuf/generated_message.dart b/protobuf/lib/src/protobuf/generated_message.dart
index 60d40d1..1c73597 100644
--- a/protobuf/lib/src/protobuf/generated_message.dart
+++ b/protobuf/lib/src/protobuf/generated_message.dart
@@ -506,27 +506,42 @@
// Support for generating a read-only default singleton instance.
- static final Map<Function?, Function> _defaultMakers = {};
+ static final Map<Function?, _SingletonMaker<GeneratedMessage>>
+ _defaultMakers = {};
static T Function() _defaultMakerFor<T extends GeneratedMessage>(
- T Function()? createFn) {
- return (_defaultMakers[createFn] ??= _createDefaultMakerFor<T>(createFn!))
- as T Function();
- }
-
- static T Function() _createDefaultMakerFor<T extends GeneratedMessage>(
- T Function() createFn) {
- T? defaultValue;
- T defaultMaker() {
- return defaultValue ??= createFn()..freeze();
- }
-
- return defaultMaker;
- }
+ T Function()? createFn) =>
+ _getSingletonMaker(createFn!)._frozenSingletonCreator;
/// For generated code only.
static T $_defaultFor<T extends GeneratedMessage>(T Function() createFn) =>
- _defaultMakerFor<T>(createFn)();
+ _getSingletonMaker(createFn)._frozenSingleton;
+
+ static _SingletonMaker<T> _getSingletonMaker<T extends GeneratedMessage>(
+ T Function() fun) {
+ final oldMaker = _defaultMakers[fun];
+ if (oldMaker != null) {
+ // The CFE will insert an implicit downcast to `_SingletonMaker<T>`. We
+ // avoid making that explicit because implicit downcasts are avoided by
+ // dart2js in production code.
+ return oldMaker as dynamic;
+ }
+ return _defaultMakers[fun] = _SingletonMaker<T>(fun);
+ }
+}
+
+// We use a class that creates singletones instead of a closure function. We do
+// so because the result of the lookup in [_defaultMakers] has to be downcasted.
+// A downcast to a generic interface type is much easier to perform at runtime
+// than a downcast to a generic function type.
+class _SingletonMaker<T extends GeneratedMessage> {
+ final T Function() _creator;
+
+ _SingletonMaker(this._creator);
+
+ late final T _frozenSingleton = _creator()..freeze();
+ // ignore: prefer_function_declarations_over_variables
+ late final T Function() _frozenSingletonCreator = () => _frozenSingleton;
}
/// The package name of a protobuf message.