XLua 原理分析 三

news2024/11/13 10:50:56

前面已经介绍了Lua与C#的基础通信原理,和Wrap中间文件的作用。有了前面2篇的基础,大概已经能搞清这块的原理。

为了加深对这块的印象,这里开始正式分析Xlua中的Lua和C#的通信。

一、Lua如何调用CS的过程

lua的初始化代码:

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

            function metatable:__index(key) 
                local fqn = rawget(self,'.fqn')
                fqn = ((fqn and fqn .. '.') or '') .. key

                local obj = import_type(fqn)

                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

            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 = 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
            ";

这段在该系列的一 里有介绍,就是弄了个CS的table。CS设置了原方法:

__index:查找方法,先看有没有加载过,没有就加载,设置元表是metatable(和CS同一个元表)。然后返回。

import_type:对应C#的ImportType方法。
映射的建立在这里:

压栈import_type字符,压栈C#ImportType方法,压栈raw_set并执行。

C#对应方法:先查有没有映射过,没有就映射然后缓存压栈。

__newindex:调用就报错,可能为了防止查bug不好查吧。

__call:对应C#的构造方法

查找C#的类,优先去调Wrap适配代码,没有就反射查找调用。

Wrap文件加载都会调到__Register进行Lua表的注册,如:

Util的RegisterFunc就是把Lua的元表元素与C#对应的方法进行映射

idx可以简单理解为定义的值

反射部分代码:

在Lua虚拟机创建新的table,然后为其先收集元数据。对应在table里同名进行映射。相比较Wrap模式代码,性能比较浪费。

注册方法为例图示:

最后不论哪种模式,都会走到SetCSTable,期目的是创建一个luatable并把对应C#方法塞进来。

反射的调用走的

把lua和C#靠一个映射关系联系起来,对应key

总结:

1.xlua在程序运行时,先调用init_xlua。目的是创建CS表并且设置__index,__call元方法

2.lua调用C#时,触发元方法,先找缓存没缓存优先加载Wrap文件注册,没有就反射查找。属性什么的都用rawset设置lua与C#方法进行映射

Wrap模式:

反射模式:

