dart_mappable icon indicating copy to clipboard operation
dart_mappable copied to clipboard

profile mode serialization error

Open chirastefan opened this issue 1 year ago • 8 comments

I get this error only in profile and release mode and only for physical device(iPhone 14 Pro Max). I tried to reproduce my setup in another project, but I cannot reproduce it in that repo. The only thing I get is this stack trace:

flutter: ResolvedType _resolvedTypes[type]
flutter: null
flutter: ResolvedType info
flutter: Map<String, Player>
flutter: ResolvedType match
flutter: Instance of 'TypeMatch'
flutter: TypeMatch resolve args
flutter: [Instance of 'TypeMatch', Instance of 'TypeMatch']
flutter: TypeMatch resolve args
flutter: []
flutter: o
flutter: ()
flutter: b
flutter: Closure: (dynamic) => dynamic
flutter: ResolvedType factory
flutter: Closure: (dynamic) => dynamic
flutter: ResolvedType args
flutter: []
flutter: TypeMatch resolve args
flutter: []
flutter: o
flutter: ()
flutter: b
flutter: Closure: (dynamic) => dynamic
flutter: ResolvedType factory
flutter: Closure: (dynamic) => dynamic
flutter: ResolvedType args
flutter: []
flutter: o
flutter: (ResolvedType{base: String, args: []}, ResolvedType{base: Player, args: []})
flutter: b
flutter: Closure: (dynamic) => dynamic
flutter: ResolvedType factory
flutter: Closure: (dynamic) => dynamic
flutter: ResolvedType args
flutter: [ResolvedType{base: String, args: []}, ResolvedType{base: Player, args: []}]
flutter: e
flutter: MapperException: Failed to encode (_Map<String, Player>): NoSuchMethodError: Closure call with mismatched arguments: function 'TypePlus.add.<anonymous closure>'
Receiver: Closure: (dynamic) => dynamic
Tried calling: TypePlus.add.<anonymous closure><String, Player>(Closure: <Y0>() => (<F1Y0>() => dynamic) => dynamic)
Found: TypePlus.add.<anonymous closure>(dynamic) => dynamic
flutter: stacktrace
flutter: #0      Object.noSuchMethod (dart:core-patch/object_patch.dart:38)
#1      _objectNoSuchMethod (dart:core-patch/object_patch.dart:88)
#2      TypeSwitcher.apply.<anonymous closure>.<anonymous closure> (package:type_plus/src/type_switcher.dart:137)
#3      TypeSwitcher.apply.$params (package:type_plus/src/type_switcher.dart:93)
#4      TypeSwitcher.apply.<anonymous closure> (package:type_plus/src/type_switcher.dart:135)
#5      TypeSwitcher.apply.$args.<anonymous closure>.<anonymous closure> (package:type_plus/src/type_switcher.dart:60)
#6      MapperBase.typeFactory.<anonymous closure> (package:dart_mappable/src/mappers/mapper_base.dart:22)
#7      TypeSwitcher.apply.<anonymous closure>.<anonymous closure> (package:type_plus/src/type_switcher.dart:111)
#8      TypeSwitcher.apply.$params (package:type_plus/src/type_switcher.dart:93)
#9      TypeSwitcher.apply.<anonymous closure> (package:type_plus/src/type_switcher.dart:109)
#10     TypeSwitcher.apply.$args (package:type_plus/src/type_switcher.dart:58)
#11     TypeSwitcher.apply (package:type_plus/src/type_switcher.dart:108)
#12     TypeSwitcher.apply.$args.call (package:type_plus/src/type_switcher.dart:49)
#13     TypeSwitcher.apply.$args.<anonymous closure> (package:type_plus/src/type_switcher.dart:60)
#14     _sdkTypes.<anonymous closure> (package:type_plus/src/types_registry.dart:24)
#15     TypeSwitcher.apply.<anonymous closure>.<anonymous closure> (package:type_plus/src/type_switcher.dart:111)
#16     TypeSwitcher.apply.$params (package:type_plus/src/type_switcher.dart:93)
#17     TypeSwitcher.apply.<anonymous closure> (package:type_plus/src/type_switcher.dart:109)
#18     TypeSwitcher.apply.$args (package:type_plus/src/type_switcher.dart:58)
#19     TypeSwitcher.apply (package:type_plus/src/type_switcher.dart:108)
#20     TypeSwitcher.apply.$args.call (package:type_plus/src/type_switcher.dart:49)
#21     TypeSwitcher.apply.$args (package:type_plus/src/type_switcher.dart:60)
#22     TypeSwitcher.apply (package:type_plus/src/type_switcher.dart:108)
#23     new ResolvedType (package:type_plus/src/resolved_type.dart:54)
#24     TypeMatch.resolve (package:type_plus/src/resolved_type.dart:29)
#25     _SyncStarIterator.moveNext (dart:async-patch/async_patch.dart:595)
#26     WhereIterator.moveNext (dart:_internal/iterable.dart:467)
#27     IterableExtensions.firstOrNull (dart:collection/iterable.dart:43)
#28     ResolvedType.from (package:type_plus/src/resolved_type.dart:116)
#29     TypePlus._resolved (package:type_plus/src/type_plus.dart:10)
#30     TypePlus.args (package:type_plus/src/type_plus.dart:16)
#31     MapperBase.encodeValue.<anonymous closure> (package:dart_mappable/src/mappers/mapper_base.dart:77)
#32     MappingContext.args (package:dart_mappable/src/mappers/mapping_context.dart:13)
#33     _MapEncoder.encode (package:dart_mappable/src/mappers/map_mapper.dart)
#34     MapMapper.encoder (package:dart_mappable/src/mappers/map_mapper.dart:34)
#35     MapperBase.encodeValue (package:dart_mappable/src/mappers/mapper_base.dart:67)
#36     _MapperContainerBase.toValue (package:dart_mappable/src/mapper_container.dart:405)
#37     EncodingUtil.$enc (package:dart_mappable/src/mapper_utils.dart:55)
#38     Field.encode (package:dart_mappable/src/mappers/interface_mapper.dart:70)
#39     InterfaceMapperBase.encodeFields (package:dart_mappable/src/mappers/interface_mapper.dart:200)
#40     ClassMapperBase.encode (package:dart_mappable/src/mappers/class_mapper.dart:206)
#41     InterfaceMapperBase.encoder (package:dart_mappable/src/mappers/interface_mapper.dart:171)
#42     ClassMapperBase.encoder (package:dart_mappable/src/mappers/class_mapper.dart:194)
#43     MapperBase.encodeValue (package:dart_mappable/src/mappers/mapper_base.dart:67)
#44     ClassMapperBase.encodeValue (package:dart_mappable/src/mappers/class_mapper.dart:113)
#45     InterfaceMapperBase.encodeMap (package:dart_mappable/src/mappers/interface_mapper.dart:211)
#46     ReduxStateMappable.toJson (package:my_app/models/redux_state/redux_state.mapper.dart:181)
#47     _defaultToEncodable (dart:convert/json.dart:658)
#48     _JsonStringifier.writeObject (dart:convert/json.dart:818)
#49     _JsonStringStringifier.printOn (dart:convert/json.dart:1024)
#50     _JsonStringStringifier.stringify (dart:convert/json.dart:1005)
#51     JsonEncoder.convert (dart:convert/json.dart:353)
#52     JsonCodec.encode (dart:convert/json.dart:238)
#53     JsonSerializer.encode (package:redux_persist/src/serialization.dart:30)
#54     Persistor.save (package:redux_persist/src/persistor.dart:156)
#55     Persistor.createMiddleware.<anonymous closure>.<anonymous closure> (package:redux_persist/src/persistor.dart:66)
#56     Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18)
#57     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:410)
#58     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:441)
#59     _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:194)
flutter: Persistor debug: Error while saving: Converting object to an encodable object failed: Instance of 'ReduxState'
[ERROR:flutter/runtime/dart_vm_initializer.cc(40)] Unhandled Exception: Converting object to an encodable object failed: Instance of 'ReduxState'
#0      _JsonStringifier.writeObject (dart:convert/json.dart:824)
#1      _JsonStringStringifier.printOn (dart:convert/json.dart:1024)
#2      _JsonStringStringifier.stringify (dart:convert/json.dart:1005)
#3      JsonEncoder.convert (dart:convert/json.dart:353)
#4      JsonCodec.encode (dart:convert/json.dart:238)
#5      JsonSerializer.encode (package:redux_persist/src/serialization.dart:30)
#6      Persistor.save (package:redux_persist/src/persistor.dart:156)
#7      Persistor.createMiddleware.<anonymous closure>.<anonymous closure> (package:redux_persist/src/persistor.dart:66)

