LuaJit分析(八)LuaJit预编译库函数加载过程

2024-08-30 08:28

本文主要是介绍LuaJit分析(八)LuaJit预编译库函数加载过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在luajit的pmain函数中,在执行命令行输入的参数之前,会执行如下函数:

luaL_openlibs(L);

该函数位于lib_init.c中,实现如下:

LUALIB_API void luaL_openlibs(lua_State *L)
{const luaL_Reg *lib;for (lib = lj_lib_load; lib->func; lib++) {lua_pushcfunction(L, lib->func);lua_pushstring(L, lib->name);lua_call(L, 1, 0);}luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD",sizeof(lj_lib_preload)/sizeof(lj_lib_preload[0])-1);for (lib = lj_lib_preload; lib->func; lib++) {lua_pushcfunction(L, lib->func);lua_setfield(L, -2, lib->name);}lua_pop(L, 1);
}

它通过编遍历lj_lib_load,加载所有的库函数,lj_lib_load定义如下:

static const luaL_Reg lj_lib_load[] = {{ "",     luaopen_base },{ LUA_LOADLIBNAME,  luaopen_package },{ LUA_TABLIBNAME, luaopen_table },{ LUA_IOLIBNAME,  luaopen_io },{ LUA_OSLIBNAME,  luaopen_os },{ LUA_STRLIBNAME, luaopen_string },{ LUA_MATHLIBNAME,  luaopen_math },{ LUA_DBLIBNAME,  luaopen_debug },{ LUA_BITLIBNAME, luaopen_bit },{ LUA_JITLIBNAME, luaopen_jit },{ LUA_FFILIBNAME, luaopen_ffi },{ NULL,   NULL }
};

这里定义了luajit中的库函数类型,第二个参数是定义的C函数,以luaopen_base为例,定义如下,位于lib_base.c中:

LUALIB_API int luaopen_base(lua_State *L)
{/* NOBARRIER: Table and value are the same. */GCtab *env = tabref(L->env);settabV(L, lj_tab_setstr(L, env, lj_str_newlit(L, "_G")), env);lua_pushliteral(L, LUA_VERSION);  /* top-3. */newproxy_weaktable(L);  /* top-2. */LJ_LIB_REG(L, "_G", base);LJ_LIB_REG(L, LUA_COLIBNAME, coroutine);return 2;
}

其中有一个宏定义 LJ_LIB_REG 用于注册库函数,宏定义如下:

#define LJ_LIB_REG(L, regname, name) \lj_lib_register(L, regname, lj_lib_init_##name, lj_lib_cf_##name)

从函数名即可看出是注册函数用,lj_lib_register位于lj_lib.c中,实现如下:

void lj_lib_register(lua_State *L, const char *libname,const uint8_t *p, const lua_CFunction *cf)
{GCtab *env = tabref(L->env);GCfunc *ofn = NULL;int ffid = *p++;BCIns *bcff = &L2GG(L)->bcff[*p++];GCtab *tab = lib_create_table(L, libname, *p++);ptrdiff_t tpos = L->top - L->base;/* Avoid barriers further down. */lj_gc_anybarriert(L, tab);tab->nomm = 0;for (;;) {uint32_t tag = *p++;MSize len = tag & LIBINIT_LENMASK;tag &= LIBINIT_TAGMASK;if (tag != LIBINIT_STRING) {const char *name;MSize nuv = (MSize)(L->top - L->base - tpos);GCfunc *fn = lj_func_newC(L, nuv, env);if (nuv) {L->top = L->base + tpos;memcpy(fn->c.upvalue, L->top, sizeof(TValue)*nuv);}fn->c.ffid = (uint8_t)(ffid++);name = (const char *)p;p += len;if (tag == LIBINIT_CF)setmref(fn->c.pc, &G(L)->bc_cfunc_int);elsesetmref(fn->c.pc, bcff++);if (tag == LIBINIT_ASM_)fn->c.f = ofn->c.f;  /* Copy handler from previous function. */elsefn->c.f = *cf++;  /* Get cf or handler from C function table. */if (len) {/* NOBARRIER: See above for common barrier. */setfuncV(L, lj_tab_setstr(L, tab, lj_str_new(L, name, len)), fn);}ofn = fn;} else {switch (tag | len) {case LIBINIT_LUA:p = lib_read_lfunc(L, p, tab);break;case LIBINIT_SET:L->top -= 2;if (tvisstr(L->top+1) && strV(L->top+1)->len == 0)env = tabV(L->top);else  /* NOBARRIER: See above for common barrier. */copyTV(L, lj_tab_set(L, tab, L->top+1), L->top);break;case LIBINIT_NUMBER:memcpy(&L->top->n, p, sizeof(double));L->top++;p += sizeof(double);break;case LIBINIT_COPY:copyTV(L, L->top, L->top - *p++);L->top++;break;case LIBINIT_LASTCL:setfuncV(L, L->top++, ofn);break;case LIBINIT_FFID:ffid++;break;case LIBINIT_END:return;default:setstrV(L, L->top++, lj_str_new(L, (const char *)p, len));p += len;break;}}}
}

其中下面这句:

switch (tag | len) {case LIBINIT_LUA:p = lib_read_lfunc(L, p, tab);break;

当是一个LUA的库函数时,使用lib_read_lfunc函数读取,即预编译的那么几个库函数,lib_read_lfunc函数实现如下:

static const uint8_t *lib_read_lfunc(lua_State *L, const uint8_t *p, GCtab *tab)
{int len = *p++;GCstr *name = lj_str_new(L, (const char *)p, len);LexState ls;GCproto *pt;GCfunc *fn;memset(&ls, 0, sizeof(ls));ls.L = L;ls.p = (const char *)(p+len);ls.pe = (const char *)~(uintptr_t)0;ls.c = -1;ls.level = (BCDUMP_F_STRIP|(LJ_BE*BCDUMP_F_BE));ls.chunkname = name;pt = lj_bcread_proto(&ls);pt->firstline = ~(BCLine)0;fn = lj_func_newL_empty(L, pt, tabref(L->env));/* NOBARRIER: See below for common barrier. */setfuncV(L, lj_tab_setstr(L, tab, name), fn);return (const uint8_t *)ls.p;
}

可以看到,该函数通过调用lj_bcread_proto读取luajit字节码的原型数据,接着使用原型创建一个新函数,然后保存该函数变量

这篇关于LuaJit分析(八)LuaJit预编译库函数加载过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1120296

相关文章

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

浅析Spring如何控制Bean的加载顺序

《浅析Spring如何控制Bean的加载顺序》在大多数情况下,我们不需要手动控制Bean的加载顺序,因为Spring的IoC容器足够智能,但在某些特殊场景下,这种隐式的依赖关系可能不存在,下面我们就来... 目录核心原则:依赖驱动加载手动控制 Bean 加载顺序的方法方法 1:使用@DependsOn(最直

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

SpringBoot整合liteflow的详细过程

《SpringBoot整合liteflow的详细过程》:本文主要介绍SpringBoot整合liteflow的详细过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋...  liteflow 是什么? 能做什么?总之一句话:能帮你规范写代码逻辑 ,编排并解耦业务逻辑,代码

Java中调用数据库存储过程的示例代码

《Java中调用数据库存储过程的示例代码》本文介绍Java通过JDBC调用数据库存储过程的方法,涵盖参数类型、执行步骤及数据库差异,需注意异常处理与资源管理,以优化性能并实现复杂业务逻辑,感兴趣的朋友... 目录一、存储过程概述二、Java调用存储过程的基本javascript步骤三、Java调用存储过程示

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性: