Skip to content
Open
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 @@ -81,7 +81,7 @@ public override string ToPresentation(object characteristicValue, Characteristic
{
// 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
18 changes: 12 additions & 6 deletions src/BenchmarkDotNet/Code/DeclarationsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ internal abstract class DeclarationsProvider

public virtual string WorkloadMethodReturnTypeName => WorkloadMethodReturnType.GetCorrectCSharpTypeName();

public virtual string WorkloadMethodDelegate(string passArguments) => Descriptor.WorkloadMethod.Name;
public string WorkloadMethodPrefix => Descriptor.WorkloadMethod.IsStatic ? Descriptor.WorkloadMethod.DeclaringType.GetCorrectCSharpTypeName() : "base";

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

public virtual string WorkloadMethodReturnTypeModifiers => null;

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

public virtual string ConsumeField => null;

Expand Down Expand Up @@ -150,9 +154,10 @@ internal class TaskDeclarationsProvider : VoidDeclarationsProvider
public TaskDeclarationsProvider(Descriptor descriptor) : base(descriptor) { }

public override string WorkloadMethodDelegate(string passArguments)
=> $"({passArguments}) => {{ BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments})); }}";
=> $"({passArguments}) => {{ BenchmarkDotNet.Helpers.AwaitHelper.GetResult({WorkloadMethodPrefix}.{Descriptor.WorkloadMethod.Name}({passArguments})); }}";

public override string GetWorkloadMethodCall(string passArguments) => $"BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments}))";
public override string GetWorkloadMethodCall(string passArguments)
=> $"BenchmarkDotNet.Helpers.AwaitHelper.GetResult({WorkloadMethodPrefix}.{Descriptor.WorkloadMethod.Name}({passArguments}))";

protected override Type WorkloadMethodReturnType => typeof(void);
}
Expand All @@ -167,8 +172,9 @@ public GenericTaskDeclarationsProvider(Descriptor descriptor) : base(descriptor)
protected override Type WorkloadMethodReturnType => Descriptor.WorkloadMethod.ReturnType.GetTypeInfo().GetGenericArguments().Single();

public override string WorkloadMethodDelegate(string passArguments)
=> $"({passArguments}) => {{ return BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments})); }}";
=> $"({passArguments}) => {{ return BenchmarkDotNet.Helpers.AwaitHelper.GetResult({WorkloadMethodPrefix}.{Descriptor.WorkloadMethod.Name}({passArguments})); }}";

public override string GetWorkloadMethodCall(string passArguments) => $"BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments}))";
public override string GetWorkloadMethodCall(string passArguments)
=> $"BenchmarkDotNet.Helpers.AwaitHelper.GetResult({WorkloadMethodPrefix}.{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 @@ -42,7 +42,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
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
60 changes: 30 additions & 30 deletions src/BenchmarkDotNet/Templates/BenchmarkProgram.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace BenchmarkDotNet.Autogenerated
public class UniqueProgramName // we need different name than typical "Program" to avoid problems with referencing "Program" types from benchmarked code, #691
{
$ExtraAttribute$
public static System.Int32 Main(System.String[] args)
public static global::System.Int32 Main(global::System.String[] args)
{
// this method MUST NOT have any dependencies to BenchmarkDotNet and any other external dlls! (CoreRT is exception from this rule)
// otherwise if LINQPad's shadow copy is enabled, we will not register for AssemblyLoading event
Expand All @@ -22,17 +22,17 @@ namespace BenchmarkDotNet.Autogenerated
return AfterAssemblyLoadingAttached(args);
}

private static System.Int32 AfterAssemblyLoadingAttached(System.String[] args)
private static global::System.Int32 AfterAssemblyLoadingAttached(global::System.String[] args)
{
BenchmarkDotNet.Engines.IHost host; // this variable name is used by CodeGenerator.GetCoreRtSwitch, do NOT change it
if (BenchmarkDotNet.Engines.AnonymousPipesHost.TryGetFileHandles(args, out System.String writeHandle, out System.String readHandle))
host = new BenchmarkDotNet.Engines.AnonymousPipesHost(writeHandle, readHandle);
global::BenchmarkDotNet.Engines.IHost host; // this variable name is used by CodeGenerator.GetCoreRtSwitch, do NOT change it
if (global::BenchmarkDotNet.Engines.AnonymousPipesHost.TryGetFileHandles(args, out global::System.String writeHandle, out global::System.String readHandle))
host = new global::BenchmarkDotNet.Engines.AnonymousPipesHost(writeHandle, readHandle);
else
host = new BenchmarkDotNet.Engines.NoAcknowledgementConsoleHost();
host = new global::BenchmarkDotNet.Engines.NoAcknowledgementConsoleHost();

// the first thing to do is to let diagnosers hook in before anything happens
// so all jit-related diagnosers can catch first jit compilation!
BenchmarkDotNet.Engines.HostExtensions.BeforeAnythingElse(host);
global::BenchmarkDotNet.Engines.HostExtensions.BeforeAnythingElse(host);

try
{
Expand All @@ -41,10 +41,10 @@ namespace BenchmarkDotNet.Autogenerated
// we have some jitting diagnosers and we want them to catch all the informations!!

// this variable name is used by CodeGenerator.GetCoreRtSwitch, do NOT change it
System.String benchmarkName = System.Linq.Enumerable.FirstOrDefault(System.Linq.Enumerable.Skip(System.Linq.Enumerable.SkipWhile(args, arg => arg != "--benchmarkName"), 1)) ?? "not provided";
BenchmarkDotNet.Diagnosers.RunMode diagnoserRunMode = (BenchmarkDotNet.Diagnosers.RunMode) System.Int32.Parse(System.Linq.Enumerable.FirstOrDefault(System.Linq.Enumerable.Skip(System.Linq.Enumerable.SkipWhile(args, arg => arg != "--diagnoserRunMode"), 1)) ?? "0");
System.Int32 id = args.Length > 0
? System.Int32.Parse(args[args.Length - 1]) // this variable name is used by CodeGenerator.GetCoreRtSwitch, do NOT change it
global::System.String benchmarkName = global::System.Linq.Enumerable.FirstOrDefault(global::System.Linq.Enumerable.Skip(global::System.Linq.Enumerable.SkipWhile(args, arg => arg != "--benchmarkName"), 1)) ?? "not provided";
global::BenchmarkDotNet.Diagnosers.RunMode diagnoserRunMode = (global::BenchmarkDotNet.Diagnosers.RunMode) global::System.Int32.Parse(global::System.Linq.Enumerable.FirstOrDefault(global::System.Linq.Enumerable.Skip(global::System.Linq.Enumerable.SkipWhile(args, arg => arg != "--diagnoserRunMode"), 1)) ?? "0");
global::System.Int32 id = args.Length > 0
? global::System.Int32.Parse(args[args.Length - 1]) // this variable name is used by CodeGenerator.GetCoreRtSwitch, do NOT change it
: 0; // used when re-using generated exe without BDN (typically to repro a bug)

if (args.Length == 0)
Expand All @@ -54,12 +54,12 @@ namespace BenchmarkDotNet.Autogenerated
#if NATIVEAOT
$NativeAotSwitch$
#else
System.Type type = typeof(BenchmarkDotNet.Autogenerated.UniqueProgramName).Assembly.GetType($"BenchmarkDotNet.Autogenerated.Runnable_{id}");
type.GetMethod("Run", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).Invoke(null, new System.Object[] { host, benchmarkName, diagnoserRunMode });
global::System.Type type = typeof(global::BenchmarkDotNet.Autogenerated.UniqueProgramName).Assembly.GetType($"BenchmarkDotNet.Autogenerated.Runnable_{id}");
type.GetMethod("Run", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.Static).Invoke(null, new global::System.Object[] { host, benchmarkName, diagnoserRunMode });
#endif
return 0;
}
catch (System.Exception oom) when (oom is System.OutOfMemoryException || oom is System.Reflection.TargetInvocationException reflection && reflection.InnerException is System.OutOfMemoryException)
catch (global::System.Exception oom) when (oom is global::System.OutOfMemoryException || oom is global::System.Reflection.TargetInvocationException reflection && reflection.InnerException is global::System.OutOfMemoryException)
{
host.WriteLine();
host.WriteLine("OutOfMemoryException!");
Expand All @@ -71,60 +71,60 @@ namespace BenchmarkDotNet.Autogenerated

return -1;
}
catch(System.Exception ex)
catch(global::System.Exception ex)
{
host.WriteLine();
host.WriteLine(ex.ToString());
return -1;
}
finally
{
BenchmarkDotNet.Engines.HostExtensions.AfterAll(host);
global::BenchmarkDotNet.Engines.HostExtensions.AfterAll(host);

host.Dispose();
}
}
}

#if NETFRAMEWORK
internal class DirtyAssemblyResolveHelper : System.IDisposable
internal class DirtyAssemblyResolveHelper : global::System.IDisposable
{
internal DirtyAssemblyResolveHelper() => System.AppDomain.CurrentDomain.AssemblyResolve += HelpTheFrameworkToResolveTheAssembly;
internal DirtyAssemblyResolveHelper() => global::System.AppDomain.CurrentDomain.AssemblyResolve += HelpTheFrameworkToResolveTheAssembly;

public void Dispose() => System.AppDomain.CurrentDomain.AssemblyResolve -= HelpTheFrameworkToResolveTheAssembly;
public void Dispose() => global::System.AppDomain.CurrentDomain.AssemblyResolve -= HelpTheFrameworkToResolveTheAssembly;

/// <summary>
/// according to https://msdn.microsoft.com/en-us/library/ff527268(v=vs.110).aspx
/// "the handler is invoked whenever the runtime fails to bind to an assembly by name."
/// </summary>
/// <returns>not null when we find it manually, null when can't help</returns>
private System.Reflection.Assembly HelpTheFrameworkToResolveTheAssembly(System.Object sender, System.ResolveEventArgs args)
private global::System.Reflection.Assembly HelpTheFrameworkToResolveTheAssembly(global::System.Object sender, global::System.ResolveEventArgs args)
{
#if SHADOWCOPY // used for LINQPad
const System.String shadowCopyFolderPath = @"$ShadowCopyFolderPath$";
const global::System.String shadowCopyFolderPath = @"$ShadowCopyFolderPath$";

System.String guessedPath = System.IO.Path.Combine(shadowCopyFolderPath, $"{new System.Reflection.AssemblyName(args.Name).Name}.dll");
global::System.String guessedPath = global::System.IO.Path.Combine(shadowCopyFolderPath, $"{new global::System.Reflection.AssemblyName(args.Name).Name}.dll");

return System.IO.File.Exists(guessedPath) ? System.Reflection.Assembly.LoadFrom(guessedPath) : null;
return global::System.IO.File.Exists(guessedPath) ? global::System.Reflection.Assembly.LoadFrom(guessedPath) : null;
#else
System.Reflection.AssemblyName fullName = new System.Reflection.AssemblyName(args.Name);
System.String simpleName = fullName.Name;
global::System.Reflection.AssemblyName fullName = new global::System.Reflection.AssemblyName(args.Name);
global::System.String simpleName = fullName.Name;

System.String guessedPath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, $"{simpleName}.dll");
global::System.String guessedPath = global::System.IO.Path.Combine(global::System.AppDomain.CurrentDomain.BaseDirectory, $"{simpleName}.dll");

if (!System.IO.File.Exists(guessedPath))
if (!global::System.IO.File.Exists(guessedPath))
{
System.Console.WriteLine($"// Wrong assembly binding redirects for {args.Name}.");
global::System.Console.WriteLine($"// Wrong assembly binding redirects for {args.Name}.");
return null; // we can't help, and we also don't call Assembly.Load which if fails comes back here, creates endless loop and causes StackOverflow
}

// the file is right there, but has most probably different version and there is no assembly binding redirect or there is a wrong one...
// so we just load it and ignore the version mismatch

// we warn the user about that, in case some Super User want to be aware of that
System.Console.WriteLine($"// Wrong assembly binding redirects for {simpleName}, loading it from disk anyway.");
global::System.Console.WriteLine($"// Wrong assembly binding redirects for {simpleName}, loading it from disk anyway.");

return System.Reflection.Assembly.LoadFrom(guessedPath);
return global::System.Reflection.Assembly.LoadFrom(guessedPath);
#endif // SHADOWCOPY
}
}
Expand Down
Loading