Modified files:

.pub-cache/hosted/pub.dev/redux_persist-0.9.0/lib/src/serialization.dart

class JsonSerializer<T> implements StateSerializer<T> {
  /// Turns the dynamic [json] (can be null) to [T]
  final T? Function(dynamic? json) decoder;

  JsonSerializer(this.decoder);

  @override
  T? decode(Uint8List? data) {
    return decoder(data != null ? json.decode(uint8ListToString(data)!) : null);
  }

  @override
  Uint8List? encode(T state) {
    print('serialization encode state');
    // print(state);
    if (state == null) {
      return null;
    }

    return stringToUint8List(json.encode(state));
  }
}

flutter/bin/cache/dart-sdk/lib/convert/json.dart

  String encode(Object? value, {Object? toEncodable(dynamic object)?}) {
    toEncodable ??= _toEncodable;
    print('dart-sdk encode _toEncodable != null');
    print(_toEncodable != null);
    if (toEncodable == null) return encoder.convert(value);
    return JsonEncoder(toEncodable).convert(value);
  }
...
  void writeObject(Object? object) {
    // Tries stringifying object directly. If it's not a simple value, List or
    // Map, call toJson() to get a custom representation and try serializing
    // that.
    if (writeJsonValue(object)) return;
    _checkCycle(object);
    try {
      var customJson = _toEncodable(object);
      print('writeObject customJson');
      // print(customJson);
      if (!writeJsonValue(customJson)) {
        print('if(!writeJsonValue(customJson))');
        throw JsonUnsupportedObjectError(object, partialResult: _partialResult);
      }
      _removeSeen(object);
    } catch (e) {
      print(e);
      throw JsonUnsupportedObjectError(
        object,
        cause: e,
        partialResult: _partialResult,
      );
    }
  }

