diff --git a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/compiler/IrCompiler.java b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/compiler/IrCompiler.java index e935e21..86d9f2d 100644 --- a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/compiler/IrCompiler.java +++ b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/compiler/IrCompiler.java @@ -447,8 +447,8 @@ public IrNode visitStringConcat(StringConcatContext ctx) { @Override public IrNode visitNumberLiteral(NumberLiteralContext ctx) { - // TODO non-decimal numbers - return new LuaConstant(Double.valueOf(ctx.Numeral().getText())); + var value = Double.valueOf(ctx.Numeral().getText()); + return new LuaConstant(value.intValue() == value ? value.intValue() : value); } @Override diff --git a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/LuaType.java b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/LuaType.java index 0cfc5fe..1b016bf 100644 --- a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/LuaType.java +++ b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/LuaType.java @@ -6,15 +6,12 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; import fi.benjami.code4jvm.Type; import fi.benjami.code4jvm.Value; import fi.benjami.code4jvm.lua.compiler.CompiledFunction; import fi.benjami.code4jvm.lua.compiler.CompiledShape; import fi.benjami.code4jvm.lua.compiler.FunctionCompiler; -import fi.benjami.code4jvm.lua.compiler.LuaContext; -import fi.benjami.code4jvm.lua.compiler.ShapeGenerator; import fi.benjami.code4jvm.lua.compiler.ShapeTypes; import fi.benjami.code4jvm.lua.ir.stmt.ReturnStmt; import fi.benjami.code4jvm.lua.runtime.LuaFunction; @@ -232,7 +229,8 @@ public boolean equals(Object obj) { // Lua standard types static final LuaType NIL = new Simple("nil", Type.OBJECT); static final LuaType BOOLEAN = new Simple("boolean", Type.BOOLEAN); - static final LuaType NUMBER = new Simple("number", Type.DOUBLE); + static final LuaType INTEGER = new Simple("number", Type.INT); + static final LuaType FLOAT = new Simple("number", Type.DOUBLE); static final LuaType STRING = new Simple("string", Type.STRING); static final LuaType TABLE = new Simple("table", LuaTable.TYPE); // TODO userdata, thread @@ -281,23 +279,6 @@ public static Shape shape() { return new Shape(); } - public static List readList(String str) { - var types = new ArrayList(); - for (var i = 0; i < str.length(); i++) { - types.add(switch (str.charAt(i)) { - case 'V' -> LuaType.NIL; - case 'B' -> LuaType.BOOLEAN; - case 'N' -> LuaType.NUMBER; - case 'S' -> LuaType.STRING; - case 'U' -> LuaType.UNKNOWN; - case 'T' -> throw new UnsupportedOperationException("todo"); - case 'F' -> throw new UnsupportedOperationException("todo"); - default -> throw new IllegalArgumentException("unknown type: " + str.charAt(i)); - }); - } - return types; - } - /** * Name of type for Lua code. * @return Lua type name. @@ -317,4 +298,8 @@ public static List readList(String str) { default boolean isAssignableFrom(LuaType other) { return this == LuaType.UNKNOWN || equals(other); } + + default boolean isNumber() { + return this == LuaType.INTEGER || this == LuaType.FLOAT; + } } diff --git a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/LuaTypeSupport.java b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/LuaTypeSupport.java index 3323bea..5d4950f 100644 --- a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/LuaTypeSupport.java +++ b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/LuaTypeSupport.java @@ -10,8 +10,10 @@ class LuaTypeSupport { public static final Map TYPE_TO_TYPE = Map.of( Type.BOOLEAN, LuaType.BOOLEAN, Type.of(Boolean.class), LuaType.BOOLEAN, - Type.DOUBLE, LuaType.NUMBER, - Type.of(Double.class), LuaType.NUMBER, + Type.INT, LuaType.INTEGER, + Type.of(Integer.class), LuaType.INTEGER, + Type.DOUBLE, LuaType.FLOAT, + Type.of(Double.class), LuaType.FLOAT, Type.STRING, LuaType.STRING, LuaTable.TYPE, LuaType.TABLE ); @@ -19,8 +21,10 @@ class LuaTypeSupport { public static final Map, LuaType> CLASS_TO_TYPE = Map.of( boolean.class, LuaType.BOOLEAN, Boolean.class, LuaType.BOOLEAN, - double.class, LuaType.NUMBER, - Double.class, LuaType.NUMBER, + int.class, LuaType.INTEGER, + Integer.class, LuaType.INTEGER, + double.class, LuaType.FLOAT, + Double.class, LuaType.FLOAT, String.class, LuaType.STRING, LuaTable.class, LuaType.TABLE ); diff --git a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/ArithmeticExpr.java b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/ArithmeticExpr.java index fca67ab..9c908de 100644 --- a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/ArithmeticExpr.java +++ b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/ArithmeticExpr.java @@ -3,6 +3,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.util.List; import java.util.function.BiFunction; import fi.benjami.code4jvm.Expression; @@ -38,7 +39,10 @@ public record ArithmeticExpr( public enum Kind { POWER(MATH_POW::call, "power", "__pow"), MULTIPLY(Arithmetic::multiply, "multiply", "__mul"), - DIVIDE(Arithmetic::divide, "divide", "__div"), + DIVIDE((lhs, rhs) -> { + // Lua uses float division unless integer division is explicitly request (see below) + return Arithmetic.divide(lhs.cast(Type.DOUBLE), rhs.cast(Type.DOUBLE)); + }, "divide", "__div"), FLOOR_DIVIDE(FLOOR_DIV::call, "floorDivide", "__idiv"), MODULO((lhs, rhs) -> (block -> { // Lua expects modulo to be always positive; Java's remainder can return negative values @@ -53,16 +57,27 @@ public enum Kind { Kind(BiFunction directEmitter, String methodName, String metamethod) { this.directEmitter = directEmitter; - MethodHandle fastPath; + var intReturnType = methodName == "power" || methodName.equals("divide") ? double.class : int.class; + MethodHandle doublePath, intPath; try { // Drop the call target argument, it is not needed - fastPath = MethodHandles.dropArguments(LOOKUP.findStatic(ArithmeticExpr.class, methodName, + doublePath = MethodHandles.dropArguments(LOOKUP.findStatic(ArithmeticExpr.class, methodName, MethodType.methodType(double.class, double.class, double.class)), 0, Object.class); + intPath = MethodHandles.dropArguments(LOOKUP.findStatic(ArithmeticExpr.class, methodName, + MethodType.methodType(intReturnType, int.class, int.class)), 0, Object.class); } catch (NoSuchMethodException | IllegalAccessException e) { throw new AssertionError(e); } - this.callTarget = BinaryOp.newTarget(Double.class, fastPath, metamethod, - (a, b) -> new LuaException("attempted to perform arithmetic on non-number values")); + // If we have any doubles at all, take the double path + var paths = List.of( + new BinaryOp.Path(Integer.class, Integer.class, intPath), + new BinaryOp.Path(Double.class, Double.class, doublePath), + new BinaryOp.Path(Integer.class, Double.class, MethodHandles.explicitCastArguments(doublePath, MethodType.methodType(double.class, Object.class, int.class, double.class))), + new BinaryOp.Path(Double.class, Integer.class, MethodHandles.explicitCastArguments(doublePath, MethodType.methodType(double.class, Object.class, double.class, int.class))) + ); + this.callTarget = BinaryOp.newTarget(paths, metamethod, + (a, b) -> new LuaException("cannot " + methodName + " " + + LuaType.of(a).name() + " and " + LuaType.of(b).name())); } } @@ -102,11 +117,44 @@ private static double subtract(double lhs, double rhs) { return lhs - rhs; } + @SuppressWarnings("unused") + private static double power(int lhs, int rhs) { + return Math.pow(lhs, rhs); + } + + @SuppressWarnings("unused") + private static int multiply(int lhs, int rhs) { + return lhs * rhs; + } + + private static double divide(int lhs, int rhs) { + return ((double) lhs) / ((double) rhs); + } + + public static int floorDivide(int lhs, int rhs) { + return (int) Math.floor(divide(lhs, rhs)); + } + + @SuppressWarnings("unused") + private static int modulo(int lhs, int rhs) { + return Math.abs(lhs % rhs); + } + + @SuppressWarnings("unused") + private static int add(int lhs, int rhs) { + return lhs + rhs; + } + + @SuppressWarnings("unused") + private static int subtract(int lhs, int rhs) { + return lhs - rhs; + } + @Override public Value emit(LuaContext ctx, Block block) { var lhsValue = lhs.emit(ctx, block); var rhsValue = rhs.emit(ctx, block); - if (outputType(ctx).equals(LuaType.NUMBER)) { + if (outputType(ctx).isNumber()) { // Both arguments are known to be numbers; emit arithmetic operation directly return block.add(kind.directEmitter.apply(lhsValue, rhsValue)); } else { @@ -117,8 +165,19 @@ public Value emit(LuaContext ctx, Block block) { @Override public LuaType outputType(LuaContext ctx) { - return lhs.outputType(ctx).equals(LuaType.NUMBER) && rhs.outputType(ctx).equals(LuaType.NUMBER) - ? LuaType.NUMBER : LuaType.UNKNOWN; + var lhsOut = lhs.outputType(ctx); + var rhsOut = rhs.outputType(ctx); + if (lhsOut.isNumber() && rhsOut.isNumber()) { + if (kind == Kind.POWER || kind == Kind.DIVIDE) { + // Lua spec says that these always produce floats + return LuaType.FLOAT; + } else if (lhsOut == LuaType.INTEGER && rhsOut == LuaType.INTEGER) { + return LuaType.INTEGER; // Both sides are integers + } + return LuaType.FLOAT; // Float on at least one side + } else { + return LuaType.UNKNOWN; + } } } diff --git a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/LengthExpr.java b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/LengthExpr.java index dd50eab..3bb7a0c 100644 --- a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/LengthExpr.java +++ b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/LengthExpr.java @@ -25,10 +25,8 @@ public record LengthExpr(IrNode expr) implements IrNode { static { var lookup = MethodHandles.lookup(); try { - TABLE_LENGTH = MethodHandles.dropArguments(lookup.findVirtual(LuaTable.class, "arraySize", MethodType.methodType(int.class)) - .asType(MethodType.methodType(double.class, LuaTable.class)), 0, Object.class); - STRING_LENGTH = MethodHandles.dropArguments(lookup.findVirtual(String.class, "length", MethodType.methodType(int.class)) - .asType(MethodType.methodType(double.class, String.class)), 0, Object.class); + TABLE_LENGTH = MethodHandles.dropArguments(lookup.findVirtual(LuaTable.class, "arraySize", MethodType.methodType(int.class)), 0, Object.class); + STRING_LENGTH = MethodHandles.dropArguments(lookup.findVirtual(String.class, "length", MethodType.methodType(int.class)), 0, Object.class); } catch (NoSuchMethodException | IllegalAccessException e) { throw new AssertionError(e); } @@ -50,7 +48,7 @@ public Value emit(LuaContext ctx, Block block) { @Override public LuaType outputType(LuaContext ctx) { // We can't do type analysis through metatables (yet) - return expr.outputType(ctx).equals(LuaType.STRING) ? LuaType.NUMBER : LuaType.UNKNOWN; + return expr.outputType(ctx).equals(LuaType.STRING) ? LuaType.INTEGER : LuaType.UNKNOWN; } } diff --git a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/LuaConstant.java b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/LuaConstant.java index 0817b07..29267c0 100644 --- a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/LuaConstant.java +++ b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/LuaConstant.java @@ -27,6 +27,8 @@ public Value emit(LuaContext ctx, Block block) { return Constant.nullValue(Type.OBJECT); } else if (value instanceof Boolean bool) { return Constant.of(bool); + } else if (value instanceof Integer num) { + return Constant.of(num); } else if (value instanceof Double num) { return Constant.of(num); } else if (value instanceof String str) { diff --git a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/NegateExpr.java b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/NegateExpr.java index cd9259d..9d075ed 100644 --- a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/NegateExpr.java +++ b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/NegateExpr.java @@ -41,7 +41,7 @@ private static double negate(Object callable, double value) { @Override public Value emit(LuaContext ctx, Block block) { var value = expr.emit(ctx, block); - if (outputType(ctx).equals(LuaType.NUMBER)) { + if (outputType(ctx).isNumber()) { return block.add(Arithmetic.negate(value)); } else { return block.add(LuaLinker.setupCall(ctx, CallSiteOptions.nonFunction(ctx.owner(), LuaType.UNKNOWN, LuaType.UNKNOWN), TARGET, value)); @@ -50,8 +50,14 @@ public Value emit(LuaContext ctx, Block block) { @Override public LuaType outputType(LuaContext ctx) { + var exprType = expr.outputType(ctx); + if (exprType == LuaType.INTEGER) { + return LuaType.INTEGER; + } else if (exprType == LuaType.FLOAT) { + return LuaType.FLOAT; + } // We can't do type analysis through metatables (yet) - return expr.outputType(ctx).equals(LuaType.NUMBER) ? LuaType.NUMBER : LuaType.UNKNOWN; + return LuaType.UNKNOWN; } } diff --git a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/StringConcatExpr.java b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/StringConcatExpr.java index 845f0f2..d73ac3b 100644 --- a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/StringConcatExpr.java +++ b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/expr/StringConcatExpr.java @@ -35,7 +35,7 @@ public record StringConcatExpr( throw new AssertionError(); } - TARGET = BinaryOp.newTarget(String.class, CONCAT_TWO, "__concat", + TARGET = BinaryOp.newTarget(List.of(new BinaryOp.Path(String.class, String.class, CONCAT_TWO)), "__concat", (a, b) -> new LuaException("attempted to concatenate non-string values")); } diff --git a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/linker/BinaryOp.java b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/linker/BinaryOp.java index 257bf00..2df52e6 100644 --- a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/linker/BinaryOp.java +++ b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/linker/BinaryOp.java @@ -3,6 +3,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.util.List; import java.util.function.BiFunction; import fi.benjami.code4jvm.lua.ir.LuaType; @@ -19,8 +20,8 @@ */ public class BinaryOp { - private static final boolean checkTypes(Class expected, Object callable, Object lhs, Object rhs) { - return lhs != null && lhs.getClass() == expected && rhs != null && rhs.getClass() == expected; + private static final boolean checkTypes(Class expectedLhs, Class expectedRhs, Object callable, Object lhs, Object rhs) { + return lhs != null && lhs.getClass() == expectedLhs && rhs != null && rhs.getClass() == expectedRhs; } @SuppressWarnings("unused") // MethodHandle @@ -37,7 +38,7 @@ private static final boolean checkLhsMetamethod(String metamethod, Object callab var lookup = MethodHandles.lookup(); try { CHECK_TYPES = lookup.findStatic(BinaryOp.class, "checkTypes", - MethodType.methodType(boolean.class, Class.class, Object.class, Object.class, Object.class)); + MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class, Object.class)); CHECK_LHS_METAMETHOD = lookup.findStatic(BinaryOp.class, "checkLhsMetamethod", MethodType.methodType(boolean.class, String.class, Object.class, Object.class)); } catch (NoSuchMethodException | IllegalAccessException e) { @@ -45,30 +46,36 @@ private static final boolean checkLhsMetamethod(String metamethod, Object callab } } + public record Path( + Class lhsType, + Class rhsType, + MethodHandle target + ) {} + /** * Produces a dynamic call target for a binary operation call site. - * @param expectedType Type that the fast path supports. - * @param fastPath Fast path that is entered if both sides are of expected - * type. The fast path should accept the call target as first parameter, - * LHS as second and RHS as third. + * @param fastPaths Fast paths, to be evaluated in order. * @param metamethod Name of the metamethod call if metatables are present. * @param errorHandler Called when either value has invalid type and * metamethods are not found. Returns a Lua exception that is thrown. * @return Call target. */ - public static DynamicTarget newTarget(Class expectedType, MethodHandle fastPath, String metamethod, + public static DynamicTarget newTarget(List fastPaths, String metamethod, BiFunction errorHandler) { - assert !expectedType.isPrimitive(); // LHS and RHS will be in their boxed forms - assert !expectedType.equals(LuaType.class); // This is currently unnecessary for Lua return (meta, args) -> { assert args.length == 2; var lhs = args[0]; var rhs = args[1]; - if (checkTypes(expectedType, null, lhs, rhs)) { - // Fast path, e.g. arithmetic operation on numbers or string concatenation on strings - var guard = CHECK_TYPES.bindTo(expectedType); - return new LuaCallTarget(fastPath, guard); - } else if (lhs instanceof LuaTable table + for (var path : fastPaths) { + if (checkTypes(path.lhsType, path.rhsType, null, lhs, rhs)) { + // Fast path, e.g. arithmetic operation on numbers or string concatenation on strings + var guard = MethodHandles.insertArguments(CHECK_TYPES, 0, path.lhsType, path.rhsType); + return new LuaCallTarget(path.target, guard); + } + } + + // None of the fast paths matched + if (lhs instanceof LuaTable table && table.metatable() != null && table.metatable().get(metamethod) != null) { // Slower path, call LHS metamethod diff --git a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/runtime/LuaTable.java b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/runtime/LuaTable.java index e78f169..537530e 100644 --- a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/runtime/LuaTable.java +++ b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/runtime/LuaTable.java @@ -37,6 +37,16 @@ private static int hash(Object key) { return key == null ? 0 : (hash = key.hashCode()) ^ (hash >>> 16); } + static Object normalizeKey(Object key) { + if (key instanceof Double num) { + var intVal = num.intValue(); + if (intVal == num) { + return intVal; + } + } + return key; + } + int getSlot(Object key) { if (keys == EMPTY) { return -1; @@ -55,9 +65,8 @@ int getSlot(Object key) { } int getArrayIndex(Object key) { - if (key instanceof Double num) { - var index = num.intValue(); - if (index == num.doubleValue() && index < arrayCapacity) { + if (key instanceof Integer index) { + if (index < arrayCapacity) { return index; } } @@ -76,6 +85,7 @@ Object getAt(int slot) { } public Object getRaw(Object key) { + key = normalizeKey(key); var arrayIndex = getArrayIndex(key); if (arrayIndex != -1) { return table[arrayIndex]; @@ -142,16 +152,16 @@ void setAt(int slot, Object key, Object value) { } public void setRaw(Object key, Object value) { - if (key instanceof Double num) { + key = normalizeKey(key); + if (key instanceof Integer index) { // The logic here is subtly different from getArrayIndex() // We allow appending to array (with a few gaps) even if it is full - var integer = num.intValue(); - if (integer == num.doubleValue() && integer < arrayCapacity + 3) { - if (integer >= arrayCapacity) { + if (index < arrayCapacity + 3) { + if (index >= arrayCapacity) { enlargeArray(); } - table[integer] = value; - arraySize = Math.max(arraySize, integer + 1); + table[index] = value; + arraySize = Math.max(arraySize, index + 1); return; } } @@ -191,7 +201,7 @@ public void set(Object key, Object value) { } } - int getFreeSlot(Object key) { + private int getFreeSlot(Object key) { if (keys == EMPTY) { return -1; } @@ -251,11 +261,10 @@ private void enlargeArray() { // Scan rest of the table to find if any keys should be moved to array part for (var i = 0; i < keys.length; i++) { var key = keys[i]; - if (key instanceof Double d) { - var integer = d.intValue(); - if (integer == d.doubleValue() && integer < newCapacity) { + if (key instanceof Integer index) { + if (index < newCapacity) { // Move to array part - newTable[integer] = table[arrayCapacity + i]; + newTable[index] = table[arrayCapacity + i]; table[arrayCapacity + i] = null; keys[i] = null; } @@ -320,7 +329,7 @@ public Object[] next(Object prevKey) { if (prevKey == null) { if (arraySize != 0) { // First call, array has at least one member - return new Object[] {1d, getArray(1)}; + return new Object[] {1, getArray(1)}; } else { // First call, no array members -> return "first" table member for (var i = arrayCapacity; i < table.length; i++) { @@ -332,11 +341,11 @@ public Object[] next(Object prevKey) { } int slot; - if (prevKey instanceof Double index) { + if (prevKey instanceof Integer index) { // Iterate the array in order as long as we have elements if (index < arraySize - 1) { var newIndex = index + 1; - return new Object[] {newIndex, getArray((int) newIndex)}; + return new Object[] {newIndex, getArray(newIndex)}; } else { slot = 0; // First entry after array part } @@ -407,7 +416,7 @@ private boolean nextArray() { // Reached array end // ... but if there were previously gaps, some hash table entries // might also need to be visible to array iterators - var nextEntry = getRaw((double) index); + var nextEntry = getRaw(index); if (nextEntry != null) { return true; } // else: REALLY reached array end @@ -436,7 +445,7 @@ private boolean nextTable() { } public Object key() { - return array ? (double) index : keys[index - arrayCapacity]; + return array ? index : keys[index - arrayCapacity]; } public Object value() { diff --git a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/runtime/TableAccess.java b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/runtime/TableAccess.java index 359de85..65f2985 100644 --- a/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/runtime/TableAccess.java +++ b/lua4jvm/src/main/java/fi/benjami/code4jvm/lua/runtime/TableAccess.java @@ -67,7 +67,7 @@ private static LuaCallTarget resolveConstantGet(LuaCallSite meta, Object[] args) return new LuaCallTarget(GET); } - var key = args[1]; + var key = LuaTable.normalizeKey(args[1]); // Low-level table APIs don't do this for us if (args[0] instanceof LuaTable table) { var metatable = table.metatable(); var arrayIndex = table.getArrayIndex(key); diff --git a/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/FunctionTest.java b/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/FunctionTest.java index a8699a9..4c69a24 100644 --- a/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/FunctionTest.java +++ b/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/FunctionTest.java @@ -58,7 +58,7 @@ public void upvalues() throws Throwable { var a = new LuaLocalVar("a"); var b = new LuaLocalVar("b"); var type = LuaType.function( - List.of(new UpvalueTemplate(a, LuaType.NUMBER)), + List.of(new UpvalueTemplate(a, LuaType.FLOAT)), List.of(b), new LuaBlock(List.of(new ReturnStmt(List.of( new ArithmeticExpr(new VariableExpr(a), ArithmeticExpr.Kind.ADD, new VariableExpr(b)) @@ -107,7 +107,7 @@ public void declareFunction() throws Throwable { var insideB = new LuaLocalVar("b"); var c = new LuaLocalVar("c"); var type = LuaType.function( - List.of(new UpvalueTemplate(a, LuaType.NUMBER)), + List.of(new UpvalueTemplate(a, LuaType.FLOAT)), List.of(b), new LuaBlock(List.of( new ReturnStmt(List.of(new FunctionDeclExpr( diff --git a/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/TableTest.java b/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/TableTest.java index fe17fb8..983fd1d 100644 --- a/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/TableTest.java +++ b/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/TableTest.java @@ -269,7 +269,7 @@ public void tableIterators() { } } while (prevKey != null); - var keys = Set.of(1d, "foo", "bar", "baz"); + var keys = Set.of(1, "foo", "bar", "baz"); var values = Set.of("test", 1d, 2d, 3d); assertEquals(keys, itKeys); @@ -298,7 +298,7 @@ public void arrayIterator() { itVals.add(it.value()); } - var keys = Set.of(1d, 2d); + var keys = Set.of(1, 2); var values = Set.of("test", "second"); assertEquals(keys, itKeys); @@ -317,7 +317,7 @@ public void arrayIterator() { itVals.add(it.value()); } - var keys = Set.of(1d, 2d, 3d, 4d); + var keys = Set.of(1, 2, 3, 4); var values = Set.of("test", "second", "later!", "third"); assertEquals(keys, itKeys); @@ -337,7 +337,7 @@ public void fakeArray() { { var it = table.arrayIterator(); assertTrue(it.next()); - assertEquals(1d, it.key()); + assertEquals(1, it.key()); assertEquals("test", "test"); assertFalse(it.next()); } @@ -356,7 +356,7 @@ public void fakeArray() { itVals.add(it.value()); } - var keys = Set.of(1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d, 9d, 10d); + var keys = Set.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); var values = Set.of("test", "second", 2d, 3d, 4d, 5d, 6d, 7d, 8d, 9d); assertEquals(keys, itKeys); diff --git a/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/UnaryOpTest.java b/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/UnaryOpTest.java index 7af9dba..eed7db2 100644 --- a/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/UnaryOpTest.java +++ b/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/UnaryOpTest.java @@ -48,8 +48,8 @@ public void negateMetatable() throws Throwable { @Test public void stringLength() throws Throwable { - assertEquals(5d, vm.execute("return #\"12345\"")); - assertEquals(5d, vm.execute(""" + assertEquals(5, vm.execute("return #\"12345\"")); + assertEquals(5, vm.execute(""" str = "12345" return #str """)); @@ -58,9 +58,9 @@ public void stringLength() throws Throwable { @Test public void tableLength() throws Throwable { // Array length - assertEquals(0d, vm.execute("return #{}")); - assertEquals(5d, vm.execute("return #{1, 2, 3, false, true}")); - assertEquals(0d, vm.execute("return #{foo = 1}")); + assertEquals(0, vm.execute("return #{}")); + assertEquals(5, vm.execute("return #{1, 2, 3, false, true}")); + assertEquals(0, vm.execute("return #{foo = 1}")); // Metatables var metaTbl = new LuaTable(); @@ -76,6 +76,6 @@ public void tableLength() throws Throwable { assertEquals("nope!", vm.execute("return #tbl")); metaTbl.set("__len", null); - assertEquals(0d, vm.execute("return #tbl")); + assertEquals(0, vm.execute("return #tbl")); } } diff --git a/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/VariableTest.java b/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/VariableTest.java index 0028ed9..ec3a0e2 100644 --- a/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/VariableTest.java +++ b/lua4jvm/src/test/java/fi/benjami/code4jvm/lua/test/VariableTest.java @@ -188,8 +188,8 @@ public void writeTable() throws Throwable { false), new SetVariablesStmt( List.of( - new TableField(new VariableExpr(c), new LuaConstant(1d, LuaType.NUMBER)), - new TableField(new VariableExpr(c), new LuaConstant(2d, LuaType.NUMBER)) + new TableField(new VariableExpr(c), new LuaConstant(1d, LuaType.FLOAT)), + new TableField(new VariableExpr(c), new LuaConstant(2d, LuaType.FLOAT)) ), List.of(new VariableExpr(a), new VariableExpr(b)), false),