【lua】tolua(Luajit) require 重复加载了的问题(路径混用斜杠/和点.造成的问题)

最近在分析untiy客户端lua的性能时发现, 真机上有的lua重复加载了2次策划数值表。

打印了一下当时package.loaded (按理require了之后这里会存一下,后面require就不会重复加载了),

发现了存在 data/xxxx data.xxxx 两个路径的。

那是项目里 require "data/xxxx" require "data.xxxx" 两种方式混用造成的。

在Unity编辑器里跑没问题,package.loaded里打印出来都是点.连接的。 但真机上却重复了。

再仔细分析源码问题,发现真机为了编译lua用了luajit (lib_package.c 有个版本 require实现没有 /替换成 .), unity上用的是tolua编出来的dll(macnojit 的 loadlib.c 里 require实现做了/换成.)

简单解决方案,在第一个加载.lua文件开头把require函数重写一下:

Lua 复制代码
-- 保存原始的 require 函数  
local original_require = require  
-- 新的 require 函数,重写路径处理  
function require(modname)  
    -- 将路径中的斜杠替换为点  
    local modified_modname = modname:gsub("/", ".")
    
    -- 调用原始的 require 函数加载模块  
    return original_require(modified_modname)  
end  
  

就能避免这个重复加载了的问题。

真机上跑了一下,运行内存竟然少了近几十M(项目里数值表占用太多吧= =), 加载卡顿也少了一些。

放一下lua源码里的 require实现:
https://www.lua.org/source/5.4/loadlib.c.html

cpp 复制代码
static int ll_require (lua_State *L) {
  const char *name = luaL_checkstring(L, 1);
  lua_settop(L, 1);  /* LOADED table will be at index 2 */
  lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
  lua_getfield(L, 2, name);  /* LOADED[name] */
  if (lua_toboolean(L, -1))  /* is it there? */
    return 1;  /* package is already loaded */
  /* else must load package */
  lua_pop(L, 1);  /* remove 'getfield' result */
  findloader(L, name);
  lua_rotate(L, -2, 1);  /* function <-> loader data */
  lua_pushvalue(L, 1);  /* name is 1st argument to module loader */
  lua_pushvalue(L, -3);  /* loader data is 2nd argument */
  /* stack: ...; loader data; loader function; mod. name; loader data */
  lua_call(L, 2, 1);  /* run loader to load module */
  /* stack: ...; loader data; result from loader */
  if (!lua_isnil(L, -1))  /* non-nil return? */
    lua_setfield(L, 2, name);  /* LOADED[name] = returned value */
  else
    lua_pop(L, 1);  /* pop nil */
  if (lua_getfield(L, 2, name) == LUA_TNIL) {   /* module set no value? */
    lua_pushboolean(L, 1);  /* use true as result */
    lua_copy(L, -1, -2);  /* replace loader result */
    lua_setfield(L, 2, name);  /* LOADED[name] = true */
  }
  lua_rotate(L, -2, 1);  /* loader data <-> module result  */
  return 2;  /* return module result and loader data */
}

用lua翻译就是:

Lua 复制代码
--require 函数的实现
function require(name)
	if not package.loaded[name] then
		local loader = findloader(name)	//这一步演示在代码中以抽象函数findloader来表示
		if loader == nil then
			error("unable to load module" .. name)
		end
		package.loaded[name] = true
		local res = loader(name)
		if res ~= nil then
			package.loaded[name] = res
		end
	end
	return package.loaded[name]
end

from Lua-require_lua require原理-CSDN博客

ToLua\macnojit\lua\loadlib.c里的实现 luaL_gsub(L, name, "/", ".");做了替换

cpp 复制代码
static int ll_require (lua_State *L) {
  const char *name = luaL_checkstring(L, 1);
  int i;
  lua_settop(L, 1);  /* _LOADED table will be at index 2 */
  const char* key = luaL_gsub(L, name, "/", ".");
  lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
  lua_getfield(L, 3, key);
  if (lua_toboolean(L, -1)) {  /* is it there? */
    if (lua_touserdata(L, -1) == sentinel)  /* check loops */
      luaL_error(L, "loop or previous error loading module " LUA_QS, name);
    return 1;  /* package is already loaded */
  }
  /* else must load it; iterate over available loaders */
  lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
  if (!lua_istable(L, -1))
    luaL_error(L, LUA_QL("package.loaders") " must be a table");
  lua_pushliteral(L, "");  /* error message accumulator */
  for (i=1; ; i++) {
    lua_rawgeti(L, -2, i);  /* get a loader */
    if (lua_isnil(L, -1))
      luaL_error(L, "module " LUA_QS " not found:%s",
                    name, lua_tostring(L, -2));
    lua_pushstring(L, name);
    lua_call(L, 1, 1);  /* call it */
    if (lua_isfunction(L, -1))  /* did it find module? */
      break;  /* module loaded successfully */
    else if (lua_isstring(L, -1))  /* loader returned error message? */
      lua_concat(L, 2);  /* accumulate it */
    else
      lua_pop(L, 1);
  }
  lua_pushlightuserdata(L, sentinel);
  lua_setfield(L, 3, key);  /* _LOADED[name] = sentinel */
  lua_pushstring(L, name);  /* pass name as argument to module */
  lua_call(L, 1, 1);  /* run loaded module */
  if (!lua_isnil(L, -1))  /* non-nil return? */
    lua_setfield(L, 3, key);  /* _LOADED[name] = returned value */
  lua_getfield(L, 3, key);
  if (lua_touserdata(L, -1) == sentinel) {   /* module did not set a value? */
    lua_pushboolean(L, 1);  /* use true as result */
    lua_pushvalue(L, -1);  /* extra copy to be returned */
    lua_setfield(L, 3, key);  /* _LOADED[name] = true */
  }
  return 1;
}

