Skip to content

Commit a21c3cc

Browse files
author
dd
committed
implemented support for method info caching also for open generic types
1 parent a7080eb commit a21c3cc

6 files changed

Lines changed: 100 additions & 21 deletions

File tree

src/MethodBoundaryAspect.Fody.UnitTests.NetFramework/AsyncGenericTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public void IfGenericClassIsWeavedForOnException_ThenExceptionThrownBetweenAwait
2323
}
2424

2525
[Fact]
26-
public void IfGenericClassIsWeavedForOnExit_ThenTaskIsPasedToAspectReturnValue()
26+
public void IfGenericClassIsWeavedForOnExit_ThenTaskIsPassedToAspectReturnValue()
2727
{
2828
// Act
2929
WeaveAssemblyClassAndLoad(OpenClass);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using FluentAssertions;
3+
using MethodBoundaryAspect.Fody.UnitTests.TestAssembly.NetFramework;
4+
using Xunit;
5+
6+
namespace MethodBoundaryAspect.Fody.UnitTests.NetFramework
7+
{
8+
public class GenericClassWithOpenTypeTests : MethodBoundaryAspectNetFrameworkTestBase
9+
{
10+
[Fact]
11+
public void IfCGenericClassWithOpenType_ThenTheAssemblyShouldBeValid()
12+
{
13+
// Arrange
14+
const string testMethodName = nameof(GenericClassWithOpenType.CallOpenTypeMethod);
15+
var testClassType = typeof(GenericClassWithOpenType);
16+
17+
// Act
18+
WeaveAssemblyMethodAndLoad(testClassType, nameof(GenericClassWithOpenType.OpenTypeMethod));
19+
var result = AssemblyLoader.InvokeMethod(testClassType.TypeInfo(), testMethodName, 42);
20+
21+
// Assert
22+
result.Should().Be("OpenTypeMethod");
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using MethodBoundaryAspect.Fody.Attributes;
2+
3+
namespace MethodBoundaryAspect.Fody.UnitTests.TestAssembly.NetFramework.Aspects
4+
{
5+
public class GenericClassWithOpenTypeSetReturnValueAspect : OnMethodBoundaryAspect
6+
{
7+
public override void OnExit(MethodExecutionArgs arg)
8+
{
9+
GenericClassWithOpenType.Result = arg.Method.Name;
10+
}
11+
}
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using MethodBoundaryAspect.Fody.UnitTests.TestAssembly.NetFramework.Aspects;
2+
3+
namespace MethodBoundaryAspect.Fody.UnitTests.TestAssembly.NetFramework
4+
{
5+
public class GenericClassWithOpenType
6+
{
7+
public static object Result { get; set; }
8+
9+
[GenericClassWithOpenTypeSetReturnValueAspect]
10+
public static T OpenTypeMethod<T>(T arg)
11+
{
12+
return default;
13+
}
14+
15+
public static void CallOpenTypeMethod(int value)
16+
{
17+
OpenTypeMethod(value);
18+
}
19+
}
20+
}

src/MethodBoundaryAspect.Fody/InstructionBlockChainCreator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public NamedInstructionBlockChain CreateMethodExecutionArgsInstance(
144144
var methodBaseVariable = _creator.CreateVariable(methodBaseTypeRef);
145145
InstructionBlock callGetCurrentMethodBlock;
146146
var variablePersistable = new VariablePersistable(methodBaseVariable);
147-
if (methodInfoCompileTimeWeaver?.IsEnabled != true || !methodInfoCompileTimeWeaver.CanWeave(method))
147+
if (methodInfoCompileTimeWeaver?.IsEnabled != true)
148148
{
149149
// fallback: slow GetCurrentMethod
150150
var methodBaseGetCurrentMethod = _referenceFinder.GetMethodReference(methodBaseTypeRef,

src/MethodBoundaryAspect.Fody/MethodInfoCompileTimeWeaver.cs

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -65,39 +65,49 @@ public void AddMethod(MethodDefinition method)
6565
public InstructionBlock PushMethodInfoOnStack(MethodDefinition method, VariablePersistable variablePersistable)
6666
{
6767
var fieldDefinition = _fieldsCache[method];
68-
var instruction = Instruction.Create(OpCodes.Ldsfld, fieldDefinition);
68+
var getMethodFromHandle2 = ImportGetMethodFromHandleArg2();
69+
70+
var instructions = new List<Instruction>();
71+
if (ContainsOpenTypeRecursive(method))
72+
{
73+
instructions.Add(Instruction.Create(OpCodes.Ldtoken, method));
74+
instructions.Add(Instruction.Create(OpCodes.Ldtoken, method.DeclaringType));
75+
instructions.Add(Instruction.Create(OpCodes.Call, getMethodFromHandle2));
76+
}
77+
else
78+
instructions.Add(Instruction.Create(OpCodes.Ldsfld, fieldDefinition));
79+
6980
var store = variablePersistable.Store(
70-
new InstructionBlock("", instruction),
81+
new InstructionBlock("", instructions),
7182
variablePersistable.PersistedType);
7283
return new InstructionBlock($"Load method info for '{method.Name}'", store.Instructions);
7384
}
7485

7586
public void Finish()
7687
{
77-
if (_fieldsCache.Any())
78-
CreateStaticCtor();
88+
CreateStaticCtor();
7989
}
80-
81-
public bool CanWeave(MethodDefinition method)
90+
91+
private static bool ContainsOpenTypeRecursive(MethodDefinition method)
8292
{
83-
// no support for open generic types
84-
if (IsOpenType(method))
85-
return false;
93+
if (ContainsOpenType(method))
94+
return true;
8695

8796
var parentType = method.DeclaringType;
8897
while (parentType != null)
8998
{
90-
if (IsOpenType(parentType))
91-
return false;
99+
if (ContainsOpenType(parentType))
100+
return true;
101+
92102
parentType = parentType.DeclaringType;
93103
}
94104

95-
return true;
105+
return false;
96106
}
97107

98-
private static bool IsOpenType(IGenericParameterProvider method)
108+
private static bool ContainsOpenType(IGenericParameterProvider method)
99109
{
100-
return method.GenericParameters.Any(x => x.IsGenericParameter);
110+
return method.GenericParameters.Any(x => x.ContainsGenericParameter);
101111
}
102112

103113
private string CreateIdentifier(MemberReference method)
@@ -120,15 +130,21 @@ private void CreateStaticCtor()
120130
var cctor = new MethodDefinition(".cctor", staticConstructorAttributes, typeReferenceVoid);
121131

122132
// taken from https://gist.github.com/jbevain/390902
123-
var getMethodFromHandle = ImportGetMethodFromHandle();
133+
var getMethodFromHandle = ImportGetMethodFromHandleArg1();
124134
var cctorInstructions = new List<Instruction>();
125135
foreach (var entry in _fieldsCache)
126136
{
137+
if (ContainsOpenTypeRecursive(entry.Key))
138+
continue; // method info has to be resolved during runtime so we don't need a cache entry
139+
127140
cctorInstructions.Add(Instruction.Create(OpCodes.Ldtoken, entry.Key));
128141
cctorInstructions.Add(Instruction.Create(OpCodes.Call, getMethodFromHandle));
129142
cctorInstructions.Add(Instruction.Create(OpCodes.Stsfld, entry.Value));
130143
}
131144

145+
if (!cctorInstructions.Any())
146+
return;
147+
132148
cctorInstructions.Add(Instruction.Create(OpCodes.Ret));
133149

134150
foreach (var methodInstruction in cctorInstructions)
@@ -153,18 +169,24 @@ private TypeReference GetTypeReference(string name)
153169
return _mainModule.TypeSystem.Void;
154170
default:
155171
{
156-
var splitted = name.Split('.');
157-
var namespaceName = string.Join(".", splitted.Take(splitted.Length - 1));
158-
var typeName = splitted.Last();
172+
var split = name.Split('.');
173+
var namespaceName = string.Join(".", split.Take(split.Length - 1));
174+
var typeName = split.Last();
159175
return new TypeReference(namespaceName, typeName, _mainModule, _mainModule.TypeSystem.CoreLibrary);
160176
}
161177
}
162178
}
163179

164-
private MethodReference ImportGetMethodFromHandle()
180+
private MethodReference ImportGetMethodFromHandleArg1()
165181
{
166182
return _mainModule.ImportReference(typeof(MethodBase)
167183
.GetMethod("GetMethodFromHandle", new[] {typeof(RuntimeMethodHandle)}));
168184
}
185+
186+
private MethodReference ImportGetMethodFromHandleArg2()
187+
{
188+
return _mainModule.ImportReference(typeof(MethodBase)
189+
.GetMethod("GetMethodFromHandle", new[] {typeof(RuntimeMethodHandle),typeof(RuntimeTypeHandle)}));
190+
}
169191
}
170192
}

0 commit comments

Comments
 (0)