3.运行维护堆栈来进行通信(lua找C#对象也就是个Int索引,不会真把内存都压栈给lua)

二、CS如何调用Lua的过程

2.1 C#如何Get Lua代码的数据

ObjectCasters:负责转换lua的数据到C#

看个简单的:

从lua堆栈中取元素,然后转化。取LuaTable也差不多

从下面这段可以看出对应C#而言,lua的table就是一个指针,只需要对应的类来处理这个指针相关的信息就可以达到对lua table操作。

需要注意每次这里都是new个新的table,为了保证唯一性,项目里可以搞个缓存记录一下。避免出现好多个c#的LuaTable指向同一个Lua里的Table。

2.2 C#如何调用lua的方法

这块比较简单,就是简单的压栈操作调用call方法。直接看代码直观

  public object[] Call(object[] args, Type[] returnTypes)
        {
#if THREAD_SAFE || HOTFIX_ENABLE
            lock (luaEnv.luaEnvLock)
            {
#endif
                int nArgs = 0;
                var L = luaEnv.L;
                var translator = luaEnv.translator;
                int oldTop = LuaAPI.lua_gettop(L);

                int errFunc = LuaAPI.load_error_func(L, luaEnv.errorFuncRef);
                LuaAPI.lua_getref(L, luaReference);
                if (args != null)
                {
                    nArgs = args.Length;
                    for (int i = 0; i < args.Length; i++)
                    {
                        translator.PushAny(L, args[i]);
                    }
                }
                int error = LuaAPI.lua_pcall(L, nArgs, -1, errFunc);
                if (error != 0)
                    luaEnv.ThrowExceptionFromError(oldTop);

                LuaAPI.lua_remove(L, errFunc);
                if (returnTypes != null)
                    return translator.popValues(L, oldTop, returnTypes);
                else
                    return translator.popValues(L, oldTop);
#if THREAD_SAFE || HOTFIX_ENABLE
            }
#endif
        }
2.3 C#如何设置lua的属性

Set也比较简单,入栈lua部分的key和C#的value直接调用一个 LuaAPI.xlua_psettable。然后再还原堆栈信息

        public void Set<TKey, TValue>(TKey key, TValue value)
        {
#if THREAD_SAFE || HOTFIX_ENABLE
            lock (luaEnv.luaEnvLock)
            {
#endif
                var L = luaEnv.L;
                int oldTop = LuaAPI.lua_gettop(L);
                var translator = luaEnv.translator;

                LuaAPI.lua_getref(L, luaReference);
                translator.PushByType(L, key);
                translator.PushByType(L, value);

                if (0 != LuaAPI.xlua_psettable(L, -3))
                {
                    luaEnv.ThrowExceptionFromError(oldTop);
                }
                LuaAPI.lua_settop(L, oldTop);
#if THREAD_SAFE || HOTFIX_ENABLE
            }
#endif
        }

测试代码:

        private void Demo5()
        {
            LuaEnv luaenv = new LuaEnv();
            luaenv.DoString(@"luaTable = {a=123,b=456,c=789}
                                    function luaTable:Func()
                                        print('testLua:'.. tostring(self.a))
                                    end 
                                    go = CS.UnityEngine.GameObject()
                                    go.name = 'luaGo'
                            ");
            LuaTable luaTable = luaenv.Global.Get<LuaTable>("luaTable");
            LuaFunction func = luaTable.Get<LuaFunction>("Func");
            int a = luaTable.Get<int>("a");
            func.Call(luaTable);
            luaTable.Set("a",999);
            func.Call(luaTable);
            GameObject go = luaenv.Global.Get<GameObject>("go");
            go.name = "CSharpGo";
            luaenv.Dispose();
        }
Global:对应lua的_G表。

1.先从G表获取名为luaTable的表。调用C#注册通信的lua方法拿到lua的内存指针,并且创建一个C#的LuaTable类。

2.同样的方式拿到Function

3.拿到变量a有所区别,直接从堆栈获取,因为是基础类型

4.调用Call方法,进行压栈。走到  LuaAPI.lua_pcall

5.缓存堆栈信息,进行压栈操作后,设置lua属性。还原堆栈

6.GameObject的生成改名也差不多就不复述了。

更详细的部分得需要去查看lua的c语言实现部分,碍于时间和水平的原因。暂时不详细看了。大体上也是入栈出栈调用pcall方法

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

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

相关文章

驱动开发系列06 - 内存映射和DMA

目录 一:概述 二:Linux中的内存管理 地址类型 物理地址与页 高内存和低内存 内存映射和页结构体 页表 虚拟内存区域 vm_area_struct 结构 进程内存映射 三:mmap 设备操作 使用 remap_pfn_range 一个简单的实现 添加 VMA 操作 使用 nopage 映射内存 重新映射特定 I/O…

QT5.9.9+Android开发环境搭建

文章目录 1.安装准备1.1 下载地址1.2 安装前准备2.安装过程2.1 JDK安装2.1.1 安装2.1.2 环境变量配置2.2 SDK配置2.2.1 安装2.2.2 环境变量配置2.2.3 adb 错误解决2.2.4 其他SDK安装2.2.5 AVD虚拟机配置2.3 NDK配置2.4 QT 5.9.9安装配置2.4.1 QT安装2.4.2 配置安卓环境3.QT工程…

Android Kotlin:协程

目录&#xff1a; 1&#xff09;协程是什么&#xff1f; 2&#xff09;协程和线程的关系&#xff1f; 3&#xff09;协程如何使用&#xff1f;切线程是什么 4&#xff09;挂起函数是什么&#xff1f; 5&#xff09;withContext和lanuch的区别在哪里&#xff1f; 6&#xff09;…

某矿webpack逆向

免责声明&#xff1a; 本篇博文的初衷是分享自己学习逆向分析时的个人感悟&#xff0c;所涉及的内容仅供学习、交流&#xff0c;请勿将其用于非法用途&#xff01;&#xff01;&#xff01;任何由此引发的法律纠纷均与作者本人无关&#xff0c;请自行负责&#xff01;&#xf…

华媒舍:6个媒体宣发套餐,快速突破传播界限

在当今信息爆炸的社会中&#xff0c;有效地传播自己的信息变得愈发困难。特别是对于媒体宣发来说&#xff0c;如何在市场竞争激烈的情况下突破传播界限&#xff0c;让自己的消息传达给更多的人&#xff0c;这是每个企业和个人都面临的难题。 为了解决这个问题&#xff0c;我们推…

图片变更检测

20240723 By wdhuag 目录 前言&#xff1a; 参考&#xff1a; 文件监控&#xff1a; 图片占用问题&#xff1a; 源码&#xff1a; 前言&#xff1a; 由于第三方图像处理软件不能回传图片&#xff08;正常都能做&#xff0c;这里只是不想做&#xff09;&#xff0c;只能在…

有什么好用的AI工具推荐吗?

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/ 所有打工人集合&#xff01;根据你问题中的描述&#xff0c;本小白正好都有涉及过相关领域的AI工具。 今天一次性讲清能处理所有办公场景的AI工具…

Harmony学习(二)------ArkUI(2)

1.主轴对齐方式.justifyContent build() {Column(){Text().width(200).height(100).backgroundColor(Color.Yellow).border({width:2})Text().width(200).height(100).backgroundColor(Color.Yellow).border({width:2}).margin(10)Text().width(200).height(100).backgroundCol…

现货黄金布林线怎么看?又应如何使用?

在现货黄金投资中&#xff0c;技术指标是很多人做交易分析和决策时所需要的工具。当然&#xff0c;由于电脑技术的发达&#xff0c;现在各种技术指标已经有成千上万种&#xff0c;其中一些经典的指标还是很受市场欢迎&#xff0c;比方说布林线。下面我们就来简单地介绍布林线在…

【调试笔记-20240723-Linux-gitee 仓库同步 github 仓库,并保持所有访问链接调整为指向 gitee 仓库的 URL】

调试笔记-系列文章目录 调试笔记-20240723-Linux-gitee 仓库同步 github 仓库&#xff0c;并保持所有访问链接调整为指向 gitee 仓库的 URL 文章目录 调试笔记-系列文章目录调试笔记-20240723-Linux-gitee 仓库同步 github 仓库&#xff0c;并保持所有访问链接调整为指向 gite…

Python爬虫(5) --爬取网页视频

文章目录 爬虫爬取视频指定url发送请求UA伪装请求页面 获取想要的数据解析定位定位音视频位置 存放视频完整代码实现总结 爬虫 Python 爬虫是一种自动化工具&#xff0c;用于从互联网上抓取网页数据并提取有用的信息。Python 因其简洁的语法和丰富的库支持&#xff08;如 requ…

typora激活问题

不使用激活码解决。 1.右键桌面图标&#xff0c;打开文件位置 2.按照 Typora路径到 —> resources —> page-dist —> static —> js 这个路径找到这两个文件 LicenseIndex.180dd4c7.xxxxxxx.chunk.js LicenseIndex.180dd4c7.xxxxxxx.chunk.js &#xff08;也可…

MySQL简介(超详细)

课程目标 • 了解数据库基本概念 • 熟悉MySQL数据库的常用操作 • 掌握Insert、Delete、Update、Select等常用SQL语句 • 理解MySQL数据库的事务&#xff0c;索引以及函数 • 了解MySQL数据库的存储过程和触发器 一、什么是数据库&#xff1f; 概念&#xff1a;数据库(D…

立创梁山派--移植开源的SFUD和FATFS实现SPI-FLASH文件系统

本文主要是在sfud的基础上进行fatfs文件系统的移植&#xff0c;并不对sfud的移植再进行过多的讲解了哦&#xff0c;所以如果想了解sfud的移植过程&#xff0c;请参考我的另外一篇文章&#xff1a;传送门 正文开始咯 首先我们需要先准备资料准备好&#xff0c;这里对于fatfs的…

Windows图形界面(GUI)-MFC-C/C++ - MFC项目工程框架解析

公开视频 -> 链接点击跳转公开课程博客首页 -> e​​​​​​链接点击跳转博客主页 目录 MFC项目 项目选择 配置安装 程序引导 MFC框架 环境设置 程序框架 代码编写 MFC解析 程序入口 执行流程 代码结构 应用程序类 窗口框架类 消息处理 消息类型 消息…

探索扫描二维码登录的奥秘:从前端到后端的无缝连接

&#x1f389; 博客主页&#xff1a;【剑九 六千里-CSDN博客】 &#x1f3a8; 上一篇文章&#xff1a;【React中的无状态组件&#xff1a;简约之美】 &#x1f3a0; 系列专栏&#xff1a;【面试题-八股系列】 &#x1f496; 感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1、…

[Jenkins]jenkins-cli.jar调用用户token启动任务

背景&#xff1a;项目入了一群od伙伴&#xff0c;但是od伙伴有单独的构建工程需要提交&#xff0c;由于jenkins的版本太拉闸&#xff0c;不能配置根据role和项目分权限&#xff0c;插件安装失败&#xff0c;不得已想到一个办法。让OD伙伴&#xff0c;在本地&#xff0c;用java&…

音频剪辑里的几种基础操作

音频对于视频的重要性&#xff0c;怎么强调都不为过&#xff0c;它在视频里扮演着举足轻重的角色&#xff0c;对观众有着极为深远的影响。下面为您阐述音频在视频中的关键意义&#xff1a; ① 情感传递&#xff1a;音频有强大的情感传达能力&#xff0c;借助声音的起伏变化、音…

windows网络应急排查

一、系统排查 msinfo32 #GUI显示的系统信息systeminfo #简单了解系统信息用户信息排查 排查恶意账号&#xff1a; 黑客喜欢建立相关账号用作远控: 1.建立新账号2.激活默认账号3.建立隐藏账号(windows中账号名$)cmd方法 net user #打印用户账号信息 ---看不到$结尾的隐藏账…

postgresql 使用navicat 导出报 gs_package 关系不存在问题解决。

1. 问题描述 临时接手的项目&#xff0c;使用的数据库是postgresql&#xff0c;使用navicat 17 Lite 免费版&#xff0c;导出就会报如下图所示的错误&#xff1a;2. 尝试的办法&#xff1a; 1) 换navicat 17 和navicat 17 for postgresql 试用版本 还是一样的错误。 2) 换pos…