From bb6aa171283367daaa4743735090a42d3f4f8b81 Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Tue, 30 Jan 2024 19:17:54 +0100 Subject: [PATCH] improve performance by lazily resolving type args --- .../lib/src/mapper_container.dart | 12 +++++-- .../dart_mappable/lib/src/mapper_utils.dart | 36 +++++-------------- .../lib/src/mappers/class_mapper.dart | 5 +-- .../lib/src/mappers/interface_mapper.dart | 16 ++++++--- .../lib/src/mappers/mapper_base.dart | 25 ++++++------- .../lib/src/mappers/mapping_context.dart | 23 +++++++----- .../lib/src/mappers/record_mapper.dart | 8 ++--- .../src/generators/mixins/decoding_mixin.dart | 2 +- 8 files changed, 65 insertions(+), 62 deletions(-) diff --git a/packages/dart_mappable/lib/src/mapper_container.dart b/packages/dart_mappable/lib/src/mapper_container.dart index e0008983..6176c49f 100644 --- a/packages/dart_mappable/lib/src/mapper_container.dart +++ b/packages/dart_mappable/lib/src/mapper_container.dart @@ -471,11 +471,17 @@ class _MapperContainerBase implements MapperContainer, TypeProvider { var mapper = _mapperFor(value); if (mapper != null) { try { - var typeArgs = value.runtimeType.args + return fn( + mapper, + value, + MappingContext( + container: this, + args: () { + return value.runtimeType.args .map((t) => t == UnresolvedType ? dynamic : t) .toList(); - return fn( - mapper, value, MappingContext(container: this, args: typeArgs)); + }, + )); } catch (e, stacktrace) { Error.throwWithStackTrace( MapperException.chain(method, hint(), e), diff --git a/packages/dart_mappable/lib/src/mapper_utils.dart b/packages/dart_mappable/lib/src/mapper_utils.dart index beaa817c..fb603803 100644 --- a/packages/dart_mappable/lib/src/mapper_utils.dart +++ b/packages/dart_mappable/lib/src/mapper_utils.dart @@ -1,5 +1,3 @@ -import 'dart:collection'; - import 'package:type_plus/type_plus.dart'; import 'annotations.dart'; @@ -81,25 +79,6 @@ extension TypeCheck on T { } } -class LazyList with ListMixin { - LazyList(this.create); - - final Iterable Function() create; - - late final List _list = create().toList(); - - @override - int get length => _list.length; - @override - set length(int newLength) => _list.length = newLength; - - @override - T operator [](int index) => _list[index]; - - @override - void operator []=(int index, T value) => _list[index] = value; -} - extension MapperUtils on MapperBase { bool isValueEqual(T? value, Object? other, [MapperContainer? container]) { if (value == null) { @@ -109,8 +88,9 @@ extension MapperUtils on MapperBase { if (!isFor(other)) return false; var context = MappingContext( container: container, - args: LazyList(() => value.runtimeType.args - .map((t) => t == UnresolvedType ? dynamic : t)), + args: () => value.runtimeType.args + .map((t) => t == UnresolvedType ? dynamic : t) + .toList(), ); return equals(value, other as T, context); } catch (e, stacktrace) { @@ -128,8 +108,9 @@ extension MapperUtils on MapperBase { try { var context = MappingContext( container: container, - args: LazyList(() => value.runtimeType.args - .map((t) => t == UnresolvedType ? dynamic : t)), + args: () => value.runtimeType.args + .map((t) => t == UnresolvedType ? dynamic : t) + .toList(), ); return hash(value, context); } catch (e, stacktrace) { @@ -147,8 +128,9 @@ extension MapperUtils on MapperBase { try { var context = MappingContext( container: container, - args: LazyList(() => value.runtimeType.args - .map((t) => t == UnresolvedType ? dynamic : t)), + args: () => value.runtimeType.args + .map((t) => t == UnresolvedType ? dynamic : t) + .toList(), ); return stringify(value, context); } catch (e, stacktrace) { diff --git a/packages/dart_mappable/lib/src/mappers/class_mapper.dart b/packages/dart_mappable/lib/src/mappers/class_mapper.dart index 964d0a72..559a2a84 100644 --- a/packages/dart_mappable/lib/src/mappers/class_mapper.dart +++ b/packages/dart_mappable/lib/src/mappers/class_mapper.dart @@ -136,8 +136,9 @@ abstract class ClassMapperBase /// The set of constructor parameters of this class. /// /// See [FieldMode] for more. - late final List> _params = - fields.values.where((f) => f.mode != FieldMode.member).toList(); + late final List> _params = fields.values + .where((f) => f.mode != FieldMode.member && f.getter != null) + .toList(); @override T decoder(Object? value, DecodingContext context) { diff --git a/packages/dart_mappable/lib/src/mappers/interface_mapper.dart b/packages/dart_mappable/lib/src/mappers/interface_mapper.dart index 7d3980ef..410f596d 100644 --- a/packages/dart_mappable/lib/src/mappers/interface_mapper.dart +++ b/packages/dart_mappable/lib/src/mappers/interface_mapper.dart @@ -177,9 +177,7 @@ abstract class InterfaceMapperBase extends MapperBase { } @protected - Object? encode(T value, EncodingContext context) { - return encodeFields(value, fields.values, ignoreNull, context); - } + Object? encode(T value, EncodingContext context); @protected @pragma('vm:prefer-inline') @@ -189,12 +187,20 @@ abstract class InterfaceMapperBase extends MapperBase { bool ignoreNull, EncodingContext context) { bool shallow = context.options?.shallow ?? false; + if (shallow) { return { for (var f in fields) - if (f.getter != null && (!ignoreNull || f.get(value) != null)) - f.key: shallow ? f.get(value) : f.encode(value, context), + if (!ignoreNull || f.get(value) != null) f.key: f.get(value), }; } + if (ignoreNull) { + return { + for (var f in fields) + if (f.get(value) != null) f.key: f.get(value), + }; + } + return {for (var f in fields) f.key: f.encode(value, context)}; + } V decodeMap(Map map) => decodeValue(map); diff --git a/packages/dart_mappable/lib/src/mappers/mapper_base.dart b/packages/dart_mappable/lib/src/mappers/mapper_base.dart index de80515f..f8c5922e 100644 --- a/packages/dart_mappable/lib/src/mappers/mapper_base.dart +++ b/packages/dart_mappable/lib/src/mappers/mapper_base.dart @@ -44,7 +44,7 @@ abstract class MapperBase { value, DecodingContext( container: container, - args: type.args, + args: () => type.args, options: options, ), ) as V; @@ -59,28 +59,29 @@ abstract class MapperBase { Object? encodeValue(V value, [EncodingOptions? options, MapperContainer? container]) { try { - Type type = V; - var includeTypeId = options?.includeTypeId; includeTypeId ??= this.includeTypeId(value); - if (includeTypeId) { + var result = this.encoder( + value as T, + EncodingContext( + container: container, + options: options?.inheritOptions ?? false ? options : null, + args: () { + Type type = V; + if (includeTypeId ?? false) { type = value.runtimeType; } - var typeArgs = type.args.map((t) => t == UnresolvedType ? dynamic : t); + var typeArgs = + type.args.map((t) => t == UnresolvedType ? dynamic : t); var fallback = this.type.base.args; if (typeArgs.length != fallback.length) { typeArgs = fallback; } - - var result = this.encoder( - value as T, - EncodingContext( - container: container, - options: options?.inheritOptions ?? false ? options : null, - args: typeArgs.toList(), + return typeArgs.toList(); + }, ), ); diff --git a/packages/dart_mappable/lib/src/mappers/mapping_context.dart b/packages/dart_mappable/lib/src/mappers/mapping_context.dart index 61fb1168..3c360130 100644 --- a/packages/dart_mappable/lib/src/mappers/mapping_context.dart +++ b/packages/dart_mappable/lib/src/mappers/mapping_context.dart @@ -8,10 +8,13 @@ class MappingContext { final MapperContainer container; /// A list of type arguments to get the concrete type for a generic mapper. - final List args; + final List Function()? _args; - MappingContext({MapperContainer? container, this.args = const []}) - : container = container ?? MapperContainer.globals; + late final List args = _args?.call() ?? []; + + MappingContext({MapperContainer? container, List Function()? args}) + : _args = args, + container = container ?? MapperContainer.globals; Type arg(int index, [List argIndices = const []]) { var a = args[index]; @@ -35,16 +38,19 @@ class DecodingContext extends MappingContext { final bool inherited; DecodingContext change( - {MapperContainer? container, List? args, bool? inherited}) { + {MapperContainer? container, + List Function()? args, + bool? inherited}) { return DecodingContext( container: container ?? this.container, - args: args ?? this.args, + args: args ?? _args, options: options, inherited: inherited ?? this.inherited, ); } - DecodingContext inherit({MapperContainer? container, List? args}) { + DecodingContext inherit( + {MapperContainer? container, List Function()? args}) { return change(container: container, args: args, inherited: true); } } @@ -55,10 +61,11 @@ class EncodingContext extends MappingContext { final EncodingOptions? options; - EncodingContext change({MapperContainer? container, List? args}) { + EncodingContext change( + {MapperContainer? container, List Function()? args}) { return EncodingContext( container: container ?? this.container, - args: args ?? this.args, + args: args ?? _args, options: options, ); } diff --git a/packages/dart_mappable/lib/src/mappers/record_mapper.dart b/packages/dart_mappable/lib/src/mappers/record_mapper.dart index 63eb0b82..926772bc 100644 --- a/packages/dart_mappable/lib/src/mappers/record_mapper.dart +++ b/packages/dart_mappable/lib/src/mappers/record_mapper.dart @@ -21,19 +21,19 @@ abstract class RecordMapperBase extends InterfaceMapperBase @override T decode(Object? value, DecodingContext context) { - return super.decode(value, context.change(args: apply(context))); + return super.decode(value, context.change(args: () => apply(context))); } @override Object? encode(T value, EncodingContext context) { - return super.encode(value, context.change(args: apply(context))); + return InterfaceMapperBase.encodeFields(value, fields.values, ignoreNull, + context.change(args: () => apply(context))); } @override bool isForType(Type type) { try { - var args = type.args; - var newArgs = apply(DecodingContext(args: args)); + var newArgs = apply(DecodingContext(args: () => type.args)); var instantiatedType = typeFactory .callWith(parameters: [() => T], typeArguments: newArgs); return type == instantiatedType; diff --git a/packages/dart_mappable_builder/lib/src/generators/mixins/decoding_mixin.dart b/packages/dart_mappable_builder/lib/src/generators/mixins/decoding_mixin.dart index 33999aba..0a0948d3 100644 --- a/packages/dart_mappable_builder/lib/src/generators/mixins/decoding_mixin.dart +++ b/packages/dart_mappable_builder/lib/src/generators/mixins/decoding_mixin.dart @@ -74,7 +74,7 @@ mixin DecodingMixin on MapperGenerator { output.write(''' @override DecodingContext inherit(DecodingContext context) { - return context.inherit(args: [${args.join(', ')}]); + return context.inherit(args: () => [${args.join(', ')}]); } '''); }