https://github.com/LuaJIT/LuaJIT/blob/v2.1/src/lib_package.c 里没有实现替换,这边项目里本来改过的,后来升级一次不小心又改掉这个修改了:

cpp 复制代码
#define KEY_SENTINEL	(U64x(80000000,00000000)|'s')

static int lj_cf_package_require(lua_State *L)
{
  const char *name = luaL_checkstring(L, 1);
  int i;
  lua_settop(L, 1);  /* _LOADED table will be at index 2 */
  lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
  lua_getfield(L, 2, name);
  if (lua_toboolean(L, -1)) {  /* is it there? */
    if ((L->top-1)->u64 == KEY_SENTINEL)  /* check loops */
      luaL_error(L, "loop or previous error loading module " LUA_QS, name);
    return 1;  /* package is already loaded */
  }
  /* else must load it; iterate over available loaders */
  lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
  if (!lua_istable(L, -1))
    luaL_error(L, LUA_QL("package.loaders") " must be a table");
  lua_pushliteral(L, "");  /* error message accumulator */
  for (i = 1; ; i++) {
    lua_rawgeti(L, -2, i);  /* get a loader */
    if (lua_isnil(L, -1))
      luaL_error(L, "module " LUA_QS " not found:%s",
		 name, lua_tostring(L, -2));
    lua_pushstring(L, name);
    lua_call(L, 1, 1);  /* call it */
    if (lua_isfunction(L, -1))  /* did it find module? */
      break;  /* module loaded successfully */
    else if (lua_isstring(L, -1))  /* loader returned error message? */
      lua_concat(L, 2);  /* accumulate it */
    else
      lua_pop(L, 1);
  }
  (L->top++)->u64 = KEY_SENTINEL;
  lua_setfield(L, 2, name);  /* _LOADED[name] = sentinel */
  lua_pushstring(L, name);  /* pass name as argument to module */
  lua_call(L, 1, 1);  /* run loaded module */
  if (!lua_isnil(L, -1))  /* non-nil return? */
    lua_setfield(L, 2, name);  /* _LOADED[name] = returned value */
  lua_getfield(L, 2, name);
  if ((L->top-1)->u64 == KEY_SENTINEL) {   /* module did not set a value? */
    lua_pushboolean(L, 1);  /* use true as result */
    lua_pushvalue(L, -1);  /* extra copy to be returned */
    lua_setfield(L, 2, name);  /* _LOADED[name] = true */
  }
  lj_lib_checkfpu(L);
  return 1;
}
相关推荐
青莳吖10 分钟前
Java通过Map实现与SQL中的group by相同的逻辑
java·开发语言·sql
Buleall17 分钟前
期末考学C
java·开发语言
重生之绝世牛码19 分钟前
Java设计模式 —— 【结构型模式】外观模式详解
java·大数据·开发语言·设计模式·设计原则·外观模式
小蜗牛慢慢爬行25 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
Algorithm157635 分钟前
云原生相关的 Go 语言工程师技术路线(含博客网址导航)
开发语言·云原生·golang
shinelord明1 小时前
【再谈设计模式】享元模式~对象共享的优化妙手
开发语言·数据结构·算法·设计模式·软件工程
Monly211 小时前
Java(若依):修改Tomcat的版本
java·开发语言·tomcat
boligongzhu1 小时前
DALSA工业相机SDK二次开发(图像采集及保存)C#版
开发语言·c#·dalsa
Eric.Lee20211 小时前
moviepy将图片序列制作成视频并加载字幕 - python 实现
开发语言·python·音视频·moviepy·字幕视频合成·图像制作为视频
7yewh1 小时前
嵌入式Linux QT+OpenCV基于人脸识别的考勤系统 项目
linux·开发语言·arm开发·驱动开发·qt·opencv·嵌入式linux