XLua原理(一)

news2024/9/22 21:17:06

项目中活动都是用xlua开发的,项目周更热修也是用xlua的hotfix特性来做的。现研究底层原理,对于项目性能有个更好的把控。

本文认为看到该文章的人已具备使用xlua开发的能力,只研究介绍下xlua的底层实现原理。

一.luac#交互原理

概括:

通过栈来实现。lua调用c#就是将lua层的参数和c#导出函数入栈,然后执行函数。c#调用lua就是将c#层的参数和lua函数入栈,然后执行函数

1.1 C#访问lua

直接上代码:

 private void Demo1()
        {
            IntPtr L = LuaAPI.luaL_newstate();
            if (LuaAPI.luaL_loadbuffer(L, @"function addandsub(x,y) 
            return x+y , x-y end", "selfTagChunk") != 0)
            {
                Debug.LogError(LuaAPI.lua_tostring(L,-1));
            }

            //LuaAPI.lua_pushnumber(L,20);
            //LuaAPI.lua_pushnumber(L,21);
            LuaAPI.lua_pcall(L, 0, 0, 0);
            LuaAPI.xlua_getglobal(L, "addandsub");
            LuaAPI.lua_pushnumber(L,10);
            LuaAPI.lua_pushnumber(L,7);
          
            int valueB = LuaAPI.xlua_tointeger(L, -1);//7
            int valueB2 = LuaAPI.xlua_tointeger(L, -2);//10
            LuaTypes luaTypeB3 = LuaAPI.lua_type(L, -3);//function
            
            int valueC4 = LuaAPI.xlua_tointeger(L, 3);//7
            int valueC3 = LuaAPI.xlua_tointeger(L, 2);//10
            LuaTypes luaTypeC2 = LuaAPI.lua_type(L, 1);//function
            
            if (LuaAPI.lua_pcall(L, 2, 2, 0) != 0)
            {
                Debug.LogError(LuaAPI.lua_tostring(L,-1));
            }
            
            int value = LuaAPI.xlua_tointeger(L, -1); //3
            int value2 = LuaAPI.xlua_tointeger(L, -2); //17
            LuaAPI.lua_close(L);
        }

Api备注:

LuaAPI.luaL_newstate:开辟lua虚拟机,执行lua程序
LuaAPI.lua_close:关闭lua虚拟机,释放资源
LuaAPI.luaL_loadbuffer:编译一段lua代码,但没执行
LuaAPI.lua_pcall:执行lua代码,这时候可以用

这里:先把addandsub方法压栈,然后把10和7分别压栈。所以看到的内存里的数据如批注所示。

在执行第2个lua_pcall。会把之前的堆栈信息清空,把函数执行返回的结果进行压栈操作

1.2 lua访问C#

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void TestCSFunction(IntPtr L);

        [MonoPInvokeCallback(typeof(TestCSFunction))]
        public static void TestLuaCallCSharp(IntPtr L)
        {
            Debug.Log("TestLuaCallCSharp");
        }
        
        private void Demo2()
        {
            IntPtr L = LuaAPI.luaL_newstate();
            //Marshal 提供对非托管类型的操作
            IntPtr function = Marshal.GetFunctionPointerForDelegate(new TestCSFunction(TestLuaCallCSharp));
            //函数入栈
            LuaAPI.lua_pushcclosure(L,function,0);
            LuaAPI.lua_pcall(L, 0, 0, 0);
            LuaAPI.lua_close(L);
        }

UnmanagedFunctionPointer:定义为了让其不受C#托管管理
MonoPInvokeCallback:标记可以使其用C或C++调用

上述可以看到是把C#函数包装成指针,然后进行压栈操作供lua调用

二.xlua中的LuaEnv

  private void Demo3()
  {
     LuaEnv luaenv = new LuaEnv();
     luaenv.DoString("CS.UnityEngine.Debug.Log('hello world')");
     luaenv.Dispose();
  }

LuaEnv:是xlua封装好的lua环境,类似上面的luaL_newstate。

具体的事情:

XLua框架中最重要的一个类,那就是LuaEnv。它包含了lua中的状态机RealStatePrt。lua的G表,还有注册表LuaIndexes.LUA_REGISTRYINDEX等等,下面从LuaEnv的构造函数开始,看看这个类做了些什么事情

//节选LuaEnv构造函数部分代码

//拿到Lua中的注册表
LuaIndexes.LUA_REGISTRYINDEX = LuaAPI.xlua_get_registry_index

//创建Lua状态机
rawL = LuaAPI.luaL_newstate()

//十分重要的一个对象,用于c#和lua的交互
translator = new ObjectTranslator(this, rawL); 
translator.createFunctionMetatable(rawL);	//添加_gc元方法到注册表
translator.OpenLib(rawL);	//将init_xlua中会用到的方法,全部定义出来

//添加搜索路径
AddSearcher(StaticLuaCallbacks.LoadBuiltinLib, 2); 
//添加自定义解析Lua文件的方法,对应的是LuaEnv.CustomLoader
AddSearcher(StaticLuaCallbacks.LoadFromCustomLoaders, 3);
#if !XLUA_GENERAL
AddSearcher(StaticLuaCallbacks.LoadFromResource, 4);
AddSearcher(StaticLuaCallbacks.LoadFromStreamingAssetsPath, -1);
#endif

//十分重要!! 初始化xLua
DoString(init_xlua, "Init");

#if !UNITY_SWITCH || UNITY_EDITOR
AddBuildin("socket.core", StaticLuaCallbacks.LoadSocketCore);
AddBuildin("socket", StaticLuaCallbacks.LoadSocketCore);
#endif
AddBuildin("CS", StaticLuaCallbacks.LoadCS);

调用Init方法:

private string init_xlua = @" 
            local metatable = {}
            local rawget = rawget
            local setmetatable = setmetatable
            local import_type = xlua.import_type
            local import_generic_type = xlua.import_generic_type
            local load_assembly = xlua.load_assembly

            --fqn就是类型和命名空间名,通过import_type去获取对应的udata并且入栈
            function metatable:__index(key) 
                --查询key不调用元方法,更简单的表达只在自己的表内查询
                local fqn = rawget(self,'.fqn')
                fqn = ((fqn and fqn .. '.') or '') .. key
                --查询C#类型
                local obj = import_type(fqn)
                --如果不是 再次查询C#命名空间
                if obj == nil then
                    -- It might be an assembly, so we load it too.
                    obj = { ['.fqn'] = fqn }
                    setmetatable(obj, metatable)
                elseif obj == true then
                    return rawget(self, key)
                end

                -- Cache this lookup
                rawset(self, key, obj)
                return obj
            end
            --既然是C#对象 就不要再newindex了,避免产生未知的错误
            function metatable:__newindex()
                error('No such type: ' .. rawget(self,'.fqn'), 2)
            end
            
            -- A non-type has been called; e.g. foo = System.Foo()
            function metatable:__call(...)
                local n = select('#', ...)
                local fqn = rawget(self,'.fqn')
                if n > 0 then
                    local gt = import_generic_type(fqn, ...)
                    if gt then
                        return rawget(CS, gt)
                    end
                end
                error('No such type: ' .. fqn, 2)
            end

            CS = CS or {}
            setmetatable(CS, metatable)
            
            --定义typeof 这下知道了 typeof是xlua自己搞的,不是lua本身语言特性
            typeof = function(t) return t.UnderlyingSystemType end
            cast = xlua.cast
            if not setfenv or not getfenv then
                local function getfunction(level)
                    local info = debug.getinfo(level + 1, 'f')
                    return info and info.func
                end

                function setfenv(fn, env)
                  if type(fn) == 'number' then fn = getfunction(fn + 1) end
                  local i = 1
                  while true do
                    local name = debug.getupvalue(fn, i)
                    if name == '_ENV' then
                      debug.upvaluejoin(fn, i, (function()
                        return env
                      end), 1)
                      break
                    elseif not name then
                      break
                    end

                    i = i + 1
                  end

                  return fn
                end

                function getfenv(fn)
                  if type(fn) == 'number' then fn = getfunction(fn + 1) end
                  local i = 1
                  while true do
                    local name, val = debug.getupvalue(fn, i)
                    if name == '_ENV' then
                      return val
                    elseif not name then
                      break
                    end
                    i = i + 1
                  end
                end
            end

            xlua.hotfix = function(cs, field, func)
                if func == nil then func = false end
                local tbl = (type(field) == 'table') and field or {[field] = func}
                for k, v in pairs(tbl) do
                    local cflag = ''
                    if k == '.ctor' then
                        cflag = '_c'
                        k = 'ctor'
                    end
                    local f = type(v) == 'function' and v or nil
                    xlua.access(cs, cflag .. '__Hotfix0_'..k, f) -- at least one
                    pcall(function()
                        for i = 1, 99 do
                            xlua.access(cs, cflag .. '__Hotfix'..i..'_'..k, f)
                        end
                    end)
                end
                xlua.private_accessible(cs)
            end
            xlua.getmetatable = function(cs)
                return xlua.metatable_operation(cs)
            end
            xlua.setmetatable = function(cs, mt)
                return xlua.metatable_operation(cs, mt)
            end
            xlua.setclass = function(parent, name, impl)
                impl.UnderlyingSystemType = parent[name].UnderlyingSystemType
                rawset(parent, name, impl)
            end
            
            local base_mt = {
                __index = function(t, k)
                    local csobj = t['__csobj']
                    local func = csobj['<>xLuaBaseProxy_'..k]
                    return function(_, ...)
                         return func(csobj, ...)
                    end
                end
            }
            base = function(csobj)
                return setmetatable({__csobj = csobj}, base_mt)
            end
            ";

设置了metatble扩充原方法,并且设置CS的元表示metatable:

__index:会把C#没有缓存的命名空间下的元数据导入进来。

__call:与index同理,这里能看出来了为什么在lua里调C#方法是CS.UnityEngine.Vector3(x,x,x)。

这步是让lua可以去访问C#对象。

因为在Lua中定义了全局的CS表,并且在lua中调用CS的时候,会先调用StaticLuaCallbacks.LoadCS获取到注册表中的CS表,然后在调用XXX的时候,如果访问到了CS表中不存在的元素,则会调用其元表,在元表中通过映射到c#的StaticLuaCallbacks.ImportType方法完成查找。

lua在查找时调用了import_type进行查找调用,提前在OpenLib里把方法进行注册:

而importType会从中转器查询,查询的过程就是从一个维护的字典里看能不能取到,取不到就加载命名空间并注入到lua虚拟机中。

后面介绍lua与C#之间通信会详解介绍

参考:

Lua与C#交互原理(转)_lua与c#的交互原理-CSDN博客

https://zhuanlan.zhihu.com/p/441169478

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1938603.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

香橙派AIpro部署边缘端夜莺监控

文章目录 硬件信息硬件简介技术路线硬件参数到手实拍接口详情图应用场景相关资源香橙派官方昇腾论坛 开箱使用准备工作上电准备启动设备开发板状态 连接设备方式一、显示器直连方式二、Micro Usb 数据线串口连接方式三、Micro Usb 数据线方式网络直连方式四、Micro Usb数据线方…

R语言画散点图-饼图-折线图-柱状图-箱线图-直方图-等高线图-曲线图-热力图-雷达图-韦恩图(二D)

R语言画散点图-饼图-折线图-柱状图-箱线图-直方图-等高线图-曲线图-热力图-雷达图-韦恩图&#xff08;二D&#xff09; 散点图示例解析效果 饼图示例解析效果 折线图示例解析效果 柱状图示例解析效果 箱线图示例解析效果 直方图示例解析效果 等高线图使用filled.contour函数示例…

Pixel6 GKI 内核编译

前言 前段时间写了一篇关于pixel4 Android内核编译编译内核的流程。 但是随着Android版本的提升Google开始推崇GKI方式发内核模式,这种模式可以方便供应商剥离内核和驱动的捆绑性&#xff0c;官方抽象出一部分接口(GKI)提供给产生使用极大便利和解耦开发复杂性。 在pixel4 And…

python-爬虫实例(1):获取京东商品评论

目录 前言 道路千万条&#xff0c;安全第一条 爬虫不谨慎&#xff0c;亲人两行泪 获取京东商品评论信息 一、实例示范 二、爬虫四步走 1.UA伪装 2.获取Url 3.发送请求 4获取响应数据进行解析并保存 总结 前言 道路千万条&#xff0c;安全第一条 爬虫不谨慎&#xff0c;亲…

【BUG】已解决:AttributeError: ‘str‘ object has no attribute ‘get‘

已解决&#xff1a;AttributeError: ‘str‘ object has no attribute ‘get‘ 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c…

JVM中的GC流程与对象晋升机制

JVM中的GC流程与对象晋升机制 1、JVM堆内存结构2、Minor GC流程3、Full GC流程4、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java虚拟机&#xff08;JVM&#xff09;中&#xff0c;垃圾回收&#xff08;GC&#xff09;是自动管…

Let’s Encrypt申请证书流程(附带问题解决方式)

文章目录 前言Let’s Encrypt 和 OpenSSL的区别文章相关 正文1、安装snapd2. 使用snapd安装certbot3. 生成证书&#xff08;需要指定nginx&#xff09;5. Nginx.conf的配置 问题1、error: system does not fully support snapd原因解决方式 2、The error was: PluginError(‘Ng…

宠物医院管理系统视频介绍

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货、宠物等连锁店使用。 详细介绍请…

实现给Nginx的指定站点开启目录浏览功能

一、问题描述 需要实现在浏览器上可以浏览Nginx部署网站内容并下载一些常用的软件安装包、文件资料等内容;并且针对一些文件夹或内容需要进行认证后才能查看;有一些格式类型的文件也不能够访问查看。 二、问题分析 1、实现浏览器上可以浏览下载文件内容; 2、针对一些文件…

H3CNE(计算机网络的概述)

1. 计算机网络的概述 1.1 计算机网络的三大基本功能 1. 资源共享 2. 分布式处理与负载均衡 3. 综合信息服务 1.2 计算机网络的三大基本类型 1.3 网络拓扑 定义&#xff1a; 网络设备连接排列的方式 网络拓扑的类型&#xff1a; 总线型拓扑&#xff1a; 所有的设备共享一…

继承和多态(下)

目录 1.多态 2.多态成立的条件 虚函数重写&#xff1a; 虚函数重写的两个例外 3.override 和 final 4.重载&#xff0c;重定义&#xff08;隐藏&#xff09;和重写 5.抽象类 接口继承和实现继承 6.多态的原理 虚函数表 多态的原理 动态绑定和静态绑定 7.单继承虚表…

C语言高校人员管理系统

该系统使用easyx插件。 以下是部分页面 以下是部分源码&#xff0c;需要源码的私信 #define _CRT_SECURE_NO_WARNINGS 1//vs的取消报警 #include<easyx.h> #include<stdio.h> #include<stdlib.h>#define width 1280 #define height 840 #define font_w 35 …

《样式设计002:tab切换与底部swiper滑动块》

描述&#xff1a;在开发小程序过程中&#xff0c;发现一些不错的案例&#xff0c;平时使用也比较多&#xff0c;稍微总结了下经验&#xff0c;以下内容可以直接复制使用&#xff0c;希望对大家有所帮助&#xff0c;废话不多说直接上干货&#xff01; 一、tab切换与底部swiper滑…

AD9826 spi读写源码贡献

AD9826是一款专为成像应用设计的模拟信号处理器&#xff0c;它采用三通道架构&#xff0c;设计用于对三线彩色CCD阵列的输出进行采样和调理。这款处理器集成了多个关键功能组件&#xff0c;包括输入箝位电路、相关双采样器(CDS)、偏移DAC和可编程增益放大器(PGA)&#xff0c;并…

新手小白的pytorch学习第八弹------分类问题模型和简单预测

目录 1 启动损失函数和优化器2 训练模型创建训练和测试循环 3 预测和评估模型 这篇是接着新手小白的pytorch学习第七弹------分类问题模型这一篇的&#xff0c;代码也是哟~ 1 启动损失函数和优化器 对于我们的二分类问题&#xff0c;我们经常使用 binary cross entropy 作为损…

秋招突击——7/19——队列同步器AQS学习

文章目录 引言基础知识简介AQS接口和示例第一类&#xff1a;访问和修改同步状态的方法第二类&#xff0c;5个重写的方法第三类&#xff0c;9个模板方法 队列同步器实现原理同步队列独占式同步获取和释放共享式同步获取和释放独占式同步获取和释放 相关面试题怎么理解Lock和AQS的…

如何解决热插拔时的电压过冲

摘要 热插拔是指将上电电压源连接到电子器件的输入电源或电池连接器。热插拔产生的电压瞬态尖峰会损坏器件内部的集成电路。该文解释了此类电压瞬变的根本原因&#xff0c;并提供了防止这些瞬变损坏电子产品中的集成电路(IC) 的可能设计。 1 引言 当将高于 5V 的 USB 适配器…

达梦数据库的系统视图v$dmwatcher

达梦数据库的系统视图v$dmwatcher 查询当前登录实例所对应的守护进程信息&#xff0c;注意一个守护进程可以同时守护多个组的实例&#xff0c;因此查询结果中部分字段&#xff08;N_GROUP、SWITCH_COUNT&#xff09;为守护进程的全局信息&#xff0c;并不是当前登录实例自身的…

BUUCTF - Web - 1

文章目录 1. [极客大挑战 2019]EasySQL 1【SQL注入-万能密码】2. [极客大挑战 2019]Havefun 1【前端代码审计-注释泄漏】3. [HCTF 2018]WarmUp 1【PHP代码审计】4. [ACTF2020 新生赛]Include 1【PHP伪协议】5. [ACTF2020 新生赛]Exec 1【命令注入-基础】6. [GXYCTF2019]Ping Pi…

张量网络碎碎念:CGC

在本系列 上一篇文章 中&#xff0c;我介绍了张量网络的一些基础概念。其中很大一部分来自 github 上一个教程。事实上&#xff0c;该教程的大部分内容来自 e3nn 官网。 除了上篇文章介绍的一些可视化技巧&#xff0c;官网还提供了其他一些可视化模块。使用这些功能能使我们更深…