diff --git a/build.gradle b/build.gradle index ee88c213..6f87a894 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ group 'party.iroiro.luajava' -version (System.getenv('IS_RELEASE') == 'true' ? '3.1.2' : '3.1.2-SNAPSHOT') +version(System.getenv('IS_RELEASE') == 'true' ? '3.1.3' : '3.1.3-SNAPSHOT') buildscript { repositories { @@ -41,14 +41,22 @@ apply from: 'publish.gradle' apply from: 'jacoco.gradle' task allJavadoc(type: Javadoc) { - source subprojects.findAll { !['example', 'android', 'android-test'].contains(it.name) } - .collect { - it.sourceSets.main.allJava - } - classpath = files(subprojects.findAll { !['example', 'android', 'android-test'].contains(it.name) } - .collect { - it.sourceSets.main.compileClasspath - }) - exclude '**/party/iroiro/luajava/util/**' + Set projects = [ + 'lua51', + 'lua52', + 'lua53', + 'lua54', + 'luajit', + 'luajava', + ] + source subprojects + .findAll { projects.contains(it.name) } + .collect { it.sourceSets.main.allJava } + classpath = files(subprojects.findAll { projects.contains(it.name) } + .collect { it.sourceSets.main.compileClasspath }) + exclude( + '**/party/iroiro/luajava/util/**', + '**/party/iroiro/luajava/cleaner/**', + ) destinationDir = file("${buildDir}/docs/javadoc") } diff --git a/docs/examples/java.md b/docs/examples/java.md index 0add0000..858288d8 100644 --- a/docs/examples/java.md +++ b/docs/examples/java.md @@ -180,3 +180,53 @@ See [`unref(int)`](../javadoc/party/iroiro/luajava/Lua.html#unref(int)) and [`unRef(int, int)`](../javadoc/party/iroiro/luajava/Lua.html#unRef(int,int)) for more info. +### Pre-compiled chunks + +According to the Lua reference manual: + +> Chunks can also be pre-compiled into binary form; +> see program luac for details. +> Programs in source and compiled forms are interchangeable; +> Lua automatically detects the file type and acts accordingly. + +Replacing Lua source files with pre-compiled chunks speeds up loading. +However, according to `luac` manual: + +> Precompiled chunks are *not* portable across different architectures. +> Moreover, the internal format of precompiled chunks is likely to change +> when a new version of Lua is released. Make sure you save the source +> files of all Lua programs that you precompile. + +Binary chunks compiled on one platform may not run on another. +Since we are using Java / JVM-based languages, this is absolutely not desirable. +To work around this, you may either: +1. Provide precompiled chunks for all your target platforms; +2. Or compile them at runtime for once and just reuse the compiled binaries. + +Here is a tiny example for the second approach: + +:::: code-group +::: code-group-item Use lua_dump +```java +Lua L = getYourLuaInstance(); +ByteBuffer code = readFromFile("MyScript.lua"); +// L.load(...) pushes on stack a precompiled function +L.load(code, "MyScript.lua"); +// L.dump() calls lua_dump, dumping the precompiled binary +ByteBuffer precompiledChunk = L.dump(); +``` +::: +::: code-group-item Use string.dump +```java +Lua L = getYourLuaInstance(); +L.openLibrary("string"); +// string.dump(...) returns the precompiled binary as a Lua string +L.run("return string.dump(function(a, b) return a + b end)"); +// L.toBuffer(...) stores the precompiled binary into a buffer and returns it +ByteBuffer precompiledChunk = L.toBuffer(-1); +``` +::: +:::: + +Dumping functions involves some more Lua knowledge such as up-values and environments. +What these terms mean might differ between versions and is not a topic of this document. diff --git a/example/suite/src/main/java/party/iroiro/luajava/LuaTestSuite.java b/example/suite/src/main/java/party/iroiro/luajava/LuaTestSuite.java index 6eeba623..d96eae68 100644 --- a/example/suite/src/main/java/party/iroiro/luajava/LuaTestSuite.java +++ b/example/suite/src/main/java/party/iroiro/luajava/LuaTestSuite.java @@ -41,6 +41,7 @@ public LuaTestSuite(T L, LuaTestSupplier constructor) { public void test() { L.openLibraries(); LuaScriptSuite.addAssertThrows(L); + testDump(); testException(); testExternalLoader(); testGc(); @@ -60,6 +61,97 @@ public void test() { testThreads(); } + private void testDump() { + try (T L = constructor.get(); + T J = constructor.get()) { + // Simple functions + assertEquals(OK, L.run("return function(a, b) return a + b end")); + ByteBuffer add = L.dump(); + assertNotNull(add); + for (int i = 0; i < 100; i += 17) { + for (int j = 0; j < 100; j += 37) { + assertEquals(OK, J.load(add, "addition.lua")); + J.push(i); + J.push(j); + assertEquals(OK, J.pCall(2, 1)); + assertEquals(i + j, J.toNumber(-1), 0.000001); + J.pop(1); + } + } + L.pop(1); + + // toBuffer with string.dump + L.openLibrary("string"); + assertEquals(OK, L.run("return string.dump(function(a, b) return a + b end)")); + ByteBuffer dumpedAdd = L.toBuffer(-1); + assertNotNull(dumpedAdd); + for (int i = 0; i < 100; i += 17) { + for (int j = 0; j < 100; j += 37) { + assertEquals(OK, J.load(dumpedAdd, "stringDumpedAdd.lua")); + J.push(i); + J.push(j); + assertEquals(OK, J.pCall(2, 1)); + assertEquals(i + j, J.toNumber(-1), 0.000001); + J.pop(1); + } + } + L.pop(1); + + // Non-functions + L.createTable(0, 0); + assertNull(L.dump()); + L.pop(1); + L.pushNil(); + assertNull(L.dump()); + L.pop(1); + + // C functions + assertEquals(OK, L.run("return java.new")); + assertTrue(L.isFunction(-1)); + assertNull(L.dump()); + L.pop(1); + + // Functions with up-values + assertEquals(OK, L.run("value = 1000")); + assertEquals(OK, L.run("return function(b) return b + value end")); + ByteBuffer upAdd = L.dump(); + assertNotNull(upAdd); + assertEquals(OK, J.load(upAdd, "upAdd.lua")); + J.push(24); + assertEquals(RUNTIME, J.pCall(1, 1)); + assertEquals(OK, J.run("value = 1000")); + assertEquals(OK, J.load(upAdd, "upAdd.lua")); + J.push(24); + assertEquals(OK, J.pCall(1, 1)); + assertEquals(1024., J.toNumber(-1), 0.000001); + L.pop(1); + + // Large string to buffer + J.openLibrary("string"); + // Aiming 1 MB + J.run("s = 's'; for i = 1, 11 do s = string.rep(s, 4) end"); + J.getGlobal("s"); + int size = 4 * 1024 * 1024; + assertEquals(size, J.rawLength(-1)); + ByteBuffer buffer = J.toBuffer(-1); + assertNotNull(buffer); + assertEquals(size, buffer.capacity()); + for (int i = 0; i < size; i++) { + assertEquals('s', buffer.get(i)); + } + ByteBuffer direct = J.toDirectBuffer(-1); + assertNotNull(direct); + assertTrue(direct.isDirect()); + assertTrue(direct.isReadOnly()); + assertEquals(size, direct.limit()); + assertEquals(size, direct.capacity()); + J.pop(1); + J.createTable(0, 0); + assertNull(J.toDirectBuffer(-1)); + J.pop(1); + } + } + private void testGc() { try (T L = constructor.get()) { L.createTable(0, 100000); diff --git a/jni/luajava/jua.cpp b/jni/luajava/jua.cpp index dd1bc3e4..6856efe3 100644 --- a/jni/luajava/jua.cpp +++ b/jni/luajava/jua.cpp @@ -4,6 +4,8 @@ #include "jua.h" #include "juaapi.h" +#include + // For template usage const char JAVA_CLASS_META_REGISTRY[] = "__jclass__"; const char JAVA_OBJECT_META_REGISTRY[] = "__jobject__"; @@ -36,6 +38,7 @@ jmethodID juaapi_import = NULL; jmethodID juaapi_proxy = NULL; jmethodID juaapi_unwrap = NULL; jmethodID juaapi_load = NULL; +jmethodID juaapi_allocatedirect = NULL; // java.lang.Throwable jclass java_lang_throwable_class = NULL; jmethodID throwable_getmessage = NULL; @@ -162,6 +165,8 @@ int initBindings(JNIEnv * env) { "unwrap", "(ILjava/lang/Object;)I"); juaapi_load = bindJavaStaticMethod(env, juaapi_class, "load", "(ILjava/lang/String;)I"); + juaapi_allocatedirect = bindJavaStaticMethod(env, juaapi_class, + "allocateDirect", "(I)Ljava/nio/ByteBuffer;"); if (java_lang_class_class == NULL || java_lang_class_forname == NULL || java_lang_throwable_class == NULL @@ -186,7 +191,8 @@ int initBindings(JNIEnv * env) { || juaapi_luaify == NULL || juaapi_import == NULL || juaapi_proxy == NULL - || juaapi_load == NULL) { + || juaapi_load == NULL + || juaapi_allocatedirect == NULL) { return -1; } else { return 0; @@ -413,3 +419,91 @@ int luaJ_insertloader(lua_State * L, const char * searchers) { void luaJ_gc(lua_State * L) { lua_gc(L, LUA_GCCOLLECT, 0); } + +static jint nextCapacity(jint capacity, jint size) { + while (capacity > 0 && capacity < size) { + capacity <<= 1; + } + return capacity; +} + +struct DumpBuffer { + unsigned char * buffer = NULL; + jint size = 0; + jint capacity = 0; +}; + +int dumpBufferWriter(lua_State * L, const void * p, size_t sz, void * ud) { + DumpBuffer * dump = (DumpBuffer *) ud; + jint size = dump->size + sz; + if (size < 0) { + /* Overflows */ + return 1; + } + if (size > dump->capacity) { + jint capacity = nextCapacity(dump->capacity, size); + if (capacity <= 0) { + /* Overflows */ + return 1; + } + void * buffer = realloc(dump->buffer, capacity); + if (buffer == NULL) { + return 1; + } + dump->capacity = capacity; + dump->buffer = (unsigned char *) buffer; + } + memcpy(dump->buffer + dump->size, p, sz); + dump->size = size; + return 0; +} + +static jobject toBuffer(JNIEnv * env, lua_State * L, const void * ptr, jint size) { + jobject buffer = env->CallStaticObjectMethod(juaapi_class, juaapi_allocatedirect, (jint) size); + if (checkIfError(env, L)) { + return NULL; + } + void * addr = env->GetDirectBufferAddress(buffer); + memcpy(addr, ptr, size); + return buffer; +} + +jobject luaJ_dumptobuffer(lua_State * L) { + DumpBuffer dump; + dump.size = 0; + dump.capacity = 4096; + dump.buffer = (unsigned char *) malloc(dump.capacity); + if (luaJ_dump(L, dumpBufferWriter, &dump)) { + free(dump.buffer); + return NULL; + } + JNIEnv * env = getJNIEnv(L); + jobject buffer = toBuffer(env, L, dump.buffer, dump.size); + free(dump.buffer); + return buffer; +} + +jobject luaJ_tobuffer(lua_State * L, int i) { + size_t len; + const char * str = lua_tolstring(L, i, &len); + if (str == NULL) { + return NULL; + } + JNIEnv * env = getJNIEnv(L); + return toBuffer(env, L, str, len); +} + +jobject luaJ_todirectbuffer(lua_State * L, int i) { + size_t len; + const void * str = lua_tolstring(L, i, &len); + if (str == NULL) { + return NULL; + } + JNIEnv * env = getJNIEnv(L); + /* Have to const_cast. We are to use it "asReadOnlyBuffer" on the Java side. */ + jobject buffer = env->NewDirectByteBuffer(const_cast(str), (jlong) len); + if (checkIfError(env, L)) { + return NULL; + } + return buffer; +} diff --git a/jni/luajava/jua.h b/jni/luajava/jua.h index 3e215856..4e93b1ac 100644 --- a/jni/luajava/jua.h +++ b/jni/luajava/jua.h @@ -55,6 +55,10 @@ void luaJ_pusharray(JNIEnv * env, lua_State * L, jobject array); jobject luaJ_toobject(lua_State * L, int index); int luaJ_isobject(lua_State * L, int index); +jobject luaJ_dumptobuffer(lua_State * L); +jobject luaJ_tobuffer(lua_State * L, int i); +jobject luaJ_todirectbuffer(lua_State * L, int i); + int luaJ_insertloader(lua_State * L, const char * searchers); int luaJ_invokespecial(JNIEnv * env, lua_State * L, diff --git a/lua51/jni/mod/luacomp.h b/lua51/jni/mod/luacomp.h index 34e4f73e..5241029d 100644 --- a/lua51/jni/mod/luacomp.h +++ b/lua51/jni/mod/luacomp.h @@ -89,4 +89,8 @@ static int luaJ_initloader(lua_State * L) { return luaJ_insertloader(L, "loaders"); } +static int luaJ_dump (lua_State *L, lua_Writer writer, void *data) { + return lua_dump (L, writer, data); +} + #endif /* !LUACOMP_H */ \ No newline at end of file diff --git a/lua51/src/main/java/party/iroiro/luajava/Lua51Natives.java b/lua51/src/main/java/party/iroiro/luajava/Lua51Natives.java index 78f75ae9..3ba8570b 100644 --- a/lua51/src/main/java/party/iroiro/luajava/Lua51Natives.java +++ b/lua51/src/main/java/party/iroiro/luajava/Lua51Natives.java @@ -3701,4 +3701,60 @@ protected Lua51Natives() throws IllegalStateException { */ + /** + * A wrapper function + * + *

+ * See lua_dump + *

+ * + * @param ptr the lua_State* pointer + * @return see description + */ + protected native Object luaJ_dumptobuffer(long ptr); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_dumptobuffer((lua_State *) L); + return returnValueReceiver; + */ + + + /** + * A wrapper function + * + *

+ * See lua_tolstring + *

+ * + * @param ptr the lua_State* pointer + * @param index the stack position of the element + * @return see description + */ + protected native Object luaJ_tobuffer(long ptr, int index); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_tobuffer((lua_State *) L, (int) index); + return returnValueReceiver; + */ + + + /** + * A wrapper function + * + *

+ * See lua_tolstring + *

+ * + * @param ptr the lua_State* pointer + * @param index the stack position of the element + * @return see description + */ + protected native Object luaJ_todirectbuffer(long ptr, int index); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_todirectbuffer((lua_State *) L, (int) index); + return returnValueReceiver; + */ + + } \ No newline at end of file diff --git a/lua52/jni/mod/luacomp.h b/lua52/jni/mod/luacomp.h index b876994d..5f857bb0 100644 --- a/lua52/jni/mod/luacomp.h +++ b/lua52/jni/mod/luacomp.h @@ -70,4 +70,8 @@ static int luaJ_initloader(lua_State * L) { return luaJ_insertloader(L, "searchers"); } +static int luaJ_dump (lua_State *L, lua_Writer writer, void *data) { + return lua_dump (L, writer, data); +} + #endif /* !LUACOMP_H */ diff --git a/lua52/src/main/java/party/iroiro/luajava/Lua52Natives.java b/lua52/src/main/java/party/iroiro/luajava/Lua52Natives.java index 2798ee53..d88e4696 100644 --- a/lua52/src/main/java/party/iroiro/luajava/Lua52Natives.java +++ b/lua52/src/main/java/party/iroiro/luajava/Lua52Natives.java @@ -4502,4 +4502,60 @@ protected Lua52Natives() throws IllegalStateException { */ + /** + * A wrapper function + * + *

+ * See lua_dump + *

+ * + * @param ptr the lua_State* pointer + * @return see description + */ + protected native Object luaJ_dumptobuffer(long ptr); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_dumptobuffer((lua_State *) L); + return returnValueReceiver; + */ + + + /** + * A wrapper function + * + *

+ * See lua_tolstring + *

+ * + * @param ptr the lua_State* pointer + * @param index the stack position of the element + * @return see description + */ + protected native Object luaJ_tobuffer(long ptr, int index); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_tobuffer((lua_State *) L, (int) index); + return returnValueReceiver; + */ + + + /** + * A wrapper function + * + *

+ * See lua_tolstring + *

+ * + * @param ptr the lua_State* pointer + * @param index the stack position of the element + * @return see description + */ + protected native Object luaJ_todirectbuffer(long ptr, int index); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_todirectbuffer((lua_State *) L, (int) index); + return returnValueReceiver; + */ + + } \ No newline at end of file diff --git a/lua53/jni/mod/luacomp.h b/lua53/jni/mod/luacomp.h index b876994d..6734f23a 100644 --- a/lua53/jni/mod/luacomp.h +++ b/lua53/jni/mod/luacomp.h @@ -70,4 +70,8 @@ static int luaJ_initloader(lua_State * L) { return luaJ_insertloader(L, "searchers"); } +static int luaJ_dump (lua_State *L, lua_Writer writer, void *data) { + return lua_dump (L, writer, data, true); +} + #endif /* !LUACOMP_H */ diff --git a/lua53/src/main/java/party/iroiro/luajava/Lua53Natives.java b/lua53/src/main/java/party/iroiro/luajava/Lua53Natives.java index 47c01941..1815a7fa 100644 --- a/lua53/src/main/java/party/iroiro/luajava/Lua53Natives.java +++ b/lua53/src/main/java/party/iroiro/luajava/Lua53Natives.java @@ -4679,4 +4679,60 @@ protected Lua53Natives() throws IllegalStateException { */ + /** + * A wrapper function + * + *

+ * See lua_dump + *

+ * + * @param ptr the lua_State* pointer + * @return see description + */ + protected native Object luaJ_dumptobuffer(long ptr); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_dumptobuffer((lua_State *) L); + return returnValueReceiver; + */ + + + /** + * A wrapper function + * + *

+ * See lua_tolstring + *

+ * + * @param ptr the lua_State* pointer + * @param index the stack position of the element + * @return see description + */ + protected native Object luaJ_tobuffer(long ptr, int index); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_tobuffer((lua_State *) L, (int) index); + return returnValueReceiver; + */ + + + /** + * A wrapper function + * + *

+ * See lua_tolstring + *

+ * + * @param ptr the lua_State* pointer + * @param index the stack position of the element + * @return see description + */ + protected native Object luaJ_todirectbuffer(long ptr, int index); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_todirectbuffer((lua_State *) L, (int) index); + return returnValueReceiver; + */ + + } \ No newline at end of file diff --git a/lua54/jni/mod/luacomp.h b/lua54/jni/mod/luacomp.h index 38898ff6..cc1c1176 100644 --- a/lua54/jni/mod/luacomp.h +++ b/lua54/jni/mod/luacomp.h @@ -71,4 +71,8 @@ static int luaJ_initloader(lua_State * L) { return luaJ_insertloader(L, "searchers"); } +static int luaJ_dump (lua_State *L, lua_Writer writer, void *data) { + return lua_dump (L, writer, data, true); +} + #endif /* !LUACOMP_H */ diff --git a/lua54/src/main/java/party/iroiro/luajava/Lua54Natives.java b/lua54/src/main/java/party/iroiro/luajava/Lua54Natives.java index c05a29f2..aa5e4f2a 100644 --- a/lua54/src/main/java/party/iroiro/luajava/Lua54Natives.java +++ b/lua54/src/main/java/party/iroiro/luajava/Lua54Natives.java @@ -4736,4 +4736,60 @@ protected Lua54Natives() throws IllegalStateException { */ + /** + * A wrapper function + * + *

