XLua 原理分析 三

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

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

一、Lua如何调用CS的过程

lua的初始化代码:

private string init_xlua = @" local metatable = {}local rawget = rawgetlocal setmetatable = setmetatablelocal import_type = xlua.import_typelocal import_generic_type = xlua.import_generic_typelocal load_assembly = xlua.load_assemblyfunction metatable:__index(key) local fqn = rawget(self,'.fqn')fqn = ((fqn and fqn .. '.') or '') .. keylocal 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 thenreturn rawget(self, key)end-- Cache this lookuprawset(self, key, obj)return objendfunction 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 thenlocal gt = import_generic_type(fqn, ...)if gt thenreturn rawget(CS, gt)endenderror('No such type: ' .. fqn, 2)endCS = CS or {}setmetatable(CS, metatable)typeof = function(t) return t.UnderlyingSystemType endcast = xlua.castif not setfenv or not getfenv thenlocal function getfunction(level)local info = debug.getinfo(level + 1, 'f')return info and info.funcendfunction setfenv(fn, env)if type(fn) == 'number' then fn = getfunction(fn + 1) endlocal i = 1while true dolocal name = debug.getupvalue(fn, i)if name == '_ENV' thendebug.upvaluejoin(fn, i, (function()return envend), 1)breakelseif not name thenbreakendi = i + 1endreturn fnendfunction getfenv(fn)if type(fn) == 'number' then fn = getfunction(fn + 1) endlocal i = 1while true dolocal name, val = debug.getupvalue(fn, i)if name == '_ENV' thenreturn valelseif not name thenbreakendi = i + 1endendendxlua.hotfix = function(cs, field, func)if func == nil then func = false endlocal tbl = (type(field) == 'table') and field or {[field] = func}for k, v in pairs(tbl) dolocal cflag = ''if k == '.ctor' thencflag = '_c'k = 'ctor'endlocal f = type(v) == 'function' and v or nilxlua.access(cs, cflag .. '__Hotfix0_'..k, f) -- at least onepcall(function()for i = 1, 99 doxlua.access(cs, cflag .. '__Hotfix'..i..'_'..k, f)endend)endxlua.private_accessible(cs)endxlua.getmetatable = function(cs)return xlua.metatable_operation(cs)endxlua.setmetatable = function(cs, mt)return xlua.metatable_operation(cs, mt)endxlua.setclass = function(parent, name, impl)impl.UnderlyingSystemType = parent[name].UnderlyingSystemTyperawset(parent, name, impl)endlocal base_mt = {__index = function(t, k)local csobj = t['__csobj']local func = csobj['<>xLuaBaseProxy_'..k]return function(_, ...)return func(csobj, ...)endend}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_ENABLElock (luaEnv.luaEnvLock){
#endifint 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);elsereturn 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_ENABLElock (luaEnv.luaEnvLock){
#endifvar 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.xdnf.cn/news/1489561.html

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

相关文章

python基础巩固

基本数据类型 可以用isinstance来判断 a111 isinstance(a,int) True数值运算&#xff1a; >>> 2 / 4 # 除法&#xff0c;得到一个浮点数 0.5 >>> 2 // 4 # 除法&#xff0c;得到一个整数 0 >>> 17 % 3 # 取余 2Python 字符串不能被改变。向一个…

vuex学习day02-state状态、严格模式(strict)、mutations、辅助函数mapMutations、actions

4、state状态 &#xff08;1&#xff09;作用&#xff1a;提供共享数据 &#xff08;2&#xff09;步骤&#xff1a; 1&#xff09;找到仓库&#xff0c;通过state提供共享数据 报错1&#xff1f;&#xff1a; 解决方式&#xff1a; 找到.eslintrc.js文件&#xff0c;添加一…

LabVIEW操作系列1

系列文章目录 我的记录&#xff1a; LabVIEW操作系列 文章目录 系列文章目录前言五、特殊用法5.1 取值范围表示5.2 对输入值取值范围进行限定5.3 控制多个While循环停止运行。5.4 获取按钮上的文本5.5 获取按钮上的文本【进阶】 六、使用步骤1.引入库2.读入数据 七、其余功能7.…

python+barcode快速生成条形码3-PyQt6微界面(电商条形码生成工具)

背景 继续上一片文章的电商测试小工具&#xff0c;进行了优化 需求 生成条形码之后&#xff0c;可以通过界面方式读取条形码的图片 支持当个条形码快速生成&#xff0c;以及批量导入 csv文件导入 添加微界面图像按钮&#xff0c;方便操作&#xff0c;更像是在实现测试工具的…

AI在Facebook的应用:预见智能化社交的新前景

在数字化时代&#xff0c;社交媒体平台已成为我们生活的重要组成部分&#xff0c;而人工智能&#xff08;AI&#xff09;的快速发展正推动着这些平台向更智能、更个性化的方向发展。Facebook&#xff0c;作为全球最大的社交网络平台之一&#xff0c;正不断探索和应用AI技术&…

