Skip to content

Commit 3cc3cdb

Browse files
committed
Use AsmResolver instead of Mono.Cecil.
Run task after CoreCompile instead of AfterBuild.
1 parent cbc5ad9 commit 3cc3cdb

File tree

4 files changed

+47
-75
lines changed

4 files changed

+47
-75
lines changed

src/BenchmarkDotNet.Weaver/BenchmarkDotNet.Weaver.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
1111
<IncludeBuildOutput>false</IncludeBuildOutput>
1212
<NoWarn>$(NoWarn);NU5100;NU5128</NoWarn>
13+
<SignAssembly>false</SignAssembly>
14+
<DelaySign>false</DelaySign>
1315
</PropertyGroup>
1416

1517
<ItemGroup>
18+
<PackageReference Include="AsmResolver.DotNet" Version="5.5.1" />
1619
<PackageReference Include="Microsoft.Build.Framework" Version="17.12.6" />
1720
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.12.6" />
18-
<PackageReference Include="Mono.Cecil" Version="0.11.6" />
1921
</ItemGroup>
2022

2123
<ItemGroup>

src/BenchmarkDotNet.Weaver/buildTransitive/netstandard2.0/BenchmarkDotNet.Weaver.targets

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<UsingTask TaskName="BenchmarkDotNet.Weaver.WeaveAssemblyTask" AssemblyFile="$(MSBuildThisFileDirectory)..\..\tasks\netstandard2.0\BenchmarkDotNet.Weaver.dll" />
44

5-
<Target Name="WeaveAssemblies" AfterTargets="AfterBuild" >
6-
<WeaveAssemblyTask TargetDir="$(TargetDir)" TargetAssembly="$(TargetDir)$(TargetFileName)" />
5+
<Target Name="WeaveAssemblies" AfterTargets="CoreCompile" >
6+
<WeaveAssemblyTask TargetDir="$(IntermediateOutputPath)" TargetAssembly="$(IntermediateOutputPath)$(TargetFileName)" />
77
</Target>
88
</Project>

src/BenchmarkDotNet.Weaver/src/WeaveAssemblyTask.cs

Lines changed: 42 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
1+
using AsmResolver.DotNet;
2+
using AsmResolver.PE.DotNet.Metadata.Tables.Rows;
3+
using Microsoft.Build.Framework;
4+
using Microsoft.Build.Utilities;
15
using System;
26
using System.IO;
37
using System.Linq;
4-
using Microsoft.Build.Framework;
5-
using Microsoft.Build.Utilities;
6-
using Mono.Cecil;
78

89
namespace BenchmarkDotNet.Weaver;
910

10-
internal class CustomAssemblyResolver : DefaultAssemblyResolver
11-
{
12-
public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
13-
// NetStandard causes StackOverflow. https://github.com/jbevain/cecil/issues/573
14-
// Mscorlib fails to resolve in Visual Studio. https://github.com/jbevain/cecil/issues/966
15-
// We don't care about any types from runtime assemblies anyway, so just skip resolving them.
16-
=> name.Name is "netstandard" or "mscorlib" or "System.Runtime" or "System.Private.CoreLib"
17-
? null
18-
: base.Resolve(name, parameters);
19-
}
20-
2111
/// <summary>
2212
/// The Task used by MSBuild to weave the assembly.
2313
/// </summary>
@@ -45,72 +35,52 @@ public override bool Execute()
4535
{
4636
Log.LogError($"Assembly not found: {TargetAssembly}");
4737
return false;
48-
}
49-
50-
var resolver = new CustomAssemblyResolver();
51-
resolver.AddSearchDirectory(TargetDir);
52-
53-
// ReaderParameters { ReadWrite = true } is necessary to later write the file.
54-
// https://stackoverflow.com/questions/41840455/locked-target-assembly-with-mono-cecil-and-pcl-code-injection
55-
var readerParameters = new ReaderParameters
56-
{
57-
ReadWrite = true,
58-
AssemblyResolver = resolver
59-
};
38+
}
6039

