From de1a4c7e3875c8f79f3dd9cf4533786c1c709bfc Mon Sep 17 00:00:00 2001 From: mikera Date: Tue, 1 Oct 2024 08:25:26 +0100 Subject: [PATCH] Make out of range `long` or `int` casts throw :ARGUMENT --- .../src/main/java/convex/core/data/Blob.java | 2 ++ .../main/java/convex/core/data/type/Blob.java | 4 ++-- .../java/convex/core/data/type/Double.java | 2 +- .../java/convex/core/data/type/Integer.java | 2 +- .../convex/core/data/type/StringType.java | 4 +++- .../src/main/java/convex/core/lang/Core.java | 10 +++++++-- .../core/lang/exception/ErrorValue.java | 17 ++++++++++++++ .../test/java/convex/core/lang/CastTest.java | 18 ++++++++++++++- .../test/java/convex/core/lang/CoreTest.java | 22 ++++++++++--------- 9 files changed, 63 insertions(+), 18 deletions(-) diff --git a/convex-core/src/main/java/convex/core/data/Blob.java b/convex-core/src/main/java/convex/core/data/Blob.java index 521cb8ef7..04131f386 100644 --- a/convex-core/src/main/java/convex/core/data/Blob.java +++ b/convex-core/src/main/java/convex/core/data/Blob.java @@ -24,6 +24,7 @@ public class Blob extends AArrayBlob { public static final Blob EMPTY = Cells.intern(wrap(Utils.EMPTY_BYTES)); public static final Blob SINGLE_ZERO = Cells.intern(wrap(new byte[] {0})); public static final Blob SINGLE_ONE = Cells.intern(wrap(new byte[] {1})); + public static final Blob SINGLE_A =wrap(new byte[] {0x41}); public static final Blob NULL_ENCODING = Blob.wrap(new byte[] {Tag.NULL}); @@ -251,6 +252,7 @@ public int estimatedEncodingSize() { */ public static final int MAX_ENCODING_LENGTH=1+Format.getVLCCountLength(CHUNK_LENGTH)+CHUNK_LENGTH; + @Override public boolean isCanonical() { return count <= Blob.CHUNK_LENGTH; diff --git a/convex-core/src/main/java/convex/core/data/type/Blob.java b/convex-core/src/main/java/convex/core/data/type/Blob.java index 08713ce27..381971ce6 100644 --- a/convex-core/src/main/java/convex/core/data/type/Blob.java +++ b/convex-core/src/main/java/convex/core/data/type/Blob.java @@ -9,7 +9,7 @@ public class Blob extends AStandardType { public static final Blob INSTANCE = new Blob(); - + private Blob() { super(ABlob.class); } @@ -26,7 +26,7 @@ public String toString() { @Override public ABlob defaultValue() { - return convex.core.data.Blob.EMPTY; + return convex.core.data.Blob.SINGLE_A; } @Override diff --git a/convex-core/src/main/java/convex/core/data/type/Double.java b/convex-core/src/main/java/convex/core/data/type/Double.java index 1fe10ffac..47e0676aa 100644 --- a/convex-core/src/main/java/convex/core/data/type/Double.java +++ b/convex-core/src/main/java/convex/core/data/type/Double.java @@ -30,7 +30,7 @@ public String toString () { @Override public CVMDouble defaultValue() { - return CVMDouble.ZERO; + return CVMDouble.ONE; } @Override diff --git a/convex-core/src/main/java/convex/core/data/type/Integer.java b/convex-core/src/main/java/convex/core/data/type/Integer.java index b8ebb9a7b..063e262aa 100644 --- a/convex-core/src/main/java/convex/core/data/type/Integer.java +++ b/convex-core/src/main/java/convex/core/data/type/Integer.java @@ -31,7 +31,7 @@ public String toString () { @Override public AInteger defaultValue() { - return CVMLong.ZERO; + return CVMLong.ONE; } @Override diff --git a/convex-core/src/main/java/convex/core/data/type/StringType.java b/convex-core/src/main/java/convex/core/data/type/StringType.java index ce08254c3..5d84c735e 100644 --- a/convex-core/src/main/java/convex/core/data/type/StringType.java +++ b/convex-core/src/main/java/convex/core/data/type/StringType.java @@ -8,6 +8,8 @@ * Type that represents CVM Byte values */ public final class StringType extends AStandardType { + + private static final StringShort DEFAULT_VALUE=StringShort.create("A"); /** * Singleton runtime instance */ @@ -30,7 +32,7 @@ public String toString () { @Override public AString defaultValue() { - return StringShort.EMPTY; + return DEFAULT_VALUE; } @Override diff --git a/convex-core/src/main/java/convex/core/lang/Core.java b/convex-core/src/main/java/convex/core/lang/Core.java index 5cf2793b7..9202f5be1 100644 --- a/convex-core/src/main/java/convex/core/lang/Core.java +++ b/convex-core/src/main/java/convex/core/lang/Core.java @@ -1759,7 +1759,10 @@ public Context invoke(Context context, ACell[] args) { ACell a = args[0]; CVMLong result = RT.castLong(a); - if (result == null) return context.withCastError(0, args,Types.LONG); + if (result == null) { + if (a instanceof ANumeric) return context.withArgumentError("Out of range"); + return context.withCastError(0, args,Types.LONG); + } return context.withResult(Juice.ARITHMETIC, result); } @@ -1773,7 +1776,10 @@ public Context invoke(Context context, ACell[] args) { ACell a = args[0]; AInteger result = RT.castInteger(a); - if (result == null) return context.withCastError(0, args,Types.INTEGER); + if (result == null) { + if (a instanceof ANumeric) return context.withArgumentError("Out of range"); + return context.withCastError(0, args,Types.INTEGER); + } // TODO: bigint construction cost? return context.withResult(Juice.ARITHMETIC, result); } diff --git a/convex-core/src/main/java/convex/core/lang/exception/ErrorValue.java b/convex-core/src/main/java/convex/core/lang/exception/ErrorValue.java index 8b9c5c7d5..9995940ba 100644 --- a/convex-core/src/main/java/convex/core/lang/exception/ErrorValue.java +++ b/convex-core/src/main/java/convex/core/lang/exception/ErrorValue.java @@ -1,8 +1,10 @@ package convex.core.lang.exception; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import convex.core.ErrorCodes; import convex.core.data.ACell; import convex.core.data.AString; import convex.core.data.Address; @@ -28,17 +30,32 @@ */ public class ErrorValue extends AThrowable { + private static final HashMap defaultErrors= new HashMap<>(); + private final ACell message; private final ArrayList trace=new ArrayList<>(); private ACell log; private Address address=null; + + static { + addDefaultError(ErrorCodes.ARGUMENT,"Invalid argument value"); + addDefaultError(ErrorCodes.NOBODY,"Account does not exist"); + } private ErrorValue(ACell code, ACell message) { super (code); this.message=message; } + private static void addDefaultError(Keyword code, String message) { + defaultErrors.put(code,create(code,message)); + } + public static ErrorValue create(Keyword code) { + if (defaultErrors.containsKey(code)) { + return defaultErrors.get(code); + } + return new ErrorValue(code,null); } diff --git a/convex-core/src/test/java/convex/core/lang/CastTest.java b/convex-core/src/test/java/convex/core/lang/CastTest.java index bc3d6170b..8efba028d 100644 --- a/convex-core/src/test/java/convex/core/lang/CastTest.java +++ b/convex-core/src/test/java/convex/core/lang/CastTest.java @@ -2,20 +2,31 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; import convex.core.ErrorCodes; import convex.core.data.ACell; +import convex.core.data.Address; import convex.core.data.Strings; import convex.core.data.Symbol; import convex.core.data.prim.CVMChar; +import convex.core.data.type.AType; + +import static convex.test.Assertions.*; public class CastTest extends ACVMTest { static final String[] casts = new String[] {"double","int", "long", "boolean","blob","address", "str","name","symbol","keyword","char"}; static final String[] vals = new String[] {"##Inf","1e308","0.0", "-0.0", "999999999999999999999999999", "9223372036854775807", "1","0","-1","0xcafebabe1234567890","0x41","0x","\\c","\\u0474", "\"hello\"","\"\"","#12",":foo","'baz","true","false","nil"}; + @Test + public void testRoundTrips() { + assertCVMEquals(1L,eval("(long (address 1))")); + assertCVMEquals(Address.ZERO,eval("(address (blob #0))")); + } + @Test public void testAllCasts() { for (String c: casts) { @@ -25,7 +36,12 @@ public void testAllCasts() { if (ctx.isError()) { ACell code=ctx.getErrorCode(); if (ErrorCodes.ARGUMENT.equals(code)) { - // anything to test? + // the default value should be in range + AType type=RT.getType(Reader.read(v)); + Context nctx=step("("+c+" "+RT.print(type.defaultValue())+")"); + if (nctx.isError()) { + fail("ARGUMENT fallback not working in: "+cmd); + } } else { assertEquals(ErrorCodes.CAST,code,()->"Unexpected "+code+" in "+cmd); } diff --git a/convex-core/src/test/java/convex/core/lang/CoreTest.java b/convex-core/src/test/java/convex/core/lang/CoreTest.java index 9d82495d1..f1dc1a5f3 100644 --- a/convex-core/src/test/java/convex/core/lang/CoreTest.java +++ b/convex-core/src/test/java/convex/core/lang/CoreTest.java @@ -417,10 +417,11 @@ public void testLong() { assertEquals(9007199254740992L,evalL("(long 9007199254740992.0)")); // 2^53 assertEquals(9007199254740992L,evalL("(long (double 9007199254740993))")); - // Cast errors on non-finite doubles - assertCastError(step("(long ##NaN)")); - assertCastError(step("(long ##Inf)")); - assertCastError(step("(long ##-Inf)")); + // :ARGUMENT errors on non-finite or out of range doubles + assertArgumentError(step("(long ##NaN)")); + assertArgumentError(step("(long ##Inf)")); + assertArgumentError(step("(long ##-Inf)")); + assertArgumentError(step("(long 1e50)")); assertArityError(step("(long)")); assertArityError(step("(long 1 2)")); @@ -436,9 +437,9 @@ public void testLong() { // Long limits and overflow assertEquals(Long.MAX_VALUE,evalL("(long 9223372036854775807)")); assertEquals(Long.MIN_VALUE,evalL("(long -9223372036854775808)")); - assertCastError(step("(long 18446744073709551616)")); - assertCastError(step("(long 9223372036854775808)")); - assertCastError(step("(long -9223372036854775809)")); + assertArgumentError(step("(long 18446744073709551616)")); + assertArgumentError(step("(long 9223372036854775808)")); + assertArgumentError(step("(long -9223372036854775809)")); } @@ -468,9 +469,10 @@ public void testInt() { assertEquals(CVMLong.MAX_VALUE,eval("(int 9223372036854775807.0)")); // actual max value assertEquals(CVMLong.MAX_VALUE,eval("(int 9223372036854775809.0)")); // above max value - assertCastError(step("(int ##NaN)")); - assertCastError(step("(int ##Inf)")); - assertCastError(step("(int ##-Inf)")); + // These are :ARGUMENT error because of out of range. Other doubles might work. + assertArgumentError(step("(int ##NaN)")); + assertArgumentError(step("(int ##Inf)")); + assertArgumentError(step("(int ##-Inf)")); // Currently we disallow bools to explicitly cast to longs. Not round trippable assertCastError(step("(int true)"));