+ * See lua_dump + *

+ * + * @param ptr the lua_State* pointer + * @return see description + */ + protected native Object luaJ_dumptobuffer(long ptr); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_dumptobuffer((lua_State *) L); + return returnValueReceiver; + */ + + + /** + * A wrapper function + * + *

+ * See lua_tolstring + *

+ * + * @param ptr the lua_State* pointer + * @param index the stack position of the element + * @return see description + */ + protected native Object luaJ_tobuffer(long ptr, int index); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_tobuffer((lua_State *) L, (int) index); + return returnValueReceiver; + */ + + + /** + * A wrapper function + * + *

+ * See lua_tolstring + *

+ * + * @param ptr the lua_State* pointer + * @param index the stack position of the element + * @return see description + */ + protected native Object luaJ_todirectbuffer(long ptr, int index); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_todirectbuffer((lua_State *) L, (int) index); + return returnValueReceiver; + */ + + } \ No newline at end of file diff --git a/luajava/build.gradle b/luajava/build.gradle index c33e706d..5711c7e6 100644 --- a/luajava/build.gradle +++ b/luajava/build.gradle @@ -13,7 +13,7 @@ version = rootProject.version dependencies { implementation 'com.badlogicgames.gdx:gdx-jnigen-loader:2.3.1' - implementation 'com.google.errorprone:error_prone_annotations:2.14.0' + implementation 'com.google.errorprone:error_prone_annotations:2.15.0' implementation 'org.jetbrains:annotations:23.0.0' } diff --git a/luajava/src/main/java/party/iroiro/luajava/AbstractLua.java b/luajava/src/main/java/party/iroiro/luajava/AbstractLua.java index 9ec781a6..79baf6b1 100644 --- a/luajava/src/main/java/party/iroiro/luajava/AbstractLua.java +++ b/luajava/src/main/java/party/iroiro/luajava/AbstractLua.java @@ -37,6 +37,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.nio.Buffer; +import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -350,6 +351,21 @@ public boolean toBoolean(int index) { return C.lua_tostring(L, index); } + @Override + public @Nullable ByteBuffer toBuffer(int index) { + return (ByteBuffer) C.luaJ_tobuffer(L, index); + } + + @Override + public @Nullable ByteBuffer toDirectBuffer(int index) { + ByteBuffer buffer = (ByteBuffer) C.luaJ_todirectbuffer(L, index); + if (buffer == null) { + return null; + } else { + return buffer.asReadOnlyBuffer(); + } + } + @Override public @Nullable Object toJavaObject(int index) { return C.luaJ_toobject(L, index); @@ -563,6 +579,11 @@ public LuaError run(Buffer buffer, String name) { } } + @Override + public ByteBuffer dump() { + return (ByteBuffer) C.luaJ_dumptobuffer(L); + } + @Override public LuaError pCall(int nArgs, int nResults) { checkStack(Math.max(nResults - nArgs - 1, 0)); diff --git a/luajava/src/main/java/party/iroiro/luajava/JuaAPI.java b/luajava/src/main/java/party/iroiro/luajava/JuaAPI.java index f78c6e61..bde25af2 100644 --- a/luajava/src/main/java/party/iroiro/luajava/JuaAPI.java +++ b/luajava/src/main/java/party/iroiro/luajava/JuaAPI.java @@ -28,6 +28,7 @@ import party.iroiro.luajava.value.LuaValue; import java.lang.reflect.*; +import java.nio.ByteBuffer; import java.util.*; import java.util.regex.Pattern; @@ -38,6 +39,17 @@ *

*/ public abstract class JuaAPI { + /** + * Allocates a direct buffer whose memory is managed by Java + * + * @param size the buffer size + * @return a direct buffer + */ + @SuppressWarnings("unused") + public static ByteBuffer allocateDirect(int size) { + return ByteBuffer.allocateDirect(size); + } + /** * Pushes on stack the backing Lua table for a proxy * @@ -188,6 +200,7 @@ public static int threadNewId(int mainId, long ptr) { /** * Closes a sub-thread + * * @param id the thread id * @return 1 */ diff --git a/luajava/src/main/java/party/iroiro/luajava/Lua.java b/luajava/src/main/java/party/iroiro/luajava/Lua.java index 39d4f9df..31690e6a 100644 --- a/luajava/src/main/java/party/iroiro/luajava/Lua.java +++ b/luajava/src/main/java/party/iroiro/luajava/Lua.java @@ -27,6 +27,7 @@ import party.iroiro.luajava.value.LuaValue; import java.nio.Buffer; +import java.nio.ByteBuffer; import java.util.*; /** @@ -229,6 +230,33 @@ public interface Lua extends AutoCloseable { */ @Nullable String toString(int index); + /** + * Creates a {@link java.nio.ByteBuffer} from the string at the specific index + * + *

+ * You may want to use this instead of {@link #toString(int)} when the string is binary + * (e.g., those returned by {@code string.dump} and contains null characters). + *

+ * + * @param index the stack index + * @return the created buffer + */ + @Nullable ByteBuffer toBuffer(int index); + + /** + * Creates a read-only direct {@link java.nio.ByteBuffer} from the string at the specific index + * + *

+ * The memory of this buffer is managed by Lua. + * So you should never use the buffer after poping the corresponding value + * from the Lua stack. + *

+ * + * @param index the stack index + * @return the created read-only buffer + */ + @Nullable ByteBuffer toDirectBuffer(int index); + /** * Get the element at the specified stack position, if the element is a Java object / array / class * @@ -580,6 +608,20 @@ public interface Lua extends AutoCloseable { */ LuaError run(Buffer buffer, String name); + /** + * Dumps a function as a binary chunk + * + *

+ * Receives a Lua function on the top of the stack + * and produces a binary chunk that, + * if loaded again, results in a function equivalent to the one dumped. + *

+ * + * @return the binary chunk, null if an error occurred + */ + @Nullable + ByteBuffer dump(); + /** * Calls a function in protected mode * diff --git a/luajava/src/main/java/party/iroiro/luajava/LuaNative.java b/luajava/src/main/java/party/iroiro/luajava/LuaNative.java index 2eebfb01..3e310ddf 100644 --- a/luajava/src/main/java/party/iroiro/luajava/LuaNative.java +++ b/luajava/src/main/java/party/iroiro/luajava/LuaNative.java @@ -1281,6 +1281,71 @@ public abstract class LuaNative { */ protected abstract long lua_touserdata(long ptr, int index); + /** + * Wrapper of lua_dump + * + *

+     * [-0, +0, m]
+     * 
+ * + *

+     * int lua_dump (lua_State *L, lua_Writer writer, void *data);
+     * 
+ * + *

+ * Dumps a function as a binary chunk. + * Receives a Lua function on the top of the stack + * and produces a binary chunk that, + * if loaded again, results in a function equivalent to the one dumped. + * As it produces parts of the chunk, + * lua_dump + * calls function writer (see lua_Writer) + * with the given data to write them. + *

+ * + *

+ * The value returned is the error code returned by the last call to the writer; + * 0 means no errors. + *

+ * + *

+ * This function does not pop the Lua function from the stack. + *

+ * + * @param ptr the lua_State* pointer + * @return a nullable {@link java.nio.ByteBuffer} containing the dumped binary + */ + protected abstract Object luaJ_dumptobuffer(long ptr); + + /** + * Creates a {@link java.nio.ByteBuffer} from the string at the specific index + * + *

+ * This method copies the content of the string into a buffer managed by Java, + * and you are safe to pop the string from Lua stack. + *

+ * + * @param ptr the lua_State* pointer + * @param index the stack position of the element + * @return a nullable {@link java.nio.ByteBuffer} containing the string + */ + protected abstract Object luaJ_tobuffer(long ptr, int index); + + /** + * Creates a direct {@link java.nio.ByteBuffer} backed by the string at the stack index + * + *

+ * This method creates a buffer that directly accesses the memory managed by Lua. + * You are not expected to modify the content of the buffer, + * and the buffer will become invalid after the string. + *

+ * + * @param ptr the lua_State* pointer + * @param index the stack position of the element + * @return a nullable {@link java.nio.ByteBuffer} containing the string + */ + protected abstract Object luaJ_todirectbuffer(long ptr, int index); + /** * A wrapper function * diff --git a/luajit/jni/mod/luacomp.h b/luajit/jni/mod/luacomp.h index 66f7e2a2..00767359 100644 --- a/luajit/jni/mod/luacomp.h +++ b/luajit/jni/mod/luacomp.h @@ -70,4 +70,8 @@ static int luaJ_initloader(lua_State * L) { return luaJ_insertloader(L, "loaders"); } +static int luaJ_dump (lua_State *L, lua_Writer writer, void *data) { + return lua_dump (L, writer, data); +} + #endif /* !LUACOMP_H */ \ No newline at end of file diff --git a/luajit/src/main/java/party/iroiro/luajava/LuaJitNatives.java b/luajit/src/main/java/party/iroiro/luajava/LuaJitNatives.java index e6a2c1ac..f58374a6 100644 --- a/luajit/src/main/java/party/iroiro/luajava/LuaJitNatives.java +++ b/luajit/src/main/java/party/iroiro/luajava/LuaJitNatives.java @@ -3696,9 +3696,65 @@ protected LuaJitNatives() throws IllegalStateException { */ protected native void luaJ_gc(long ptr); /* lua_State * L = (lua_State *) ptr; - + luaJ_gc((lua_State *) L); */ + /** + * A wrapper function + * + *

+ * See lua_dump + *

+ * + * @param ptr the lua_State* pointer + * @return see description + */ + protected native Object luaJ_dumptobuffer(long ptr); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_dumptobuffer((lua_State *) L); + return returnValueReceiver; + */ + + + /** + * A wrapper function + * + *

+ * See lua_tolstring + *

+ * + * @param ptr the lua_State* pointer + * @param index the stack position of the element + * @return see description + */ + protected native Object luaJ_tobuffer(long ptr, int index); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_tobuffer((lua_State *) L, (int) index); + return returnValueReceiver; + */ + + + /** + * A wrapper function + * + *

+ * See lua_tolstring + *

+ * + * @param ptr the lua_State* pointer + * @param index the stack position of the element + * @return see description + */ + protected native Object luaJ_todirectbuffer(long ptr, int index); /* + lua_State * L = (lua_State *) ptr; + + jobject returnValueReceiver = (jobject) luaJ_todirectbuffer((lua_State *) L, (int) index); + return returnValueReceiver; + */ + + } \ No newline at end of file diff --git a/scripts/comm-abstract.py b/scripts/comm-abstract.py index 6caf75a1..019668dd 100644 --- a/scripts/comm-abstract.py +++ b/scripts/comm-abstract.py @@ -1,7 +1,6 @@ #!/bin/python -# python scripts/comm-abstract.py \ -# lua*/src/main/java/party/iroiro/luajava/*Natives* +# python scripts/comm-abstract.py lua*/src/main/java/party/iroiro/luajava/*Natives* import sys import re diff --git a/scripts/comm-abstract.sh b/scripts/comm-abstract.sh deleted file mode 100644 index bb1783ce..00000000 --- a/scripts/comm-abstract.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# bash jni/scripts/comm-abstract.sh > src/main/java/party/iroiro/jua/LuaNative.java - -COMM12=`comm -12 <(grep '^ protected' lua51/src/main/java/party/iroiro/jua/Lua51Natives.java| sort) <(grep '^ protected' lua52/src/main/java/party/iroiro/jua/Lua52Natives.java| sort)` -COMM123=`comm -12 <(echo "$COMM12") <(grep '^ protected' lua53/src/main/java/party/iroiro/jua/Lua53Natives.java| sort)` -COMM1234=`comm -12 <(echo "$COMM123") <(grep '^ protected' lua54/src/main/java/party/iroiro/jua/Lua54Natives.java| sort)` -echo "package party.iroiro.jua;" -echo -echo "import java.nio.Buffer;" -echo -echo "/**" -echo " * Generated from the common parts of Lua5.[1..4]" -echo " */" -echo '@SuppressWarnings("unused")' -echo "public abstract class LuaNative {" -echo -echo "$COMM1234" | sed -e 's/native/abstract/' -e 's# /\*#\n#' -# Param name differs -echo " protected abstract void lua_rawseti(long ptr, int index, int i);" -echo "}" diff --git a/scripts/jnigen-lua.html b/scripts/jnigen-lua.html deleted file mode 100644 index 3c47d7d2..00000000 --- a/scripts/jnigen-lua.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - -
-
- - - {{ link }} -
- -
- - - diff --git a/scripts/jnigen-lua.py b/scripts/jnigen-lua.py index dd0818e1..ea5567c0 100644 --- a/scripts/jnigen-lua.py +++ b/scripts/jnigen-lua.py @@ -633,6 +633,38 @@ def addExtra(functions): ], }, }) + functions.append({ + 'name': 'luaJ_dumptobuffer', + 'description': 'See lua_dump', + 'signature': { + 'return': 'jobject', + 'params': [ + ['lua_State *', 'L'], + ], + }, + }) + functions.append({ + 'name': 'luaJ_tobuffer', + 'description': 'See lua_tolstring', + 'signature': { + 'return': 'jobject', + 'params': [ + ['lua_State *', 'L'], + ['int', 'index'], + ], + }, + }) + functions.append({ + 'name': 'luaJ_todirectbuffer', + 'description': 'See lua_tolstring', + 'signature': { + 'return': 'jobject', + 'params': [ + ['lua_State *', 'L'], + ['int', 'index'], + ], + }, + }) def getWhole(luaVersion, package):