Skip to content

Commit

Permalink
improve performance by lazily resolving type args
Browse files Browse the repository at this point in the history
  • Loading branch information
schultek committed Jan 30, 2024
1 parent db2ca09 commit bb6aa17
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 62 deletions.
12 changes: 9 additions & 3 deletions packages/dart_mappable/lib/src/mapper_container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
36 changes: 9 additions & 27 deletions packages/dart_mappable/lib/src/mapper_utils.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:collection';

import 'package:type_plus/type_plus.dart';

import 'annotations.dart';
Expand Down Expand Up @@ -81,25 +79,6 @@ extension TypeCheck<T> on T {
}
}

class LazyList<T> with ListMixin<T> {
LazyList(this.create);

final Iterable<T> Function() create;

late final List<T> _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<T extends Object> on MapperBase<T> {
bool isValueEqual(T? value, Object? other, [MapperContainer? container]) {
if (value == null) {
Expand All @@ -109,8 +88,9 @@ extension MapperUtils<T extends Object> on MapperBase<T> {
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) {
Expand All @@ -128,8 +108,9 @@ extension MapperUtils<T extends Object> on MapperBase<T> {
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) {
Expand All @@ -147,8 +128,9 @@ extension MapperUtils<T extends Object> on MapperBase<T> {
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) {
Expand Down
5 changes: 3 additions & 2 deletions packages/dart_mappable/lib/src/mappers/class_mapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@ abstract class ClassMapperBase<T extends Object>
/// The set of constructor parameters of this class.
///
/// See [FieldMode] for more.
late final List<Field<T, dynamic>> _params =
fields.values.where((f) => f.mode != FieldMode.member).toList();
late final List<Field<T, dynamic>> _params = fields.values
.where((f) => f.mode != FieldMode.member && f.getter != null)
.toList();

@override
T decoder(Object? value, DecodingContext context) {
Expand Down
16 changes: 11 additions & 5 deletions packages/dart_mappable/lib/src/mappers/interface_mapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,7 @@ abstract class InterfaceMapperBase<T extends Object> extends MapperBase<T> {
}

@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')
Expand All @@ -189,12 +187,20 @@ abstract class InterfaceMapperBase<T extends Object> extends MapperBase<T> {
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<V>(Map<String, dynamic> map) => decodeValue<V>(map);

Expand Down
25 changes: 13 additions & 12 deletions packages/dart_mappable/lib/src/mappers/mapper_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ abstract class MapperBase<T extends Object> {
value,
DecodingContext(
container: container,
args: type.args,
args: () => type.args,
options: options,
),
) as V;
Expand All @@ -59,28 +59,29 @@ abstract class MapperBase<T extends Object> {
Object? encodeValue<V>(V value,
[EncodingOptions? options, MapperContainer? container]) {
try {
Type type = V;

var includeTypeId = options?.includeTypeId;
includeTypeId ??= this.includeTypeId<V>(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();
},
),
);

Expand Down
23 changes: 15 additions & 8 deletions packages/dart_mappable/lib/src/mappers/mapping_context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<Type> args;
final List<Type> Function()? _args;

MappingContext({MapperContainer? container, this.args = const []})
: container = container ?? MapperContainer.globals;
late final List<Type> args = _args?.call() ?? [];

MappingContext({MapperContainer? container, List<Type> Function()? args})
: _args = args,
container = container ?? MapperContainer.globals;

Type arg(int index, [List<int> argIndices = const []]) {
var a = args[index];
Expand All @@ -35,16 +38,19 @@ class DecodingContext extends MappingContext {
final bool inherited;

DecodingContext change(
{MapperContainer? container, List<Type>? args, bool? inherited}) {
{MapperContainer? container,
List<Type> 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<Type>? args}) {
DecodingContext inherit(
{MapperContainer? container, List<Type> Function()? args}) {
return change(container: container, args: args, inherited: true);
}
}
Expand All @@ -55,10 +61,11 @@ class EncodingContext extends MappingContext {

final EncodingOptions? options;

EncodingContext change({MapperContainer? container, List<Type>? args}) {
EncodingContext change(
{MapperContainer? container, List<Type> Function()? args}) {
return EncodingContext(
container: container ?? this.container,
args: args ?? this.args,
args: args ?? _args,
options: options,
);
}
Expand Down
8 changes: 4 additions & 4 deletions packages/dart_mappable/lib/src/mappers/record_mapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@ abstract class RecordMapperBase<T extends Record> extends InterfaceMapperBase<T>

@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>() => T], typeArguments: newArgs);
return type == instantiatedType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ mixin DecodingMixin on MapperGenerator<TargetClassMapperElement> {
output.write('''
@override
DecodingContext inherit(DecodingContext context) {
return context.inherit(args: [${args.join(', ')}]);
return context.inherit(args: () => [${args.join(', ')}]);
}
''');
}
Expand Down

0 comments on commit bb6aa17

Please sign in to comment.