diff --git a/build.gradle b/build.gradle index 40fe367..363bf79 100644 --- a/build.gradle +++ b/build.gradle @@ -60,7 +60,7 @@ testing { all { testTask.configure { systemProperty 'java.util.logging.config.file', 'src/test/resources/logging.properties' - systemProperty 'jextract.trace.downcalls', 'true' + //systemProperty 'jextract.trace.downcalls', 'true' jvmArgs '--enable-native-access=ALL-UNNAMED' if(currentOs.isMacOsX()) { environment "DYLD_LIBRARY_PATH", "${project.buildDir}/lua-libs/" diff --git a/src/main/java/org/itsallcode/luava/LuaInterpreter.java b/src/main/java/org/itsallcode/luava/LuaInterpreter.java index 9e55fa9..d15eea5 100644 --- a/src/main/java/org/itsallcode/luava/LuaInterpreter.java +++ b/src/main/java/org/itsallcode/luava/LuaInterpreter.java @@ -97,9 +97,7 @@ public void setGlobalFunction(final String name, final Object object, final Meth } private void setGlobalFunction(final String name, final IntSupplier fn) { - setGlobalFunction(name, (final MemorySegment L) -> { - return fn.getAsInt(); - }); + setGlobalFunction(name, (final MemorySegment l) -> fn.getAsInt()); } private void setGlobalFunction(final String name, final lua_CFunction.Function fn) { diff --git a/src/main/java/org/itsallcode/luava/LuaStack.java b/src/main/java/org/itsallcode/luava/LuaStack.java index 9070cb8..af8f819 100644 --- a/src/main/java/org/itsallcode/luava/LuaStack.java +++ b/src/main/java/org/itsallcode/luava/LuaStack.java @@ -107,7 +107,7 @@ private void setTop(final int n) { Lua.lua_settop(state, n); } - private Void popNil() { + Void popNil() { pop(); return null; } @@ -125,13 +125,12 @@ boolean popBoolean() { private String toString(final int idx) { if (!isString(idx)) { throw new LuaException( - "Expected string at index " + idx + " but was " + getType(idx) + ", " + printStack()); + "Expected string at index " + idx + " but was " + getType(idx)); } final MemorySegment len = arena.allocateFrom(Lua.size_t, 0); final MemorySegment result = Lua.lua_tolstring(state, idx, len); final long stringLength = len.get(Lua.size_t, 0); final byte[] bytes = result.reinterpret(stringLength).toArray(Lua.C_CHAR); - int length = bytes.length; if (bytes.length > 0 && bytes[bytes.length - 1] == 0x0) { length = length - 1; @@ -145,11 +144,13 @@ String popString() { return value; } + @SuppressWarnings("java:S1941") private double toNumber(final int idx) { final MemorySegment isNumber = arena.allocateFrom(Lua.C_INT, 0); final double value = Lua.lua_tonumberx(state, idx, isNumber); if (isNumber.get(Lua.C_INT, 0) != 1) { - throw new LuaException("No number at index " + idx + " but is " + getType(idx)); + throw new LuaException("No number at index " + idx + " but is " + + getType(idx)); } return value; } @@ -160,6 +161,7 @@ private double toNumber(final int idx) { return number; } + @SuppressWarnings("java:S1941") private long toInteger(final int idx) { final MemorySegment isNumber = arena.allocateFrom(Lua.C_INT, 0); final long value = Lua.lua_tointegerx(state, idx, isNumber); diff --git a/src/test/java/org/itsallcode/luava/LuaStackTest.java b/src/test/java/org/itsallcode/luava/LuaStackTest.java index a15e3c0..8f78556 100644 --- a/src/test/java/org/itsallcode/luava/LuaStackTest.java +++ b/src/test/java/org/itsallcode/luava/LuaStackTest.java @@ -2,10 +2,13 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.params.provider.*; class LuaStackTest { private LowLevelLua lua; @@ -37,17 +40,57 @@ void pushNil() { } @Test - void pushNumber() { + void popNilAsBoolean() { + stack.pushNil(); + assertThat(stack.popBoolean(), equalTo(false)); + } + + @Test + void popNilAsString() { + stack.pushNil(); + final LuaException exception = assertThrows(LuaException.class, stack::popString); + assertThat(exception.getMessage(), equalTo("Expected string at index -1 but was NIL")); + } + + @Test + void pushPopNumber() { stack.pushNumber(3.14); assertThat(stack.popNumber(), equalTo(3.14)); } + @Test + void pushPopNumberButIsInteger() { + stack.pushInteger(42); + assertThat(stack.popNumber(), equalTo(42.0)); + } + + @Test + void popNumberWrongType() { + stack.pushString("NaN"); + final LuaException exception = assertThrows(LuaException.class, stack::popNumber); + assertThat(exception.getMessage(), equalTo("No number at index -1 but is STRING")); + } + @Test void pushInteger() { stack.pushInteger(42); assertThat(stack.popInteger(), equalTo(42L)); } + @Test + void popIntegerWrongType() { + stack.pushString("NaN"); + final LuaException exception = assertThrows(LuaException.class, stack::popInteger); + assertThat(exception.getMessage(), equalTo("No integer at index -1 but is STRING")); + } + + @Test + void popIntegerButIsNumber() { + stack.pushNumber(3.14); + final LuaException exception = assertThrows(LuaException.class, stack::popInteger); + assertThat(exception.getMessage(), equalTo("No integer at index -1 but is NUMBER")); + } + @ParameterizedTest @ValueSource(booleans = { true, false }) void pushBoolean(final boolean value) { @@ -65,4 +108,46 @@ void getTop() { stack.pop(); assertThat(stack.getTop(), equalTo(1)); } + + static Arguments nilMapping(final Class type) { + return mapping("nil", type, null); + } + + static Arguments stringMapping(final String value) { + return mapping("'" + value + "'", String.class, value); + } + + static Arguments longMapping(final long value) { + return mapping(String.valueOf(value), Long.class, value); + } + + static Arguments doubleMapping(final double value) { + return mapping(String.valueOf(value), Double.class, value); + } + + static Arguments booleanMapping(final boolean value) { + return mapping(String.valueOf(value), Boolean.class, value); + } + + static Arguments mapping(final String luaValue, final Class type, final Object expected) { + return Arguments.of(luaValue, type, expected); + } + + static Stream toObjectArgs() { + return Stream.of(nilMapping(String.class), nilMapping(Long.class), nilMapping(long.class), + nilMapping(Double.class), nilMapping(double.class), nilMapping(boolean.class), + nilMapping(Boolean.class), stringMapping(""), stringMapping("value"), + stringMapping("value with space"), longMapping(Long.MAX_VALUE), longMapping(Long.MIN_VALUE), + longMapping(0), doubleMapping(Double.MIN_NORMAL), doubleMapping(Double.MAX_VALUE), + doubleMapping(Double.MIN_VALUE), doubleMapping(0.0), booleanMapping(true), booleanMapping(true)); + } + + @ParameterizedTest + @MethodSource("toObjectArgs") + void toObject(final String luaValue, final Class type, final Object expected) { + lua.loadString("var = " + luaValue); + lua.pcall(0, 0); + lua.getGlobal("var"); + assertThat(stack.toObject(type), equalTo(expected)); + } }