diff --git a/src/Riok.Mapperly.Abstractions/AggressiveInliningMode.cs b/src/Riok.Mapperly.Abstractions/AggressiveInliningMode.cs
new file mode 100644
index 00000000000..b551ec1a6f9
--- /dev/null
+++ b/src/Riok.Mapperly.Abstractions/AggressiveInliningMode.cs
@@ -0,0 +1,27 @@
+namespace Riok.Mapperly.Abstractions;
+
+///
+/// Indicates how AggressiveInlining should be applied to methods.
+///
+public enum AggressiveInliningMode
+{
+ ///
+ /// Does not apply AggressiveInlining
+ ///
+ None,
+
+ ///
+ /// Applies AggressiveInlining to value types
+ ///
+ ValueTypes,
+
+ ///
+ /// Applies AggressiveInlining to reference types
+ ///
+ ReferenceTypes,
+
+ ///
+ /// Applies AggressiveInlining to all types
+ ///
+ All,
+}
diff --git a/src/Riok.Mapperly.Abstractions/MapperAttribute.cs b/src/Riok.Mapperly.Abstractions/MapperAttribute.cs
index 3a129163bc3..8f2293c457f 100644
--- a/src/Riok.Mapperly.Abstractions/MapperAttribute.cs
+++ b/src/Riok.Mapperly.Abstractions/MapperAttribute.cs
@@ -120,4 +120,10 @@ public class MapperAttribute : Attribute
/// partial methods are discovered.
///
public bool AutoUserMappings { get; set; } = true;
+
+ ///
+ /// Gets or sets the aggressive inlining mode.
+ /// Defaults to .
+ ///
+ public AggressiveInliningMode AggressiveInliningMode { get; set; } = AggressiveInliningMode.ValueTypes;
}
diff --git a/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt b/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt
index 3b12015a40a..8e2fc040865 100644
--- a/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt
+++ b/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt
@@ -186,3 +186,10 @@ Riok.Mapperly.Abstractions.MappingTargetAttribute
Riok.Mapperly.Abstractions.MappingTargetAttribute.MappingTargetAttribute() -> void
Riok.Mapperly.Abstractions.MapPropertyAttribute.MapPropertyAttribute(string! source, string![]! target) -> void
Riok.Mapperly.Abstractions.MapPropertyAttribute.MapPropertyAttribute(string![]! source, string! target) -> void
+Riok.Mapperly.Abstractions.AggressiveInliningMode
+Riok.Mapperly.Abstractions.AggressiveInliningMode.None = 0 -> Riok.Mapperly.Abstractions.AggressiveInliningMode
+Riok.Mapperly.Abstractions.AggressiveInliningMode.ValueTypes = 1 -> Riok.Mapperly.Abstractions.AggressiveInliningMode
+Riok.Mapperly.Abstractions.AggressiveInliningMode.ReferenceTypes = 2 -> Riok.Mapperly.Abstractions.AggressiveInliningMode
+Riok.Mapperly.Abstractions.AggressiveInliningMode.All = 3 -> Riok.Mapperly.Abstractions.AggressiveInliningMode
+Riok.Mapperly.Abstractions.MapperAttribute.AggressiveInliningMode.get -> Riok.Mapperly.Abstractions.AggressiveInliningMode
+Riok.Mapperly.Abstractions.MapperAttribute.AggressiveInliningMode.set -> void
diff --git a/src/Riok.Mapperly/Configuration/MapperConfiguration.cs b/src/Riok.Mapperly/Configuration/MapperConfiguration.cs
index a82b9a80dc6..e31678bace2 100644
--- a/src/Riok.Mapperly/Configuration/MapperConfiguration.cs
+++ b/src/Riok.Mapperly/Configuration/MapperConfiguration.cs
@@ -115,4 +115,10 @@ public record MapperConfiguration
/// Whether to consider non-partial methods in a mapper as user implemented mapping methods.
///
public bool? AutoUserMappings { get; init; }
+
+ ///
+ /// Gets or sets the aggressive inlining mode.
+ /// Defaults to .
+ ///
+ public AggressiveInliningMode? AggressiveInliningMode { get; set; }
}
diff --git a/src/Riok.Mapperly/Configuration/MapperConfigurationMerger.cs b/src/Riok.Mapperly/Configuration/MapperConfigurationMerger.cs
index d85911f0163..64298864a17 100644
--- a/src/Riok.Mapperly/Configuration/MapperConfigurationMerger.cs
+++ b/src/Riok.Mapperly/Configuration/MapperConfigurationMerger.cs
@@ -62,6 +62,11 @@ public static MapperAttribute Merge(MapperConfiguration mapperConfiguration, Map
mapper.AutoUserMappings =
mapperConfiguration.AutoUserMappings ?? defaultMapperConfiguration.AutoUserMappings ?? mapper.AutoUserMappings;
+ mapper.AggressiveInliningMode =
+ mapperConfiguration.AggressiveInliningMode
+ ?? defaultMapperConfiguration.AggressiveInliningMode
+ ?? mapper.AggressiveInliningMode;
+
return mapper;
}
}
diff --git a/src/Riok.Mapperly/Descriptors/Mappings/MethodMapping.cs b/src/Riok.Mapperly/Descriptors/Mappings/MethodMapping.cs
index 2dae8a315f2..6afade6b885 100644
--- a/src/Riok.Mapperly/Descriptors/Mappings/MethodMapping.cs
+++ b/src/Riok.Mapperly/Descriptors/Mappings/MethodMapping.cs
@@ -1,7 +1,9 @@
using System.Diagnostics;
+using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Emit;
using Riok.Mapperly.Emit.Syntax;
using Riok.Mapperly.Helpers;
@@ -96,10 +98,37 @@ public virtual MethodDeclarationSyntax BuildMethod(SourceEmitterContext ctx)
return MethodDeclaration(returnType.AddTrailingSpace(), Identifier(MethodName))
.WithModifiers(TokenList(BuildModifiers(ctx.IsStatic)))
.WithParameterList(parameters)
- .WithAttributeLists(ctx.SyntaxFactory.GeneratedCodeAttributeList())
+ .WithAttributeLists(ctx.SyntaxFactory.GeneratedCodeAttributeList().AddRange(GenerateMethodImplAttributeListIfNeeded(ctx)))
.WithBody(ctx.SyntaxFactory.Block(BuildBody(typeMappingBuildContext)));
}
+ private SyntaxList GenerateMethodImplAttributeListIfNeeded(SourceEmitterContext ctx)
+ {
+ var hasMethodImplAttribute = MethodDeclarationSyntax.HasAttribute();
+
+ //Skip if method already has [MethodImple] attribute
+ if (hasMethodImplAttribute)
+ return [];
+
+ var aggressiveInliningMode = GetAggressiveInliningMode();
+
+ return aggressiveInliningMode switch
+ {
+ AggressiveInliningMode.ValueTypes when SourceType.IsValueType => ctx.SyntaxFactory.MethodImplAttributeList(),
+ AggressiveInliningMode.ReferenceTypes when SourceType.IsReferenceType => ctx.SyntaxFactory.MethodImplAttributeList(),
+ AggressiveInliningMode.All => ctx.SyntaxFactory.MethodImplAttributeList(),
+ AggressiveInliningMode.None => [],
+ _ => [],
+ };
+ }
+
+ private AggressiveInliningMode GetAggressiveInliningMode()
+ {
+ //Read value from AggressiveInliningMode property of [Mapper] attribute annotated on containing class
+ //I guess from SimpleMappingBuilderContext.Configuration.Mapper.AggressiveInliningMode
+ return AggressiveInliningMode.ReferenceTypes;
+ }
+
public abstract IEnumerable BuildBody(TypeMappingBuildContext ctx);
internal void SetMethodNameIfNeeded(Func methodNameBuilder)
diff --git a/src/Riok.Mapperly/Emit/Syntax/SyntaxFactoryHelper.Literal.cs b/src/Riok.Mapperly/Emit/Syntax/SyntaxFactoryHelper.Literal.cs
index 6e748d85184..f86f961efda 100644
--- a/src/Riok.Mapperly/Emit/Syntax/SyntaxFactoryHelper.Literal.cs
+++ b/src/Riok.Mapperly/Emit/Syntax/SyntaxFactoryHelper.Literal.cs
@@ -17,4 +17,11 @@ public static LiteralExpressionSyntax BooleanLiteral(bool b) =>
public static LiteralExpressionSyntax StringLiteral(string content) =>
LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(content));
+
+ public static MemberAccessExpressionSyntax EnumLiteral(Enum @enum) =>
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName($"global::{@enum.GetType().FullName}"),
+ IdentifierName(@enum.ToString())
+ );
}
diff --git a/src/Riok.Mapperly/Emit/Syntax/SyntaxFactoryHelper.MethodImpl.cs b/src/Riok.Mapperly/Emit/Syntax/SyntaxFactoryHelper.MethodImpl.cs
new file mode 100644
index 00000000000..345e563ce0c
--- /dev/null
+++ b/src/Riok.Mapperly/Emit/Syntax/SyntaxFactoryHelper.MethodImpl.cs
@@ -0,0 +1,15 @@
+using System.Runtime.CompilerServices;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Riok.Mapperly.Emit.Syntax;
+
+public partial struct SyntaxFactoryHelper
+{
+ private const string MethodImplAttributeName = "global::System.Runtime.CompilerServices.MethodImpl";
+
+ public SyntaxList MethodImplAttributeList()
+ {
+ return AttributeList(MethodImplAttributeName, EnumLiteral(MethodImplOptions.AggressiveInlining));
+ }
+}
diff --git a/src/Riok.Mapperly/Helpers/MethodDeclarationSyntaxExtensions.cs b/src/Riok.Mapperly/Helpers/MethodDeclarationSyntaxExtensions.cs
new file mode 100644
index 00000000000..4bf81c372fd
--- /dev/null
+++ b/src/Riok.Mapperly/Helpers/MethodDeclarationSyntaxExtensions.cs
@@ -0,0 +1,28 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Riok.Mapperly.Helpers;
+
+public static class MethodDeclarationSyntaxExtensions
+{
+ public static bool HasAttribute(this MethodDeclarationSyntax methodDeclarationSyntax)
+ where TAttribute : Attribute
+ {
+ var csharpCompilation = CSharpCompilation.Create(
+ assemblyName: null,
+ syntaxTrees: [methodDeclarationSyntax!.SyntaxTree],
+ references: [MetadataReference.CreateFromFile(typeof(TAttribute).Assembly.Location)]
+ );
+
+ var semanticModel = csharpCompilation.GetSemanticModel(methodDeclarationSyntax.SyntaxTree);
+
+ var attributes = methodDeclarationSyntax.AttributeLists.SelectMany(p => p.Attributes);
+
+ return attributes.Any(attributeSyntax =>
+ {
+ var symbols = semanticModel.GetSymbolInfo(attributeSyntax).CandidateSymbols.OfType();
+ return symbols.Any(p => string.Equals(p.ContainingType.ToString(), typeof(TAttribute).FullName, StringComparison.Ordinal));
+ });
+ }
+}
diff --git a/test/Riok.Mapperly.Tests/TestSourceBuilder.cs b/test/Riok.Mapperly.Tests/TestSourceBuilder.cs
index 9d393a31c62..82c677ccc82 100644
--- a/test/Riok.Mapperly.Tests/TestSourceBuilder.cs
+++ b/test/Riok.Mapperly.Tests/TestSourceBuilder.cs
@@ -97,6 +97,7 @@ private static string BuildAttribute(TestSourceBuilderOptions options)
Attribute(options.IncludedMembers),
Attribute(options.PreferParameterlessConstructors),
Attribute(options.AutoUserMappings),
+ Attribute(options.AggressiveInliningMode),
}.WhereNotNull();
return $"[Mapper({string.Join(", ", attrs)})]";
diff --git a/test/Riok.Mapperly.Tests/TestSourceBuilderOptions.cs b/test/Riok.Mapperly.Tests/TestSourceBuilderOptions.cs
index 43f6431b67d..3ac019cfce8 100644
--- a/test/Riok.Mapperly.Tests/TestSourceBuilderOptions.cs
+++ b/test/Riok.Mapperly.Tests/TestSourceBuilderOptions.cs
@@ -20,7 +20,8 @@ public record TestSourceBuilderOptions(
MemberVisibility? IncludedMembers = null,
bool Static = false,
bool PreferParameterlessConstructors = true,
- bool AutoUserMappings = true
+ bool AutoUserMappings = true,
+ AggressiveInliningMode? AggressiveInliningMode = null
)
{
public const string DefaultMapperClassName = "Mapper";
@@ -44,6 +45,9 @@ public static TestSourceBuilderOptions WithRequiredMappingStrategy(RequiredMappi
public static TestSourceBuilderOptions WithMemberVisibility(MemberVisibility memberVisibility) =>
new(IncludedMembers: memberVisibility);
+ public static TestSourceBuilderOptions WithAggressiveInliningMode(AggressiveInliningMode aggressiveInliningMode) =>
+ new(AggressiveInliningMode: aggressiveInliningMode);
+
public static TestSourceBuilderOptions WithDisabledMappingConversion(params MappingConversionType[] conversionTypes)
{
var enabled = MappingConversionType.All;