Больше тестов Lua

This commit is contained in:
DBotThePony 2023-02-23 09:40:35 +07:00
parent da6e5aa694
commit 7a8e366c46
Signed by: DBot
GPG Key ID: DCC23B5715498507
5 changed files with 118 additions and 92 deletions

View File

@ -17,6 +17,6 @@ public final class LuaJNI {
} }
static { static {
//System.load(new File("lua_glue.dll").getAbsolutePath()); System.load(new File("lua_glue.dll").getAbsolutePath());
} }
} }

View File

@ -13,7 +13,7 @@ import javax.annotation.Nullable;
public interface LuaJNR { public interface LuaJNR {
public int lua_pcallk(@NotNull Pointer luaState, int numArgs, int numResults, int msgh, @LongLong long ctx, @LongLong long callback); public int lua_pcallk(@NotNull Pointer luaState, int numArgs, int numResults, int msgh, @LongLong long ctx, @LongLong long callback);
public int lua_callk(@NotNull Pointer luaState, int numArgs, int numResults, @LongLong long ctx, @LongLong long callback); public int lua_callk(@NotNull Pointer luaState, int numArgs, int numResults, @LongLong long ctx, @LongLong long callback);
public long lua_atpanic(@NotNull Pointer luaState, long fn); public long lua_atpanic(@NotNull Pointer luaState, @LongLong long fn);
@Nullable @Nullable
public Pointer luaL_newstate(); public Pointer luaL_newstate();
@ -75,7 +75,7 @@ public interface LuaJNR {
public void lua_pushstring(@NotNull Pointer luaState, @NotNull String value); public void lua_pushstring(@NotNull Pointer luaState, @NotNull String value);
// двоичная строка // двоичная строка
public long lua_pushlstring(@NotNull Pointer luaState, @NotNull Pointer stringPointer, @LongLong long length); public long lua_pushlstring(@NotNull Pointer luaState, @LongLong long stringPointer, @LongLong long length);
// Загрузка Lua значений на стек // Загрузка Lua значений на стек
public int lua_getglobal(@NotNull Pointer luaState, @NotNull String name); public int lua_getglobal(@NotNull Pointer luaState, @NotNull String name);

View File

@ -31,37 +31,18 @@ fun main() {
if (true) { if (true) {
val lua = LuaState() val lua = LuaState()
Thread.sleep(5_000L) Thread.sleep(4_000L)
lua.load(File("test.lua").readText()) lua.load(File("test.lua").readText())
//lua.load("print('hello world!', ...)") lua.call()
//lua.push(GsonBuilder().create().fromJson(File("playerdata.json").reader(), JsonElement::class.java))
/*lua.push(JsonObject().also {
it.add("Сыр", JsonPrimitive("Гиршок"))
it.add("сас", JsonPrimitive("сос"))
it.add("сыс", JsonPrimitive(4))
it.add("ы", JsonNull.INSTANCE)
it.add("s", JsonObject().also {
it.add("Вложенный", JsonPrimitive("Объект!"))
})
})*/
lua.pcall()
lua.loadGlobal("printTable") lua.loadGlobal("printTable")
lua.push(GsonBuilder().create().fromJson(File("playerdata.json").reader(), JsonElement::class.java)) lua.push(GsonBuilder().create().fromJson(File("playerdata.json").reader(), JsonElement::class.java))
lua.pcall(1) lua.call(1)
/*for (t in 1 .. 100) { lua.loadGlobal("test")
lua.loadGlobal("test") lua.push("s".repeat(10))
lua.call(1)
for (i in 0 until t)
lua.push("sass".repeat(t))
lua.pcall(t)
}*/
Thread.sleep(4_000L) Thread.sleep(4_000L)

View File

@ -9,18 +9,19 @@ import com.kenai.jffi.CallContext
import com.kenai.jffi.CallingConvention import com.kenai.jffi.CallingConvention
import com.kenai.jffi.Closure import com.kenai.jffi.Closure
import com.kenai.jffi.ClosureManager import com.kenai.jffi.ClosureManager
import com.kenai.jffi.MemoryIO
import com.kenai.jffi.Type import com.kenai.jffi.Type
import com.sun.jna.Native
import com.sun.jna.ptr.LongByReference
import jnr.ffi.Memory import jnr.ffi.Memory
import jnr.ffi.NativeType import jnr.ffi.NativeType
import jnr.ffi.Pointer import org.apache.logging.log4j.LogManager
import org.lwjgl.system.MemoryUtil import org.lwjgl.system.MemoryUtil
import java.io.Closeable import java.io.Closeable
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
import java.lang.ref.Cleaner
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.ByteOrder import java.nio.ByteOrder
import kotlin.system.exitProcess
private fun stringToBuffer(str: String): ByteBuffer { private fun stringToBuffer(str: String): ByteBuffer {
val bytes = str.toByteArray(charset = Charsets.UTF_8) val bytes = str.toByteArray(charset = Charsets.UTF_8)
@ -34,17 +35,34 @@ private fun stringToBuffer(str: String): ByteBuffer {
class LuaState : Closeable { class LuaState : Closeable {
private val pointer = LuaJNR.INSTANCE.luaL_newstate() ?: throw OutOfMemoryError("Unable to allocate new LuaState") private val pointer = LuaJNR.INSTANCE.luaL_newstate() ?: throw OutOfMemoryError("Unable to allocate new LuaState")
private val cleanable: Cleaner.Cleanable
private val sharedStringBufferPtr = MemoryIO.getInstance().allocateMemory(2L shl 16, false)
private var destroyed = false init {
private val panicHandler = object : LuaJNR.lua_CFunction { val pointer = pointer
override fun invoke(luaState: Pointer): Int { val sharedStringBufferPtr = sharedStringBufferPtr
println("$this panicked!")
return 0 cleanable = CLEANER.register(this) {
LuaJNR.INSTANCE.lua_close(pointer)
MemoryIO.getInstance().freeMemory(sharedStringBufferPtr)
} }
} }
private val panicHandler = ClosureManager.getInstance().newClosure(
{
LOGGER.fatal("${this@LuaState} at $pointer has panicked! This should be impossible!")
exitProcess(1)
},
CallContext.getCallContext(Type.SINT, arrayOf(Type.POINTER), CallingConvention.DEFAULT, false)
)
override fun close() {
cleanable.clean()
}
init { init {
//LuaJNR.INSTANCE.lua_atpanic(pointer, panicHandler) LuaJNR.INSTANCE.lua_atpanic(pointer, panicHandler.address)
LuaJNR.INSTANCE.luaopen_base(pointer) LuaJNR.INSTANCE.luaopen_base(pointer)
storeGlobal("_G") storeGlobal("_G")
@ -64,26 +82,15 @@ class LuaState : Closeable {
storeGlobal("debug") storeGlobal("debug")
} }
fun pushClosure(lambda: (state: LuaState) -> Unit) { val stackTop: Int get() {
LuaJNI.lua_pushcclosure(pointer.address()) lazy@{ val value = LuaJNR.INSTANCE.lua_gettop(pointer)
try { check(value >= 0) { "Invalid stack top $value" }
lambda.invoke(this@LuaState) return value
} catch (err: Throwable) {
val builder = StringWriter()
val printWriter = PrintWriter(builder)
err.printStackTrace(printWriter)
push(builder.toString())
return@lazy 1
}
return@lazy 0
}
}
fun checkStack(minAmount: Int): Boolean {
return LuaJNR.INSTANCE.lua_checkstack(pointer, minAmount) > 0
} }
/**
* Converts the acceptable index idx into an equivalent absolute index (that is, one that does not depend on the stack size).
*/
fun absoluteIndex(index: Int): Int { fun absoluteIndex(index: Int): Int {
return LuaJNR.INSTANCE.lua_absindex(pointer, index) return LuaJNR.INSTANCE.lua_absindex(pointer, index)
} }
@ -116,17 +123,17 @@ class LuaState : Closeable {
closure.dispose() closure.dispose()
} }
fun pcall(numArgs: Int = 0, numResults: Int = 0): Int { fun call(numArgs: Int = 0, numResults: Int = 0): Int {
val status = LuaJNR.INSTANCE.lua_pcallk(pointer, numArgs, numResults, 0, 0L, 0L) val status = LuaJNR.INSTANCE.lua_pcallk(pointer, numArgs, numResults, 0, 0L, 0L)
if (status == LUA_ERRRUN) { if (status == LUA_ERRRUN) {
throw LuaRuntimeException(getString()) throw LuaRuntimeException(popString())
} }
return status return status
} }
fun getString(index: Int = -1, limit: Long = 4096): String? { fun popString(index: Int = -1, limit: Long = 2 shl 16): String? {
val len = Memory.allocateDirect(LuaJNR.RUNTIME, NativeType.SLONGLONG) val len = Memory.allocateDirect(LuaJNR.RUNTIME, NativeType.SLONGLONG)
val p = LuaJNR.INSTANCE.lua_tolstring(pointer, absoluteIndex(index), len) ?: return null val p = LuaJNR.INSTANCE.lua_tolstring(pointer, absoluteIndex(index), len) ?: return null
@ -143,21 +150,6 @@ class LuaState : Closeable {
return readBytes.toString(charset = Charsets.UTF_8) return readBytes.toString(charset = Charsets.UTF_8)
} }
override fun close() {
if (destroyed) {
throw IllegalStateException("Already destroyed")
}
LuaJNR.INSTANCE.lua_close(pointer)
destroyed = true
}
val stackTop: Int get() {
val value = LuaJNR.INSTANCE.lua_gettop(pointer)
check(value >= 0) { "Invalid stack top $value" }
return value
}
fun storeGlobal(name: String) { fun storeGlobal(name: String) {
LuaJNR.INSTANCE.lua_setglobal(pointer, name) LuaJNR.INSTANCE.lua_setglobal(pointer, name)
} }
@ -166,6 +158,22 @@ class LuaState : Closeable {
LuaJNR.INSTANCE.lua_getglobal(pointer, name) LuaJNR.INSTANCE.lua_getglobal(pointer, name)
} }
fun push(closure: (state: LuaState) -> Unit) {
LuaJNI.lua_pushcclosure(pointer.address()) lazy@{
try {
closure.invoke(this@LuaState)
} catch (err: Throwable) {
val builder = StringWriter()
val printWriter = PrintWriter(builder)
err.printStackTrace(printWriter)
push(builder.toString())
return@lazy 1
}
return@lazy 0
}
}
fun push() { fun push() {
LuaJNR.INSTANCE.lua_pushnil(pointer) LuaJNR.INSTANCE.lua_pushnil(pointer)
} }
@ -178,23 +186,53 @@ class LuaState : Closeable {
LuaJNR.INSTANCE.lua_pushinteger(pointer, value) LuaJNR.INSTANCE.lua_pushinteger(pointer, value)
} }
fun push(value: Double) {
LuaJNR.INSTANCE.lua_pushnumber(pointer, value)
}
fun push(value: Float) {
LuaJNR.INSTANCE.lua_pushnumber(pointer, value.toDouble())
}
fun push(value: Boolean) {
LuaJNR.INSTANCE.lua_pushboolean(pointer, if (value) 1 else 0)
}
fun push(value: String) { fun push(value: String) {
val bytes = value.toByteArray(Charsets.UTF_8) val bytes = value.toByteArray(Charsets.UTF_8)
val block = LuaJNR.RUNTIME.memoryManager.allocateDirect(bytes.size) ?: throw OutOfMemoryError("Unable to allocate ${bytes.size} bytes on heap") if (bytes.size < 2 shl 16) {
MemoryIO.getInstance().putByteArray(sharedStringBufferPtr, bytes, 0, bytes.size)
LuaJNR.INSTANCE.lua_pushlstring(pointer, sharedStringBufferPtr, bytes.size.toLong())
} else {
val mem = MemoryIO.getInstance()
val block = mem.allocateMemory(bytes.size.toLong(), false)
//try { if (block == 0L)
block.put(0L, bytes, 0, bytes.size) throw OutOfMemoryError("Unable to allocate ${bytes.size} bytes on heap")
LuaJNR.INSTANCE.lua_pushlstring(pointer, block, bytes.size.toLong())
//} finally { try {
// Memory.allocate() mem.putByteArray(block, bytes, 0, bytes.size)
//} LuaJNR.INSTANCE.lua_pushlstring(pointer, block, bytes.size.toLong())
} finally {
mem.freeMemory(block)
}
}
}
fun pushTable(arraySize: Int = 0, hashSize: Int = 0): Int {
LuaJNR.INSTANCE.lua_createtable(pointer, arraySize, hashSize)
return stackTop
}
fun setTableValue(stackIndex: Int) {
LuaJNR.INSTANCE.lua_settable(pointer, stackIndex)
} }
fun push(value: JsonElement) { fun push(value: JsonElement) {
when (value) { when (value) {
JsonNull.INSTANCE -> { JsonNull.INSTANCE -> {
LuaJNR.INSTANCE.lua_pushnil(pointer) push()
} }
is JsonPrimitive -> { is JsonPrimitive -> {
@ -202,39 +240,37 @@ class LuaState : Closeable {
val num = value.asNumber val num = value.asNumber
when (num) { when (num) {
is Int, is Long -> LuaJNR.INSTANCE.lua_pushinteger(pointer, num.toLong()) is Int, is Long -> push(num.toLong())
else -> LuaJNR.INSTANCE.lua_pushnumber(pointer, num.toDouble()) else -> push(num.toDouble())
} }
} else if (value.isString) { } else if (value.isString) {
push(value.asString) push(value.asString)
} else if (value.isBoolean) { } else if (value.isBoolean) {
LuaJNR.INSTANCE.lua_pushboolean(pointer, if (value.asBoolean) 1 else 0) push(value.asBoolean)
} else { } else {
throw IllegalArgumentException(value.toString()) throw IllegalArgumentException(value.toString())
} }
} }
is JsonArray -> { is JsonArray -> {
LuaJNR.INSTANCE.lua_createtable(pointer, value.size(), 0) val index = pushTable(arraySize = value.size())
val index = stackTop
for ((i, v) in value.withIndex()) { for ((i, v) in value.withIndex()) {
LuaJNR.INSTANCE.lua_pushinteger(pointer, i.toLong() + 1L) push(i + 1L)
push(v) push(v)
LuaJNR.INSTANCE.lua_settable(pointer, index) setTableValue(index)
} }
} }
is JsonObject -> { is JsonObject -> {
LuaJNR.INSTANCE.lua_createtable(pointer, 0, value.size()) val index = pushTable(hashSize = value.size())
val index = stackTop
for ((k, v) in value.entrySet()) { for ((k, v) in value.entrySet()) {
push(k) push(k)
push(v) push(v)
LuaJNR.INSTANCE.lua_settable(pointer, index) setTableValue(index)
} }
} }
@ -243,4 +279,13 @@ class LuaState : Closeable {
} }
} }
} }
companion object {
private val LOGGER = LogManager.getLogger()
private val CLEANER = Cleaner.create {
val thread = Thread(it, "LuaState cleaner")
thread.priority = 1
thread
}
}
} }

View File

@ -16,7 +16,7 @@ function printTable(input)
end end
function test(...) function test(...)
print('Called test with ' .. select('#', ...) .. ' arguments: ' .. table.concat({...}, ', ')) print('Called test with ' .. select('#', ...) .. ' arguments: ' .. #table.concat({...}, ', '))
end end
print(collectgarbage('count') * 1024) print(collectgarbage('count') * 1024)