LuaInterface的反射调用机制研究

一直不明白LuaInterface和lua之间反射调用的原理,花了两天时间读了一下代码,稍微总结了一下

附上所用LuaInterface的地址,可以用git直接clone

https://github.com/fengxiaorui/luainterface

下面进入正题:

先说说两个关键的载入函数,也是进行反射调用的基础:

1、LoadAssembly

在Lua调用load_assembly后会被调用,目的是载入某个程序集到程序集缓存中

通过代码可以发现这里只是通过栈上传入的字符串加载对应的程序集,并加入到缓存中

(注意)这个程序集缓存在开启时就已经Add了正在执行的程序集,不需要重复加载

2、ImportType

在Lua调用import_type时被调用,从已经加载的程序集中查找对应的类,没有找到返回空,找到则调用pushType,往

堆栈上压一种新的类型,并缓存在luaNet_objects元表中,matable的类型为luaNet_class。这一步的主要作用是方便C#将这些类型压入堆栈后将返回值压进栈

然后会调用到把CLR Object压进栈的一般方法

这里会优先查找objectsBackMap这个索引表中查找这个object是否已经缓存,如果已经缓存返回对应的index,然后直接在名为luaNet_objects的元表中取第index项,即为要进栈的object

如果当前的索引表中没有找到这个对象,则说明需要插入一个新的对象,则调用pushNewObject

这里metatable为luaNet_metatable时说明要构建相应类型的元表,名称为AssemblyQualifiedName,这个元表会包含一个名为cache的table字段,用来缓存方法,和一个已经在全局域的luaNet_indexfunction作为其__index原方法

由于现在传入的metatable字段是luaNet_class,所以是直接取名为luaNet_class的元表(这个元表在ObjectTranslator初始化时已经生成好了,过程在createClassMetatable中)

然后将luaNet_class元表赋给新建的userdata,并将这个userdata设置为luaNet_objects元表的第index项,并留一份拷贝在栈上,至此,大工告成,类型成功import。当下次要压入同样类型到栈上时,只需要查找索引表找到index,然后去luaNet_objects元表的第index项直接取就好了。

然后是lua调用相应方法和返回值的过程:

1、CLR Object调用方法的流程是先调用自己的__index元方法,对应luaNet_indexfuction方法,定义如下:

这里会先取得以自己的元表(o.GetType().AssemblyQualifiedName为名的元表),查看相应的CSFuntion是否已经缓存在cache字段中,如果有就直接从cache字段里取,如果没有则调用get_object_member,利用反射的结构去查找对应的函数,并将结果和函数体返回到这里,缓存后将函数体返回

其中get_object_member方法值得一提:

它在C#对应的方法为getMethod:

这里会从堆栈取出对象和函数名,然后开始查找过程:

大部分情况下会走到这里:

可以注意到这里有一个Hashtable类型的memberCache,其中key为objType,value为另一个子HashTable,这个子HashTable存储了方法名和对应的LuaCSFuction。memberCache主要是在每次getMethod后缓存根据类型和方法名查找到的方法(LuaCSFuction),下一次在查找相同的方法时,直接从这个缓存表返回,省去了每次都要反射查找的问题

