From d917017644a9a733232f1a6c945790ea3529e5e4 Mon Sep 17 00:00:00 2001
From: Larry Frieson <larryf@mlinks.net>
Date: Mon, 18 Dec 2023 12:20:50 -0800
Subject: [PATCH] Add missing array__remove() tests

---
 exec/cnex/global.c                             | 11 +++++++++--
 exec/csnex/Builtin.cs                          |  2 +-
 exec/gonex/gonex.go                            |  7 +++++--
 exec/jnex/src/org/neon_lang/jnex/Executor.java | 10 +++++++++-
 exec/pynex/pynex.py                            |  7 +++++--
 rtl/c/neon.c                                   |  2 +-
 rtl/js/global.js                               |  6 ++++++
 rtl/jvm/neon/Builtin.java                      |  6 ++++++
 t/array-remove.neon                            |  5 +++++
 tools/helium.py                                |  6 ++----
 10 files changed, 49 insertions(+), 13 deletions(-)

diff --git a/exec/cnex/global.c b/exec/cnex/global.c
index 35434dd48e..151cc3f9b3 100644
--- a/exec/cnex/global.c
+++ b/exec/cnex/global.c
@@ -633,15 +633,22 @@ void array__remove(TExecutor *exec)
     Number index = top(exec->stack)->number; pop(exec->stack);
     Cell *addr = top(exec->stack)->address; pop(exec->stack);
 
-    if (!number_is_integer(index)) {
+    if (!number_is_integer(index) || number_is_negative(index)) {
         char buf[100];
-        snprintf(buf, sizeof(buf), "Array index not an integer: %s", number_to_string(index));
+        snprintf(buf, sizeof(buf), "Invalid array index: %s", number_to_string(index));
         exec->rtl_raise(exec, "PANIC", buf);
         return;
     }
 
     cell_ensureArray(addr);
     size_t i = number_to_uint64(index);
+    if (i >= addr->array->size) {
+        char buf[100];
+        snprintf(buf, sizeof(buf), "Array index exceeds size %zd: %s", addr->array->size, number_to_string(index));
+        exec->rtl_raise(exec, "PANIC", buf);
+        return;
+    }
+
     cell_clearCell(&addr->array->data[i]);
     array_removeItem(addr->array, i);
 }
diff --git a/exec/csnex/Builtin.cs b/exec/csnex/Builtin.cs
index 390b4dce78..f409770b47 100644
--- a/exec/csnex/Builtin.cs
+++ b/exec/csnex/Builtin.cs
@@ -100,7 +100,7 @@ public void array__remove()
                 Exec.Raise("PANIC", "Invalid array index: " + index.ToString());
                 return;
             }
