Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public override string ToPresentation(object? characteristicValue, Characteristi
{
// TODO: DO NOT hardcode Characteristic suffix
string id = characteristic.Id;
string type = characteristic.DeclaringType.FullName!;
string type = characteristic.DeclaringType.GetCorrectCSharpTypeName();
string value = SourceCodeHelper.ToSourceCode(characteristicValue);
return $"{type}.{id}Characteristic[job] = {value}";
}
Expand Down
17 changes: 5 additions & 12 deletions src/BenchmarkDotNet/Code/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ internal static string Generate(BuildPartition buildPartition)
.Replace("$IterationSetupMethodName$", provider.IterationSetupMethodName)
.Replace("$IterationCleanupMethodName$", provider.IterationCleanupMethodName)
.Replace("$JobSetDefinition$", GetJobsSetDefinition(benchmark))
.Replace("$ParamsInitializer$", GetParamsInitializer(benchmark))
.Replace("$ParamsContent$", GetParamsContent(benchmark))
.Replace("$ArgumentsDefinition$", GetArgumentsDefinition(benchmark))
.Replace("$DeclareArgumentFields$", GetDeclareArgumentFields(benchmark))
.Replace("$InitializeArgumentFields$", GetInitializeArgumentFields(benchmark)).Replace("$LoadArguments$", GetLoadArguments(benchmark))
.Replace("$InitializeArgumentFields$", GetInitializeArgumentFields(benchmark))
.Replace("$LoadArguments$", GetLoadArguments(benchmark))
.Replace("$PassArguments$", passArguments)
.Replace("$EngineFactoryType$", GetEngineFactoryTypeName(benchmark))
.Replace("$MeasureExtraStats$", buildInfo.Config.HasExtraStatsDiagnoser() ? "true" : "false")
Expand Down Expand Up @@ -159,21 +159,14 @@ private static DeclarationsProvider GetDeclarationsProvider(Descriptor descripto
return new SyncDeclarationsProvider(descriptor);
}

private static string GetParamsInitializer(BenchmarkCase benchmarkCase)
=> string.Join(
", ",
benchmarkCase.Parameters.Items
.Where(parameter => !parameter.IsArgument && !parameter.IsStatic)
.Select(parameter => $"{parameter.Name} = default"));

// internal for tests

internal static string GetParamsContent(BenchmarkCase benchmarkCase)
=> string.Join(
string.Empty,
benchmarkCase.Parameters.Items
.Where(parameter => !parameter.IsArgument)
.Select(parameter => $"{(parameter.IsStatic ? "" : "instance.")}{parameter.Name} = {parameter.ToSourceCode()};"));
.Select(parameter => $"{(parameter.IsStatic ? benchmarkCase.Descriptor.Type.GetCorrectCSharpTypeName() : "base")}.{parameter.Name} = {parameter.ToSourceCode()};"));

private static string GetArgumentsDefinition(BenchmarkCase benchmarkCase)
=> string.Join(
Expand All @@ -191,13 +184,13 @@ private static string GetInitializeArgumentFields(BenchmarkCase benchmarkCase)
=> string.Join(
Environment.NewLine,
benchmarkCase.Descriptor.WorkloadMethod.GetParameters()
.Select((parameter, index) => $"__argField{index} = {benchmarkCase.Parameters.GetArgument(parameter.Name).ToSourceCode()};")); // we init the fields in ctor to provoke all possible allocations and overhead of other type
.Select((parameter, index) => $"this.__argField{index} = {benchmarkCase.Parameters.GetArgument(parameter.Name).ToSourceCode()};")); // we init the fields in ctor to provoke all possible allocations and overhead of other type

private static string GetLoadArguments(BenchmarkCase benchmarkCase)
=> string.Join(
Environment.NewLine,
benchmarkCase.Descriptor.WorkloadMethod.GetParameters()
.Select((parameter, index) => $"{(parameter.ParameterType.IsByRef ? "ref" : string.Empty)} {parameter.ParameterType.GetCorrectCSharpTypeName()} arg{index} = {(parameter.ParameterType.IsByRef ? "ref" : string.Empty)} __argField{index};"));
.Select((parameter, index) => $"{(parameter.ParameterType.IsByRef ? "ref" : string.Empty)} {parameter.ParameterType.GetCorrectCSharpTypeName()} arg{index} = {(parameter.ParameterType.IsByRef ? "ref" : string.Empty)} this.__argField{index};"));

