public static FindExecuteMethodsResult FindExecuteMethods(IEnumerable <System.Reflection.Assembly> assemblyList, BurstReflectionAssemblyOptions options)
        {
            var methodsToCompile    = new List <BurstCompileTarget>();
            var methodsToCompileSet = new HashSet <MethodInfo>();

            var valueTypes          = new List <TypeToVisit>();
            var interfaceToProducer = new Dictionary <Type, Type>();

            // This method can be called on a background thread, so we can't call Debug.Log etc.
            // Instead collect all the log messages and return them.
            var logMessages = new List <LogMessage>();

            //Debug.Log("Filtered Assembly List: " + string.Join(", ", assemblyList.Select(assembly => assembly.GetName().Name)));

            // Find all ways to execute job types (via producer attributes)
            var typesVisited = new HashSet <string>();
            var typesToVisit = new HashSet <string>();
            var allTypesAssembliesCollected = new HashSet <Type>();

            foreach (var assembly in assemblyList)
            {
                var types = new List <Type>();
                try
                {
                    var typesFromAssembly = assembly.GetTypes();
                    types.AddRange(typesFromAssembly);
                    foreach (var typeInAssembly in typesFromAssembly)
                    {
                        allTypesAssembliesCollected.Add(typeInAssembly);
                    }

                    // Collect all generic type instances (excluding indirect instances)
                    CollectGenericTypeInstances(assembly, x => true, types, allTypesAssembliesCollected);
                }
                catch (Exception ex)
                {
                    logMessages.Add(new LogMessage(LogType.Warning, "Unexpected exception while collecting types in assembly `" + assembly.FullName + "` Exception: " + ex));
                }

                for (var i = 0; i < types.Count; i++)
                {
                    var t = types[i];
                    if (typesToVisit.Add(t.FullName))
                    {
                        // Because the list of types returned by CollectGenericTypeInstances does not detect nested generic classes that are not
                        // used explicitly, we need to create them if a declaring type is actually used
                        // so for example if we have:
                        // class MyClass<T> { class MyNestedClass { } }
                        // class MyDerived : MyClass<int> { }
                        // The CollectGenericTypeInstances will return typically the type MyClass<int>, but will not list MyClass<int>.MyNestedClass
                        // So the following code is correcting this in order to fully query the full graph of generic instance types, including indirect types
                        var nestedTypes = t.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic);
                        foreach (var nestedType in nestedTypes)
                        {
                            if (t.IsGenericType && !t.IsGenericTypeDefinition)
                            {
                                var parentGenericTypeArguments = t.GetGenericArguments();
                                // Only create nested types that are closed generic types (full generic instance types)
                                // It happens if for example the parent class is `class MClass<T> { class MyNestedGeneric<T1> {} }`
                                // In that case, MyNestedGeneric<T1> is opened in the context of MClass<int>, so we don't process them
                                if (nestedType.GetGenericArguments().Length == parentGenericTypeArguments.Length)
                                {
                                    try
                                    {
                                        var instanceNestedType = nestedType.MakeGenericType(parentGenericTypeArguments);
                                        types.Add(instanceNestedType);
                                    }
                                    catch (Exception ex)
                                    {
                                        var error = $"Unexpected Burst Inspector error. Invalid generic type instance. Trying to instantiate the generic type {nestedType.FullName} with the generic arguments <{string.Join(", ", parentGenericTypeArguments.Select(x => x.FullName))}> is not supported: {ex}";
                                        logMessages.Add(new LogMessage(LogType.Warning, error));
                                    }
                                }
                            }
                            else
                            {
                                types.Add(nestedType);
                            }
                        }
                    }
                }

                foreach (var t in types)
                {
                    // If the type has been already visited, don't try to visit it
                    if (!typesVisited.Add(t.FullName) || (t.IsGenericTypeDefinition && !t.IsInterface))
                    {
                        continue;
                    }

                    try
                    {
                        // collect methods with types having a [BurstCompile] attribute
                        bool visitStaticMethods = HasBurstCompileAttribute(t);
                        bool isValueType        = false;

                        if (t.IsInterface)
                        {
                            object[] attrs = t.GetCustomAttributes(typeof(JobProducerTypeAttribute), false);
                            if (attrs.Length == 0)
                            {
                                continue;
                            }

                            JobProducerTypeAttribute attr = (JobProducerTypeAttribute)attrs[0];

                            interfaceToProducer.Add(t, attr.ProducerType);

                            //Debug.Log($"{t} has producer {attr.ProducerType}");
                        }
                        else if (t.IsValueType)
                        {
                            // NOTE: Make sure that we don't use a value type generic definition (e.g `class Outer<T> { struct Inner { } }`)
                            // We are only working on plain type or generic type instance!
                            if (!t.IsGenericTypeDefinition)
                            {
                                isValueType = true;
                            }
                        }

                        if (isValueType || visitStaticMethods)
                        {
                            valueTypes.Add(new TypeToVisit(t, visitStaticMethods));
                        }
                    }
                    catch (Exception ex)
                    {
                        logMessages.Add(new LogMessage(LogType.Warning,
                                                       "Unexpected exception while inspecting type `" + t +
                                                       "` IsConstructedGenericType: " + t.IsConstructedGenericType +
                                                       " IsGenericTypeDef: " + t.IsGenericTypeDefinition +
                                                       " IsGenericParam: " + t.IsGenericParameter +
                                                       " Exception: " + ex));
                    }
                }
            }

            //Debug.Log($"Mapped {interfaceToProducer.Count} producers; {valueTypes.Count} value types");

            // Revisit all types to find things that are compilable using the above producers.
            foreach (var typePair in valueTypes)
            {
                var type = typePair.Type;

                // collect static [BurstCompile] methods
                if (typePair.CollectStaticMethods)
                {
                    try
                    {
                        var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
                        foreach (var method in methods)
                        {
                            if (!method.ContainsGenericParameters && HasBurstCompileAttribute(method))
                            {
                                var target = new BurstCompileTarget(method, type, null, true);
                                methodsToCompile.Add(target);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        logMessages.Add(new LogMessage(ex));
                    }
                }

                // If the type is not a value type, we don't need to proceed with struct Jobs
                if (!type.IsValueType)
                {
                    continue;
                }

                // Otherwise try to find if we have an interface producer setup on the class
                foreach (var interfaceType in type.GetInterfaces())
                {
                    var genericLessInterface = interfaceType;
                    if (interfaceType.IsGenericType)
                    {
                        genericLessInterface = interfaceType.GetGenericTypeDefinition();
                    }

                    Type foundProducer;
                    if (interfaceToProducer.TryGetValue(genericLessInterface, out foundProducer))
                    {
                        var genericParams = new List <Type> {
                            type
                        };
                        if (interfaceType.IsGenericType)
                        {
                            genericParams.AddRange(interfaceType.GenericTypeArguments);
                        }

                        try
                        {
                            var executeType   = foundProducer.MakeGenericType(genericParams.ToArray());
                            var executeMethod = executeType.GetMethod("Execute", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
                            if (executeMethod == null)
                            {
                                throw new InvalidOperationException($"Burst reflection error. The type `{executeType}` does not contain an `Execute` method");
                            }

                            // We will not try to record more than once a method in the methods to compile
                            // This can happen if a job interface is inheriting from another job interface which are using in the end the same
                            // job producer type
                            if (methodsToCompileSet.Add(executeMethod))
                            {
                                var target = new BurstCompileTarget(executeMethod, type, interfaceType, false);
                                methodsToCompile.Add(target);
                            }
                        }
                        catch (Exception ex)
                        {
                            logMessages.Add(new LogMessage(ex));
                        }
                    }
                }
            }

            return(new FindExecuteMethodsResult(methodsToCompile, logMessages));
        }
        // The TypeCache API was added in 2019.2. So there are two versions of FindExecuteMethods,
        // one that uses TypeCache and one that doesn't.
#if UNITY_2019_2_OR_NEWER
        public static FindExecuteMethodsResult FindExecuteMethods(List <System.Reflection.Assembly> assemblyList, BurstReflectionAssemblyOptions options)
        {
            var methodsToCompile    = new List <BurstCompileTarget>();
            var methodsToCompileSet = new HashSet <MethodInfo>();
            var logMessages         = new List <LogMessage>();
            var interfaceToProducer = new Dictionary <Type, Type>();

            var assemblySet = new HashSet <System.Reflection.Assembly>(assemblyList);

            void AddTarget(BurstCompileTarget target)
            {
                // We will not try to record more than once a method in the methods to compile
                // This can happen if a job interface is inheriting from another job interface which are using in the end the same
                // job producer type
                if (!target.IsStaticMethod && !methodsToCompileSet.Add(target.Method))
                {
                    return;
                }

                if (options.HasFlag(BurstReflectionAssemblyOptions.ExcludeTestAssemblies) &&
                    target.JobType.Assembly.GetReferencedAssemblies().Any(x => IsNUnitDll(x.Name)))
                {
                    return;
                }

                methodsToCompile.Add(target);
            }

            var staticMethodTypes = new HashSet <Type>();

            // -------------------------------------------
            // Find job structs using TypeCache.
            // -------------------------------------------

            var jobProducerImplementations = TypeCache.GetTypesWithAttribute <JobProducerTypeAttribute>();

            foreach (var jobProducerImplementation in jobProducerImplementations)
            {
                var attrs = jobProducerImplementation.GetCustomAttributes(typeof(JobProducerTypeAttribute), false);
                if (attrs.Length == 0)
                {
                    continue;
                }

                staticMethodTypes.Add(jobProducerImplementation);

                var attr = (JobProducerTypeAttribute)attrs[0];
                interfaceToProducer.Add(jobProducerImplementation, attr.ProducerType);
            }

            foreach (var jobProducerImplementation in jobProducerImplementations)
            {
                if (!jobProducerImplementation.IsInterface)
                {
                    continue;
                }

                var jobTypes = TypeCache.GetTypesDerivedFrom(jobProducerImplementation);

                foreach (var jobType in jobTypes)
                {
                    if (jobType.IsGenericType || !jobType.IsValueType)
                    {
                        continue;
                    }

                    ScanJobType(jobType, interfaceToProducer, logMessages, AddTarget);
                }
            }

            // -------------------------------------------
            // Find static methods using TypeCache.
            // -------------------------------------------

            void AddStaticMethods(TypeCache.MethodCollection methods)
            {
                foreach (var method in methods)
                {
                    if (HasBurstCompileAttribute(method.DeclaringType))
                    {
                        staticMethodTypes.Add(method.DeclaringType);

                        // NOTE: Make sure that we don't use a value type generic definition (e.g `class Outer<T> { struct Inner { } }`)
                        // We are only working on plain type or generic type instance!
                        if (!method.DeclaringType.IsGenericTypeDefinition &&
                            method.IsStatic &&
                            !method.ContainsGenericParameters)
                        {
                            AddTarget(new BurstCompileTarget(method, method.DeclaringType, null, true));
                        }
                    }
                }
            }

            // Add [BurstCompile] static methods.
            AddStaticMethods(TypeCache.GetMethodsWithAttribute <BurstCompileAttribute>());

            // Add [TestCompiler] static methods.
            if (!options.HasFlag(BurstReflectionAssemblyOptions.ExcludeTestAssemblies))
            {
                var testCompilerAttributeType = Type.GetType("Burst.Compiler.IL.Tests.TestCompilerAttribute, Unity.Burst.Tests.UnitTests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
                if (testCompilerAttributeType != null)
                {
                    AddStaticMethods(TypeCache.GetMethodsWithAttribute(testCompilerAttributeType));
                }
            }

            // -------------------------------------------
            // Find job types and static methods based on
            // generic instances types. These will not be
            // found by the TypeCache scanning above.
            // -------------------------------------------
            FindExecuteMethodsForGenericInstances(
                assemblySet,
                staticMethodTypes,
                interfaceToProducer,
                AddTarget,
                logMessages);

            return(new FindExecuteMethodsResult(methodsToCompile, logMessages));
        }