.Net 7 的Native AOT为啥不支持反射,你知道吗

楔子

Native AOT为啥不支持反射呢,问题萦绕脑海?微软官方说不支持运行时生成的代码,这很难让人彻底理解它。
本篇从源码层面来解读下。

代码

上一段简单的代码例子

public class A
        {
            public void FirstMethod()
            {

            }
       
        static void Main(string[] args)
        {
            A a = new A();    
            Type type =a.GetType();
            MethodInfo[] methodinfo= type.GetMethods();
            foreach (MethodInfo method in methodinfo)
            {
                Console.WriteLine(method.Name);
            }
        }

Debug

一:对上面的代码进行ILC调试的时候,ILC控制台直接报错,因为用到了反射,具体错误如下:
fc9681a1361556a7e04b1611059e097a.png

这个错误很明显是它告诉你调用了

System.Type.GetMethods()这个函数导致的。而这个函数刚好就是反射获取方法。那么这个错误从何而来呢?

:为了研究这个错误,可以整个ILC项目搜索下这个错误的字符串,其中有一个字符串:Trim analysis warning可以找到错误的结果。搜索它之后,很快在Logger.cs这个文件里面找到了相同的字符串。而这个字符串在Logger.cs类的一个方法名称叫做:ShouldSuppressAnalysisWarningsForRequires里面。直接在里面下断点。
果然断点跳转到了它里面去,如下图所示:
fc3c49b7390c2f93933bfff5c824cd71.png

:更进一步,通过调试找到了类:

TrimAnalysisPatternStore。这个类里面有个方法:

MarkAndProduceDiagnostics,如下图所示:
ee24ef0f8124659289bb41ab314b381d.png

可以看到,这个方法里MethodCallPatterns.Values 的循环, MethodCallPatterns.Values就是Program.Main方法里面所有调用的方法。其中就包括了反射System.Type.GetMethods。

:它循环到System.Type.GetMethods,然后进入到MarkAndProduceDiagnostics函数。这个函数经过一系列调用,进入到了HandleCallAction类,调用了里面的Invoke方法。在HandleCallAction类的1145行处,它会判断GetMethods这个方法是否是静态方法,如果不是,则调用

_requireDynamicallyAccessedMembersAction.invoke里面会判断方法。如下图所示:
3e68782d0c4a519357152ff66b7873bd.png

:看一个函数

_requireDynamicallyAccessedMembersAction.invoke里面通过

Annotations.SourceHasRequiredAnnotations方法判断

GetMethods是否是被

DynamicallyAccessedMembersAttribute特性标记,如果标记了返回

true.最后调用_diagnosticContext.AddDiagnostic写入最上面报错的那段话。
Annotations.SourceHasRequiredAnnotations函数原型如下:

public static bool SourceHasRequiredAnnotations (
			DynamicallyAccessedMemberTypes sourceMemberTypes,
			DynamicallyAccessedMemberTypes targetMemberTypes,
			out string missingMemberTypesString)
		{
			missingMemberTypesString = string.Empty;
                       //获取反射GetMethods的特性
			var missingMemberTypes = GetMissingMemberTypes (targetMemberTypes, sourceMemberTypes);
			//特性如果等于空,则返回true。由于System.Type.GetMethods()函数的特性不为空所以这里返回false,也刚好调用了上面的_diagnosticContext.AddDiagnostic,在控制台上报错。
			if (missingMemberTypes == DynamicallyAccessedMemberTypes.None)
				return true;

			missingMemberTypesString = GetMemberTypesString (missingMemberTypes);
			return false;
		}

托管和非拖的反射

System.Type.GetMethods()方法原型如下:

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
		public MethodInfo[] GetMethods()
		{
			return this.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
		}

		// Token: 0x060009AA RID: 2474
		[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
		public abstract MethodInfo[] GetMethods(BindingFlags bindingAttr);

可以看到它的特性DynamicallyAccessedMembers,跟上面分析相印证。

GetMethods()这个函数最后会调用

System.RuntimeType.GetMethods。其原型如下:

public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
		{
			return this.GetMethodCandidates(null, -1, bindingAttr, CallingConventions.Any, null, false).ToArray();
		}

这个跟踪下去最后调用的是FCall/QCall。

结尾

总结下,就是ILC会判断这个方法是否被标记了DynamicallyAccessedMembersAttribute特性,并且是否调用了FCall/QCall。如果满足以上条件,则进行ILC代理,直接写入错误日志,导入到控制台上。

作者:江湖评谈(公众号同名)专研.Net 7的CLR和ILC核心技术。包括并不局限于:Roslyn,RyuJIT,二进制,汇编,C/C++。版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值