private static string GetPassArguments(BenchmarkCase benchmarkCase)
=> string.Join(
Expand Down
21 changes: 12 additions & 9 deletions src/BenchmarkDotNet/Code/DeclarationsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ internal abstract class DeclarationsProvider

public string IterationCleanupMethodName => Descriptor.IterationCleanupMethod?.Name ?? EmptyAction;

public virtual string GetWorkloadMethodCall(string passArguments) => $"{Descriptor.WorkloadMethod.Name}({passArguments})";
public abstract string GetWorkloadMethodCall(string passArguments);

protected static string GetMethodPrefix(MethodInfo method)
=> method.IsStatic ? method.DeclaringType.GetCorrectCSharpTypeName() : "base";

private string GetMethodName(MethodInfo method)
{
Expand All @@ -41,22 +44,22 @@ private string GetMethodName(MethodInfo method)
(method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>) ||
method.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>))))
{
return $"() => BenchmarkDotNet.Helpers.AwaitHelper.GetResult({method.Name}())";
return $"() => global::BenchmarkDotNet.Helpers.AwaitHelper.GetResult({GetMethodPrefix(Descriptor.WorkloadMethod)}.{method.Name}())";
}

return method.Name;
return $"{GetMethodPrefix(Descriptor.WorkloadMethod)}.{method.Name}";
}
}

internal class SyncDeclarationsProvider : DeclarationsProvider
internal class SyncDeclarationsProvider(Descriptor descriptor) : DeclarationsProvider(descriptor)
{
public SyncDeclarationsProvider(Descriptor descriptor) : base(descriptor) { }
public override string GetWorkloadMethodCall(string passArguments)
=> $"{GetMethodPrefix(Descriptor.WorkloadMethod)}.{Descriptor.WorkloadMethod.Name}({passArguments})";
}

internal class AsyncDeclarationsProvider : DeclarationsProvider
internal class AsyncDeclarationsProvider(Descriptor descriptor) : DeclarationsProvider(descriptor)
{
public AsyncDeclarationsProvider(Descriptor descriptor) : base(descriptor) { }

public override string GetWorkloadMethodCall(string passArguments) => $"BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments}))";
public override string GetWorkloadMethodCall(string passArguments)
=> $"global::BenchmarkDotNet.Helpers.AwaitHelper.GetResult({GetMethodPrefix(Descriptor.WorkloadMethod)}.{Descriptor.WorkloadMethod.Name}({passArguments}))";
}
}
18 changes: 13 additions & 5 deletions src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using BenchmarkDotNet.Attributes;

