diff --git a/benchmarks/dart/lib/src/workloads.dart b/benchmarks/dart/lib/src/workloads.dart index 12766c37d0..32f598872a 100644 --- a/benchmarks/dart/lib/src/workloads.dart +++ b/benchmarks/dart/lib/src/workloads.dart @@ -158,7 +158,7 @@ buildBenchmarkDefinitions() { } Fory newBenchmarkFory() { - final fory = Fory(config: const Config(compatible: true)); + final fory = Fory(compatible: true); registerBenchmarkTypes(fory); return fory; } diff --git a/dart/packages/fory/lib/src/codegen/fory_generator.dart b/dart/packages/fory/lib/src/codegen/fory_generator.dart index 424301df19..563279041e 100644 --- a/dart/packages/fory/lib/src/codegen/fory_generator.dart +++ b/dart/packages/fory/lib/src/codegen/fory_generator.dart @@ -501,6 +501,14 @@ final class ForyGenerator extends Generator { output.writeln( ' value.${field.name} = ${_directGeneratedTypedContainerReadExpression(structSpec.name, field, 'fields[$index]')};', ); + } else if (_usesDirectGeneratedStructFieldFastPath(field)) { + output.writeln( + ' value.${field.name} = $readerFunctionName(readGeneratedStructDirectValue(context, fields[$index]), value.${field.name});', + ); + } else if (_usesDirectGeneratedDeclaredReadFastPath(field)) { + output.writeln( + ' value.${field.name} = $readerFunctionName(readGeneratedStructDeclaredValue(context, fields[$index]), value.${field.name});', + ); } else { output.writeln( ' value.${field.name} = $readerFunctionName(readGeneratedStructFieldInfoValue(context, fields[$index], value.${field.name}), value.${field.name});', @@ -531,12 +539,6 @@ final class ForyGenerator extends Generator { output.writeln(' return value;'); case _ConstructorMode.constructor: output.writeln(' final slots = generatedStructReadSlots(context);'); - for (var index = 0; index < structSpec.fields.length; index += 1) { - final field = structSpec.fields[index]; - output.writeln( - ' late final ${field.displayType} ${field.localName};', - ); - } output.writeln(' if (slots == null) {'); if (hasDirectPrimitiveFastPath || directCursorRuns.isNotEmpty) { output.writeln(' final buffer = context.buffer;'); @@ -555,19 +557,27 @@ final class ForyGenerator extends Generator { } if (_usesReservedGeneratedFastPath(field)) { output.writeln( - ' ${field.localName} = ${_directGeneratedCursorReadExpression(field, 'cursor${directCursorStartByIndex[index]}')};', + ' final ${field.displayType} ${field.localName} = ${_directGeneratedCursorReadExpression(field, 'cursor${directCursorStartByIndex[index]}')};', ); } else if (_usesDirectGeneratedBasicFastPath(field)) { output.writeln( - ' ${field.localName} = ${_directGeneratedReadExpression(field)};', + ' final ${field.displayType} ${field.localName} = ${_directGeneratedReadExpression(field)};', ); } else if (_usesDirectGeneratedTypedContainerReadFastPath(field)) { output.writeln( - ' ${field.localName} = ${_directGeneratedTypedContainerReadExpression(structSpec.name, field, 'fields[$index]')};', + ' final ${field.displayType} ${field.localName} = ${_directGeneratedTypedContainerReadExpression(structSpec.name, field, 'fields[$index]')};', + ); + } else if (_usesDirectGeneratedStructFieldFastPath(field)) { + output.writeln( + ' final ${field.displayType} ${field.localName} = $readerFunctionName(readGeneratedStructDirectValue(context, fields[$index]));', + ); + } else if (_usesDirectGeneratedDeclaredReadFastPath(field)) { + output.writeln( + ' final ${field.displayType} ${field.localName} = $readerFunctionName(readGeneratedStructDeclaredValue(context, fields[$index]));', ); } else { output.writeln( - ' ${field.localName} = $readerFunctionName(readGeneratedStructFieldInfoValue(context, fields[$index]));', + ' final ${field.displayType} ${field.localName} = $readerFunctionName(readGeneratedStructFieldInfoValue(context, fields[$index]));', ); } final directCursorEndRun = directCursorRunByEnd[index]; @@ -575,28 +585,45 @@ final class ForyGenerator extends Generator { output.writeln(' cursor${directCursorEndRun.start}.finish();'); } } - output.writeln(' } else {'); + final constructorInvocation = _constructorInvocation(structSpec); + output + ..writeln(' final value = $constructorInvocation;') + ..writeln(' context.reference(value);'); + for (final fieldName + in structSpec.constructorPlan.postConstructionFieldNames) { + final field = + structSpec.fields.firstWhere((item) => item.name == fieldName); + output.writeln(' value.${field.name} = ${field.localName};'); + } + output.writeln(' return value;'); + // Slow path: schema-evolution slots present. Use `late final` so each + // field can be conditionally assigned from its slot or read fresh. + output.writeln(' }'); + for (var index = 0; index < structSpec.fields.length; index += 1) { + final field = structSpec.fields[index]; + output.writeln( + ' late final ${field.displayType} ${field.localName};', + ); + } for (var index = 0; index < structSpec.fields.length; index += 1) { final field = structSpec.fields[index]; final readerFunctionName = field.readerFunctionName(structSpec.name); final rawValueName = 'raw${structSpec.name}$index'; output.writeln( - ' if (slots.containsSlot($index)) {', + ' if (slots.containsSlot($index)) {', ); output.writeln( - ' final $rawValueName = slots.valueForSlot($index);', + ' final $rawValueName = slots.valueForSlot($index);', ); output.writeln( - ' ${field.localName} = $readerFunctionName(${_slotResolvedRawExpression(rawValueName)});', + ' ${field.localName} = $readerFunctionName(${_slotResolvedRawExpression(rawValueName)});', ); - output.writeln(' } else {'); + output.writeln(' } else {'); output.writeln( - ' ${field.localName} = $readerFunctionName(null);', + ' ${field.localName} = $readerFunctionName(null);', ); - output.writeln(' }'); + output.writeln(' }'); } - output.writeln(' }'); - final constructorInvocation = _constructorInvocation(structSpec); output ..writeln(' final value = $constructorInvocation;') ..writeln(' context.reference(value);'); @@ -939,6 +966,31 @@ GeneratedFieldType( field.fieldType.typeId == TypeIds.enumById; } + bool _usesDirectGeneratedDeclaredReadFastPath( + _GeneratedFieldSpec field, + ) { + if (field.fieldType.nullable || + field.fieldType.ref || + field.fieldType.dynamic == true) { + return false; + } + final typeId = field.fieldType.typeId; + return typeId == TypeIds.ext || typeId == TypeIds.namedExt; + } + + bool _usesDirectGeneratedStructFieldFastPath(_GeneratedFieldSpec field) { + if (field.fieldType.nullable || + field.fieldType.ref || + field.fieldType.dynamic == true) { + return false; + } + final typeId = field.fieldType.typeId; + return typeId == TypeIds.struct || + typeId == TypeIds.compatibleStruct || + typeId == TypeIds.namedStruct || + typeId == TypeIds.namedCompatibleStruct; + } + bool _usesDirectGeneratedTypedContainerReadFastPath( _GeneratedFieldSpec field, ) { diff --git a/dart/packages/fory/lib/src/codegen/generated_support.dart b/dart/packages/fory/lib/src/codegen/generated_support.dart index ddaac63357..44ba641055 100644 --- a/dart/packages/fory/lib/src/codegen/generated_support.dart +++ b/dart/packages/fory/lib/src/codegen/generated_support.dart @@ -559,16 +559,12 @@ void registerGeneratedStruct( @internal StructWriteSlots? generatedStructWriteSlots(WriteContext context) { - return context.getContextObject( - structWriteSlotsKey, - ); + return context.structWriteSlots; } @internal StructReadSlots? generatedStructReadSlots(ReadContext context) { - return context.getContextObject( - structReadSlotsKey, - ); + return context.structReadSlots; } @internal @@ -687,6 +683,40 @@ Object? readGeneratedStructFieldInfoValue( return readFieldValue(context, field, fallback); } +@internal +Object? readGeneratedStructDeclaredValue( + ReadContext context, + GeneratedStructFieldInfo field, +) { + final resolved = fieldDeclaredTypeInfo(context.typeResolver, field)!; + if (fieldUsesDeclaredType(context.typeResolver, field)) { + return context.readResolvedValue(resolved, field.fieldType); + } + final actualResolved = context.readTypeMetaValue( + resolved.isNamed ? resolved : null, + ); + return context.readResolvedValue(actualResolved, field.fieldType); +} + +@internal +Object readGeneratedStructDirectValue( + ReadContext context, + GeneratedStructFieldInfo field, +) { + final declared = fieldDeclaredTypeInfo(context.typeResolver, field)!; + final resolver.TypeInfo resolved; + if (fieldUsesDeclaredType(context.typeResolver, field)) { + resolved = declared; + } else { + resolved = + context.readTypeMetaValue(declared.isNamed ? declared : null); + } + context.increaseDepth(); + final value = resolved.structSerializer!.readValue(context, resolved); + context.decreaseDepth(); + return value; +} + @internal List readGeneratedDirectListValue( ReadContext context, diff --git a/dart/packages/fory/lib/src/context/read_context.dart b/dart/packages/fory/lib/src/context/read_context.dart index ec78c2f7d2..f70a79f4e7 100644 --- a/dart/packages/fory/lib/src/context/read_context.dart +++ b/dart/packages/fory/lib/src/context/read_context.dart @@ -12,6 +12,7 @@ import 'package:fory/src/serializer/map_serializers.dart'; import 'package:fory/src/serializer/primitive_serializers.dart'; import 'package:fory/src/serializer/scalar_serializers.dart'; import 'package:fory/src/serializer/serializer.dart'; +import 'package:fory/src/serializer/struct_slots.dart'; import 'package:fory/src/serializer/typed_array_serializers.dart'; import 'package:fory/src/types/float16.dart'; @@ -29,7 +30,7 @@ final class ReadContext { late Buffer _buffer; final List _sharedTypes = []; - final Map _contextObjects = {}; + StructReadSlots? _structReadSlots; int _depth = 0; @internal @@ -50,7 +51,7 @@ final class ReadContext { _sharedTypes.clear(); _refReader.reset(); _metaStringReader.reset(); - _contextObjects.clear(); + _structReadSlots = null; _depth = 0; } @@ -64,33 +65,11 @@ final class ReadContext { RefReader get refReader => _refReader; @internal - bool hasContextObject(Object key) { - return _contextObjects.containsKey(key); - } - - @internal - T? getContextObject(Object key) { - return _contextObjects[key] as T?; - } + StructReadSlots? get structReadSlots => _structReadSlots; @internal - Object? replaceContextObject(Object key, Object? next) { - final previous = _contextObjects[key]; - if (next == null) { - _contextObjects.remove(key); - } else { - _contextObjects[key] = next; - } - return previous; - } - - @internal - void restoreContextObject(Object key, Object? previous) { - if (previous == null) { - _contextObjects.remove(key); - } else { - _contextObjects[key] = previous; - } + set structReadSlots(StructReadSlots? value) { + _structReadSlots = value; } @internal diff --git a/dart/packages/fory/lib/src/context/write_context.dart b/dart/packages/fory/lib/src/context/write_context.dart index f039e2f422..d803f87c91 100644 --- a/dart/packages/fory/lib/src/context/write_context.dart +++ b/dart/packages/fory/lib/src/context/write_context.dart @@ -15,6 +15,7 @@ import 'package:fory/src/serializer/collection_serializers.dart'; import 'package:fory/src/serializer/map_serializers.dart'; import 'package:fory/src/serializer/primitive_serializers.dart'; import 'package:fory/src/serializer/scalar_serializers.dart'; +import 'package:fory/src/serializer/struct_slots.dart'; import 'package:fory/src/serializer/typed_array_serializers.dart'; import 'package:fory/src/types/float16.dart'; import 'package:fory/src/types/local_date.dart'; @@ -35,8 +36,7 @@ final class WriteContext { late Buffer _buffer; final LinkedHashMap _typeDefIds = LinkedHashMap.identity(); - final LinkedHashMap _contextObjects = - LinkedHashMap.identity(); + StructWriteSlots? _structWriteSlots; bool _rootTrackRef = false; int _depth = 0; @@ -59,7 +59,7 @@ final class WriteContext { _typeDefIds.clear(); _refWriter.reset(); _metaStringWriter.reset(); - _contextObjects.clear(); + _structWriteSlots = null; _rootTrackRef = false; _depth = 0; } @@ -77,33 +77,11 @@ final class WriteContext { bool get rootTrackRef => _rootTrackRef; @internal - bool hasContextObject(Object key) { - return _contextObjects.containsKey(key); - } + StructWriteSlots? get structWriteSlots => _structWriteSlots; @internal - T? getContextObject(Object key) { - return _contextObjects[key] as T?; - } - - @internal - Object? replaceContextObject(Object key, Object? next) { - final previous = _contextObjects[key]; - if (next == null) { - _contextObjects.remove(key); - } else { - _contextObjects[key] = next; - } - return previous; - } - - @internal - void restoreContextObject(Object key, Object? previous) { - if (previous == null) { - _contextObjects.remove(key); - } else { - _contextObjects[key] = previous; - } + set structWriteSlots(StructWriteSlots? value) { + _structWriteSlots = value; } /// Records entry into one more nested write frame. diff --git a/dart/packages/fory/lib/src/serializer/struct_serializer.dart b/dart/packages/fory/lib/src/serializer/struct_serializer.dart index 4b59466060..6c7cdde374 100644 --- a/dart/packages/fory/lib/src/serializer/struct_serializer.dart +++ b/dart/packages/fory/lib/src/serializer/struct_serializer.dart @@ -92,10 +92,7 @@ final class StructSerializer extends Serializer { final previousCompatibleFields = _replaceWriteSlots(internal, resolved, value); _payloadSerializer.write(context, value); - internal.restoreContextObject( - structWriteSlotsKey, - previousCompatibleFields, - ); + internal.structWriteSlots = previousCompatibleFields; } Object readValue( @@ -122,35 +119,29 @@ final class StructSerializer extends Serializer { hasCurrentPreservedRef: hasCurrentPreservedRef, ); } - final previousReadSlots = internal.replaceContextObject( - structReadSlotsKey, - null, - ); + final previousReadSlots = internal.structReadSlots; + internal.structReadSlots = null; final value = internal.readSerializerPayload( _payloadSerializer, resolved, hasCurrentPreservedRef: hasCurrentPreservedRef, ); - internal.restoreContextObject( - structReadSlotsKey, - previousReadSlots, - ); + internal.structReadSlots = previousReadSlots; _rememberRemoteMetadata(internal, resolved, value); return value; } - Object? _replaceWriteSlots( + StructWriteSlots? _replaceWriteSlots( WriteContext context, TypeInfo resolved, Object value, ) { final layout = _compatibleWriteLayoutForValue(context, resolved, value); - return context.replaceContextObject( - structWriteSlotsKey, - layout == null - ? null - : StructWriteSlots(layout.fields, _localFields.length), - ); + final previous = context.structWriteSlots; + context.structWriteSlots = layout == null + ? null + : StructWriteSlots(layout.fields, _localFields.length); + return previous; } _CompatibleWriteLayout? _compatibleWriteLayoutForValue( @@ -256,19 +247,14 @@ final class StructSerializer extends Serializer { ); presentSlots[slot] = true; } - final previousCompatibleFields = context.replaceContextObject( - structReadSlotsKey, - StructReadSlots(compatibleValues, presentSlots), - ); + final previousCompatibleFields = context.structReadSlots; + context.structReadSlots = StructReadSlots(compatibleValues, presentSlots); final value = context.readSerializerPayload( _payloadSerializer, resolved, hasCurrentPreservedRef: hasCurrentPreservedRef, ); - context.restoreContextObject( - structReadSlotsKey, - previousCompatibleFields, - ); + context.structReadSlots = previousCompatibleFields; _rememberRemoteMetadata(context, resolved, value); return value; } diff --git a/dart/packages/fory/lib/src/serializer/struct_slots.dart b/dart/packages/fory/lib/src/serializer/struct_slots.dart index 73b59f6101..8bd2dd56dd 100644 --- a/dart/packages/fory/lib/src/serializer/struct_slots.dart +++ b/dart/packages/fory/lib/src/serializer/struct_slots.dart @@ -1,12 +1,6 @@ import 'package:meta/meta.dart'; import 'package:fory/src/serializer/serialization_field_info.dart'; -@internal -final Object structWriteSlotsKey = Object(); - -@internal -final Object structReadSlotsKey = Object(); - @internal final class StructWriteSlots { final List orderedFields;