61-
bool benchmarkMethodsImplAdjusted = false;
62-
try
63-
{
64-
using var module = ModuleDefinition.ReadModule(TargetAssembly, readerParameters);
65-
66-
foreach (var type in module.Types)
67-
{
68-
ProcessType(type, ref benchmarkMethodsImplAdjusted);
69-
}
70-
71-
// Write the modified assembly to file.
72-
module.Write();
73-
}
74-
catch (Exception e)
75-
{
76-
if (benchmarkMethodsImplAdjusted)
40+
// Load the assembly using AsmResolver.
41+
var module = ModuleDefinition.FromFile(TargetAssembly);
42+
43+
bool benchmarkMethodsImplAdjusted = false;
44+
try
45+
{
46+
foreach (var type in module.GetAllTypes())
7747
{
78-
Log.LogWarning($"Benchmark methods were found that require NoInlining, and assembly weaving failed.{Environment.NewLine}{e}");
79-
}
80-
}
48+
// We can skip non-public types as they are not valid for benchmarks.
49+
if (type.IsNotPublic)
50+
{
51+
continue;
52+
}
53+
54+
foreach (var method in type.Methods)
55+
{
56+
if (method.CustomAttributes.Any(IsBenchmarkAttribute))
57+
{
58+
var oldImpl = method.ImplAttributes;
59+
// Remove AggressiveInlining and add NoInlining.
60+
const MethodImplAttributes AggressiveInlining = (MethodImplAttributes) 512;
61+
method.ImplAttributes = (oldImpl & ~AggressiveInlining) | MethodImplAttributes.NoInlining;
62+
benchmarkMethodsImplAdjusted |= (oldImpl & MethodImplAttributes.NoInlining) == 0;
63+
}
64+
}
65+
}
66+
67+
// Write the modified assembly to file.
68+
module.Write(TargetAssembly);
69+
}
70+
catch (Exception e)
71+
{
72+
if (benchmarkMethodsImplAdjusted)
73+
{
74+
Log.LogWarning($"Benchmark methods were found that require NoInlining, and assembly weaving failed.{Environment.NewLine}{e}");
75+
}
76+
}
8177
return true;
8278
}
8379

84-
private static void ProcessType(TypeDefinition type, ref bool benchmarkMethodsImplAdjusted)
85-
{
86-
// We can skip non-public types as they are not valid for benchmarks.
87-
if (type.IsNotPublic)
88-
{
89-
return;
90-
}
91-
92-
// Remove AggressiveInlining and add NoInlining to all [Benchmark] methods.
93-
foreach (var method in type.Methods)
94-
{
95-
if (method.CustomAttributes.Any(IsBenchmarkAttribute))
96-
{
97-
var oldImpl = method.ImplAttributes;
98-
method.ImplAttributes = (oldImpl & ~MethodImplAttributes.AggressiveInlining) | MethodImplAttributes.NoInlining;
99-
benchmarkMethodsImplAdjusted |= (oldImpl & MethodImplAttributes.NoInlining) == 0;
100-
}
101-
}
102-
103-
// Recursively process nested types
104-
foreach (var nestedType in type.NestedTypes)
105-
{
106-
ProcessType(nestedType, ref benchmarkMethodsImplAdjusted);
107-
}
108-
}
109-
11080
private static bool IsBenchmarkAttribute(CustomAttribute attribute)
11181
{
11282
// BenchmarkAttribute is unsealed, so we need to walk its hierarchy.
113-
for (var attr = attribute.AttributeType; attr != null; attr = attr.Resolve()?.BaseType)
83+
for (var attr = attribute.Constructor.DeclaringType; attr != null; attr = attr.Resolve()?.BaseType)
11484
{
11585
if (attr.FullName == "BenchmarkDotNet.Attributes.BenchmarkAttribute")
11686
{

0 commit comments

Comments
 (0)