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.