<commit_before>﻿using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Linq.Expressions;
using MonoMod.Utils;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using System.Linq;
using System.Runtime.InteropServices;

#if NETSTANDARD
using TypeOrTypeInfo = System.Reflection.TypeInfo;
using static System.Reflection.IntrospectionExtensions;
#else
using TypeOrTypeInfo = System.Type;
#endif

namespace MonoMod.Utils {
    public static class ReflectionHelper {

        public static readonly Dictionary<string, Assembly> AssemblyCache = new Dictionary<string, Assembly>();
        public static readonly Dictionary<MemberReference, MemberInfo> ResolveReflectionCache = new Dictionary<MemberReference, MemberInfo>();

        private static MemberInfo _Cache(MemberReference key, MemberInfo value) {
            if (key != null && value != null) {
                lock (ResolveReflectionCache) {
                    ResolveReflectionCache[key] = value;
                }
            }
            return value;
        }

        public static Type ResolveReflection(this TypeReference mref)
            => (_ResolveReflection(mref, null) as TypeOrTypeInfo).AsType();
        public static MethodBase ResolveReflection(this MethodReference mref)
            => _ResolveReflection(mref, null) as MethodBase;
        public static FieldInfo ResolveReflection(this FieldReference mref)
            => _ResolveReflection(mref, null) as FieldInfo;
        public static PropertyInfo ResolveReflection(this PropertyReference mref)
            => _ResolveReflection(mref, null) as PropertyInfo;
        public static EventInfo ResolveReflection(this EventReference mref)
            => _ResolveReflection(mref, null) as EventInfo;

        public static MemberInfo ResolveReflection(this MemberReference mref)
            => _ResolveReflection(mref, null);

