美文网首页程序员
在JVM中运行LuaVM

在JVM中运行LuaVM

作者: 328cd40c64b0 | 来源:发表于2019-03-08 16:04 被阅读2次

    目前已有 luaj 项目可在JVM中执行Lua脚本,由于其编译效率和执行效率不如 luac ,所以开发了这个接口和luaj相似的、使用luac作为Lua虚拟机的Java库。

    对于使用过luaj的朋友,初学luac一定会非常懵逼,因为luaj虚拟机做了很好的封装,所以使用或理解起来非常简单;而luac只给程序员提供了一个lua环境(lua_State)、操作lua堆栈的一堆函数和把lua数据类型转为C数据类型的函数。这篇文章重点讲述本项目 LuaJava 的实现原理和使用方法。

    简单使用方法:

    Globals globals = Globals.createLState(true); //创建Lua虚拟机
    boolean result = globals.loadString("name", str);//加载lua源码
    boolean result = globals.loadData("name", data); //加载lua源码或二进制码
    if (result) result = globals.callLoadedData();//执行刚加载成功的lua脚本
    

    代码分析

    第一行,和luaj很像,创建Lua虚拟机,并获取全局表。在C层,通过lua函数创建虚拟机环境(lua_State);初始化lua自带库;初始化自定义的lua错误函数;在全局表中初始化一个存放Java Global引用的表(用来在虚拟机销毁的时候释放Global引用,后面会提到);在lua package.searchers 表中增加通过Java代码寻找对应lua文件函数(lua脚本require时会调用)。

    第二行和第三行,作用相同,加载lua脚本源码或加载二进制码,若加载失败,可通过globals.getLoadState()获取错误码,通过globals.getErrorMsg()获取错误信息。在C层,通过lua函数加载脚本。

    第四行,执行在此虚拟机中刚加载的脚本,若未加载脚本,或加载失败,则会抛出异常,若执行失败,可通过globals.getExecuteState()获取错误码,通过globals.getErrorMsg()获取错误信息。在C层,通过lua函数调用在栈顶的lua函数,并返回。

    注册静态函数

    Java代码:

    globals.registerStaticBridgeSimple("LuaBridgeClass", BridgeA.class, "bridge1", "bridge2"...); //注册Java静态方法
    

    lua代码:

    LuaBridgeClass:bridge1(params1)--调用java中的BridgeA.bridge1
    LuaBridgeClass:bridge2(params2)--调用java中的BridgeA.bridge2
    

    代码分析

    使用过luaj的同学应该知道,LuaBridgeClass:bridge1在lua的语境中,实际上是在global表或注册表中寻找名称为LuaBridgeClass的table,再在此table中或其元表(metatable)中寻找名称为bridge1相关的函数,然后将参数传入并执行。

    本项目中,原理相似,registerStaticBridgeSimple在C层创建一个表,使用键值LuaBridgeClass放入到Global表中,表中每个键对应传入的方法名,值为lua的Closure;在Closure中,存放了相关java类(jclass)和相关方法(jmethodID),在虚拟机调用此Closure时,通过JNI接口调用到java对应的方法中,并将java返回的结果依次放入堆栈中返回。

    由于jclass为非Globals引用,在函数结束时就不可用了,所以在这里会创建一个jclass的Globals引用,并将Globas引用放入到前文提到的存放Java Global引用表中。

    注册Java对象

    Java代码:

    globals.registerUserdataSimple("LuaBridgeClass2", BridgeB.class, "bridge1", "bridge1"...); //注册Java对象
    

    lua代码:

    local ud = LuaBridgeClass2(initParams)--创建一个ud实例
    ud:bridge1(params1)--调用java中的BridgeB.bridge1方法
    ud:bridge2(params2)--调用java中的BridgeB.bridge2方法
    

    代码分析

    创建UD实例和调用静态Bridge相似,在Global表或注册表中寻找名称为LuaBridgeClass2的函数,执行这个函数,返回一个lua的userdata类型。函数中通过JNI接口创建一个java对象,将对象包装成lua可识别的userdata类型,设置元表,并返回;元表在注册时已生成,调用bridge方法,会通过JNI接口调用对应的java方法,并将java返回结果依次放入堆栈中。

    java对象的内存由lua虚拟机的gc系统管理,当需要被gc时,会调用元表中__gc对应方法,释放Global引用,并调用对应java类中的__onLuaGc方法。

    详细内容请参考项目 LuaJava 的实现。

    相关文章

      网友评论

        本文标题:在JVM中运行LuaVM

        本文链接:https://www.haomeiwen.com/subject/amgrpqtx.html