$ShadowCopyDefines$
$ExtraDefines$
// <auto-generated />
$AdditionalLogic$
// this file must not be importing any namespaces
// we should use full names everywhere to avoid any potential naming conflicts, example: #1007, #778

// the namespace name must be in sync with WindowsDisassembler.BuildArguments
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)
        {
            // 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
            // before .NET Framework tries to load it for this method
#if NETFRAMEWORK
            using(new DirtyAssemblyResolveHelper())
#endif
                return AfterAssemblyLoadingAttached(args);
        }

        private static System.Int32 AfterAssemblyLoadingAttached(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);
            else
                host = new 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);

            try
            {
                // we are not using Runnable here in any direct way in order to avoid strong dependency Main<=>Runnable
                // which could cause the jitting/assembly loading to happen before we do anything
                // 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";
                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
                    : 0; // used when re-using generated exe without BDN (typically to repro a bug)

                if (args.Length == 0)
                {
                    host.WriteLine("You have not specified benchmark id (an integer) so the first benchmark will be executed.");
                }

#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 });
#endif
                return 0;
            }
            catch (System.Exception oom) when (oom is System.OutOfMemoryException || oom is System.Reflection.TargetInvocationException reflection && reflection.InnerException is System.OutOfMemoryException)
            {
                host.WriteLine();
                host.WriteLine("OutOfMemoryException!");
                host.WriteLine("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects.");
                host.WriteLine("If your benchmark allocates memory and keeps it alive, you are creating a memory leak.");
                host.WriteLine("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that.");
                host.WriteLine();
                host.WriteLine(oom.ToString());

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

                host.Dispose();
            }
        }
    }

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

        public void Dispose() => 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)
        {
#if SHADOWCOPY // used for LINQPad
            const System.String shadowCopyFolderPath = @"$ShadowCopyFolderPath$";

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

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

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

            if (!System.IO.File.Exists(guessedPath))
            {
                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.");

            return System.Reflection.Assembly.LoadFrom(guessedPath);
#endif // SHADOWCOPY
        }
    }
#endif // NETFRAMEWORK

    $DerivedTypes$
}