        private static MemberInfo _ResolveReflection(MemberReference mref, Module module) {
            if (mref == null)
                return null;

            lock (ResolveReflectionCache) {
                if (ResolveReflectionCache.TryGetValue(mref, out MemberInfo cached) && cached != null)
                    return cached;
            }

            TypeOrTypeInfo type;

            // Special cases.
            if (mref is GenericParameter genParam) {
                // TODO: Handle GenericParameter in ResolveReflection.
                throw new NotSupportedException("ResolveReflection on GenericParameter currently not supported");
            }

            if (mref is MethodReference method && mref.DeclaringType is ArrayType) {
                // ArrayType holds special methods.
                type = _ResolveReflection(mref.DeclaringType, module) as TypeOrTypeInfo;
                // ... but all of the methods have the same MetadataToken. We couldn't compare it anyway.

                string methodID = method.GetFindableID(withType: false);
                MethodInfo found = type.AsType().GetMethods().First(m => m.GetFindableID(withType: false) == methodID);
                if (found != null)
                    return _Cache(mref, found);
            }

            TypeReference tscope =
                mref.DeclaringType ??
                mref as TypeReference ??
                throw new ArgumentException("MemberReference hasn't got a DeclaringType / isn't a TypeReference in itself");

            if (module == null) {
                string asmName;
                string moduleName;

                switch (tscope.Scope) {
                    case AssemblyNameReference asmNameRef:
                        asmName = asmNameRef.FullName;
                        moduleName = null;
                        break;

                    case ModuleDefinition moduleDef:
                        asmName = moduleDef.Assembly.FullName;
                        moduleName = moduleDef.Name;
                        break;

                    case ModuleReference moduleRef:
                        // TODO: Is this correct? It's what cecil itself is doing...
                        asmName = tscope.Module.Assembly.FullName;
                        moduleName = tscope.Module.Name;
                        break;

                    default:
                        throw new NotSupportedException($"Unsupported scope type {tscope.Scope.GetType().FullName}");
                }

                Assembly asm;
                lock (AssemblyCache) {
                    if (!AssemblyCache.TryGetValue(asmName, out asm))
                        AssemblyCache[asmName] = asm = Assembly.Load(new AssemblyName(asmName));
                }
                module = string.IsNullOrEmpty(moduleName) ? asm.GetModules()[0] : asm.GetModule(moduleName);
            }

            if (mref is TypeReference tref) {
                if (tref.FullName == "<Module>")
                    throw new ArgumentException("Type <Module> cannot be resolved to a runtime reflection type");

                if (mref is TypeSpecification ts) {
                    type = _ResolveReflection(ts.ElementType, null) as TypeOrTypeInfo;
                    if (type == null)
                        return null;

                    if (ts.IsByReference)
                        return ResolveReflectionCache[mref] = type.MakeByRefType().GetTypeInfo();

                    if (ts.IsPointer)
                        return ResolveReflectionCache[mref] = type.MakePointerType().GetTypeInfo();

                    if (ts.IsArray)
                        return ResolveReflectionCache[mref] = type.MakeArrayType((ts as ArrayType).Dimensions.Count).GetTypeInfo();

                    if (ts.IsGenericInstance)
                        return ResolveReflectionCache[mref] = type.MakeGenericType((ts as GenericInstanceType).GenericArguments.Select(arg => (_ResolveReflection(arg, null) as TypeOrTypeInfo).AsType()).ToArray()).GetTypeInfo();

                } else {
                    type = module.GetType(mref.FullName.Replace("/", "+"), false, false).GetTypeInfo();
#if !NETSTANDARD1_X
                    if (type == null)
                        type = module.GetTypes().FirstOrDefault(m => mref.Is(m));
#endif
                }

                return _Cache(mref, type);
            }

            bool typeless = mref.DeclaringType.FullName == "<Module>";

            MemberInfo member;<commit_msg>/git abuse/ Utils: Add type finding fallback to ResolveReflection<commit_after>﻿using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Linq.Expressions;
using MonoMod.Utils;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using System.Linq;
using System.Runtime.InteropServices;

#if NETSTANDARD
using TypeOrTypeInfo = System.Reflection.TypeInfo;
using static System.Reflection.IntrospectionExtensions;
#else
using TypeOrTypeInfo = System.Type;
#endif

namespace MonoMod.Utils {
    public static class ReflectionHelper {

        public static readonly Dictionary<string, Assembly> AssemblyCache = new Dictionary<string, Assembly>();
        public static readonly Dictionary<MemberReference, MemberInfo> ResolveReflectionCache = new Dictionary<MemberReference, MemberInfo>();

        private static MemberInfo _Cache(MemberReference key, MemberInfo value) {
            if (key != null && value != null) {
                lock (ResolveReflectionCache) {
                    ResolveReflectionCache[key] = value;
                }
            }
            return value;
        }

        public static Type ResolveReflection(this TypeReference mref)
            => (_ResolveReflection(mref, null) as TypeOrTypeInfo).AsType();
        public static MethodBase ResolveReflection(this MethodReference mref)
            => _ResolveReflection(mref, null) as MethodBase;
        public static FieldInfo ResolveReflection(this FieldReference mref)
            => _ResolveReflection(mref, null) as FieldInfo;
        public static PropertyInfo ResolveReflection(this PropertyReference mref)
            => _ResolveReflection(mref, null) as PropertyInfo;
        public static EventInfo ResolveReflection(this EventReference mref)
            => _ResolveReflection(mref, null) as EventInfo;

        public static MemberInfo ResolveReflection(this MemberReference mref)
            => _ResolveReflection(mref, null);

        private static MemberInfo _ResolveReflection(MemberReference mref, Module module) {
            if (mref == null)
                return null;

            lock (ResolveReflectionCache) {
                if (ResolveReflectionCache.TryGetValue(mref, out MemberInfo cached) && cached != null)
                    return cached;
            }

            TypeOrTypeInfo type;

            // Special cases.
            if (mref is GenericParameter genParam) {
                // TODO: Handle GenericParameter in ResolveReflection.
                throw new NotSupportedException("ResolveReflection on GenericParameter currently not supported");
            }

            if (mref is MethodReference method && mref.DeclaringType is ArrayType) {
                // ArrayType holds special methods.
                type = _ResolveReflection(mref.DeclaringType, module) as TypeOrTypeInfo;
                // ... but all of the methods have the same MetadataToken. We couldn't compare it anyway.

                string methodID = method.GetFindableID(withType: false);
                MethodInfo found = type.AsType().GetMethods().First(m => m.GetFindableID(withType: false) == methodID);
                if (found != null)
                    return _Cache(mref, found);
            }

            TypeReference tscope =
                mref.DeclaringType ??
                mref as TypeReference ??
                throw new ArgumentException("MemberReference hasn't got a DeclaringType / isn't a TypeReference in itself");

            if (module == null) {
                string asmName;
                string moduleName;

                switch (tscope.Scope) {
                    case AssemblyNameReference asmNameRef:
                        asmName = asmNameRef.FullName;
                        moduleName = null;
                        break;

                    case ModuleDefinition moduleDef:
                        asmName = moduleDef.Assembly.FullName;
                        moduleName = moduleDef.Name;
                        break;

                    case ModuleReference moduleRef:
                        // TODO: Is this correct? It's what cecil itself is doing...
                        asmName = tscope.Module.Assembly.FullName;
                        moduleName = tscope.Module.Name;
                        break;

                    default:
                        throw new NotSupportedException($"Unsupported scope type {tscope.Scope.GetType().FullName}");
                }

                Assembly asm;
                lock (AssemblyCache) {
                    if (!AssemblyCache.TryGetValue(asmName, out asm))
                        AssemblyCache[asmName] = asm = Assembly.Load(new AssemblyName(asmName));
                }
                module = string.IsNullOrEmpty(moduleName) ? asm.GetModules()[0] : asm.GetModule(moduleName);
            }

            if (mref is TypeReference tref) {
                if (tref.FullName == "<Module>")
                    throw new ArgumentException("Type <Module> cannot be resolved to a runtime reflection type");

                if (mref is TypeSpecification ts) {
                    type = _ResolveReflection(ts.ElementType, null) as TypeOrTypeInfo;
                    if (type == null)
                        return null;

                    if (ts.IsByReference)
                        return ResolveReflectionCache[mref] = type.MakeByRefType().GetTypeInfo();

                    if (ts.IsPointer)
                        return ResolveReflectionCache[mref] = type.MakePointerType().GetTypeInfo();

                    if (ts.IsArray)
                        return ResolveReflectionCache[mref] = type.MakeArrayType((ts as ArrayType).Dimensions.Count).GetTypeInfo();

                    if (ts.IsGenericInstance)
                        return ResolveReflectionCache[mref] = type.MakeGenericType((ts as GenericInstanceType).GenericArguments.Select(arg => (_ResolveReflection(arg, null) as TypeOrTypeInfo).AsType()).ToArray()).GetTypeInfo();

                } else {
                    type = module.GetType(mref.FullName.Replace("/", "+"), false, false).GetTypeInfo();
#if !NETSTANDARD1_X
                    if (type == null)
                        type = module.GetTypes().FirstOrDefault(m => mref.Is(m)).GetTypeInfo();
#endif
                }

                return _Cache(mref, type);
            }

            bool typeless = mref.DeclaringType.FullName == "<Module>";

            MemberInfo member;
{'fraction_non_alphanumeric': 0.08109784462707396, 'fraction_numerical': 0.0003101256008683517, 'mean_word_length': 1.8934499775684164, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '225381', 'n_tokens_mistral': 3110, 'n_tokens_neox': 2979, 'n_words': 799}