private TypeMemo AddStaticForwarders(TypeDefinition systemType)
        {
            var mod          = systemType.Module;
            var intPtrRef    = mod.ImportReference(typeof(IntPtr));
            var intPtrToVoid = mod.ImportReference(intPtrRef.Resolve().Methods.FirstOrDefault(x => x.Name == nameof(IntPtr.ToPointer)));

            TypeMemo memo = default;

            memo.m_SystemType = systemType;
            memo.m_Wrappers   = new MethodDefinition[3];

            for (int i = 0; i < MethodNames.Length; ++i)
            {
                var name      = MethodNames[i];
                var methodDef = new MethodDefinition(GeneratedMethodNames[i], MethodAttributes.Static | MethodAttributes.Private, mod.ImportReference(typeof(void)));
                methodDef.Parameters.Add(new ParameterDefinition("self", ParameterAttributes.None, intPtrRef));
                methodDef.Parameters.Add(new ParameterDefinition("state", ParameterAttributes.None, intPtrRef));

                var targetMethod = systemType.Methods.FirstOrDefault(x => x.Name == name && x.Parameters.Count == 1).Resolve();

                // Transfer any BurstCompile attribute from target function to the forwarding wrapper
                var burstAttribute = targetMethod.CustomAttributes.FirstOrDefault(x => x.Constructor.DeclaringType.Name == nameof(BurstCompileAttribute));
                if (burstAttribute != null)
                {
                    methodDef.CustomAttributes.Add(new CustomAttribute(burstAttribute.Constructor, burstAttribute.GetBlob()));
                    memo.m_BurstCompileBits |= 1 << i;
                }

                var processor = methodDef.Body.GetILProcessor();

                processor.Emit(OpCodes.Ldarga, 0);
                processor.Emit(OpCodes.Call, intPtrToVoid);
                processor.Emit(OpCodes.Ldarga, 1);
                processor.Emit(OpCodes.Call, intPtrToVoid);
                processor.Emit(OpCodes.Call, targetMethod);
                processor.Emit(OpCodes.Ret);

                systemType.Methods.Add(methodDef);
                memo.m_Wrappers[i] = methodDef;
            }

            return(memo);
        }
        private TypeMemo AddStaticForwarders(TypeDefinition systemType)
        {
            var mod          = systemType.Module;
            var intPtrRef    = mod.ImportReference(typeof(IntPtr));
            var intPtrToVoid = mod.ImportReference(intPtrRef.Resolve().Methods.FirstOrDefault(x => x.Name == nameof(IntPtr.ToPointer)));

            TypeMemo memo = default;

            memo.m_SystemType = systemType;
            memo.m_Wrappers   = new MethodDefinition[5];

            var hasStartStop = systemType.Interfaces.Any((x) => x.InterfaceType.FullName == "Unity.Entities.ISystemBaseStartStop");

            for (int i = 0; i < MethodNames.Length; ++i)
            {
                var name     = MethodNames[i];
                var fullName = MethodFullNames[i]; // Cecil sees interface method names from other assemblies as the full namespaced name

                if (!hasStartStop && i >= 3)
                {
                    break;
                }

                var methodDef = new MethodDefinition(GeneratedPrefix + name, MethodAttributes.Static | MethodAttributes.Assembly, mod.ImportReference(typeof(void)));
                methodDef.Parameters.Add(new ParameterDefinition("self", ParameterAttributes.None, intPtrRef));
                methodDef.Parameters.Add(new ParameterDefinition("state", ParameterAttributes.None, intPtrRef));

                var targetMethod = systemType.Methods.FirstOrDefault(x => x.Parameters.Count == 1 && (x.Name == name || x.Name == fullName));
                if (targetMethod == null)
                {
                    continue;
                }

                // Transfer any BurstCompile attribute from target function to the forwarding wrapper
                var burstAttribute = targetMethod.CustomAttributes.FirstOrDefault(x => x.Constructor.DeclaringType.Name == nameof(BurstCompileAttribute));
                if (burstAttribute != null)
                {
                    methodDef.CustomAttributes.Add(new CustomAttribute(burstAttribute.Constructor, burstAttribute.GetBlob()));
                    memo.m_BurstCompileBits |= 1 << i;
                }

#if UNITY_DOTSRUNTIME
                // Burst CompileFunctionPointer in DOTS Runtime will not currently decorate methods as [MonoPInvokeCallback]
                // so we add that here until that is supported
                var monoPInvokeCallbackAttributeConstructor = typeof(Jobs.MonoPInvokeCallbackAttribute).GetConstructor(Type.EmptyTypes);
                methodDef.CustomAttributes.Add(new CustomAttribute(mod.ImportReference(monoPInvokeCallbackAttributeConstructor)));
#endif

                var processor = methodDef.Body.GetILProcessor();

                processor.Emit(OpCodes.Ldarga, 0);
                processor.Emit(OpCodes.Call, intPtrToVoid);
                processor.Emit(OpCodes.Ldarga, 1);
                processor.Emit(OpCodes.Call, intPtrToVoid);
                processor.Emit(OpCodes.Call, targetMethod);
                processor.Emit(OpCodes.Ret);

                systemType.Methods.Add(methodDef);
                memo.m_Wrappers[i] = methodDef;
            }

            return(memo);
        }