基于JAVA+SpringBoot+Vue的oa系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、SpringCloud、Layui、Echarts图表、Nodejs、爬…

x64dbg反汇编技术入门学习笔记

EIP EIP是程序下一次要运行地方 寄存器 临时存放数据,按照Intel规定去存放 window API 微软提供的&#xff0c;用户可以操作系统的一些接口&#xff0c;以函数的形式体现 杀软是如何查杀恶意的 镜像地址 实际地址 实际运行后代码的地址 查外部调用段就可以定位到.rdat…

Android Kotlin:协程

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

Jenkins的相关概述和安装

Jenkins 1.什么是jenkins Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件项目可以进行持续集成。耗内存max 2. 为什么使用Jenkins 拉取、编译、打包…

【初阶数据结构】9.二叉树(4)

文章目录 5.二叉树算法题5.1 单值二叉树5.2 相同的树5.3 另一棵树的子树5.4 二叉树遍历5.5 二叉树的构建及遍历 6.二叉树选择题 5.二叉树算法题 5.1 单值二叉树 点击链接做题 代码&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* …

探索 Electron:构建用户友好的登录页面流程

Electron是一个开源的桌面应用程序开发框架&#xff0c;它允许开发者使用Web技术&#xff08;如 HTML、CSS 和 JavaScript&#xff09;构建跨平台的桌面应用程序&#xff0c;它的出现极大地简化了桌面应用程序的开发流程&#xff0c;让更多的开发者能够利用已有的 Web 开发技能…

安防巡检机器人:守护安全的智能卫士

安防巡检机器人&#xff0c;作为机器人技术在安防领域的杰出应用&#xff0c;是一种集自主导航、智能巡检、环境监测、远程监控等多功能于一体的智能装备。这些机器人通过集成先进的传感器、高清摄像头、智能算法和导航系统等模块&#xff0c;实现了全天候、全方位、自主化的安…

maven项目容器化运行之3-优雅的利用Jenkins和maven使用docker插件调用远程docker构建服务并在1Panel中运行

一.背景 在《maven项目容器化运行之1》中&#xff0c;我们开启了1Panel环境中docker构建服务给到了局域网。在《maven项目容器化运行之2》中&#xff0c;我们基本实现了maven工程创建、远程调用docker构建镜像、在1Panel选择镜像运行容器三大步骤。 但是&#xff0c;存在一个问…

HDU1059——Dividing,HDU1060——Leftmost Digit,HDU1061——Rightmost Digit

目录 HDU1059——Dividing 题目描述 运行代码 代码思路 HDU1060——Leftmost Digit 题目描述 ​编辑​编辑 运行代码 代码思路 HDU1061——Rightmost Digit 题目描述 运行代码&#xff08;快速幂&#xff09; 代码思路 HDU1059——Dividing 题目描述 Problem - …

索引(数据库优化)事务

索引 事务 Spring事务管理 上图模拟的异常为运行时异常 加上这个配置之后如果回滚会显示下面异常信息 事务进阶

数模打怪(八)之图论模型

一、作图 图的数学语言描述&#xff1a; G( V(G), E(G) )&#xff0c;G&#xff08;graph&#xff09;&#xff1a;图&#xff0c;V&#xff08;vertex&#xff09;&#xff1a;顶点集&#xff0c;E&#xff08;edge&#xff09;&#xff1a;边集 1、在线作图 https://csac…

【单词搜索】python刷题记录

R2-回溯:DFS剪枝. class Solution:def exist(self, board: List[List[str]], word: str) -> bool:#回溯经典问题&#xff1a;DFS剪枝解决mlen(board)nlen(board[0])def dfs(i,j,k):#3种剪枝策略if not 0<i<m or not 0<j<n or board[i][j]!word[k]:return Falsei…

whaler_通过镜像导出dockerfile

1、Whaler简介 Whaler:从镜像导出Dockerfile&#xff0c;whaler英文释义捕鲸船。 2、下载安装 # wget -cO /usr/local/bin/whaler https://github.com/P3GLEG/Whaler/releases/download/1.0/Whaler_linux_amd64 3、赋予可执行权限 [rootlocalhost ~]# chmod x /usr/local/…

学习测试11-移动自动化(略)

安卓SDK 链接: https://pan.baidu.com/s/1P4v9K2RYAGEoA5M_93hHlQ?pwdqsbu 提取码: qsbu 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 记得配置环境变量 下载Appium软件 hub网址&#xff1a;https://github.com/appium/appium-desktop/releases 链接: https…

c++入门----类与对象(中)

OK呀&#xff0c;家人们承接上文&#xff0c;当大家看过鄙人的上一篇博客后&#xff0c;我相信大家对我们的c已经有一点印象了。那么我们现在趁热打铁再深入的学习c入门的一些知识。 类的默认成员函数 首先我们学习的是我们的默认函数。不知道大家刚读这个名词是什么反应。默认…