Больше тестов 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 {
//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 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 long lua_atpanic(@NotNull Pointer luaState, long fn);
public long lua_atpanic(@NotNull Pointer luaState, @LongLong long fn);
@Nullable
public Pointer luaL_newstate();
@ -75,7 +75,7 @@ public interface LuaJNR {
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 значений на стек
public int lua_getglobal(@NotNull Pointer luaState, @NotNull String name);

View File

@ -31,37 +31,18 @@ fun main() {
if (true) {
val lua = LuaState()
Thread.sleep(5_000L)
Thread.sleep(4_000L)
lua.load(File("test.lua").readText())
//lua.load("print('hello world!', ...)")
//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.call()
lua.loadGlobal("printTable")
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")
for (i in 0 until t)
lua.push("sass".repeat(t))
lua.pcall(t)
}*/
lua.loadGlobal("test")
lua.push("s".repeat(10))
lua.call(1)
Thread.sleep(4_000L)

View File

@ -9,18 +9,19 @@ import com.kenai.jffi.CallContext
import com.kenai.jffi.CallingConvention
import com.kenai.jffi.Closure
import com.kenai.jffi.ClosureManager
import com.kenai.jffi.MemoryIO
import com.kenai.jffi.Type
import com.sun.jna.Native
import com.sun.jna.ptr.LongByReference
import jnr.ffi.Memory
import jnr.ffi.NativeType
import jnr.ffi.Pointer
import org.apache.logging.log4j.LogManager
import org.lwjgl.system.MemoryUtil
import java.io.Closeable
import java.io.PrintWriter
import java.io.StringWriter
import java.lang.ref.Cleaner
import java.nio.ByteBuffer
import java.nio.ByteOrder
import kotlin.system.exitProcess
private fun stringToBuffer(str: String): ByteBuffer {
val bytes = str.toByteArray(charset = Charsets.UTF_8)
@ -34,17 +35,34 @@ private fun stringToBuffer(str: String): ByteBuffer {
class LuaState : Closeable {
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
private val panicHandler = object : LuaJNR.lua_CFunction {
override fun invoke(luaState: Pointer): Int {
println("$this panicked!")
return 0
init {
val pointer = pointer
val sharedStringBufferPtr = sharedStringBufferPtr
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 {
//LuaJNR.INSTANCE.lua_atpanic(pointer, panicHandler)
LuaJNR.INSTANCE.lua_atpanic(pointer, panicHandler.address)
LuaJNR.INSTANCE.luaopen_base(pointer)
storeGlobal("_G")
@ -64,26 +82,15 @@ class LuaState : Closeable {
storeGlobal("debug")
}
fun pushClosure(lambda: (state: LuaState) -> Unit) {
LuaJNI.lua_pushcclosure(pointer.address()) lazy@{
try {
lambda.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 checkStack(minAmount: Int): Boolean {
return LuaJNR.INSTANCE.lua_checkstack(pointer, minAmount) > 0
val stackTop: Int get() {
val value = LuaJNR.INSTANCE.lua_gettop(pointer)
check(value >= 0) { "Invalid stack top $value" }
return value
}
/**
* 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 {
return LuaJNR.INSTANCE.lua_absindex(pointer, index)
}
@ -116,17 +123,17 @@ class LuaState : Closeable {
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)
if (status == LUA_ERRRUN) {
throw LuaRuntimeException(getString())
throw LuaRuntimeException(popString())
}
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 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)
}
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) {
LuaJNR.INSTANCE.lua_setglobal(pointer, name)
}
@ -166,6 +158,22 @@ class LuaState : Closeable {
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() {
LuaJNR.INSTANCE.lua_pushnil(pointer)
}
@ -178,23 +186,53 @@ class LuaState : Closeable {
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) {
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 {
block.put(0L, bytes, 0, bytes.size)
LuaJNR.INSTANCE.lua_pushlstring(pointer, block, bytes.size.toLong())
//} finally {
// Memory.allocate()
//}
if (block == 0L)
throw OutOfMemoryError("Unable to allocate ${bytes.size} bytes on heap")
try {
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) {
when (value) {
JsonNull.INSTANCE -> {
LuaJNR.INSTANCE.lua_pushnil(pointer)
push()
}
is JsonPrimitive -> {
@ -202,39 +240,37 @@ class LuaState : Closeable {
val num = value.asNumber
when (num) {
is Int, is Long -> LuaJNR.INSTANCE.lua_pushinteger(pointer, num.toLong())
else -> LuaJNR.INSTANCE.lua_pushnumber(pointer, num.toDouble())
is Int, is Long -> push(num.toLong())
else -> push(num.toDouble())
}
} else if (value.isString) {
push(value.asString)
} else if (value.isBoolean) {
LuaJNR.INSTANCE.lua_pushboolean(pointer, if (value.asBoolean) 1 else 0)
push(value.asBoolean)
} else {
throw IllegalArgumentException(value.toString())
}
}
is JsonArray -> {
LuaJNR.INSTANCE.lua_createtable(pointer, value.size(), 0)
val index = stackTop
val index = pushTable(arraySize = value.size())
for ((i, v) in value.withIndex()) {
LuaJNR.INSTANCE.lua_pushinteger(pointer, i.toLong() + 1L)
push(i + 1L)
push(v)
LuaJNR.INSTANCE.lua_settable(pointer, index)
setTableValue(index)
}
}
is JsonObject -> {
LuaJNR.INSTANCE.lua_createtable(pointer, 0, value.size())
val index = stackTop
val index = pushTable(hashSize = value.size())
for ((k, v) in value.entrySet()) {
push(k)
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
function test(...)
print('Called test with ' .. select('#', ...) .. ' arguments: ' .. table.concat({...}, ', '))
print('Called test with ' .. select('#', ...) .. ' arguments: ' .. #table.concat({...}, ', '))
end
print(collectgarbage('count') * 1024)