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.mzph.cn/bicheng/50927.shtml

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

相关文章

乌班图下的vscode粘贴代码后一直在输入CTRLV命令

最近在VMware中使用vscode开发c程序中&#xff0c;拷贝一段代码后&#xff0c;代码界面一直输入CTRLV命令&#xff0c;导致乌班图桌面死掉&#xff0c;无法操作、 解决方法&#xff1a; 1、强制重启。长按电源按钮强制关机&#xff0c;然后再次开机。 2、使用命令行界面。同时…

1.ESP32-CAM 下使用 ESP-IDF 打开摄像头

主要资料&#xff1a; 乐鑫官方编程指南 ESP-IDF 编程指南安信可官方模块页 安信可-ESP32-CAM摄像头开发板官方使用教程 安信可ESP32-CAM摄像头开发demo–局域网拍照、实时视频、人脸识别 &#xff08;开发环境是Linux&#xff09; 本文目标是在 Windows 下跑通摄像头 hello …

快手电商Android一面凉经(2024)

快手电商Android一面凉经(2024) 笔者作为一名双非二本毕业7年老Android, 最近面试了不少公司, 目前已告一段落, 整理一下各家的面试问题, 打算陆续发布出来, 供有缘人参考。今天给大家带来的是《快手电商Android一面凉经(2024)》。 面试职位: Android工程师 技术一面 面试形式…

python count返回什么

描述 count() 方法用于统计字符串中某个子字符串出现的次数&#xff0c;可选参数为开始搜索与结束搜索的位置索引。 语法 count() 方法语法&#xff1a; S.count(sub[,start0[,endlen(S)]]) 参数 sub -- 搜索的子字符串。 S -- 父字符串。 start -- 可选参数&#xff0c;…

【数据结构】哈希表的模拟实现

文章目录 1. 哈希的概念2. 哈希表与哈希函数2.1 哈希冲突2.2 哈希函数2.3 哈希冲突的解决2.3.1 闭散列&#xff08;线性探测&#xff09;2.3.2 闭散列的实现2.3.3 开散列(哈希桶)2.3.4 开散列的实现 2.4 开散列与闭散列比较 1. 哈希的概念 在我们之前所接触到的所有的数据结构…

FastAPI(八十一)实战开发《在线课程学习系统》接口开发-- 推荐课程列表与课程点赞

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 推荐课程列表 逻辑很简单 点赞数 > 500 那么符合要求的课程是&#xff1a; def get_like_course(db: Session):"""获取推荐课程…

一番赏小程序搭建,线上一番赏市场

一番赏作为一个经久不衰的潮流市场&#xff0c;一直流行于消费者市场中。一番赏商品拥有不同系列&#xff0c;涵盖了热门动漫、漫画、影视等主题&#xff0c;商品包含了手办等周边商品&#xff0c;具有非常大的收藏价值。相比于其他潮玩模式&#xff0c;一番赏的性价比更高&…

Kylin 入门教程

Apache Kylin 是一个开源的分布式数据仓库和 OLAP(在线分析处理)引擎,旨在提供亚秒级查询响应时间,即使在处理超大规模数据集时也是如此。Kylin 可以有效地将原始数据预计算为多维数据立方体(Cube),并利用这些预计算结果来提供快速查询。本文将带你从基础知识到操作实践…

GC(垃圾收集)简析:为什么Java需要它?

GC&#xff08;垃圾收集&#xff09;简析&#xff1a;为什么Java需要它&#xff1f; 1、什么是GC&#xff1f;2、为什么Java需要GC&#xff1f;3、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、什么是GC&#xff1f; GC&#xff…

黄果树瀑布

早上我们按计划6点吃饭&#xff0c;可是酒店没水了&#xff0c;等我们吃完饭已经近七点 &#xff0c;到景区已经7点多了。景区入口的人特别多&#xff0c;验过包裹&#xff0c;只刷脸就进去了。排队坐大巴&#xff0c;向左是陡坡塘和七星桥。 第一站陡坡塘&#xff0c;服务中心…

synergy Ubuntu 18 和Windows 配置

下载链接 Windows 版本 https://altushost-swe.dl.sourceforge.net/project/synergy-stable-builds/v1.8.8-stable/synergy-v1.8.8-stable-Windows-x64.msi?viasf1Ubuntu 18 版本 sudo apt-get install synergy配置方式 我这边配置需求是 笔记本电脑&#xff08;Windows系统…

为什么用LeSS?

实现适应性 LeSS是一个产品开发的组织系统&#xff0c;旨在最大化一个组织的适应性。关于适应性&#xff08;或者敏捷性&#xff0c;也就是敏捷开发的初衷&#xff09;我们是指优化&#xff1a; 以相对低的成本改变方向的能力&#xff0c;主要是基于通过频繁交付产生的探索。从…

基于微信小程序的搬家服务系统(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝10W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 涉及技术栈&#xff1a;SpringBoot、Vue、SSM、H…

【PostgreSQL 16】专栏日常

本专栏从 3 个月前开始着手准备&#xff0c;利用周末及节假日的时间来整理。 ldczzDESKTOP-HVJOUVN MINGW64 ~/mypostgres (dev) $ git lg |tee * 7a7f468 - (HEAD -> dev, origin/main, origin/dev, main) 完成服务端编程的初步整理 (6 minutes ago) <Laven Liu> * …

将手机作为服务器运行docker服务

前言 目前手机的配置并不低&#xff0c;即使是2019年生产的一加七Pro&#xff0c;配置也有12256&#xff0c;CPU是骁龙855&#xff0c;作为服务器运行着配置绰绰有余了&#xff0c;二手的价格现在是400左右也能接受。相对于是自带ups电源的便携低耗docker服务器&#xff0c;还…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第六十一章 Linux内核定时器

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第四十二章 LED驱动实验

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

【python014】Python爬取并解析潮汐天气简报-潮历数据

1.欢迎点赞、关注、批评、指正&#xff0c;互三走起来&#xff0c;小手动起来&#xff01; 【python014】Python爬取并解析潮汐天气简报-潮历数据&#xff0c;源代码下载【python014】Python爬取并解析潮汐天气简报-潮历数据&#xff0c;源代码下载【python014】Python爬取并解…

EtherNet/IP转Profinet协议转化网关(功能与配置)

如何将EtherNet/IP和Profinet网络连接通讯起来呢?近来几天有几个朋友问到这个问题&#xff0c;那么作者在这里统一说明一下。其实有一个不错的设备产品可以很轻易地解决这个问题&#xff0c;名为捷米特JM-PN-EIP。接下来作者就从该设备的功能及配置详细说明一下。 一&#xf…

Leetcode283. 移动零(java实现)

今天分享的题目是leetcode283移动0&#xff08;成都人狂喜&#xff09; 先来看下题目描述 这道题目我也看了官方的题解感觉有点冗余了。 那么我们就不用官方的题解了&#xff0c;分享下我们自己的解体方式。 解题步骤&#xff1a;1.找到非0元素&#xff0c;如果是非0进行记录2…