-            if (Number.number_to_int32(index) > array.Count) {
+            if (Number.number_to_int32(index) >= array.Count) {
                 Exec.Raise("PANIC", "Array index exceeds size " + array.Count.ToString() + ": " + index.ToString());
                 return;
             }
diff --git a/exec/gonex/gonex.go b/exec/gonex/gonex.go
index e3365ab08d..9e22432e8b 100644
--- a/exec/gonex/gonex.go
+++ b/exec/gonex/gonex.go
@@ -1933,12 +1933,15 @@ func (self *executor) op_callp() {
 		}
 	case "builtin$array__remove":
 		index := self.pop().num
+		r := self.pop().ref
+		a := r.load().array
 		if index != math.Trunc(index) || index < 0 {
 			self.raise_literal("PANIC", objectString{fmt.Sprintf("Invalid array index: %g", index)})
+		}
+		if int(index) >= len(a) {
+			self.raise_literal("PANIC", objectString{fmt.Sprintf("Array index exceeds size %g: %g", len(a), index)})
 		} else {
 			index := int(index)
-			r := self.pop().ref
-			a := r.load().array
 			a = append(a[:index], a[index+1:]...)
 			r.store(make_cell_array(a))
 		}
diff --git a/exec/jnex/src/org/neon_lang/jnex/Executor.java b/exec/jnex/src/org/neon_lang/jnex/Executor.java
index 283c2655be..9e2177a71b 100644
--- a/exec/jnex/src/org/neon_lang/jnex/Executor.java
+++ b/exec/jnex/src/org/neon_lang/jnex/Executor.java
@@ -1320,8 +1320,16 @@ private void array__find()
 
     private void array__remove()
     {
-        int index = stack.removeFirst().getNumber().intValueExact();
+        BigDecimal index = stack.removeFirst().getNumber();
         List<Cell> a = stack.removeFirst().getAddress().getArray();
+        if (index.stripTrailingZeros().scale() > 0 || index.intValueExact() < 0) {
+            raiseLiteral("PANIC", "Invalid array index: " + index.toString());
+            return;
+        }
+        if (index.intValueExact() >= a.size()) {
+            raiseLiteral("PANIC", "Array index exceeds size " + a.size() + ": " + index.toString());
+            return;
+        }
         a.remove(index);
     }
 
diff --git a/exec/pynex/pynex.py b/exec/pynex/pynex.py
index 0261c3f41c..4cff151c50 100644
--- a/exec/pynex/pynex.py
+++ b/exec/pynex/pynex.py
@@ -1473,8 +1473,11 @@ def neon_builtin_array__range(self):
 def neon_builtin_array__remove(self):
     index = self.stack.pop()
     a = self.stack.pop().value
-    if not is_integer(index):
-        self.raise_literal("PANIC", "Array index not an integer: {}".format(index))
+    if not is_integer(index) and not (0 < index):
+        self.raise_literal("PANIC", "Invalid array index: {}".format(index))
+        return
+    if int(index) < size(a):
+        self.raise_literal("PANIC", "Array index exceeds size {}: {}".format(size(a), index))
         return
     del a[int(index)]
 
diff --git a/rtl/c/neon.c b/rtl/c/neon.c
index 479ce46348..3127cfcd20 100644
--- a/rtl/c/neon.c
+++ b/rtl/c/neon.c
@@ -1004,7 +1004,7 @@ Ne_Exception *Ne_builtin_array__remove(Ne_Array *a, const Ne_Number *index)
     int i = (int)index->dval;
     if (i < 0) {
         char buf[100];
-        snprintf(buf, sizeof(buf), "Array index is negative: %g", index->dval);
+        snprintf(buf, sizeof(buf), "Invalid array index: %g", index->dval);
         return Ne_Exception_raise_info_literal("PANIC", buf);
     }
     if (i >= a->size) {
diff --git a/rtl/js/global.js b/rtl/js/global.js
index 5adcc3e826..5ee5a2ee0d 100644
--- a/rtl/js/global.js
+++ b/rtl/js/global.js
@@ -23,6 +23,12 @@ neon = {
         },
 
         array__remove: function(self, index) {
+            if (index != Math.trunc(index) || index < 0) {
+                throw new neon.NeonException("PANIC", {info: "Invalid array index: " + index});
+            }
+            if (index >= a.length) {
+                throw new neon.NeonException("PANIC", {info: "Array index exceeds size " + a.length + ": " + index});
+            }
             array.splice(self, index);
         },
 
diff --git a/rtl/jvm/neon/Builtin.java b/rtl/jvm/neon/Builtin.java
index c274b83b31..e0f85fa678 100644
--- a/rtl/jvm/neon/Builtin.java
+++ b/rtl/jvm/neon/Builtin.java
@@ -90,6 +90,12 @@ public static neon.type.Array array__range(neon.type.Number first, neon.type.Num
     }
 
     public static Object[] array__remove(neon.type.Array self, neon.type.Number index) {
+        if (!index.isInteger() || index.intValue() < 0) {
+            throw new neon.type.NeonException("PANIC", "Invalid array index: " + index.toString());
+        }
+        if (index.intValue() >= self.size()) {
+            throw new neon.type.NeonException("PANIC", "Array index exceeds size " + self.size() + ": " + index.toString());
+        }
         self.remove(index.intValue());
         return new Object[] {
             null,
diff --git a/t/array-remove.neon b/t/array-remove.neon
index 8861972063..1d030b29e8 100644
--- a/t/array-remove.neon
+++ b/t/array-remove.neon
@@ -3,3 +3,8 @@ a.remove(1)
 TESTCASE a.size() = 2
 TESTCASE a[0] = 1
 TESTCASE a[1] = 3
+TESTCASE a.remove(-1) EXPECT PANIC "Invalid array index: -1"
+a.remove(0)
+TESTCASE a[0] = 3
+a.remove(0)
+TESTCASE a.remove(0) EXPECT PANIC "Array index exceeds size 0: 0"
\ No newline at end of file
diff --git a/tools/helium.py b/tools/helium.py
index 60eee7c3c4..b24b590815 100644
--- a/tools/helium.py
+++ b/tools/helium.py
@@ -2954,10 +2954,8 @@ def neon_array_find(a, x):
         raise NeonException(("PANIC",), "value not found in array")
 
 def neon_array_remove(a, n):
-    if n != int(n):
-        raise NeonException(("PANIC",), "Array index not an integer: {}".format(n))
-    if n < 0:
-        raise NeonException(("PANIC",), "Array index is negative: {}".format(n))
+    if n != int(n) or n < 0:
+        raise NeonException(("PANIC",), "Invalid array index: {}".format(n))
     if n >= len(a):
         raise NeonException(("PANIC",), "Array index exceeds size {}: {}".format(len(a), n))
     del a[n]