(C#这里有memberCache在缓存,lua那边每一个类的元表上还有Cache字段在缓存,这个相互关系待明确)

目前考虑memberCache这边函数和成员都有缓存,Cache这边只缓存函数

这里Getmember如果成功则将LuaCSFunction和结果入栈,回到刚才的luaNet_indexfuction方法处理

这里的LuaCSFunction实际上是一个函数代理,执行它就相当执行LuaMethodWrapper的call方法,这是一次反射调用,也就是说LuaInterface所有的C#函数调用实际上都是反射调用

LuaMethodWrapper这个类的作用就是根据提供的类型和方法名,反射调用相应的方法,并将结果压栈给lua

如果返回类型是一个基本类型,就调用相应的Lua API入栈:

如果返回类型是一个CLR类型,则会在push时调用:

至此就将一个封装好的object(userdata,同时设置了cache域和绑定__index元方法)压栈,至此就完成了全部的调用过程。

如果有不准确的地方,欢迎大家在下面评论留言讨论!

时间: 2024-10-12 18:58:01

LuaInterface的反射调用机制研究的相关文章

java动态加载指定的类或者jar包反射调用其方法

序言 有时候,项目中会用到java动态加载指定的类或者jar包反射调用其方法来达到模块的分离,使各个功能之间耦合性大大降低,更加的模块化,代码利用率更高.模式中的代理模式就用到java的这一机制.下边就让我们通过代码来看看如何实现此功能. 代码详细 package loadjarclass; import java.io.File; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoad

java动态载入指定的类或者jar包反射调用其方法

序言 有时候.项目中会用到java动态载入指定的类或者jar包反射调用其方法来达到模块的分离,使各个功能之间耦合性大大减少,更加的模块化.代码利用率更高.模式中的代理模式就用到java的这一机制. 下边就让我们通过代码来看看怎样实现此功能. 代码具体 package loadjarclass; import java.io.File; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoa

Android 反射调用资源和id

本文介绍利用反射调用资源和id 提出问题: app有一种叫应用墙的广告,应用墙是在你的程序中弹出一个Activity来展示广告,比如豌豆广点通等,集成的时候需要将资源通过复制添加到自己的项目中,但是app墙的代码是封装好的jar代码.不是源码,看不到,也不能修改.那么jar中的代码是如何加载本地资源的呢? 自己的项目中加载资源的时候都是通过本项目的R文件来初始化资源,R文件是你自己的项目的R文件,和项目有关,如果第三方的jar文件中使用的R是来第三方SDK项目中的资源R,代码更换了项目之后铁定了

使用反射调用匿名内部类时应该注意的一些地方

06年写的 在使用匿名内部类时,当使用了反射机制来调用其中的方法就会出现访问异常,这是在前几天写程序时遇到的,所以在写匿名内部类时一定要注意是否在其它地方使用了反射调用.下面给出部份代码来说明此问题. public class SuperTest { public void hello() { System.out.println("Hello from SuperTest"); } } public class Exec { public static void run(SuperT

Android系统篇之----Binder机制和远程服务调用机制分析

一.前景概要 最近要实现Android中免注册Activity就可以运行的问题,那么结果是搞定了,就是可以不用在AndroidManifest.xml中声明这个Activity即可运行,主要是通过骗取系统,偷龙转凤技术的,这个知识点后面会详细讲解的,因为在研究了这个问题过程中遇到了很多知识点,当然最重要也是最根本的就是Android中的Binder机制和远程服务调用机制,而关于Binder机制的话,在Android中算是一个非常大的系统架构模块了,光这篇文章是肯定不能讲解到全部的,而且本人也不是

Java 反射调用私有域和方法(setAccessible)

Java 反射调用私有域和方法(setAccessible) @author ixenos AccessibleObject类 Method.Field和Constructor类共同继承了AccessibleObject类,该基类有两个setAccessible方法能在运行时压制Java语言访问控制检查(Java language access control checks),从而能任意调用被私有化保护的方法.域和构造方法 public class AccessibleObjectextends

Android:利用Java反射调用@hide的API

置使用3G数据功能: 从源代码看到隐藏的API(ConnectivityManager.java): 查看文本打印? /** * Sets the persisted value for enabling/disabling Mobile data. * * @param enabled Whether the mobile data connection should be *            used or not. * @hide */ public void setMobileDa

【Java】反射调用与面向对象结合使用产生的惊艳

缘起 我在看Spring的源码时,发现了一个隐藏的问题,就是父类方法(Method)在子类实例上的反射(Reflect)调用. 初次看到,感觉有些奇特,因为父类方法可能是抽象的或私有的,但我没有去怀疑什么,这可是Spring的源码,肯定不会有错. 不过我去做了测试,发现确实是正确的,那一瞬间竟然给我了一丝的惊艳. 这其实是面向对象(继承与重写,即多态)和反射结合的产物.下面先来看测试,最后再进行总结. 友情提示:测试内容较多,不过还是值得一看. 具体方法的继承与重写 先准备一个父类,有三个方法,

[LinqPad妙用]-在Net MVC中反射调用LinqPad中的Dump函数

LinqPad有个非常强大的Dump函数.这篇讲解一下如何将Dump函数应用在.Net MVC Web开发中. 先看效果: 一.用.Net Reflector反编译LinqPad.exe,找出Dump函数的定义: 经过反编译发现,Dump函数调用了LINQPad.ObjectGraph.Formatters.XhtmlWriter类中FormatObject函数,把对象转成了Html. 二.反射调用FormatObject函数: 由于FormatObject函数是protect类型,不能直接调用