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

相关文章

MybatisPlus3.3.1整合clickhouse的过程

《MybatisPlus3.3.1整合clickhouse的过程》:本文主要介绍MybatisPlus3.3.1整合clickhouse的过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定... 前言ClickHouse是俄罗斯Yandex发布的一款数据分析型数据库支持sql语法,详情可以访问官网,

IDEA下"File is read-only"可能原因分析及"找不到或无法加载主类"的问题

《IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题》:本文主要介绍IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题,具有很好的参... 目录1.File is read-only”可能原因2.“找不到或无法加载主类”问题的解决总结1.File

Dubbo之SPI机制的实现原理和优势分析

《Dubbo之SPI机制的实现原理和优势分析》:本文主要介绍Dubbo之SPI机制的实现原理和优势,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Dubbo中SPI机制的实现原理和优势JDK 中的 SPI 机制解析Dubbo 中的 SPI 机制解析总结Dubbo中

C#继承之里氏替换原则分析

《C#继承之里氏替换原则分析》:本文主要介绍C#继承之里氏替换原则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#里氏替换原则一.概念二.语法表现三.类型检查与转换总结C#里氏替换原则一.概念里氏替换原则是面向对象设计的基本原则之一:核心思想:所有引py

Spring AI 实现 STDIO和SSE MCP Server的过程详解

《SpringAI实现STDIO和SSEMCPServer的过程详解》STDIO方式是基于进程间通信,MCPClient和MCPServer运行在同一主机,主要用于本地集成、命令行工具等场景... 目录Spring AI 实现 STDIO和SSE MCP Server1.新建Spring Boot项目2.a

使用Java将实体类转换为JSON并输出到控制台的完整过程

《使用Java将实体类转换为JSON并输出到控制台的完整过程》在软件开发的过程中,Java是一种广泛使用的编程语言,而在众多应用中,数据的传输和存储经常需要使用JSON格式,用Java将实体类转换为J... 在软件开发的过程中,Java是一种广泛使用的编程语言,而在众多应用中,数据的传输和存储经常需要使用j

基于Go语言实现Base62编码的三种方式以及对比分析

《基于Go语言实现Base62编码的三种方式以及对比分析》Base62编码是一种在字符编码中使用62个字符的编码方式,在计算机科学中,,Go语言是一种静态类型、编译型语言,它由Google开发并开源,... 目录一、标准库现状与解决方案1. 标准库对比表2. 解决方案完整实现代码(含边界处理)二、关键实现细

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

PostgreSQL 序列(Sequence) 与 Oracle 序列对比差异分析

《PostgreSQL序列(Sequence)与Oracle序列对比差异分析》PostgreSQL和Oracle都提供了序列(Sequence)功能,但在实现细节和使用方式上存在一些重要差异,... 目录PostgreSQL 序列(Sequence) 与 oracle 序列对比一 基本语法对比1.1 创建序

在 PyQt 加载 UI 三种常见方法

《在PyQt加载UI三种常见方法》在PyQt中,加载UI文件通常指的是使用QtDesigner设计的.ui文件,并将其转换为Python代码,以便在PyQt应用程序中使用,这篇文章给大家介绍在... 目录方法一:使用 uic 模块动态加载 (不推荐用于大型项目)方法二:将 UI 文件编译为 python 模