namespace BenchmarkDotNet.Extensions
Expand Down Expand Up @@ -36,7 +37,7 @@ public static bool IsInitOnly(this PropertyInfo propertyInfo)
/// <summary>
/// returns type name which can be used in generated C# code
/// </summary>
internal static string GetCorrectCSharpTypeName(this Type type, bool includeNamespace = true, bool includeGenericArgumentsNamespace = true)
internal static string GetCorrectCSharpTypeName(this Type type, bool includeNamespace = true, bool includeGenericArgumentsNamespace = true, bool prefixWithGlobal = true)
{
while (!(type.IsPublic || type.IsNestedPublic) && type.BaseType != null)
type = type.BaseType;
Expand All @@ -49,16 +50,23 @@ internal static string GetCorrectCSharpTypeName(this Type type, bool includeName
return "void";
if (type == typeof(void*))
return "void*";

string prefix = "";

if (!string.IsNullOrEmpty(type.Namespace) && includeNamespace)
{
prefix += type.Namespace + ".";

if (prefixWithGlobal)
prefix = $"global::{prefix}";
}

if (type.GetTypeInfo().IsGenericParameter)
return type.Name;

if (type.IsArray)
{
var typeName = GetCorrectCSharpTypeName(type.GetElementType());
var typeName = GetCorrectCSharpTypeName(type.GetElementType(), includeNamespace, includeGenericArgumentsNamespace, prefixWithGlobal);
var parts = typeName.Split(['['], count: 2);

string repr = parts[0] + '[' + new string(',', type.GetArrayRank() - 1) + ']';
Expand All @@ -68,11 +76,11 @@ internal static string GetCorrectCSharpTypeName(this Type type, bool includeName
return repr;
}

return prefix + string.Join(".", GetNestedTypeNames(type, includeGenericArgumentsNamespace).Reverse());
return prefix + string.Join(".", GetNestedTypeNames(type, includeGenericArgumentsNamespace, prefixWithGlobal).Reverse());
}

// from most nested to least
private static IEnumerable<string> GetNestedTypeNames(Type type, bool includeGenericArgumentsNamespace)
private static IEnumerable<string> GetNestedTypeNames(Type type, bool includeGenericArgumentsNamespace, bool prefixWithGlobal)
{
var allTypeParameters = new Stack<Type>(type.GetGenericArguments());

Expand All @@ -92,7 +100,7 @@ private static IEnumerable<string> GetNestedTypeNames(Type type, bool includeGen
.Select(_ => allTypeParameters.Pop())
.Reverse();

var args = string.Join(", ", typeParameters.Select(T => GetCorrectCSharpTypeName(T, includeGenericArgumentsNamespace, includeGenericArgumentsNamespace)));
var args = string.Join(", ", typeParameters.Select(T => GetCorrectCSharpTypeName(T, includeGenericArgumentsNamespace, includeGenericArgumentsNamespace, prefixWithGlobal)));
name = $"{mainName}<{args}>";
}

Expand Down
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Helpers/FolderNameHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public static string ToFolderName(object? value)
// we can't simply use type.FullName, because for generics it's too long
// example: typeof(List<int>).FullName => "System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"
public static string ToFolderName(Type type, bool includeNamespace = true, bool includeGenericArgumentsNamespace = false)
=> Escape(new StringBuilder(type.GetCorrectCSharpTypeName(includeNamespace, includeGenericArgumentsNamespace)));
=> Escape(new StringBuilder(type.GetCorrectCSharpTypeName(includeNamespace, includeGenericArgumentsNamespace, prefixWithGlobal: false)));

private static string Escape(StringBuilder builder)
{
Expand Down
6 changes: 3 additions & 3 deletions src/BenchmarkDotNet/Parameters/SmartParamBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public string ToSourceCode()
else
{
// If the source member is non-static, we mustn't include the type name, as this would be a compiler error when accessing a non-static source member in the base class of this generated type.
methodCall = source.Name;
methodCall = $"base.{source.Name}";
}

// we do something like enumerable.ElementAt(sourceIndex)[argumentIndex];
Expand Down Expand Up @@ -157,12 +157,12 @@ public string ToSourceCode()
{
string cast = $"({parameterType.GetCorrectCSharpTypeName()})"; // it's an object so we need to cast it to the right type

string instancePrefix = method.IsStatic ? source.DeclaringType.GetCorrectCSharpTypeName() : "instance";
string callPrefix = method.IsStatic ? source.DeclaringType.GetCorrectCSharpTypeName() : "base";

string callPostfix = source is PropertyInfo ? string.Empty : "()";

// we so something like enumerable.ElementAt(index);
return $"{cast}BenchmarkDotNet.Parameters.ParameterExtractor.GetParameter({instancePrefix}.{source.Name}{callPostfix}, {index});";
return $"{cast}BenchmarkDotNet.Parameters.ParameterExtractor.GetParameter({callPrefix}.{source.Name}{callPostfix}, {index});";
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos)
var benchmarkWithHighestIdForGivenType = benchmarkRunInfo.BenchmarksCases.Last();
if (benchmarkToBuildResult[benchmarkWithHighestIdForGivenType].Id.Value <= idToResume)
{
compositeLogger.WriteLineInfo($"Skipping {benchmarkRunInfo.BenchmarksCases.Length} benchmark(s) defined by {benchmarkRunInfo.Type.GetCorrectCSharpTypeName()}.");
compositeLogger.WriteLineInfo($"Skipping {benchmarkRunInfo.BenchmarksCases.Length} benchmark(s) defined by {benchmarkRunInfo.Type.GetCorrectCSharpTypeName(prefixWithGlobal: false)}.");
continue;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Running/Descriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public Descriptor(

public bool HasCategory(string category) => Categories.Any(c => c.EqualsWithIgnoreCase(category));

public string GetFilterName() => $"{Type.GetCorrectCSharpTypeName(includeGenericArgumentsNamespace: false)}.{WorkloadMethod.Name}";
public string GetFilterName() => $"{Type.GetCorrectCSharpTypeName(includeGenericArgumentsNamespace: false, prefixWithGlobal: false)}.{WorkloadMethod.Name}";

public bool Equals(Descriptor? other) => GetFilterName().Equals(other?.GetFilterName());

Expand Down
Loading