.pub-cache/hosted/pub.dev/dart_mappable-4.4.0/lib/src/mappers/mapper_base.dart

  Object? encodeValue<V>(V value,
      [EncodingOptions? options, MapperContainer? container]) {
    try {
      var includeTypeId = options?.includeTypeId;
      includeTypeId ??= this.includeTypeId<V>(value);

      print('encodeValue this');
      print(this);
      var result = this.encoder(
        value as T,
        EncodingContext(
          container: container,
          options: options?.inheritOptions ?? false ? options : null,
          args: () {
            Type type = V;
            print('MapperBase encodeValue type');
            print(type);
            print('MapperBase encodeValue type.args');
            print(type.args);
            if (includeTypeId ?? false) {
              type = value.runtimeType;
              print('if type');
              print(type);
            }

            var typeArgs =
                type.args.map((t) => t == UnresolvedType ? dynamic : t);
            print('MapperBase typeArgs');
            print(typeArgs);
            var fallback = this.type.base.args;
            if (typeArgs.length != fallback.length) {
              typeArgs = fallback;
            }
            return typeArgs.toList();
          },
        ),
      );

      if (includeTypeId && result is Map<String, dynamic>) {
        result[MapperContainer.typeIdKey] = value.runtimeType.id;
      }

      return result;
    } catch (e, stacktrace) {
      Error.throwWithStackTrace(
        MapperException.chain(MapperMethod.encode, '(${value.runtimeType})', e),
        stacktrace,
      );
    }
  }

.pub-cache/hosted/pub.dev/type_plus-2.1.1/lib/src/resolved_type.dart

  static ResolvedType from<T>([Type? t]) {
    var type = t ?? T;
    print('ResolvedType type');
    print(type);
    print('ResolvedType _resolvedTypes');
    print(_resolvedTypes);
    print('ResolvedType _resolvedTypes[type]');
    print(_resolvedTypes[type]);

    if (_resolvedTypes[type] != null) {
      return _resolvedTypes[type]!;
    }

    var info = TypeInfo.fromType(type);
    print('ResolvedType info');
    print(info);
    var match = TypeMatch.fromInfo(info);
    print('ResolvedType match');
    print(match);

    var resolved = match.resolve().where((o) => o.reversed == type).firstOrNull;
    print('ResolvedType resolved');
    print(resolved);
    return resolved ?? ResolvedType.unresolved(info);
  }

The class in question:

@immutable
@MappableClass()
class ReduxState with ReduxStateMappable {
  final Map<String, Player> players;

  const ReduxState({
    required this.players,
  }) : super();

  factory ReduxState.initial() {
    return ReduxState(
      players: const <String, Player>{},
    );
  }

  static ReduxState? fromJsonDecoder(dynamic json) {
    if (json == null) return null;
    return ReduxStateMapper.fromJson(json as Map<String, dynamic>);
  }
}

chirastefan avatar Mar 07 '25 21:03 chirastefan

What version of type_plus are you using? Did you modify the package in any way? I wonder where the first lines of print outs come from.

schultek avatar Mar 08 '25 09:03 schultek

I modified it. I added a bunch of logs(print()) inside redux_persist, dart_mappable, type_plus, dart_sdk, sky_engine. Basicaly along the stack trace. I have type_plus 2.1.1 and dart_mappable 4.4.0. Flutter doctor output. I added more code in the initial comment.

[✓] Flutter (Channel stable, 3.29.0, on macOS 15.3.1 24D70 darwin-arm64, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2024.2)
[✓] IntelliJ IDEA Ultimate Edition (version 2024.3.4.1)
[✓] VS Code (version 1.97.2)
[✓] Connected device (5 available)
[✓] Network resources

• No issues found!

chirastefan avatar Mar 09 '25 19:03 chirastefan

Can you run it with the --enable-asserts flag? That should give a more descriptive error.

schultek avatar Mar 10 '25 07:03 schultek

flutter run build --profile --enable-asserts

Could not find an option named "--enable-asserts".

chirastefan avatar Mar 10 '25 18:03 chirastefan

True that only exists for dart run, not flutter.

Ok other try, can you send me the full ReduxState and Player classes as well as their generated code pls. I wanna see if I can reproduce.

schultek avatar Mar 13 '25 18:03 schultek

I can't post them here, unfortunately. I'll try to reproduce this issue in a separate repo with minimal similar data.

chirastefan avatar Mar 13 '25 23:03 chirastefan

I created a private repo and added you (check the readme file). I think I found the issue and a fix, but I'm not sure if this is a bug or misuse of the package. Please let me know.

chirastefan avatar Mar 18 '25 21:03 chirastefan

I'm running into this issue as well. It seems to happen when I use Map<String, dynamic> in any MappableClasses, and only happens in profile or release mode, but works fine on debug.

arianneorpilla avatar Dec 02 '25 02:12 arianneorpilla